diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | bgpd/Makefile.am | 7 | ||||
-rw-r--r-- | bgpd/bgp.h | 62 | ||||
-rw-r--r-- | bgpd/bgp_common.h | 18 | ||||
-rw-r--r-- | bgpd/bgp_connection.c | 101 | ||||
-rw-r--r-- | bgpd/bgp_connection.h | 56 | ||||
-rw-r--r-- | bgpd/bgp_fsm.c | 79 | ||||
-rw-r--r-- | bgpd/bgp_msg_write.c | 2902 | ||||
-rw-r--r-- | bgpd/bgp_msg_write.h | 112 | ||||
-rw-r--r-- | bgpd/bgp_notification.c | 20 | ||||
-rw-r--r-- | bgpd/bgp_notification.h | 109 | ||||
-rw-r--r-- | bgpd/bgp_open_state.c | 72 | ||||
-rw-r--r-- | bgpd/bgp_open_state.h | 37 | ||||
-rw-r--r-- | bgpd/bgp_packet.h | 16 | ||||
-rw-r--r-- | bgpd/bgp_route_refresh.c | 186 | ||||
-rw-r--r-- | bgpd/bgp_route_refresh.h | 135 | ||||
-rw-r--r-- | bgpd/bgp_session.c | 4 | ||||
-rw-r--r-- | bgpd/bgp_session.h | 14 | ||||
-rw-r--r-- | lib/Makefile.am | 2 | ||||
-rw-r--r-- | lib/confirm.h | 28 | ||||
-rw-r--r-- | lib/memtypes.c | 2 | ||||
-rw-r--r-- | lib/stream.c | 14 | ||||
-rw-r--r-- | lib/stream.h | 1 | ||||
-rw-r--r-- | lib/zassert.h | 15 |
24 files changed, 1422 insertions, 2572 deletions
@@ -39,3 +39,5 @@ build .project .settings/ m4/*.m4 +commit.log + diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am index 3788e0f3..69f42c9a 100644 --- a/bgpd/Makefile.am +++ b/bgpd/Makefile.am @@ -17,7 +17,8 @@ libbgp_a_SOURCES = \ bgp_dump.c bgp_snmp.c bgp_ecommunity.c bgp_mplsvpn.c bgp_nexthop.c \ bgp_peer.c bgp_damp.c bgp_table.c bgp_advertise.c bgp_vty.c \ bgp_engine.c bgp_session.c bgp_connection.c \ - bgp_common.c bgp_notification.c bgp_peer_index.c bgp_msg_write.c + bgp_common.c bgp_notification.c bgp_peer_index.c bgp_msg_write.c \ + bgp_route_refresh.c noinst_HEADERS = \ bgp_aspath.h bgp_attr.h bgp_community.h bgp_debug.h bgp_fsm.h \ @@ -26,8 +27,8 @@ noinst_HEADERS = \ bgp_ecommunity.h bgp_mplsvpn.h bgp_nexthop.h bgp_damp.h bgp_table.h \ bgp_peer.h bgp_advertise.h bgp_snmp.h bgp_vty.h \ bgp_engine.h bgp_session.h bgp_connection.h \ - bgp_common.h bgp_notification.h bgp_peer_index.h bgp_msg_write.h - bgp.h + bgp_common.h bgp_notification.h bgp_peer_index.h bgp_msg_write.h \ + bgp_route_refresh.h bgp.h bgpd_SOURCES = bgp_main.c bgpd_LDADD = libbgp.a ../lib/libzebra.la @LIBCAP@ @LIBM@ @@ -28,19 +28,10 @@ * These are independent of Quagga. */ -/*============================================================================== - * BGP Finite State Machine (FSM) - */ - - -#endif /* _QUAGGA_BGP_H */ - #define _GMCH_BGP_H "19-Dec-2009" -#ifdef _GMCH_BGP_H - -#include "zassert.h" #include <stdint.h> +#include "confirm.h" /*############################################################################## * BGP RFC's we know about -- as of 16-Oct-2009 @@ -141,25 +132,30 @@ typedef uint32_t asn_t ; /* general ASN */ typedef uint16_t as2_t ; /* specifically 2 Octet ASN */ typedef uint32_t as4_t ; /* specifically 4 Octet ASN -- RFC4893 */ +typedef enum asn_type asn_type_t ; enum asn_type { AS2 = 2, AS4 = 4 } ; -typedef enum asn_type asn_type_t ; - /* Other stuff.... */ -typedef uint32_t bgp_id_t ; /* actually an IPv4 IP Address */ +typedef uint32_t bgp_id_t ; /* actually an IPv4 IP Address */ + +typedef bgp_id_t bgp_id_ht ; /* in host order */ +typedef bgp_id_t bgp_id_nt ; /* in network order */ -VALUE(BGP_MAX_NEXT_HOP_L = 32) ; /* maximum expected Next Hop address length */ +/* Size of BGP packets or thing in such */ +typedef uint16_t bgp_size_t; + +VALUE(BGP_NEXT_HOP_MAX_L = 32) ; /* maximum expected Next Hop address length */ /*============================================================================== * BGP Message Structure */ -VALUE(BGP_MAX_MSG_L = 4096) ; /* RFC4271 hard limit on message length */ +VALUE(BGP_MSG_MAX_L = 4096) ; /* RFC4271 hard limit on message length */ /* Message Header Format ----------------------------------------------------*/ @@ -174,7 +170,7 @@ VALUE(BGP_MH_HEAD_L = /* message header length */ + sizeof(BGP_MH_TYPE_T) ) ; CONFIRM(BGP_MH_HEAD_L == 19) ; /* well known value ! */ -#define BGP_MAX_BODY_L (BGP_MAX_MSG_L - BGP_MH_HEAD_L) +VALUE(BGP_MSG_BODY_MAX_L = BGP_MSG_MAX_L - BGP_MH_HEAD_L) ; enum /* order of entries in Message Header */ { @@ -197,6 +193,8 @@ enum BGP_MT BGP_MT_ROUTE_REFRESH = 5, /* RFC2918 */ BGP_MT_MAX = 5, /* max known message type */ + + BGP_MT_ROUTE_REFRESH_pre = 128 /* pre RFC2918 (Sep-2000) */ } ; /* Open Message (type = BGP_MT_OPEN) ------------------------------------------- @@ -292,6 +290,9 @@ enum BGP_CAN { BGP_CAN_ADD_PATH = 69, /* ADD-PATH [draft-idr] */ BGP_CAN_MAX = 69, /* but mind the gap(s) ! */ + + BGP_CAN_R_REFRESH_pre = 128, /* pre-RFC value */ + BGP_CAN_ORF_pre = 130, /* pre-RFC value */ } ; /* Update Message (type = BGP_MT_UPDATE) --------------------------------------- @@ -556,6 +557,12 @@ enum /* order */ BGP_RRM_ORFS, /* start of ORF collections */ } ; +enum /* values for the BGP_RRM_ORF_WHEN byte */ +{ + BGP_ORF_WTR_IMMEDIATE = 1, /* when-to-refresh == immediately */ + BGP_ORF_WTR_DEFER = 2 /* when-to-refresh == defer */ +} ; + /* ORFS come in collections...................................................*/ typedef U8 BGP_ORF_TYPE_T ; /* ORF Type */ @@ -577,6 +584,8 @@ enum /* order */ typedef U8 BGP_ORF_E_ACTION_T ; /* see below for action/match bits */ typedef UBX BGP_ORF_E_REST_T ; /* rest depends on ORF Type */ +VALUE(BGP_ORF_E_COM_L = sizeof(BGP_ORF_E_ACTION_T)) ; + /* Known BGP_ORF_TYPE values..................................................*/ enum BGP_ORF { @@ -584,19 +593,22 @@ enum BGP_ORF { BGP_ORF_T_PREFIX = 64, /* Address Prefix ORF RFC5292 */ - BGP_ORF_T_MAX = 64 + BGP_ORF_T_MAX = 64, + + BGP_ORF_T_PREFIX_pre = 128 /* pre RFC value */ } ; /* Known BGP_ORF_E_ACTION bits................................................*/ enum { - BGP_ORF_EA_MASK = 0xC0, /* mask to extract Action */ + BGP_ORF_EA_MASK = 0x3 << 6, /* mask to extract Action */ - BGP_ORF_EA_ADD = 0x00, /* Action: add ORF */ - BGP_ORF_EA_REMOVE = 0x40, /* Action: remove ORF */ - BGP_ORF_EA_REM_ALL = 0x80, /* Action: remove all ORF */ + BGP_ORF_EA_ADD = 0 << 6, /* Action: ADD */ + BGP_ORF_EA_REMOVE = 1 << 6, /* Action: REMOVE */ + BGP_ORF_EA_RM_ALL = 2 << 6, /* Action: REMOVE-ALL */ - BGP_ORF_EA_DENY = 0x20, /* deny -- otherwise permit */ + BGP_ORF_EA_PERMIT = 0 << 5, /* Match: PERMIT */ + BGP_ORF_EA_DENY = 1 << 5, /* Match: DENY */ } ; /* Address Prefix ORF (BGP_ORF_T_PREFIX) type specific entry part.............*/ @@ -607,6 +619,12 @@ typedef U8 BGP_ORF_E_P_MAX_T ; /* Maxlen */ typedef U8 BGP_ORF_E_P_LEN_T ; /* Prefix Length */ typedef UBX BGP_ORF_E_P_PFIX_T ; /* Prefix -- variable ! */ +VALUE(BGP_ORF_E_P_MIN_L = BGP_ORF_E_COM_L + + sizeof(BGP_ORF_E_P_SEQ_T) + + sizeof(BGP_ORF_E_P_MIN_T) + + sizeof(BGP_ORF_E_P_MAX_T) + + sizeof(BGP_ORF_E_P_LEN_T) ) ; + /*============================================================================== * Capability Values */ diff --git a/bgpd/bgp_common.h b/bgpd/bgp_common.h index a5f66318..65f2476c 100644 --- a/bgpd/bgp_common.h +++ b/bgpd/bgp_common.h @@ -23,14 +23,20 @@ #define _QUAGGA_BGP_COMMON_H #include <stdint.h> +#include "bgpd/bgp.h" #include "qafi_safi.h" -#include "zassert.h" +#include "confirm.h" #ifndef Inline #define Inline static inline #endif /*============================================================================== + * Local "bool" + */ +typedef uint8_t flag_t ; + +/*============================================================================== * Here are a number of "incomplete" declarations, which allow a number of * bgpd structures to refer to each other. */ @@ -115,16 +121,6 @@ enum bgp_session_events typedef uint32_t as_t ; typedef uint16_t as16_t ; /* we may still encounter 16 Bit asnums */ -/* BGP Identifier -- usually an IPv4 address ! */ -#ifndef _GMCH_BGP_H -typedef uint32_t bgp_id_t ; -#endif - -typedef bgp_id_t bgp_id_ht ; /* in host order */ -typedef bgp_id_t bgp_id_nt ; /* in network order */ - -/* Size of BGP packets or thing in such */ -typedef uint16_t bgp_size_t; /*============================================================================== * AFI/SAFI encodings for bgpd diff --git a/bgpd/bgp_connection.c b/bgpd/bgp_connection.c index 35e76871..edfc19bc 100644 --- a/bgpd/bgp_connection.c +++ b/bgpd/bgp_connection.c @@ -20,7 +20,6 @@ */ #include <zebra.h> -#include "bgpd/bgp.h" #include "bgpd/bgpd.h" @@ -125,6 +124,8 @@ bgp_connection_init_new(bgp_connection connection, bgp_session session, * * su_remote NULL -- no address, yet * * hold_timer_interval none -- set when connection is opened * * keepalive_timer_interval none -- set when connection is opened + * * as4 not AS4 conversation + * * route_refresh_pre not pre-RFC ROUTE-REFRESH * * read_pending nothing pending * * read_header not reading header * * notification_pending nothing pending @@ -160,8 +161,8 @@ bgp_connection_init_new(bgp_connection connection, bgp_session session, bgp_connection_init_host(connection, bgp_connection_tags[ordinal]) ; /* Need two empty "stream" buffers */ - connection->ibuf = stream_new(BGP_MAX_MSG_L) ; - connection->obuf = stream_new(BGP_MAX_MSG_L) ; + connection->ibuf = stream_new(BGP_MSG_MAX_L) ; + connection->obuf = stream_new(BGP_MSG_MAX_L) ; /* Ensure mqueue_local_queue is empty. */ mqueue_local_init_new(&connection->pending_queue) ; @@ -238,7 +239,10 @@ bgp_connection_make_primary(bgp_connection connection) bgp_connection_init_host(connection, "") ; session->hold_timer_interval = connection->hold_timer_interval ; - session->keepalive_timer_interval = session->keepalive_timer_interval ; + session->keepalive_timer_interval = connection->keepalive_timer_interval ; + + session->as4 = connection->as4 ; + session->route_refresh_pre = connection->route_refresh_pre ; session->su_local = connection->su_local ; connection->su_local = NULL ; @@ -284,24 +288,6 @@ bgp_connection_free(bgp_connection connection) } ; /*------------------------------------------------------------------------------ - * Full if not enough room for a maximum size BGP message. - */ -static inline int -bgp_write_buffer_full(bgp_wbuffer wb) -{ - return ((wb->limit - wb->p_in) < BGP_MAX_MSG_L) ; -} ; - -/*------------------------------------------------------------------------------ - * Empty if in and out pointers are equal (but may need to be reset !) - */ -static inline int -bgp_write_buffer_empty(bgp_wbuffer wb) -{ - return (wb->p_out == wb->p_in) ; -} ; - -/*------------------------------------------------------------------------------ * Allocate new write buffer and initialise pointers * * NB: assumes structure has been zeroised by the initialisation of the @@ -316,9 +302,6 @@ bgp_write_buffer_init_new(bgp_wbuffer wb, size_t size) wb->limit = wb->base + size ; wb->p_in = wb->p_out = wb->base ; - wb->full = bgp_write_buffer_full(wb) ; - - assert(!wb->full) ; } ; /*============================================================================== @@ -565,7 +548,6 @@ bgp_connection_close(bgp_connection connection) connection->wbuff.p_in = connection->wbuff.base ; connection->wbuff.p_out = connection->wbuff.base ; - connection->wbuff.full = 0 ; /* Empty out the pending queue and remove from connection queue */ mqueue_local_reset_keep(&connection->pending_queue) ; @@ -638,9 +620,6 @@ bgp_connection_part_close(bgp_connection connection) else wb->p_in = wb->p_out = wb->base ; - wb->full = bgp_write_buffer_full(wb) ; - assert(!wb->full) ; - /* Empty out the pending queue and remove from connection queue */ mqueue_local_reset_keep(&connection->pending_queue) ; bgp_connection_queue_del(connection) ; @@ -660,57 +639,72 @@ bgp_connection_part_close(bgp_connection connection) * Returns true <=> able to write the entire buffer without blocking. */ -static int bgp_connection_write_direct(bgp_connection connection) ; +static int bgp_connection_write_direct(bgp_connection connection, + struct stream* s) ; static void bgp_connection_write_action(qps_file qf, void* file_info) ; /*------------------------------------------------------------------------------ - * Write the contents of the obuf -- MUST not be here if wbuff is full ! + * Write the contents of the given stream, if possible * - * Returns: 1 => all written -- obuf and wbuff are empty - * 0 => written -- obuf now empty - * -1 => failed -- error event generated + * Writes everything or nothing. + * + * If the write buffer is empty, then will attempt to write directly to the + * socket, buffering anything that cannot be sent immediately. Any errors + * encountered in this process generate an FSM event. + * + * In case it is relevant, identifies when the data has been written all the + * way into the TCP buffer. + * + * Returns: 2 => written to TCP -- it's gone -- stream reset, empty + * 1 => written to wbuff -- waiting for socket -- stream reset, empty + * 0 => nothing written -- insufficient space in wbuff + * -1 => failed -- error event generated */ extern int -bgp_connection_write(bgp_connection connection) +bgp_connection_write(bgp_connection connection, struct stream* s) { bgp_wbuffer wb = &connection->wbuff ; if (bgp_write_buffer_empty(wb)) { /* write buffer is empty -- attempt to write directly */ - return bgp_connection_write_direct(connection) ; + return bgp_connection_write_direct(connection, s) ; } ; - /* Transfer the obuf contents to the staging buffer. */ - wb->p_in = stream_transfer(wb->p_in, connection->obuf, wb->limit) ; + /* Write nothing if cannot write everything */ + if (!bgp_write_buffer_can(wb, stream_pending(s))) + return 0 ; + + /* Transfer the obuf contents to the write buffer. */ + wb->p_in = stream_transfer(wb->p_in, s, wb->limit) ; - return 1 ; + return 1 ; /* written as far as the write buffer */ } ; /*------------------------------------------------------------------------------ - * The write buffer is empty -- so try to write obuf directly. + * The write buffer is empty -- so try to write stream directly. * - * If cannot empty the obuf directly to the TCP buffers, transfer it to to the - * write buffer, and enable the qpselect action. + * If cannot empty the stream directly to the TCP buffers, transfer it to to + * the write buffer, and enable the qpselect action. * (This is where the write buffer is allocated, if it hasn't yet been.) * - * Either way, the obuf is cleared and can be reused (unless failed). + * Either way, the stream is cleared and can be reused (unless failed). * - * Returns: 1 => written obuf to TCP buffer -- all buffers empty - * 0 => written obuf to wbuff -- obuf empty - * -1 => failed -- stopping, dead + * Returns: 2 => written to TCP -- it's gone -- stream reset, empty + * 1 => written to wbuff -- waiting for socket -- stream reset, empty + * -1 => failed -- error event generated */ -enum { bgp_wbuff_size = BGP_MAX_MSG_L * 10 } ; +enum { bgp_wbuff_size = BGP_MSG_MAX_L * 10 } ; static int -bgp_connection_write_direct(bgp_connection connection) +bgp_connection_write_direct(bgp_connection connection, struct stream* s) { int ret ; - ret = stream_flush_try(connection->obuf, qps_file_fd(&connection->qf)) ; + ret = stream_flush_try(s, qps_file_fd(&connection->qf)) ; if (ret == 0) - return 1 ; /* Done: wbuff and obuf are empty */ + return 2 ; /* Done: wbuff and stream are empty */ else if (ret > 0) { @@ -721,7 +715,7 @@ bgp_connection_write_direct(bgp_connection connection) bgp_write_buffer_init_new(wb, bgp_wbuff_size) ; /* Transfer *entire* message to staging buffer */ - wb->p_in = stream_transfer(wb->base, connection->obuf, wb->limit) ; + wb->p_in = stream_transfer(wb->base, s, wb->limit) ; wb->p_out = wb->p_in - ret ; /* output from here */ @@ -729,7 +723,7 @@ bgp_connection_write_direct(bgp_connection connection) qps_enable_mode(&connection->qf, qps_write_mnum, bgp_connection_write_action) ; - return 0 ; /* Done: obuf is empty, but wbuff is not */ + return 1 ; /* Done: wbuff is not empty -- stream is */ } ; /* write failed -- signal error and return failed */ @@ -782,7 +776,6 @@ bgp_connection_write_action(qps_file qf, void* file_info) /* Buffer is empty -- reset it and disable write mode */ wb->p_out = wb->p_in = wb->base ; - wb->full = 0 ; qps_disable_modes(&connection->qf, qps_write_mbit) ; @@ -824,7 +817,7 @@ bgp_connection_read_enable(bgp_connection connection) * Performs the checks on the BGP message header: * * * Marker is all '1's - * * Length is <= BGP_MAX_MSG_L + * * Length is <= BGP_MSG_MAX_L * * Type is OPEN/UPDATE/NOTIFICATION/KEEPALIVE * */ diff --git a/bgpd/bgp_connection.h b/bgpd/bgp_connection.h index b4882a2f..f10eebbd 100644 --- a/bgpd/bgp_connection.h +++ b/bgpd/bgp_connection.h @@ -28,6 +28,9 @@ #include "lib/qpselect.h" #include "lib/sockunion.h" +#include "lib/stream.h" + +//#include "bgpd/bgp.h" #include "bgpd/bgp_common.h" #include "bgpd/bgp_session.h" @@ -98,8 +101,6 @@ enum bgp_fsm_events typedef struct bgp_wbuffer* bgp_wbuffer ; struct bgp_wbuffer { - int full ; /* not enough room for max length BGP message */ - uint8_t* p_out ; uint8_t* p_in ; @@ -148,6 +149,9 @@ struct bgp_connection unsigned hold_timer_interval ; /* subject to negotiation */ unsigned keepalive_timer_interval ; /* subject to negotiation */ + flag_t as4 ; /* subject to negotiation */ + flag_t route_refresh_pre ; /* subject to negotiation */ + struct qtimer hold_timer ; struct qtimer keepalive_timer ; @@ -200,7 +204,7 @@ extern void bgp_connection_read_enable(bgp_connection connection) ; extern int -bgp_connection_write(bgp_connection connection) ; +bgp_connection_write(bgp_connection connection, struct stream* s) ; extern void bgp_connection_queue_add(bgp_connection connection) ; @@ -211,6 +215,52 @@ bgp_connection_queue_del(bgp_connection connection) ; extern void bgp_connection_queue_process(void) ; + +/*------------------------------------------------------------------------------ + * Full if not enough room for a maximum size BGP message. + */ +Inline int +bgp_write_buffer_can(bgp_wbuffer wb, size_t want) +{ + return ((size_t)(wb->limit - wb->p_in) <= want) ; +} ; + +/*------------------------------------------------------------------------------ + * Full if not enough room for a maximum size BGP message + 1 + */ +Inline int +bgp_write_buffer_full(bgp_wbuffer wb) +{ + return bgp_write_buffer_can(wb, BGP_MAX_MSG_L + 1) ; +} ; + +/*------------------------------------------------------------------------------ + * Empty if in and out pointers are equal (but may need to be reset !) + */ +Inline int +bgp_write_buffer_empty(bgp_wbuffer wb) +{ + return (wb->p_out == wb->p_in) ; +} ; + +/*------------------------------------------------------------------------------ + * As above, for connection + */ +Inline int +bgp_connection_write_full(bgp_connection connection) +{ + return bgp_write_buffer_full(&connection->wbuff) ; +} ; + +/*------------------------------------------------------------------------------ + * As above, for connection + */ +Inline int +bgp_connection_write_empty(bgp_connection connection) +{ + return bgp_write_buffer_empty(&connection->wbuff) ; +} ; + /*============================================================================== * Access functions via bgp_connection for bgp_session attributes. * diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index a5e3c58e..ccc16a7c 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -23,27 +23,22 @@ */ #include <zebra.h> -#include "bgpd/bgp.h" +//#include "bgpd/bgp.h" #include "log.h" -#include "bgpd/bgp_engine.h" #include "bgpd/bgp_session.h" #include "bgpd/bgp_connection.h" #include "bgpd/bgp_notification.h" #include "bgpd/bgp_fsm.h" +#include "bgpd/bgp_msg_write.h" #include "lib/qtimers.h" #include "lib/sockunion.h" #include "bgpd/bgp_debug.h" -#include "bgpd/bgp_packet.h" #include "bgpd/bgp_network.h" #include "bgpd/bgp_dump.h" -#include "bgpd/bgp_open.h" -#ifdef HAVE_SNMP -#include "bgpd/bgp_snmp.h" -#endif /* HAVE_SNMP */ /*============================================================================== * The BGP Finite State Machine @@ -1634,7 +1629,8 @@ static void bgp_hold_timer_recharge(bgp_connection connection) ; static bgp_fsm_state_t -bgp_fsm_send_notification(bgp_connection connection, bgp_notify notification) ; +bgp_fsm_send_notification(bgp_connection connection, + bgp_fsm_state_t next_state) ; /*------------------------------------------------------------------------------ * Null action -- do nothing at all. @@ -2124,8 +2120,7 @@ bgp_fsm_catch(bgp_connection connection, bgp_fsm_state_t next_state) if ( (connection->notification != NULL) && (connection->except != bgp_session_eNOM_recv) ) { - next_state = bgp_fsm_send_notification(connection, - connection->notification) ; + next_state = bgp_fsm_send_notification(connection, next_state) ; } else bgp_connection_close(connection) ; @@ -2147,6 +2142,70 @@ bgp_fsm_catch(bgp_connection connection, bgp_fsm_state_t next_state) return next_state ; } ; +/*------------------------------------------------------------------------------ + * Dispatch notification message + * + * Part closing the connection guarantees that can get the notification + * message into the buffers. + * + * Process will generate the following events: + * + * -- I/O failure of any sort + * -- Sent_NOTIFICATION_message + * -- HoldTimer expired + * + * When get Sent_NOTIFICATION_message, will set final "courtesy" timer, so + * unless I/O fails, final end of process is HoldTimer expired (with + * + */ +static bgp_fsm_state_t +bgp_fsm_send_notification(bgp_connection connection, bgp_fsm_state_t next_state) +{ + int ret ; + + /* If the next_state is not Stopping, then the sending of the notification + * holds the FSM in the current state. Will move forward when the + * HoldTimer expires -- either because lost patience in getting the + * notification away, or at the end of the "courtesy" time. + */ + if (next_state != bgp_fsm_Stopping) + next_state = connection->state ; + + /* Close for reading and flush write buffers. */ + bgp_connection_part_close(connection) ; + + /* Write the message + * + * If the write fails it raises a suitable event, which will now be + * sitting waiting to be processed on the way out of the FSM. + */ + ret = bgp_msg_write_notification(connection, connection->notification) ; + + connection->notification_pending = (ret >= 0) ; + /* is pending if not failed */ + if (ret > 0) + /* notification reached the TCP buffers instantly + * + * Send ourselves the good news ! + */ + bgp_fsm_event(connection, bgp_fsm_Sent_NOTIFICATION_message) ; + + else if (ret == 0) + /* notification is sitting in the write buffer + * + * Set notification_pending so that write action will raise the required + * event in due course. + * + * Set the HoldTimer to something suitable. Don't really expect this + * to happen in anything except Established state -- but copes. (Is + * ready to wait 20 seconds in Stopping state and 5 otherwise.) + */ + bgp_hold_timer_set(connection, (next_state == bgp_fsm_Stopping) ? 20 : 5) ; + + /* Return suitable state. */ + return next_state ; +} ; + /*============================================================================== * The BGP connections timers handling. * diff --git a/bgpd/bgp_msg_write.c b/bgpd/bgp_msg_write.c index d15f1a26..fbc7ba0e 100644 --- a/bgpd/bgp_msg_write.c +++ b/bgpd/bgp_msg_write.c @@ -21,6 +21,11 @@ * Boston, MA 02111-1307, USA. */ +#include "bgpd/bgp_common.h" +#include "bgpd/bgp_msg_write.h" +#include "bgpd/bgp_route_refresh.h" + + #include <zebra.h> #include "thread.h" @@ -54,2423 +59,794 @@ #include "bgpd/bgp_advertise.h" #include "bgpd/bgp_vty.h" -int stream_put_prefix (struct stream *, struct prefix *); - -/* Set up BGP packet marker and packet type. */ -static int -bgp_packet_set_marker (struct stream *s, u_char type) -{ - int i; - - /* Fill in marker. */ - for (i = 0; i < BGP_MARKER_SIZE; i++) - stream_putc (s, 0xff); - - /* Dummy total length. This field is should be filled in later on. */ - stream_putw (s, 0); - - /* BGP packet type. */ - stream_putc (s, type); - - /* Return current stream size. */ - return stream_get_endp (s); -} - -/* Set BGP packet header size entry. If size is zero then use current - stream size. */ -static int -bgp_packet_set_size (struct stream *s) -{ - int cp; - - /* Preserve current pointer. */ - cp = stream_get_endp (s); - stream_putw_at (s, BGP_MARKER_SIZE, cp); +/*============================================================================== + * BGP Engine BGP Message encoding and sending. + * + * + */ - return cp; -} +/*============================================================================== + * NOTIFICATION and KEEPALIVE + */ -/* Add new packet to the peer. */ -static void -bgp_packet_add (struct peer *peer, struct stream *s) +/*------------------------------------------------------------------------------ + * Make NOTIFICATION message and dispatch. + * + * NB: the write buffers will have been flushed -- so expect success ! + * + * Returns: 2 => written to TCP -- it's gone + * 1 => written to wbuff -- waiting for socket + * 0 => nothing written -- wbuff was not empty ! + * -1 => failed -- error event generated + */ +extern int +bgp_msg_write_notification(bgp_connection connection, bgp_notify notification) { - /* Add packet to the end of list. */ - stream_fifo_push (peer->obuf, s); -} + struct stream *s = connection->obuf ; + int length; -/* Free first packet. */ -static void -bgp_packet_delete (struct peer *peer) -{ - stream_free (stream_fifo_pop (peer->obuf)); -} + assert(notification != NULL) ; -/* Check file descriptor whether connect is established. */ -static void -bgp_connect_check (struct peer *peer) -{ - int status; - socklen_t slen; - int ret; + /* Make NOTIFY message header */ + bgp_packet_set_marker (s, BGP_MSG_NOTIFY); - /* Anyway I have to reset read and write thread. */ - BGP_READ_OFF (peer->t_read); - BGP_WRITE_OFF (peer->t_write); + /* Set notify code and subcode */ + stream_putc(s, bgp_notify_get_code(notification)) ; + stream_putc(s, bgp_notify_get_subcode(notification)) ; - /* Check file descriptor. */ - slen = sizeof (status); - ret = getsockopt(peer->fd, SOL_SOCKET, SO_ERROR, (void *) &status, &slen); + /* Copy the data portion, if any. */ + length = bgp_notify_get_length(notification) ; + if (length != 0) + stream_write(s, bgp_notify_get_data(notification), length) ; - /* If getsockopt is fail, this is fatal error. */ - if (ret < 0) - { - zlog (peer->log, LOG_INFO, "can't get sockopt for nonblocking connect"); - BGP_EVENT_ADD (peer, TCP_fatal_error); - return; - } + /* Set and get BGP packet length. */ + length = bgp_packet_set_size(s); - /* When status is 0 then TCP connection is established. */ - if (status == 0) - { - BGP_EVENT_ADD (peer, TCP_connection_open); - } - else - { - if (BGP_DEBUG (events, EVENTS)) - plog_debug (peer->log, "%s [Event] Connect failed (%s)", - peer->host, safe_strerror (errno)); - BGP_EVENT_ADD (peer, TCP_connection_open_failed); - } -} + /* Logging */ + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug("%s send message type %d, length (incl. header) %d", + connection->host, BGP_MSG_NOTIFY, length) ; -/* Make BGP update packet. */ -static struct stream * -bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi) + /* For debug */ + { + bgp_notify text_form ; + const char* form ; + char c[4] ; + char* p ; + + length = bgp_notify_get_length(notification) ; + p = bgp_notify_get_data(notification) ; + + /* Make new copy of notification, with data portion large enough + * for the data rendered as hex characters. + */ + text_form = bgp_notify_new(bgp_notify_get_code(notification), + bgp_notify_get_subcode(notification), + (length * 3)) ; + form = "%02x" ; + while (length--) + { + sprintf (c, form, *p++) ; + text_form = bgp_notify_append_data(text_form, c, strlen(c)) ; + form = " %02x" ; + } ; + text_form = bgp_notify_append_data(text_form, "\0", 1) ; + + /* TODO: restore bgp_notify_print */ +#if 0 + bgp_notify_print(peer, text_form, "sending") ; +#endif + bgp_notify_free(&text_form) ; + } ; + + /* Finally -- write the obuf away */ + return bgp_connection_write(connection, s) ; +} ; + +/*------------------------------------------------------------------------------ + * Make KEEPALIVE message and dispatch. + * + * NB: does nothing if the write buffer is not empty. This is not a problem, + * the KEEPALIVE is redundant if there is stuff waiting to go ! + * + * KEEPALIVE is sent in response to OPEN, and that MUST be sent. But if the + * buffers are full at that point, something is broken ! + * + * Returns: 2 => written to TCP -- it's gone + * 1 => written to wbuff -- waiting for socket + * 0 => nothing written -- wbuff was not empty ! + * -1 => failed -- error event generated + */ +extern int +bgp_msg_send_keepalive(bgp_connection connection) { - struct stream *s; - struct bgp_adj_out *adj; - struct bgp_advertise *adv; - struct stream *packet; - struct bgp_node *rn = NULL; - struct bgp_info *binfo = NULL; - bgp_size_t total_attr_len = 0; - unsigned long pos; - char buf[BUFSIZ]; - - s = peer->work; - stream_reset (s); - - adv = FIFO_HEAD (&peer->sync[afi][safi]->update); - - while (adv) - { - assert (adv->rn); - rn = adv->rn; - adj = adv->adj; - if (adv->binfo) - binfo = adv->binfo; - - /* When remaining space can't include NLRI and it's length. */ - if (STREAM_REMAIN (s) <= BGP_NLRI_LENGTH + PSIZE (rn->p.prefixlen)) - break; - - /* If packet is empty, set attribute. */ - if (stream_empty (s)) - { - struct prefix_rd *prd = NULL; - u_char *tag = NULL; - struct peer *from = NULL; - - if (rn->prn) - prd = (struct prefix_rd *) &rn->prn->p; - if (binfo && binfo->extra) - { - tag = binfo->extra->tag; - from = binfo->peer; - } - - bgp_packet_set_marker (s, BGP_MSG_UPDATE); - stream_putw (s, 0); - pos = stream_get_endp (s); - stream_putw (s, 0); - total_attr_len = bgp_packet_attribute (NULL, peer, s, - adv->baa->attr, - &rn->p, afi, safi, - from, prd, tag); - stream_putw_at (s, pos, total_attr_len); - } - - if (afi == AFI_IP && safi == SAFI_UNICAST) - stream_put_prefix (s, &rn->p); - - if (BGP_DEBUG (update, UPDATE_OUT)) - zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d", - peer->host, - inet_ntop (rn->p.family, &(rn->p.u.prefix), buf, BUFSIZ), - rn->p.prefixlen); - - /* Synchnorize attribute. */ - if (adj->attr) - bgp_attr_unintern (adj->attr); - else - peer->scount[afi][safi]++; - - adj->attr = bgp_attr_intern (adv->baa->attr); - - adv = bgp_advertise_clean (peer, adj, afi, safi); - - if (! (afi == AFI_IP && safi == SAFI_UNICAST)) - break; - } + struct stream *s = connection->obuf ; + int length; - if (! stream_empty (s)) - { - bgp_packet_set_size (s); - packet = stream_dup (s); - bgp_packet_add (peer, packet); - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); - stream_reset (s); - return packet; - } - return NULL; -} + if (!bgp_connection_write_empty(connection)) + return 0 ; -static struct stream * -bgp_update_packet_eor (struct peer *peer, afi_t afi, safi_t safi) -{ - struct stream *s; - struct stream *packet; + /* Make KEEPALIVE message -- comprises header only */ + bgp_packet_set_marker(s, BGP_MSG_KEEPALIVE); + length = bgp_packet_set_size(s); - if (DISABLE_BGP_ANNOUNCE) - return NULL; + /* Dump packet if debug option is set. */ + /* bgp_packet_dump (s); */ + if (BGP_DEBUG (keepalive, KEEPALIVE)) + zlog_debug ("%s sending KEEPALIVE", connection->host); if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("send End-of-RIB for %s to %s", afi_safi_print (afi, safi), peer->host); - - s = stream_new (BGP_MAX_PACKET_SIZE); + zlog_debug ("%s send message type %d, length (incl. header) %d", + connection->host, BGP_MSG_KEEPALIVE, length); - /* Make BGP update packet. */ - bgp_packet_set_marker (s, BGP_MSG_UPDATE); + /* Finally -- write the obuf away */ + return bgp_connection_write(connection, s) ; +} ; - /* Unfeasible Routes Length */ - stream_putw (s, 0); +/*============================================================================== + * OPEN message -- transform bgp_open_state into BGP message + */ - if (afi == AFI_IP && safi == SAFI_UNICAST) - { - /* Total Path Attribute Length */ - stream_putw (s, 0); - } - else - { - /* Total Path Attribute Length */ - stream_putw (s, 6); - stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); - stream_putc (s, BGP_ATTR_MP_UNREACH_NLRI); - stream_putc (s, 3); - stream_putw (s, afi); - stream_putc (s, safi); - } +static void +bgp_open_options(struct stream *s, bgp_open_state open_state) ; - bgp_packet_set_size (s); - packet = stream_dup (s); - bgp_packet_add (peer, packet); - stream_free (s); - return packet; -} +static void +bgp_open_capability_orf (struct stream *s, iAFI_t afi, iSAFI_t safi, + u_char cap_code, u_char orf_type, u_char mode) ; -/* Make BGP withdraw packet. */ -static struct stream * -bgp_withdraw_packet (struct peer *peer, afi_t afi, safi_t safi) +/*------------------------------------------------------------------------------ + * Make OPEN message and dispatch. + * + * OPEN is the first message to be sent. If the buffers are not empty, + * something is badly wrong ! + * + * Returns: 2 => written to TCP -- it's gone + * 1 => written to wbuff -- waiting for socket + * 0 => nothing written -- wbuff was too full !!! + * -1 => failed -- error event generated + */ +extern int +bgp_msg_send_open(bgp_connection connection, bgp_open_state open_state) { - struct stream *s; - struct stream *packet; - struct bgp_adj_out *adj; - struct bgp_advertise *adv; - struct bgp_node *rn; - unsigned long pos; - bgp_size_t unfeasible_len; - bgp_size_t total_attr_len; - char buf[BUFSIZ]; - - s = peer->work; - stream_reset (s); - - while ((adv = FIFO_HEAD (&peer->sync[afi][safi]->withdraw)) != NULL) - { - assert (adv->rn); - adj = adv->adj; - rn = adv->rn; - - if (STREAM_REMAIN (s) - < (BGP_NLRI_LENGTH + BGP_TOTAL_ATTR_LEN + PSIZE (rn->p.prefixlen))) - break; - - if (stream_empty (s)) - { - bgp_packet_set_marker (s, BGP_MSG_UPDATE); - stream_putw (s, 0); - } - - if (afi == AFI_IP && safi == SAFI_UNICAST) - stream_put_prefix (s, &rn->p); - else - { - struct prefix_rd *prd = NULL; - - if (rn->prn) - prd = (struct prefix_rd *) &rn->prn->p; - pos = stream_get_endp (s); - stream_putw (s, 0); - total_attr_len - = bgp_packet_withdraw (peer, s, &rn->p, afi, safi, prd, NULL); + struct stream *s = connection->obuf ; + int length ; - /* Set total path attribute length. */ - stream_putw_at (s, pos, total_attr_len); - } + assert(bgp_connection_write_empty(connection)) ; - if (BGP_DEBUG (update, UPDATE_OUT)) - zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d -- unreachable", - peer->host, - inet_ntop (rn->p.family, &(rn->p.u.prefix), buf, BUFSIZ), - rn->p.prefixlen); - - peer->scount[afi][safi]--; - - bgp_adj_out_remove (rn, adj, peer, afi, safi); - bgp_unlock_node (rn); - - if (! (afi == AFI_IP && safi == SAFI_UNICAST)) - break; - } - - if (! stream_empty (s)) - { - if (afi == AFI_IP && safi == SAFI_UNICAST) - { - unfeasible_len - = stream_get_endp (s) - BGP_HEADER_SIZE - BGP_UNFEASIBLE_LEN; - stream_putw_at (s, BGP_HEADER_SIZE, unfeasible_len); - stream_putw (s, 0); - } - bgp_packet_set_size (s); - packet = stream_dup (s); - bgp_packet_add (peer, packet); - stream_reset (s); - return packet; - } + /* Make OPEN message header */ + bgp_packet_set_marker(s, BGP_MSG_OPEN) ; - return NULL; -} + /* Set OPEN message fixed part */ + stream_putc(s, BGP_VERSION_4) ; + stream_putw(s, (open_state->my_as <= BGP_AS_MAX) + ? (u_int16_t) open_state->my_as : BGP_AS_TRANS) ; + stream_putw(s, open_state->holdtime) ; + stream_putl(s, open_state->bgp_id) ; -void -bgp_default_update_send (struct peer *peer, struct attr *attr, - afi_t afi, safi_t safi, struct peer *from) -{ - struct stream *s; - struct stream *packet; - struct prefix p; - unsigned long pos; - bgp_size_t total_attr_len; - char attrstr[BUFSIZ]; - char buf[BUFSIZ]; - - if (DISABLE_BGP_ANNOUNCE) - return; + /* Set OPEN message options */ + bgp_open_options(s, open_state) ; - if (afi == AFI_IP) - str2prefix ("0.0.0.0/0", &p); -#ifdef HAVE_IPV6 - else - str2prefix ("::/0", &p); -#endif /* HAVE_IPV6 */ + /* Set BGP message length. */ + length = bgp_packet_set_size(s) ; - /* Logging the attribute. */ - if (BGP_DEBUG (update, UPDATE_OUT)) + if (BGP_DEBUG (normal, NORMAL)) { - bgp_dump_attr (peer, attr, attrstr, BUFSIZ); - zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d %s", - peer->host, inet_ntop(p.family, &(p.u.prefix), buf, BUFSIZ), - p.prefixlen, attrstr); - } - - s = stream_new (BGP_MAX_PACKET_SIZE); - - /* Make BGP update packet. */ - bgp_packet_set_marker (s, BGP_MSG_UPDATE); + struct in_addr bgp_id ; + char buf[INET_ADDRSTRLEN] ; - /* Unfeasible Routes Length. */ - stream_putw (s, 0); + bgp_id.s_addr = htonl(open_state->bgp_id) ; + inet_ntop(AF_INET, &bgp_id.s_addr, buf, INET_ADDRSTRLEN) ; - /* Make place for total attribute length. */ - pos = stream_get_endp (s); - stream_putw (s, 0); - total_attr_len = bgp_packet_attribute (NULL, peer, s, attr, &p, afi, safi, from, NULL, NULL); + zlog_debug ("%s sending OPEN, version %d, my as %u, holdtime %d, id %s", + connection->host, BGP_VERSION_4, open_state->my_as, + open_state->holdtime, buf) ; - /* Set Total Path Attribute Length. */ - stream_putw_at (s, pos, total_attr_len); + } ; - /* NLRI set. */ - if (p.family == AF_INET && safi == SAFI_UNICAST) - stream_put_prefix (s, &p); - - /* Set size. */ - bgp_packet_set_size (s); - - packet = stream_dup (s); - stream_free (s); + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s send message type %d, length (incl. header) %d", + connection->host, BGP_MSG_OPEN, length); /* Dump packet if debug option is set. */ -#ifdef DEBUG - /* bgp_packet_dump (packet); */ -#endif /* DEBUG */ - - /* Add packet to the peer. */ - bgp_packet_add (peer, packet); + /* bgp_packet_dump (s); */ - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); + /* Finally -- write the obuf away */ + return bgp_connection_write(connection, s) ; } -void -bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi) +enum { - struct stream *s; - struct stream *packet; - struct prefix p; - unsigned long pos; - unsigned long cp; - bgp_size_t unfeasible_len; - bgp_size_t total_attr_len; - char buf[BUFSIZ]; - - if (DISABLE_BGP_ANNOUNCE) - return; - - if (afi == AFI_IP) - str2prefix ("0.0.0.0/0", &p); + have_ipv6 = #ifdef HAVE_IPV6 - else - str2prefix ("::/0", &p); -#endif /* HAVE_IPV6 */ - - total_attr_len = 0; - pos = 0; - - if (BGP_DEBUG (update, UPDATE_OUT)) - zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d -- unreachable", - peer->host, inet_ntop(p.family, &(p.u.prefix), buf, BUFSIZ), - p.prefixlen); - - s = stream_new (BGP_MAX_PACKET_SIZE); - - /* Make BGP update packet. */ - bgp_packet_set_marker (s, BGP_MSG_UPDATE); + 1 +#else + 0 +#endif +} ; + +/*------------------------------------------------------------------------------ + * Add options to given encoded OPEN message. + * + * Supports the status quo: only Capability Options. + * + * Creates an empty options part of there are no capabilities to set. + */ +static void +bgp_open_options(struct stream *s, bgp_open_state open_state) +{ + u_char len ; + unsigned long cp ; + qafx_num_t qafx ; - /* Unfeasible Routes Length. */; + /* Remember current pointer for Opt Parm Len. */ cp = stream_get_endp (s); - stream_putw (s, 0); - /* Withdrawn Routes. */ - if (p.family == AF_INET && safi == SAFI_UNICAST) - { - stream_put_prefix (s, &p); + /* Opt Parm Len. */ + stream_putc(s, 0); - unfeasible_len = stream_get_endp (s) - cp - 2; + /* If do not send capability, quit now -- zero options. */ + if (!open_state->can_capability) + return; - /* Set unfeasible len. */ - stream_putw_at (s, cp, unfeasible_len); + /* TODO: RFC 5492 (2009): SHOULD send only one Capability Option !! */ + /* RFC 3392 (2002): silent on the matter */ + /* RFC 2842 (2000): silent on the matter */ - /* Set total path attribute length. */ - stream_putw (s, 0); - } - else + /* Send capability for every AFI/SAFI supported. */ + for (qafx = qafx_num_first ; qafx <= qafx_num_last ; ++qafx) { - pos = stream_get_endp (s); - stream_putw (s, 0); - total_attr_len = bgp_packet_withdraw (peer, s, &p, afi, safi, NULL, NULL); - - /* Set total path attribute length. */ - stream_putw_at (s, pos, total_attr_len); - } - - bgp_packet_set_size (s); - - packet = stream_dup (s); - stream_free (s); - - /* Add packet to the peer. */ - bgp_packet_add (peer, packet); - - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); -} - -/* Get next packet to be written. */ -static struct stream * -bgp_write_packet (struct peer *peer) -{ - afi_t afi; - safi_t safi; - struct stream *s = NULL; - struct bgp_advertise *adv; - - s = stream_fifo_head (peer->obuf); - if (s) - return s; - - for (afi = AFI_IP; afi < AFI_MAX; afi++) - for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) - { - adv = FIFO_HEAD (&peer->sync[afi][safi]->withdraw); - if (adv) - { - s = bgp_withdraw_packet (peer, afi, safi); - if (s) - return s; - } - } - - for (afi = AFI_IP; afi < AFI_MAX; afi++) - for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) - { - adv = FIFO_HEAD (&peer->sync[afi][safi]->update); - if (adv) - { - if (adv->binfo && adv->binfo->uptime < peer->synctime) - { - if (CHECK_FLAG (adv->binfo->peer->cap, PEER_CAP_RESTART_RCV) - && CHECK_FLAG (adv->binfo->peer->cap, PEER_CAP_RESTART_ADV) - && ! CHECK_FLAG (adv->binfo->flags, BGP_INFO_STALE) - && safi != SAFI_MPLS_VPN) - { - if (CHECK_FLAG (adv->binfo->peer->af_sflags[afi][safi], - PEER_STATUS_EOR_RECEIVED)) - s = bgp_update_packet (peer, afi, safi); - } - else - s = bgp_update_packet (peer, afi, safi); - } - - if (s) - return s; - } - - if (CHECK_FLAG (peer->cap, PEER_CAP_RESTART_RCV)) - { - if (peer->afc_nego[afi][safi] && peer->synctime - && ! CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_EOR_SEND) - && safi != SAFI_MPLS_VPN) - { - SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_EOR_SEND); - return bgp_update_packet_eor (peer, afi, safi); - } - } - } - - return NULL; -} - -/* Is there partially written packet or updates we can send right - now. */ -static int -bgp_write_proceed (struct peer *peer) -{ - afi_t afi; - safi_t safi; - struct bgp_advertise *adv; - - if (stream_fifo_head (peer->obuf)) - return 1; + if (open_state->can_mp_ext & qafx_bit(qafx)) + { + iAFI_t afi = get_iAFI(qafx) ; + iSAFI_t safi = get_iSAFI(qafx) ; - for (afi = AFI_IP; afi < AFI_MAX; afi++) - for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) - if (FIFO_HEAD (&peer->sync[afi][safi]->withdraw)) - return 1; + if (!have_ipv6 && (afi == iAFI_IP6)) + continue ; - for (afi = AFI_IP; afi < AFI_MAX; afi++) - for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) - if ((adv = FIFO_HEAD (&peer->sync[afi][safi]->update)) != NULL) - if (adv->binfo->uptime < peer->synctime) - return 1; + stream_putc (s, BGP_OPEN_OPT_CAP); + stream_putc (s, CAPABILITY_CODE_MP_LEN + 2); + stream_putc (s, CAPABILITY_CODE_MP); + stream_putc (s, CAPABILITY_CODE_MP_LEN); + stream_putw (s, afi); + stream_putc (s, 0); + stream_putc (s, safi); + } ; + } ; - return 0; -} + /* Route refresh. */ -/* Write packet to the peer. */ -int -bgp_write (struct thread *thread) -{ - struct peer *peer; - u_char type; - struct stream *s; - int num; - unsigned int count = 0; - int write_errno; - - /* Yes first of all get peer pointer. */ - peer = THREAD_ARG (thread); - peer->t_write = NULL; - - /* For non-blocking IO check. */ - if (peer->status == Connect) + if (open_state->can_r_refresh & bgp_cap_form_old) { - bgp_connect_check (peer); - return 0; - } + stream_putc (s, BGP_OPEN_OPT_CAP) ; + stream_putc (s, CAPABILITY_CODE_REFRESH_LEN + 2) ; + stream_putc (s, CAPABILITY_CODE_REFRESH_OLD) ; + stream_putc (s, CAPABILITY_CODE_REFRESH_LEN) ; + } ; - /* Nonblocking write until TCP output buffer is full. */ - while (1) + if (open_state->can_r_refresh & bgp_cap_form_old) { - int writenum; - int val; + stream_putc (s, BGP_OPEN_OPT_CAP) ; + stream_putc (s, CAPABILITY_CODE_REFRESH_LEN + 2) ; + stream_putc (s, CAPABILITY_CODE_REFRESH) ; + stream_putc (s, CAPABILITY_CODE_REFRESH_LEN) ; + } ; - s = bgp_write_packet (peer); - if (! s) - return 0; + /* AS4 */ - /* XXX: FIXME, the socket should be NONBLOCK from the start - * status shouldnt need to be toggled on each write - */ - val = fcntl (peer->fd, F_GETFL, 0); - fcntl (peer->fd, F_SETFL, val|O_NONBLOCK); - - /* Number of bytes to be sent. */ - writenum = stream_get_endp (s) - stream_get_getp (s); - - /* Call write() system call. */ - num = write (peer->fd, STREAM_PNT (s), writenum); - write_errno = errno; - fcntl (peer->fd, F_SETFL, val); - if (num <= 0) - { - /* Partial write. */ - if (write_errno == EWOULDBLOCK || write_errno == EAGAIN) - break; - - BGP_EVENT_ADD (peer, TCP_fatal_error); - return 0; - } - if (num != writenum) - { - stream_forward_getp (s, num); - - if (write_errno == EAGAIN) - break; - - continue; - } - - /* Retrieve BGP packet type. */ - stream_set_getp (s, BGP_MARKER_SIZE + 2); - type = stream_getc (s); - - switch (type) - { - case BGP_MSG_OPEN: - peer->open_out++; - break; - case BGP_MSG_UPDATE: - peer->update_out++; - break; - case BGP_MSG_NOTIFY: - peer->notify_out++; - /* Double start timer. */ - peer->v_start *= 2; - - /* Overflow check. */ - if (peer->v_start >= (60 * 2)) - peer->v_start = (60 * 2); - - /* Flush any existing events */ - BGP_EVENT_ADD (peer, BGP_Stop); - return 0; - case BGP_MSG_KEEPALIVE: - peer->keepalive_out++; - break; - case BGP_MSG_ROUTE_REFRESH_NEW: - case BGP_MSG_ROUTE_REFRESH_OLD: - peer->refresh_out++; - break; - case BGP_MSG_CAPABILITY: - peer->dynamic_cap_out++; - break; - } - - /* OK we send packet so delete it. */ - bgp_packet_delete (peer); - - if (++count >= BGP_WRITE_PACKET_MAX) - break; - } - - if (bgp_write_proceed (peer)) - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); - - return 0; -} - -/* This is only for sending NOTIFICATION message to neighbor. */ -static int -bgp_write_notify (struct peer *peer) -{ - int ret; - u_char type; - struct stream *s; - - /* There should be at least one packet. */ - s = stream_fifo_head (peer->obuf); - if (!s) - return 0; - assert (stream_get_endp (s) >= BGP_HEADER_SIZE); - - /* I'm not sure fd is writable. */ - ret = writen (peer->fd, STREAM_DATA (s), stream_get_endp (s)); - if (ret <= 0) + if (open_state->can_as4) { - BGP_EVENT_ADD (peer, TCP_fatal_error); - return 0; - } - - /* Retrieve BGP packet type. */ - stream_set_getp (s, BGP_MARKER_SIZE + 2); - type = stream_getc (s); - - assert (type == BGP_MSG_NOTIFY); - - /* Type should be notify. */ - peer->notify_out++; - - /* Double start timer. */ - peer->v_start *= 2; - - /* Overflow check. */ - if (peer->v_start >= (60 * 2)) - peer->v_start = (60 * 2); - - BGP_EVENT_ADD (peer, BGP_Stop); - - return 0; -} - -/* Make keepalive packet and send it to the peer. */ -void -bgp_keepalive_send (struct peer *peer) -{ - struct stream *s; - int length; - - s = stream_new (BGP_MAX_PACKET_SIZE); - - /* Make keepalive packet. */ - bgp_packet_set_marker (s, BGP_MSG_KEEPALIVE); - - /* Set packet size. */ - length = bgp_packet_set_size (s); - - /* Dump packet if debug option is set. */ - /* bgp_packet_dump (s); */ - - if (BGP_DEBUG (keepalive, KEEPALIVE)) - zlog_debug ("%s sending KEEPALIVE", peer->host); - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s send message type %d, length (incl. header) %d", - peer->host, BGP_MSG_KEEPALIVE, length); - - /* Add packet to the peer. */ - bgp_packet_add (peer, s); - - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); -} - -/* Make open packet and send it to the peer. */ -void -bgp_open_send (struct peer *peer) -{ - struct stream *s; - int length; - u_int16_t send_holdtime; - as_t local_as; - - if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER)) - send_holdtime = peer->holdtime; - else - send_holdtime = peer->bgp->default_holdtime; - - /* local-as Change */ - if (peer->change_local_as) - local_as = peer->change_local_as; - else - local_as = peer->local_as; + stream_putc (s, BGP_OPEN_OPT_CAP); + stream_putc (s, CAPABILITY_CODE_AS4_LEN + 2); + stream_putc (s, CAPABILITY_CODE_AS4); + stream_putc (s, CAPABILITY_CODE_AS4_LEN); + stream_putl (s, open_state->my_as) ; + } ; - s = stream_new (BGP_MAX_PACKET_SIZE); + /* ORF Capabilities */ - /* Make open packet. */ - bgp_packet_set_marker (s, BGP_MSG_OPEN); - - /* Set open packet values. */ - stream_putc (s, BGP_VERSION_4); /* BGP version */ - stream_putw (s, (local_as <= BGP_AS_MAX) ? (u_int16_t) local_as - : BGP_AS_TRANS); - stream_putw (s, send_holdtime); /* Hold Time */ - stream_put_in_addr (s, &peer->local_id); /* BGP Identifier */ - - /* Set capability code. */ - bgp_open_capability (s, peer); - - /* Set BGP packet length. */ - length = bgp_packet_set_size (s); - - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s sending OPEN, version %d, my as %u, holdtime %d, id %s", - peer->host, BGP_VERSION_4, local_as, - send_holdtime, inet_ntoa (peer->local_id)); - - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s send message type %d, length (incl. header) %d", - peer->host, BGP_MSG_OPEN, length); - - /* Dump packet if debug option is set. */ - /* bgp_packet_dump (s); */ - - /* Add packet to the peer. */ - bgp_packet_add (peer, s); - - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); -} - -/* Send BGP notify packet with data potion. */ -void -bgp_notify_send_with_data (struct peer *peer, u_char code, u_char sub_code, - u_char *data, size_t datalen) -{ - struct stream *s; - int length; - - /* Allocate new stream. */ - s = stream_new (BGP_MAX_PACKET_SIZE); - - /* Make nitify packet. */ - bgp_packet_set_marker (s, BGP_MSG_NOTIFY); - - /* Set notify packet values. */ - stream_putc (s, code); /* BGP notify code */ - stream_putc (s, sub_code); /* BGP notify sub_code */ - - /* If notify data is present. */ - if (data) - stream_write (s, data, datalen); - - /* Set BGP packet length. */ - length = bgp_packet_set_size (s); - - /* Add packet to the peer. */ - stream_fifo_clean (peer->obuf); - bgp_packet_add (peer, s); - - /* For debug */ - { - struct bgp_notify bgp_notify; - int first = 0; - int i; - char c[4]; - - bgp_notify.code = code; - bgp_notify.subcode = sub_code; - bgp_notify.data = NULL; - bgp_notify.length = length - BGP_MSG_NOTIFY_MIN_SIZE; - - if (bgp_notify.length) - { - bgp_notify.data = XMALLOC (MTYPE_TMP, bgp_notify.length * 3); - for (i = 0; i < bgp_notify.length; i++) - if (first) - { - sprintf (c, " %02x", data[i]); - strcat (bgp_notify.data, c); - } - else - { - first = 1; - sprintf (c, "%02x", data[i]); - strcpy (bgp_notify.data, c); - } - } - bgp_notify_print (peer, &bgp_notify, "sending"); - if (bgp_notify.data) - XFREE (MTYPE_TMP, bgp_notify.data); - } - - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s send message type %d, length (incl. header) %d", - peer->host, BGP_MSG_NOTIFY, length); - - /* peer reset cause */ - if (sub_code != BGP_NOTIFY_CEASE_CONFIG_CHANGE) + for (qafx = qafx_num_first ; qafx <= qafx_num_last ; ++qafx) { - if (sub_code == BGP_NOTIFY_CEASE_ADMIN_RESET) - peer->last_reset = PEER_DOWN_USER_RESET; - else if (sub_code == BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN) - peer->last_reset = PEER_DOWN_USER_SHUTDOWN; - else - peer->last_reset = PEER_DOWN_NOTIFY_SEND; - } + u_char mode = 0 ; - /* Call imidiately. */ - BGP_WRITE_OFF (peer->t_write); + if (open_state->can_orf_prefix_send & qafx_bit(qafx)) + mode |= ORF_MODE_SEND ; + if (open_state->can_orf_prefix_recv & qafx_bit(qafx)) + mode |= ORF_MODE_RECEIVE ; - bgp_write_notify (peer); -} + confirm((ORF_MODE_SEND | ORF_MODE_RECEIVE) == ORF_MODE_BOTH) ; -/* Send BGP notify packet. */ -void -bgp_notify_send (struct peer *peer, u_char code, u_char sub_code) + if (mode != 0) + { + iAFI_t afi = get_iAFI(qafx) ; + iSAFI_t safi = get_iSAFI(qafx) ; + + if (!have_ipv6 && (afi == iAFI_IP6)) + continue ; + + if (open_state->can_orf_prefix & bgp_cap_form_old) + bgp_open_capability_orf(s, afi, safi, CAPABILITY_CODE_ORF_OLD, + ORF_TYPE_PREFIX_OLD, mode) ; + + if (open_state->can_orf_prefix & bgp_cap_form_new) + bgp_open_capability_orf(s, afi, safi, CAPABILITY_CODE_ORF, + ORF_TYPE_PREFIX, mode) ; + } ; + } ; + + /* Dynamic capability. */ + if (open_state->can_dynamic) + { + stream_putc (s, BGP_OPEN_OPT_CAP); + stream_putc (s, CAPABILITY_CODE_DYNAMIC_LEN + 2); + stream_putc (s, CAPABILITY_CODE_DYNAMIC); + stream_putc (s, CAPABILITY_CODE_DYNAMIC_LEN); + } ; + + /* Graceful restart capability */ + if (open_state->can_g_restart) + { + stream_putc (s, BGP_OPEN_OPT_CAP); + stream_putc (s, CAPABILITY_CODE_RESTART_LEN + 2); + stream_putc (s, CAPABILITY_CODE_RESTART); + stream_putc (s, CAPABILITY_CODE_RESTART_LEN); + stream_putw (s, open_state->restart_time); + } ; + + /* TODO: restarting flag ?? */ + /* TODO: graceful restart preserving forwarding state */ + + /* Total Opt Parm Len. */ + len = stream_get_endp (s) - cp - 1; + stream_putc_at (s, cp, len); +} ; + +/*------------------------------------------------------------------------------ + * Add an ORF capability to the given encoded OPEN message. + * + * Supports the status quo: only prefix-list filtering ! + */ +static void +bgp_open_capability_orf (struct stream *s, iAFI_t afi, iSAFI_t safi, + u_char cap_code, u_char orf_type, u_char mode) { - bgp_notify_send_with_data (peer, code, sub_code, NULL, 0); -} + u_char cap_len; + u_char orf_len; + unsigned long capp; + unsigned long orfp; + unsigned long numberp; -/* Send route refresh message to the peer. */ -void -bgp_route_refresh_send (struct peer *peer, afi_t afi, safi_t safi, - u_char orf_type, u_char when_to_refresh, int remove) -{ - struct stream *s; - struct stream *packet; - int length; - struct bgp_filter *filter; - int orf_refresh = 0; + int number_of_orfs = 0; - if (DISABLE_BGP_ANNOUNCE) - return; + stream_putc (s, BGP_OPEN_OPT_CAP); + capp = stream_get_endp (s); /* Set Capability Len Pointer */ + stream_putc (s, 0); /* Capability Length */ + stream_putc (s, cap_code); /* Capability Code */ + orfp = stream_get_endp (s); /* Set ORF Len Pointer */ + stream_putc (s, 0); /* ORF Length */ - filter = &peer->filter[afi][safi]; - - /* Adjust safi code. */ - if (safi == SAFI_MPLS_VPN) - safi = BGP_SAFI_VPNV4; - - s = stream_new (BGP_MAX_PACKET_SIZE); - - /* Make BGP update packet. */ - if (CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV)) - bgp_packet_set_marker (s, BGP_MSG_ROUTE_REFRESH_NEW); - else - bgp_packet_set_marker (s, BGP_MSG_ROUTE_REFRESH_OLD); - - /* Encode Route Refresh message. */ stream_putw (s, afi); stream_putc (s, 0); stream_putc (s, safi); - if (orf_type == ORF_TYPE_PREFIX - || orf_type == ORF_TYPE_PREFIX_OLD) - if (remove || filter->plist[FILTER_IN].ref) - { - u_int16_t orf_len; - unsigned long orfp; - - orf_refresh = 1; - stream_putc (s, when_to_refresh); - stream_putc (s, orf_type); - orfp = stream_get_endp (s); - stream_putw (s, 0); - - if (remove) - { - UNSET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND); - stream_putc (s, ORF_COMMON_PART_REMOVE_ALL); - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s sending REFRESH_REQ to remove ORF(%d) (%s) for afi/safi: %d/%d", - peer->host, orf_type, - (when_to_refresh == REFRESH_DEFER ? "defer" : "immediate"), - afi, safi); - } - else - { - SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND); - prefix_bgp_orf_entry (s, filter->plist[FILTER_IN].ref, - ORF_COMMON_PART_ADD, ORF_COMMON_PART_PERMIT, - ORF_COMMON_PART_DENY); - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s sending REFRESH_REQ with pfxlist ORF(%d) (%s) for afi/safi: %d/%d", - peer->host, orf_type, - (when_to_refresh == REFRESH_DEFER ? "defer" : "immediate"), - afi, safi); - } - - /* Total ORF Entry Len. */ - orf_len = stream_get_endp (s) - orfp - 2; - stream_putw_at (s, orfp, orf_len); - } - - /* Set packet size. */ - length = bgp_packet_set_size (s); - - if (BGP_DEBUG (normal, NORMAL)) - { - if (! orf_refresh) - zlog_debug ("%s sending REFRESH_REQ for afi/safi: %d/%d", - peer->host, afi, safi); - zlog_debug ("%s send message type %d, length (incl. header) %d", - peer->host, CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV) ? - BGP_MSG_ROUTE_REFRESH_NEW : BGP_MSG_ROUTE_REFRESH_OLD, length); - } - - /* Make real packet. */ - packet = stream_dup (s); - stream_free (s); - - /* Add packet to the peer. */ - bgp_packet_add (peer, packet); - - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); -} - -/* Send capability message to the peer. */ -void -bgp_capability_send (struct peer *peer, afi_t afi, safi_t safi, - int capability_code, int action) -{ - struct stream *s; - struct stream *packet; - int length; - - /* Adjust safi code. */ - if (safi == SAFI_MPLS_VPN) - safi = BGP_SAFI_VPNV4; + numberp = stream_get_endp (s); /* Set Number Pointer */ + stream_putc (s, 0); /* Number of ORFs */ - s = stream_new (BGP_MAX_PACKET_SIZE); + /* Address Prefix ORF */ + stream_putc (s, orf_type) ; /* type of ORF */ + stream_putc (s, mode) ; - /* Make BGP update packet. */ - bgp_packet_set_marker (s, BGP_MSG_CAPABILITY); + number_of_orfs++; - /* Encode MP_EXT capability. */ - if (capability_code == CAPABILITY_CODE_MP) - { - stream_putc (s, action); - stream_putc (s, CAPABILITY_CODE_MP); - stream_putc (s, CAPABILITY_CODE_MP_LEN); - stream_putw (s, afi); - stream_putc (s, 0); - stream_putc (s, safi); - - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s sending CAPABILITY has %s MP_EXT CAP for afi/safi: %d/%d", - peer->host, action == CAPABILITY_ACTION_SET ? - "Advertising" : "Removing", afi, safi); - } + /* Total Number of ORFs. */ + stream_putc_at (s, numberp, number_of_orfs); - /* Set packet size. */ - length = bgp_packet_set_size (s); + /* Total ORF Len. */ + orf_len = stream_get_endp (s) - orfp - 1; + stream_putc_at (s, orfp, orf_len); - /* Make real packet. */ - packet = stream_dup (s); - stream_free (s); + /* Total Capability Len. */ + cap_len = stream_get_endp (s) - capp - 1; + stream_putc_at (s, capp, cap_len); +} ; - /* Add packet to the peer. */ - bgp_packet_add (peer, packet); - - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s send message type %d, length (incl. header) %d", - peer->host, BGP_MSG_CAPABILITY, length); - - BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); -} +/*============================================================================== + * ROUTE-REFRESH -- transform bgp_prefix_orf_update into BGP message + */ -/* RFC1771 6.8 Connection collision detection. */ static int -bgp_collision_detect (struct peer *new, struct in_addr remote_id) -{ - struct peer *peer; - struct listnode *node, *nnode; - struct bgp *bgp; - - bgp = bgp_get_default (); - if (! bgp) - return 0; - - /* Upon receipt of an OPEN message, the local system must examine - all of its connections that are in the OpenConfirm state. A BGP - speaker may also examine connections in an OpenSent state if it - knows the BGP Identifier of the peer by means outside of the - protocol. If among these connections there is a connection to a - remote BGP speaker whose BGP Identifier equals the one in the - OPEN message, then the local system performs the following - collision resolution procedure: */ - - for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) - { - /* Under OpenConfirm status, local peer structure already hold - remote router ID. */ - - if (peer != new - && (peer->status == OpenConfirm || peer->status == OpenSent) - && sockunion_same (&peer->su, &new->su)) - { - /* 1. The BGP Identifier of the local system is compared to - the BGP Identifier of the remote system (as specified in - the OPEN message). */ - - if (ntohl (peer->local_id.s_addr) < ntohl (remote_id.s_addr)) - { - /* 2. If the value of the local BGP Identifier is less - than the remote one, the local system closes BGP - connection that already exists (the one that is - already in the OpenConfirm state), and accepts BGP - connection initiated by the remote system. */ - - if (peer->fd >= 0) - bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); - return 1; - } - else - { - /* 3. Otherwise, the local system closes newly created - BGP connection (the one associated with the newly - received OPEN message), and continues to use the - existing one (the one that is already in the - OpenConfirm state). */ - - if (new->fd >= 0) - bgp_notify_send (new, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); - return -1; - } - } - } - return 0; -} - +bgp_msg_orf_part(struct stream* s, bgp_connection connection, + bgp_route_refresh rr) ; static int -bgp_open_receive (struct peer *peer, bgp_size_t size) -{ - int ret; - u_char version; - u_char optlen; - u_int16_t holdtime; - u_int16_t send_holdtime; - as_t remote_as; - as_t as4 = 0; - struct peer *realpeer; - struct in_addr remote_id; - int capability; - u_int8_t notify_data_remote_as[2]; - u_int8_t notify_data_remote_id[4]; - - realpeer = NULL; - - /* Parse open packet. */ - version = stream_getc (peer->ibuf); - memcpy (notify_data_remote_as, stream_pnt (peer->ibuf), 2); - remote_as = stream_getw (peer->ibuf); - holdtime = stream_getw (peer->ibuf); - memcpy (notify_data_remote_id, stream_pnt (peer->ibuf), 4); - remote_id.s_addr = stream_get_ipv4 (peer->ibuf); - - /* Receive OPEN message log */ - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s rcv OPEN, version %d, remote-as (in open) %u," - " holdtime %d, id %s", - peer->host, version, remote_as, holdtime, - inet_ntoa (remote_id)); - - /* BEGIN to read the capability here, but dont do it yet */ - capability = 0; - optlen = stream_getc (peer->ibuf); +bgp_msg_orf_unknown(struct stream* s, bgp_orf_unknown_entry orf_unknown, + bgp_size_t left) ; +static int +bgp_msg_orf_remove_all(struct stream* s, bgp_size_t left) ; - if (optlen != 0) - { - /* We need the as4 capability value *right now* because - * if it is there, we have not got the remote_as yet, and without - * that we do not know which peer is connecting to us now. - */ - as4 = peek_for_as4_capability (peer, optlen); - } +static int +bgp_msg_orf_prefix(struct stream* s, uint8_t common, + bgp_orf_prefix_entry orf_prefix, bgp_size_t left) ; - /* Just in case we have a silly peer who sends AS4 capability set to 0 */ - if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) && !as4) - { - zlog_err ("%s bad OPEN, got AS4 capability, but AS4 set to 0", - peer->host); - bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_BAD_PEER_AS); - return -1; - } +/*------------------------------------------------------------------------------ + * Make Route-Refresh message(s) and dispatch. + * + * May return before all required messages have been sent, if the write + * buffer is or becomes full. The "next" entry in the "bgp_prefix_orf_update" + * allows the process to be continued, later. + * + * If has to send more than one message, then all but the last will be set + * "defer". The last will be set as per the defer flag. + * + * Supports the status quo, only Address-Prefix ORF. + * + * Returns: > 0 => all written + * 0 => unable to write everything + * < 0 => failed -- error event generated + */ +extern int +bgp_msg_send_route_refresh(bgp_connection connection, bgp_route_refresh rr) +{ + struct stream *s = connection->obuf ; + uint8_t msg_type ; + flag_t done ; + bgp_size_t msg_len ; + int ret ; - if (remote_as == BGP_AS_TRANS) - { - /* Take the AS4 from the capability. We must have received the - * capability now! Otherwise we have a asn16 peer who uses - * BGP_AS_TRANS, for some unknown reason. - */ - if (as4 == BGP_AS_TRANS) - { - zlog_err ("%s [AS4] NEW speaker using AS_TRANS for AS4, not allowed", - peer->host); - bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_BAD_PEER_AS); - return -1; - } - - if (!as4 && BGP_DEBUG (as4, AS4)) - zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS, but no AS4." - " Odd, but proceeding.", peer->host); - else if (as4 < BGP_AS_MAX && BGP_DEBUG (as4, AS4)) - zlog_debug ("%s [AS4] OPEN remote_as is AS_TRANS, but AS4 (%u) fits " - "in 2-bytes, very odd peer.", peer->host, as4); - if (as4) - remote_as = as4; - } - else - { - /* We may have a partner with AS4 who has an asno < BGP_AS_MAX */ - /* If we have got the capability, peer->as4cap must match remote_as */ - if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) - && as4 != remote_as) - { - /* raise error, log this, close session */ - zlog_err ("%s bad OPEN, got AS4 capability, but remote_as %u" - " mismatch with 16bit 'myasn' %u in open", - peer->host, as4, remote_as); - bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_BAD_PEER_AS); - return -1; - } - } + msg_type = connection->route_refresh_pre ? BGP_MT_ROUTE_REFRESH_pre + : BGP_MT_ROUTE_REFRESH ; + done = (bgp_orf_get_count(rr) == 0) ; - /* Lookup peer from Open packet. */ - if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + do { - int as = 0; - - realpeer = peer_lookup_with_open (&peer->su, remote_as, &remote_id, &as); - - if (! realpeer) - { - /* Peer's source IP address is check in bgp_accept(), so this - must be AS number mismatch or remote-id configuration - mismatch. */ - if (as) - { - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s bad OPEN, wrong router identifier %s", - peer->host, inet_ntoa (remote_id)); - bgp_notify_send_with_data (peer, BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_BAD_BGP_IDENT, - notify_data_remote_id, 4); - } - else - { - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s bad OPEN, remote AS is %u, expected %u", - peer->host, remote_as, peer->as); - bgp_notify_send_with_data (peer, BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_BAD_PEER_AS, - notify_data_remote_as, 2); - } - return -1; - } - } + if (bgp_connection_write_full(connection)) + return 0 ; - /* When collision is detected and this peer is closed. Retrun - immidiately. */ - ret = bgp_collision_detect (peer, remote_id); - if (ret < 0) - return ret; + /* Construct BGP message header for new/old form ROUTE-REFRESH */ + bgp_packet_set_marker(s, msg_type) ; - /* Hack part. */ - if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) - { - if (realpeer->status == Established - && CHECK_FLAG (realpeer->sflags, PEER_STATUS_NSF_MODE)) - { - realpeer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; - SET_FLAG (realpeer->sflags, PEER_STATUS_NSF_WAIT); - } - else if (ret == 0 && realpeer->status != Active - && realpeer->status != OpenSent - && realpeer->status != OpenConfirm - && realpeer->status != Connect) - { - /* XXX: This is an awful problem.. - * - * According to the RFC we should just let this connection (of the - * accepted 'peer') continue on to Established if the other - * connection (the 'realpeer' one) is in state Connect, and deal - * with the more larval FSM as/when it gets far enough to receive - * an Open. We don't do that though, we instead close the (more - * developed) accepted connection. - * - * This means there's a race, which if hit, can loop: - * - * FSM for A FSM for B - * realpeer accept-peer realpeer accept-peer - * - * Connect Connect - * Active - * OpenSent OpenSent - * <arrive here, - * Notify, delete> - * Idle Active - * OpenSent OpenSent - * <arrive here, - * Notify, delete> - * Idle - * <wait> <wait> - * Connect Connect - * - * - * If both sides are Quagga, they're almost certain to wait for - * the same amount of time of course (which doesn't preclude other - * implementations also waiting for same time). The race is - * exacerbated by high-latency (in bgpd and/or the network). - * - * The reason we do this is because our FSM is tied to our peer - * structure, which carries our configuration information, etc. - * I.e. we can't let the accepted-peer FSM continue on as it is, - * cause it's not associated with any actual peer configuration - - * it's just a dummy. - * - * It's possible we could hack-fix this by just bgp_stop'ing the - * realpeer and continueing on with the 'transfer FSM' below. - * Ideally, we need to seperate FSMs from struct peer. - * - * Setting one side to passive avoids the race, as a workaround. - */ - if (BGP_DEBUG (events, EVENTS)) - zlog_debug ("%s peer status is %s close connection", - realpeer->host, LOOKUP (bgp_status_msg, - realpeer->status)); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_CONNECT_REJECT); - - return -1; - } - - if (BGP_DEBUG (events, EVENTS)) - zlog_debug ("%s [Event] Transfer accept BGP peer to real (state %s)", - peer->host, - LOOKUP (bgp_status_msg, realpeer->status)); - - bgp_stop (realpeer); - - /* Transfer file descriptor. */ - realpeer->fd = peer->fd; - peer->fd = -1; - - /* Transfer input buffer. */ - stream_free (realpeer->ibuf); - realpeer->ibuf = peer->ibuf; - realpeer->packet_size = peer->packet_size; - peer->ibuf = NULL; - - /* Transfer status. */ - realpeer->status = peer->status; - bgp_stop (peer); - - /* peer pointer change. Open packet send to neighbor. */ - peer = realpeer; - bgp_open_send (peer); - if (peer->fd < 0) - { - zlog_err ("bgp_open_receive peer's fd is negative value %d", - peer->fd); - return -1; - } - BGP_READ_ON (peer->t_read, bgp_read, peer->fd); - } + /* Encode Route Refresh message. */ + stream_putw(s, rr->afi) ; + stream_putc(s, 0); + stream_putc(s, rr->safi); - /* remote router-id check. */ - if (remote_id.s_addr == 0 - || ntohl (remote_id.s_addr) >= 0xe0000000 - || ntohl (peer->local_id.s_addr) == ntohl (remote_id.s_addr)) - { - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s bad OPEN, wrong router identifier %s", - peer->host, inet_ntoa (remote_id)); - bgp_notify_send_with_data (peer, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_BAD_BGP_IDENT, - notify_data_remote_id, 4); - return -1; - } + /* Process as many (remaining) ORF entries as can into message */ + if (!done) + done = bgp_msg_orf_part(s, connection, rr) ; - /* Set remote router-id */ - peer->remote_id = remote_id; + /* Set BGP message length & dispatch. */ + msg_len = bgp_packet_set_size(s) ; - /* Peer BGP version check. */ - if (version != BGP_VERSION_4) - { - u_int8_t maxver = BGP_VERSION_4; - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s bad protocol version, remote requested %d, local request %d", - peer->host, version, BGP_VERSION_4); - bgp_notify_send_with_data (peer, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_UNSUP_VERSION, - &maxver, 1); - return -1; - } - - /* Check neighbor as number. */ - if (remote_as != peer->as) - { if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s bad OPEN, remote AS is %u, expected %u", - peer->host, remote_as, peer->as); - bgp_notify_send_with_data (peer, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_BAD_PEER_AS, - notify_data_remote_as, 2); - return -1; - } - - /* From the rfc: Upon receipt of an OPEN message, a BGP speaker MUST - calculate the value of the Hold Timer by using the smaller of its - configured Hold Time and the Hold Time received in the OPEN message. - The Hold Time MUST be either zero or at least three seconds. An - implementation may reject connections on the basis of the Hold Time. */ - - if (holdtime < 3 && holdtime != 0) - { - bgp_notify_send (peer, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_UNACEP_HOLDTIME); - return -1; - } - - /* From the rfc: A reasonable maximum time between KEEPALIVE messages - would be one third of the Hold Time interval. KEEPALIVE messages - MUST NOT be sent more frequently than one per second. An - implementation MAY adjust the rate at which it sends KEEPALIVE - messages as a function of the Hold Time interval. */ - - if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER)) - send_holdtime = peer->holdtime; - else - send_holdtime = peer->bgp->default_holdtime; + zlog_debug ("%s sending REFRESH_REQ for afi/safi: %d/%d length %d", + connection->host, rr->afi, rr->safi, msg_len) ; - if (holdtime < send_holdtime) - peer->v_holdtime = holdtime; - else - peer->v_holdtime = send_holdtime; - - peer->v_keepalive = peer->v_holdtime / 3; - - /* Open option part parse. */ - if (optlen != 0) - { - ret = bgp_open_option_parse (peer, optlen, &capability); + ret = bgp_connection_write(connection, s) ; if (ret < 0) - return ret; - } - else - { - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s rcvd OPEN w/ OPTION parameter len: 0", - peer->host); - } + return ret ; - /* Override capability. */ - if (! capability || CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) - { - peer->afc_nego[AFI_IP][SAFI_UNICAST] = peer->afc[AFI_IP][SAFI_UNICAST]; - peer->afc_nego[AFI_IP][SAFI_MULTICAST] = peer->afc[AFI_IP][SAFI_MULTICAST]; - peer->afc_nego[AFI_IP6][SAFI_UNICAST] = peer->afc[AFI_IP6][SAFI_UNICAST]; - peer->afc_nego[AFI_IP6][SAFI_MULTICAST] = peer->afc[AFI_IP6][SAFI_MULTICAST]; - } + } while (!done) ; - /* Get sockname. */ - bgp_getsockname (peer); + return done ; +} ; - BGP_EVENT_ADD (peer, Receive_OPEN_message); +/*------------------------------------------------------------------------------ + * Set the current ORF entry length (if any) + * + * Returns total length of BGP message. + */ +inline static bgp_size_t +bgp_msg_set_orf_length(struct stream* s, unsigned long elenp) +{ + bgp_size_t length = stream_get_endp(s) ; - peer->packet_size = 0; - if (peer->ibuf) - stream_reset (peer->ibuf); + if (elenp != 0) + stream_putw_at(s, elenp, length - elenp - 2) ; - return 0; -} + return length ; +} ; -/* Parse BGP Update packet and make attribute object. */ +/*------------------------------------------------------------------------------ + * Put ORF entries to the given stream until run out of entries or run out + * of room in the message. + * + * There MUST BE at least one ORF entry to go. + * + * Returns true <=> done all available entries. + */ static int -bgp_update_receive (struct peer *peer, bgp_size_t size) +bgp_msg_orf_part(struct stream* s, bgp_connection connection, + bgp_route_refresh rr) { - int ret; - u_char *end; - struct stream *s; - struct attr attr; - bgp_size_t attribute_len; - bgp_size_t update_len; - bgp_size_t withdraw_len; - struct bgp_nlri update; - struct bgp_nlri withdraw; - struct bgp_nlri mp_update; - struct bgp_nlri mp_withdraw; - char attrstr[BUFSIZ] = ""; - - /* Status must be Established. */ - if (peer->status != Established) - { - zlog_err ("%s [FSM] Update packet received under status %s", - peer->host, LOOKUP (bgp_status_msg, peer->status)); - bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0); - return -1; - } - - /* Set initial values. */ - memset (&attr, 0, sizeof (struct attr)); - memset (&update, 0, sizeof (struct bgp_nlri)); - memset (&withdraw, 0, sizeof (struct bgp_nlri)); - memset (&mp_update, 0, sizeof (struct bgp_nlri)); - memset (&mp_withdraw, 0, sizeof (struct bgp_nlri)); - - s = peer->ibuf; - end = stream_pnt (s) + size; - - /* RFC1771 6.3 If the Unfeasible Routes Length or Total Attribute - Length is too large (i.e., if Unfeasible Routes Length + Total - Attribute Length + 23 exceeds the message Length), then the Error - Subcode is set to Malformed Attribute List. */ - if (stream_pnt (s) + 2 > end) - { - zlog_err ("%s [Error] Update packet error" - " (packet length is short for unfeasible length)", - peer->host); - bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); - return -1; - } - - /* Unfeasible Route Length. */ - withdraw_len = stream_getw (s); + bgp_orf_entry entry ; - /* Unfeasible Route Length check. */ - if (stream_pnt (s) + withdraw_len > end) - { - zlog_err ("%s [Error] Update packet error" - " (packet unfeasible length overflow %d)", - peer->host, withdraw_len); - bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); - return -1; - } + uint8_t orf_type ; + uint8_t orf_type_sent ; - /* Unfeasible Route packet format check. */ - if (withdraw_len > 0) - { - ret = bgp_nlri_sanity_check (peer, AFI_IP, stream_pnt (s), withdraw_len); - if (ret < 0) - return -1; + unsigned long whenp ; /* where the "when" byte is */ + unsigned long elenp ; /* where the entries length is */ - if (BGP_DEBUG (packet, PACKET_RECV)) - zlog_debug ("%s [Update:RECV] Unfeasible NLRI received", peer->host); + bgp_size_t left ; + bgp_size_t length ; + flag_t done ; + flag_t first ; - withdraw.afi = AFI_IP; - withdraw.safi = SAFI_UNICAST; - withdraw.nlri = stream_pnt (s); - withdraw.length = withdraw_len; - stream_forward_getp (s, withdraw_len); - } - - /* Attribute total length check. */ - if (stream_pnt (s) + 2 > end) - { - zlog_warn ("%s [Error] Packet Error" - " (update packet is short for attribute length)", - peer->host); - bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); - return -1; - } + /* Heading for Prefix-Address ORF type section */ + whenp = stream_get_endp(s) ; /* position of "when" */ + stream_putc(s, rr->defer ? BGP_ORF_WTR_DEFER : BGP_ORF_WTR_IMMEDIATE) ; - /* Fetch attribute total length. */ - attribute_len = stream_getw (s); + /* Process ORF entries until run out of entries or space */ - /* Attribute length check. */ - if (stream_pnt (s) + attribute_len > end) - { - zlog_warn ("%s [Error] Packet Error" - " (update packet attribute length overflow %d)", - peer->host, attribute_len); - bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); - return -1; - } + elenp = 0 ; /* no entries length, yet */ + orf_type = 0 ; /* no ORF type, yet */ - /* Parse attribute when it exists. */ - if (attribute_len) - { - ret = bgp_attr_parse (peer, &attr, attribute_len, - &mp_update, &mp_withdraw); - if (ret < 0) - return -1; - } + first = 1 ; /* next entry is first of its ORF type */ - /* Logging the attribute. */ - if (BGP_DEBUG (update, UPDATE_IN)) + while (1) { - ret= bgp_dump_attr (peer, &attr, attrstr, BUFSIZ); + entry = bgp_orf_get_entry(rr, rr->next) ; + done = (entry == NULL) ; - if (ret) - zlog (peer->log, LOG_DEBUG, "%s rcvd UPDATE w/ attr: %s", - peer->host, attrstr); - } + if (done) + break ; - /* Network Layer Reachability Information. */ - update_len = end - stream_pnt (s); + /* How much space is there left -- give up if very little + * + * What is "very little" is arbitrary, BUT MUST cover the ORF Type + * byte and the Length of ORF entries word, AT LEAST. + * */ + left = BGP_MSG_MAX_L - stream_get_endp(s) ; - if (update_len) - { - /* Check NLRI packet format and prefix length. */ - ret = bgp_nlri_sanity_check (peer, AFI_IP, stream_pnt (s), update_len); - if (ret < 0) - return -1; - - /* Set NLRI portion to structure. */ - update.afi = AFI_IP; - update.safi = SAFI_UNICAST; - update.nlri = stream_pnt (s); - update.length = update_len; - stream_forward_getp (s, update_len); - } + if (left < 16) + break ; /* NB: done == false */ - /* NLRI is processed only when the peer is configured specific - Address Family and Subsequent Address Family. */ - if (peer->afc[AFI_IP][SAFI_UNICAST]) - { - if (withdraw.length) - bgp_nlri_parse (peer, NULL, &withdraw); - - if (update.length) - { - /* We check well-known attribute only for IPv4 unicast - update. */ - ret = bgp_attr_check (peer, &attr); - if (ret < 0) - return -1; - - bgp_nlri_parse (peer, &attr, &update); - } - - if (mp_update.length - && mp_update.afi == AFI_IP - && mp_update.safi == SAFI_UNICAST) - bgp_nlri_parse (peer, &attr, &mp_update); - - if (mp_withdraw.length - && mp_withdraw.afi == AFI_IP - && mp_withdraw.safi == SAFI_UNICAST) - bgp_nlri_parse (peer, NULL, &mp_withdraw); - - if (! attribute_len && ! withdraw_len) - { - /* End-of-RIB received */ - SET_FLAG (peer->af_sflags[AFI_IP][SAFI_UNICAST], - PEER_STATUS_EOR_RECEIVED); - - /* NSF delete stale route */ - if (peer->nsf[AFI_IP][SAFI_UNICAST]) - bgp_clear_stale_route (peer, AFI_IP, SAFI_UNICAST); - - if (BGP_DEBUG (normal, NORMAL)) - zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for IPv4 Unicast from %s", - peer->host); - } - } - if (peer->afc[AFI_IP][SAFI_MULTICAST]) - { - if (mp_update.length - && mp_update.afi == AFI_IP - && mp_update.safi == SAFI_MULTICAST) - bgp_nlri_parse (peer, &attr, &mp_update); - - if (mp_withdraw.length - && mp_withdraw.afi == AFI_IP - && mp_withdraw.safi == SAFI_MULTICAST) - bgp_nlri_parse (peer, NULL, &mp_withdraw); - - if (! withdraw_len - && mp_withdraw.afi == AFI_IP - && mp_withdraw.safi == SAFI_MULTICAST - && mp_withdraw.length == 0) - { - /* End-of-RIB received */ - SET_FLAG (peer->af_sflags[AFI_IP][SAFI_MULTICAST], - PEER_STATUS_EOR_RECEIVED); - - /* NSF delete stale route */ - if (peer->nsf[AFI_IP][SAFI_MULTICAST]) - bgp_clear_stale_route (peer, AFI_IP, SAFI_MULTICAST); - - if (BGP_DEBUG (normal, NORMAL)) - zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for IPv4 Multicast from %s", - peer->host); - } - } - if (peer->afc[AFI_IP6][SAFI_UNICAST]) - { - if (mp_update.length - && mp_update.afi == AFI_IP6 - && mp_update.safi == SAFI_UNICAST) - bgp_nlri_parse (peer, &attr, &mp_update); - - if (mp_withdraw.length - && mp_withdraw.afi == AFI_IP6 - && mp_withdraw.safi == SAFI_UNICAST) - bgp_nlri_parse (peer, NULL, &mp_withdraw); - - if (! withdraw_len - && mp_withdraw.afi == AFI_IP6 - && mp_withdraw.safi == SAFI_UNICAST - && mp_withdraw.length == 0) - { - /* End-of-RIB received */ - SET_FLAG (peer->af_sflags[AFI_IP6][SAFI_UNICAST], PEER_STATUS_EOR_RECEIVED); - - /* NSF delete stale route */ - if (peer->nsf[AFI_IP6][SAFI_UNICAST]) - bgp_clear_stale_route (peer, AFI_IP6, SAFI_UNICAST); - - if (BGP_DEBUG (normal, NORMAL)) - zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for IPv6 Unicast from %s", - peer->host); - } - } - if (peer->afc[AFI_IP6][SAFI_MULTICAST]) - { - if (mp_update.length - && mp_update.afi == AFI_IP6 - && mp_update.safi == SAFI_MULTICAST) - bgp_nlri_parse (peer, &attr, &mp_update); - - if (mp_withdraw.length - && mp_withdraw.afi == AFI_IP6 - && mp_withdraw.safi == SAFI_MULTICAST) - bgp_nlri_parse (peer, NULL, &mp_withdraw); - - if (! withdraw_len - && mp_withdraw.afi == AFI_IP6 - && mp_withdraw.safi == SAFI_MULTICAST - && mp_withdraw.length == 0) - { - /* End-of-RIB received */ - - /* NSF delete stale route */ - if (peer->nsf[AFI_IP6][SAFI_MULTICAST]) - bgp_clear_stale_route (peer, AFI_IP6, SAFI_MULTICAST); - - if (BGP_DEBUG (update, UPDATE_IN)) - zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for IPv6 Multicast from %s", - peer->host); - } - } - if (peer->afc[AFI_IP][SAFI_MPLS_VPN]) - { - if (mp_update.length - && mp_update.afi == AFI_IP - && mp_update.safi == BGP_SAFI_VPNV4) - bgp_nlri_parse_vpnv4 (peer, &attr, &mp_update); - - if (mp_withdraw.length - && mp_withdraw.afi == AFI_IP - && mp_withdraw.safi == BGP_SAFI_VPNV4) - bgp_nlri_parse_vpnv4 (peer, NULL, &mp_withdraw); - - if (! withdraw_len - && mp_withdraw.afi == AFI_IP - && mp_withdraw.safi == BGP_SAFI_VPNV4 - && mp_withdraw.length == 0) - { - /* End-of-RIB received */ - - if (BGP_DEBUG (update, UPDATE_IN)) - zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for VPNv4 Unicast from %s", - peer->host); - } - } - - /* Everything is done. We unintern temporary structures which - interned in bgp_attr_parse(). */ - if (attr.aspath) - aspath_unintern (attr.aspath); - if (attr.community) - community_unintern (attr.community); - if (attr.extra) - { - if (attr.extra->ecommunity) - ecommunity_unintern (attr.extra->ecommunity); - if (attr.extra->cluster) - cluster_unintern (attr.extra->cluster); - if (attr.extra->transit) - transit_unintern (attr.extra->transit); - bgp_attr_extra_free (&attr); - } + confirm(16 > BGP_ORF_MIN_L) ; /* Type & Length */ - /* If peering is stopped due to some reason, do not generate BGP - event. */ - if (peer->status != Established) - return 0; + /* Start new collection of ORF entries, if required. */ + if (first || (orf_type != entry->orf_type)) + { + /* fill in length of previous ORF entries, if any */ + bgp_msg_set_orf_length(s, elenp) ; - /* Increment packet counter. */ - peer->update_in++; - peer->update_time = time (NULL); + /* set type and dummy entries length. */ + orf_type = entry->orf_type ; + orf_type_sent = entry->orf_type ; - /* Generate BGP event. */ - BGP_EVENT_ADD (peer, Receive_UPDATE_message); + if ((orf_type == BGP_ORF_T_PREFIX) && connection->route_refresh_pre) + orf_type_sent = BGP_ORF_T_PREFIX_pre ; - return 0; -} - -/* Notify message treatment function. */ -static void -bgp_notify_receive (struct peer *peer, bgp_size_t size) -{ - struct bgp_notify bgp_notify; + stream_putc(s, orf_type_sent) ; /* ORF entries type */ - if (peer->notify.data) - { - XFREE (MTYPE_TMP, peer->notify.data); - peer->notify.data = NULL; - peer->notify.length = 0; - } + elenp = stream_get_endp(s) ; /* offset of the length */ + stream_putw(s, 0) ; /* length of ORF entries */ - bgp_notify.code = stream_getc (peer->ibuf); - bgp_notify.subcode = stream_getc (peer->ibuf); - bgp_notify.length = size - 2; - bgp_notify.data = NULL; + first = 1 ; /* next ORF entry is first of collection */ + } ; - /* Preserv notify code and sub code. */ - peer->notify.code = bgp_notify.code; - peer->notify.subcode = bgp_notify.subcode; - /* For further diagnostic record returned Data. */ - if (bgp_notify.length) - { - peer->notify.length = size - 2; - peer->notify.data = XMALLOC (MTYPE_TMP, size - 2); - memcpy (peer->notify.data, stream_pnt (peer->ibuf), size - 2); - } + /* Insert the entry, if will fit. + * + * sets done <=> fitted + */ + if (entry->unknown) + done = bgp_msg_orf_unknown(s, &entry->body.orf_unknown, left) ; + else + { + if (entry->remove_all) + done = bgp_msg_orf_remove_all(s, left) ; + else + { + uint8_t common = (entry->remove ? BGP_ORF_EA_REMOVE + : BGP_ORF_EA_ADD) + | (entry->deny ? BGP_ORF_EA_DENY + : BGP_ORF_EA_PERMIT) ; + switch (entry->orf_type) + { + case BGP_ORF_T_PREFIX: + done = bgp_msg_orf_prefix(s, common, + &entry->body.orf_prefix, left) ; + break ; + default: + zabort("unknown ORF type") ; + break ; + } ; + } ; + } ; + + /* exit loop now if not enough room for current ORF entry */ + if (!done) + break ; + + /* Done ORF entry. Step to the next. NB: done == true */ + ++rr->next ; + first = 0 ; /* no longer first */ + } ; + + /* If not done, need to: + * + * a) force defer, + * b) undo ORF entries if none output of current type + * + */ + if (!done) + { + stream_putc_at(s, whenp , BGP_ORF_WTR_DEFER) ; + + if (first) + { + stream_set_endp(s, elenp - 1) ; + elenp = 0 ; /* no entries length to set */ + } ; + } ; + + /* fill in length of last ORF entries (if any) */ + length = bgp_msg_set_orf_length(s, elenp) ; + + /* Something has gone wrong if nothing has been output after the "when" + * byte. Two possibilities: + * + * a) have been called again after having reported "done" (so there are + * no more entries to deal with. + * + * b) have been asked to output an "unknown" ORF entry which is too long + * for a BGP message !! + */ + if (length == (whenp + 1)) + { + if (entry == NULL) + zabort("called bgp_msg_send_route_refresh() after said was done") ; + + if (entry->unknown) + zlog_err("%s sending REFRESH_REQ with impossible length (%d) ORF", + connection->host, entry->body.orf_unknown.length) ; + else + zabort("failed to put even one ORF entry") ; - /* For debug */ - { - int i; - int first = 0; - char c[4]; + done = 1 ; /* force done */ + } ; - if (bgp_notify.length) - { - bgp_notify.data = XMALLOC (MTYPE_TMP, bgp_notify.length * 3); - for (i = 0; i < bgp_notify.length; i++) - if (first) - { - sprintf (c, " %02x", stream_getc (peer->ibuf)); - strcat (bgp_notify.data, c); - } - else - { - first = 1; - sprintf (c, "%02x", stream_getc (peer->ibuf)); - strcpy (bgp_notify.data, c); - } - } - - bgp_notify_print(peer, &bgp_notify, "received"); - if (bgp_notify.data) - XFREE (MTYPE_TMP, bgp_notify.data); - } - - /* peer count update */ - peer->notify_in++; - - if (peer->status == Established) - peer->last_reset = PEER_DOWN_NOTIFY_RECEIVED; - - /* We have to check for Notify with Unsupported Optional Parameter. - in that case we fallback to open without the capability option. - But this done in bgp_stop. We just mark it here to avoid changing - the fsm tables. */ - if (bgp_notify.code == BGP_NOTIFY_OPEN_ERR && - bgp_notify.subcode == BGP_NOTIFY_OPEN_UNSUP_PARAM ) - UNSET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN); - - /* Also apply to Unsupported Capability until remote router support - capability. */ - if (bgp_notify.code == BGP_NOTIFY_OPEN_ERR && - bgp_notify.subcode == BGP_NOTIFY_OPEN_UNSUP_CAPBL) - UNSET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN); - - BGP_EVENT_ADD (peer, Receive_NOTIFICATION_message); -} + return done ; +} ; -/* Keepalive treatment function -- get keepalive send keepalive */ -static void -bgp_keepalive_receive (struct peer *peer, bgp_size_t size) +/*------------------------------------------------------------------------------ + * Put given unknown ORF entry to stream -- verbatim -- if possible. + */ +static int +bgp_msg_orf_unknown(struct stream* s, bgp_orf_unknown_entry orf_unknown, + bgp_size_t left) { - if (BGP_DEBUG (keepalive, KEEPALIVE)) - zlog_debug ("%s KEEPALIVE rcvd", peer->host); + if (left < orf_unknown->length) + return 0 ; - BGP_EVENT_ADD (peer, Receive_KEEPALIVE_message); -} + stream_write(s, orf_unknown->data, orf_unknown->length) ; + return 1 ; +} ; -/* Route refresh message is received. */ -static void -bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) +/*------------------------------------------------------------------------------ + * Put remove all ORF entry to stream -- if possible. + */ +static int +bgp_msg_orf_remove_all(struct stream* s, bgp_size_t left) { - afi_t afi; - safi_t safi; - u_char reserved; - struct stream *s; + if (left == 1) /* only one byte required ! */ + return 0 ; - /* If peer does not have the capability, send notification. */ - if (! CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_ADV)) - { - plog_err (peer->log, "%s [Error] BGP route refresh is not enabled", - peer->host); - bgp_notify_send (peer, - BGP_NOTIFY_HEADER_ERR, - BGP_NOTIFY_HEADER_BAD_MESTYPE); - return; - } - - /* Status must be Established. */ - if (peer->status != Established) - { - plog_err (peer->log, - "%s [Error] Route refresh packet received under status %s", - peer->host, LOOKUP (bgp_status_msg, peer->status)); - bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0); - return; - } - - s = peer->ibuf; - - /* Parse packet. */ - afi = stream_getw (s); - reserved = stream_getc (s); - safi = stream_getc (s); - - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s rcvd REFRESH_REQ for afi/safi: %d/%d", - peer->host, afi, safi); - - /* Check AFI and SAFI. */ - if ((afi != AFI_IP && afi != AFI_IP6) - || (safi != SAFI_UNICAST && safi != SAFI_MULTICAST - && safi != BGP_SAFI_VPNV4)) - { - if (BGP_DEBUG (normal, NORMAL)) - { - zlog_debug ("%s REFRESH_REQ for unrecognized afi/safi: %d/%d - ignored", - peer->host, afi, safi); - } - return; - } - - /* Adjust safi code. */ - if (safi == BGP_SAFI_VPNV4) - safi = SAFI_MPLS_VPN; - - if (size != BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE) - { - u_char *end; - u_char when_to_refresh; - u_char orf_type; - u_int16_t orf_len; - - if (size - (BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE) < 5) - { - zlog_info ("%s ORF route refresh length error", peer->host); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); - return; - } - - when_to_refresh = stream_getc (s); - end = stream_pnt (s) + (size - 5); - - while ((stream_pnt (s) + 2) < end) - { - orf_type = stream_getc (s); - orf_len = stream_getw (s); - - /* orf_len in bounds? */ - if ((stream_pnt (s) + orf_len) > end) - break; /* XXX: Notify instead?? */ - if (orf_type == ORF_TYPE_PREFIX - || orf_type == ORF_TYPE_PREFIX_OLD) - { - u_char *p_pnt = stream_pnt (s); - u_char *p_end = stream_pnt (s) + orf_len; - struct orf_prefix orfp; - u_char common = 0; - u_int32_t seq; - int psize; - char name[BUFSIZ]; - char buf[BUFSIZ]; - int ret; - - if (BGP_DEBUG (normal, NORMAL)) - { - zlog_debug ("%s rcvd Prefixlist ORF(%d) length %d", - peer->host, orf_type, orf_len); - } - - /* we're going to read at least 1 byte of common ORF header, - * and 7 bytes of ORF Address-filter entry from the stream - */ - if (orf_len < 7) - break; - - /* ORF prefix-list name */ - sprintf (name, "%s.%d.%d", peer->host, afi, safi); - - while (p_pnt < p_end) - { - memset (&orfp, 0, sizeof (struct orf_prefix)); - common = *p_pnt++; - if (common & ORF_COMMON_PART_REMOVE_ALL) - { - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s rcvd Remove-All pfxlist ORF request", peer->host); - prefix_bgp_orf_remove_all (name); - break; - } - memcpy (&seq, p_pnt, sizeof (u_int32_t)); - p_pnt += sizeof (u_int32_t); - orfp.seq = ntohl (seq); - orfp.ge = *p_pnt++; - orfp.le = *p_pnt++; - orfp.p.prefixlen = *p_pnt++; - orfp.p.family = afi2family (afi); - psize = PSIZE (orfp.p.prefixlen); - memcpy (&orfp.p.u.prefix, p_pnt, psize); - p_pnt += psize; - - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s rcvd %s %s seq %u %s/%d ge %d le %d", - peer->host, - (common & ORF_COMMON_PART_REMOVE ? "Remove" : "Add"), - (common & ORF_COMMON_PART_DENY ? "deny" : "permit"), - orfp.seq, - inet_ntop (orfp.p.family, &orfp.p.u.prefix, buf, BUFSIZ), - orfp.p.prefixlen, orfp.ge, orfp.le); - - ret = prefix_bgp_orf_set (name, afi, &orfp, - (common & ORF_COMMON_PART_DENY ? 0 : 1 ), - (common & ORF_COMMON_PART_REMOVE ? 0 : 1)); - - if (ret != CMD_SUCCESS) - { - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s Received misformatted prefixlist ORF. Remove All pfxlist", peer->host); - prefix_bgp_orf_remove_all (name); - break; - } - } - peer->orf_plist[afi][safi] = - prefix_list_lookup (AFI_ORF_PREFIX, name); - } - stream_forward_getp (s, orf_len); - } - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s rcvd Refresh %s ORF request", peer->host, - when_to_refresh == REFRESH_DEFER ? "Defer" : "Immediate"); - if (when_to_refresh == REFRESH_DEFER) - return; - } - - /* First update is deferred until ORF or ROUTE-REFRESH is received */ - if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH)) - UNSET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH); - - /* Perform route refreshment to the peer */ - bgp_announce_route (peer, afi, safi); -} + stream_putc(s, BGP_ORF_EA_RM_ALL) ; + return 1 ; +} ; +/*------------------------------------------------------------------------------ + * Put given Address-Prefix ORF entry to stream -- if possible. + */ static int -bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length) +bgp_msg_orf_prefix(struct stream* s, uint8_t common, + bgp_orf_prefix_entry orf_prefix, bgp_size_t left) { - u_char *end; - struct capability_mp_data mpc; - struct capability_header *hdr; - u_char action; - struct bgp *bgp; - afi_t afi; - safi_t safi; - - bgp = peer->bgp; - end = pnt + length; - - while (pnt < end) - { - /* We need at least action, capability code and capability length. */ - if (pnt + 3 > end) - { - zlog_info ("%s Capability length error", peer->host); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); - return -1; - } - action = *pnt; - hdr = (struct capability_header *)(pnt + 1); - - /* Action value check. */ - if (action != CAPABILITY_ACTION_SET - && action != CAPABILITY_ACTION_UNSET) - { - zlog_info ("%s Capability Action Value error %d", - peer->host, action); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); - return -1; - } - - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s CAPABILITY has action: %d, code: %u, length %u", - peer->host, action, hdr->code, hdr->length); - - /* Capability length check. */ - if ((pnt + hdr->length + 3) > end) - { - zlog_info ("%s Capability length error", peer->host); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); - return -1; - } + bgp_size_t plen = (orf_prefix->pfx.prefixlen + 7) / 8 ; - /* Fetch structure to the byte stream. */ - memcpy (&mpc, pnt + 3, sizeof (struct capability_mp_data)); + if (left < (BGP_ORF_E_P_MIN_L + plen)) + return 0 ; - /* We know MP Capability Code. */ - if (hdr->code == CAPABILITY_CODE_MP) - { - afi = ntohs (mpc.afi); - safi = mpc.safi; + stream_putc(s, common) ; + stream_putl(s, orf_prefix->seq) ; + stream_putc(s, orf_prefix->min) ; + stream_putc(s, orf_prefix->max) ; + stream_putc(s, orf_prefix->pfx.prefixlen) ; + stream_write(s, &orf_prefix->pfx.u.prefix, plen) ; - /* Ignore capability when override-capability is set. */ - if (CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) - continue; + return 1 ; +} ; - if (!bgp_afi_safi_valid_indices (afi, &safi)) - { - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s Dynamic Capability MP_EXT afi/safi invalid " - "(%u/%u)", peer->host, afi, safi); - continue; - } - - /* Address family check. */ - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s CAPABILITY has %s MP_EXT CAP for afi/safi: %u/%u", - peer->host, - action == CAPABILITY_ACTION_SET - ? "Advertising" : "Removing", - ntohs(mpc.afi) , mpc.safi); - - if (action == CAPABILITY_ACTION_SET) - { - peer->afc_recv[afi][safi] = 1; - if (peer->afc[afi][safi]) - { - peer->afc_nego[afi][safi] = 1; - bgp_announce_route (peer, afi, safi); - } - } - else - { - peer->afc_recv[afi][safi] = 0; - peer->afc_nego[afi][safi] = 0; - - if (peer_active_nego (peer)) - bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_NORMAL); - else - BGP_EVENT_ADD (peer, BGP_Stop); - } - } - else - { - zlog_warn ("%s unrecognized capability code: %d - ignored", - peer->host, hdr->code); - } - pnt += hdr->length + 3; - } - return 0; -} +/*============================================================================== + * End-of-RIB -- send an End-of-RIB BGP message (see Graceful Restart) + */ -/* Dynamic Capability is received. +/*------------------------------------------------------------------------------ + * Make End-of-RIB message and dispatch. * - * This is exported for unit-test purposes - */ extern int bgp_capability_receive(struct peer*, bgp_size_t) ; -int -bgp_capability_receive (struct peer *peer, bgp_size_t size) + * + * + * Returns: 2 => written to TCP -- it's gone + * 1 => written to wbuff -- waiting for socket + * 0 => nothing written -- wbuff was not empty ! + * -1 => failed -- error event generated + */ +extern int +bgp_msg_send_end_of_rib(bgp_connection connection, iAFI_t afi, iSAFI_t safi) { - u_char *pnt; + struct stream *s = connection->obuf ; - /* Fetch pointer. */ - pnt = stream_pnt (peer->ibuf); + if (!bgp_connection_write_empty(connection)) + return 0 ; - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s rcv CAPABILITY", peer->host); + /* Make UPDATE message header */ + bgp_packet_set_marker(s, BGP_MSG_UPDATE) ; - /* If peer does not have the capability, send notification. */ - if (! CHECK_FLAG (peer->cap, PEER_CAP_DYNAMIC_ADV)) - { - plog_err (peer->log, "%s [Error] BGP dynamic capability is not enabled", - peer->host); - bgp_notify_send (peer, - BGP_NOTIFY_HEADER_ERR, - BGP_NOTIFY_HEADER_BAD_MESTYPE); - return -1; - } + /* Minimum size UPDATE */ + stream_putw(s, 0) ; /* no Withdrawn Routes */ + stream_putw(s, 0) ; /* no Attributes => no NLRI */ - /* Status must be Established. */ - if (peer->status != Established) - { - plog_err (peer->log, - "%s [Error] Dynamic capability packet received under status %s", peer->host, LOOKUP (bgp_status_msg, peer->status)); - bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0); - return -1; - } + /* If not IPv4/Unicast, need empty MP Unreachable attribute */ + if ((afi != iAFI_IP) || (safi != iSAFI_Unicast)) + { + bgp_size_t attrp = stream_get_endp(s) ; - /* Parse packet. */ - return bgp_capability_msg_parse (peer, pnt, size); -} + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); + stream_putc (s, BGP_ATTR_MP_UNREACH_NLRI); + stream_putc (s, 3); + stream_putw (s, afi); + stream_putc (s, safi); -/* BGP read utility function. */ -static int -bgp_read_packet (struct peer *peer) -{ - int nbytes; - int readsize; - - readsize = peer->packet_size - stream_get_endp (peer->ibuf); - - /* If size is zero then return. */ - if (! readsize) - return 0; - - /* Read packet from fd. */ - nbytes = stream_read_unblock (peer->ibuf, peer->fd, readsize); - - /* If read byte is smaller than zero then error occured. */ - if (nbytes < 0) - { - if (errno == EAGAIN) - return -1; - - plog_err (peer->log, "%s [Error] bgp_read_packet error: %s", - peer->host, safe_strerror (errno)); - - if (peer->status == Established) - { - if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_MODE)) - { - peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; - SET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT); - } - else - peer->last_reset = PEER_DOWN_CLOSE_SESSION; - } - - BGP_EVENT_ADD (peer, TCP_fatal_error); - return -1; + stream_putw_at(s, attrp-2, stream_get_endp(s) - attrp) ; } - /* When read byte is zero : clear bgp peer and return */ - if (nbytes == 0) - { - if (BGP_DEBUG (events, EVENTS)) - plog_debug (peer->log, "%s [Event] BGP connection closed fd %d", - peer->host, peer->fd); - - if (peer->status == Established) - { - if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_MODE)) - { - peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; - SET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT); - } - else - peer->last_reset = PEER_DOWN_CLOSE_SESSION; - } - - BGP_EVENT_ADD (peer, TCP_connection_closed); - return -1; - } + bgp_packet_set_size(s); - /* We read partial packet. */ - if (stream_get_endp (peer->ibuf) != peer->packet_size) - return -1; + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("send End-of-RIB for %s to %s", afi_safi_print (afi, safi), + connection->host) ; - return 0; -} + /* Finally -- write the obuf away */ + return bgp_connection_write(connection, s) ; +} ; -/* Marker check. */ -static int -bgp_marker_all_one (struct stream *s, int length) +/*============================================================================== + * Utilities for creating BGP messages + */ + /* 0 1 2 3 4 5 6 7 */ +static const char bgp_header[] = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" /* 8 */ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" /* 16 */ + "\x00" ; +CONFIRM(sizeof(bgp_header) == (BGP_MARKER_SIZE + 2)) ; + +/*------------------------------------------------------------------------------ + * Insert BGP message standard header + * + * 16 bytes of 0xFF + * 2 bytes -- total length of message -- filled in later + * 1 byte -- the type of message as given + */ +extern int +bgp_packet_set_marker(struct stream *s, uint8_t type) { - int i; + /* Fill in marker & dummy total length (to be filled in later on) */ + stream_write(s, bgp_header, BGP_MARKER_SIZE + 2) ; - for (i = 0; i < length; i++) - if (s->data[i] != 0xff) - return 0; + /* BGP packet type. */ + stream_putc (s, type); - return 1; -} + /* Return current stream size. */ + return stream_get_endp (s); +} ; -/* Starting point of packet process function. */ -int -bgp_read (struct thread *thread) +/*------------------------------------------------------------------------------ + * Set BGP packet header size entry and return same. + */ +extern int +bgp_packet_set_size (struct stream *s) { - int ret; - u_char type = 0; - struct peer *peer; - bgp_size_t size; - char notify_data_length[2]; - - /* Yes first of all get peer pointer. */ - peer = THREAD_ARG (thread); - peer->t_read = NULL; - - /* For non-blocking IO check. */ - if (peer->status == Connect) - { - bgp_connect_check (peer); - goto done; - } - else - { - if (peer->fd < 0) - { - zlog_err ("bgp_read peer's fd is negative value %d", peer->fd); - return -1; - } - BGP_READ_ON (peer->t_read, bgp_read, peer->fd); - } - - /* Read packet header to determine type of the packet */ - if (peer->packet_size == 0) - peer->packet_size = BGP_HEADER_SIZE; - - if (stream_get_endp (peer->ibuf) < BGP_HEADER_SIZE) - { - ret = bgp_read_packet (peer); - - /* Header read error or partial read packet. */ - if (ret < 0) - goto done; - - /* Get size and type. */ - stream_forward_getp (peer->ibuf, BGP_MARKER_SIZE); - memcpy (notify_data_length, stream_pnt (peer->ibuf), 2); - size = stream_getw (peer->ibuf); - type = stream_getc (peer->ibuf); - - if (BGP_DEBUG (normal, NORMAL) && type != 2 && type != 0) - zlog_debug ("%s rcv message type %d, length (excl. header) %d", - peer->host, type, size - BGP_HEADER_SIZE); - - /* Marker check */ - if (((type == BGP_MSG_OPEN) || (type == BGP_MSG_KEEPALIVE)) - && ! bgp_marker_all_one (peer->ibuf, BGP_MARKER_SIZE)) - { - bgp_notify_send (peer, - BGP_NOTIFY_HEADER_ERR, - BGP_NOTIFY_HEADER_NOT_SYNC); - goto done; - } - - /* BGP type check. */ - if (type != BGP_MSG_OPEN && type != BGP_MSG_UPDATE - && type != BGP_MSG_NOTIFY && type != BGP_MSG_KEEPALIVE - && type != BGP_MSG_ROUTE_REFRESH_NEW - && type != BGP_MSG_ROUTE_REFRESH_OLD - && type != BGP_MSG_CAPABILITY) - { - if (BGP_DEBUG (normal, NORMAL)) - plog_debug (peer->log, - "%s unknown message type 0x%02x", - peer->host, type); - bgp_notify_send_with_data (peer, - BGP_NOTIFY_HEADER_ERR, - BGP_NOTIFY_HEADER_BAD_MESTYPE, - &type, 1); - goto done; - } - /* Mimimum packet length check. */ - if ((size < BGP_HEADER_SIZE) - || (size > BGP_MAX_PACKET_SIZE) - || (type == BGP_MSG_OPEN && size < BGP_MSG_OPEN_MIN_SIZE) - || (type == BGP_MSG_UPDATE && size < BGP_MSG_UPDATE_MIN_SIZE) - || (type == BGP_MSG_NOTIFY && size < BGP_MSG_NOTIFY_MIN_SIZE) - || (type == BGP_MSG_KEEPALIVE && size != BGP_MSG_KEEPALIVE_MIN_SIZE) - || (type == BGP_MSG_ROUTE_REFRESH_NEW && size < BGP_MSG_ROUTE_REFRESH_MIN_SIZE) - || (type == BGP_MSG_ROUTE_REFRESH_OLD && size < BGP_MSG_ROUTE_REFRESH_MIN_SIZE) - || (type == BGP_MSG_CAPABILITY && size < BGP_MSG_CAPABILITY_MIN_SIZE)) - { - if (BGP_DEBUG (normal, NORMAL)) - plog_debug (peer->log, - "%s bad message length - %d for %s", - peer->host, size, - type == 128 ? "ROUTE-REFRESH" : - bgp_type_str[(int) type]); - bgp_notify_send_with_data (peer, - BGP_NOTIFY_HEADER_ERR, - BGP_NOTIFY_HEADER_BAD_MESLEN, - (u_char *) notify_data_length, 2); - goto done; - } - - /* Adjust size to message length. */ - peer->packet_size = size; - } - - ret = bgp_read_packet (peer); - if (ret < 0) - goto done; - - /* Get size and type again. */ - size = stream_getw_from (peer->ibuf, BGP_MARKER_SIZE); - type = stream_getc_from (peer->ibuf, BGP_MARKER_SIZE + 2); - - /* BGP packet dump function. */ - bgp_dump_packet (peer, type, peer->ibuf); - - size = (peer->packet_size - BGP_HEADER_SIZE); - - /* Read rest of the packet and call each sort of packet routine */ - switch (type) - { - case BGP_MSG_OPEN: - peer->open_in++; - bgp_open_receive (peer, size); /* XXX return value ignored! */ - break; - case BGP_MSG_UPDATE: - peer->readtime = time(NULL); /* Last read timer reset */ - bgp_update_receive (peer, size); - break; - case BGP_MSG_NOTIFY: - bgp_notify_receive (peer, size); - break; - case BGP_MSG_KEEPALIVE: - peer->readtime = time(NULL); /* Last read timer reset */ - bgp_keepalive_receive (peer, size); - break; - case BGP_MSG_ROUTE_REFRESH_NEW: - case BGP_MSG_ROUTE_REFRESH_OLD: - peer->refresh_in++; - bgp_route_refresh_receive (peer, size); - break; - case BGP_MSG_CAPABILITY: - peer->dynamic_cap_in++; - bgp_capability_receive (peer, size); - break; - } + int cp; - /* Clear input buffer. */ - peer->packet_size = 0; - if (peer->ibuf) - stream_reset (peer->ibuf); + /* Preserve current pointer. */ + cp = stream_get_endp (s); + stream_putw_at (s, BGP_MARKER_SIZE, cp); - done: - if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) - { - if (BGP_DEBUG (events, EVENTS)) - zlog_debug ("%s [Event] Accepting BGP peer delete", peer->host); - peer_delete (peer); - } - return 0; -} + return cp; +} ; diff --git a/bgpd/bgp_msg_write.h b/bgpd/bgp_msg_write.h index 8f0ebe31..68891a67 100644 --- a/bgpd/bgp_msg_write.h +++ b/bgpd/bgp_msg_write.h @@ -1,57 +1,55 @@ -/* BGP packet management header. - Copyright (C) 1999 Kunihiro Ishiguro - -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. */ - -#ifndef _QUAGGA_BGP_PACKET_H -#define _QUAGGA_BGP_PACKET_H - -#define BGP_NLRI_LENGTH 1U -#define BGP_TOTAL_ATTR_LEN 2U -#define BGP_UNFEASIBLE_LEN 2U -#define BGP_WRITE_PACKET_MAX 10U - -/* When to refresh */ -#define REFRESH_IMMEDIATE 1 -#define REFRESH_DEFER 2 - -/* ORF Common part flag */ -#define ORF_COMMON_PART_ADD 0x00 -#define ORF_COMMON_PART_REMOVE 0x80 -#define ORF_COMMON_PART_REMOVE_ALL 0xC0 -#define ORF_COMMON_PART_PERMIT 0x00 -#define ORF_COMMON_PART_DENY 0x20 - -/* Packet send and receive function prototypes. */ -extern int bgp_read (struct thread *); -extern int bgp_write (struct thread *); - -extern void bgp_keepalive_send (struct peer *); -extern void bgp_open_send (struct peer *); -extern void bgp_notify_send (struct peer *, u_int8_t, u_int8_t); -extern void bgp_notify_send_with_data (struct peer *, u_int8_t, u_int8_t, - u_int8_t *, size_t); -extern void bgp_route_refresh_send (struct peer *, afi_t, safi_t, u_char, u_char, int); -extern void bgp_capability_send (struct peer *, afi_t, safi_t, int, int); -extern void bgp_default_update_send (struct peer *, struct attr *, - afi_t, safi_t, struct peer *); -extern void bgp_default_withdraw_send (struct peer *, afi_t, safi_t); - -extern int bgp_capability_receive (struct peer *, bgp_size_t); - -#endif /* _QUAGGA_BGP_PACKET_H */ +/* BGP message writing -- in BGP Engine -- header + * Copyright (C) 1999 Kunihiro Ishiguro + * + * Recast for pthreaded bgpd: 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. + */ + +#ifndef _QUAGGA_BGP_MSG_WRITE_H +#define _QUAGGA_BGP_MSG_WRITE_H + +#include <stdint.h> + +#include "bgpd/bgp_common.h" +#include "bgpd/bgp_connection.h" +#include "bgpd/bgp_notification.h" +#include "bgpd/bgp_open_state.h" +#include "bgpd/bgp_route_refresh.h" + +#include "lib/stream.h" + +extern int +bgp_msg_write_notification(bgp_connection connection, bgp_notify notification) ; + +extern int +bgp_msg_send_keepalive(bgp_connection connection) ; + +extern int +bgp_msg_send_open(bgp_connection connection, bgp_open_state open_state) ; + +extern int +bgp_msg_send_route_refresh(bgp_connection connection, bgp_route_refresh rr) ; + +extern int +bgp_packet_set_marker(struct stream *s, uint8_t type) ; + +extern int +bgp_packet_set_size (struct stream *s) ; + +#endif /* _QUAGGA_BGP_MSG_WRITE_H */ diff --git a/bgpd/bgp_notification.c b/bgpd/bgp_notification.c index 3f6ecad9..bd52d516 100644 --- a/bgpd/bgp_notification.c +++ b/bgpd/bgp_notification.c @@ -21,7 +21,11 @@ * Boston, MA 02111-1307, USA. */ +#include <string.h> + +#include "lib/zassert.h" #include "lib/memory.h" + #include "bgpd/bgp_notification.h" /*============================================================================== @@ -100,7 +104,7 @@ bgp_notify_dup(bgp_notify notification) extern void bgp_notify_set(bgp_notify* p_dst, bgp_notify src) { - bgp_notify_free(*p_dst) ; + bgp_notify_free(p_dst) ; *p_dst = src ; } ; @@ -125,7 +129,7 @@ bgp_notify_set_dup(bgp_notify* p_dst, bgp_notify src) extern void bgp_notify_set_mov(bgp_notify* p_dst, bgp_notify* p_src) { - bgp_notify_free(*p_dst) ; + bgp_notify_free(p_dst) ; *p_dst = *p_src ; *p_src = NULL ; } ; @@ -145,10 +149,13 @@ bgp_notify_free(bgp_notify* p_notification) /*============================================================================== * Append data to given notification * + * Copes with zero length append. + * * NB: returns possibly NEW ADDRESS of the notification. */ extern bgp_notify -bgp_notify_append_data(bgp_notify notification, void* data, bgp_size_t len) +bgp_notify_append_data(bgp_notify notification, const void* data, + bgp_size_t len) { bgp_size_t new_length = notification->length + len ; @@ -156,12 +163,15 @@ bgp_notify_append_data(bgp_notify notification, void* data, bgp_size_t len) { bgp_size_t size = bgp_notify_size(new_length) ; notification = XREALLOC(MTYPE_BGP_NOTIFY, notification, size) ; - memset((void*)notification + notification->size, 0, + memset((char*)notification + notification->size, 0, size - notification->size) ; notification->size = size ; } ; - memcpy((void*)(notification->data) + notification->length, data, len) ; + if (len > 0) + memcpy((char*)(notification->data) + notification->length, data, len) ; notification->length = new_length ; + + return notification ; } ; diff --git a/bgpd/bgp_notification.h b/bgpd/bgp_notification.h index 72608534..b9670e8a 100644 --- a/bgpd/bgp_notification.h +++ b/bgpd/bgp_notification.h @@ -24,6 +24,7 @@ #ifndef _QUAGGA_BGP_NOTIFY_H #define _QUAGGA_BGP_NOTIFY_H +#include <stddef.h> #include "bgpd/bgp_common.h" #ifndef Inline @@ -36,111 +37,6 @@ typedef unsigned char bgp_nom_code_t ; typedef unsigned char bgp_nom_subcode_t ; -#ifndef _GMCH_BGP_H - -/* Notification Message Error Codes...........................................*/ -enum BGP_NOMC -{ - BGP_NOMC_UNDEF = 0, /* Nothing defined for this code */ - - BGP_NOMC_HEADER = 1, /* Message Header Error */ - BGP_NOMC_OPEN = 2, /* Open Message Error */ - BGP_NOMC_UPDATE = 3, /* Update Message Error */ - BGP_NOMC_HOLD_EXP = 4, /* Hold timer expired */ - BGP_NOMC_FSM = 5, /* Finite State Machine Error */ - BGP_NOMC_CEASE = 6, /* Cease RFC4486 */ - - BGP_NOMC_MAX = 6 /* max known error code */ -} ; - -/* Notification Message Error Subcodes........................................*/ - -enum BGP_NOMS -{ - BGP_NOMS_UNSPECIFIC = 0 /* If nothing else applies */ -}; - -enum BGP_NOMS_HEADER /* BGP_NOMC_HEADER subcodes */ -{ - BGP_NOMS_H_NOT_SYNC = 1, /* Connection Not Synchronised */ - /* (Marker field not all = 1,'s !) */ - BGP_NOMS_H_BAD_LEN = 2, /* Bad Message Length */ - /* DATA: the length that failed */ - BGP_NOMS_H_BAD_TYPE = 3, /* Bad Message Type */ - /* DATA: the message type objected to */ - - BGP_NOMS_H_MAX = 3, /* max known subcode */ -} ; - -enum BGP_NOMS_OPEN /* BGP_NOMC_OPEN subcodes */ -{ - BGP_NOMS_O_VERSION = 1, /* Unsupported Version Number */ - /* DATA: largest supported version */ - BGP_NOMS_O_BAD_AS = 2, /* Bad Peer AS */ - BGP_NOMS_O_BAD_ID = 3, /* Bad BGP Identifier */ - BGP_NOMS_O_OPTION = 4, /* Unsupported Optional Parameter */ - BGP_NOMS_O_AUTH = 5, /* Authentication Failure (depr.) */ - BGP_NOMS_O_H_TIME = 6, /* Unacceptable Hold Time */ - - BGP_NOMS_O_CAPABILITY = 7, /* Unsupported Capability RFC5492 */ - /* DATA: the unsupported capabilities */ - - BGP_NOMS_O_MAX = 7, /* max known subcode */ -} ; - -enum BGP_NOMS_UPDATE /* BGP_NOMC_UPDATE subcodes */ -{ - BGP_NOMS_U_A_LIST = 1, /* Malformed Attribute List */ - /* (Attribute repeated) */ - BGP_NOMS_U_UNKNOWN = 2, /* Unrecognised Well-known Attrib */ - /* DATA: erroneous attribute */ - BGP_NOMS_U_MISSING = 3, /* Missing Well-known Attrib. */ - /* DATA: type of missing attribute(s?) */ - BGP_NOMS_U_A_FLAGS = 4, /* Attribute Flags Error */ - /* DATA: erroneous attribute */ - BGP_NOMS_U_A_LENGTH = 5, /* Attribute Length Error */ - /* DATA: erroneous attribute */ - BGP_NOMS_U_ORIGIN = 6, /* Invalid Origin Attribute */ - /* DATA: erroneous attribute */ - BGP_NOMS_U_AS_LOOP = 7, /* AS Routeing Loop (deprecated) */ - BGP_NOMS_U_NEXT_HOP = 8, /* Invalid NEXT_HOP Attrib. */ - /* DATA: erroneous attribute */ - BGP_NOMS_U_OPTIONAL = 9, /* Optional Attribute Error */ - /* DATA: erroneous attribute */ - BGP_NOMS_U_NETWORK = 10, /* Invalid Network Field */ - /* (badly formed NLRI) */ - BGP_NOMS_U_AS_PATH = 11, /* Malformed AS Path */ - - BGP_NOMS_U_MAX = 11, /* max known subcode */ -} ; - -enum BGP_NOMS_HOLD_EXP /* BGP_NOMC_HOLD_EXP subcodes */ -{ - BGP_NOMS_HE_MAX = 0 /* max known subcode */ -} ; - -enum BGP_NOMC_FSM /* BGP_NOMC_FSM subcodes */ -{ - BGP_NOMS_F_MAX = 0 /* max known subcode */ -} ; - -enum BGP_NOMS_CEASE /* BGP_NOMC_CEASE subcodes RFC4486 */ -{ - BGP_NOMS_C_MAX_PREF = 1, /* Max Number of Prefixes Reached MUST */ - /* DATA: MAY be: AFI/SAFI/Upper-Bound */ - BGP_NOMS_C_SHUTDOWN = 2, /* Administrative Shutdown SHOULD */ - BGP_NOMS_C_DECONFIG = 3, /* Peer De-configured SHOULD */ - BGP_NOMS_C_RESET = 4, /* Administrative Reset SHOULD */ - BGP_NOMS_C_REJECTED = 5, /* Connection Rejected SHOULD */ - BGP_NOMS_C_CONFIG = 6, /* Other Configuration Change SHOULD */ - BGP_NOMS_C_COLLISION = 7, /* Connection Collision Res. SHOULD */ - BGP_NOMS_C_RESOURCES = 8, /* Out of Resources MAY */ - - BGP_NOMS_C_MAX = 8 /* max known subcode */ -} ; - -#endif - /*============================================================================== * */ @@ -259,7 +155,8 @@ bgp_notify_set_subcode(bgp_notify notification, bgp_nom_subcode_t subcode) } ; extern bgp_notify -bgp_notify_append_data(bgp_notify notification, void* data, bgp_size_t len) ; +bgp_notify_append_data(bgp_notify notification, const void* data, + bgp_size_t len) ; Inline bgp_nom_code_t diff --git a/bgpd/bgp_open_state.c b/bgpd/bgp_open_state.c index 4c44bbd5..55d3b345 100644 --- a/bgpd/bgp_open_state.c +++ b/bgpd/bgp_open_state.c @@ -46,12 +46,19 @@ bgp_open_state_init_new(bgp_open_state state) else memset(state, 0, sizeof(struct bgp_open_state)) ; + vector_init_new(&state->unknowns, 0) ; + return state ; } bgp_open_state bgp_open_state_free(bgp_open_state state) { + bgp_cap_unknown unknown ; + + while ((unknown = vector_ream_keep(&state->unknowns)) != NULL) + XFREE(MTYPE_TMP, unknown) ; + if (state != NULL) XFREE(MTYPE_BGP_OPEN_STATE, state) ; return NULL ; @@ -87,8 +94,10 @@ bgp_peer_open_state_init_new(bgp_open_state state, bgp_peer peer) /* Set our bgpd_id */ state->bgp_id = peer->local_id ; - /* TODO: can_capability? */ - state->can_capability = 0; + /* Do not send capability. */ /* TODO: can_capability? */ + state->can_capability = + CHECK_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN) + && (! CHECK_FLAG(peer->flags, PEER_FLAG_DONT_CAPABILITY) ) ; /* Announce self as AS4 speaker if required */ state->can_as4 = ((peer->cap & PEER_CAP_AS4_ADV) != 0) ; @@ -122,11 +131,14 @@ bgp_peer_open_state_init_new(bgp_open_state state, bgp_peer peer) ? (bgp_cap_form_old | bgp_cap_form_new) : bgp_cap_form_none ; + /* Dynamic Capabilities TODO: check requirement */ + state->can_dynamic = ( CHECK_FLAG(peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY) + != 0 ) ; + /* Graceful restart capability */ - /* TODO: check that support graceful restart for all supported AFI/SAFI */ if (bgp_flag_check(peer->bgp, BGP_FLAG_GRACEFUL_RESTART)) { - state->can_g_restart = state->can_mp_ext ; + state->can_g_restart = 1 ; state->restart_time = peer->bgp->restart_time ; } else @@ -136,12 +148,60 @@ bgp_peer_open_state_init_new(bgp_open_state state, bgp_peer peer) } ; /* TODO: check not restarting and not preserving forwarding state (?) */ - state->can_nsf = 0 ; - state->restarting = 0 ; + state->can_preserve = 0 ; /* cannot preserve forwarding */ + state->has_preserved = 0 ; /* has not preserved forwarding */ + state->restarting = 0 ; /* is not restarting */ return state; } +/*============================================================================== + * Unknown capabilities handling. + * + */ + +/*------------------------------------------------------------------------------ + * Add given unknown capability and its value to the given open_state. + */ +extern void +bgp_open_state_unknown_add(bgp_open_state state, uint8_t code, + void* value, bgp_size_t length) +{ + bgp_cap_unknown unknown ; + + unknown = XCALLOC(MTYPE_TMP, sizeof(struct bgp_cap_unknown) + length) ; + + unknown->code = code ; + unknown->length = length ; + + if (length != 0) + memcpy(unknown->value, value, length) ; + + vector_push_item(&state->unknowns, unknown) ; +} ; + +/*------------------------------------------------------------------------------ + * Get count of number of unknown capabilities in given open_state. + */ +extern void +bgp_open_state_unknown_count(bgp_open_state state) +{ + return vector_end(&state->unknowns) ; +} ; + +/*------------------------------------------------------------------------------ + * Get n'th unknown capability -- if exists. + */ +extern bgp_cap_unknown +bgp_open_state_unknown_cap(bgp_open_state state, unsigned index) +{ + return vector_get_item(&state->unknowns, index) ; +} ; + +/*============================================================================== + * + */ + /* Received an open, update the peer's state */ void bgp_peer_open_state_receive(bgp_peer peer) diff --git a/bgpd/bgp_open_state.h b/bgpd/bgp_open_state.h index 534dcadf..c67e0108 100644 --- a/bgpd/bgp_open_state.h +++ b/bgpd/bgp_open_state.h @@ -22,8 +22,11 @@ #ifndef _QUAGGA_BGP_OPEN_STATE_H #define _QUAGGA_BGP_OPEN_STATE_H -#include "bgpd/bgp_common.h" +#include <stdint.h> +#include "bgpd/bgp.h" +#include "bgpd/bgp_common.h" +#include "lib/vector.h" #ifndef Inline #define Inline static inline @@ -40,7 +43,8 @@ enum bgp_cap_form { bgp_cap_form_none = 0, bgp_cap_form_old = 1, - bgp_cap_form_new = 2 + bgp_cap_form_new = 2, + bgp_cap_form_both = 3 /* _old and _new are bits ! */ } ; /*============================================================================== @@ -51,6 +55,14 @@ enum bgp_cap_form * */ +typedef struct bgp_cap_unknown* bgp_cap_unknown ; +struct bgp_cap_unknown /* to capture unknown capability */ +{ + uint8_t code ; + bgp_size_t length ; + uint8_t value[] ; +} ; + struct bgp_open_state { as_t my_as ; /* generic ASN */ @@ -63,18 +75,22 @@ struct bgp_open_state qafx_set_t can_mp_ext ; /* will accept, may send these */ - bgp_cap_form_t can_r_refresh ; /* none/old/new */ - bgp_cap_form_t can_orf_prefix ; /* none/old/new */ + bgp_cap_form_t can_r_refresh ; /* none/old/new/both */ + bgp_cap_form_t can_orf_prefix ; /* none/old/new/both */ qafx_set_t can_orf_prefix_send ; /* wish to send ORF Prefix-List */ qafx_set_t can_orf_prefix_recv ; /* will accept ORF Prefix-List */ - qafx_set_t can_g_restart ; /* will gracefully restart these */ - qafx_set_t can_nsf ; /* will preserve forwarding state */ + int can_dynamic ; + + int can_g_restart ; /* can do graceful restart */ + qafx_set_t can_preserve ; /* can preserve forwarding for these */ + qafx_set_t has_preserved ; /* has preserved forwarding for these */ int restarting ; /* Restart State flag */ int restart_time ; /* Restart Time in seconds */ + struct vector unknowns ; /* list of bgp_cap_unknown */ } ; /*============================================================================== @@ -87,6 +103,15 @@ bgp_open_state_init_new(bgp_open_state state) ; extern bgp_open_state bgp_open_state_free(bgp_open_state state) ; +extern void +bgp_open_state_unknown_add(bgp_open_state state, uint8_t code, + void* value, bgp_size_t length) ; +extern void +bgp_open_state_unknown_count(bgp_open_state state) ; + +extern bgp_cap_unknown +bgp_open_state_unknown_cap(bgp_open_state state, unsigned index) ; + extern bgp_open_state bgp_peer_open_state_init_new(bgp_open_state state, bgp_peer peer); diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h index 8f0ebe31..8be95f40 100644 --- a/bgpd/bgp_packet.h +++ b/bgpd/bgp_packet.h @@ -28,14 +28,16 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA /* When to refresh */ #define REFRESH_IMMEDIATE 1 -#define REFRESH_DEFER 2 +#define REFRESH_DEFER 2 /* ORF Common part flag */ -#define ORF_COMMON_PART_ADD 0x00 -#define ORF_COMMON_PART_REMOVE 0x80 -#define ORF_COMMON_PART_REMOVE_ALL 0xC0 -#define ORF_COMMON_PART_PERMIT 0x00 -#define ORF_COMMON_PART_DENY 0x20 +#define ORF_COMMON_PART_ADD 0x00 +/* TODO: BUG REPORT... ORF_COMMON_PART_REMOVE should be 0x40 ! */ +#define ORF_COMMON_PART_REMOVE 0x80 +/* TODO: BUG REPORT... ORF_COMMON_PART_REMOVE_ALL should be 0x80 ! */ +#define ORF_COMMON_PART_REMOVE_ALL 0xC0 +#define ORF_COMMON_PART_PERMIT 0x00 +#define ORF_COMMON_PART_DENY 0x20 /* Packet send and receive function prototypes. */ extern int bgp_read (struct thread *); @@ -44,7 +46,7 @@ extern int bgp_write (struct thread *); extern void bgp_keepalive_send (struct peer *); extern void bgp_open_send (struct peer *); extern void bgp_notify_send (struct peer *, u_int8_t, u_int8_t); -extern void bgp_notify_send_with_data (struct peer *, u_int8_t, u_int8_t, +extern void bgp_notify_send_with_data (struct peer *, u_int8_t, u_int8_t, u_int8_t *, size_t); extern void bgp_route_refresh_send (struct peer *, afi_t, safi_t, u_char, u_char, int); extern void bgp_capability_send (struct peer *, afi_t, safi_t, int, int); diff --git a/bgpd/bgp_route_refresh.c b/bgpd/bgp_route_refresh.c new file mode 100644 index 00000000..d50f977b --- /dev/null +++ b/bgpd/bgp_route_refresh.c @@ -0,0 +1,186 @@ +/* BGP ROUTE-REFRESH and ORF handling + * Copyright (C) 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 <string.h> + +#include "lib/zassert.h" +#include "lib/memory.h" +#include "lib/vector.h" + +#include "bgpd/bgp_route_refresh.h" + +/*============================================================================== + * A bgp_route_refresh structure encapsulates the contents of a BGP + * ROUTE-REFRESH message, with or without ORF part. + * + * For incoming messages, expect a bgp_route_refresh structure to be the + * contents of a single ROUTE-REFRESH message. + * + * For outgoing messages, a bgp_route_refresh structure may contain a large + * number of ORF entries, and those are carved up into as many ROUTE-REFRESH + * messages as required. + */ + +/*============================================================================== + * Create/Destroy bgp_route_refresh + */ + +/*------------------------------------------------------------------------------ + * Allocate and initialise new bgp_route_refresh + * + * Constructs complete simple ROUTE-REFRESH -- ORF stuff can then be added. + * + * Can specify an expected number of ORF entries (need not be actual number). + */ +extern bgp_route_refresh +bgp_route_refresh_new(iAFI_t afi, iSAFI_t safi, unsigned count) +{ + bgp_route_refresh rr ; + + rr = XCALLOC(MTYPE_BGP_ROUTE_REFRESH, sizeof(struct bgp_route_refresh)) ; + + rr->afi = afi ; + rr->safi = safi ; + + vector_init_new(&rr->entries, count) ; + + /* rest of bgp_route_refresh zeroised -- not relevant when vector empty */ + + return rr ; +} ; + +/*------------------------------------------------------------------------------ + * Free bgp_route_refresh + */ +extern void +bgp_route_refresh_free(bgp_route_refresh rr) +{ + bgp_orf_entry entry ; + while((entry = vector_ream_keep(&rr->entries)) != NULL) + XFREE(MTYPE_BGP_ORF_ENTRY, entry) ; + + XFREE(MTYPE_BGP_ROUTE_REFRESH, rr) ; +} ; + +/*------------------------------------------------------------------------------ + * Allocate new bgp_orf_entry -- for known or unknown type. + * + * Is for known type if unknown_size == 0 ! + * + * Sets orf_type and unknown elements of the entry. + * + * NB: it is a FATAL error to set an unknown ORF type unless is explicitly + * unknown ! (In this context "pre-RFC" types are unknown.) + * + * Zeroises rest of structure -- in particular remove_all == false ! + * + * Pushes entry onto the bgp_route_refresh list. + */ +static bgp_orf_entry +bgp_orf_entry_new(bgp_route_refresh rr, uint8_t orf_type, size_t unknown_size) +{ + bgp_orf_entry orfe ; + size_t e_size ; + + if (unknown_size == 0) + { + if (orf_type != BGP_ORF_T_PREFIX) + zabort("unknown ORF type") ; + e_size = 0 ; + } + else + { + if (unknown_size < bgp_orf_unknown_min_l) + e_size = 0 ; + else + e_size = unknown_size - bgp_orf_unknown_min_l ; + } ; + + orfe = XCALLOC(MTYPE_BGP_ORF_ENTRY, sizeof(struct bgp_orf_entry) + e_size) ; + + orfe->orf_type = orf_type ; + orfe->unknown = (unknown_size != 0) ; + + vector_push_item(&rr->entries, orfe) ; + + return orfe ; +} ; + +/*============================================================================== + * Creating new ORF entries. + */ + +/*------------------------------------------------------------------------------ + * Add an entry for known type of ORF. + * + * Sets the common ORF entry part. + * + * Returns address of the ORF type specific structure, to be filled in. + * + * The orf_type presented must be a known BGP_ORF_T_xxx value, EXCLUDING any + * "pre-RFC" types. (Any use of pre-RFC values is looked after at the message + * level). + * + * NB: it is a FATAL error to set an unknown ORF type + */ +extern void* +bgp_orf_add(bgp_route_refresh rr, uint8_t orf_type, flag_t remove, flag_t deny) +{ + bgp_orf_entry orfe = bgp_orf_entry_new(rr, orf_type, 0) ; + + orfe->remove = (remove != 0) ; + orfe->deny = (deny != 0) ; + + return &orfe->body ; +} ; + +/*------------------------------------------------------------------------------ + * Add a remove_all entry. + * + * The orf_type presented must be a known BGP_ORF_T_xxx value, EXCLUDING any + * "pre-RFC" types. (Any use of pre-RFC values is looked after at the message + * level). + * + * NB: it is a FATAL error to set an unknown ORF type + */ +extern void +bgp_orf_add_remove_all(bgp_route_refresh rr, uint8_t orf_type) +{ + bgp_orf_entry orfe = bgp_orf_entry_new(rr, orf_type, 0) ; + + orfe->remove_all = 1 ; +} ; + +/*------------------------------------------------------------------------------ + * Add an entry for an unknown type of ORF -- copy the entry verbatim. + * + * Provided so that can capture entire contents of incoming message. + */ +extern void +bgp_orf_add_unknown(bgp_route_refresh rr, uint8_t orf_type, bgp_size_t length, + const void* entries) +{ + bgp_orf_entry orfe = bgp_orf_entry_new(rr, orf_type, + (length > 0) ? length : 1) ; + + if (length != 0) + memcpy(&orfe->body.orf_unknown.data, entries, length) ; +} ; diff --git a/bgpd/bgp_route_refresh.h b/bgpd/bgp_route_refresh.h new file mode 100644 index 00000000..8ac6059b --- /dev/null +++ b/bgpd/bgp_route_refresh.h @@ -0,0 +1,135 @@ +/* BGP ROUTE-REFRESH and ORF handling -- header + * Copyright (C) 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. + */ + +#ifndef _QUAGGA_BGP_ROUTE_REFRESH_H +#define _QUAGGA_BGP_ROUTE_REFRESH_H + +#include <stddef.h> +#include "bgpd/bgp_common.h" + +#include "lib/prefix.h" + +#ifndef Inline +#define Inline static inline +#endif + + +/*============================================================================== + * Structures to hold ROUTE-REFRESH and ORF + */ + +typedef struct bgp_orf_prefix_entry* bgp_orf_prefix_entry ; +struct bgp_orf_prefix_entry +{ + uint32_t seq ; + uint8_t min ; + uint8_t max ; + + struct prefix pfx ; +} ; + +typedef struct bgp_orf_unknown_entry* bgp_orf_unknown_entry ; +struct bgp_orf_unknown_entry +{ + bgp_size_t length ; + uint8_t data[] ; +} ; + +typedef struct bgp_orf_entry* bgp_orf_entry ; +struct bgp_orf_entry +{ + uint8_t orf_type ; /* BGP_ORF_T_xxx */ + + flag_t unknown ; /* ignore everything other than the */ + /* unknown data part */ + + flag_t remove_all ; /* rest is ignored if this is set */ + + flag_t remove ; /* otherwise: add */ + flag_t deny ; /* otherwise: permit */ + + union { /* must be last... */ + struct bgp_orf_prefix_entry orf_prefix ; + struct bgp_orf_unknown_entry orf_unknown ; /*... flexible array. */ + } body ; +} ; + +typedef struct bgp_orf_entry bgp_orf_entry_t ; /* calm down Eclipse */ +enum +{ + bgp_orf_unknown_min_l = sizeof(struct bgp_orf_entry) + - offsetof(bgp_orf_entry_t, body.orf_unknown.data) +} ; + +typedef struct bgp_route_refresh* bgp_route_refresh ; +struct bgp_route_refresh +{ + iAFI_t afi ; /* NB: Internet AFI/SAFI */ + iSAFI_t safi ; + + struct vector entries ; /* empty => simple ROUTE-REFRESH */ + + flag_t defer ; /* otherwise: immediate */ + + /* These support the output of ROUTE-REFRESH messages. + * + * These are zeroised when the bgp_route_refresh stucture is created. + */ + unsigned next ; /* next entry to process */ + uint8_t last_orf_type ; /* type of last ORF entry processed */ +} ; + +/*============================================================================== + * Prototypes + */ + +extern bgp_route_refresh +bgp_route_refresh_new(iAFI_t afi, iSAFI_t safi, unsigned count) ; + +extern void +bgp_route_refresh_set_orf_defer(bgp_route_refresh rr, flag_t defer) ; + +extern void +bgp_route_refresh_free(bgp_route_refresh rr) ; + +extern void* +bgp_orf_add(bgp_route_refresh rr, uint8_t orf_type, flag_t remove, flag_t deny); + +extern void +bgp_orf_add_remove_all(bgp_route_refresh rr, uint8_t orf_type) ; + +extern void +bgp_orf_add_unknown(bgp_route_refresh rr, uint8_t orf_type, bgp_size_t length, + const void* entries) ; + +Inline unsigned +bgp_orf_get_count(bgp_route_refresh rr) +{ + return vector_end(&rr->entries) ; +} ; + +Inline bgp_orf_entry +bgp_orf_get_entry(bgp_route_refresh rr, unsigned index) +{ + return vector_get_item(&rr->entries, index) ; +} ; + +#endif /* _QUAGGA_BGP_ROUTE_REFRESH_H */ diff --git a/bgpd/bgp_session.c b/bgpd/bgp_session.c index 214829ef..dbf6c5ca 100644 --- a/bgpd/bgp_session.c +++ b/bgpd/bgp_session.c @@ -115,6 +115,9 @@ bgp_session_init_new(bgp_session session, bgp_peer peer) * connect -- unset, false * listen -- unset, false * + * cap_override -- unset, false + * cap_strict -- unset, false + * * ttl -- unset * port -- unset * su_peer -- NULL -- none @@ -130,6 +133,7 @@ bgp_session_init_new(bgp_session session, bgp_peer peer) * keepalive_timer_interval ) * * as4 -- unset, false + * route_refresh_pre -- unset, false * * su_local -- NULL -- none * su_remote -- NULL -- none diff --git a/bgpd/bgp_session.h b/bgpd/bgp_session.h index 416a1876..b84fca04 100644 --- a/bgpd/bgp_session.h +++ b/bgpd/bgp_session.h @@ -112,7 +112,7 @@ struct bgp_session * session was ever established. */ bgp_session_state_t state ; - int made ; /* set when -> sEstablished */ + flag_t made ; /* set when -> sEstablished */ /* The BGP Engine records the last event, NOTIFICATION and errno here. * @@ -138,8 +138,11 @@ struct bgp_session /* The following are set by the Routeing Engine before a session is * enabled, and not changed at any other time by either engine. */ - int connect ; /* initiate connections */ - int listen ; /* listen for connections */ + flag_t connect ; /* initiate connections */ + flag_t listen ; /* listen for connections */ + + flag_t cap_override ; /* override ... TODO: what ? */ + flag_t cap_strict ; /* strict... TODO: what ? */ int ttl ; /* TTL to set, if not zero */ unsigned short port ; /* destination port for peer */ @@ -155,7 +158,7 @@ struct bgp_session unsigned open_hold_timer_interval ; /* These are set by the Routeing Engine before a session is enabled, - * and may be changed by the BGP Engine when the session is established. + * but are affected by the capabilities received in the OPEN message. * * The Routeing Engine may read these once sEstablished (under mutex). * @@ -164,7 +167,8 @@ struct bgp_session unsigned hold_timer_interval ; /* subject to negotiation */ unsigned keepalive_timer_interval ; /* subject to negotiation */ - int as4 ; + flag_t as4 ; /* set by OPEN */ + flag_t route_refresh_pre ; /* use pre-RFC version */ /* These are cleared by the Routeing Engine before a session is enabled, * and set by the BGP Engine when the session is established. diff --git a/lib/Makefile.am b/lib/Makefile.am index d6d3022a..c11d1959 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -31,7 +31,7 @@ pkginclude_HEADERS = \ privs.h sigevent.h pqueue.h jhash.h zassert.h memtypes.h \ workqueue.h route_types.h symtab.h heap.h \ qtime.h qpthreads.h mqueue.h qpselect.h qtimers.h qpnexus.h \ - command_queue.h qlib_init.h qafi_safi.h + command_queue.h qlib_init.h qafi_safi.h confirm.h EXTRA_DIST = regex.c regex-gnu.h memtypes.awk route_types.awk route_types.txt diff --git a/lib/confirm.h b/lib/confirm.h new file mode 100644 index 00000000..caccf742 --- /dev/null +++ b/lib/confirm.h @@ -0,0 +1,28 @@ +/* Compile time CONFIRM gizmo + * Copyright (C) 2009 Chris Hall (GMCH), Highwayman + *. + * This file is part of GNU Zebra. + * + * 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. + */ + +/*============================================================================== + * Compile time CONFIRM gizmo + * + * Two forms: CONFIRM(e) for use at top (file) level + * confirm(e) for use inside compound statements + */ +#ifndef CONFIRM + + #define CONFIRM(e) extern void CONFIRMATION(char CONFIRM[(e) ? 1 : -1]) ; + #define confirm(e) { CONFIRM(e) } + +#endif diff --git a/lib/memtypes.c b/lib/memtypes.c index 7c4c6f61..96dd8f4a 100644 --- a/lib/memtypes.c +++ b/lib/memtypes.c @@ -119,6 +119,8 @@ struct memory_list memory_list_bgp[] = { MTYPE_BGP_SESSION, "BGP session" }, { MTYPE_BGP_CONNECTION, "BGP connection" }, { MTYPE_BGP_NOTIFY, "BGP notification" }, + { MTYPE_BGP_ROUTE_REFRESH, "BGP route refresh" }, + { MTYPE_BGP_ORF_ENTRY, "BGP ORF entry" }, { MTYPE_ATTR, "BGP attribute" }, { MTYPE_ATTR_EXTRA, "BGP extra attributes" }, { MTYPE_AS_PATH, "BGP aspath" }, diff --git a/lib/stream.c b/lib/stream.c index 36bbba1e..dc636361 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -236,6 +236,20 @@ stream_set_getp (struct stream *s, size_t pos) s->getp = pos; } +void +stream_set_endp (struct stream *s, size_t pos) +{ + STREAM_VERIFY_SANE(s); + + if (!ENDP_VALID (s, pos)) + { + STREAM_BOUND_WARN (s, "set endp"); + return ; + } + + s->endp = pos; +} + /* Forward pointer. */ void stream_forward_getp (struct stream *s, size_t size) diff --git a/lib/stream.h b/lib/stream.h index 4c8a4fa9..957bf495 100644 --- a/lib/stream.h +++ b/lib/stream.h @@ -147,6 +147,7 @@ extern size_t stream_get_size (struct stream *); extern u_char *stream_get_data (struct stream *); extern void stream_set_getp (struct stream *, size_t); +extern void stream_set_endp (struct stream *, size_t); extern void stream_forward_getp (struct stream *, size_t); extern void stream_forward_endp (struct stream *, size_t); diff --git a/lib/zassert.h b/lib/zassert.h index 424688b9..a766eb7b 100644 --- a/lib/zassert.h +++ b/lib/zassert.h @@ -5,6 +5,8 @@ #ifndef _QUAGGA_ASSERT_H #define _QUAGGA_ASSERT_H +#include "confirm.h" + extern void _zlog_assert_failed (const char *assertion, const char *file, unsigned int line, const char *function) __attribute__ ((noreturn)); @@ -61,17 +63,4 @@ extern void _zlog_abort_err (const char *mess, int err, const char *file, #define zabort_err(MS, ERR) _zlog_abort_err(MS, ERR, __FILE__, __LINE__, \ __ASSERT_FUNCTION) -/*============================================================================== - * Compile time CONFIRM gizmo - * - * Two forms: CONFIRM(e) for use at top (file) level - * confirm(e) for use inside compound statements - */ -#ifndef CONFIRM - - #define CONFIRM(e) extern void CONFIRMATION(char CONFIRM[(e) ? 1 : -1]) ; - #define confirm(e) { CONFIRM(e) } - -#endif - #endif /* _QUAGGA_ASSERT_H */ |