diff options
Diffstat (limited to 'bgpd')
-rw-r--r-- | bgpd/bgp_advertise.c | 2 | ||||
-rw-r--r-- | bgpd/bgp_common.h | 2 | ||||
-rw-r--r-- | bgpd/bgp_connection.c | 309 | ||||
-rw-r--r-- | bgpd/bgp_connection.h | 101 | ||||
-rw-r--r-- | bgpd/bgp_engine.h | 27 | ||||
-rw-r--r-- | bgpd/bgp_fsm.c | 105 | ||||
-rw-r--r-- | bgpd/bgp_main.c | 54 | ||||
-rw-r--r-- | bgpd/bgp_msg_read.c | 2 | ||||
-rw-r--r-- | bgpd/bgp_msg_write.c | 74 | ||||
-rw-r--r-- | bgpd/bgp_network.c | 2 | ||||
-rw-r--r-- | bgpd/bgp_packet.c | 105 | ||||
-rw-r--r-- | bgpd/bgp_packet.h | 2 | ||||
-rw-r--r-- | bgpd/bgp_peer.c | 30 | ||||
-rw-r--r-- | bgpd/bgp_peer_index.h | 2 | ||||
-rw-r--r-- | bgpd/bgp_route.c | 85 | ||||
-rw-r--r-- | bgpd/bgp_route_refresh.h | 5 | ||||
-rw-r--r-- | bgpd/bgp_session.c | 164 | ||||
-rw-r--r-- | bgpd/bgp_session.h | 36 | ||||
-rw-r--r-- | bgpd/bgpd.c | 2 | ||||
-rw-r--r-- | bgpd/bgpd.cx | 4777 |
20 files changed, 5339 insertions, 547 deletions
diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c index 90579e7c..04f1f847 100644 --- a/bgpd/bgp_advertise.c +++ b/bgpd/bgp_advertise.c @@ -300,7 +300,7 @@ bgp_adj_out_unset (struct bgp_node *rn, struct peer *peer, struct prefix *p, bgp_advertise_fifo_add(&peer->sync[afi][safi]->withdraw, adv); /* Schedule packet write. */ - bgp_write(peer); + bgp_write(peer, NULL) ; } else { diff --git a/bgpd/bgp_common.h b/bgpd/bgp_common.h index f6f06446..19db9d8a 100644 --- a/bgpd/bgp_common.h +++ b/bgpd/bgp_common.h @@ -107,7 +107,7 @@ enum bgp_session_events bgp_session_null_event = 0, bgp_session_eEstablished, /* session state -> sEstablished */ - bgp_session_eDisabled, /* disabled by Peering Engine */ + bgp_session_eDisabled, /* disabled by Routeing Engine */ bgp_session_eStart, /* enter sConnect/sAccept from sIdle */ bgp_session_eRetry, /* loop round in sConnect/sAccept */ diff --git a/bgpd/bgp_connection.c b/bgpd/bgp_connection.c index 8320ae49..1c427318 100644 --- a/bgpd/bgp_connection.c +++ b/bgpd/bgp_connection.c @@ -93,7 +93,6 @@ static const char* bgp_connection_tags[] = static void bgp_connection_init_host(bgp_connection connection, const char* tag) ; -static void bgp_write_buffer_init_new(bgp_wbuffer wb, size_t size) ; static void bgp_write_buffer_free(bgp_wbuffer wb) ; /*------------------------------------------------------------------------------ @@ -122,7 +121,8 @@ bgp_connection_init_new(bgp_connection connection, bgp_session session, * * comatose not comatose * * next NULL -- not on the connection queue * * prev NULL -- not on the connection queue - * * post bgp_fsm_null_event + * * follow_on bgp_fsm_null_event + * * exception bgp_session_null_event * * fsm_active not active * * notification NULL -- none received or sent * * err no error, so far @@ -138,15 +138,11 @@ bgp_connection_init_new(bgp_connection connection, bgp_session session, * * msg_type none -- set when reading message * * msg_size none -- set when reading message * * notification_pending nothing pending - * * wbuff all pointers NULL -- empty buffer - * *except* must set limit so is not "full". + * * wbuff all pointers NULL -- empty but not writable */ - - confirm(bgp_fsm_sInitial == 0) ; - confirm(bgp_fsm_null_event == 0) ; - - connection->wbuff.limit = connection->wbuff.base + - bgp_write_buffer_full_threshold ; + confirm(bgp_fsm_sInitial == 0) ; + confirm(bgp_fsm_null_event == 0) ; + confirm(bgp_session_null_event == 0) ; /* Link back to session, point at its mutex and point session here */ connection->session = session ; @@ -327,8 +323,8 @@ bgp_connection_exit(bgp_connection connection) static void bgp_connection_free(bgp_connection connection) { - assert( (connection->state == bgp_fsm_sStopping) - && (connection->session == NULL) + assert( (connection->state == bgp_fsm_sStopping) + && (connection->session == NULL) && ( (connection->lock_count == 0) || (connection->lock_count == CUT_LOOSE_LOCK_COUNT) ) ) ; @@ -353,20 +349,22 @@ bgp_connection_free(bgp_connection connection) } ; /*------------------------------------------------------------------------------ - * Allocate new write buffer and initialise pointers + * If required, allocate new write buffer. + * Initialise pointers empty and writable. * - * NB: assumes structure has been zeroised by the initialisation of the - * enclosing connection. + * NB: structure was zeroised the enclosing connection was initialised. + * Buffer may have been allocated since then. */ static void -bgp_write_buffer_init_new(bgp_wbuffer wb, size_t size) +bgp_write_buffer_init(bgp_wbuffer wb, size_t size) { - assert(wb->base == NULL) ; - - wb->base = XMALLOC(MTYPE_STREAM_DATA, size) ; - wb->limit = wb->base + size ; + if (wb->base == NULL) + { + wb->base = XMALLOC(MTYPE_STREAM_DATA, size) ; + wb->limit = wb->base + size ; + } ; - wb->p_in = wb->p_out = wb->base ; + bgp_write_buffer_reset(wb) ; } ; /*------------------------------------------------------------------------------ @@ -376,7 +374,9 @@ static void bgp_write_buffer_free(bgp_wbuffer wb) { if (wb->base != NULL) - XFREE(MTYPE_STREAM_DATA, wb->base) ; + XFREE(MTYPE_STREAM_DATA, wb->base) ; /* sets wb->base = NULL */ + + wb->p_in = wb->p_out = wb->limit = wb->base; } ; /*============================================================================== @@ -449,12 +449,18 @@ bgp_connection_queue_del(bgp_connection connection) * pending queue (success) or remove connection from the pending queue. * * This is also where connections come to die. + * + * Returns: 0 => nothing to do + * 1 => dealt with one or more queued bits of work */ -extern void +extern int bgp_connection_queue_process(void) { mqueue_block mqb ; + if (bgp_connection_queue == NULL) + return 0 ; + while (bgp_connection_queue != NULL) { /* select the first in the queue, and step to the next */ @@ -486,6 +492,8 @@ bgp_connection_queue_process(void) if (mqb == mqueue_local_head(&connection->pending_queue)) bgp_connection_queue_del(connection) ; } ; + + return 1 ; } ; /*------------------------------------------------------------------------------ @@ -527,17 +535,29 @@ bgp_connection_add_pending(bgp_connection connection, mqueue_block mqb, * * Expects connection to either be newly created or recently closed. * + * For connect() connections this is done at connect() time, so before any + * connection comes up. + * + * For accept() connections this is done at accept() time, so when the + * connection comes up. + * + * The file is disabled in all modes. + * + * To complete the process must bgp_connection_start(), which resets the write + * buffer (allocating if required), and ensures that all is ready to read/write. + * * Resets: * * * closes any file that may be lingering (should never be) - * * resets all buffering (should all be empty) + * * reset all stream buffers to empty (should already be) + * * set write buffer unwritable * * Sets: * * * if secondary connection, turn off accept() - * * sets the qfile and fd ready for use + * * sets the qfile and fd ready for use -- disabled in all modes * * clears err -- must be OK so far - * * discards any open_state and notification + * * discards any open_state * * copies hold_timer_interval and keep_alive_timer_interval from session * * Expects: @@ -545,13 +565,15 @@ bgp_connection_add_pending(bgp_connection connection, mqueue_block mqb, * * links to/from session to be set up (including ordinal) * * timers to be initialised * * log and host to be set up - * * buffers to exist + * * stream buffers to exist * * Does not touch: * - * * state of the connection (including post event) + * * state of the connection (including exception and follow-on event) * * timers -- FSM looks after those * + * NB: nothing can be written until bgp_connection_start() has been called. + * * NB: requires the session to be LOCKED. */ extern void @@ -580,7 +602,65 @@ bgp_connection_open(bgp_connection connection, int fd) } ; /*------------------------------------------------------------------------------ + * Start connection which has just come up -- connect() or accept() + * + * Copy the local and remote addresses and note the effective address family. + * + * Make sure now have a write buffer, and set it empty and writable. + */ +extern void +bgp_connection_start(bgp_connection connection, union sockunion* su_local, + union sockunion* su_remote) +{ + sockunion_set_dup(&connection->su_local, su_local) ; + sockunion_set_dup(&connection->su_remote, su_remote) ; + + connection->paf = sockunion_family(connection->su_local) ; + + bgp_write_buffer_init(&connection->wbuff, bgp_wbuff_size) ; +} ; + +/*------------------------------------------------------------------------------ + * Stop connection + * + * * reset stream buffers + * * empty out any pending queue + * * remove from the BGP Engine connection queue, if there + * * clear session->active flag, so will not process any more messages + * that expect some message to be sent. + * * no notification pending (yet) + * + * If required: + * + * * set write buffer unwritable + * * disable file in write mode + * + * NB: requires the session to be LOCKED. + */ +static void +bgp_connection_stop(bgp_connection connection, int stop_writer) +{ + /* Reset all stream buffering empty. */ + stream_reset(connection->ibuf) ; + stream_reset(connection->obuf) ; + + connection->read_pending = 0 ; + connection->read_header = 0 ; + connection->notification_pending = 0 ; + + /* Empty out the pending queue and remove from connection queue */ + mqueue_local_reset_keep(&connection->pending_queue) ; + bgp_connection_queue_del(connection) ; + + /* If required: set write buffer *unwritable* (and empty). */ + if (stop_writer) + bgp_write_buffer_unwritable(&connection->wbuff) ; +} ; + +/*------------------------------------------------------------------------------ * Enable connection for accept() + * + * NB: requires the session to be LOCKED. */ extern void bgp_connection_enable_accept(bgp_connection connection) @@ -590,6 +670,8 @@ bgp_connection_enable_accept(bgp_connection connection) /*------------------------------------------------------------------------------ * Disable connection for accept() -- assuming still have session ! + * + * NB: requires the session to be LOCKED. */ extern void bgp_connection_disable_accept(bgp_connection connection) @@ -605,7 +687,8 @@ bgp_connection_disable_accept(bgp_connection connection) * * if there is an fd, close it * * if qfile is active, remove it * * forget any addresses - * * reset all buffering to empty + * * reset all stream buffers to empty + * * reset write buffer to unwritable * * empties the pending queue -- destroying all messages * * * for secondary connection: disable accept @@ -630,6 +713,8 @@ bgp_connection_disable_accept(bgp_connection connection) * * bgp_connection_free() -- to finally discard * * * bgp_connection_full_close() -- can do this again + * + * NB: requires the session to be LOCKED. */ extern void bgp_connection_full_close(bgp_connection connection, int unset_timers) @@ -658,20 +743,8 @@ bgp_connection_full_close(bgp_connection connection, int unset_timers) sockunion_unset(&connection->su_local) ; sockunion_unset(&connection->su_remote) ; - /* Reset all buffering empty. */ - stream_reset(connection->ibuf) ; - stream_reset(connection->obuf) ; - - connection->read_pending = 0 ; - connection->read_header = 0 ; - connection->notification_pending = 0 ; - - connection->wbuff.p_in = connection->wbuff.base ; - connection->wbuff.p_out = connection->wbuff.base ; - - /* Empty out the pending queue and remove from connection queue */ - mqueue_local_reset_keep(&connection->pending_queue) ; - bgp_connection_queue_del(connection) ; + /* Bring connection to a stop. */ + bgp_connection_stop(connection, 1) ; } ; /*------------------------------------------------------------------------------ @@ -691,34 +764,39 @@ bgp_connection_full_close(bgp_connection connection, int unset_timers) * be written (at least as far as the write buffer). * * Everything else is left untouched. + * + * Returns: 1 => OK, ready to send NOTIFICATION now + * 0 => no file descriptor => no chance of sending NOTIFICATION + * + * NB: requires the session to be LOCKED. */ -extern void +extern int bgp_connection_part_close(bgp_connection connection) { + bgp_session session = connection->session ; bgp_wbuffer wb = &connection->wbuff ; int fd ; uint8_t* p ; bgp_size_t mlen ; - /* close the qfile and any associate file descriptor */ + /* Check that have a usable file descriptor */ fd = qps_file_fd(&connection->qf) ; - if (fd != fd_undef) - { - shutdown(fd, SHUT_RD) ; - qps_disable_modes(&connection->qf, qps_read_mbit) ; - } ; - /* Reset all input buffering. */ - stream_reset(connection->ibuf) ; + if (fd == fd_undef) + return 0 ; - connection->read_pending = 0 ; - connection->read_header = 0 ; + /* Shutdown the read side of this connection */ + shutdown(fd, SHUT_RD) ; + qps_disable_modes(&connection->qf, qps_read_mbit) ; - /* Reset obuf and purge wbuff. */ - stream_reset(connection->obuf) ; + /* Stop all buffering activity, except for write buffer. */ + bgp_connection_stop(connection, 0) ; - connection->notification_pending = 0 ; + /* Turn off session->active (if still attached). */ + if (session != NULL) + session->active = 0 ; + /* Purge wbuff of all but current partly written message (if any) */ if (wb->p_in != wb->p_out) /* will be equal if buffer is empty */ { passert(wb->p_out < wb->p_in) ; @@ -739,62 +817,49 @@ bgp_connection_part_close(bgp_connection connection) wb->p_in = wb->base + mlen ; } else - wb->p_in = wb->p_out = wb->base ; + bgp_write_buffer_reset(wb) ; - /* Empty out the pending queue and remove from connection queue */ - mqueue_local_reset_keep(&connection->pending_queue) ; - bgp_connection_queue_del(connection) ; + /* OK -- part closed, ready to send NOTIFICATION */ + return 1 ; } ; /*============================================================================== * Writing to BGP connection -- once TCP connection has come up. * - * All writing is done by preparing a BGP message in the "obuf" buffer, - * and then calling bgp_connection_write(). + * Nothing is written directly -- all writing is qpselect driven. * - * If possible, that is written away immediately. If not, then no further - * messages may be prepared until the buffer has been cleared. - * - * Write the contents of the "work" buffer. + * All writing is done by preparing a BGP message in a stream buffer, + * and then calling bgp_connection_write(). The contents of the stream buffer + * are transferred to the connection's write buffer. * * Returns true <=> able to write the entire buffer without blocking. */ -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 given stream, if possible - * - * Writes everything or nothing. + * Write the contents of the given stream * - * 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. + * Writes everything or FATAL error. * - * In case it is relevant, identifies when the data has been written all the - * way into the TCP buffer. + * Returns: 1 => written to wbuff -- stream reset, empty * - * 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 + * NB: actual I/O occurs in the qpselect action function -- so this cannot + * fail ! */ extern int 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, s) ; - } ; - - /* Write nothing if cannot write everything */ + /* FATAL error if cannot write everything. */ if (bgp_write_buffer_cannot(wb, stream_pending(s))) - return 0 ; + zabort("Write buffer does not have enough room") ; + + /* If buffer is empty, enable write mode */ + if (bgp_write_buffer_empty(wb)) + qps_enable_mode(&connection->qf, qps_write_mnum, + bgp_connection_write_action) ; /* Transfer the obuf contents to the write buffer. */ wb->p_in = stream_transfer(wb->p_in, s, wb->limit) ; @@ -803,71 +868,19 @@ bgp_connection_write(bgp_connection connection, struct stream* s) } ; /*------------------------------------------------------------------------------ - * The write buffer is empty -- so try to write stream directly. - * - * 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 stream is cleared and can be reused (unless failed). - * - * 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_MSG_MAX_L * 10 } ; - -static int -bgp_connection_write_direct(bgp_connection connection, struct stream* s) -{ - int ret ; - - ret = stream_flush_try(s, qps_file_fd(&connection->qf)) ; - - if (ret == 0) - return 2 ; /* Done: wbuff and stream are empty */ - - else if (ret > 0) - { - bgp_wbuffer wb = &connection->wbuff ; - - /* Partial write -- set up buffering, if required. */ - if (wb->base == NULL) - bgp_write_buffer_init_new(wb, bgp_wbuff_size) ; - - /* Transfer *entire* message to staging buffer */ - wb->p_in = stream_transfer(wb->base, s, wb->limit) ; - - wb->p_out = wb->p_in - ret ; /* output from here */ - - /* Must now be enabled to write */ - qps_enable_mode(&connection->qf, qps_write_mnum, - bgp_connection_write_action) ; - - return 1 ; /* Done: wbuff is not empty -- stream is */ - } ; - - /* write failed -- signal error and return failed */ - bgp_fsm_io_error(connection, errno) ; - - return -1 ; -} ; - -/*------------------------------------------------------------------------------ * Write Action for bgp connection. * * Empty the write buffer if we can. * * If empties that, disable write mode, then: * - * -- if notification is pending, then generate a notification sent event + * -- if notification is pending, generate a notification sent event * * -- otherwise: place connection on the connection queue, so can start to * flush out anything on the connection's pending queue. * - * If empty out everything, disable write mode. - * - * If encounter an error, generate TCP_fatal_error event. + * If encounter an error, generate TCP_fatal_error event, forcing buffer + * empty but unwritable. */ static void bgp_connection_write_action(qps_file qf, void* file_info) @@ -894,14 +907,16 @@ bgp_connection_write_action(qps_file qf, void* file_info) continue ; if ((ret != EAGAIN) && (ret != EWOULDBLOCK)) - bgp_fsm_io_error(connection, errno) ; - + { + bgp_write_buffer_unwritable(wb) ; + bgp_fsm_io_error(connection, errno) ; + } ; return ; } ; } ; /* Buffer is empty -- reset it and disable write mode */ - wb->p_out = wb->p_in = wb->base ; + bgp_write_buffer_reset(wb) ; qps_disable_modes(&connection->qf, qps_write_mbit) ; diff --git a/bgpd/bgp_connection.h b/bgpd/bgp_connection.h index 054cd953..d50d2985 100644 --- a/bgpd/bgp_connection.h +++ b/bgpd/bgp_connection.h @@ -90,31 +90,24 @@ enum bgp_fsm_events } ; /*============================================================================== - * BGP Connection Structure + * BGP Connection Structures * - * The BGP Connection is the main data structure for the BGP Engine. + *------------------------------------------------------------------------------ + * Write buffer for connection. * - * When a session terminates, or a connection is shut it may have a short - * independent life, if a NOTIFICATION message is pending. + * NB: when connection is initialised all the pointers are set NULL. * - */ - -/* Write buffer for connection. + * The buffer is not allocated until the TCP connection comes up. * * NB: p_out == p_in => buffer is empty * - * BUT: buffer is not allocated until required, and until then - * p_out == p_in == NULL -- empty does NOT imply usable ! + * BUT: p_out == limit => buffer is not writable. * - * AND: when buffer is emptied, p_out and p_in will be some way down the - * buffer. + * When connection is first initialised all pointers are NULL, so the + * buffer is "empty but not writable". * - * SO: before writing, check for base != NULL and set p_out = p_in = base. - * - * NB: before buffer is allocated base == NULL, but limit is set to NULL + n, - * so that buffer does not appear full. - * - * SO: not full does NOT imply that p_out/p_in/base are set, either ! + * When connection is opened, closed or fails, buffer is set into this + * "empty but not writable" state. */ typedef struct bgp_wbuffer* bgp_wbuffer ; struct bgp_wbuffer @@ -126,7 +119,17 @@ struct bgp_wbuffer uint8_t* limit ; } ; +/* Buffer is allocated for a number of maximum size BGP messages. */ +enum { bgp_wbuff_size = BGP_MSG_MAX_L * 10 } ; +/*============================================================================== + * BGP Connection Structure + * + * The BGP Connection is the main data structure for the BGP Engine. + * + * When a session terminates, or a connection is shut it may have a short + * independent life, if a NOTIFICATION message is pending. + */ struct bgp_connection { bgp_session session ; /* session connection belongs to */ @@ -147,7 +150,7 @@ struct bgp_connection int fsm_active ; /* active in FSM counter */ bgp_fsm_event_t follow_on ; /* event raised within FSM */ - bgp_session_event_t except ; /* exception posted here */ + bgp_session_event_t exception; /* exception posted here */ bgp_notify notification ; /* if any sent/received */ int err ; /* erno, if any */ @@ -201,6 +204,9 @@ extern void bgp_connection_open(bgp_connection connection, int fd) ; extern void +bgp_connection_start(bgp_connection connection, union sockunion* su_local, + union sockunion* su_remote) ; +extern void bgp_connection_enable_accept(bgp_connection connection) ; extern void @@ -218,7 +224,7 @@ bgp_connection_full_close(bgp_connection connection, int unset_timers) ; #define bgp_connection_close(conn) bgp_connection_full_close(conn, 0) #define bgp_connection_close_down(conn) bgp_connection_full_close(conn, 1) -extern void +extern int bgp_connection_part_close(bgp_connection connection) ; extern void @@ -236,7 +242,7 @@ bgp_connection_queue_add(bgp_connection connection) ; extern void bgp_connection_queue_del(bgp_connection connection) ; -extern void +extern int bgp_connection_queue_process(void) ; Inline int @@ -251,12 +257,28 @@ bgp_connection_add_pending(bgp_connection connection, mqueue_block mqb, bgp_connection* is_pending) ; /*------------------------------------------------------------------------------ + * Set buffer *unwritable* (buffer appears full, but nothing pending). + */ +Inline void +bgp_write_buffer_unwritable(bgp_wbuffer wb) +{ + wb->p_in = wb->p_out = wb->limit ; +} ; + +/*------------------------------------------------------------------------------ + * If allocated: set buffer empty + * If unallocated: buffer remains *unwritable* + */ +Inline void +bgp_write_buffer_reset(bgp_wbuffer wb) +{ + wb->p_in = wb->p_out = wb->base ; +} ; + +/*------------------------------------------------------------------------------ * See if do NOT have enough room for what want to write PLUS 1. * - * NB: before using the buffer the caller MUST ensure it has been allocated. - * - * Unallocated buffer is made to appear to have room for one maximum - * size BGP message. + * NB: there is never any room in an unallocated buffer. */ Inline int bgp_write_buffer_cannot(bgp_wbuffer wb, size_t want) @@ -267,30 +289,35 @@ bgp_write_buffer_cannot(bgp_wbuffer wb, size_t want) /*------------------------------------------------------------------------------ * Full if NOT enough room for a maximum size BGP message + 1 * - * NB: this will be FALSE if the buffer has not been allocated -- because can - * allocate a buffer and proceed if required. + * NB: there is never any room in an unallocated buffer. */ enum { bgp_write_buffer_full_threshold = BGP_MSG_MAX_L + 1 } ; Inline int -bgp_write_buffer_full(bgp_wbuffer wb) +bgp_write_buffer_cannot_max(bgp_wbuffer wb) { return bgp_write_buffer_cannot(wb, BGP_MSG_MAX_L) ; } ; /*------------------------------------------------------------------------------ - * Empty if in and out pointers are equal. - * - * NB: buffer is empty if it has not yet been allocated. - * - * NOT empty => allocated. + * See if buffer has anything in it. * - * NB: empty does NOT imply that both pointers are at the start of the buffer. + * If empty, ensures that the buffer has been allocated, and sets the pointers + * to the start of the buffer -- so all set to go. */ Inline int bgp_write_buffer_empty(bgp_wbuffer wb) { - return (wb->p_out == wb->p_in) ; + if (wb->p_out < wb->p_in) + return 0 ; /* not empty => has buffer */ + + dassert(wb->p_out == wb->p_in) ; + + passert(wb->base != NULL) ; /* must have buffer */ + + bgp_write_buffer_reset(wb) ; /* pointers to start of buffer */ + + return 1 ; /* empty and all ready to go */ } ; /*------------------------------------------------------------------------------ @@ -299,8 +326,6 @@ bgp_write_buffer_empty(bgp_wbuffer wb) * NB: if returns 0, may not yet have been allocated. * * > 0 => allocated. - * - * NB: 0 does NOT imply that both pointers are at the start of the buffer. */ Inline int bgp_write_buffer_pending(bgp_wbuffer wb) @@ -313,9 +338,9 @@ bgp_write_buffer_pending(bgp_wbuffer wb) * As above, for connection */ Inline int -bgp_connection_write_full(bgp_connection connection) +bgp_connection_write_cannot_max(bgp_connection connection) { - return bgp_write_buffer_full(&connection->wbuff) ; + return bgp_write_buffer_cannot_max(&connection->wbuff) ; } ; /*------------------------------------------------------------------------------ diff --git a/bgpd/bgp_engine.h b/bgpd/bgp_engine.h index fdbcef70..3a751885 100644 --- a/bgpd/bgp_engine.h +++ b/bgpd/bgp_engine.h @@ -51,7 +51,7 @@ struct queue_stats } ; static struct queue_stats bgp_engine_queue_stats ; -static struct queue_stats peering_engine_queue_stats ; +static struct queue_stats routing_engine_queue_stats ; Inline void bgp_queue_logging(const char* name, mqueue_queue mq, struct queue_stats* stats) @@ -62,6 +62,8 @@ bgp_queue_logging(const char* name, mqueue_queue mq, struct queue_stats* stats) ++stats->count ; + qpt_mutex_lock(&mq->mutex) ; + if (mq->count > stats->max) stats->max = mq->count ; if (mq->count > stats->recent) @@ -70,7 +72,10 @@ bgp_queue_logging(const char* name, mqueue_queue mq, struct queue_stats* stats) stats->total += mq->count ; if (stats->count < 1000) - return ; + { + qpt_mutex_unlock(&mq->mutex) ; + return ; + } ; my_count = 0 ; @@ -83,6 +88,8 @@ bgp_queue_logging(const char* name, mqueue_queue mq, struct queue_stats* stats) assert(my_count == mq->count) ; + qpt_mutex_unlock(&mq->mutex) ; + average = stats->total ; average /= stats->count ; @@ -121,24 +128,24 @@ bgp_to_bgp_engine_priority(mqueue_block mqb) * */ -/* Send given message to the Peering Engine -- ordinary +/* Send given message to the Routing Engine -- ordinary */ Inline void -bgp_to_peering_engine(mqueue_block mqb) +bgp_to_routing_engine(mqueue_block mqb) { mqueue_enqueue(routing_nexus->queue, mqb, 0) ; - bgp_queue_logging("Peering Engine", routing_nexus->queue, - &peering_engine_queue_stats) ; + bgp_queue_logging("Routing Engine", routing_nexus->queue, + &routing_engine_queue_stats) ; } ; -/* Send given message to the Peering Engine -- priority +/* Send given message to the Routing Engine -- priority */ Inline void -bgp_to_peering_engine_priority(mqueue_block mqb) +bgp_to_routing_engine_priority(mqueue_block mqb) { mqueue_enqueue(routing_nexus->queue, mqb, 1) ; - bgp_queue_logging("Peering Engine", routing_nexus->queue, - &peering_engine_queue_stats) ; + bgp_queue_logging("Routing Engine", routing_nexus->queue, + &routing_engine_queue_stats) ; } ; #endif /* QUAGGA_BGP_ENGINE_H */ diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 8a8be52d..77afa12f 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -57,7 +57,7 @@ * * In general the FSM manages connections, but there is some interaction with * the session. In particular, exceptions are expressed as session_eXXX - * values -- which are passed to the Peering Engine as session events. The + * values -- which are passed to the Routing Engine as session events. The * handling of FSM events is depends mostly on the FSM state, but any * exception influences that too. * @@ -273,7 +273,7 @@ * notification -- any NOTIFICATION message * err -- any I/O or other error * - * on exit from the FSM this information is passed to the Peering Engine. + * on exit from the FSM this information is passed to the Routing Engine. * * Can throw exceptions within the FSM, as discussed above. * @@ -446,7 +446,7 @@ bgp_fsm_enable_session(bgp_session session) * */ static void -bgp_fsm_throw(bgp_connection connection, bgp_session_event_t except, +bgp_fsm_throw(bgp_connection connection, bgp_session_event_t exception, bgp_notify notification, int err, bgp_fsm_event_t event) ; static bgp_fsm_state_t @@ -548,10 +548,10 @@ bgp_fsm_disable_session(bgp_session session, bgp_notify notification) * fsm_active/follow_on mechanism looks after this. */ extern void -bgp_fsm_exception(bgp_connection connection, bgp_session_event_t except, +bgp_fsm_exception(bgp_connection connection, bgp_session_event_t exception, bgp_notify notification) { - bgp_fsm_throw(connection, except, notification, 0, bgp_fsm_eBGP_Stop) ; + bgp_fsm_throw(connection, exception, notification, 0, bgp_fsm_eBGP_Stop) ; } ; /*------------------------------------------------------------------------------ @@ -559,7 +559,7 @@ bgp_fsm_exception(bgp_connection connection, bgp_session_event_t except, * * A connection will discard any sibling if: * - * * the session is being disabled (by the Peering Engine) + * * the session is being disabled (by the Routing Engine) * * * an invalid event is bringing down the session * @@ -655,7 +655,7 @@ bgp_fsm_io_error(bgp_connection connection, int err) * This is used by the connect() and accept() qpselect actions. It is also * used if a connect() attempt fails immediately. * - * If err == 0, then all is well: copy the local and remote sockunions + * If err == 0, then all is well: start the connection (can now write to it) * and generate TCP_connection_open event * * If err is one of: @@ -666,6 +666,9 @@ bgp_fsm_io_error(bgp_connection connection, int err) * these errors.) * * Other errors are reported as TCP_fatal_error. + * + * NB: in any case on entry to this function the file is *disabled* in all + * modes. */ extern void bgp_fsm_connect_completed(bgp_connection connection, int err, @@ -674,12 +677,8 @@ bgp_fsm_connect_completed(bgp_connection connection, int err, { if (err == 0) { + bgp_connection_start(connection, su_local, su_remote) ; bgp_fsm_event(connection, bgp_fsm_eTCP_connection_open) ; - - sockunion_set_dup(&connection->su_local, su_local) ; - sockunion_set_dup(&connection->su_remote, su_remote) ; - - connection->paf = sockunion_family(connection->su_local) ; } else if ( (err == ECONNREFUSED) || (err == ECONNRESET) @@ -697,12 +696,12 @@ bgp_fsm_connect_completed(bgp_connection connection, int err, * NB: takes responsibility for the notification structure. */ static void -bgp_fsm_throw(bgp_connection connection, bgp_session_event_t except, +bgp_fsm_throw(bgp_connection connection, bgp_session_event_t exception, bgp_notify notification, int err, bgp_fsm_event_t event) { - connection->except = except ; + connection->exception = exception ; bgp_notify_set(&connection->notification, notification) ; - connection->err = err ; + connection->err = err ; bgp_fsm_event(connection, event) ; } ; @@ -721,10 +720,10 @@ bgp_fsm_throw(bgp_connection connection, bgp_session_event_t except, * NB: takes responsibility for the notification structure. */ static bgp_fsm_state_t -bgp_fsm_throw_stop(bgp_connection connection, bgp_session_event_t except, +bgp_fsm_throw_stop(bgp_connection connection, bgp_session_event_t exception, bgp_notify notification) { - bgp_fsm_throw(connection, except, notification, 0, bgp_fsm_eBGP_Stop) ; + bgp_fsm_throw(connection, exception, notification, 0, bgp_fsm_eBGP_Stop) ; return connection->state ; } ; @@ -1477,7 +1476,7 @@ bgp_fsm_event(bgp_connection connection, bgp_fsm_event_t event) } while (--connection->fsm_active != 0) ; /* If required, post session event. */ - if (connection->except != bgp_session_null_event) + if (connection->exception != bgp_session_null_event) { int stopped = (connection->state == bgp_fsm_sStopping) ; int has_session = (connection->session != NULL) ; @@ -1488,16 +1487,16 @@ bgp_fsm_event(bgp_connection connection, bgp_fsm_event_t event) * connection->state will be Stopping is when the session is being * stopped. (eDiscard and eCollision go quietly to Stopping !) */ - if ((connection->except <= bgp_session_max_event) && has_session) - bgp_session_event(connection->session, connection->except, + if ((connection->exception <= bgp_session_max_event) && has_session) + bgp_session_event(connection->session, connection->exception, bgp_notify_take(&connection->notification), connection->err, connection->ordinal, stopped) ; /* Tidy up -- notification already cleared */ - connection->except = bgp_session_null_event ; - connection->err = 0 ; + connection->exception = bgp_session_null_event ; + connection->err = 0 ; bgp_notify_unset(&connection->notification) ; /* if any */ if (stopped && has_session) @@ -1552,12 +1551,12 @@ static bgp_fsm_action(bgp_fsm_enter) */ static bgp_fsm_action(bgp_fsm_stop) { - if (connection->except == bgp_session_null_event) + if (connection->exception == bgp_session_null_event) return bgp_fsm_invalid(connection, bgp_fsm_sStopping, event) ; - if ( (connection->except == bgp_session_eDisabled) - || (connection->except == bgp_session_eDiscard) - || (connection->except == bgp_session_eInvalid) ) + if ( (connection->exception == bgp_session_eDisabled) + || (connection->exception == bgp_session_eDiscard) + || (connection->exception == bgp_session_eInvalid) ) next_state = bgp_fsm_sStopping ; return bgp_fsm_catch(connection, next_state) ; @@ -1590,7 +1589,7 @@ static bgp_fsm_action(bgp_fsm_invalid) * * Enters either sConnect or sActive, depending on primary/secondary. * - * Throws a session_eStart exception so the Peering Engine gets to see this, + * Throws a session_eStart exception so the Routing Engine gets to see this, * and a follow-on fsm_eBGP_Start event to kick the connect() or accept() into * life. * @@ -2029,12 +2028,12 @@ bgp_fsm_catch(bgp_connection connection, bgp_fsm_state_t next_state) { bgp_notify send_notification ; - assert(connection->except != bgp_session_null_event) ; + assert(connection->exception != bgp_session_null_event) ; /* Have a notification to send iff not just received one, and is in a * suitable state to send one at all. */ - if (connection->except == bgp_session_eNOM_recv) + if (connection->exception == bgp_session_eNOM_recv) send_notification = NULL ; else { @@ -2046,54 +2045,36 @@ bgp_fsm_catch(bgp_connection connection, bgp_fsm_state_t next_state) send_notification = connection->notification ; } ; - /* If there is a NOTIFICATION to send, now is the time to do that. + /* If there is a NOTIFICATION to send, send it if possible. * Otherwise, close the connection but leave the timers. * * The state transition stuff looks after timers. In particular an error * in Connect/Active states leaves the ConnectRetryTimer running. */ - if (send_notification != NULL) + if ((send_notification != NULL) && bgp_connection_part_close(connection)) { - int ret ; - /* If not changing to stopping, we hold in the current state until * the NOTIFICATION process is complete. */ if (next_state != bgp_fsm_sStopping) next_state = connection->state ; - /* Close for reading and flush write buffers. */ - bgp_connection_part_close(connection) ; - + /* Make sure that cannot pop out a Keepalive ! */ qtimer_unset(&connection->keepalive_timer) ; - /* Write the message + /* Write the message */ + bgp_msg_write_notification(connection, send_notification) ; + + /* notification is sitting in the write buffer + * + * notification_pending is set, so write action will raise the required + * event in due course. * - * 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. + * Set the HoldTimer to something suitable. Don't really expect this + * to happen in anything except sEstablished state -- but copes. (Is + * ready to wait 20 seconds in sStopping state and 5 otherwise.) */ - ret = bgp_msg_write_notification(connection, send_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_notification_sent(connection) ; - - else if (ret == 0) - /* notification is sitting in the write buffer - * - * notification_pending is set, so 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 sEstablished state -- but copes. (Is - * ready to wait 20 seconds in sStopping state and 5 otherwise.) - */ - bgp_hold_timer_set(connection, + bgp_hold_timer_set(connection, (next_state == bgp_fsm_sStopping) ? 20 : 5) ; } else @@ -2106,7 +2087,7 @@ bgp_fsm_catch(bgp_connection connection, bgp_fsm_state_t next_state) /* If sStopping and not eDiscard, do in any sibling */ if ( (next_state == bgp_fsm_sStopping) - && (connection->except != bgp_session_eDiscard) ) + && (connection->exception != bgp_session_eDiscard) ) { bgp_connection sibling ; diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 94f8c7e5..3c6d70aa 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -37,6 +37,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "plist.h" #include "qpnexus.h" #include "qlib_init.h" +#include "thread.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_attr.h" @@ -84,8 +85,8 @@ void sigusr2 (void); static void bgp_exit (int); static void init_second_stage(int pthreads); static void bgp_in_thread_init(void); -static qtime_mono_t routing_event_hook(enum qpn_priority priority); -static qtime_mono_t bgp_event_hook(enum qpn_priority priority); +static int routing_foreground(void); +static int routing_background(void); static void sighup_action(mqueue_block mqb, mqb_flag_t flag); static void sighup_enqueue(void); static void sigterm_action(mqueue_block mqb, mqb_flag_t flag); @@ -129,7 +130,6 @@ char *config_file = NULL; /* Have we done the second stage initialization? */ static int done_2nd_state_init = 0; - /* Process ID saved for use by init system */ static const char *pid_file = PATH_BGPD_PID; @@ -390,26 +390,32 @@ init_second_stage(int pthreads) /* if using pthreads create additional nexus */ if (qpthreads_enabled) { - bgp_nexus = qpn_init_new(bgp_nexus, 0); + bgp_nexus = qpn_init_new(bgp_nexus, 0); routing_nexus = qpn_init_new(routing_nexus, 0); } else { /* we all share the single nexus and single thread */ - bgp_nexus = cli_nexus; + bgp_nexus = cli_nexus; routing_nexus = cli_nexus; } + /* Tell thread stuff to use this qtimer pile */ + thread_set_qtimer_pile(routing_nexus->pile) ; + /* Nexus hooks. * Beware if !qpthreads_enabled then there is only 1 nexus object - * with all nexus pointers being aliases for it. So only one routine - * per hook for *all* nexus. + * with all nexus pointers being aliases for it. */ - bgp_nexus->in_thread_init = bgp_in_thread_init; - bgp_nexus->in_thread_final = bgp_close_listeners; - routing_nexus->event_hook[0] = routing_event_hook; - bgp_nexus->event_hook[1] = bgp_event_hook; - confirm(NUM_EVENT_HOOK >= 2); + bgp_nexus->in_thread_init = bgp_in_thread_init ; + bgp_nexus->in_thread_final = bgp_close_listeners ; + + qpn_add_hook_function(&routing_nexus->foreground, routing_foreground) ; + qpn_add_hook_function(&bgp_nexus->foreground, bgp_connection_queue_process) ; + + qpn_add_hook_function(&routing_nexus->background, routing_background) ; + + confirm(qpn_hooks_max >= 2) ; /* vty and zclient can use either nexus or threads. * For bgp client we always want nexus, regardless of pthreads. @@ -616,26 +622,18 @@ bgp_in_thread_init(void) bgp_open_listeners(bm->port, bm->address); } -/* legacy threads */ -static qtime_mono_t -routing_event_hook(enum qpn_priority priority) +/* legacy threads in routing engine */ +static int +routing_foreground(void) { - struct thread thread; - qtime_mono_t event_wait; - - while (thread_fetch_event (priority, master, &thread, &event_wait)) - thread_call (&thread); - - return event_wait; + return thread_dispatch(master) ; } -/* BGP local queued events */ -static qtime_mono_t -bgp_event_hook(enum qpn_priority priority) +/* background threads in routing engine */ +static int +routing_background(void) { - if (priority >= qpn_pri_fourth) - bgp_connection_queue_process(); - return 0; + return thread_dispatch_background(master) ; } /* SIGINT/TERM SIGHUP need to tell routing engine what to do */ diff --git a/bgpd/bgp_msg_read.c b/bgpd/bgp_msg_read.c index 48227364..b218c5cf 100644 --- a/bgpd/bgp_msg_read.c +++ b/bgpd/bgp_msg_read.c @@ -1373,7 +1373,7 @@ bgp_msg_update_receive (bgp_connection connection, bgp_size_t body_size) ++connection->session->stats.update_in ; connection->session->stats.update_time = time(NULL) ; - /* PRO TEM: pass raw update message across to Peering Engine */ + /* PRO TEM: pass raw update message across to Routing Engine */ /* TODO: decode update messages in the BGP Engine. */ bgp_session_update_recv(connection->session, connection->ibuf, body_size); } diff --git a/bgpd/bgp_msg_write.c b/bgpd/bgp_msg_write.c index ef843157..d61ba642 100644 --- a/bgpd/bgp_msg_write.c +++ b/bgpd/bgp_msg_write.c @@ -72,12 +72,12 @@ /*------------------------------------------------------------------------------ * Make NOTIFICATION message and dispatch. * - * NB: the write buffers will have been flushed -- so expect success ! + * NB: the write buffers MUST have been flushed -- so demand success ! * - * Returns: 2 => written to TCP -- it's gone - * 1 => written to wbuff -- waiting for socket - * 0 => nothing written -- insufficient space in wbuff - * -1 => failed -- error event generated + * Returns: 1 => written to wbuff -- qpselect will write from there + * + * NB: actual I/O occurs in the qpselect action function -- so this cannot + * fail ! * * NB: requires the session LOCKED -- connection-wise */ @@ -143,7 +143,12 @@ bgp_msg_write_notification(bgp_connection connection, bgp_notify notification) bgp_notify_free(text_form) ; } ; - /* Finally -- write the obuf away */ + /* Set flag so that write_action raises required event when buffer becomes + * empty. + */ + connection->notification_pending = 1 ; + + /* Finally -- write the obuf away */ return bgp_connection_write(connection, s) ; } ; @@ -156,10 +161,11 @@ bgp_msg_write_notification(bgp_connection connection, bgp_notify notification) * 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 -- insufficient space in wbuff - * -1 => failed -- error event generated + * Returns: 1 => written to wbuff -- qpselect will write from there + * 0 => nothing written -- no need, buffer not empty ! + * + * NB: actual I/O occurs in the qpselect action function -- so this cannot + * fail ! * * NB: requires the session LOCKED -- connection-wise */ @@ -208,10 +214,10 @@ bgp_open_capability_orf (struct stream *s, iAFI_t afi, iSAFI_t safi, * 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 + * Returns: 1 => written to wbuff -- qpselect will write from there + * + * NB: actual I/O occurs in the qpselect action function -- so this cannot + * fail ! * * NB: requires the session LOCKED -- connection-wise */ @@ -221,8 +227,6 @@ bgp_msg_send_open(bgp_connection connection, bgp_open_state open_state) struct stream *s = connection->obuf ; int length ; - assert(bgp_connection_write_empty(connection)) ; - ++connection->session->stats.open_out ; /* Make OPEN message header */ @@ -262,7 +266,7 @@ bgp_msg_send_open(bgp_connection connection, bgp_open_state open_state) /* Finally -- write the obuf away */ return bgp_connection_write(connection, s) ; -} +} ; enum { @@ -488,9 +492,11 @@ bgp_msg_orf_prefix(struct stream* s, uint8_t common, * * Supports the status quo, only Address-Prefix ORF. * - * Returns: > 0 => all written - * 0 => unable to write everything - * < 0 => failed -- error event generated + * Returns: 1 => written to wbuff -- qpselect will write from there + * 0 => nothing written -- insufficient space in wbuff + * + * NB: actual I/O occurs in the qpselect action function -- so this cannot + * fail ! * * NB: requires the session LOCKED -- connection-wise */ @@ -501,7 +507,6 @@ bgp_msg_send_route_refresh(bgp_connection connection, bgp_route_refresh rr) uint8_t msg_type ; flag_t done ; bgp_size_t msg_len ; - int ret ; ++connection->session->stats.refresh_out ; @@ -512,7 +517,7 @@ bgp_msg_send_route_refresh(bgp_connection connection, bgp_route_refresh rr) do { - if (bgp_connection_write_full(connection)) + if (bgp_connection_write_cannot_max(connection)) return 0 ; /* Construct BGP message header for new/old form ROUTE-REFRESH */ @@ -534,10 +539,7 @@ bgp_msg_send_route_refresh(bgp_connection connection, bgp_route_refresh rr) zlog_debug ("%s sending REFRESH_REQ for afi/safi: %d/%d length %d", connection->host, rr->afi, rr->safi, msg_len) ; - ret = bgp_connection_write(connection, s) ; - if (ret < 0) - return ret ; - + bgp_connection_write(connection, s) ; } while (!done) ; return done ; @@ -779,17 +781,18 @@ bgp_msg_orf_prefix(struct stream* s, uint8_t common, /*------------------------------------------------------------------------------ * Make UPDATE message and dispatch. * - * Returns: 2 => written to TCP -- it's gone - * 1 => written to wbuff -- waiting for socket + * Returns: 1 => written to wbuff -- qpselect will write from there * 0 => nothing written -- insufficient space in wbuff - * -1 => failed -- error event generated + * + * NB: actual I/O occurs in the qpselect action function -- so this cannot + * fail ! * * NB: requires the session LOCKED -- connection-wise */ extern int bgp_msg_send_update(bgp_connection connection, struct stream* s) { - if (bgp_connection_write_full(connection)) + if (bgp_connection_write_cannot_max(connection)) return 0 ; ++connection->session->stats.update_out ; @@ -804,19 +807,18 @@ bgp_msg_send_update(bgp_connection connection, struct stream* s) /*------------------------------------------------------------------------------ * Make End-of-RIB message and dispatch. * - * - * - * Returns: 2 => written to TCP -- it's gone - * 1 => written to wbuff -- waiting for socket + * Returns: 1 => written to wbuff -- qpselect will write from there * 0 => nothing written -- insufficient space in wbuff - * -1 => failed -- error event generated + * + * NB: actual I/O occurs in the qpselect action function -- so this cannot + * fail ! */ extern int bgp_msg_send_end_of_rib(bgp_connection connection, iAFI_t afi, iSAFI_t safi) { struct stream *s = connection->obuf ; - if (bgp_connection_write_full(connection)) + if (bgp_connection_write_cannot_max(connection)) return 0 ; /* Make UPDATE message header */ diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 0c1072c9..fa1dbd37 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -479,7 +479,7 @@ bgp_accept_action(qps_file qf, void* file_info) * This is running in the BGP Engine thread, so cannot in any case be * foxed by the other connection making changes. * - * The session is active, so the Peering Engine will not make any changes + * The session is active, so the Routing Engine will not make any changes * except under the mutex, and will not destroy the session. */ diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index d565c265..8955be3b 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -87,21 +87,6 @@ bgp_packet_set_size (struct stream *s) return cp; } -/* Add new packet to the peer. */ -static void -bgp_packet_add (struct peer *peer, struct stream *s) -{ - /* Add packet to the end of list. */ - stream_fifo_push (peer->obuf, s); -} - -/* Free first packet. */ -static void -bgp_packet_delete (struct peer *peer) -{ - stream_free (stream_fifo_pop (peer->obuf)); -} - #if 0 /* Check file descriptor whether connect is established. */ static void @@ -437,8 +422,7 @@ bgp_default_update_send (struct peer *peer, struct attr *attr, #endif /* DEBUG */ /* Add packet to the peer. */ - bgp_packet_add (peer, stream_dup (s)); - bgp_write(peer); + bgp_write(peer, s); } /*------------------------------------------------------------------------------ @@ -513,8 +497,7 @@ bgp_default_withdraw_send (struct peer *peer, afi_t afi, safi_t safi) bgp_packet_set_size (s); /* Add packet to the peer. */ - bgp_packet_add (peer, stream_dup (s)); - bgp_write(peer); + bgp_write(peer, s); } /*------------------------------------------------------------------------------ @@ -616,69 +599,35 @@ bgp_write_proceed (struct peer *peer) /*------------------------------------------------------------------------------ * Write packets to the peer -- subject to the XON flow control. * - * Empties the obuf queue first. + * Takes an optional stream argument, if not NULL then must be peer->work, + * in which there is a message to be sent. * * Then processes the peer->sync structure to generate further updates. * * TODO: work out how bgp_routeadv_timer fits into this. */ int -bgp_write (bgp_peer peer) +bgp_write (bgp_peer peer, struct stream* s) { - u_char type; - struct stream *s; - int free_s ; + if (s != NULL) + stream_fifo_push(peer->obuf, stream_dup(s)) ; while (bgp_session_is_XON(peer)) { - free_s = 0 ; - - s = stream_fifo_head(peer->obuf) ; /* returns own stream */ - if (s != NULL) - free_s = 1 ; - else - { - s = bgp_write_packet(peer); /* uses peer->work */ - if (s == NULL) - break; - } ; - - bgp_session_update_send(peer->session, s); - - /* Retrieve BGP packet type. */ - stream_set_getp (s, BGP_MARKER_SIZE + 2); - type = stream_getc (s); + s = bgp_write_packet(peer); /* uses peer->work */ + if (s == NULL) + break; - switch (type) - { - case BGP_MSG_OPEN: - break; - case BGP_MSG_UPDATE: - break; - case BGP_MSG_NOTIFY: - /* Double start timer. */ - peer->v_start *= 2; - - /* Overflow check. */ - if (peer->v_start >= (60 * 2)) - peer->v_start = (60 * 2); - - assert(0); /* shouldn't get notifies through here */ - return 0; - case BGP_MSG_KEEPALIVE: - break; - case BGP_MSG_ROUTE_REFRESH_NEW: - case BGP_MSG_ROUTE_REFRESH_OLD: - break; - case BGP_MSG_CAPABILITY: - break; - } + stream_fifo_push (peer->obuf, stream_dup(s)) ; - /* OK we sent packet so delete it. */ - if (free_s) - bgp_packet_delete (peer); + /* Count down flow control, send fifo if hits BGP_XON_KICK */ + if (bgp_session_dec_flow_count(peer)) + bgp_session_update_send(peer->session, peer->obuf) ; + } ; - } + /* In any case, send what's in the FIFO */ + if (stream_fifo_head(peer->obuf) != NULL) + bgp_session_update_send(peer->session, peer->obuf) ; return 0; } @@ -842,7 +791,7 @@ bgp_notify_send_with_data (struct peer *peer, u_char code, u_char sub_code, peer->last_reset = PEER_DOWN_NOTIFY_SEND; } - bgp_peer_disable(peer, notification); + bgp_peer_disable(peer, notification); } /* Send BGP notify packet. */ @@ -1032,14 +981,14 @@ 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; - s = stream_new (BGP_MAX_PACKET_SIZE); + s = peer->work; + stream_reset (s); /* Make BGP update packet. */ bgp_packet_set_marker (s, BGP_MSG_CAPABILITY); @@ -1063,18 +1012,12 @@ bgp_capability_send (struct peer *peer, afi_t afi, safi_t safi, /* Set packet size. */ length = bgp_packet_set_size (s); - /* Make real packet. */ - packet = stream_dup (s); - stream_free (s); - - /* 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); + peer->host, BGP_MSG_CAPABILITY, length); - bgp_write(peer); + /* Add packet to the peer. */ + bgp_write(peer, s); } #if 0 diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h index 81937522..f0798846 100644 --- a/bgpd/bgp_packet.h +++ b/bgpd/bgp_packet.h @@ -44,7 +44,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA /* Packet send and receive function prototypes. */ extern int bgp_read (struct thread *); -extern int bgp_write (bgp_peer peer); +extern int bgp_write (bgp_peer peer, struct stream*); extern void bgp_keepalive_send (struct peer *); extern void bgp_open_send (struct peer *); diff --git a/bgpd/bgp_peer.c b/bgpd/bgp_peer.c index e6be06c7..16ec8bd9 100644 --- a/bgpd/bgp_peer.c +++ b/bgpd/bgp_peer.c @@ -202,6 +202,7 @@ bgp_session_has_established(bgp_peer peer) bgp_notify_unset(&(peer->session->notification)); /* Clear start timer value to default. */ + /* TODO: figure out where to increase the IdleHoldTimer */ peer->v_start = BGP_INIT_START_TIMER; /* Increment established count. */ @@ -547,7 +548,9 @@ bgp_timer_set (struct peer *peer) static int bgp_routeadv_timer (struct thread *thread) { - struct peer *peer; + struct peer *peer; + uint32_t jittered ; + uint32_t jitter ; peer = THREAD_ARG (thread); peer->t_routeadv = NULL; @@ -559,10 +562,21 @@ bgp_routeadv_timer (struct thread *thread) peer->synctime = time (NULL); - bgp_write(peer); + bgp_write(peer, NULL); + + /* Apply +/- 10% jitter to the route advertise timer. + * + * The time is in seconds, so for anything less than 10 seconds this forced + * to be +/- 1 second. + */ + jittered = jitter = peer->v_routeadv ; + if (jitter < 10) + jitter = 10 ; + jittered = (jittered * 90) + (rand() % (jitter * 20)) ; /* jitter is +/-10% */ + jittered = (jittered + 50) / 100 ; - BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer, - peer->v_routeadv); + /* TODO: move this to the Routeing Engine qtimer pile. */ + BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer, jittered) ; return 0; } @@ -1023,7 +1037,15 @@ void bgp_peer_disable(bgp_peer peer, bgp_notify notification) { if (bgp_session_is_active(peer->session)) + { + /* This code has been moved from where it was, in bgp_write */ + /* TODO: not clear whether v_start handling is still correct */ + peer->v_start *= 2; + if (peer->v_start >= (60 * 2)) + peer->v_start = (60 * 2); + bgp_session_disable(peer, notification); + } else { bgp_notify_free(notification) ; diff --git a/bgpd/bgp_peer_index.h b/bgpd/bgp_peer_index.h index 38d70907..c99ec710 100644 --- a/bgpd/bgp_peer_index.h +++ b/bgpd/bgp_peer_index.h @@ -40,7 +40,7 @@ typedef unsigned bgp_peer_id_t ; struct bgp_peer_index_entry { - bgp_peer peer ; /* used by Peering Engine */ + bgp_peer peer ; /* used by Routing Engine */ /* The accept pointer is used by the listening socket(s) to find the * session when it is prepared to accept a connection. diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 7d3ad901..c5191b18 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -1457,10 +1457,12 @@ struct bgp_process_queue safi_t safi; }; +WQ_ARGS_SIZE_OK(bgp_process_queue) ; + static wq_item_status -bgp_process_rsclient (struct work_queue *wq, void *data) +bgp_process_rsclient (struct work_queue *wq, work_queue_item item) { - struct bgp_process_queue *pq = data; + struct bgp_process_queue *pq = work_queue_item_args(item) ; struct bgp *bgp = pq->bgp; struct bgp_node *rn = pq->rn; afi_t afi = pq->afi; @@ -1518,9 +1520,9 @@ bgp_process_rsclient (struct work_queue *wq, void *data) } static wq_item_status -bgp_process_main (struct work_queue *wq, void *data) +bgp_process_main (struct work_queue *wq, work_queue_item item) { - struct bgp_process_queue *pq = data; + struct bgp_process_queue *pq = work_queue_item_args(item) ; struct bgp *bgp = pq->bgp; struct bgp_node *rn = pq->rn; afi_t afi = pq->afi; @@ -1592,15 +1594,14 @@ bgp_process_main (struct work_queue *wq, void *data) } static void -bgp_processq_del (struct work_queue *wq, void *data) +bgp_processq_del (struct work_queue *wq, work_queue_item item) { - struct bgp_process_queue *pq = data; + struct bgp_process_queue *pq = work_queue_item_args(item); struct bgp_table *table = pq->rn->table; bgp_unlock (pq->bgp); bgp_unlock_node (pq->rn); bgp_table_unlock (table); - XFREE (MTYPE_BGP_PROCESS_QUEUE, pq); } static void @@ -1617,21 +1618,23 @@ bgp_process_queue_init (void) exit (1); } - bm->process_main_queue->spec.workfunc = &bgp_process_main; - bm->process_rsclient_queue->spec.workfunc = &bgp_process_rsclient; - bm->process_main_queue->spec.del_item_data = &bgp_processq_del; - bm->process_rsclient_queue->spec.del_item_data - = bm->process_main_queue->spec.del_item_data; - bm->process_main_queue->spec.max_retries - = bm->process_main_queue->spec.max_retries = 0; - bm->process_rsclient_queue->spec.hold - = bm->process_main_queue->spec.hold = 50; + bm->process_main_queue->spec.data = bm->master ; + bm->process_main_queue->spec.errorfunc = NULL ; + bm->process_main_queue->spec.workfunc = &bgp_process_main; + bm->process_main_queue->spec.del_item_data = &bgp_processq_del; + bm->process_main_queue->spec.completion_func = NULL ; + bm->process_main_queue->spec.max_retries = 0; + bm->process_main_queue->spec.hold = 50; + + bm->process_rsclient_queue->spec = bm->process_main_queue->spec ; + bm->process_rsclient_queue->spec.workfunc = &bgp_process_rsclient; } void bgp_process (struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi) { struct bgp_process_queue *pqnode; + struct work_queue* wq ; /* already scheduled for processing? */ if (CHECK_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED)) @@ -1641,29 +1644,31 @@ bgp_process (struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi) (bm->process_rsclient_queue == NULL) ) bgp_process_queue_init (); - pqnode = XCALLOC (MTYPE_BGP_PROCESS_QUEUE, - sizeof (struct bgp_process_queue)); - if (!pqnode) - return; - - /* all unlocked in bgp_processq_del */ - bgp_table_lock (rn->table); - pqnode->rn = bgp_lock_node (rn); - pqnode->bgp = bgp; - bgp_lock (bgp); - pqnode->afi = afi; - pqnode->safi = safi; - switch (rn->table->type) { case BGP_TABLE_MAIN: - work_queue_add (bm->process_main_queue, pqnode); + wq = bm->process_main_queue ; break; case BGP_TABLE_RSCLIENT: - work_queue_add (bm->process_rsclient_queue, pqnode); + wq = bm->process_rsclient_queue ; break; + default: + zabort("invalid rn->table->type") ; } + pqnode = work_queue_item_add(wq); + + if (!pqnode) + return; + + /* all unlocked in bgp_processq_del */ + bgp_table_lock (rn->table); + pqnode->rn = bgp_lock_node (rn); + pqnode->bgp = bgp; + bgp_lock (bgp); + pqnode->afi = afi; + pqnode->safi = safi; + return; } @@ -2672,17 +2677,18 @@ bgp_soft_reconfig_in (struct peer *peer, afi_t afi, safi_t safi) bgp_soft_reconfig_table (peer, afi, safi, table); } - struct bgp_clear_node_queue { struct bgp_node *rn; enum bgp_clear_route_type purpose; }; +WQ_ARGS_SIZE_OK(bgp_clear_node_queue) ; + static wq_item_status -bgp_clear_route_node (struct work_queue *wq, void *data) +bgp_clear_route_node (struct work_queue *wq, work_queue_item item) { - struct bgp_clear_node_queue *cnq = data; + struct bgp_clear_node_queue *cnq = work_queue_item_args(item) ; struct bgp_node *rn = cnq->rn; struct peer *peer = wq->spec.data; struct bgp_info *ri; @@ -2708,15 +2714,14 @@ bgp_clear_route_node (struct work_queue *wq, void *data) } static void -bgp_clear_node_queue_del (struct work_queue *wq, void *data) +bgp_clear_node_queue_del (struct work_queue *wq, work_queue_item item) { - struct bgp_clear_node_queue *cnq = data; + struct bgp_clear_node_queue *cnq = work_queue_item_args(item) ; struct bgp_node *rn = cnq->rn; struct bgp_table *table = rn->table; bgp_unlock_node (rn); bgp_table_unlock (table); - XFREE (MTYPE_BGP_CLEAR_NODE_QUEUE, cnq); } static void @@ -2823,11 +2828,9 @@ bgp_clear_route_table (struct peer *peer, afi_t afi, safi_t safi, /* both unlocked in bgp_clear_node_queue_del */ bgp_table_lock (rn->table); bgp_lock_node (rn); - cnq = XCALLOC (MTYPE_BGP_CLEAR_NODE_QUEUE, - sizeof (struct bgp_clear_node_queue)); - cnq->rn = rn; + cnq = work_queue_item_add(peer->clear_node_queue) ; + cnq->rn = rn; cnq->purpose = purpose; - work_queue_add (peer->clear_node_queue, cnq); break; } diff --git a/bgpd/bgp_route_refresh.h b/bgpd/bgp_route_refresh.h index b6e5eaf5..3afd997e 100644 --- a/bgpd/bgp_route_refresh.h +++ b/bgpd/bgp_route_refresh.h @@ -65,7 +65,10 @@ struct bgp_orf_entry } body ; } ; -typedef struct bgp_orf_entry bgp_orf_entry_t ; /* calm down Eclipse */ +/* (The typedef is required to stop Eclipse (3.4.2 with CDT 5.0) whining + * about first argument of offsetof().) + */ +typedef struct bgp_orf_entry bgp_orf_entry_t ; enum { bgp_orf_unknown_min_l = sizeof(struct bgp_orf_entry) diff --git a/bgpd/bgp_session.c b/bgpd/bgp_session.c index a2b49da5..9d17e36c 100644 --- a/bgpd/bgp_session.c +++ b/bgpd/bgp_session.c @@ -80,10 +80,10 @@ static void bgp_session_do_route_refresh_recv(mqueue_block mqb, mqb_flag_t flag) * change any shared item in the session, except under the mutex. And * even then it may make no sense ! * - * NB: a session reaches eDisabled when the Peering Engine has sent a disable + * NB: a session reaches eDisabled when the Routing Engine has sent a disable * request to the BGP Engine, AND an eDisabled event has come back. * - * While the Peering Engine is waiting for the eDisabled event, the session + * While the Routing Engine is waiting for the eDisabled event, the session * is in sLimping state. * * The BGP Engine's primary interest is in its (private) bgp_connection @@ -212,7 +212,7 @@ bgp_session_free(bgp_session session) } /*============================================================================== - * Peering Engine: enable session for given peer -- allocate if required. + * Routing Engine: enable session for given peer -- allocate if required. * * Sets up the session given the current state of the peer. If the state * changes, then need to disable the session and re-enable it again with new @@ -226,12 +226,12 @@ bgp_session_enable(bgp_peer peer) /* Set up session if required. Check session if already exists. * - * Only the Peering Engine creates sessions, so it is safe to pick up the + * Only the Routing Engine creates sessions, so it is safe to pick up the * peer->session pointer and test it. * * If session exists, it MUST be inactive. * - * Peering Engine does not require the mutex while the session is inactive. + * Routing Engine does not require the mutex while the session is inactive. */ session = peer->session ; @@ -348,7 +348,7 @@ bgp_session_do_enable(mqueue_block mqb, mqb_flag_t flag) } ; /*============================================================================== - * Peering Engine: disable session for given peer -- if enabled (!). + * Routing Engine: disable session for given peer -- if enabled (!). * * Passes any bgp_notify to the BGP Engine, which will dispose of it in due * course. @@ -398,7 +398,7 @@ bgp_session_disable(bgp_peer peer, bgp_notify notification) * * the disable is being issued in response to a stopped event from * the BGP Engine. * - * * the session is stopped, but the message to the Peering Engine is + * * the session is stopped, but the message to the Routing Engine is * still in its message queue. * * * the session is stopped while the disable message is in the @@ -410,11 +410,11 @@ bgp_session_disable(bgp_peer peer, bgp_notify notification) * * NB: The BGP Engine will discard any outstanding work for the session. * - * The Peering Engine should discard all further messages for this + * The Routing Engine should discard all further messages for this * session up to the eDisabled, and must then discard any other * messages for the session. * - * NB: the Peering Engine MUST not issue any further messages until it sees + * NB: the Routing Engine MUST not issue any further messages until it sees * the returned eDisabled event. */ mqb = mqb_init_new(NULL, bgp_session_do_disable, session) ; @@ -433,7 +433,6 @@ bgp_session_disable(bgp_peer peer, bgp_notify notification) c = 0 ; s = 0 ; } ; - fprintf(stderr, " session disable %d/%d", c, s) ; } ; ++bgp_engine_queue_stats.event ; @@ -469,7 +468,7 @@ bgp_session_do_disable(mqueue_block mqb, mqb_flag_t flag) /*============================================================================== * BGP Engine: send session event signal to Routeing Engine * - * NB: is passing responsibility for the notification to the Peering Engine. + * NB: is passing responsibility for the notification to the Routing Engine. */ extern void bgp_session_event(bgp_session session, bgp_session_event_t event, @@ -494,21 +493,20 @@ bgp_session_event(bgp_session session, bgp_session_event_t event, args->ordinal = ordinal ; args->stopped = stopped, - ++peering_engine_queue_stats.event ; + ++routing_engine_queue_stats.event ; - bgp_to_peering_engine(mqb) ; -} + bgp_to_routing_engine(mqb) ; +} ; /*============================================================================== - * Peering Engine: dispatch update to peer -> BGP Engine + * Routing Engine: dispatch update(s) to peer -> BGP Engine * - * PRO TEM -- this is being passed the pre-packaged BGP message. + * PRO TEM -- this is being passed the pre-packaged BGP message(s). * - * The BGP Engine takes care of discarding the stream block once it's been - * dealt with. + * The BGP Engine takes care of discarding the stream block(s) once dealt with. */ extern void -bgp_session_update_send(bgp_session session, struct stream* upd) +bgp_session_update_send(bgp_session session, struct stream_fifo* fifo) { struct bgp_session_update_args* args ; mqueue_block mqb ; @@ -516,37 +514,38 @@ bgp_session_update_send(bgp_session session, struct stream* upd) mqb = mqb_init_new(NULL, bgp_session_do_update_send, session) ; args = mqb_get_args(mqb) ; - args->buf = stream_dup(upd) ; + args->buf = stream_fifo_head(fifo) ; args->is_pending = NULL ; - args->xon_kick = (session->flow_control == BGP_XON_KICK); - session->flow_control--; + args->xon_kick = (session->flow_control == BGP_XON_KICK); ++bgp_engine_queue_stats.update ; bgp_to_bgp_engine(mqb) ; + + stream_fifo_reset(fifo) ; } ; /*------------------------------------------------------------------------------ - * BGP Engine: write given BGP update message -- mqb action function. + * BGP Engine: write given BGP update message(s) -- mqb action function. * * Each connection has a pending queue associated with it, onto which messages * are put if the connection's write buffer is unable to absorb any further * messages. * - * This function is called both when the mqb is received from the Peering + * This function is called both when the mqb is received from the Routing * Engine, and when the BGP Engine is trying to empty the connection's pending * queue. * - * When the mqb is received from the Peering Engine, then: + * When the mqb is received from the Routing Engine, then: * - * -- if the connection's pending queue is empty, try to send the message. + * -- if the connection's pending queue is empty, try to send the message(s). * * When the mqb is from connection's pending queue, then: * - * -- try to send the message. + * -- try to send the message(s). * - * In any case, if cannot send the message (and not encountered any error), add - * it (back) to the connection's pending queue. + * In any case, if cannot send all the message(s), add it (back) to the + * connection's pending queue. * * If the mqb has been dealt with, it is freed, along with the stream buffer. * Also, update the flow control counter, and issue XON if required. @@ -557,43 +556,54 @@ bgp_session_do_update_send(mqueue_block mqb, mqb_flag_t flag) struct bgp_session_update_args* args = mqb_get_args(mqb) ; bgp_session session = mqb_get_arg0(mqb) ; - if ((flag == mqb_action) && session->active) + while (args->buf != NULL) { - bgp_connection connection = session->connections[bgp_connection_primary] ; - assert(connection != NULL) ; + struct stream* buf ; - /* If established, try and send. */ - if (connection->state == bgp_fsm_sEstablished) + if ((flag == mqb_action) && session->active) { - int ret = bgp_connection_no_pending(connection, &args->is_pending) ; + bgp_connection connection ; - if (ret != 0) - ret = bgp_msg_send_update(connection, args->buf) ; + connection = session->connections[bgp_connection_primary] ; + assert(connection != NULL) ; - if (ret == 0) - { - /* Either there is already a pending queue, or the message - * could not be sent (and has not failed) -- so add to the - * pending queue. - */ - bgp_connection_add_pending(connection, mqb, &args->is_pending) ; - return ; /* Quit now, with message intact. */ - } - else if (ret > 0) + /* If established, try and send. */ + if (connection->state == bgp_fsm_sEstablished) { - /* Successfully wrote the message. XON if requested */ - if (args->xon_kick) - bgp_session_XON(session); + int ret ; + ret = bgp_connection_no_pending(connection, &args->is_pending) ; + + if (ret != 0) + ret = bgp_msg_send_update(connection, args->buf) ; + + if (ret == 0) + { + /* Either there is already a pending queue, or the message + * could not be sent (and has not failed) -- so add to the + * pending queue. + */ + bgp_connection_add_pending(connection, mqb, + &args->is_pending) ; + return ; /* Quit now, with message intact. */ + } } ; } ; + + buf = args->buf ; + args->buf = buf->next ; + + stream_free(buf) ; } ; - stream_free(args->buf) ; + /* If gets to here, then has dealt with all message(s). */ + if ((flag == mqb_action) && (args->xon_kick)) + bgp_session_XON(session) ; + mqb_free(mqb) ; } ; /*------------------------------------------------------------------------------ - * Peering Engine: are we in XON state ? + * Routing Engine: are we in XON state ? */ extern int bgp_session_is_XON(bgp_peer peer) @@ -606,8 +616,20 @@ bgp_session_is_XON(bgp_peer peer) return result; } ; +/*------------------------------------------------------------------------------ + * Count down flow control -- signal if reached XON point. + */ +extern int +bgp_session_dec_flow_count(bgp_peer peer) +{ + bgp_session session = peer->session; + + assert(session->flow_control > 0) ; + return (--session->flow_control == BGP_XON_KICK) ; +} ; + /*============================================================================== - * Peering Engine: dispatch Route Refresh to peer -> BGP Engine + * Routing Engine: dispatch Route Refresh to peer -> BGP Engine * * The BGP Engine takes care of discarding the bgp_route_refresh once it's been * dealt with. @@ -671,7 +693,7 @@ bgp_session_do_route_refresh_send(mqueue_block mqb, mqb_flag_t flag) } ; /*============================================================================== - * Peering Engine: dispatch End-of-RIB to peer -> BGP Engine + * Routing Engine: dispatch End-of-RIB to peer -> BGP Engine */ extern void bgp_session_end_of_rib_send(bgp_session session, qAFI_t afi, qSAFI_t safi) @@ -736,11 +758,11 @@ bgp_session_do_end_of_rib_send(mqueue_block mqb, mqb_flag_t flag) } ; /*============================================================================== - * BGP Engine: forward incoming update -> Peering Engine + * BGP Engine: forward incoming update -> Routing Engine * * PRO TEM -- this is being passed the raw BGP message. * - * The Peering Engine takes care of discarding the stream block once it's been + * The Routing Engine takes care of discarding the stream block once it's been * dealt with. */ extern void @@ -756,13 +778,13 @@ bgp_session_update_recv(bgp_session session, struct stream* buf, bgp_size_t size args->size = size; args->xon_kick = 0; - ++peering_engine_queue_stats.update ; + ++routing_engine_queue_stats.update ; - bgp_to_peering_engine(mqb) ; + bgp_to_routing_engine(mqb) ; } /*------------------------------------------------------------------------------ - * Peering Engine: process incoming update message -- mqb action function. + * Routing Engine: process incoming update message -- mqb action function. */ static void bgp_session_do_update_recv(mqueue_block mqb, mqb_flag_t flag) @@ -787,7 +809,7 @@ bgp_session_do_update_recv(mqueue_block mqb, mqb_flag_t flag) /*============================================================================== * BGP Engine: received Route Refresh to peer * - * The Peering Engine takes care of discarding the bgp_route_refresh once + * The Routing Engine takes care of discarding the bgp_route_refresh once * it's been dealt with. */ extern void @@ -802,11 +824,11 @@ bgp_session_route_refresh_recv(bgp_session session, bgp_route_refresh rr) args->rr = rr ; args->is_pending = NULL ; - bgp_to_peering_engine(mqb) ; + bgp_to_routing_engine(mqb) ; } ; /*------------------------------------------------------------------------------ - * Peering Engine: receive given BGP route refresh message -- mqb action + * Routing Engine: receive given BGP route refresh message -- mqb action * function. */ static void @@ -823,7 +845,7 @@ bgp_session_do_route_refresh_recv(mqueue_block mqb, mqb_flag_t flag) } /*============================================================================== - * BGP Engine: send XON message to Peering Engine + * BGP Engine: send XON message to Routing Engine * * Can be sent more packets now */ @@ -836,13 +858,13 @@ bgp_session_XON(bgp_session session) confirm(sizeof(struct bgp_session_XON_args) == 0) ; - ++peering_engine_queue_stats.xon ; + ++routing_engine_queue_stats.xon ; - bgp_to_peering_engine(mqb) ; + bgp_to_routing_engine(mqb) ; } /*------------------------------------------------------------------------------ - * Peering Engine: process incoming XON message -- mqb action function. + * Routing Engine: process incoming XON message -- mqb action function. */ static void bgp_session_do_XON(mqueue_block mqb, mqb_flag_t flag) @@ -854,14 +876,14 @@ bgp_session_do_XON(mqueue_block mqb, mqb_flag_t flag) int xoff = (session->flow_control <= 0); session->flow_control = BGP_XON_REFRESH; if (xoff) - bgp_write (session->peer) ; + bgp_write (session->peer, NULL) ; } mqb_free(mqb) ; } /*============================================================================== - * Peering Engine: send set ttl message to BGP Engine + * Routing Engine: send set ttl message to BGP Engine * */ void @@ -918,7 +940,7 @@ bgp_session_do_set_ttl(mqueue_block mqb, mqb_flag_t flag) * pointer is NULL -- this is largely paranoia, but it would be a grave * mistake for the listening socket(s) to find a session which is not active ! * - * NB: accessing Peering Engine "private" variable -- no lock required. + * NB: accessing Routing Engine "private" variable -- no lock required. * * accessing index_entry when not active -- no lock required. */ @@ -943,13 +965,13 @@ bgp_session_is_active(bgp_session session) } ; /*------------------------------------------------------------------------------ - * Peering Engine: if session is limping we defer re-enabling the session + * Routing Engine: if session is limping we defer re-enabling the session * until it is disabled. * * returns 1 if limping and defer * returns 0 if not limping * - * NB: accessing Peering Engine "private" variable -- no lock required. + * NB: accessing Routing Engine "private" variable -- no lock required. */ static int bgp_session_defer_if_limping(bgp_session session) diff --git a/bgpd/bgp_session.h b/bgpd/bgp_session.h index 5af81688..5b144db1 100644 --- a/bgpd/bgp_session.h +++ b/bgpd/bgp_session.h @@ -59,7 +59,7 @@ * For simplicity, the BGP Engine may lock the session associated with the * connection it is dealing with. * - * Parts of the session structure are private to the Peering Engine, and + * Parts of the session structure are private to the Routing Engine, and * do not require the mutex for access. * * NB: the connections associated with a BGP session are private to the BGP @@ -99,34 +99,31 @@ struct bgp_session /* While sIdle and sStopped: * - * the session belongs to the Peering Engine. + * the session belongs to the Routing Engine. * * The BGP Engine will not touch a session in these states and the - * Peering Engine may do what it likes with it. + * Routing Engine may do what it likes with it. * * While sEnabled, sEstablished and sStopping: * * the session belongs to the BGP Engine. * - * A (very) few items in the session may be accessed by the Peering Engine, + * A (very) few items in the session may be accessed by the Routing Engine, * as noted below. (Subject to the mutex.) * - * Only the Peering Engine creates and destroys sessions. The BGP Engine + * Only the Routing Engine creates and destroys sessions. The BGP Engine * assumes that a session will not be destroyed while it is sEnabled, * sEstablished or sStopping. * - * These are private to the Peering Engine. + * These are private to the Routing Engine. */ bgp_session_state_t state ; int defer_enable ; /* set when waiting for stop */ - /* Flow control. Incremented when an update packet is sent - * from peering to BGP engine. Decremented when packet processed - * by BGP engine. On transition to 0 BGP engine should send an XON. - */ - int flow_control; + int flow_control ; /* limits number of updates sent + by the Routing Engine */ - /* These are private to the Peering Engine, and are set each time a session + /* These are private to the Routing Engine, and are set each time a session * event message is received from the BGP Engine. */ bgp_session_event_t event ; /* last event */ @@ -208,11 +205,11 @@ struct bgp_session * the session, and sets the stopped flag. * * The active flag is set when one or more connections are activated, and - * cleared when either the BGP Engine stops the session or the Peering + * cleared when either the BGP Engine stops the session or the Routing * Engine disables it. When not "active" all messages other than disable * and enable are ignored. This deals with the hiatus that exists between * the BGP Engine signalling that it has stopped (because of some exception) - * and the Peering Engine acknowledging that (by disabling the session). + * and the Routing Engine acknowledging that (by disabling the session). */ bgp_connection connections[bgp_connection_count] ; @@ -282,8 +279,8 @@ struct bgp_session_XON_args /* to Routeing Engine */ /* no further arguments */ } ; MQB_ARGS_SIZE_OK(bgp_session_XON_args) ; -enum { BGP_XON_REFRESH = 12, - BGP_XON_KICK = 4, +enum { BGP_XON_REFRESH = 40, + BGP_XON_KICK = 20, } ; struct bgp_session_ttl_args /* to bgp Engine */ @@ -330,7 +327,7 @@ bgp_session_event(bgp_session session, bgp_session_event_t event, int stopped) ; extern void -bgp_session_update_send(bgp_session session, struct stream* upd) ; +bgp_session_update_send(bgp_session session, struct stream_fifo* fifo) ; extern void bgp_session_route_refresh_send(bgp_session session, bgp_route_refresh rr) ; @@ -346,10 +343,9 @@ extern void bgp_session_route_refresh_recv(bgp_session session, bgp_route_refresh rr); extern int -bgp_session_is_XOFF(bgp_peer peer); - -extern int bgp_session_is_XON(bgp_peer peer); +extern int +bgp_session_dec_flow_count(bgp_peer peer) ; extern void bgp_session_set_ttl(bgp_session session, int ttl); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 154cb28d..238bd01c 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -4712,7 +4712,6 @@ bgp_terminate (int terminating, int retain_mode) for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp)) for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) { -fprintf(stderr, ">>> %s:", peer->host) ; if (retain_mode) bgp_peer_disable(peer, NULL); else if (terminating) @@ -4725,7 +4724,6 @@ fprintf(stderr, ">>> %s:", peer->host) ; else bgp_notify_send(peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_ADMIN_RESET); -fprintf(stderr, "<<<\n") ; } if (!retain_mode) diff --git a/bgpd/bgpd.cx b/bgpd/bgpd.cx new file mode 100644 index 00000000..955b344b --- /dev/null +++ b/bgpd/bgpd.cx @@ -0,0 +1,4777 @@ +/* BGP-4, BGP-4+ daemon program + Copyright (C) 1996, 97, 98, 99, 2000 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. */ + +#include <zebra.h> + +#include "prefix.h" +#include "thread.h" +#include "buffer.h" +#include "stream.h" +#include "command.h" +#include "sockunion.h" +#include "network.h" +#include "memory.h" +#include "filter.h" +#include "routemap.h" +#include "str.h" +#include "log.h" +#include "plist.h" +#include "linklist.h" +#include "workqueue.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp.h" +#include "bgpd/bgp_peer.h" + +#include "bgpd/bgp_table.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_dump.h" +#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_community.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_regex.h" +#include "bgpd/bgp_clist.h" +#include "bgpd/bgp_fsm.h" +#include "bgpd/bgp_packet.h" +#include "bgpd/bgp_zebra.h" +#include "bgpd/bgp_open.h" +#include "bgpd/bgp_filter.h" +#include "bgpd/bgp_nexthop.h" +#include "bgpd/bgp_damp.h" +#include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_advertise.h" +#include "bgpd/bgp_network.h" +#include "bgpd/bgp_vty.h" +#ifdef HAVE_SNMP +#include "bgpd/bgp_snmp.h" +#endif /* HAVE_SNMP */ + +/* BGP process wide configuration. */ +static struct bgp_master bgp_master; + +extern struct in_addr router_id_zebra; + +/* BGP process wide configuration pointer to export. */ +struct bgp_master *bm; + +/* BGP process wide nexus. */ +qpn_nexus cli_nexus = NULL; +qpn_nexus bgp_nexus = NULL; +qpn_nexus routing_nexus = NULL; + +/* BGP community-list. */ +struct community_list_handler *bgp_clist; + +/* true while program terminating */ +static int program_terminating = 0; + +/* BGP global flag manipulation. */ +int +bgp_option_set (int flag) +{ + switch (flag) + { + case BGP_OPT_NO_FIB: + case BGP_OPT_MULTIPLE_INSTANCE: + case BGP_OPT_CONFIG_CISCO: + SET_FLAG (bm->options, flag); + break; + default: + return BGP_ERR_INVALID_FLAG; + } + return 0; +} + +int +bgp_option_unset (int flag) +{ + switch (flag) + { + case BGP_OPT_MULTIPLE_INSTANCE: + if (listcount (bm->bgp) > 1) + return BGP_ERR_MULTIPLE_INSTANCE_USED; + /* Fall through. */ + case BGP_OPT_NO_FIB: + case BGP_OPT_CONFIG_CISCO: + UNSET_FLAG (bm->options, flag); + break; + default: + return BGP_ERR_INVALID_FLAG; + } + return 0; +} + +int +bgp_option_check (int flag) +{ + return CHECK_FLAG (bm->options, flag); +} + +/* BGP flag manipulation. */ +int +bgp_flag_set (struct bgp *bgp, int flag) +{ + SET_FLAG (bgp->flags, flag); + return 0; +} + +int +bgp_flag_unset (struct bgp *bgp, int flag) +{ + UNSET_FLAG (bgp->flags, flag); + return 0; +} + +int +bgp_flag_check (struct bgp *bgp, int flag) +{ + return CHECK_FLAG (bgp->flags, flag); +} + +/* Internal function to set BGP structure configureation flag. */ +static void +bgp_config_set (struct bgp *bgp, int config) +{ + SET_FLAG (bgp->config, config); +} + +static void +bgp_config_unset (struct bgp *bgp, int config) +{ + UNSET_FLAG (bgp->config, config); +} + +static int +bgp_config_check (struct bgp *bgp, int config) +{ + return CHECK_FLAG (bgp->config, config); +} + +/* Set BGP router identifier. */ +int +bgp_router_id_set (struct bgp *bgp, struct in_addr *id) +{ + struct peer *peer; + struct listnode *node, *nnode; + + if (bgp_config_check (bgp, BGP_CONFIG_ROUTER_ID) + && IPV4_ADDR_SAME (&bgp->router_id, id)) + return 0; + + IPV4_ADDR_COPY (&bgp->router_id, id); + bgp_config_set (bgp, BGP_CONFIG_ROUTER_ID); + + /* Set all peer's local identifier with this value. */ + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + IPV4_ADDR_COPY (&peer->local_id, id); + + if (peer->state == bgp_peer_sEstablished) + { + peer->last_reset = PEER_DOWN_RID_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } + return 0; +} + +/* BGP's cluster-id control. */ +int +bgp_cluster_id_set (struct bgp *bgp, struct in_addr *cluster_id) +{ + struct peer *peer; + struct listnode *node, *nnode; + + if (bgp_config_check (bgp, BGP_CONFIG_CLUSTER_ID) + && IPV4_ADDR_SAME (&bgp->cluster_id, cluster_id)) + return 0; + + IPV4_ADDR_COPY (&bgp->cluster_id, cluster_id); + bgp_config_set (bgp, BGP_CONFIG_CLUSTER_ID); + + /* Clear all IBGP peer. */ + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (peer_sort (peer) != BGP_PEER_IBGP) + continue; + + if (peer->state == bgp_peer_sEstablished) + { + peer->last_reset = PEER_DOWN_CLID_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } + return 0; +} + +int +bgp_cluster_id_unset (struct bgp *bgp) +{ + struct peer *peer; + struct listnode *node, *nnode; + + if (! bgp_config_check (bgp, BGP_CONFIG_CLUSTER_ID)) + return 0; + + bgp->cluster_id.s_addr = 0; + bgp_config_unset (bgp, BGP_CONFIG_CLUSTER_ID); + + /* Clear all IBGP peer. */ + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (peer_sort (peer) != BGP_PEER_IBGP) + continue; + + if (peer->state == bgp_peer_sEstablished) + { + peer->last_reset = PEER_DOWN_CLID_CHANGE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } + return 0; +} + +/* BGP timer configuration. */ +int +bgp_timers_set (struct bgp *bgp, u_int32_t keepalive, u_int32_t holdtime) +{ + bgp->default_keepalive = (keepalive < holdtime / 3 + ? keepalive : holdtime / 3); + bgp->default_holdtime = holdtime; + + return 0; +} + +int +bgp_timers_unset (struct bgp *bgp) +{ + bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE; + bgp->default_holdtime = BGP_DEFAULT_HOLDTIME; + + return 0; +} + +/* BGP confederation configuration. */ +int +bgp_confederation_id_set (struct bgp *bgp, as_t as) +{ + struct peer *peer; + struct listnode *node, *nnode; + int already_confed; + + if (as == 0) + return BGP_ERR_INVALID_AS; + + /* Remember - were we doing confederation before? */ + already_confed = bgp_config_check (bgp, BGP_CONFIG_CONFEDERATION); + bgp->confed_id = as; + bgp_config_set (bgp, BGP_CONFIG_CONFEDERATION); + + /* If we were doing confederation already, this is just an external + AS change. Just Reset EBGP sessions, not CONFED sessions. If we + were not doing confederation before, reset all EBGP sessions. */ + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + /* We're looking for peers who's AS is not local or part of our + confederation. */ + if (already_confed) + { + if (peer_sort (peer) == BGP_PEER_EBGP) + { + peer->local_as = as; + peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } + else + { + /* Not doign confederation before, so reset every non-local + session */ + if (peer_sort (peer) != BGP_PEER_IBGP) + { + /* Reset the local_as to be our EBGP one */ + if (peer_sort (peer) == BGP_PEER_EBGP) + peer->local_as = as; + peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } + } + return 0; +} + +int +bgp_confederation_id_unset (struct bgp *bgp) +{ + struct peer *peer; + struct listnode *node, *nnode; + + bgp->confed_id = 0; + bgp_config_unset (bgp, BGP_CONFIG_CONFEDERATION); + + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + /* We're looking for peers who's AS is not local */ + if (peer_sort (peer) != BGP_PEER_IBGP) + { + peer->local_as = bgp->as; + peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } + return 0; +} + +/* Is an AS part of the confed or not? */ +int +bgp_confederation_peers_check (struct bgp *bgp, as_t as) +{ + int i; + + if (! bgp) + return 0; + + for (i = 0; i < bgp->confed_peers_cnt; i++) + if (bgp->confed_peers[i] == as) + return 1; + + return 0; +} + +/* Add an AS to the confederation set. */ +int +bgp_confederation_peers_add (struct bgp *bgp, as_t as) +{ + struct peer *peer; + struct listnode *node, *nnode; + + if (! bgp) + return BGP_ERR_INVALID_BGP; + + if (bgp->as == as) + return BGP_ERR_INVALID_AS; + + if (bgp_confederation_peers_check (bgp, as)) + return -1; + + if (bgp->confed_peers) + bgp->confed_peers = XREALLOC (MTYPE_BGP_CONFED_LIST, + bgp->confed_peers, + (bgp->confed_peers_cnt + 1) * sizeof (as_t)); + else + bgp->confed_peers = XMALLOC (MTYPE_BGP_CONFED_LIST, + (bgp->confed_peers_cnt + 1) * sizeof (as_t)); + + bgp->confed_peers[bgp->confed_peers_cnt] = as; + bgp->confed_peers_cnt++; + + if (bgp_config_check (bgp, BGP_CONFIG_CONFEDERATION)) + { + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (peer->as == as) + { + peer->local_as = bgp->as; + peer->last_reset = PEER_DOWN_CONFED_PEER_CHANGE; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } + } + return 0; +} + +/* Delete an AS from the confederation set. */ +int +bgp_confederation_peers_remove (struct bgp *bgp, as_t as) +{ + int i; + int j; + struct peer *peer; + struct listnode *node, *nnode; + + if (! bgp) + return -1; + + if (! bgp_confederation_peers_check (bgp, as)) + return -1; + + for (i = 0; i < bgp->confed_peers_cnt; i++) + if (bgp->confed_peers[i] == as) + for(j = i + 1; j < bgp->confed_peers_cnt; j++) + bgp->confed_peers[j - 1] = bgp->confed_peers[j]; + + bgp->confed_peers_cnt--; + + if (bgp->confed_peers_cnt == 0) + { + if (bgp->confed_peers) + XFREE (MTYPE_BGP_CONFED_LIST, bgp->confed_peers); + bgp->confed_peers = NULL; + } + else + bgp->confed_peers = XREALLOC (MTYPE_BGP_CONFED_LIST, + bgp->confed_peers, + bgp->confed_peers_cnt * sizeof (as_t)); + + /* Now reset any peer who's remote AS has just been removed from the + CONFED */ + if (bgp_config_check (bgp, BGP_CONFIG_CONFEDERATION)) + { + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (peer->as == as) + { + peer->local_as = bgp->confed_id; + peer->last_reset = PEER_DOWN_CONFED_PEER_CHANGE; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } + } + + return 0; +} + +/* Local preference configuration. */ +int +bgp_default_local_preference_set (struct bgp *bgp, u_int32_t local_pref) +{ + if (! bgp) + return -1; + + bgp->default_local_pref = local_pref; + + return 0; +} + +int +bgp_default_local_preference_unset (struct bgp *bgp) +{ + if (! bgp) + return -1; + + bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF; + + return 0; +} + +/* If peer is RSERVER_CLIENT in at least one address family and is not member + of a peer_group for that family, return 1. + Used to check wether the peer is included in list bgp->rsclient. */ +int +peer_rsclient_active (struct peer *peer) +{ + int i; + int j; + + for (i=AFI_IP; i < AFI_MAX; i++) + for (j=SAFI_UNICAST; j < SAFI_MAX; j++) + if (CHECK_FLAG(peer->af_flags[i][j], PEER_FLAG_RSERVER_CLIENT) + && ! peer->af_group[i][j]) + return 1; + return 0; +} + +/* Peer comparison function for sorting. */ +static int +peer_cmp (struct peer *p1, struct peer *p2) +{ + return sockunion_cmp (&p1->su, &p2->su); +} + +int +peer_af_flag_check (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag) +{ + return CHECK_FLAG (peer->af_flags[afi][safi], flag); +} + +/* Reset all address family specific configuration. */ +static void +peer_af_flag_reset (struct peer *peer, afi_t afi, safi_t safi) +{ + int i; + struct bgp_filter *filter; + char orf_name[BUFSIZ]; + + filter = &peer->filter[afi][safi]; + + /* Clear neighbor filter and route-map */ + for (i = FILTER_IN; i < FILTER_MAX; i++) + { + if (filter->dlist[i].name) + { + free (filter->dlist[i].name); + filter->dlist[i].name = NULL; + } + prefix_list_unset_ref(&filter->plist[i].ref) ; + if (filter->aslist[i].name) + { + free (filter->aslist[i].name); + filter->aslist[i].name = NULL; + } + } + for (i = RMAP_IN; i < RMAP_MAX; i++) + { + if (filter->map[i].name) + { + free (filter->map[i].name); + filter->map[i].name = NULL; + } + } + + /* Clear unsuppress map. */ + if (filter->usmap.name) + free (filter->usmap.name); + filter->usmap.name = NULL; + filter->usmap.map = NULL; + + /* Clear neighbor's all address family flags. */ + peer->af_flags[afi][safi] = 0; + + /* Clear neighbor's all address family sflags. */ + peer->af_sflags[afi][safi] = 0; + + /* Clear neighbor's all address family capabilities. */ + peer->af_cap[afi][safi] = 0; + + /* Clear ORF info */ + peer->orf_plist[afi][safi] = NULL; + sprintf (orf_name, "%s.%d.%d", peer->host, afi, safi); + prefix_bgp_orf_remove_all (orf_name); + + /* Set default neighbor send-community. */ + if (! bgp_option_check (BGP_OPT_CONFIG_CISCO)) + { + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY); + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY); + } + + /* Clear neighbor default_originate_rmap */ + if (peer->default_rmap[afi][safi].name) + free (peer->default_rmap[afi][safi].name); + peer->default_rmap[afi][safi].name = NULL; + peer->default_rmap[afi][safi].map = NULL; + + /* Clear neighbor maximum-prefix */ + peer->pmax[afi][safi] = 0; + peer->pmax_threshold[afi][safi] = MAXIMUM_PREFIX_THRESHOLD_DEFAULT; +} + +/* peer global config reset */ +static void +peer_global_config_reset (struct peer *peer) +{ + peer->weight = 0; + peer->change_local_as = 0; + peer->ttl = (peer_sort (peer) == BGP_PEER_IBGP ? 255 : 1); + if (peer->update_source) + { + sockunion_free (peer->update_source); + peer->update_source = NULL; + } + if (peer->update_if) + { + XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); + peer->update_if = NULL; + } + + if (peer_sort (peer) == BGP_PEER_IBGP) + peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; + else + peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; + + peer->flags = 0; + peer->config = 0; + peer->holdtime = 0; + peer->keepalive = 0; + peer->connect = 0; + peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; +} + +/* Check peer's AS number and determin is this peer IBGP or EBGP */ +int +peer_sort (struct peer *peer) +{ + struct bgp *bgp; + + bgp = peer->bgp; + + /* Peer-group */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (peer->as) + return (bgp->as == peer->as ? BGP_PEER_IBGP : BGP_PEER_EBGP); + else + { + struct peer *peer1; + peer1 = listnode_head (peer->group->peer); + if (peer1) + return (peer1->local_as == peer1->as + ? BGP_PEER_IBGP : BGP_PEER_EBGP); + } + return BGP_PEER_INTERNAL; + } + + /* Normal peer */ + if (bgp && CHECK_FLAG (bgp->config, BGP_CONFIG_CONFEDERATION)) + { + if (peer->local_as == 0) + return BGP_PEER_INTERNAL; + + if (peer->local_as == peer->as) + { + if (peer->local_as == bgp->confed_id) + return BGP_PEER_EBGP; + else + return BGP_PEER_IBGP; + } + + if (bgp_confederation_peers_check (bgp, peer->as)) + return BGP_PEER_CONFED; + + return BGP_PEER_EBGP; + } + else + { + return (peer->local_as == 0 + ? BGP_PEER_INTERNAL : peer->local_as == peer->as + ? BGP_PEER_IBGP : BGP_PEER_EBGP); + } +} + + +/* increase reference count on a struct peer */ +struct peer * +peer_lock (struct peer *peer) +{ + assert (peer && (peer->lock >= 0)); + + peer->lock++; + + return peer; +} + +/* decrease reference count on a struct peer + * struct peer is freed and NULL returned if last reference + */ +struct peer * +peer_unlock (struct peer *peer) +{ + assert (peer && (peer->lock > 0)); + + peer->lock--; + + if (peer->lock == 0) + { +#if 0 + zlog_debug ("unlocked and freeing"); + zlog_backtrace (LOG_DEBUG); +#endif + peer_free (peer); + return NULL; + } + +#if 0 + if (peer->lock == 1) + { + zlog_debug ("unlocked to 1"); + zlog_backtrace (LOG_DEBUG); + } +#endif + + return peer; +} + + +/* Make accept BGP peer. Called from bgp_accept (). */ +struct peer * +peer_create_accept (struct bgp *bgp) +{ + struct peer *peer; + + peer = peer_new (bgp); + + peer = peer_lock (peer); /* bgp peer list reference */ + listnode_add_sort (bgp->peer, peer); + + return peer; +} + +/* Change peer's AS number. */ +static void +peer_as_change (struct peer *peer, as_t as) +{ + int type; + + /* Stop peer. */ + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + peer->last_reset = PEER_DOWN_REMOTE_AS_CHANGE; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + type = peer_sort (peer); + peer->as = as; + + if (bgp_config_check (peer->bgp, BGP_CONFIG_CONFEDERATION) + && ! bgp_confederation_peers_check (peer->bgp, as) + && peer->bgp->as != as) + peer->local_as = peer->bgp->confed_id; + else + peer->local_as = peer->bgp->as; + + /* Advertisement-interval reset */ + if (peer_sort (peer) == BGP_PEER_IBGP) + peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; + else + peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; + + /* TTL reset */ + if (peer_sort (peer) == BGP_PEER_IBGP) + peer->ttl = 255; + else if (type == BGP_PEER_IBGP) + peer->ttl = 1; + + /* reflector-client reset */ + if (peer_sort (peer) != BGP_PEER_IBGP) + { + UNSET_FLAG (peer->af_flags[AFI_IP][SAFI_UNICAST], + PEER_FLAG_REFLECTOR_CLIENT); + UNSET_FLAG (peer->af_flags[AFI_IP][SAFI_MULTICAST], + PEER_FLAG_REFLECTOR_CLIENT); + UNSET_FLAG (peer->af_flags[AFI_IP][SAFI_MPLS_VPN], + PEER_FLAG_REFLECTOR_CLIENT); + UNSET_FLAG (peer->af_flags[AFI_IP6][SAFI_UNICAST], + PEER_FLAG_REFLECTOR_CLIENT); + UNSET_FLAG (peer->af_flags[AFI_IP6][SAFI_MULTICAST], + PEER_FLAG_REFLECTOR_CLIENT); + } + + /* local-as reset */ + if (peer_sort (peer) != BGP_PEER_EBGP) + { + peer->change_local_as = 0; + UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); + } +} + +/* If peer does not exist, create new one. If peer already exists, + set AS number to the peer. */ +int +peer_remote_as (struct bgp *bgp, union sockunion *su, as_t *as, + afi_t afi, safi_t safi) +{ + struct peer *peer; + as_t local_as; + + peer = peer_lookup (bgp, su); + + if (peer) + { + /* When this peer is a member of peer-group. */ + if (peer->group) + { + if (peer->group->conf->as) + { + /* Return peer group's AS number. */ + *as = peer->group->conf->as; + return BGP_ERR_PEER_GROUP_MEMBER; + } + if (peer_sort (peer->group->conf) == BGP_PEER_IBGP) + { + if (bgp->as != *as) + { + *as = peer->as; + return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT; + } + } + else + { + if (bgp->as == *as) + { + *as = peer->as; + return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT; + } + } + } + + /* Existing peer's AS number change. */ + if (peer->as != *as) + peer_as_change (peer, *as); + } + else + { + + /* If the peer is not part of our confederation, and its not an + iBGP peer then spoof the source AS */ + if (bgp_config_check (bgp, BGP_CONFIG_CONFEDERATION) + && ! bgp_confederation_peers_check (bgp, *as) + && bgp->as != *as) + local_as = bgp->confed_id; + else + local_as = bgp->as; + + /* If this is IPv4 unicast configuration and "no bgp default + ipv4-unicast" is specified. */ + + if (bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4) + && afi == AFI_IP && safi == SAFI_UNICAST) + peer = peer_create (su, bgp, local_as, *as, 0, 0); + else + peer = peer_create (su, bgp, local_as, *as, afi, safi); + } + + return 0; +} + +/* Activate the peer or peer group for specified AFI and SAFI. */ +int +peer_activate (struct peer *peer, afi_t afi, safi_t safi) +{ + int active; + + if (peer->afc[afi][safi]) + return 0; + + /* Activate the address family configuration. */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + peer->afc[afi][safi] = 1; + else + { + active = peer_active (peer); + + peer->afc[afi][safi] = 1; + + if (! active && peer_active (peer)) + bgp_peer_enable (peer); + else +#if 0 + /* TODO: Dynamic capability */ + { + if (peer->status == Established) + { + if (CHECK_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV)) + { + peer->afc_adv[afi][safi] = 1; + bgp_capability_send (peer, afi, safi, + CAPABILITY_CODE_MP, + CAPABILITY_ACTION_SET); + if (peer->afc_recv[afi][safi]) + { + peer->afc_nego[afi][safi] = 1; + bgp_announce_route (peer, afi, safi); + } + } + else +#endif + { + peer->last_reset = PEER_DOWN_AF_ACTIVATE; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } +#if 0 + } + } +#endif + } + return 0; +} + +int +peer_deactivate (struct peer *peer, afi_t afi, safi_t safi) +{ + struct peer_group *group; + struct peer *peer1; + struct listnode *node, *nnode; + + if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + group = peer->group; + + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1)) + { + if (peer1->af_group[afi][safi]) + return BGP_ERR_PEER_GROUP_MEMBER_EXISTS; + } + } + else + { + if (peer->af_group[afi][safi]) + return BGP_ERR_PEER_BELONGS_TO_GROUP; + } + + if (! peer->afc[afi][safi]) + return 0; + + /* De-activate the address family configuration. */ + peer->afc[afi][safi] = 0; + peer_af_flag_reset (peer, afi, safi); + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (peer->state == bgp_peer_sEstablished) + { + if (CHECK_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV)) + { + peer->afc_adv[afi][safi] = 0; + peer->afc_nego[afi][safi] = 0; + + if (peer_active_nego (peer)) + { + bgp_capability_send (peer, afi, safi, + CAPABILITY_CODE_MP, + CAPABILITY_ACTION_UNSET); + bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_NORMAL); + peer->pcount[afi][safi] = 0; + } + else + { + peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } + else + { + peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE; + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + } + } + return 0; +} + +static int +peer_group_cmp (struct peer_group *g1, struct peer_group *g2) +{ + return strcmp (g1->name, g2->name); +} + +/* If peer is configured at least one address family return 1. */ +static int +peer_group_active (struct peer *peer) +{ + if (peer->af_group[AFI_IP][SAFI_UNICAST] + || peer->af_group[AFI_IP][SAFI_MULTICAST] + || peer->af_group[AFI_IP][SAFI_MPLS_VPN] + || peer->af_group[AFI_IP6][SAFI_UNICAST] + || peer->af_group[AFI_IP6][SAFI_MULTICAST]) + return 1; + return 0; +} + +/* Peer group cofiguration. */ +static struct peer_group * +peer_group_new (void) +{ + return (struct peer_group *) XCALLOC (MTYPE_PEER_GROUP, + sizeof (struct peer_group)); +} + +static void +peer_group_free (struct peer_group *group) +{ + XFREE (MTYPE_PEER_GROUP, group); +} + +struct peer_group * +peer_group_lookup (struct bgp *bgp, const char *name) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group)) + { + if (strcmp (group->name, name) == 0) + return group; + } + return NULL; +} + +struct peer_group * +peer_group_get (struct bgp *bgp, const char *name) +{ + struct peer_group *group; + + group = peer_group_lookup (bgp, name); + if (group) + return group; + + group = peer_group_new (); + group->bgp = bgp; + group->name = strdup (name); + group->peer = list_new (); + group->conf = peer_new (bgp); + if (! bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4)) + group->conf->afc[AFI_IP][SAFI_UNICAST] = 1; + group->conf->host = XSTRDUP (MTYPE_BGP_PEER_HOST, name); + group->conf->group = group; + group->conf->as = 0; + group->conf->ttl = 1; + group->conf->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; + UNSET_FLAG (group->conf->config, PEER_CONFIG_TIMER); + UNSET_FLAG (group->conf->config, PEER_CONFIG_CONNECT); + group->conf->keepalive = 0; + group->conf->holdtime = 0; + group->conf->connect = 0; + SET_FLAG (group->conf->sflags, PEER_STATUS_GROUP); + listnode_add_sort (bgp->group, group); + + return 0; +} + +static void +peer_group2peer_config_copy (struct peer_group *group, struct peer *peer, + afi_t afi, safi_t safi) +{ + int in = FILTER_IN; + int out = FILTER_OUT; + struct peer *conf; + struct bgp_filter *pfilter; + struct bgp_filter *gfilter; + + conf = group->conf; + pfilter = &peer->filter[afi][safi]; + gfilter = &conf->filter[afi][safi]; + + /* remote-as */ + if (conf->as) + peer->as = conf->as; + + /* remote-as */ + if (conf->change_local_as) + peer->change_local_as = conf->change_local_as; + + /* TTL */ + peer->ttl = conf->ttl; + + /* Weight */ + peer->weight = conf->weight; + + /* peer flags apply */ + peer->flags = conf->flags; + /* peer af_flags apply */ + peer->af_flags[afi][safi] = conf->af_flags[afi][safi]; + /* peer config apply */ + peer->config = conf->config; + + /* peer timers apply */ + peer->holdtime = conf->holdtime; + peer->keepalive = conf->keepalive; + peer->connect = conf->connect; + if (CHECK_FLAG (conf->config, PEER_CONFIG_CONNECT)) + peer->v_connect = conf->connect; + else + peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; + + /* advertisement-interval reset */ + if (peer_sort (peer) == BGP_PEER_IBGP) + peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; + else + peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; + + /* password apply */ + if (peer->password) + XFREE (MTYPE_PEER_PASSWORD, peer->password); + + if (conf->password) + peer->password = XSTRDUP (MTYPE_PEER_PASSWORD, conf->password); + else + peer->password = NULL; + + /* maximum-prefix */ + peer->pmax[afi][safi] = conf->pmax[afi][safi]; + peer->pmax_threshold[afi][safi] = conf->pmax_threshold[afi][safi]; + peer->pmax_restart[afi][safi] = conf->pmax_restart[afi][safi]; + + /* allowas-in */ + peer->allowas_in[afi][safi] = conf->allowas_in[afi][safi]; + + /* route-server-client */ + if (CHECK_FLAG(conf->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)) + { + /* Make peer's RIB point to group's RIB. */ + peer->rib[afi][safi] = group->conf->rib[afi][safi]; + + /* Import policy. */ + if (pfilter->map[RMAP_IMPORT].name) + free (pfilter->map[RMAP_IMPORT].name); + if (gfilter->map[RMAP_IMPORT].name) + { + pfilter->map[RMAP_IMPORT].name = strdup (gfilter->map[RMAP_IMPORT].name); + pfilter->map[RMAP_IMPORT].map = gfilter->map[RMAP_IMPORT].map; + } + else + { + pfilter->map[RMAP_IMPORT].name = NULL; + pfilter->map[RMAP_IMPORT].map = NULL; + } + + /* Export policy. */ + if (gfilter->map[RMAP_EXPORT].name && ! pfilter->map[RMAP_EXPORT].name) + { + pfilter->map[RMAP_EXPORT].name = strdup (gfilter->map[RMAP_EXPORT].name); + pfilter->map[RMAP_EXPORT].map = gfilter->map[RMAP_EXPORT].map; + } + } + + /* default-originate route-map */ + if (conf->default_rmap[afi][safi].name) + { + if (peer->default_rmap[afi][safi].name) + free (peer->default_rmap[afi][safi].name); + peer->default_rmap[afi][safi].name = strdup (conf->default_rmap[afi][safi].name); + peer->default_rmap[afi][safi].map = conf->default_rmap[afi][safi].map; + } + + /* update-source apply */ + if (conf->update_source) + { + if (peer->update_source) + sockunion_free (peer->update_source); + if (peer->update_if) + { + XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); + peer->update_if = NULL; + } + peer->update_source = sockunion_dup (conf->update_source); + } + else if (conf->update_if) + { + if (peer->update_if) + XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); + if (peer->update_source) + { + sockunion_free (peer->update_source); + peer->update_source = NULL; + } + peer->update_if = XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, conf->update_if); + } + + /* inbound filter apply */ + if (gfilter->dlist[in].name && ! pfilter->dlist[in].name) + { + if (pfilter->dlist[in].name) + free (pfilter->dlist[in].name); + pfilter->dlist[in].name = strdup (gfilter->dlist[in].name); + pfilter->dlist[in].alist = gfilter->dlist[in].alist; + } + if (! pfilter->plist[in].ref) + prefix_list_copy_ref(&pfilter->plist[in].ref, gfilter->plist[in].ref) ; + if (gfilter->aslist[in].name && ! pfilter->aslist[in].name) + { + if (pfilter->aslist[in].name) + free (pfilter->aslist[in].name); + pfilter->aslist[in].name = strdup (gfilter->aslist[in].name); + pfilter->aslist[in].aslist = gfilter->aslist[in].aslist; + } + if (gfilter->map[RMAP_IN].name && ! pfilter->map[RMAP_IN].name) + { + if (pfilter->map[RMAP_IN].name) + free (pfilter->map[RMAP_IN].name); + pfilter->map[RMAP_IN].name = strdup (gfilter->map[RMAP_IN].name); + pfilter->map[RMAP_IN].map = gfilter->map[RMAP_IN].map; + } + + /* outbound filter apply */ + if (gfilter->dlist[out].name) + { + if (pfilter->dlist[out].name) + free (pfilter->dlist[out].name); + pfilter->dlist[out].name = strdup (gfilter->dlist[out].name); + pfilter->dlist[out].alist = gfilter->dlist[out].alist; + } + else + { + if (pfilter->dlist[out].name) + free (pfilter->dlist[out].name); + pfilter->dlist[out].name = NULL; + pfilter->dlist[out].alist = NULL; + } + + prefix_list_copy_ref(&pfilter->plist[out].ref, gfilter->plist[out].ref) ; + + if (gfilter->aslist[out].name) + { + if (pfilter->aslist[out].name) + free (pfilter->aslist[out].name); + pfilter->aslist[out].name = strdup (gfilter->aslist[out].name); + pfilter->aslist[out].aslist = gfilter->aslist[out].aslist; + } + else + { + if (pfilter->aslist[out].name) + free (pfilter->aslist[out].name); + pfilter->aslist[out].name = NULL; + pfilter->aslist[out].aslist = NULL; + } + if (gfilter->map[RMAP_OUT].name) + { + if (pfilter->map[RMAP_OUT].name) + free (pfilter->map[RMAP_OUT].name); + pfilter->map[RMAP_OUT].name = strdup (gfilter->map[RMAP_OUT].name); + pfilter->map[RMAP_OUT].map = gfilter->map[RMAP_OUT].map; + } + else + { + if (pfilter->map[RMAP_OUT].name) + free (pfilter->map[RMAP_OUT].name); + pfilter->map[RMAP_OUT].name = NULL; + pfilter->map[RMAP_OUT].map = NULL; + } + + /* RS-client's import/export route-maps. */ + if (gfilter->map[RMAP_IMPORT].name) + { + if (pfilter->map[RMAP_IMPORT].name) + free (pfilter->map[RMAP_IMPORT].name); + pfilter->map[RMAP_IMPORT].name = strdup (gfilter->map[RMAP_IMPORT].name); + pfilter->map[RMAP_IMPORT].map = gfilter->map[RMAP_IMPORT].map; + } + else + { + if (pfilter->map[RMAP_IMPORT].name) + free (pfilter->map[RMAP_IMPORT].name); + pfilter->map[RMAP_IMPORT].name = NULL; + pfilter->map[RMAP_IMPORT].map = NULL; + } + if (gfilter->map[RMAP_EXPORT].name && ! pfilter->map[RMAP_EXPORT].name) + { + if (pfilter->map[RMAP_EXPORT].name) + free (pfilter->map[RMAP_EXPORT].name); + pfilter->map[RMAP_EXPORT].name = strdup (gfilter->map[RMAP_EXPORT].name); + pfilter->map[RMAP_EXPORT].map = gfilter->map[RMAP_EXPORT].map; + } + + if (gfilter->usmap.name) + { + if (pfilter->usmap.name) + free (pfilter->usmap.name); + pfilter->usmap.name = strdup (gfilter->usmap.name); + pfilter->usmap.map = gfilter->usmap.map; + } + else + { + if (pfilter->usmap.name) + free (pfilter->usmap.name); + pfilter->usmap.name = NULL; + pfilter->usmap.map = NULL; + } +} + +/* Peer group's remote AS configuration. */ +int +peer_group_remote_as (struct bgp *bgp, const char *group_name, as_t *as) +{ + struct peer_group *group; + struct peer *peer; + struct listnode *node, *nnode; + + group = peer_group_lookup (bgp, group_name); + if (! group) + return -1; + + if (group->conf->as == *as) + return 0; + + /* When we setup peer-group AS number all peer group member's AS + number must be updated to same number. */ + peer_as_change (group->conf, *as); + + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (peer->as != *as) + peer_as_change (peer, *as); + } + + return 0; +} + +int +peer_group_delete (struct peer_group *group) +{ + struct bgp *bgp; + struct peer *peer; + struct listnode *node, *nnode; + + bgp = group->bgp; + + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + peer->group = NULL; + peer_delete (peer); + } + list_delete (group->peer); + + free (group->name); + group->name = NULL; + + group->conf->group = NULL; + peer_delete (group->conf); + + /* Delete from all peer_group list. */ + listnode_delete (bgp->group, group); + + peer_group_free (group); + + return 0; +} + +int +peer_group_remote_as_delete (struct peer_group *group) +{ + struct peer *peer; + struct listnode *node, *nnode; + + if (! group->conf->as) + return 0; + + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + peer->group = NULL; + peer_delete (peer); + } + list_delete_all_node (group->peer); + + group->conf->as = 0; + + return 0; +} + +/* Bind specified peer to peer group. */ +int +peer_group_bind (struct bgp *bgp, union sockunion *su, + struct peer_group *group, afi_t afi, safi_t safi, as_t *as) +{ + struct peer *peer; + int first_member = 0; + + /* Check peer group's address family. */ + if (! group->conf->afc[afi][safi]) + return BGP_ERR_PEER_GROUP_AF_UNCONFIGURED; + + /* Lookup the peer. */ + peer = peer_lookup (bgp, su); + + /* Create a new peer. */ + if (! peer) + { + if (! group->conf->as) + return BGP_ERR_PEER_GROUP_NO_REMOTE_AS; + + peer = peer_create (su, bgp, bgp->as, group->conf->as, afi, safi); + peer->group = group; + peer->af_group[afi][safi] = 1; + + peer = peer_lock (peer); /* group->peer list reference */ + listnode_add (group->peer, peer); + peer_group2peer_config_copy (group, peer, afi, safi); + + return 0; + } + + /* When the peer already belongs to peer group, check the consistency. */ + if (peer->af_group[afi][safi]) + { + if (strcmp (peer->group->name, group->name) != 0) + return BGP_ERR_PEER_GROUP_CANT_CHANGE; + + return 0; + } + + /* Check current peer group configuration. */ + if (peer_group_active (peer) + && strcmp (peer->group->name, group->name) != 0) + return BGP_ERR_PEER_GROUP_MISMATCH; + + if (! group->conf->as) + { + if (peer_sort (group->conf) != BGP_PEER_INTERNAL + && peer_sort (group->conf) != peer_sort (peer)) + { + if (as) + *as = peer->as; + return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT; + } + + if (peer_sort (group->conf) == BGP_PEER_INTERNAL) + first_member = 1; + } + + peer->af_group[afi][safi] = 1; + peer->afc[afi][safi] = 1; + if (! peer->group) + { + peer->group = group; + + peer = peer_lock (peer); /* group->peer list reference */ + listnode_add (group->peer, peer); + } + else + assert (group && peer->group == group); + + if (first_member) + { + /* Advertisement-interval reset */ + if (peer_sort (group->conf) == BGP_PEER_IBGP) + group->conf->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; + else + group->conf->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; + + /* ebgp-multihop reset */ + if (peer_sort (group->conf) == BGP_PEER_IBGP) + group->conf->ttl = 255; + + /* local-as reset */ + if (peer_sort (group->conf) != BGP_PEER_EBGP) + { + group->conf->change_local_as = 0; + UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); + } + } + + if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)) + { + struct listnode *pn; + + /* If it's not configured as RSERVER_CLIENT in any other address + family, without being member of a peer_group, remove it from + list bgp->rsclient.*/ + if (! peer_rsclient_active (peer) + && (pn = listnode_lookup (bgp->rsclient, peer))) + { + peer_unlock (peer); /* peer rsclient reference */ + list_delete_node (bgp->rsclient, pn); + + /* Clear our own rsclient rib for this afi/safi. */ + bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_MY_RSCLIENT); + } + + bgp_table_finish (&peer->rib[afi][safi]); + + /* Import policy. */ + if (peer->filter[afi][safi].map[RMAP_IMPORT].name) + { + free (peer->filter[afi][safi].map[RMAP_IMPORT].name); + peer->filter[afi][safi].map[RMAP_IMPORT].name = NULL; + peer->filter[afi][safi].map[RMAP_IMPORT].map = NULL; + } + + /* Export policy. */ + if (! CHECK_FLAG(group->conf->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) + && peer->filter[afi][safi].map[RMAP_EXPORT].name) + { + free (peer->filter[afi][safi].map[RMAP_EXPORT].name); + peer->filter[afi][safi].map[RMAP_EXPORT].name = NULL; + peer->filter[afi][safi].map[RMAP_EXPORT].map = NULL; + } + } + + peer_group2peer_config_copy (group, peer, afi, safi); + + peer->last_reset = PEER_DOWN_RMAP_BIND; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + + return 0; +} + +int +peer_group_unbind (struct bgp *bgp, struct peer *peer, + struct peer_group *group, afi_t afi, safi_t safi) +{ + if (! peer->af_group[afi][safi]) + return 0; + + if (group != peer->group) + return BGP_ERR_PEER_GROUP_MISMATCH; + + peer->af_group[afi][safi] = 0; + peer->afc[afi][safi] = 0; + peer_af_flag_reset (peer, afi, safi); + + if (peer->rib[afi][safi]) + peer->rib[afi][safi] = NULL; + + if (! peer_group_active (peer)) + { + assert (listnode_lookup (group->peer, peer)); + peer_unlock (peer); /* peer group list reference */ + listnode_delete (group->peer, peer); + peer->group = NULL; + if (group->conf->as) + { + peer_delete (peer); + return 0; + } + peer_global_config_reset (peer); + } + + peer->last_reset = PEER_DOWN_RMAP_UNBIND; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + return 0; +} + +/* BGP instance creation by `router bgp' commands. */ +static struct bgp * +bgp_create (as_t *as, const char *name) +{ + struct bgp *bgp; + afi_t afi; + safi_t safi; + + if ( (bgp = XCALLOC (MTYPE_BGP, sizeof (struct bgp))) == NULL) + return NULL; + + bgp_lock (bgp); + bgp->peer_self = peer_new (bgp); + bgp->peer_self->host = XSTRDUP (MTYPE_BGP_PEER_HOST, "Static announcement"); + + bgp->peer = list_new (); + bgp->peer->cmp = (int (*)(void *, void *)) peer_cmp; + + bgp->group = list_new (); + bgp->group->cmp = (int (*)(void *, void *)) peer_group_cmp; + + bgp->rsclient = list_new (); + bgp->rsclient->cmp = (int (*)(void*, void*)) peer_cmp; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + bgp->route[afi][safi] = bgp_table_init (afi, safi); + bgp->aggregate[afi][safi] = bgp_table_init (afi, safi); + bgp->rib[afi][safi] = bgp_table_init (afi, safi); + } + + bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF; + bgp->default_holdtime = BGP_DEFAULT_HOLDTIME; + bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE; + bgp->restart_time = BGP_DEFAULT_RESTART_TIME; + bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME; + + bgp->as = *as; + + if (name) + bgp->name = strdup (name); + + return bgp; +} + +/* Return first entry of BGP. */ +struct bgp * +bgp_get_default (void) +{ + if (bm->bgp->head) + return (listgetdata (listhead (bm->bgp))); + return NULL; +} + +/* Lookup BGP entry. */ +struct bgp * +bgp_lookup (as_t as, const char *name) +{ + struct bgp *bgp; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp)) + if (bgp->as == as + && ((bgp->name == NULL && name == NULL) + || (bgp->name && name && strcmp (bgp->name, name) == 0))) + return bgp; + return NULL; +} + +/* Lookup BGP structure by view name. */ +struct bgp * +bgp_lookup_by_name (const char *name) +{ + struct bgp *bgp; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp)) + if ((bgp->name == NULL && name == NULL) + || (bgp->name && name && strcmp (bgp->name, name) == 0)) + return bgp; + return NULL; +} + +/* Called from VTY commands. */ +int +bgp_get (struct bgp **bgp_val, as_t *as, const char *name) +{ + struct bgp *bgp; + + /* Multiple instance check. */ + if (bgp_option_check (BGP_OPT_MULTIPLE_INSTANCE)) + { + if (name) + bgp = bgp_lookup_by_name (name); + else + bgp = bgp_get_default (); + + /* Already exists. */ + if (bgp) + { + if (bgp->as != *as) + { + *as = bgp->as; + return BGP_ERR_INSTANCE_MISMATCH; + } + *bgp_val = bgp; + return 0; + } + } + else + { + /* BGP instance name can not be specified for single instance. */ + if (name) + return BGP_ERR_MULTIPLE_INSTANCE_NOT_SET; + + /* Get default BGP structure if exists. */ + bgp = bgp_get_default (); + + if (bgp) + { + if (bgp->as != *as) + { + *as = bgp->as; + return BGP_ERR_AS_MISMATCH; + } + *bgp_val = bgp; + return 0; + } + } + + bgp = bgp_create (as, name); + listnode_add (bm->bgp, bgp); + bgp_router_id_set(bgp, &router_id_zebra); + *bgp_val = bgp; + + return 0; +} + +/* Delete BGP instance. */ +int +bgp_delete (struct bgp *bgp) +{ + struct peer *peer; + struct peer_group *group; + struct listnode *node; + struct listnode *next; + afi_t afi; + int i; + + /* Delete static route. */ + bgp_static_delete (bgp); + + /* Unset redistribution. */ + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + if (i != ZEBRA_ROUTE_BGP) + bgp_redistribute_unset (bgp, afi, i); + + for (ALL_LIST_ELEMENTS (bgp->peer, node, next, peer)) + peer_delete (peer); + + for (ALL_LIST_ELEMENTS (bgp->group, node, next, group)) + peer_group_delete (group); + + assert (listcount (bgp->rsclient) == 0); + + if (bgp->peer_self) { + peer_delete(bgp->peer_self); + bgp->peer_self = NULL; + } + + /* Remove visibility via the master list - there may however still be + * routes to be processed still referencing the struct bgp. + */ + listnode_delete (bm->bgp, bgp); + + bgp_unlock(bgp); /* initial reference */ + + return 0; +} + +static void bgp_free (struct bgp *); + +void +bgp_lock (struct bgp *bgp) +{ + ++bgp->lock; +} + +void +bgp_unlock(struct bgp *bgp) +{ + assert(bgp->lock > 0); + if (--bgp->lock == 0) + bgp_free (bgp); +} + +static void +bgp_free (struct bgp *bgp) +{ + afi_t afi; + safi_t safi; + + list_delete (bgp->group); + list_delete (bgp->peer); + list_delete (bgp->rsclient); + + if (bgp->name) + free (bgp->name); + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + if (bgp->route[afi][safi]) + bgp_table_finish (&bgp->route[afi][safi]); + if (bgp->aggregate[afi][safi]) + bgp_table_finish (&bgp->aggregate[afi][safi]) ; + if (bgp->rib[afi][safi]) + bgp_table_finish (&bgp->rib[afi][safi]); + } + XFREE (MTYPE_BGP, bgp); +} + +struct peer * +peer_lookup (struct bgp *bgp, union sockunion *su) +{ + struct peer *peer; + struct listnode *node, *nnode; + + if (bgp != NULL) + { + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + if (sockunion_same (&peer->su, su) + && ! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + return peer; + } + else if (bm->bgp != NULL) + { + struct listnode *bgpnode, *nbgpnode; + + for (ALL_LIST_ELEMENTS (bm->bgp, bgpnode, nbgpnode, bgp)) + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + if (sockunion_same (&peer->su, su) + && ! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + return peer; + } + return NULL; +} + +struct peer * +peer_lookup_with_open (union sockunion *su, as_t remote_as, + struct in_addr *remote_id, int *as) +{ + struct peer *peer; + struct listnode *node; + struct listnode *bgpnode; + struct bgp *bgp; + + if (! bm->bgp) + return NULL; + + for (ALL_LIST_ELEMENTS_RO (bm->bgp, bgpnode, bgp)) + { + for (ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer)) + { + if (sockunion_same (&peer->su, su) + && ! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + { + if (peer->as == remote_as + && peer->remote_id.s_addr == remote_id->s_addr) + return peer; + if (peer->as == remote_as) + *as = 1; + } + } + + for (ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer)) + { + if (sockunion_same (&peer->su, su) + && ! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + { + if (peer->as == remote_as + && peer->remote_id.s_addr == 0) + return peer; + if (peer->as == remote_as) + *as = 1; + } + } + } + return NULL; +} + +/* If peer is configured at least one address family return 1. */ +int +peer_active (struct peer *peer) +{ + if (peer->afc[AFI_IP][SAFI_UNICAST] + || peer->afc[AFI_IP][SAFI_MULTICAST] + || peer->afc[AFI_IP][SAFI_MPLS_VPN] + || peer->afc[AFI_IP6][SAFI_UNICAST] + || peer->afc[AFI_IP6][SAFI_MULTICAST]) + return 1; + return 0; +} + +/* If peer is negotiated at least one address family return 1. */ +int +peer_active_nego (struct peer *peer) +{ + if (peer->afc_nego[AFI_IP][SAFI_UNICAST] + || peer->afc_nego[AFI_IP][SAFI_MULTICAST] + || peer->afc_nego[AFI_IP][SAFI_MPLS_VPN] + || peer->afc_nego[AFI_IP6][SAFI_UNICAST] + || peer->afc_nego[AFI_IP6][SAFI_MULTICAST]) + return 1; + return 0; +} + +/* peer_flag_change_type. */ +enum peer_change_type +{ + peer_change_none, + peer_change_reset, + peer_change_reset_in, + peer_change_reset_out, +}; + +static void +peer_change_action (struct peer *peer, afi_t afi, safi_t safi, + enum peer_change_type type) +{ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return; + + if (type == peer_change_reset) + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + else if (type == peer_change_reset_in) + { + if (CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_OLD_RCV) + || CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV)) + bgp_route_refresh_send (peer, afi, safi, 0, 0, 0); + else + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + else if (type == peer_change_reset_out) + bgp_announce_route (peer, afi, safi); +} + +struct peer_flag_action +{ + /* Peer's flag. */ + u_int32_t flag; + + /* This flag can be set for peer-group member. */ + u_char not_for_member; + + /* Action when the flag is changed. */ + enum peer_change_type type; + + /* Peer down cause */ + u_char peer_down; +}; + +static const struct peer_flag_action peer_flag_action_list[] = + { + { PEER_FLAG_PASSIVE, 0, peer_change_reset }, + { PEER_FLAG_SHUTDOWN, 0, peer_change_reset }, + { PEER_FLAG_DONT_CAPABILITY, 0, peer_change_none }, + { PEER_FLAG_OVERRIDE_CAPABILITY, 0, peer_change_none }, + { PEER_FLAG_STRICT_CAP_MATCH, 0, peer_change_none }, + { PEER_FLAG_DYNAMIC_CAPABILITY, 0, peer_change_reset }, + { PEER_FLAG_DISABLE_CONNECTED_CHECK, 0, peer_change_reset }, + { 0, 0, 0 } + }; + +static const struct peer_flag_action peer_af_flag_action_list[] = + { + { PEER_FLAG_NEXTHOP_SELF, 1, peer_change_reset_out }, + { PEER_FLAG_SEND_COMMUNITY, 1, peer_change_reset_out }, + { PEER_FLAG_SEND_EXT_COMMUNITY, 1, peer_change_reset_out }, + { PEER_FLAG_SOFT_RECONFIG, 0, peer_change_reset_in }, + { PEER_FLAG_REFLECTOR_CLIENT, 1, peer_change_reset }, + { PEER_FLAG_RSERVER_CLIENT, 1, peer_change_reset }, + { PEER_FLAG_AS_PATH_UNCHANGED, 1, peer_change_reset_out }, + { PEER_FLAG_NEXTHOP_UNCHANGED, 1, peer_change_reset_out }, + { PEER_FLAG_MED_UNCHANGED, 1, peer_change_reset_out }, + { PEER_FLAG_REMOVE_PRIVATE_AS, 1, peer_change_reset_out }, + { PEER_FLAG_ALLOWAS_IN, 0, peer_change_reset_in }, + { PEER_FLAG_ORF_PREFIX_SM, 1, peer_change_reset }, + { PEER_FLAG_ORF_PREFIX_RM, 1, peer_change_reset }, + { PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED, 0, peer_change_reset_out }, + { 0, 0, 0 } + }; + +/* Proper action set. */ +static int +peer_flag_action_set (const struct peer_flag_action *action_list, int size, + struct peer_flag_action *action, u_int32_t flag) +{ + int i; + int found = 0; + int reset_in = 0; + int reset_out = 0; + const struct peer_flag_action *match = NULL; + + /* Check peer's frag action. */ + for (i = 0; i < size; i++) + { + match = &action_list[i]; + + if (match->flag == 0) + break; + + if (match->flag & flag) + { + found = 1; + + if (match->type == peer_change_reset_in) + reset_in = 1; + if (match->type == peer_change_reset_out) + reset_out = 1; + if (match->type == peer_change_reset) + { + reset_in = 1; + reset_out = 1; + } + if (match->not_for_member) + action->not_for_member = 1; + } + } + + /* Set peer clear type. */ + if (reset_in && reset_out) + action->type = peer_change_reset; + else if (reset_in) + action->type = peer_change_reset_in; + else if (reset_out) + action->type = peer_change_reset_out; + else + action->type = peer_change_none; + + return found; +} + +static void +peer_flag_modify_action (struct peer *peer, u_int32_t flag) +{ + if (flag == PEER_FLAG_SHUTDOWN) + { + if (CHECK_FLAG (peer->flags, flag)) + { + if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT)) + peer_nsf_stop (peer); + + UNSET_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW); + if (peer->t_pmax_restart) + { + BGP_TIMER_OFF (peer->t_pmax_restart); + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s Maximum-prefix restart timer cancelled", + peer->host); + } + + if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT)) + peer_nsf_stop (peer); + + bgp_notify_send_with_data(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN, NULL, 0); + } + else + { + peer->v_start = BGP_INIT_START_TIMER; + bgp_peer_disable(peer, NULL); + } + } + else + { + if (flag == PEER_FLAG_DYNAMIC_CAPABILITY) + peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE; + else if (flag == PEER_FLAG_PASSIVE) + peer->last_reset = PEER_DOWN_PASSIVE_CHANGE; + else if (flag == PEER_FLAG_DISABLE_CONNECTED_CHECK) + peer->last_reset = PEER_DOWN_MULTIHOP_CHANGE; + + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } +} + +/* Change specified peer flag. */ +static int +peer_flag_modify (struct peer *peer, u_int32_t flag, int set) +{ + int found; + int size; + struct peer_group *group; + struct listnode *node, *nnode; + struct peer_flag_action action; + + memset (&action, 0, sizeof (struct peer_flag_action)); + size = sizeof peer_flag_action_list / sizeof (struct peer_flag_action); + + found = peer_flag_action_set (peer_flag_action_list, size, &action, flag); + + /* No flag action is found. */ + if (! found) + return BGP_ERR_INVALID_FLAG; + + /* Not for peer-group member. */ + if (action.not_for_member && peer_group_active (peer)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + /* When unset the peer-group member's flag we have to check + peer-group configuration. */ + if (! set && peer_group_active (peer)) + if (CHECK_FLAG (peer->group->conf->flags, flag)) + { + if (flag == PEER_FLAG_SHUTDOWN) + return BGP_ERR_PEER_GROUP_SHUTDOWN; + else + return BGP_ERR_PEER_GROUP_HAS_THE_FLAG; + } + + /* Flag conflict check. */ + if (set + && CHECK_FLAG (peer->flags | flag, PEER_FLAG_STRICT_CAP_MATCH) + && CHECK_FLAG (peer->flags | flag, PEER_FLAG_OVERRIDE_CAPABILITY)) + return BGP_ERR_PEER_FLAG_CONFLICT; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (set && CHECK_FLAG (peer->flags, flag) == flag) + return 0; + if (! set && ! CHECK_FLAG (peer->flags, flag)) + return 0; + } + + if (set) + SET_FLAG (peer->flags, flag); + else + UNSET_FLAG (peer->flags, flag); + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (action.type == peer_change_reset) + peer_flag_modify_action (peer, flag); + + return 0; + } + + /* peer-group member updates. */ + group = peer->group; + + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (set && CHECK_FLAG (peer->flags, flag) == flag) + continue; + + if (! set && ! CHECK_FLAG (peer->flags, flag)) + continue; + + if (set) + SET_FLAG (peer->flags, flag); + else + UNSET_FLAG (peer->flags, flag); + + if (action.type == peer_change_reset) + peer_flag_modify_action (peer, flag); + } + return 0; +} + +int +peer_flag_set (struct peer *peer, u_int32_t flag) +{ + return peer_flag_modify (peer, flag, 1); +} + +int +peer_flag_unset (struct peer *peer, u_int32_t flag) +{ + return peer_flag_modify (peer, flag, 0); +} + +static int +peer_is_group_member (struct peer *peer, afi_t afi, safi_t safi) +{ + if (peer->af_group[afi][safi]) + return 1; + return 0; +} + +static int +peer_af_flag_modify (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag, + int set) +{ + int found; + int size; + struct listnode *node, *nnode; + struct peer_group *group; + struct peer_flag_action action; + + memset (&action, 0, sizeof (struct peer_flag_action)); + size = sizeof peer_af_flag_action_list / sizeof (struct peer_flag_action); + + found = peer_flag_action_set (peer_af_flag_action_list, size, &action, flag); + + /* No flag action is found. */ + if (! found) + return BGP_ERR_INVALID_FLAG; + + /* Adress family must be activated. */ + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + /* Not for peer-group member. */ + if (action.not_for_member && peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + /* Spcecial check for reflector client. */ + if (flag & PEER_FLAG_REFLECTOR_CLIENT + && peer_sort (peer) != BGP_PEER_IBGP) + return BGP_ERR_NOT_INTERNAL_PEER; + + /* Spcecial check for remove-private-AS. */ + if (flag & PEER_FLAG_REMOVE_PRIVATE_AS + && peer_sort (peer) == BGP_PEER_IBGP) + return BGP_ERR_REMOVE_PRIVATE_AS; + + /* When unset the peer-group member's flag we have to check + peer-group configuration. */ + if (! set && peer->af_group[afi][safi]) + if (CHECK_FLAG (peer->group->conf->af_flags[afi][safi], flag)) + return BGP_ERR_PEER_GROUP_HAS_THE_FLAG; + + /* When current flag configuration is same as requested one. */ + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (set && CHECK_FLAG (peer->af_flags[afi][safi], flag) == flag) + return 0; + if (! set && ! CHECK_FLAG (peer->af_flags[afi][safi], flag)) + return 0; + } + + if (set) + SET_FLAG (peer->af_flags[afi][safi], flag); + else + UNSET_FLAG (peer->af_flags[afi][safi], flag); + + /* Execute action when peer is established. */ + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP) + && peer->state == bgp_peer_sEstablished) + { + if (! set && flag == PEER_FLAG_SOFT_RECONFIG) + bgp_clear_adj_in (peer, afi, safi); + else + { + if (flag == PEER_FLAG_REFLECTOR_CLIENT) + peer->last_reset = PEER_DOWN_RR_CLIENT_CHANGE; + else if (flag == PEER_FLAG_RSERVER_CLIENT) + peer->last_reset = PEER_DOWN_RS_CLIENT_CHANGE; + else if (flag == PEER_FLAG_ORF_PREFIX_SM) + peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE; + else if (flag == PEER_FLAG_ORF_PREFIX_RM) + peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE; + + peer_change_action (peer, afi, safi, action.type); + } + + } + + /* Peer group member updates. */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + group = peer->group; + + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (! peer->af_group[afi][safi]) + continue; + + if (set && CHECK_FLAG (peer->af_flags[afi][safi], flag) == flag) + continue; + + if (! set && ! CHECK_FLAG (peer->af_flags[afi][safi], flag)) + continue; + + if (set) + SET_FLAG (peer->af_flags[afi][safi], flag); + else + UNSET_FLAG (peer->af_flags[afi][safi], flag); + + if (peer->state == bgp_peer_sEstablished) + { + if (! set && flag == PEER_FLAG_SOFT_RECONFIG) + bgp_clear_adj_in (peer, afi, safi); + else + { + if (flag == PEER_FLAG_REFLECTOR_CLIENT) + peer->last_reset = PEER_DOWN_RR_CLIENT_CHANGE; + else if (flag == PEER_FLAG_RSERVER_CLIENT) + peer->last_reset = PEER_DOWN_RS_CLIENT_CHANGE; + else if (flag == PEER_FLAG_ORF_PREFIX_SM) + peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE; + else if (flag == PEER_FLAG_ORF_PREFIX_RM) + peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE; + + peer_change_action (peer, afi, safi, action.type); + } + } + } + } + return 0; +} + +int +peer_af_flag_set (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag) +{ + return peer_af_flag_modify (peer, afi, safi, flag, 1); +} + +int +peer_af_flag_unset (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag) +{ + return peer_af_flag_modify (peer, afi, safi, flag, 0); +} + +/* EBGP multihop configuration. */ +int +peer_ebgp_multihop_set (struct peer *peer, int ttl) +{ + struct peer_group *group; + struct listnode *node, *nnode; + bgp_session session = peer->session; + + if (peer_sort (peer) == BGP_PEER_IBGP) + return 0; + + peer->ttl = ttl; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (bgp_session_is_active(session) && peer_sort (peer) != BGP_PEER_IBGP) + bgp_session_set_ttl (session, peer->ttl); + } + else + { + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (peer_sort (peer) == BGP_PEER_IBGP) + continue; + + peer->ttl = group->conf->ttl; + session = peer->session; + + if (bgp_session_is_active(session)) + bgp_session_set_ttl (session, peer->ttl); + } + } + return 0; +} + +int +peer_ebgp_multihop_unset (struct peer *peer) +{ + struct peer_group *group; + struct listnode *node, *nnode; + bgp_session session = peer->session; + + if (peer_sort (peer) == BGP_PEER_IBGP) + return 0; + + if (peer_group_active (peer)) + peer->ttl = peer->group->conf->ttl; + else + peer->ttl = 1; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (bgp_session_is_active(session) && peer_sort (peer) != BGP_PEER_IBGP) + bgp_session_set_ttl (session, peer->ttl); + } + else + { + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (peer_sort (peer) == BGP_PEER_IBGP) + continue; + + peer->ttl = 1; + session = peer->session; + + if (bgp_session_is_active(session)) + bgp_session_set_ttl (session, peer->ttl); + } + } + + return 0; +} + +/* Neighbor description. */ +int +peer_description_set (struct peer *peer, char *desc) +{ + if (peer->desc) + XFREE (MTYPE_PEER_DESC, peer->desc); + + peer->desc = XSTRDUP (MTYPE_PEER_DESC, desc); + + return 0; +} + +int +peer_description_unset (struct peer *peer) +{ + if (peer->desc) + XFREE (MTYPE_PEER_DESC, peer->desc); + + peer->desc = NULL; + + return 0; +} + +/* Neighbor update-source. */ +int +peer_update_source_if_set (struct peer *peer, const char *ifname) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + if (peer->update_if) + { + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP) + && strcmp (peer->update_if, ifname) == 0) + return 0; + + XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); + peer->update_if = NULL; + } + + if (peer->update_source) + { + sockunion_free (peer->update_source); + peer->update_source = NULL; + } + + peer->update_if = XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, ifname); + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + return 0; + } + + /* peer-group member updates. */ + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (peer->update_if) + { + if (strcmp (peer->update_if, ifname) == 0) + continue; + + XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); + peer->update_if = NULL; + } + + if (peer->update_source) + { + sockunion_free (peer->update_source); + peer->update_source = NULL; + } + + peer->update_if = XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, ifname); + + peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + return 0; +} + +int +peer_update_source_addr_set (struct peer *peer, union sockunion *su) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + if (peer->update_source) + { + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP) + && sockunion_cmp (peer->update_source, su) == 0) + return 0; + sockunion_free (peer->update_source); + peer->update_source = NULL; + } + + if (peer->update_if) + { + XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); + peer->update_if = NULL; + } + + peer->update_source = sockunion_dup (su); + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + return 0; + } + + /* peer-group member updates. */ + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (peer->update_source) + { + if (sockunion_cmp (peer->update_source, su) == 0) + continue; + sockunion_free (peer->update_source); + peer->update_source = NULL; + } + + if (peer->update_if) + { + XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); + peer->update_if = NULL; + } + + peer->update_source = sockunion_dup (su); + + peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + return 0; +} + +int +peer_update_source_unset (struct peer *peer) +{ + union sockunion *su; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP) + && ! peer->update_source + && ! peer->update_if) + return 0; + + if (peer->update_source) + { + sockunion_free (peer->update_source); + peer->update_source = NULL; + } + if (peer->update_if) + { + XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); + peer->update_if = NULL; + } + + if (peer_group_active (peer)) + { + group = peer->group; + + if (group->conf->update_source) + { + su = sockunion_dup (group->conf->update_source); + peer->update_source = su; + } + else if (group->conf->update_if) + peer->update_if = + XSTRDUP (MTYPE_PEER_UPDATE_SOURCE, group->conf->update_if); + } + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + return 0; + } + + /* peer-group member updates. */ + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (! peer->update_source && ! peer->update_if) + continue; + + if (peer->update_source) + { + sockunion_free (peer->update_source); + peer->update_source = NULL; + } + + if (peer->update_if) + { + XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); + peer->update_if = NULL; + } + + peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + return 0; +} + +int +peer_default_originate_set (struct peer *peer, afi_t afi, safi_t safi, + const char *rmap) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + /* Adress family must be activated. */ + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + /* Default originate can't be used for peer group memeber. */ + if (peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + if (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE) + || (rmap && ! peer->default_rmap[afi][safi].name) + || (rmap && strcmp (rmap, peer->default_rmap[afi][safi].name) != 0)) + { + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE); + + if (rmap) + { + if (peer->default_rmap[afi][safi].name) + free (peer->default_rmap[afi][safi].name); + peer->default_rmap[afi][safi].name = strdup (rmap); + peer->default_rmap[afi][safi].map = route_map_lookup_by_name (rmap); + } + } + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (peer->state == bgp_peer_sEstablished && peer->afc_nego[afi][safi]) + bgp_default_originate (peer, afi, safi, 0); + return 0; + } + + /* peer-group member updates. */ + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE); + + if (rmap) + { + if (peer->default_rmap[afi][safi].name) + free (peer->default_rmap[afi][safi].name); + peer->default_rmap[afi][safi].name = strdup (rmap); + peer->default_rmap[afi][safi].map = route_map_lookup_by_name (rmap); + } + + if (peer->state == bgp_peer_sEstablished && peer->afc_nego[afi][safi]) + bgp_default_originate (peer, afi, safi, 0); + } + return 0; +} + +int +peer_default_originate_unset (struct peer *peer, afi_t afi, safi_t safi) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + /* Adress family must be activated. */ + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + /* Default originate can't be used for peer group memeber. */ + if (peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE)) + { + UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE); + + if (peer->default_rmap[afi][safi].name) + free (peer->default_rmap[afi][safi].name); + peer->default_rmap[afi][safi].name = NULL; + peer->default_rmap[afi][safi].map = NULL; + } + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (peer->state == bgp_peer_sEstablished && peer->afc_nego[afi][safi]) + bgp_default_originate (peer, afi, safi, 1); + return 0; + } + + /* peer-group member updates. */ + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE); + + if (peer->default_rmap[afi][safi].name) + free (peer->default_rmap[afi][safi].name); + peer->default_rmap[afi][safi].name = NULL; + peer->default_rmap[afi][safi].map = NULL; + + if (peer->state == bgp_peer_sEstablished && peer->afc_nego[afi][safi]) + bgp_default_originate (peer, afi, safi, 1); + } + return 0; +} + +int +peer_port_set (struct peer *peer, u_int16_t port) +{ + peer->port = port; + return 0; +} + +int +peer_port_unset (struct peer *peer) +{ + peer->port = BGP_PORT_DEFAULT; + return 0; +} + +/* neighbor weight. */ +int +peer_weight_set (struct peer *peer, u_int16_t weight) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + SET_FLAG (peer->config, PEER_CONFIG_WEIGHT); + peer->weight = weight; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + /* peer-group member updates. */ + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + peer->weight = group->conf->weight; + } + return 0; +} + +int +peer_weight_unset (struct peer *peer) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + /* Set default weight. */ + if (peer_group_active (peer)) + peer->weight = peer->group->conf->weight; + else + peer->weight = 0; + + UNSET_FLAG (peer->config, PEER_CONFIG_WEIGHT); + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + /* peer-group member updates. */ + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + peer->weight = 0; + } + return 0; +} + +int +peer_timers_set (struct peer *peer, u_int32_t keepalive, u_int32_t holdtime) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + /* Not for peer group memeber. */ + if (peer_group_active (peer)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + /* keepalive value check. */ + if (keepalive > 65535) + return BGP_ERR_INVALID_VALUE; + + /* Holdtime value check. */ + if (holdtime > 65535) + return BGP_ERR_INVALID_VALUE; + + /* Holdtime value must be either 0 or greater than 3. */ + if (holdtime < 3 && holdtime != 0) + return BGP_ERR_INVALID_VALUE; + + /* Set value to the configuration. */ + SET_FLAG (peer->config, PEER_CONFIG_TIMER); + peer->holdtime = holdtime; + peer->keepalive = (keepalive < holdtime / 3 ? keepalive : holdtime / 3); + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + /* peer-group member updates. */ + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + SET_FLAG (peer->config, PEER_CONFIG_TIMER); + peer->holdtime = group->conf->holdtime; + peer->keepalive = group->conf->keepalive; + } + return 0; +} + +int +peer_timers_unset (struct peer *peer) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + if (peer_group_active (peer)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + /* Clear configuration. */ + UNSET_FLAG (peer->config, PEER_CONFIG_TIMER); + peer->keepalive = 0; + peer->holdtime = 0; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + /* peer-group member updates. */ + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + UNSET_FLAG (peer->config, PEER_CONFIG_TIMER); + peer->holdtime = 0; + peer->keepalive = 0; + } + + return 0; +} + +int +peer_timers_connect_set (struct peer *peer, u_int32_t connect) +{ + if (peer_group_active (peer)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + if (connect > 65535) + return BGP_ERR_INVALID_VALUE; + + /* Set value to the configuration. */ + SET_FLAG (peer->config, PEER_CONFIG_CONNECT); + peer->connect = connect; + + /* Set value to timer setting. */ + peer->v_connect = connect; + + return 0; +} + +int +peer_timers_connect_unset (struct peer *peer) +{ + if (peer_group_active (peer)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + /* Clear configuration. */ + UNSET_FLAG (peer->config, PEER_CONFIG_CONNECT); + peer->connect = 0; + + /* Set timer setting to default value. */ + peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; + + return 0; +} + +int +peer_advertise_interval_set (struct peer *peer, u_int32_t routeadv) +{ + if (peer_group_active (peer)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + if (routeadv > 600) + return BGP_ERR_INVALID_VALUE; + + SET_FLAG (peer->config, PEER_CONFIG_ROUTEADV); + peer->routeadv = routeadv; + peer->v_routeadv = routeadv; + + return 0; +} + +int +peer_advertise_interval_unset (struct peer *peer) +{ + if (peer_group_active (peer)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + UNSET_FLAG (peer->config, PEER_CONFIG_ROUTEADV); + peer->routeadv = 0; + + if (peer_sort (peer) == BGP_PEER_IBGP) + peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; + else + peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; + + return 0; +} + +/* neighbor interface */ +int +peer_interface_set (struct peer *peer, const char *str) +{ + if (peer->ifname) + free (peer->ifname); + peer->ifname = strdup (str); + + return 0; +} + +int +peer_interface_unset (struct peer *peer) +{ + if (peer->ifname) + free (peer->ifname); + peer->ifname = NULL; + + return 0; +} + +/* Allow-as in. */ +int +peer_allowas_in_set (struct peer *peer, afi_t afi, safi_t safi, int allow_num) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + if (allow_num < 1 || allow_num > 10) + return BGP_ERR_INVALID_VALUE; + + if (peer->allowas_in[afi][safi] != allow_num) + { + peer->allowas_in[afi][safi] = allow_num; + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN); + peer_change_action (peer, afi, safi, peer_change_reset_in); + } + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (peer->allowas_in[afi][safi] != allow_num) + { + peer->allowas_in[afi][safi] = allow_num; + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN); + peer_change_action (peer, afi, safi, peer_change_reset_in); + } + + } + return 0; +} + +int +peer_allowas_in_unset (struct peer *peer, afi_t afi, safi_t safi) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN)) + { + peer->allowas_in[afi][safi] = 0; + peer_af_flag_unset (peer, afi, safi, PEER_FLAG_ALLOWAS_IN); + } + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN)) + { + peer->allowas_in[afi][safi] = 0; + peer_af_flag_unset (peer, afi, safi, PEER_FLAG_ALLOWAS_IN); + } + } + return 0; +} + +int +peer_local_as_set (struct peer *peer, as_t as, int no_prepend) +{ + struct bgp *bgp = peer->bgp; + struct peer_group *group; + struct listnode *node, *nnode; + + if (peer_sort (peer) != BGP_PEER_EBGP + && peer_sort (peer) != BGP_PEER_INTERNAL) + return BGP_ERR_LOCAL_AS_ALLOWED_ONLY_FOR_EBGP; + + if (bgp->as == as) + return BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS; + + if (peer_group_active (peer)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + if (peer->change_local_as == as && + ((CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) && no_prepend) + || (! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) && ! no_prepend))) + return 0; + + peer->change_local_as = as; + if (no_prepend) + SET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); + else + UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + return 0; + } + + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + peer->change_local_as = as; + if (no_prepend) + SET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); + else + UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); + + peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + + return 0; +} + +int +peer_local_as_unset (struct peer *peer) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + if (peer_group_active (peer)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + if (! peer->change_local_as) + return 0; + + peer->change_local_as = 0; + UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + return 0; + } + + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + peer->change_local_as = 0; + UNSET_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND); + + peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + return 0; +} + +/* Set password for authenticating with the peer. */ +int +peer_password_set (struct peer *peer, const char *password) +{ + struct listnode *nn, *nnode; + int len = password ? strlen(password) : 0; + int ret = BGP_SUCCESS; + + if ((len < PEER_PASSWORD_MINLEN) || (len > PEER_PASSWORD_MAXLEN)) + return BGP_ERR_INVALID_VALUE; + + if (peer->password && strcmp (peer->password, password) == 0 + && ! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + if (peer->password) + XFREE (MTYPE_PEER_PASSWORD, peer->password); + + peer->password = XSTRDUP (MTYPE_PEER_PASSWORD, password); + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + return BGP_SUCCESS; + } + + for (ALL_LIST_ELEMENTS (peer->group->peer, nn, nnode, peer)) + { + if (peer->password && strcmp (peer->password, password) == 0) + continue; + + if (peer->password) + XFREE (MTYPE_PEER_PASSWORD, peer->password); + + peer->password = XSTRDUP(MTYPE_PEER_PASSWORD, password); + + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + } + + return ret; +} + +int +peer_password_unset (struct peer *peer) +{ + struct listnode *nn, *nnode; + + if (!peer->password + && !CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + if (!CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + if (peer_group_active (peer) + && peer->group->conf->password + && strcmp (peer->group->conf->password, peer->password) == 0) + return BGP_ERR_PEER_GROUP_HAS_THE_FLAG; + + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + + if (peer->password) + XFREE (MTYPE_PEER_PASSWORD, peer->password); + + peer->password = NULL; + return 0; + } + + XFREE (MTYPE_PEER_PASSWORD, peer->password); + peer->password = NULL; + + for (ALL_LIST_ELEMENTS (peer->group->peer, nn, nnode, peer)) + { + if (!peer->password) + continue; + + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_CONFIG_CHANGE); + + XFREE (MTYPE_PEER_PASSWORD, peer->password); + peer->password = NULL; + } + + return 0; +} + +/* Set distribute list to the peer. */ +int +peer_distribute_set (struct peer *peer, afi_t afi, safi_t safi, int direct, + const char *name) +{ + struct bgp_filter *filter; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + if (direct != FILTER_IN && direct != FILTER_OUT) + return BGP_ERR_INVALID_VALUE; + + if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + filter = &peer->filter[afi][safi]; + + if (filter->plist[direct].ref) + return BGP_ERR_PEER_FILTER_CONFLICT; + + if (filter->dlist[direct].name) + free (filter->dlist[direct].name); + filter->dlist[direct].name = strdup (name); + filter->dlist[direct].alist = access_list_lookup (afi, name); + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + filter = &peer->filter[afi][safi]; + + if (! peer->af_group[afi][safi]) + continue; + + if (filter->dlist[direct].name) + free (filter->dlist[direct].name); + filter->dlist[direct].name = strdup (name); + filter->dlist[direct].alist = access_list_lookup (afi, name); + } + + return 0; +} + +int +peer_distribute_unset (struct peer *peer, afi_t afi, safi_t safi, int direct) +{ + struct bgp_filter *filter; + struct bgp_filter *gfilter; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + if (direct != FILTER_IN && direct != FILTER_OUT) + return BGP_ERR_INVALID_VALUE; + + if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + filter = &peer->filter[afi][safi]; + + /* apply peer-group filter */ + if (peer->af_group[afi][safi]) + { + gfilter = &peer->group->conf->filter[afi][safi]; + + if (gfilter->dlist[direct].name) + { + if (filter->dlist[direct].name) + free (filter->dlist[direct].name); + filter->dlist[direct].name = strdup (gfilter->dlist[direct].name); + filter->dlist[direct].alist = gfilter->dlist[direct].alist; + return 0; + } + } + + if (filter->dlist[direct].name) + free (filter->dlist[direct].name); + filter->dlist[direct].name = NULL; + filter->dlist[direct].alist = NULL; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + filter = &peer->filter[afi][safi]; + + if (! peer->af_group[afi][safi]) + continue; + + if (filter->dlist[direct].name) + free (filter->dlist[direct].name); + filter->dlist[direct].name = NULL; + filter->dlist[direct].alist = NULL; + } + + return 0; +} + +/* Update distribute list. */ +static void +peer_distribute_update (struct access_list *access) +{ + afi_t afi; + safi_t safi; + int direct; + struct listnode *mnode, *mnnode; + struct listnode *node, *nnode; + struct bgp *bgp; + struct peer *peer; + struct peer_group *group; + struct bgp_filter *filter; + + for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp)) + { + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + filter = &peer->filter[afi][safi]; + + for (direct = FILTER_IN; direct < FILTER_MAX; direct++) + { + if (filter->dlist[direct].name) + filter->dlist[direct].alist = + access_list_lookup (afi, filter->dlist[direct].name); + else + filter->dlist[direct].alist = NULL; + } + } + } + for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group)) + { + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + filter = &group->conf->filter[afi][safi]; + + for (direct = FILTER_IN; direct < FILTER_MAX; direct++) + { + if (filter->dlist[direct].name) + filter->dlist[direct].alist = + access_list_lookup (afi, filter->dlist[direct].name); + else + filter->dlist[direct].alist = NULL; + } + } + } + } +} + +/* Set prefix list to the peer. */ +int +peer_prefix_list_set (struct peer *peer, afi_t afi, safi_t safi, int direct, + const char *name) +{ + struct bgp_filter *filter; + struct peer_group *group; + struct listnode *node, *nnode; + prefix_list_ref ref ; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + if (direct != FILTER_IN && direct != FILTER_OUT) + return BGP_ERR_INVALID_VALUE; + + if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + filter = &peer->filter[afi][safi]; + + if (filter->dlist[direct].name) + return BGP_ERR_PEER_FILTER_CONFLICT; + + ref = prefix_list_set_ref(&filter->plist[direct].ref, afi, name) ; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + filter = &peer->filter[afi][safi]; + + if (! peer->af_group[afi][safi]) + continue; + + prefix_list_copy_ref(&filter->plist[direct].ref, ref) ; + } + return 0; +} + +int +peer_prefix_list_unset (struct peer *peer, afi_t afi, safi_t safi, int direct) +{ + struct bgp_filter *filter; + struct bgp_filter *gfilter; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + if (direct != FILTER_IN && direct != FILTER_OUT) + return BGP_ERR_INVALID_VALUE; + + if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + filter = &peer->filter[afi][safi]; + + /* apply peer-group filter */ + if (peer->af_group[afi][safi]) + { + gfilter = &peer->group->conf->filter[afi][safi]; + + if (gfilter->plist[direct].ref) + { + prefix_list_copy_ref(&filter->plist[direct].ref, + gfilter->plist[direct].ref) ; + return 0; + } + } + + prefix_list_unset_ref(&filter->plist[direct].ref) ; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + filter = &peer->filter[afi][safi]; + + if (! peer->af_group[afi][safi]) + continue; + + prefix_list_unset_ref(&filter->plist[direct].ref) ; + } + + return 0; +} + +/* Update prefix-list list. */ +static void +peer_prefix_list_update (struct prefix_list *plist) +{ + /* This function used to fix up the addresses of prefix lists whenever + * a prefix list was changed. That is now done by the symbol reference + * mechanism. + * + * This function could have a use in updating a peer when a prefix list + * is changed ? + */ +} + +int +peer_aslist_set (struct peer *peer, afi_t afi, safi_t safi, int direct, + const char *name) +{ + struct bgp_filter *filter; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + if (direct != FILTER_IN && direct != FILTER_OUT) + return BGP_ERR_INVALID_VALUE; + + if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + filter = &peer->filter[afi][safi]; + + if (filter->aslist[direct].name) + free (filter->aslist[direct].name); + filter->aslist[direct].name = strdup (name); + filter->aslist[direct].aslist = as_list_lookup (name); + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + filter = &peer->filter[afi][safi]; + + if (! peer->af_group[afi][safi]) + continue; + + if (filter->aslist[direct].name) + free (filter->aslist[direct].name); + filter->aslist[direct].name = strdup (name); + filter->aslist[direct].aslist = as_list_lookup (name); + } + return 0; +} + +int +peer_aslist_unset (struct peer *peer,afi_t afi, safi_t safi, int direct) +{ + struct bgp_filter *filter; + struct bgp_filter *gfilter; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + if (direct != FILTER_IN && direct != FILTER_OUT) + return BGP_ERR_INVALID_VALUE; + + if (direct == FILTER_OUT && peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + filter = &peer->filter[afi][safi]; + + /* apply peer-group filter */ + if (peer->af_group[afi][safi]) + { + gfilter = &peer->group->conf->filter[afi][safi]; + + if (gfilter->aslist[direct].name) + { + if (filter->aslist[direct].name) + free (filter->aslist[direct].name); + filter->aslist[direct].name = strdup (gfilter->aslist[direct].name); + filter->aslist[direct].aslist = gfilter->aslist[direct].aslist; + return 0; + } + } + + if (filter->aslist[direct].name) + free (filter->aslist[direct].name); + filter->aslist[direct].name = NULL; + filter->aslist[direct].aslist = NULL; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + filter = &peer->filter[afi][safi]; + + if (! peer->af_group[afi][safi]) + continue; + + if (filter->aslist[direct].name) + free (filter->aslist[direct].name); + filter->aslist[direct].name = NULL; + filter->aslist[direct].aslist = NULL; + } + + return 0; +} + +static void +peer_aslist_update (void) +{ + afi_t afi; + safi_t safi; + int direct; + struct listnode *mnode, *mnnode; + struct listnode *node, *nnode; + struct bgp *bgp; + struct peer *peer; + struct peer_group *group; + struct bgp_filter *filter; + + for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp)) + { + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + filter = &peer->filter[afi][safi]; + + for (direct = FILTER_IN; direct < FILTER_MAX; direct++) + { + if (filter->aslist[direct].name) + filter->aslist[direct].aslist = + as_list_lookup (filter->aslist[direct].name); + else + filter->aslist[direct].aslist = NULL; + } + } + } + for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group)) + { + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + filter = &group->conf->filter[afi][safi]; + + for (direct = FILTER_IN; direct < FILTER_MAX; direct++) + { + if (filter->aslist[direct].name) + filter->aslist[direct].aslist = + as_list_lookup (filter->aslist[direct].name); + else + filter->aslist[direct].aslist = NULL; + } + } + } + } +} + +/* Set route-map to the peer. */ +int +peer_route_map_set (struct peer *peer, afi_t afi, safi_t safi, int direct, + const char *name) +{ + struct bgp_filter *filter; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + if (direct != RMAP_IN && direct != RMAP_OUT && + direct != RMAP_IMPORT && direct != RMAP_EXPORT) + return BGP_ERR_INVALID_VALUE; + + if ( (direct == RMAP_OUT || direct == RMAP_IMPORT) + && peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + filter = &peer->filter[afi][safi]; + + if (filter->map[direct].name) + free (filter->map[direct].name); + + filter->map[direct].name = strdup (name); + filter->map[direct].map = route_map_lookup_by_name (name); + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + filter = &peer->filter[afi][safi]; + + if (! peer->af_group[afi][safi]) + continue; + + if (filter->map[direct].name) + free (filter->map[direct].name); + filter->map[direct].name = strdup (name); + filter->map[direct].map = route_map_lookup_by_name (name); + } + return 0; +} + +/* Unset route-map from the peer. */ +int +peer_route_map_unset (struct peer *peer, afi_t afi, safi_t safi, int direct) +{ + struct bgp_filter *filter; + struct bgp_filter *gfilter; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + if (direct != RMAP_IN && direct != RMAP_OUT && + direct != RMAP_IMPORT && direct != RMAP_EXPORT) + return BGP_ERR_INVALID_VALUE; + + if ( (direct == RMAP_OUT || direct == RMAP_IMPORT) + && peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + filter = &peer->filter[afi][safi]; + + /* apply peer-group filter */ + if (peer->af_group[afi][safi]) + { + gfilter = &peer->group->conf->filter[afi][safi]; + + if (gfilter->map[direct].name) + { + if (filter->map[direct].name) + free (filter->map[direct].name); + filter->map[direct].name = strdup (gfilter->map[direct].name); + filter->map[direct].map = gfilter->map[direct].map; + return 0; + } + } + + if (filter->map[direct].name) + free (filter->map[direct].name); + filter->map[direct].name = NULL; + filter->map[direct].map = NULL; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + filter = &peer->filter[afi][safi]; + + if (! peer->af_group[afi][safi]) + continue; + + if (filter->map[direct].name) + free (filter->map[direct].name); + filter->map[direct].name = NULL; + filter->map[direct].map = NULL; + } + return 0; +} + +/* Set unsuppress-map to the peer. */ +int +peer_unsuppress_map_set (struct peer *peer, afi_t afi, safi_t safi, + const char *name) +{ + struct bgp_filter *filter; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + if (peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + filter = &peer->filter[afi][safi]; + + if (filter->usmap.name) + free (filter->usmap.name); + + filter->usmap.name = strdup (name); + filter->usmap.map = route_map_lookup_by_name (name); + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + filter = &peer->filter[afi][safi]; + + if (! peer->af_group[afi][safi]) + continue; + + if (filter->usmap.name) + free (filter->usmap.name); + filter->usmap.name = strdup (name); + filter->usmap.map = route_map_lookup_by_name (name); + } + return 0; +} + +/* Unset route-map from the peer. */ +int +peer_unsuppress_map_unset (struct peer *peer, afi_t afi, safi_t safi) +{ + struct bgp_filter *filter; + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + if (peer_is_group_member (peer, afi, safi)) + return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; + + filter = &peer->filter[afi][safi]; + + if (filter->usmap.name) + free (filter->usmap.name); + filter->usmap.name = NULL; + filter->usmap.map = NULL; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + filter = &peer->filter[afi][safi]; + + if (! peer->af_group[afi][safi]) + continue; + + if (filter->usmap.name) + free (filter->usmap.name); + filter->usmap.name = NULL; + filter->usmap.map = NULL; + } + return 0; +} + +int +peer_maximum_prefix_set (struct peer *peer, afi_t afi, safi_t safi, + u_int32_t max, u_char threshold, + int warning, u_int16_t restart) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX); + peer->pmax[afi][safi] = max; + peer->pmax_threshold[afi][safi] = threshold; + peer->pmax_restart[afi][safi] = restart; + if (warning) + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); + else + UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (! peer->af_group[afi][safi]) + continue; + + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX); + peer->pmax[afi][safi] = max; + peer->pmax_threshold[afi][safi] = threshold; + peer->pmax_restart[afi][safi] = restart; + if (warning) + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); + else + UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); + } + return 0; +} + +int +peer_maximum_prefix_unset (struct peer *peer, afi_t afi, safi_t safi) +{ + struct peer_group *group; + struct listnode *node, *nnode; + + if (! peer->afc[afi][safi]) + return BGP_ERR_PEER_INACTIVE; + + /* apply peer-group config */ + if (peer->af_group[afi][safi]) + { + if (CHECK_FLAG (peer->group->conf->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX)) + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX); + else + UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX); + + if (CHECK_FLAG (peer->group->conf->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_WARNING)) + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); + else + UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); + + peer->pmax[afi][safi] = peer->group->conf->pmax[afi][safi]; + peer->pmax_threshold[afi][safi] = peer->group->conf->pmax_threshold[afi][safi]; + peer->pmax_restart[afi][safi] = peer->group->conf->pmax_restart[afi][safi]; + return 0; + } + + UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX); + UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); + peer->pmax[afi][safi] = 0; + peer->pmax_threshold[afi][safi] = 0; + peer->pmax_restart[afi][safi] = 0; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (! peer->af_group[afi][safi]) + continue; + + UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX); + UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); + peer->pmax[afi][safi] = 0; + peer->pmax_threshold[afi][safi] = 0; + peer->pmax_restart[afi][safi] = 0; + } + return 0; +} + +int +peer_clear (struct peer *peer) +{ + if (! CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN)) + { + if (CHECK_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW)) + { + UNSET_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW); + if (peer->t_pmax_restart) + { + BGP_TIMER_OFF (peer->t_pmax_restart); + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s Maximum-prefix restart timer cancelled", + peer->host); + } + + /* Beware we may still be clearing, if so the end of + * clearing will enable the peer */ + if (peer->state == bgp_peer_sIdle) + bgp_peer_enable(peer); + + return 0; + } + + peer->v_start = BGP_INIT_START_TIMER; + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_ADMIN_RESET); + } + return 0; +} + +int +peer_clear_soft (struct peer *peer, afi_t afi, safi_t safi, + enum bgp_clear_type stype) +{ + if (peer->state == bgp_peer_sEstablished) + return 0; + + if (! peer->afc[afi][safi]) + return BGP_ERR_AF_UNCONFIGURED; + + if (stype == BGP_CLEAR_SOFT_RSCLIENT) + { + if (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)) + return 0; + bgp_check_local_routes_rsclient (peer, afi, safi); + bgp_soft_reconfig_rsclient (peer, afi, safi); + } + + if (stype == BGP_CLEAR_SOFT_OUT || stype == BGP_CLEAR_SOFT_BOTH) + bgp_announce_route (peer, afi, safi); + + if (stype == BGP_CLEAR_SOFT_IN_ORF_PREFIX) + { + if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV) + && (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV) + || CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_OLD_RCV))) + { + struct bgp_filter *filter = &peer->filter[afi][safi]; + u_char prefix_type; + + if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV)) + prefix_type = ORF_TYPE_PREFIX; + else + prefix_type = ORF_TYPE_PREFIX_OLD; + + if (filter->plist[FILTER_IN].ref) + { + if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND)) + bgp_route_refresh_send (peer, afi, safi, + prefix_type, REFRESH_DEFER, 1); + bgp_route_refresh_send (peer, afi, safi, prefix_type, + REFRESH_IMMEDIATE, 0); + } + else + { + if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_PREFIX_SEND)) + bgp_route_refresh_send (peer, afi, safi, + prefix_type, REFRESH_IMMEDIATE, 1); + else + bgp_route_refresh_send (peer, afi, safi, 0, 0, 0); + } + return 0; + } + } + + if (stype == BGP_CLEAR_SOFT_IN || stype == BGP_CLEAR_SOFT_BOTH + || stype == BGP_CLEAR_SOFT_IN_ORF_PREFIX) + { + /* If neighbor has soft reconfiguration inbound flag. + Use Adj-RIB-In database. */ + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)) + bgp_soft_reconfig_in (peer, afi, safi); + else + { + /* If neighbor has route refresh capability, send route refresh + message to the peer. */ + if (CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_OLD_RCV) + || CHECK_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV)) + bgp_route_refresh_send (peer, afi, safi, 0, 0, 0); + else + return BGP_ERR_SOFT_RECONFIG_UNCONFIGURED; + } + } + return 0; +} + +/* Display peer uptime.*/ +/* XXX: why does this function return char * when it takes buffer? */ +char * +peer_uptime (time_t uptime2, char *buf, size_t len) +{ + time_t uptime1; + struct tm *tm; + + /* Check buffer length. */ + if (len < BGP_UPTIME_LEN) + { + zlog_warn ("peer_uptime (): buffer shortage %lu", (u_long)len); + /* XXX: should return status instead of buf... */ + snprintf (buf, len, "<error> "); + return buf; + } + + /* If there is no connection has been done before print `never'. */ + if (uptime2 == 0) + { + snprintf (buf, len, "never "); + return buf; + } + + /* Get current time. */ + uptime1 = time (NULL); + uptime1 -= uptime2; + tm = gmtime (&uptime1); + + /* Making formatted timer strings. */ +#define ONE_DAY_SECOND 60*60*24 +#define ONE_WEEK_SECOND 60*60*24*7 + + if (uptime1 < ONE_DAY_SECOND) + snprintf (buf, len, "%02d:%02d:%02d", + tm->tm_hour, tm->tm_min, tm->tm_sec); + else if (uptime1 < ONE_WEEK_SECOND) + snprintf (buf, len, "%dd%02dh%02dm", + tm->tm_yday, tm->tm_hour, tm->tm_min); + else + snprintf (buf, len, "%02dw%dd%02dh", + tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); + return buf; +} + +static void +bgp_config_write_filter (struct vty *vty, struct peer *peer, + afi_t afi, safi_t safi) +{ + struct bgp_filter *filter; + struct bgp_filter *gfilter = NULL; + char *addr; + int in = FILTER_IN; + int out = FILTER_OUT; + + addr = peer->host; + filter = &peer->filter[afi][safi]; + if (peer->af_group[afi][safi]) + gfilter = &peer->group->conf->filter[afi][safi]; + + /* distribute-list. */ + if (filter->dlist[in].name) + if (! gfilter || ! gfilter->dlist[in].name + || strcmp (filter->dlist[in].name, gfilter->dlist[in].name) != 0) + vty_out (vty, " neighbor %s distribute-list %s in%s", addr, + filter->dlist[in].name, VTY_NEWLINE); + if (filter->dlist[out].name && ! gfilter) + vty_out (vty, " neighbor %s distribute-list %s out%s", addr, + filter->dlist[out].name, VTY_NEWLINE); + + /* prefix-list. */ + if ( filter->plist[in].ref && (! gfilter + || (prefix_list_ref_ident(gfilter->plist[in].ref) + != prefix_list_ref_ident(filter->plist[in].ref))) ) + vty_out (vty, " neighbor %s prefix-list %s in%s", addr, + prefix_list_ref_name(filter->plist[in].ref), VTY_NEWLINE); + + if (filter->plist[out].ref && ! gfilter) + vty_out (vty, " neighbor %s prefix-list %s out%s", addr, + prefix_list_ref_name(filter->plist[out].ref), VTY_NEWLINE); + + /* route-map. */ + if (filter->map[RMAP_IN].name) + if (! gfilter || ! gfilter->map[RMAP_IN].name + || strcmp (filter->map[RMAP_IN].name, gfilter->map[RMAP_IN].name) != 0) + vty_out (vty, " neighbor %s route-map %s in%s", addr, + filter->map[RMAP_IN].name, VTY_NEWLINE); + if (filter->map[RMAP_OUT].name && ! gfilter) + vty_out (vty, " neighbor %s route-map %s out%s", addr, + filter->map[RMAP_OUT].name, VTY_NEWLINE); + if (filter->map[RMAP_IMPORT].name && ! gfilter) + vty_out (vty, " neighbor %s route-map %s import%s", addr, + filter->map[RMAP_IMPORT].name, VTY_NEWLINE); + if (filter->map[RMAP_EXPORT].name) + if (! gfilter || ! gfilter->map[RMAP_EXPORT].name + || strcmp (filter->map[RMAP_EXPORT].name, + gfilter->map[RMAP_EXPORT].name) != 0) + vty_out (vty, " neighbor %s route-map %s export%s", addr, + filter->map[RMAP_EXPORT].name, VTY_NEWLINE); + + /* unsuppress-map */ + if (filter->usmap.name && ! gfilter) + vty_out (vty, " neighbor %s unsuppress-map %s%s", addr, + filter->usmap.name, VTY_NEWLINE); + + /* filter-list. */ + if (filter->aslist[in].name) + if (! gfilter || ! gfilter->aslist[in].name + || strcmp (filter->aslist[in].name, gfilter->aslist[in].name) != 0) + vty_out (vty, " neighbor %s filter-list %s in%s", addr, + filter->aslist[in].name, VTY_NEWLINE); + if (filter->aslist[out].name && ! gfilter) + vty_out (vty, " neighbor %s filter-list %s out%s", addr, + filter->aslist[out].name, VTY_NEWLINE); +} + +/* BGP peer configuration display function. */ +static void +bgp_config_write_peer (struct vty *vty, struct bgp *bgp, + struct peer *peer, afi_t afi, safi_t safi) +{ + struct bgp_filter *filter; + struct peer *g_peer = NULL; + char buf[SU_ADDRSTRLEN]; + char *addr; + + filter = &peer->filter[afi][safi]; + addr = peer->host; + if (peer_group_active (peer)) + g_peer = peer->group->conf; + + /************************************ + ****** Global to the neighbor ****** + ************************************/ + if (afi == AFI_IP && safi == SAFI_UNICAST) + { + /* remote-as. */ + if (! peer_group_active (peer)) + { + if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + vty_out (vty, " neighbor %s peer-group%s", addr, + VTY_NEWLINE); + if (peer->as) + vty_out (vty, " neighbor %s remote-as %u%s", addr, peer->as, + VTY_NEWLINE); + } + else + { + if (! g_peer->as) + vty_out (vty, " neighbor %s remote-as %u%s", addr, peer->as, + VTY_NEWLINE); + if (peer->af_group[AFI_IP][SAFI_UNICAST]) + vty_out (vty, " neighbor %s peer-group %s%s", addr, + peer->group->name, VTY_NEWLINE); + } + + /* local-as. */ + if (peer->change_local_as) + if (! peer_group_active (peer)) + vty_out (vty, " neighbor %s local-as %u%s%s", addr, + peer->change_local_as, + CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND) ? + " no-prepend" : "", VTY_NEWLINE); + + /* Description. */ + if (peer->desc) + vty_out (vty, " neighbor %s description %s%s", addr, peer->desc, + VTY_NEWLINE); + + /* Shutdown. */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN)) + if (! peer_group_active (peer) || + ! CHECK_FLAG (g_peer->flags, PEER_FLAG_SHUTDOWN)) + vty_out (vty, " neighbor %s shutdown%s", addr, VTY_NEWLINE); + + /* Password. */ + if (peer->password) + if (!peer_group_active (peer) + || ! g_peer->password + || strcmp (peer->password, g_peer->password) != 0) + vty_out (vty, " neighbor %s password %s%s", addr, peer->password, + VTY_NEWLINE); + + /* BGP port. */ + if (peer->port != BGP_PORT_DEFAULT) + vty_out (vty, " neighbor %s port %d%s", addr, peer->port, + VTY_NEWLINE); + + /* Local interface name. */ + if (peer->ifname) + vty_out (vty, " neighbor %s interface %s%s", addr, peer->ifname, + VTY_NEWLINE); + + /* Passive. */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_PASSIVE)) + if (! peer_group_active (peer) || + ! CHECK_FLAG (g_peer->flags, PEER_FLAG_PASSIVE)) + vty_out (vty, " neighbor %s passive%s", addr, VTY_NEWLINE); + + /* EBGP multihop. */ + if (peer_sort (peer) != BGP_PEER_IBGP && peer->ttl != 1) + if (! peer_group_active (peer) || + g_peer->ttl != peer->ttl) + vty_out (vty, " neighbor %s ebgp-multihop %d%s", addr, peer->ttl, + VTY_NEWLINE); + + /* disable-connected-check. */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)) + if (! peer_group_active (peer) || + ! CHECK_FLAG (g_peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)) + vty_out (vty, " neighbor %s disable-connected-check%s", addr, VTY_NEWLINE); + + /* Update-source. */ + if (peer->update_if) + if (! peer_group_active (peer) || ! g_peer->update_if + || strcmp (g_peer->update_if, peer->update_if) != 0) + vty_out (vty, " neighbor %s update-source %s%s", addr, + peer->update_if, VTY_NEWLINE); + if (peer->update_source) + if (! peer_group_active (peer) || ! g_peer->update_source + || sockunion_cmp (g_peer->update_source, + peer->update_source) != 0) + vty_out (vty, " neighbor %s update-source %s%s", addr, + sockunion2str (peer->update_source, buf, SU_ADDRSTRLEN), + VTY_NEWLINE); + + /* advertisement-interval */ + if (CHECK_FLAG (peer->config, PEER_CONFIG_ROUTEADV)) + vty_out (vty, " neighbor %s advertisement-interval %d%s", + addr, peer->v_routeadv, VTY_NEWLINE); + + /* timers. */ + if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER) + && ! peer_group_active (peer)) + vty_out (vty, " neighbor %s timers %d %d%s", addr, + peer->keepalive, peer->holdtime, VTY_NEWLINE); + + if (CHECK_FLAG (peer->config, PEER_CONFIG_CONNECT)) + vty_out (vty, " neighbor %s timers connect %d%s", addr, + peer->connect, VTY_NEWLINE); + + /* Default weight. */ + if (CHECK_FLAG (peer->config, PEER_CONFIG_WEIGHT)) + if (! peer_group_active (peer) || + g_peer->weight != peer->weight) + vty_out (vty, " neighbor %s weight %d%s", addr, peer->weight, + VTY_NEWLINE); + + /* Dynamic capability. */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY)) + if (! peer_group_active (peer) || + ! CHECK_FLAG (g_peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY)) + vty_out (vty, " neighbor %s capability dynamic%s", addr, + VTY_NEWLINE); + + /* dont capability negotiation. */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_DONT_CAPABILITY)) + if (! peer_group_active (peer) || + ! CHECK_FLAG (g_peer->flags, PEER_FLAG_DONT_CAPABILITY)) + vty_out (vty, " neighbor %s dont-capability-negotiate%s", addr, + VTY_NEWLINE); + + /* override capability negotiation. */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) + if (! peer_group_active (peer) || + ! CHECK_FLAG (g_peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) + vty_out (vty, " neighbor %s override-capability%s", addr, + VTY_NEWLINE); + + /* strict capability negotiation. */ + if (CHECK_FLAG (peer->flags, PEER_FLAG_STRICT_CAP_MATCH)) + if (! peer_group_active (peer) || + ! CHECK_FLAG (g_peer->flags, PEER_FLAG_STRICT_CAP_MATCH)) + vty_out (vty, " neighbor %s strict-capability-match%s", addr, + VTY_NEWLINE); + + if (! peer_group_active (peer)) + { + if (bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4)) + { + if (peer->afc[AFI_IP][SAFI_UNICAST]) + vty_out (vty, " neighbor %s activate%s", addr, VTY_NEWLINE); + } + else + { + if (! peer->afc[AFI_IP][SAFI_UNICAST]) + vty_out (vty, " no neighbor %s activate%s", addr, VTY_NEWLINE); + } + } + } + + + /************************************ + ****** Per AF to the neighbor ****** + ************************************/ + + if (! (afi == AFI_IP && safi == SAFI_UNICAST)) + { + if (peer->af_group[afi][safi]) + vty_out (vty, " neighbor %s peer-group %s%s", addr, + peer->group->name, VTY_NEWLINE); + else + vty_out (vty, " neighbor %s activate%s", addr, VTY_NEWLINE); + } + + /* ORF capability. */ + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM) + || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM)) + if (! peer->af_group[afi][safi]) + { + vty_out (vty, " neighbor %s capability orf prefix-list", addr); + + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM) + && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM)) + vty_out (vty, " both"); + else if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM)) + vty_out (vty, " send"); + else + vty_out (vty, " receive"); + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* Route reflector client. */ + if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_REFLECTOR_CLIENT) + && ! peer->af_group[afi][safi]) + vty_out (vty, " neighbor %s route-reflector-client%s", addr, + VTY_NEWLINE); + + /* Nexthop self. */ + if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_NEXTHOP_SELF) + && ! peer->af_group[afi][safi]) + vty_out (vty, " neighbor %s next-hop-self%s", addr, VTY_NEWLINE); + + /* Remove private AS. */ + if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS) + && ! peer->af_group[afi][safi]) + vty_out (vty, " neighbor %s remove-private-AS%s", + addr, VTY_NEWLINE); + + /* send-community print. */ + if (! peer->af_group[afi][safi]) + { + if (bgp_option_check (BGP_OPT_CONFIG_CISCO)) + { + if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY) + && peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY)) + vty_out (vty, " neighbor %s send-community both%s", addr, VTY_NEWLINE); + else if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY)) + vty_out (vty, " neighbor %s send-community extended%s", + addr, VTY_NEWLINE); + else if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY)) + vty_out (vty, " neighbor %s send-community%s", addr, VTY_NEWLINE); + } + else + { + if (! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY) + && ! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY)) + vty_out (vty, " no neighbor %s send-community both%s", + addr, VTY_NEWLINE); + else if (! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY)) + vty_out (vty, " no neighbor %s send-community extended%s", + addr, VTY_NEWLINE); + else if (! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY)) + vty_out (vty, " no neighbor %s send-community%s", + addr, VTY_NEWLINE); + } + } + + /* Default information */ + if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_DEFAULT_ORIGINATE) + && ! peer->af_group[afi][safi]) + { + vty_out (vty, " neighbor %s default-originate", addr); + if (peer->default_rmap[afi][safi].name) + vty_out (vty, " route-map %s", peer->default_rmap[afi][safi].name); + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* Soft reconfiguration inbound. */ + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)) + if (! peer->af_group[afi][safi] || + ! CHECK_FLAG (g_peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)) + vty_out (vty, " neighbor %s soft-reconfiguration inbound%s", addr, + VTY_NEWLINE); + + /* maximum-prefix. */ + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) + if (! peer->af_group[afi][safi] + || g_peer->pmax[afi][safi] != peer->pmax[afi][safi] + || g_peer->pmax_threshold[afi][safi] != peer->pmax_threshold[afi][safi] + || CHECK_FLAG (g_peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING) + != CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING)) + { + vty_out (vty, " neighbor %s maximum-prefix %ld", addr, peer->pmax[afi][safi]); + if (peer->pmax_threshold[afi][safi] != MAXIMUM_PREFIX_THRESHOLD_DEFAULT) + vty_out (vty, " %d", peer->pmax_threshold[afi][safi]); + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING)) + vty_out (vty, " warning-only"); + if (peer->pmax_restart[afi][safi]) + vty_out (vty, " restart %d", peer->pmax_restart[afi][safi]); + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* Route server client. */ + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) + && ! peer->af_group[afi][safi]) + vty_out (vty, " neighbor %s route-server-client%s", addr, VTY_NEWLINE); + + /* Allow AS in. */ + if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_ALLOWAS_IN)) + if (! peer_group_active (peer) + || ! peer_af_flag_check (g_peer, afi, safi, PEER_FLAG_ALLOWAS_IN) + || peer->allowas_in[afi][safi] != g_peer->allowas_in[afi][safi]) + { + if (peer->allowas_in[afi][safi] == 3) + vty_out (vty, " neighbor %s allowas-in%s", addr, VTY_NEWLINE); + else + vty_out (vty, " neighbor %s allowas-in %d%s", addr, + peer->allowas_in[afi][safi], VTY_NEWLINE); + } + + /* Filter. */ + bgp_config_write_filter (vty, peer, afi, safi); + + /* atribute-unchanged. */ + if ((CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED) + || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED) + || CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED)) + && ! peer->af_group[afi][safi]) + { + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED) + && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED) + && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED)) + vty_out (vty, " neighbor %s attribute-unchanged%s", addr, VTY_NEWLINE); + else + vty_out (vty, " neighbor %s attribute-unchanged%s%s%s%s", addr, + (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED)) ? + " as-path" : "", + (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED)) ? + " next-hop" : "", + (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED)) ? + " med" : "", VTY_NEWLINE); + } +} + +/* Display "address-family" configuration header. */ +void +bgp_config_write_family_header (struct vty *vty, afi_t afi, safi_t safi, + int *write) +{ + if (*write) + return; + + if (afi == AFI_IP && safi == SAFI_UNICAST) + return; + + vty_out (vty, "!%s address-family ", VTY_NEWLINE); + + if (afi == AFI_IP) + { + if (safi == SAFI_MULTICAST) + vty_out (vty, "ipv4 multicast"); + else if (safi == SAFI_MPLS_VPN) + vty_out (vty, "vpnv4 unicast"); + } + else if (afi == AFI_IP6) + { + vty_out (vty, "ipv6"); + + if (safi == SAFI_MULTICAST) + vty_out (vty, " multicast"); + } + + vty_out (vty, "%s", VTY_NEWLINE); + + *write = 1; +} + +/* Address family based peer configuration display. */ +static int +bgp_config_write_family (struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi) +{ + int write = 0; + struct peer *peer; + struct peer_group *group; + struct listnode *node, *nnode; + + bgp_config_write_network (vty, bgp, afi, safi, &write); + + bgp_config_write_redistribute (vty, bgp, afi, safi, &write); + + for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group)) + { + if (group->conf->afc[afi][safi]) + { + bgp_config_write_family_header (vty, afi, safi, &write); + bgp_config_write_peer (vty, bgp, group->conf, afi, safi); + } + } + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (peer->afc[afi][safi]) + { + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + { + bgp_config_write_family_header (vty, afi, safi, &write); + bgp_config_write_peer (vty, bgp, peer, afi, safi); + } + } + } + if (write) + vty_out (vty, " exit-address-family%s", VTY_NEWLINE); + + return write; +} + +int +bgp_config_write (struct vty *vty) +{ + int write = 0; + struct bgp *bgp; + struct peer_group *group; + struct peer *peer; + struct listnode *node, *nnode; + struct listnode *mnode, *mnnode; + + /* BGP Multiple instance. */ + if (bgp_option_check (BGP_OPT_MULTIPLE_INSTANCE)) + { + vty_out (vty, "bgp multiple-instance%s", VTY_NEWLINE); + write++; + } + + /* BGP Config type. */ + if (bgp_option_check (BGP_OPT_CONFIG_CISCO)) + { + vty_out (vty, "bgp config-type cisco%s", VTY_NEWLINE); + write++; + } + + /* BGP configuration. */ + for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp)) + { + if (write) + vty_out (vty, "!%s", VTY_NEWLINE); + + /* Router bgp ASN */ + vty_out (vty, "router bgp %u", bgp->as); + + if (bgp_option_check (BGP_OPT_MULTIPLE_INSTANCE)) + { + if (bgp->name) + vty_out (vty, " view %s", bgp->name); + } + vty_out (vty, "%s", VTY_NEWLINE); + + /* No Synchronization */ + if (bgp_option_check (BGP_OPT_CONFIG_CISCO)) + vty_out (vty, " no synchronization%s", VTY_NEWLINE); + + /* BGP fast-external-failover. */ + if (CHECK_FLAG (bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER)) + vty_out (vty, " no bgp fast-external-failover%s", VTY_NEWLINE); + + /* BGP router ID. */ + if (CHECK_FLAG (bgp->config, BGP_CONFIG_ROUTER_ID)) + vty_out (vty, " bgp router-id %s%s", safe_inet_ntoa (bgp->router_id), + VTY_NEWLINE); + + /* BGP log-neighbor-changes. */ + if (bgp_flag_check (bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) + vty_out (vty, " bgp log-neighbor-changes%s", VTY_NEWLINE); + + /* BGP configuration. */ + if (bgp_flag_check (bgp, BGP_FLAG_ALWAYS_COMPARE_MED)) + vty_out (vty, " bgp always-compare-med%s", VTY_NEWLINE); + + /* BGP default ipv4-unicast. */ + if (bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4)) + vty_out (vty, " no bgp default ipv4-unicast%s", VTY_NEWLINE); + + /* BGP default local-preference. */ + if (bgp->default_local_pref != BGP_DEFAULT_LOCAL_PREF) + vty_out (vty, " bgp default local-preference %d%s", + bgp->default_local_pref, VTY_NEWLINE); + + /* BGP client-to-client reflection. */ + if (bgp_flag_check (bgp, BGP_FLAG_NO_CLIENT_TO_CLIENT)) + vty_out (vty, " no bgp client-to-client reflection%s", VTY_NEWLINE); + + /* BGP cluster ID. */ + if (CHECK_FLAG (bgp->config, BGP_CONFIG_CLUSTER_ID)) + vty_out (vty, " bgp cluster-id %s%s", safe_inet_ntoa (bgp->cluster_id), + VTY_NEWLINE); + + /* Confederation identifier*/ + if (CHECK_FLAG (bgp->config, BGP_CONFIG_CONFEDERATION)) + vty_out (vty, " bgp confederation identifier %i%s", bgp->confed_id, + VTY_NEWLINE); + + /* Confederation peer */ + if (bgp->confed_peers_cnt > 0) + { + int i; + + vty_out (vty, " bgp confederation peers"); + + for (i = 0; i < bgp->confed_peers_cnt; i++) + vty_out(vty, " %u", bgp->confed_peers[i]); + + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* BGP enforce-first-as. */ + if (bgp_flag_check (bgp, BGP_FLAG_ENFORCE_FIRST_AS)) + vty_out (vty, " bgp enforce-first-as%s", VTY_NEWLINE); + + /* BGP deterministic-med. */ + if (bgp_flag_check (bgp, BGP_FLAG_DETERMINISTIC_MED)) + vty_out (vty, " bgp deterministic-med%s", VTY_NEWLINE); + + /* BGP graceful-restart. */ + if (bgp->stalepath_time != BGP_DEFAULT_STALEPATH_TIME) + vty_out (vty, " bgp graceful-restart stalepath-time %d%s", + bgp->stalepath_time, VTY_NEWLINE); + if (bgp_flag_check (bgp, BGP_FLAG_GRACEFUL_RESTART)) + vty_out (vty, " bgp graceful-restart%s", VTY_NEWLINE); + + /* BGP bestpath method. */ + if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_IGNORE)) + vty_out (vty, " bgp bestpath as-path ignore%s", VTY_NEWLINE); + if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_CONFED)) + vty_out (vty, " bgp bestpath as-path confed%s", VTY_NEWLINE); + if (bgp_flag_check (bgp, BGP_FLAG_COMPARE_ROUTER_ID)) + vty_out (vty, " bgp bestpath compare-routerid%s", VTY_NEWLINE); + if (bgp_flag_check (bgp, BGP_FLAG_MED_CONFED) + || bgp_flag_check (bgp, BGP_FLAG_MED_MISSING_AS_WORST)) + { + vty_out (vty, " bgp bestpath med"); + if (bgp_flag_check (bgp, BGP_FLAG_MED_CONFED)) + vty_out (vty, " confed"); + if (bgp_flag_check (bgp, BGP_FLAG_MED_MISSING_AS_WORST)) + vty_out (vty, " missing-as-worst"); + vty_out (vty, "%s", VTY_NEWLINE); + } + + /* BGP network import check. */ + if (bgp_flag_check (bgp, BGP_FLAG_IMPORT_CHECK)) + vty_out (vty, " bgp network import-check%s", VTY_NEWLINE); + + /* BGP scan interval. */ + bgp_config_write_scan_time (vty); + + /* BGP flag dampening. */ + if (CHECK_FLAG (bgp->af_flags[AFI_IP][SAFI_UNICAST], + BGP_CONFIG_DAMPENING)) + bgp_config_write_damp (vty); + + /* BGP static route configuration. */ + bgp_config_write_network (vty, bgp, AFI_IP, SAFI_UNICAST, &write); + + /* BGP redistribute configuration. */ + bgp_config_write_redistribute (vty, bgp, AFI_IP, SAFI_UNICAST, &write); + + /* BGP timers configuration. */ + if (bgp->default_keepalive != BGP_DEFAULT_KEEPALIVE + && bgp->default_holdtime != BGP_DEFAULT_HOLDTIME) + vty_out (vty, " timers bgp %d %d%s", bgp->default_keepalive, + bgp->default_holdtime, VTY_NEWLINE); + + /* peer-group */ + for (ALL_LIST_ELEMENTS (bgp->group, node, nnode, group)) + { + bgp_config_write_peer (vty, bgp, group->conf, AFI_IP, SAFI_UNICAST); + } + + /* Normal neighbor configuration. */ + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + bgp_config_write_peer (vty, bgp, peer, AFI_IP, SAFI_UNICAST); + } + + /* Distance configuration. */ + bgp_config_write_distance (vty, bgp); + + /* No auto-summary */ + if (bgp_option_check (BGP_OPT_CONFIG_CISCO)) + vty_out (vty, " no auto-summary%s", VTY_NEWLINE); + + /* IPv4 multicast configuration. */ + write += bgp_config_write_family (vty, bgp, AFI_IP, SAFI_MULTICAST); + + /* IPv4 VPN configuration. */ + write += bgp_config_write_family (vty, bgp, AFI_IP, SAFI_MPLS_VPN); + + /* IPv6 unicast configuration. */ + write += bgp_config_write_family (vty, bgp, AFI_IP6, SAFI_UNICAST); + + /* IPv6 multicast configuration. */ + write += bgp_config_write_family (vty, bgp, AFI_IP6, SAFI_MULTICAST); + + write++; + } + return write; +} + +void +bgp_master_init (void) +{ + memset (&bgp_master, 0, sizeof (struct bgp_master)); + + bm = &bgp_master; + bm->bgp = list_new (); + bm->listen_sockets = list_new (); + bm->port = BGP_PORT_DEFAULT; + bm->master = thread_master_create (); + bm->start_time = time (NULL); +} + + +void +bgp_init (void) +{ + /* peer index */ + bgp_peer_index_init(NULL); + + /* BGP VTY commands installation. */ + bgp_vty_init (); + + /* Init zebra. */ + bgp_zebra_init (); + + /* BGP inits. */ + bgp_attr_init (); + bgp_debug_init (); + bgp_dump_init (); + bgp_route_init (); + bgp_route_map_init (); + bgp_scan_init (); + bgp_mplsvpn_init (); + + /* Access list initialize. */ + access_list_init (); + access_list_add_hook (peer_distribute_update); + access_list_delete_hook (peer_distribute_update); + + /* Filter list initialize. */ + bgp_filter_init (); + as_list_add_hook (peer_aslist_update); + as_list_delete_hook (peer_aslist_update); + + /* Prefix list initialize.*/ + prefix_list_init (); + prefix_list_add_hook (peer_prefix_list_update); + prefix_list_delete_hook (peer_prefix_list_update); + + /* Community list initialize. */ + bgp_clist = community_list_init (); + +#ifdef HAVE_SNMP + bgp_snmp_init (); +#endif /* HAVE_SNMP */ +} + +void +bgp_terminate (int terminating, int retain_mode) +{ + struct bgp *bgp; + struct peer *peer; + struct listnode *node, *nnode; + struct listnode *mnode, *mnnode; + + program_terminating = terminating; + + /* Disable all peers */ + for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp)) + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { +fprintf(stderr, ">>> %s:", peer->host) ; + if (retain_mode) + bgp_peer_disable(peer, NULL); + else if (terminating) + peer_flag_set(peer, PEER_FLAG_SHUTDOWN); + else + bgp_notify_send(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_ADMIN_RESET); +fprintf(stderr, "<<<\n") ; + } + + if (!retain_mode) + { + bgp_cleanup_routes (); + + if (bm->process_main_queue) + { + work_queue_free (bm->process_main_queue); + bm->process_main_queue = NULL; + } + if (bm->process_rsclient_queue) + { + work_queue_free (bm->process_rsclient_queue); + bm->process_rsclient_queue = NULL; + } + } + + /* if no sessions were enabled then need to check here */ + program_terminate_if_all_disabled(); +} + +/* If we are terminating the program, and all sessions are disabled + * then terminate all threads + */ +void +program_terminate_if_all_disabled(void) +{ + struct bgp *bgp; + struct peer *peer; + struct listnode *node, *nnode; + struct listnode *mnode, *mnnode; + + if (!program_terminating) + return; + + /* are there any active sessions remaining? */ + for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp)) + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + if (bgp_session_is_active(peer->session)) + return; + + /* ask remaining pthreads to die */ + if (qpthreads_enabled && routing_nexus != NULL) + qpn_terminate(routing_nexus); + + if (qpthreads_enabled && bgp_nexus != NULL) + qpn_terminate(bgp_nexus); + + if (cli_nexus != NULL) + qpn_terminate(cli_nexus); +} + |