diff options
author | paulo <paul@bayleaf.org.uk> | 2010-01-08 18:20:58 +0000 |
---|---|---|
committer | paulo <paul@bayleaf.org.uk> | 2010-01-08 18:20:58 +0000 |
commit | 020867eda32194967de7ab6f1c1128147a9ed0a9 (patch) | |
tree | aef36b5f51769c48653c001db3c15c16e86298e5 | |
parent | 6d4aaf90ef68a92e456afc294da1f30051d2c5f6 (diff) | |
download | quagga-020867eda32194967de7ab6f1c1128147a9ed0a9.tar.bz2 quagga-020867eda32194967de7ab6f1c1128147a9ed0a9.tar.xz |
Fixed type in bgp_open_state.c. New file bgp_msg_read.c for parsing
bgp messages
-rw-r--r-- | bgpd/bgp_msg_read.c | 919 | ||||
-rw-r--r-- | bgpd/bgp_open_state.c | 42 |
2 files changed, 940 insertions, 21 deletions
diff --git a/bgpd/bgp_msg_read.c b/bgpd/bgp_msg_read.c new file mode 100644 index 00000000..fa547789 --- /dev/null +++ b/bgpd/bgp_msg_read.c @@ -0,0 +1,919 @@ +/* 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. + */ + +/* Receive BGP open packet and parse it into the session's + * open_recv + */ +int +bgp_msg_open_receive (bgp_connection connection, bgp_size_t size) +{ + int ret; + u_char version; + u_char optlen; + u_int16_t holdtime; + u_int16_t send_holdtime; + as_t remote_as; + as_t as4 = 0; + struct peer *realpeer; + struct in_addr remote_id; + int capability; + u_int8_t notify_data_remote_as[2]; + u_int8_t notify_data_remote_id[4]; + bgp_session session = connection->session; + bgp_open_state open_recv; + + /* To receive the parsed open message */ + session->open_recv = open_recv = bgp_open_state_init_new(session->open_recv); + + realpeer = NULL; + + /* Parse open packet. */ + version = stream_getc (peer->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); + + /* 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, + 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) + { + /* 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_masg_peek_for_as4_capability (peer, optlen); + } + + /* Just in case we have a silly peer who sends AS4 capability set to 0 */ + if (open_rcvd->can_as4 && !as4) + { + zlog_err ("%s bad OPEN, got AS4 capability, but AS4 set to 0", + session->host); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_PEER_AS); + return -1; + } + + if (remote_as == BGP_AS_TRANS) + { + /* Take the AS4 from the capability. We must have received the + * capability now! Otherwise we have a asn16 peer who uses + * BGP_AS_TRANS, for some unknown reason. + */ + if (as4 == BGP_AS_TRANS) + { + zlog_err ("%s [AS4] NEW speaker using AS_TRANS for AS4, not allowed", + session->host); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_PEER_AS); + return -1; + } + + if (!as4 && BGP_DEBUG (as4, AS4)) + zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS, but no AS4." + " Odd, but proceeding.", 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 + { + /* 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_rcvd->can_as4 && as4 != remote_as) + { + /* raise error, log this, close session */ + zlog_err ("%s bad OPEN, got AS4 capability, but remote_as %u" + " mismatch with 16bit 'myasn' %u in open", + session->host, as4, remote_as); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_PEER_AS); + return -1; + } + } + + /* Lookup peer from Open packet. */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + { + int as = 0; + + realpeer = peer_lookup_with_open (&peer->su, remote_as, &remote_id, &as); + + if (! realpeer) + { + /* Peer's source IP address is check in bgp_accept(), so this + must be AS number mismatch or remote-id configuration + mismatch. */ + if (as) + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s bad OPEN, wrong router identifier %s", + peer->host, inet_ntoa (remote_id)); + bgp_notify_send_with_data (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_BGP_IDENT, + notify_data_remote_id, 4); + } + else + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s bad OPEN, remote AS is %u, expected %u", + peer->host, remote_as, peer->as); + bgp_notify_send_with_data (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_PEER_AS, + notify_data_remote_as, 2); + } + return -1; + } + } + + /* When collision is detected and this peer is closed. Return + immediately. */ + ret = bgp_collision_detect (peer, remote_id); + if (ret < 0) + return ret; + + /* Hack part. */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + { + if (realpeer->status == Established + && CHECK_FLAG (realpeer->sflags, PEER_STATUS_NSF_MODE)) + { + realpeer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; + SET_FLAG (realpeer->sflags, PEER_STATUS_NSF_WAIT); + } + else if (ret == 0 && realpeer->status != Active + && realpeer->status != OpenSent + && realpeer->status != OpenConfirm + && realpeer->status != Connect) + { + /* XXX: This is an awful problem.. + * + * According to the RFC we should just let this connection (of the + * accepted 'peer') continue on to Established if the other + * connection (the 'realpeer' one) is in state Connect, and deal + * with the more larval FSM as/when it gets far enough to receive + * an Open. We don't do that though, we instead close the (more + * developed) accepted connection. + * + * This means there's a race, which if hit, can loop: + * + * FSM for A FSM for B + * realpeer accept-peer realpeer accept-peer + * + * Connect Connect + * Active + * OpenSent OpenSent + * <arrive here, + * Notify, delete> + * Idle Active + * OpenSent OpenSent + * <arrive here, + * Notify, delete> + * Idle + * <wait> <wait> + * Connect Connect + * + * + * If both sides are Quagga, they're almost certain to wait for + * the same amount of time of course (which doesn't preclude other + * implementations also waiting for same time). The race is + * exacerbated by high-latency (in bgpd and/or the network). + * + * The reason we do this is because our FSM is tied to our peer + * structure, which carries our configuration information, etc. + * I.e. we can't let the accepted-peer FSM continue on as it is, + * cause it's not associated with any actual peer configuration - + * it's just a dummy. + * + * It's possible we could hack-fix this by just bgp_stop'ing the + * realpeer and continueing on with the 'transfer FSM' below. + * Ideally, we need to seperate FSMs from struct peer. + * + * Setting one side to passive avoids the race, as a workaround. + */ + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s peer status is %s close connection", + realpeer->host, LOOKUP (bgp_status_msg, + realpeer->status)); + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONNECT_REJECT); + + return -1; + } + + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s [Event] Transfer accept BGP peer to real (state %s)", + peer->host, + LOOKUP (bgp_status_msg, realpeer->status)); + + bgp_stop (realpeer); + + /* Transfer file descriptor. */ + realpeer->fd = peer->fd; + peer->fd = -1; + + /* Transfer input buffer. */ + stream_free (realpeer->ibuf); + realpeer->ibuf = peer->ibuf; + realpeer->packet_size = peer->packet_size; + peer->ibuf = NULL; + + /* Transfer status. */ + realpeer->status = peer->status; + bgp_stop (peer); + + /* peer pointer change. Open packet send to neighbor. */ + peer = realpeer; + bgp_open_send (peer); + if (peer->fd < 0) + { + zlog_err ("bgp_open_receive peer's fd is negative value %d", + peer->fd); + return -1; + } + BGP_READ_ON (peer->t_read, bgp_read, peer->fd); + } + + /* remote router-id check. */ + if (remote_id.s_addr == 0 + || ntohl (remote_id.s_addr) >= 0xe0000000 + || ntohl (peer->local_id.s_addr) == ntohl (remote_id.s_addr)) + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s bad OPEN, wrong router identifier %s", + peer->host, inet_ntoa (remote_id)); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_BGP_IDENT, + notify_data_remote_id, 4); + return -1; + } + + /* Set remote router-id */ + peer->remote_id = remote_id; + + /* 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_notify_send_with_data (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSUP_VERSION, + &maxver, 1); + return -1; + } + + /* Check neighbor as number. */ + if (remote_as != peer->as) + { + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s bad OPEN, remote AS is %u, expected %u", + peer->host, remote_as, peer->as); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_BAD_PEER_AS, + notify_data_remote_as, 2); + return -1; + } + + /* From the rfc: Upon receipt of an OPEN message, a BGP speaker MUST + calculate the value of the Hold Timer by using the smaller of its + configured Hold Time and the Hold Time received in the OPEN message. + The Hold Time MUST be either zero or at least three seconds. An + implementation may reject connections on the basis of the Hold Time. */ + + if (open_recv->holdtime < 3 && open_recv->holdtime != 0) + { + bgp_notify_send (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNACEP_HOLDTIME); + return -1; + } + + /* Open option part parse. */ + if (optlen != 0) + { + ret = bgp_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); + } + + /* Get sockname. */ + bgp_getsockname (peer); + + BGP_EVENT_ADD (peer, Receive_OPEN_message); + + peer->packet_size = 0; + if (peer->ibuf) + stream_reset (peer->ibuf); + + return 0; +} + + +/* 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_masg_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; + + /* The full capability parser will better flag the error.. */ + if (STREAM_READABLE(s) < length) + 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. + */ + while (stream_get_getp(s) < end) + { + u_char opt_type; + u_char opt_length; + + /* 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; + + assert (capd_end <= end); + + while (stream_get_getp (s) < capd_end) + { + struct capability_header hdr; + + if (stream_get_getp (s) + 2 > capd_end) + goto end; + + hdr.code = stream_getc (s); + hdr.length = stream_getc (s); + + if ((stream_get_getp(s) + hdr.length) > capd_end) + goto end; + + if (hdr.code == CAPABILITY_CODE_AS4) + { + if (hdr.length != CAPABILITY_CODE_AS4_LEN) + goto end; + + if (BGP_DEBUG (as4, AS4)) + zlog_info ("[AS4] found AS4 capability, about to parse"); + as4 = stream_getl (connection->ibuf); + open_recv->can_as4 = 1; + + goto end; + } + stream_forward_getp (s, hdr.length); + } + } + } + +end: + stream_set_getp (s, orig_getp); + return as4; +} + +/* Parse open option */ +static int +bgp_msg_open_option_parse (bgp_connection connection, u_char length, int *capability) +{ + 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; + bpg_session session = connection->session; + + ret = 0; + error = error_data; + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s rcv OPEN w/ OPTION parameter len: %u", + session->host, length); + + while (stream_get_getp(s) < end) + { + u_char opt_type; + u_char opt_length; + + /* Must have at least an OPEN option header */ + if (STREAM_READABLE(s) < 2) + { + zlog_info ("%s Option length error", session->host); + bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + return -1; + } + + /* Fetch option type and length. */ + opt_type = stream_getc (s); + opt_length = stream_getc (s); + + /* Option length check. */ + if (STREAM_READABLE (s) < opt_length) + { + zlog_info ("%s Option length error", session->host); + bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + return -1; + } + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s rcvd OPEN w/ optional parameter type %u (%s) len %u", + peer->host, opt_type, + opt_type == BGP_OPEN_OPT_AUTH ? "Authentication" : + opt_type == BGP_OPEN_OPT_CAP ? "Capability" : "Unknown", + opt_length); + + switch (opt_type) + { + case BGP_OPEN_OPT_AUTH: + ret = bgp_auth_parse (peer, opt_length); + break; + case BGP_OPEN_OPT_CAP: + ret = bgp_capability_parse (peer, opt_length, &error); + *capability = 1; + break; + default: + bgp_notify_send (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSUP_PARAM); + ret = -1; + break; + } + + /* 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; + } + + /* All OPEN option is parsed. Check capability when strict compare + flag is enabled.*/ + /* TODO: where is this flag in the session? */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_STRICT_CAP_MATCH)) + { + /* If Unsupported Capability exists. */ + if (error != error_data) + { + bgp_notify_send_with_data (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSUP_CAPBL, + error_data, error - error_data); + return -1; + } + + /* Check local capability does not negotiated with remote + peer. */ + if (! strict_capability_same (peer)) + { + bgp_notify_send (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSUP_CAPBL); + return -1; + } + } + + /* Check there is no common capability send Unsupported Capability + error. */ + if (*capability && ! CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) + { + if (! peer->afc_nego[AFI_IP][SAFI_UNICAST] + && ! peer->afc_nego[AFI_IP][SAFI_MULTICAST] + && ! peer->afc_nego[AFI_IP][SAFI_MPLS_VPN] + && ! peer->afc_nego[AFI_IP6][SAFI_UNICAST] + && ! peer->afc_nego[AFI_IP6][SAFI_MULTICAST]) + { + plog_err (peer->log, "%s [Error] No common capability", peer->host); + + if (error != error_data) + + bgp_notify_send_with_data (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSUP_CAPBL, + error_data, error - error_data); + else + bgp_notify_send (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSUP_CAPBL); + return -1; + } + } + return 0; +} + +/* Parse given capability. + * XXX: This is reading into a stream, but not using stream API + */ +static int +bgp_msg_capability_parse (bgp_connection connection, size_t length, u_char **error) +{ + 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; + + assert (STREAM_READABLE (s) >= length); + + while (stream_get_getp (s) < end) + { + size_t start; + u_char *sp = stream_pnt (s); + struct capability_header caphdr; + + /* We need at least capability code and capability length. */ + if (stream_get_getp(s) + 2 > end) + { + zlog_info ("%s Capability length error (< header)", session->host); + bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + return -1; + } + + 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) + { + zlog_info ("%s Capability length error (< length)", session->host); + bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + return -1; + } + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s OPEN has %s capability (%u), length %u", + peer->host, + LOOKUP (capcode_str, caphdr.code), + caphdr.code, caphdr.length); + + /* Length sanity check, type-specific, for known capabilities */ + switch (caphdr.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: + /* Check length. */ + if (caphdr.length < cap_minsizes[caphdr.code]) + { + 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_notify_send (peer, BGP_NOTIFY_CEASE, 0); + return -1; + } + /* we deliberately ignore unknown codes, see below */ + default: + break; + } + + switch (caphdr.code) + { + case CAPABILITY_CODE_MP: + { + /* Ignore capability when override-capability is set. */ + /* TODO: where is this flag in the session? */ + if (! CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) + { + /* 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; + } + } + } + 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; + } + break; + case CAPABILITY_CODE_ORF: + case CAPABILITY_CODE_ORF_OLD: + if (bgp_capability_orf (peer, &caphdr)) + return -1; + break; + case CAPABILITY_CODE_RESTART: + if (bgp_capability_restart (peer, &caphdr)) + return -1; + break; + case CAPABILITY_CODE_DYNAMIC: + SET_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV); + 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_capability_as4 (peer, &caphdr)) + return -1; + break; + default: + if (caphdr.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); + } + else + { + zlog_warn ("%s unrecognized capability code: %d - ignored", + session->host, caphdr.code); + memcpy (*error, sp, caphdr.length + 2); + *error += caphdr.length + 2; + } + } + 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); + } + } + return 0; +} + +/* Set negotiated capability value. */ +static int +bgp_msg_capability_mp (bgp_connection connection, struct capability_header *hdr) +{ + 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; + + bgp_capability_mp_data (s, &mpc); + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s OPEN has MP_EXT CAP for afi/safi: %u/%u", + session->host, mpc.afi, mpc.safi); + + if (!bgp_afi_safi_valid_indices (mpc.afi, &mpc.safi)) + return -1; + + /* Now safi remapped, and afi/safi are valid array indices */ + qb = qafx_bit(qafx_num_from_qAFI_qSAFI(mpc.afi, mpc.safi)); + + open_recv->can_mp_ext |= qb; + + /* won't negotiate on afi/safi? */ + if ((open_send->can_mp_ext & qb) == 0) + return -1; + + return 0; +} + +static int +bgp_capability_orf_entry (bgp_connection connection, struct capability_header *hdr) +{ + struct stream *s = connection->ibuf; + struct capability_orf_entry entry; + afi_t afi; + safi_t safi; + u_char type; + u_char mode; + u_int16_t sm_cap = 0; /* capability send-mode receive */ + u_int16_t rm_cap = 0; /* capability receive-mode receive */ + int i; + bgp_session session = connection->session; + qafx_bit_t qb; + + /* ORF Entry header */ + bgp_msg_capability_mp_data (s, &entry.mpc); + entry.num = stream_getc (s); + afi = entry.mpc.afi; + safi = entry.mpc.safi; + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s ORF Cap entry for afi/safi: %u/%u", + session->host, entry.mpc.afi, entry.mpc.safi); + + /* 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; + } + + /* validate number field */ + if (sizeof (struct capability_orf_entry) + (entry.num * 2) > hdr->length) + { + zlog_info ("%s ORF Capability entry length error," + " Cap length %u, num %u", + session->host, hdr->length, entry.num); + bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + return -1; + } + + for (i = 0 ; i < entry.num ; i++) + { + type = stream_getc(s); + mode = stream_getc(s); + + /* ORF Mode error check */ + switch (mode) + { + case ORF_MODE_BOTH: + case ORF_MODE_SEND: + case ORF_MODE_RECEIVE: + 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) + { + case CAPABILITY_CODE_ORF: + switch (type) + { + case ORF_TYPE_PREFIX: + break; + default: + bgp_msg_capability_orf_not_support (session->host, afi, safi, type, mode); + continue; + } + break; + case CAPABILITY_CODE_ORF_OLD: + switch (type) + { + case ORF_TYPE_PREFIX_OLD: + break; + default: + bgp_msg_capability_orf_not_support (session->host, afi, safi, type, mode); + continue; + } + break; + default: + bgp_msg_capability_orf_not_support (session->host, afi, safi, type, mode); + continue; + } + + /* AFI vs SAFI */ + if (!((afi == AFI_IP && safi == SAFI_UNICAST) + || (afi == AFI_IP && safi == SAFI_MULTICAST) + || (afi == AFI_IP6 && safi == SAFI_UNICAST))) + { + bgp_msg_capability_orf_not_support (session->host, afi, 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), + LOOKUP (orf_mode_str, mode), + entry.mpc.afi, safi); + + + if (qb & open_recv->can_orf_prefix_send) + SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV); + if (qb & open_recv->can_orf_prefix_recv) + SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV); + + if (hdr->code == CAPABILITY_CODE_ORF) + open_recv->can_orf_prefix |= bgp_cap_form_new; + else if (hdr->code == CAPABILITY_CODE_ORF_OLD) + open_recv->can_orf_prefix |= bgp_cap_form_old; + else + { + bgp_msg_capability_orf_not_support (session->host, afi, safi, type, mode); + continue; + } + + qb = qafx_bit(qafx_num_from_qAFI_qSAFI(afi, safi)); + switch (mode) + { + case ORF_MODE_BOTH: + open_recv->can_orf_prefix_send |= qb; + open_recv->can_orf_prefix_recv |= qb; + break; + case ORF_MODE_SEND: + open_recv->can_orf_prefix_send |= qb; + break; + case ORF_MODE_RECEIVE: + open_recv->can_orf_prefix_recv |= qb; + break; + } + } + return 0; +} + + +static int +bgp_msg_apability_orf (bgp_connection connection, struct capability_header *hdr) +{ + struct stream *s = connection->ibuf; + size_t end = stream_get_getp (s) + hdr->length; + + assert (stream_get_getp(s) + sizeof(struct capability_orf_entry) <= end); + + /* 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). + */ + do + { + if (bgp_msg_capability_orf_entry (connection, hdr) == -1) + return -1; + } + while (stream_get_getp(s) + sizeof(struct capability_orf_entry) < end); + + return 0; +} + +static void +bgp_msg_capability_orf_not_support (char *host, afi_t afi, safi_t safi, + u_char type, u_char mode) +{ + 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); +} diff --git a/bgpd/bgp_open_state.c b/bgpd/bgp_open_state.c index 5603364b..4c44bbd5 100644 --- a/bgpd/bgp_open_state.c +++ b/bgpd/bgp_open_state.c @@ -148,12 +148,12 @@ bgp_peer_open_state_receive(bgp_peer peer) { bgp_session session = peer->session; bgp_open_state open_send = session->open_send; - bgp_open_state open_rcvd = session->open_rcvd; + bgp_open_state open_recv = session->open_recv; int afi; int safi; /* Check neighbor as number. */ - assert(open_rcvd->my_as == peer->as); + assert(open_recv->my_as == peer->as); /* holdtime */ /* From the rfc: A reasonable maximum time between KEEPALIVE messages @@ -163,8 +163,8 @@ bgp_peer_open_state_receive(bgp_peer peer) messages as a function of the Hold Time interval. */ peer->v_holdtime = - (open_rcvd->holdtime < open_send->holdtime) - ? open_rcvd->holdtime + (open_recv->holdtime < open_send->holdtime) + ? open_recv->holdtime : open_send->holdtime; peer->v_keepalive = peer->v_holdtime / 3; @@ -174,10 +174,10 @@ bgp_peer_open_state_receive(bgp_peer peer) session->keepalive_timer_interval = peer->v_keepalive ; /* Set remote router-id */ - peer->remote_id = open_rcvd->bgp_id; + peer->remote_id = open_recv->bgp_id; /* AS4 */ - if (open_rcvd->can_as4) + if (open_recv->can_as4) SET_FLAG (peer->cap, PEER_CAP_AS4_RCV); /* AFI/SAFI */ @@ -188,7 +188,7 @@ bgp_peer_open_state_receive(bgp_peer peer) for (safi = qSAFI_MIN ; safi <= qSAFI_MAX ; ++safi) { qafx_bit_t qb = qafx_bit(qafx_num_from_qAFI_qSAFI(afi, safi)); - if (qb & open_rcvd->can_mp_ext) + if (qb & open_recv->can_mp_ext) { peer->afc_recv[afi][safi] = 1; assert(peer->afc[afi][safi]); @@ -198,9 +198,9 @@ bgp_peer_open_state_receive(bgp_peer peer) } /* Route refresh. */ - if (open_rcvd->can_r_refresh & bgp_cap_form_old) + if (open_recv->can_r_refresh & bgp_cap_form_old) SET_FLAG (peer->cap, PEER_CAP_REFRESH_OLD_RCV); - else if (open_rcvd->can_r_refresh & bgp_cap_form_new) + else if (open_recv->can_r_refresh & bgp_cap_form_new) SET_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV); /* ORF */ @@ -208,25 +208,25 @@ bgp_peer_open_state_receive(bgp_peer peer) for (safi = qSAFI_MIN ; safi <= qSAFI_MAX ; ++safi) { qafx_bit_t qb = qafx_bit(qafx_num_from_qAFI_qSAFI(afi, safi)); - if (qb & open_rcvd->can_orf_prefix_send) + if (qb & open_recv->can_orf_prefix_send) SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV); - if (qb & open_rcvd->can_orf_prefix_recv) + if (qb & open_recv->can_orf_prefix_recv) SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV); } /* ORF prefix. */ - if (open_rcvd->can_orf_prefix_send) + if (open_recv->can_orf_prefix_send) { - if (open_rcvd->can_orf_prefix & bgp_cap_form_old) + if (open_recv->can_orf_prefix & bgp_cap_form_old) SET_FLAG (peer->cap, PEER_CAP_ORF_PREFIX_SM_OLD_RCV); - else if (open_rcvd->can_orf_prefix & bgp_cap_form_new) + else if (open_recv->can_orf_prefix & bgp_cap_form_new) SET_FLAG (peer->cap, PEER_CAP_ORF_PREFIX_SM_RCV); } - if (open_rcvd->can_orf_prefix_recv) + if (open_recv->can_orf_prefix_recv) { - if (open_rcvd->can_orf_prefix & bgp_cap_form_old) + if (open_recv->can_orf_prefix & bgp_cap_form_old) SET_FLAG (peer->cap, PEER_CAP_ORF_PREFIX_RM_OLD_RCV); - else if (open_rcvd->can_orf_prefix & bgp_cap_form_new) + else if (open_recv->can_orf_prefix & bgp_cap_form_new) SET_FLAG (peer->cap, PEER_CAP_ORF_PREFIX_RM_RCV); } @@ -235,22 +235,22 @@ bgp_peer_open_state_receive(bgp_peer peer) for (safi = qSAFI_MIN ; safi <= qSAFI_MAX ; ++safi) { qafx_bit_t qb = qafx_bit(qafx_num_from_qAFI_qSAFI(afi, safi)); - if (peer->afc[afi][safi] && (qb & open_rcvd->can_g_restart)) + if (peer->afc[afi][safi] && (qb & open_recv->can_g_restart)) { SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_RCV); - if (qb & open_rcvd->can_nfs) + if (qb & open_recv->can_nfs) SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_PRESERVE_RCV); } } - peer->v_gr_restart = open_rcvd->restart_time; + peer->v_gr_restart = open_recv->restart_time; /* TODO: should we do anything with this? */ #if 0 int restarting ; /* Restart State flag */ #endif /* Override capability. */ - if (!open_rcvd->can_capability || CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) + if (!open_recv->can_capability || CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) { peer->afc_nego[AFI_IP][SAFI_UNICAST] = peer->afc[AFI_IP][SAFI_UNICAST]; peer->afc_nego[AFI_IP][SAFI_MULTICAST] = peer->afc[AFI_IP][SAFI_MULTICAST]; |