diff options
39 files changed, 3082 insertions, 2003 deletions
@@ -461,19 +461,19 @@ enum BGP_NOMC enum BGP_NOMS { - BGP_NOMS_UNSPECIFIC = 0 /* If nothing else applies */ + BGP_NOMS_UNSPECIFIC = 0 /* If nothing else applies */ }; enum BGP_NOMS_HEADER /* BGP_NOMC_HEADER subcodes */ { - BGP_NOMS_H_NOT_SYNC = 1, /* Connection Not Synchronised */ + BGP_NOMS_H_NOT_SYNC = 1, /* Connection Not Synchronised */ /* (Marker field not all = 1,'s !) */ - BGP_NOMS_H_BAD_LEN = 2, /* Bad Message Length */ + BGP_NOMS_H_BAD_LEN = 2, /* Bad Message Length */ /* DATA: the length that failed */ - BGP_NOMS_H_BAD_TYPE = 3, /* Bad Message Type */ + BGP_NOMS_H_BAD_TYPE = 3, /* Bad Message Type */ /* DATA: the message type objected to */ - BGP_NOMS_H_MAX = 3, /* max known subcode */ + BGP_NOMS_H_MAX = 3, /* max known subcode */ } ; enum BGP_NOMS_OPEN /* BGP_NOMC_OPEN subcodes */ diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c index 552b2291..495d0fdc 100644 --- a/bgpd/bgp_advertise.c +++ b/bgpd/bgp_advertise.c @@ -231,7 +231,7 @@ bgp_adj_out_set (struct bgp_node *rn, struct peer *peer, struct prefix *p, adj = XCALLOC (MTYPE_BGP_ADJ_OUT, sizeof (struct bgp_adj_out)); /* Add to list of adj_out stuff for the peer */ - adj->peer = peer_lock (peer); + adj->peer = bgp_peer_lock (peer); adj_out_head = &(peer->adj_out_head[afi][safi]) ; @@ -336,7 +336,7 @@ bgp_adj_out_remove (struct bgp_node *rn, struct bgp_adj_out *adj, else peer->adj_out_head[afi][safi] = adj->route_next ; - peer_unlock (peer); + bgp_peer_unlock (peer); /* Unhook from bgp_node */ if (adj->adj_next) @@ -379,7 +379,7 @@ bgp_adj_in_set (struct bgp_node *rn, struct peer *peer, struct attr *attr) adj->attr = bgp_attr_intern (attr); /* Add to list of adj in stuff for the peer */ - adj->peer = peer_lock (peer); + adj->peer = bgp_peer_lock (peer); adj_in_head = &(peer->adj_in_head[rn->table->afi][rn->table->safi]) ; @@ -420,7 +420,7 @@ bgp_adj_in_remove (struct bgp_node *rn, struct bgp_adj_in *bai) else *adj_in_head = bai->route_next ; - peer_unlock (peer); + bgp_peer_unlock (peer); /* Unhook from bgp_node */ if (bai->adj_next) diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index d79f25f2..7fc9749e 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -718,10 +718,10 @@ bgp_attr_aspathlimit (struct peer *peer, bgp_size_t length, { zlog (peer->log, LOG_ERR, "AS-Pathlimit attribute flag isn't transitive %d", flag); - bgp_notify_send_with_data (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, - startp, total); + bgp_peer_down_error_with_data (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, + startp, total); return -1; } @@ -729,10 +729,10 @@ bgp_attr_aspathlimit (struct peer *peer, bgp_size_t length, { zlog (peer->log, LOG_ERR, "AS-Pathlimit length, %u, is not 5", length); - bgp_notify_send_with_data (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, - startp, total); + bgp_peer_down_error_with_data (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, + startp, total); return -1; } @@ -760,10 +760,10 @@ bgp_attr_origin (struct peer *peer, bgp_size_t length, { zlog (peer->log, LOG_ERR, "Origin attribute flag isn't transitive %d", flag); - bgp_notify_send_with_data (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, - startp, total); + bgp_peer_down_error_with_data (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, + startp, total); return -1; } @@ -776,9 +776,9 @@ bgp_attr_origin (struct peer *peer, bgp_size_t length, { zlog (peer->log, LOG_ERR, "Origin attribute length is not one %d", length); - bgp_notify_send_with_data (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, - startp, total); + bgp_peer_down_error_with_data (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + startp, total); return -1; } @@ -795,10 +795,9 @@ bgp_attr_origin (struct peer *peer, bgp_size_t length, zlog (peer->log, LOG_ERR, "Origin attribute value is invalid %d", attr->origin); - bgp_notify_send_with_data (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_INVAL_ORIGIN, - startp, total); + bgp_peer_down_error_with_data (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_INVAL_ORIGIN, + startp, total); return -1; } @@ -824,10 +823,10 @@ bgp_attr_aspath (struct peer *peer, bgp_size_t length, { zlog (peer->log, LOG_ERR, "As-Path attribute flag isn't transitive %d", flag); - bgp_notify_send_with_data (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, - startp, total); + bgp_peer_down_error_with_data (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, + startp, total); return -1; } @@ -841,9 +840,8 @@ bgp_attr_aspath (struct peer *peer, bgp_size_t length, if (! attr->aspath) { zlog (peer->log, LOG_ERR, "Malformed AS path length is %d", length); - bgp_notify_send (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_AS_PATH); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_AS_PATH); return -1; } @@ -876,9 +874,8 @@ static int bgp_attr_aspath_check( struct peer *peer, (peer_sort (peer) == BGP_PEER_EBGP && aspath_confed_check (attr->aspath))) { zlog (peer->log, LOG_ERR, "Malformed AS path from %s", peer->host); - bgp_notify_send (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_AS_PATH); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_AS_PATH); return -1; } @@ -890,9 +887,8 @@ static int bgp_attr_aspath_check( struct peer *peer, { zlog (peer->log, LOG_ERR, "%s incorrect first AS (must be %u)", peer->host, peer->as); - bgp_notify_send (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_AS_PATH); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_AS_PATH); return -1; } } @@ -941,10 +937,10 @@ bgp_attr_nexthop (struct peer *peer, bgp_size_t length, { zlog (peer->log, LOG_ERR, "Origin attribute flag isn't transitive %d", flag); - bgp_notify_send_with_data (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, - startp, total); + bgp_peer_down_error_with_data (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, + startp, total); return -1; } @@ -954,7 +950,7 @@ bgp_attr_nexthop (struct peer *peer, bgp_size_t length, zlog (peer->log, LOG_ERR, "Nexthop attribute length isn't four [%d]", length); - bgp_notify_send_with_data (peer, + bgp_peer_down_error_with_data (peer, BGP_NOTIFY_UPDATE_ERR, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, startp, total); @@ -982,10 +978,10 @@ bgp_attr_med (struct peer *peer, bgp_size_t length, zlog (peer->log, LOG_ERR, "MED attribute length isn't four [%d]", length); - bgp_notify_send_with_data (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, - startp, total); + bgp_peer_down_error_with_data (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + startp, total); return -1; } @@ -1030,9 +1026,9 @@ bgp_attr_atomic (struct peer *peer, bgp_size_t length, { zlog (peer->log, LOG_ERR, "Bad atomic aggregate length %d", length); - bgp_notify_send (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + bgp_peer_down_error (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); return -1; } @@ -1059,9 +1055,8 @@ bgp_attr_aggregator (struct peer *peer, bgp_size_t length, zlog (peer->log, LOG_ERR, "Aggregator length is not %d [%d]", wantedlen, length); - bgp_notify_send (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); return -1; } @@ -1087,9 +1082,8 @@ bgp_attr_as4_aggregator (struct peer *peer, bgp_size_t length, { zlog (peer->log, LOG_ERR, "New Aggregator length is not 8 [%d]", length); - bgp_notify_send (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); return -1; } *as4_aggregator_as = stream_getl (peer->ibuf); @@ -1145,9 +1139,8 @@ bgp_attr_munge_as4_attrs (struct peer *peer, struct attr *attr, zlog (peer->log, LOG_ERR, "%s BGP not AS4 capable peer sent AS4_PATH but" " no AS_PATH, cant do anything here", peer->host); - bgp_notify_send (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); return -1; } @@ -1247,9 +1240,8 @@ bgp_attr_originator_id (struct peer *peer, bgp_size_t length, { zlog (peer->log, LOG_ERR, "Bad originator ID length %d", length); - bgp_notify_send (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); return -1; } @@ -1271,9 +1263,8 @@ bgp_attr_cluster_list (struct peer *peer, bgp_size_t length, { zlog (peer->log, LOG_ERR, "Bad cluster list length %d", length); - bgp_notify_send (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); return -1; } @@ -1511,10 +1502,10 @@ bgp_attr_unknown (struct peer *peer, struct attr *attr, u_char flag, if (! CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL)) { /* Adjust startp to do not include flag value. */ - bgp_notify_send_with_data (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_UNREC_ATTR, - startp, total); + bgp_peer_down_error_with_data (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_UNREC_ATTR, + startp, total); return -1; } @@ -1584,9 +1575,8 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, peer->host, (unsigned long) (endp - STREAM_PNT (BGP_INPUT (peer)))); - bgp_notify_send (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); return -1; } @@ -1604,9 +1594,8 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, peer->host, (unsigned long) (endp - STREAM_PNT (BGP_INPUT (peer)))); - bgp_notify_send (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); return -1; } @@ -1626,9 +1615,8 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, "%s error BGP attribute type %d appears twice in a message", peer->host, type); - bgp_notify_send (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); return -1; } @@ -1644,9 +1632,8 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, { zlog (peer->log, LOG_WARNING, "%s BGP type %d length %d is too large, attribute total length is %d. attr_endp is %p. endp is %p", peer->host, type, length, size, attr_endp, endp); - bgp_notify_send (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); return -1; } @@ -1713,9 +1700,8 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, "%s: Attribute %s, parse error", peer->host, LOOKUP (attr_str, type)); - bgp_notify_send (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); return ret; } @@ -1734,9 +1720,8 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, PEER_CAP_AS4_USE(peer) ? "" : "NOT ") ; } ; - bgp_notify_send (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); return -1; } } @@ -1747,9 +1732,8 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, zlog (peer->log, LOG_WARNING, "%s BGP attribute %s, length mismatch", peer->host, LOOKUP (attr_str, type)); - bgp_notify_send (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); return -1; } @@ -1829,10 +1813,10 @@ bgp_attr_check (struct peer *peer, struct attr *attr) zlog (peer->log, LOG_WARNING, "%s Missing well-known attribute %d.", peer->host, type); - bgp_notify_send_with_data (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MISS_ATTR, - &type, 1); + bgp_peer_down_error_with_data (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MISS_ATTR, + &type, 1); return -1; } return 0; diff --git a/bgpd/bgp_common.h b/bgpd/bgp_common.h index d35d6da6..67d1afb0 100644 --- a/bgpd/bgp_common.h +++ b/bgpd/bgp_common.h @@ -134,10 +134,10 @@ enum bgp_peer_states { bgp_peer_min_state = 0, - bgp_peer_sIdle = 1, /* session not yet established */ - bgp_peer_sEstablished = 2, /* session established */ - bgp_peer_sClearing = 3, /* Clearing routes */ - bgp_peer_sDeleted = 4, /* Deleted, linger until lock count == 0 */ + bgp_peer_pIdle = 1, /* session not yet established */ + bgp_peer_pEstablished = 2, /* session established */ + bgp_peer_pClearing = 3, /* Clearing routes */ + bgp_peer_pDeleting = 4, /* Deleting, lingers until lock count == 0 */ bgp_peer_max_state = 4 } ; diff --git a/bgpd/bgp_connection.c b/bgpd/bgp_connection.c index 41cde9a3..cdcfc10e 100644 --- a/bgpd/bgp_connection.c +++ b/bgpd/bgp_connection.c @@ -690,28 +690,65 @@ bgp_connection_stop(bgp_connection connection, int stop_writer) } ; /*------------------------------------------------------------------------------ - * Enable connection for accept() + * Enable connection/session for accept() * - * NB: requires the session to be LOCKED. + * NB: requires the session to be LOCKED */ extern void bgp_connection_enable_accept(bgp_connection connection) { + bgp_session session = connection->session ; + assert(connection->ordinal == bgp_connection_secondary) ; - connection->session->index_entry->accept = connection ; + assert((session != NULL) && (session->active)) ; + + session->accept = true ; } ; /*------------------------------------------------------------------------------ * Disable connection for accept() -- assuming still have session ! * - * NB: requires the session to be LOCKED. + * NB: requires the session to be LOCKED */ extern void bgp_connection_disable_accept(bgp_connection connection) { - bgp_session session = connection->session ; - if (session != NULL) - session->index_entry->accept = NULL ; + if (connection->session != NULL) + connection->session->accept = false ; +} ; + +/*------------------------------------------------------------------------------ + * See if there is a connection which is ready to accept() + * + * Note that if there *is* a connection, then the session is active, and is not + * subject to the whim of the Routing Engine -- in particular it cannot be + * deleted ! + * + * NB: this is *only* used in the BGP Engine. The session->active and + * session->accept flags are private variables, only set by the BGP Engine. + * + * NB: this is called under the Peer Index Mutex. The Routing Engine never + * deletes sessions while it holds the Peer Index Mutex, nor when the + * session->active is true. + * + * Only returns a connection if session->active -- so safe. + */ +extern bgp_connection +bgp_connection_query_accept(bgp_session session) +{ + bgp_connection connection ; + + if ((session != NULL) && session->active && session->accept) + { + connection = session->connections[bgp_connection_secondary] ; + assert(connection != NULL) ; + } + else + { + connection = NULL ; + } ; + + return connection ; } ; /*------------------------------------------------------------------------------ @@ -830,7 +867,7 @@ bgp_connection_part_close(bgp_connection connection) /* Turn off session->active (if still attached). */ if (session != NULL) - session->active = 0 ; + session->active = false ; /* 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 */ diff --git a/bgpd/bgp_connection.h b/bgpd/bgp_connection.h index 3d63edb7..58afc2ae 100644 --- a/bgpd/bgp_connection.h +++ b/bgpd/bgp_connection.h @@ -219,6 +219,9 @@ extern void bgp_connection_disable_accept(bgp_connection connection) ; extern bgp_connection +bgp_connection_query_accept(bgp_session session) ; + +extern bgp_connection bgp_connection_get_sibling(bgp_connection connection) ; extern void diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 607bf824..684e9651 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -81,10 +81,10 @@ const int bgp_status_msg_max = bgp_fsm_last_state + 1 ; const struct message bgp_peer_status_msg[] = { - { bgp_peer_sIdle, "Idle" }, - { bgp_peer_sEstablished, "Established" }, - { bgp_peer_sClearing, "Clearing" }, - { bgp_peer_sDeleted, "Deleted" }, + { bgp_peer_pIdle, "Idle" }, + { bgp_peer_pEstablished, "Established" }, + { bgp_peer_pClearing, "Clearing" }, + { bgp_peer_pDeleting, "Deleting" }, }; const int bgp_peer_status_msg_max = bgp_peer_max_state + 1 ; @@ -254,7 +254,7 @@ bgp_dump_attr (struct peer *peer, struct attr *attr, char *buf, size_t size) /* dump notify packet */ void -bgp_notify_print(struct peer *peer, bgp_notify notification, bool sending) +bgp_notify_print(struct peer *peer, bgp_notify notification) { const char* subcode_str ; const char* code_str ; @@ -328,15 +328,15 @@ bgp_notify_print(struct peer *peer, bgp_notify notification, bool sending) /* Output the required logging */ if (log_neighbor_changes) - zlog_info ("%%NOTIFICATION: %s neighbor %s %d/%d (%s%s) %d bytes %s", - sending ? "sent to" : "received from", peer->host, - notification->code, notification->subcode, - code_str, subcode_str, length, hex_form) ; + zlog_info("%%NOTIFICATION: %s neighbor %s %d/%d (%s%s) %d bytes %s", + notification->received ? "received from" : "sent to", peer->host, + notification->code, notification->subcode, + code_str, subcode_str, length, hex_form) ; else - plog_debug (peer->log, "%s %s NOTIFICATION %d/%d (%s%s) %d bytes %s", - peer->host, sending ? "sending" : "received", - notification->code, notification->subcode, - code_str, subcode_str, length, hex_form) ; + plog_debug(peer->log, "%s %s NOTIFICATION %d/%d (%s%s) %d bytes %s", + peer->host, notification->received ? "received" : "sending", + notification->code, notification->subcode, + code_str, subcode_str, length, hex_form) ; /* Release the */ if (alloc != NULL) diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h index 1d05cefb..2b9ca268 100644 --- a/bgpd/bgp_debug.h +++ b/bgpd/bgp_debug.h @@ -121,8 +121,7 @@ extern unsigned long term_bgp_debug_zebra; extern const char *bgp_type_str[]; extern int bgp_dump_attr (struct peer *, struct attr *, char *, size_t); -extern void bgp_notify_print (struct peer* peer, bgp_notify notification, - bool sending); +extern void bgp_notify_print (struct peer* peer, bgp_notify notification); extern const struct message bgp_status_msg[]; extern const int bgp_status_msg_max; diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 9c323ffd..9df8a955 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -66,6 +66,20 @@ * * The fsm action functions are called with the session locked. * + * TODO: restore NSF + * TODO: track incoming connections while established + * + * To do this need to accept connection while established, then wait for + * OPEN. While waiting, the established connection may drop, and + * so the session restart, but with an incoming connection already + * in place. + * + * When OPEN arrives, for NSF should close any established connection + * and restart with the new one. + * + * Otherwise, if CollisionDetectEstablishedState option is set, should + * go through collision detection ! + * *------------------------------------------------------------------------------ * FSM "events" and Session "exceptions". * @@ -1932,6 +1946,8 @@ static bgp_fsm_action(bgp_fsm_error) * Next state will be sIdle, except if is sEstablished, when will be sStopping. * * NB: requires the session LOCKED + * + * TODO: deal with: BGP_NOMC_OPEN/BGP_NOMS_O_OPTION & squash capabilities. */ static bgp_fsm_action(bgp_fsm_recv_nom) { diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 0de28b9b..60b66533 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -75,20 +75,23 @@ static const struct option longopts[] = { "ignore_warnings", no_argument, NULL, 'I'}, { 0 } }; -/* Configuration file and directory. */ +/* Configuration file and directory. */ char config_default[] = SYSCONFDIR BGP_DEFAULT_CONFIG; -/* Route retain mode flag. */ -static int retain_mode = 0; +/* Route retain mode flag. */ +static bool retain_mode = false; + +/* Have started terminating the program */ +static bool program_terminating = false ; /* whether to ignore warnings in configuration file */ static bool config_ignore_warnings = false; /* whether configured to run with qpthreads */ -static bool config_threaded = 0; +static bool config_threaded = false ; /* whether configured to run as an AS2 speaker */ -static bool config_as2_speaker = 0; +static bool config_as2_speaker = false ; /* Master of threads. */ struct thread_master *master; @@ -483,11 +486,11 @@ int main (int argc, char **argv) { char *p; - int opt; - int daemon_mode = 0; - int dryrun = 0; + int opt; + bool daemon_mode = false ; + bool dryrun = false ; char *progname; - int tmp_port; + int tmp_port; /* Set umask before anything for security */ umask (0027); @@ -520,7 +523,7 @@ main (int argc, char **argv) case 0: break; case 'd': - daemon_mode = 1; + daemon_mode = true ; break; case 'f': config_file = optarg; @@ -551,7 +554,7 @@ main (int argc, char **argv) vty_port = BGP_VTY_PORT; break; case 'r': - retain_mode = 1; + retain_mode = true ; break; case 'l': bm->address = optarg; @@ -570,19 +573,19 @@ main (int argc, char **argv) exit (0); break; case 'C': - dryrun = 1; + dryrun = true ; break; case 'h': usage (progname, 0); break; case 't': - config_threaded = 1; + config_threaded = true ; break; case 'I': - config_ignore_warnings = 1; + config_ignore_warnings = true ; break ; case '2': - config_as2_speaker = 1; + config_as2_speaker = true ; break ; default: usage (progname, 1); @@ -716,6 +719,10 @@ routing_background(void) return thread_dispatch_background(master) ; } +/*============================================================================== + * SIGHUP and SIGTERM + */ + /*------------------------------------------------------------------------------ * SIGHUP: message sent to Routeing engine and the action it then takes. * @@ -726,14 +733,14 @@ sighup_enqueue(void) { mqueue_block mqb = mqb_init_new(NULL, sighup_action, NULL) ; - mqueue_enqueue(routing_nexus->queue, mqb, 1) ; + mqueue_enqueue(routing_nexus->queue, mqb, mqb_priority) ; } /* dispatch a command from the message queue block */ static void sighup_action(mqueue_block mqb, mqb_flag_t flag) { - if (flag == mqb_action) + if ((flag == mqb_action) && !program_terminating) { zlog_info ("bgpd restarting!"); @@ -753,6 +760,32 @@ sighup_action(mqueue_block mqb, mqb_flag_t flag) } /*------------------------------------------------------------------------------ + * Foreground task to see if all peers have been deleted yet. + */ +static int +program_terminate_if_all_peers_deleted(void) +{ + if (bm->peer_linger_count == 0) + { + /* ask remaining pthreads to die + * + * Note that qpn_terminate does nothing if it has been called once + * already. + */ + 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) ; + } ; + + return 0 ; /* nothing to do, really. */ +} ; + +/*------------------------------------------------------------------------------ * SIGTERM: message sent to Routeing engine and the action it then takes. */ static void @@ -760,19 +793,26 @@ sigterm_enqueue(void) { mqueue_block mqb = mqb_init_new(NULL, sigterm_action, NULL) ; - mqueue_enqueue(routing_nexus->queue, mqb, 1) ; -} + mqueue_enqueue(routing_nexus->queue, mqb, mqb_priority) ; +} ; /* dispatch a command from the message queue block */ static void sigterm_action(mqueue_block mqb, mqb_flag_t flag) { - if (flag == mqb_action) + if ((flag == mqb_action) && !program_terminating) { /* send notify to all peers, wait for all sessions to be disables - * then terminate all pthreads */ + * then terminate all pthreads + */ + program_terminating = true ; + bgp_terminate(1, retain_mode); + + qpn_add_hook_function(&routing_nexus->foreground, + program_terminate_if_all_peers_deleted) ; } mqb_free(mqb); -} +} ; + diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 0489cf0f..983a4867 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -92,7 +92,7 @@ bgp_nlri_parse_vpnv4 (struct peer *peer, struct attr *attr, u_char *tagpnt; /* Check peer status. */ - if (peer->state != bgp_peer_sEstablished) + if (peer->state != bgp_peer_pEstablished) return 0; /* Make prefix_rd */ diff --git a/bgpd/bgp_msg_read.c b/bgpd/bgp_msg_read.c index 664aed21..e0bbfef9 100644 --- a/bgpd/bgp_msg_read.c +++ b/bgpd/bgp_msg_read.c @@ -1420,8 +1420,9 @@ bgp_msg_notify_receive (bgp_connection connection, bgp_size_t body_size) notification = bgp_notify_new_with_data(code, subcode, stream_pnt(connection->ibuf), body_size - 2) ; + bgp_notify_set_received(notification) ; - bgp_notify_print(connection->session->peer, notification, 0) ; /* Logging */ + bgp_notify_print(connection->session->peer, notification) ; /* Logging */ bgp_fsm_notification_exception(connection, notification) ; } ; diff --git a/bgpd/bgp_msg_write.c b/bgpd/bgp_msg_write.c index 0323fa83..ee4a510c 100644 --- a/bgpd/bgp_msg_write.c +++ b/bgpd/bgp_msg_write.c @@ -85,14 +85,13 @@ extern int bgp_msg_write_notification(bgp_connection connection, bgp_notify notification) { struct stream *s = connection->obuf ; - int length; + int length; ++connection->session->stats.notify_out ; assert(notification != NULL) ; - /* Logging */ - bgp_notify_print(connection->session->peer, notification, 1) ; + bgp_notify_print(connection->session->peer, notification) ; /* Make NOTIFY message header */ bgp_packet_set_marker (s, BGP_MSG_NOTIFY); diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 77c89ff3..87f4f953 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -572,7 +572,7 @@ bgp_accept_action(qps_file qf, void* file_info) bgp_connection connection ; union sockunion su_remote ; union sockunion su_local ; - int exists ; + bool exists ; int sock_fd ; int err ; int family ; diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index c72ca09f..de745007 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -438,7 +438,7 @@ bgp_scan (afi_t afi, safi_t safi) /* Maximum prefix check */ for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) { - if (peer->state != bgp_peer_sEstablished) + if (peer->state != bgp_peer_pEstablished) continue; if (peer->afc[afi][SAFI_UNICAST]) diff --git a/bgpd/bgp_notification.c b/bgpd/bgp_notification.c index 2763f296..45bf99b7 100644 --- a/bgpd/bgp_notification.c +++ b/bgpd/bgp_notification.c @@ -54,6 +54,8 @@ bgp_notify_size(bgp_size_t size) * Allocate and initialise new notification * * Can add data later if required. + * + * NB: returns a 'NOT received' notification. */ extern bgp_notify bgp_notify_new(bgp_nom_code_t code, bgp_nom_subcode_t subcode) @@ -67,9 +69,10 @@ bgp_notify_new(bgp_nom_code_t code, bgp_nom_subcode_t subcode) /* Implicitly: * - * notification->size = 0 - * notification->length = 0 - * notification->data = NULL + * notification->received = false ; + * notification->size = 0 + * notification->length = 0 + * notification->data = NULL */ return notification ; @@ -82,6 +85,8 @@ bgp_notify_new(bgp_nom_code_t code, bgp_nom_subcode_t subcode) * ...but pre-allocates at least the expected amount. * * May expect 0. + * + * NB: returns a 'NOT received' notification. */ extern bgp_notify bgp_notify_new_expect(bgp_nom_code_t code, bgp_nom_subcode_t subcode, @@ -102,6 +107,8 @@ bgp_notify_new_expect(bgp_nom_code_t code, bgp_nom_subcode_t subcode, * Allocate and initialise new notification, complete with data * * Can specify an expected amount of data. + * + * NB: returns a 'NOT received' notification. */ extern bgp_notify bgp_notify_new_with_data(bgp_nom_code_t code, bgp_nom_subcode_t subcode, @@ -228,6 +235,8 @@ bgp_notify_set_mov(bgp_notify* p_dst, bgp_notify* p_src) /*============================================================================== * Set new Code and Subcode and discard and data accumulated so far. + * + * NB: does not change the received state of the notification. */ extern bgp_notify bgp_notify_reset(bgp_notify notification, bgp_nom_code_t code, diff --git a/bgpd/bgp_notification.h b/bgpd/bgp_notification.h index a7e04501..20e96063 100644 --- a/bgpd/bgp_notification.h +++ b/bgpd/bgp_notification.h @@ -38,7 +38,10 @@ typedef unsigned char bgp_nom_code_t ; typedef unsigned char bgp_nom_subcode_t ; /*============================================================================== + * Structure for notification. * + * Note that vast majority of notification handling concerns notifications that + * are *sent* to the far end. Occasionally a notification will be received. */ typedef struct bgp_notify* bgp_notify ; @@ -47,6 +50,8 @@ struct bgp_notify bgp_nom_code_t code ; bgp_nom_subcode_t subcode ; + bool received ; + bgp_size_t length ; bgp_size_t size ; ptr_t data ; @@ -166,6 +171,12 @@ bgp_notify_set_subcode(bgp_notify notification, bgp_nom_subcode_t subcode) notification->subcode = subcode ; } ; +Inline void +bgp_notify_set_received(bgp_notify notification) +{ + notification->received = true ; +} ; + extern bgp_notify bgp_notify_reset(bgp_notify notification, bgp_nom_code_t code, bgp_nom_subcode_t subcode) ; @@ -193,6 +204,12 @@ bgp_notify_get_subcode(bgp_notify notification) return (notification != NULL) ? notification->subcode : BGP_NOMS_UNSPECIFIC ; } ; +Inline bool +bgp_notify_get_received(bgp_notify notification) +{ + return (notification != NULL) ? notification->received : false ; +} ; + Inline bgp_size_t bgp_notify_get_length(bgp_notify notification) { diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index 7bcddca0..ab9acf63 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -58,7 +58,8 @@ bgp_capability_vty_out (struct vty *vty, struct peer *peer) struct capability_mp_data mpc; struct capability_header *hdr; - if (peer == NULL || peer->session == NULL || peer->session->notification == NULL) + if ((peer == NULL) || (peer->session == NULL) + || (peer->session->notification == NULL)) return; pnt = (char*)peer->session->notification->data; @@ -254,8 +255,9 @@ bgp_capability_orf_entry (struct peer *peer, struct capability_header *hdr) { zlog_info ("%s ORF Capability entry length error," " Cap length %u, num %u", - peer->host, hdr->length, entry.num); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + peer->host, hdr->length, entry.num) ; + /* TODO: is this the right notification ?? */ + bgp_peer_down_error (peer, BGP_NOTIFY_CEASE, 0); return -1; } @@ -497,7 +499,8 @@ bgp_capability_parse (struct peer *peer, size_t length, u_char **error) if (stream_get_getp(s) + 2 > end) { zlog_info ("%s Capability length error (< header)", peer->host); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + /* TODO: Is this the right notification ?? */ + bgp_peer_down_error (peer, BGP_NOTIFY_CEASE, 0); return -1; } @@ -509,7 +512,8 @@ bgp_capability_parse (struct peer *peer, size_t length, u_char **error) if (start + caphdr.length > end) { zlog_info ("%s Capability length error (< length)", peer->host); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + /* TODO: Is this the right notification ?? */ + bgp_peer_down_error (peer, BGP_NOTIFY_CEASE, 0); return -1; } @@ -539,7 +543,8 @@ bgp_capability_parse (struct peer *peer, size_t length, u_char **error) LOOKUP (capcode_str, caphdr.code), caphdr.length, (unsigned) cap_minsizes[caphdr.code]); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + /* TODO: Is this the right notification ?? */ + bgp_peer_down_error (peer, BGP_NOTIFY_CEASE, 0); return -1; } /* we deliberately ignore unknown codes, see below */ @@ -628,9 +633,8 @@ bgp_capability_parse (struct peer *peer, size_t length, u_char **error) static int bgp_auth_parse (struct peer *peer, size_t length) { - bgp_notify_send (peer, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_AUTH_FAILURE); + bgp_peer_down_error (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_AUTH_FAILURE); return -1; } @@ -752,7 +756,8 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability) if (STREAM_READABLE(s) < 2) { zlog_info ("%s Option length error", peer->host); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + /* TODO: Is this the right notification ?? */ + bgp_peer_down_error (peer, BGP_NOTIFY_CEASE, 0); return -1; } @@ -764,7 +769,8 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability) if (STREAM_READABLE (s) < opt_length) { zlog_info ("%s Option length error", peer->host); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + /* TODO: Is this the right notification ?? */ + bgp_peer_down_error (peer, BGP_NOTIFY_CEASE, 0); return -1; } @@ -785,9 +791,8 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability) *capability = 1; break; default: - bgp_notify_send (peer, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_UNSUP_PARAM); + bgp_peer_down_error (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSUP_PARAM); ret = -1; break; } @@ -807,10 +812,10 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability) /* If Unsupported Capability exists. */ if (error != error_data) { - bgp_notify_send_with_data (peer, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_UNSUP_CAPBL, - error_data, error - error_data); + bgp_peer_down_error_with_data (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSUP_CAPBL, + error_data, error - error_data); return -1; } @@ -818,9 +823,8 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability) peer. */ if (! strict_capability_same (peer)) { - bgp_notify_send (peer, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_UNSUP_CAPBL); + bgp_peer_down_error (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSUP_CAPBL); return -1; } } @@ -839,14 +843,13 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability) if (error != error_data) - bgp_notify_send_with_data (peer, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_UNSUP_CAPBL, - error_data, error - error_data); + bgp_peer_down_error_with_data (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSUP_CAPBL, + error_data, error - error_data); else - bgp_notify_send (peer, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_UNSUP_CAPBL); + bgp_peer_down_error (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSUP_CAPBL); return -1; } } diff --git a/bgpd/bgp_open_state.c b/bgpd/bgp_open_state.c index 17f9ae36..c08160eb 100644 --- a/bgpd/bgp_open_state.c +++ b/bgpd/bgp_open_state.c @@ -293,12 +293,15 @@ bgp_open_state_afi_safi_cap(bgp_open_state state, unsigned index) * */ -/* Received an open, update the peer's state */ +/* Received an open, update the peer's state + * + * NB: for safety, best to have the session locked -- though won't, in fact, + * change any of this information after the session is established. + */ void bgp_peer_open_state_receive(bgp_peer peer) { - bgp_session session = peer->session; - bgp_open_state open_send = session->open_send; + bgp_session session = peer->session; bgp_open_state open_recv = session->open_recv; qAFI_t afi; qSAFI_t safi; @@ -315,16 +318,13 @@ bgp_peer_open_state_receive(bgp_peer peer) implementation MAY adjust the rate at which it sends KEEPALIVE messages as a function of the Hold Time interval. */ - peer->v_holdtime = - (open_recv->holdtime < open_send->holdtime) - ? open_recv->holdtime - : open_send->holdtime; - - peer->v_keepalive = peer->v_holdtime / 3; - - /* TODO: update session state as well? */ - session->hold_timer_interval = peer->v_holdtime ; - session->keepalive_timer_interval = peer->v_keepalive ; + /* The BGP Engine sets the session's HoldTimer and KeepaliveTimer intervals + * to the values negotiated when the OPEN messages were exchanged. + * + * Take copies of that information. + */ + peer->v_holdtime = session->hold_timer_interval ; + peer->v_keepalive = session->keepalive_timer_interval ; /* Set remote router-id */ peer->remote_id.s_addr = open_recv->bgp_id; @@ -404,12 +404,18 @@ bgp_peer_open_state_receive(bgp_peer peer) if (open_recv->can_dynamic) SET_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV); - /* Graceful restart */ + /* Graceful restart + * + * NB: appear not to care about open_recv->has_restarted ! + */ + if (open_recv->can_g_restart) + SET_FLAG (peer->cap, PEER_CAP_RESTART_RCV) ; + for (afi = qAFI_min ; afi <= qAFI_max ; ++afi) for (safi = qSAFI_min ; safi <= qSAFI_max ; ++safi) { qafx_bit_t qb = qafx_bit_from_qAFI_qSAFI(afi, safi); - if (peer->afc[afi][safi] && (qb & open_recv->can_g_restart)) + if (peer->afc[afi][safi] && (qb & open_recv->can_preserve)) { SET_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_RCV); if (qb & open_recv->has_preserved) diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 71779567..62d76c60 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -764,35 +764,6 @@ bgp_open_send (struct peer *peer) } #endif -/* Send BGP notify packet with data portion. */ -void -bgp_notify_send_with_data (struct peer *peer, u_char code, u_char sub_code, - u_char *data, size_t datalen) -{ - bgp_notify notification; - notification = bgp_notify_new_with_data(code, sub_code, data, datalen); - - /* peer reset cause */ - if (sub_code != BGP_NOTIFY_CEASE_CONFIG_CHANGE) - { - if (sub_code == BGP_NOTIFY_CEASE_ADMIN_RESET) - peer->last_reset = PEER_DOWN_USER_RESET; - else if (sub_code == BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN) - peer->last_reset = PEER_DOWN_USER_SHUTDOWN; - else - peer->last_reset = PEER_DOWN_NOTIFY_SEND; - } - - bgp_peer_disable(peer, notification); -} - -/* Send BGP notify packet. */ -void -bgp_notify_send (struct peer *peer, u_char code, u_char sub_code) -{ - bgp_notify_send_with_data (peer, code, sub_code, NULL, 0); -} - /* Send route refresh message to the peer. */ void @@ -1449,11 +1420,11 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) char attrstr[BUFSIZ] = ""; /* Status must be Established. */ - if (peer->state != bgp_peer_sEstablished) + if (peer->state != bgp_peer_pEstablished) { zlog_err ("%s [FSM] Update packet received under status %s", peer->host, LOOKUP (bgp_peer_status_msg, peer->state)); - bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0); + bgp_peer_down_error (peer, BGP_NOTIFY_FSM_ERR, 0); return -1; } @@ -1476,8 +1447,8 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) zlog_err ("%s [Error] Update packet error" " (packet length is short for unfeasible length)", peer->host); - bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); return -1; } @@ -1490,8 +1461,8 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) zlog_err ("%s [Error] Update packet error" " (packet unfeasible length overflow %d)", peer->host, withdraw_len); - bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); return -1; } @@ -1518,8 +1489,8 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) zlog_warn ("%s [Error] Packet Error" " (update packet is short for attribute length)", peer->host); - bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); return -1; } @@ -1532,8 +1503,8 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) zlog_warn ("%s [Error] Packet Error" " (update packet attribute length overflow %d)", peer->host, attribute_len); - bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_MAL_ATTR); + bgp_peer_down_error (peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_MAL_ATTR); return -1; } @@ -1754,7 +1725,7 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) /* If peering is stopped due to some reason, do not generate BGP event. */ - if (peer->state != bgp_peer_sEstablished) + if (peer->state != bgp_peer_pEstablished) return 0; /* Generate BGP event. */ @@ -1949,9 +1920,8 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) { plog_err (peer->log, "%s [Error] BGP route refresh is not enabled", peer->host); - bgp_notify_send (peer, - BGP_NOTIFY_HEADER_ERR, - BGP_NOTIFY_HEADER_BAD_MESTYPE); + bgp_peer_down_error (peer, BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_BAD_MESTYPE); return; } @@ -1961,7 +1931,7 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) plog_err (peer->log, "%s [Error] Route refresh packet received under status %s", peer->host, LOOKUP (bgp_status_msg, peer->status)); - bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0); + bgp_peer_down_error (peer, BGP_NOTIFY_FSM_ERR, 0); return; } @@ -2003,7 +1973,7 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) if (size - (BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE) < 5) { zlog_info ("%s ORF route refresh length error", peer->host); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + bgp_peer_down_error (peer, BGP_NOTIFY_CEASE, 0); return; } @@ -2130,7 +2100,8 @@ bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length) if (pnt + 3 > end) { zlog_info ("%s Capability length error", peer->host); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + /* TODO: Is this the right notification ?? */ + bgp_peer_down_error (peer, BGP_NOTIFY_CEASE, 0); return -1; } action = *pnt; @@ -2142,7 +2113,8 @@ bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length) { zlog_info ("%s Capability Action Value error %d", peer->host, action); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + /* TODO: Is this the right notification ?? */ + bgp_peer_down_error (peer, BGP_NOTIFY_CEASE, 0); return -1; } @@ -2154,7 +2126,8 @@ bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length) if ((pnt + hdr->length + 3) > end) { zlog_info ("%s Capability length error", peer->host); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + /* TODO: Is this the right notification ?? */ + bgp_peer_down_error (peer, BGP_NOTIFY_CEASE, 0); return -1; } @@ -2200,16 +2173,20 @@ bgp_capability_msg_parse (struct peer *peer, u_char *pnt, bgp_size_t length) { peer->afc_recv[afi][safi] = 0; peer->afc_nego[afi][safi] = 0; + bool completed ; if (peer_active_nego (peer)) - bgp_clear_route_normal (peer, afi, safi); + completed = bgp_clear_routes (peer, afi, safi, false); else { + completed = true ; /* TODO: only used for unit tests. Test will need fixing */ #if 0 BGP_EVENT_ADD (peer, BGP_Stop); #endif - } + } ; + /* if bgp_clear_routes does not complete. what do we do ? */ + passert(completed) ; } } else @@ -2242,19 +2219,18 @@ bgp_capability_receive (struct peer *peer, bgp_size_t size) { plog_err (peer->log, "%s [Error] BGP dynamic capability is not enabled", peer->host); - bgp_notify_send (peer, - BGP_NOTIFY_HEADER_ERR, - BGP_NOTIFY_HEADER_BAD_MESTYPE); + bgp_peer_down_error (peer, BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_BAD_MESTYPE); return -1; } /* Status must be Established. */ - if (peer->state != bgp_peer_sEstablished) + if (peer->state != bgp_peer_pEstablished) { plog_err (peer->log, "%s [Error] Dynamic capability packet received under status %s", peer->host, LOOKUP (bgp_status_msg, peer->state)); - bgp_notify_send (peer, BGP_NOTIFY_FSM_ERR, 0); + bgp_peer_down_error (peer, BGP_NOTIFY_FSM_ERR, 0); return -1; } @@ -2405,9 +2381,8 @@ bgp_read (struct thread *thread) if (((type == BGP_MSG_OPEN) || (type == BGP_MSG_KEEPALIVE)) && ! bgp_marker_all_one (peer->ibuf, BGP_MARKER_SIZE)) { - bgp_notify_send (peer, - BGP_NOTIFY_HEADER_ERR, - BGP_NOTIFY_HEADER_NOT_SYNC); + bgp_peer_down_error (peer, BGP_NOTIFY_HEADER_ERR, + BGP_NOTIFY_HEADER_NOT_SYNC); goto done; } @@ -2508,7 +2483,7 @@ bgp_read (struct thread *thread) { if (BGP_DEBUG (events, EVENTS)) zlog_debug ("%s [Event] Accepting BGP peer delete", peer->host); - peer_delete (peer); + bgp_peer_delete (peer); } return 0; } diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h index f0798846..6cf9a394 100644 --- a/bgpd/bgp_packet.h +++ b/bgpd/bgp_packet.h @@ -46,11 +46,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA extern int bgp_read (struct thread *); extern int bgp_write (bgp_peer peer, struct stream*); -extern void bgp_keepalive_send (struct peer *); -extern void bgp_open_send (struct peer *); -extern void bgp_notify_send (struct peer *, u_int8_t, u_int8_t); -extern void bgp_notify_send_with_data (struct peer *, u_int8_t, u_int8_t, - u_int8_t *, size_t); extern void bgp_route_refresh_send (struct peer *, afi_t, safi_t, u_char, u_char, int); extern void bgp_capability_send (struct peer *, afi_t, safi_t, int, int); extern void bgp_default_update_send (struct peer *, struct attr *, diff --git a/bgpd/bgp_peer.c b/bgpd/bgp_peer.c index 18634fb0..2062c527 100644 --- a/bgpd/bgp_peer.c +++ b/bgpd/bgp_peer.c @@ -58,16 +58,6 @@ #include "bgpd/bgp_snmp.h" #endif /* HAVE_SNMP */ -/* prototypes */ - -static int bgp_session_has_established(bgp_peer peer); -static int bgp_session_has_stopped(bgp_peer peer); -static int bgp_session_has_disabled(bgp_peer peer); -static void bgp_uptime_reset (struct peer *peer); -static int bgp_routeadv_timer (struct thread *thread); -static int bgp_graceful_restart_timer_expire (struct thread *thread); -static int bgp_graceful_stale_timer_expire (struct thread *thread); - /*============================================================================== * This is the high level management of BGP Peers and peering conversations. * @@ -76,7 +66,7 @@ static int bgp_graceful_stale_timer_expire (struct thread *thread); * Here we look after... * * * the peer state and the effects of changes in that state - * * + * * * timers for advertisements, graceful restart, ... * * The naming of peers/sessions and bgp_session_index @@ -91,8 +81,122 @@ static int bgp_graceful_stale_timer_expire (struct thread *thread); * The bgp_peer_index maps IP addresses to the peer, and hence to the session * (if any exists and is active). * + * [To support multi-instance BGP, might be nice to use the "update source" + * address as part of the name of a peer. But that is another story.] + * + *------------------------------------------------------------------------------ + * The Peer State. + * + * The peer has a small number of states. Where there is a session, its state + * is a sub-state of the main peer state. + * + * The states of a peer are as follows: + * + * 1. bgp_peer_pIdle + * + * All peers start in this state. + * + * This is the case when: + * + * a. peer is administratively down/disabled/deactivated + * + * b. waiting for route flap or other such timer before reawakening. + * + * c. waiting for previous session to be closed -- in BGP Engine + * + * d. waiting for session to establish -- in BGP Engine + * + * The session states relate to the above as follows: + * + * sIdle, sDisabled or no session at all -- (a) or (b) + * + * sEnabled -- (d) + * + * sEstablished -- IMPOSSIBLE + * + * sLimping -- (c) + * + * Only when the peer is none of (a), (b) or (c) can a session be enabled + * and the session move to (d), sEnabled. So changes either in the state + * of the peer or in the state of the session have to check whether it + * is time to: + * + * 1. enable a new session + * + * 2. disable a session which has yet to reach sEstablished, but is + * now no longer correctly configured, or the like. + * + * 2. bgp_peer_pEstablished + * + * Reaches this state from pIdle when a session becomes established. + * + * This can only be the case when the session is sEstablished. + * + * If the session is stopped for any reason, issues a Disable request to + * the BGP Engine: + * + * peer -> pClearing + * session -> sLimping + * + * 3. bgp_peer_pClearing + * + * Reaches this state from pEstablished *only*, as above. + * + * In this state the session can only be: + * + * sLimping -- session disable has been sent to the BGP Engine. + * sDisabled -- session has been disabled by the BGP Engine + * + * Tidies up the peer, including clearing routes etc. Once the peer is + * completely tidy: + * + * peer -> pIdle + * + * On entry to pClearing the session will be sLimping, on exit it may be + * still sLimping, or have advanced to sDisabled. + * + * NB: while in pClearing the peer's routes and RIBs are being processed. + * All other parts of the peer may be modified... but mindful of the + * "background" task which is yet to complete. + * + * 4. bgp_peer_pDeleted + * + * This is an exotic state, reached only when a peer is being completely + * deleted. + * + * This state may be reached from any of the above. + * + * If there is an active session, it will be sLimping. When advances to + * sDisabled it will be deleted. + * + * The remaining tasks are to clear out routes, dismantle the peer + * structure and delete it. While that is happening, the peer is in this + * state. */ +/* prototypes */ + +static void bgp_session_has_established(bgp_session session); +static void bgp_session_has_stopped(bgp_session session); +static void bgp_session_has_disabled(bgp_session session); +static void bgp_uptime_reset (struct peer *peer); +static void bgp_peer_stop (struct peer *peer, bool nsf) ; +static void bgp_peer_reset_idle(struct peer *peer) ; +static void bgp_peer_down_notify(bgp_peer peer, peer_down_t why_down, + bgp_notify notification) ; +static void bgp_peer_shutdown(bgp_peer peer) ; +static bgp_notify bgp_peer_map_peer_down(peer_down_t why_down) ; +static peer_down_t bgp_peer_map_notification(bgp_notify notification) ; +static void bgp_peer_free (struct peer *peer) ; +static void bgp_peer_change_status (bgp_peer peer, bgp_peer_state_t new_state); +static void bgp_peer_timers_set (struct peer *peer) ; +static int bgp_routeadv_timer (struct thread *thread); +static int bgp_graceful_restart_timer_expire (struct thread *thread); +static void bgp_graceful_restart_timer_cancel (struct peer* peer) ; +static int bgp_graceful_stale_timer_expire (struct thread *thread); +static void bgp_graceful_stale_timer_cancel (struct peer* peer) ; + + /*============================================================================== * Deal with change in session state -- mqueue_action function. * @@ -106,17 +210,16 @@ bgp_session_do_event(mqueue_block mqb, mqb_flag_t flag) { struct bgp_session_event_args * args = mqb_get_args(mqb) ; - bgp_session session = mqb_get_arg0(mqb) ; - bgp_peer peer = session->peer ; + bgp_session session = mqb_get_arg0(mqb) ; if (flag == mqb_action) { - BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + /* Pull stuff into Routing Engine *private* fields in the session */ session->event = args->event ; /* last event */ bgp_notify_set(&session->notification, args->notification) ; /* if any sent/received */ - session->err = args->err ; /* errno, if any */ + session->err = args->err ; /* errno, if any */ session->ordinal = args->ordinal ; /* primary/secondary connection */ switch(args->event) @@ -132,14 +235,18 @@ bgp_session_do_event(mqueue_block mqb, mqb_flag_t flag) if (session->state == bgp_session_sLimping) break ; - bgp_session_has_established(peer); + bgp_session_has_established(session); break ; /* If now Disabled, then the BGP Engine is acknowledging the a * session disable, and the session is now disabled. + * + * BEWARE: this may be the last thing that happens to the session + * and/or the related peer -- which may be deleted inside + * bgp_session_has_disabled(). */ case bgp_session_eDisabled: - bgp_session_has_disabled(peer); + bgp_session_has_disabled(session); break ; /* If now Stopped, then for some reason the BGP Engine has either @@ -152,11 +259,9 @@ bgp_session_do_event(mqueue_block mqb, mqb_flag_t flag) break ; if (args->stopped) - bgp_session_has_stopped(peer); + bgp_session_has_stopped(session) ; break ; } ; - - BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ } else bgp_notify_free(args->notification) ; @@ -164,41 +269,49 @@ bgp_session_do_event(mqueue_block mqb, mqb_flag_t flag) mqb_free(mqb) ; } -/* BGP Session has been Established. - * - * NB: holding the session structure mutex. - * - * Send keepalive packet then make first update information. +/*------------------------------------------------------------------------------ + * BGP Session has been Established. */ -static int -bgp_session_has_established(bgp_peer peer) +static void +bgp_session_has_established(bgp_session session) { afi_t afi; safi_t safi; - int nsf_af_count = 0; + int nsf_af_count ; - bgp_session session = peer->session ; + bgp_peer peer = session->peer ; + assert(peer->session == session) ; /* Safety first */ + /* Session state change -- Routing Engine private fields */ assert(session->state == bgp_session_sEnabled) ; session->state = bgp_session_sEstablished ; session->flow_control = BGP_XON_REFRESH; /* updates can be sent */ - peer_change_status (peer, bgp_peer_sEstablished); - /* update peer state from received open */ - bgp_peer_open_state_receive(peer); + /* Peer state change. + * + * This stops all timers other than the Graceful Stale Timer. + */ + bgp_peer_change_status (peer, bgp_peer_pEstablished); + + /* Extracting information from shared fields. */ + BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - /* get the local and remote addresses, and set the nexthop. */ + bgp_peer_open_state_receive(peer); sockunion_set_dup(&peer->su_local, session->su_local) ; sockunion_set_dup(&peer->su_remote, session->su_remote) ; + + BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ + + /* Install next hop, as required. */ bgp_nexthop_set(peer->su_local, peer->su_remote, &peer->nexthop, peer) ; /* Reset capability open status flag. */ if (! CHECK_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN)) SET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN); - /* Clear last notification data. */ + /* Clear last notification data -- Routing Engine private field */ bgp_notify_unset(&(peer->session->notification)); /* Clear start timer value to default. */ @@ -212,17 +325,29 @@ bgp_session_has_established(bgp_peer peer) if (bgp_flag_check (peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) zlog_info ("%%ADJCHANGE: neighbor %s Up", peer->host); - /* graceful restart */ - UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT); + /* graceful restart */ + UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT) ; + + nsf_af_count = 0 ; for (afi = AFI_IP ; afi < AFI_MAX ; afi++) for (safi = SAFI_UNICAST ; safi < SAFI_UNICAST_MULTICAST ; safi++) { + /* If the afi/safi has been negotiated, and have received Graceful + * Restart capability, and is Restarting, and will Gracefully Restart + * the afi/safi, then.... + */ if (peer->afc_nego[afi][safi] && CHECK_FLAG (peer->cap, PEER_CAP_RESTART_ADV) && CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_RCV)) { + /* If have held onto routes for this afi/safi but forwarding has + * not been preserved, then clean out the stale routes. + * + * Set NSF for this address family for next time. + */ if (peer->nsf[afi][safi] - && ! CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_RESTART_AF_PRESERVE_RCV)) + && ! CHECK_FLAG (peer->af_cap[afi][safi], + PEER_CAP_RESTART_AF_PRESERVE_RCV)) bgp_clear_stale_route (peer, afi, safi); peer->nsf[afi][safi] = 1; @@ -230,6 +355,7 @@ bgp_session_has_established(bgp_peer peer) } else { + /* Remove stale routes, if any for this afi/safi */ if (peer->nsf[afi][safi]) bgp_clear_stale_route (peer, afi, safi); peer->nsf[afi][safi] = 0; @@ -241,28 +367,9 @@ bgp_session_has_established(bgp_peer peer) else { UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE); - if (peer->t_gr_stale) - { - BGP_TIMER_OFF (peer->t_gr_stale); - if (BGP_DEBUG (events, EVENTS)) - zlog_debug ("%s graceful restart stalepath timer stopped", peer->host); - } + bgp_graceful_stale_timer_cancel(peer) ; } - if (peer->t_gr_restart) - { - BGP_TIMER_OFF (peer->t_gr_restart); - if (BGP_DEBUG (events, EVENTS)) - zlog_debug ("%s graceful restart timer stopped", peer->host); - } - -#ifdef HAVE_SNMP - bgpTrapEstablished (peer); -#endif /* HAVE_SNMP */ - - /* Reset uptime, send keepalive, send current table. */ - bgp_uptime_reset (peer); - /* Send route-refresh when ORF is enabled */ for (afi = AFI_IP ; afi < AFI_MAX ; afi++) for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++) @@ -284,145 +391,268 @@ bgp_session_has_established(bgp_peer peer) || CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_OLD_RCV)) SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH); + /* Reset uptime, send current table. */ + bgp_uptime_reset (peer); + bgp_announce_route_all (peer); BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer, 1); - return 0; +#ifdef HAVE_SNMP + bgpTrapEstablished (peer); +#endif /* HAVE_SNMP */ } /*------------------------------------------------------------------------------ - * State change to sLimping, session mutex locked + * BGP Session has stopped of its own accord -> disable the peer & session. * * The BGP Engine has signalled that it has stopped the session. Response to * that is to tell it to disable the session, and then wait in sLimping state - * until the BGP Engine acknowledges the disable request. + * until the BGP Engine completes the disable request and signals that. + * + * TODO: session stopped because we stopped it or because the other end did ? + * TODO: restore NSF !! */ -static int -bgp_session_has_stopped(bgp_peer peer) +static void +bgp_session_has_stopped(bgp_session session) { - bgp_session session = peer->session ; + peer_down_t why_down ; - assert(bgp_session_is_active(session)) ; - bgp_peer_disable(peer, NULL); - /* TODO: needs to deal with NOTIFICATION, if any ?? */ + bgp_peer peer = session->peer ; + assert(peer->session == session) ; /* Safety first */ - return 0; -} + assert(bgp_session_is_active(session)) ; /* "confused" if not */ + + if (session->state == bgp_session_sEstablished) + { + /* 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); + } ; + + if (session->notification == NULL) + why_down = PEER_DOWN_CLOSE_SESSION ; + else + { + if (session->notification->received) + why_down = PEER_DOWN_NOTIFY_RECEIVED ; + else + why_down = bgp_peer_map_notification(session->notification) ; + } ; + + bgp_peer_down_notify(peer, why_down, session->notification) ; +} ; /*------------------------------------------------------------------------------ - * State change to sDisabled, session mutex locked + * BGP Session is now disabled -> can now move peer on to next state. * - * The BGP Engine has acknowledged the disable request. + * The BGP Engine has closed the session in response to a disable request, and + * no longer has an interest in it, and has signalled that. */ -static int -bgp_session_has_disabled(bgp_peer peer) +static void +bgp_session_has_disabled(bgp_session session) { - bgp_session session = peer->session; + bgp_peer peer = session->peer ; + assert(peer->session == session) ; /* Safety first */ assert(session->state == bgp_session_sLimping) ; session->state = bgp_session_sDisabled ; - /* Immediately discard any other messages for this session. */ + /* Immediately discard any other messages for this session. */ mqueue_revoke(routing_nexus->queue, session) ; - /* does the peer need to be re-enabled? */ - if (session->defer_enable || peer->state == bgp_peer_sIdle) - { - session->defer_enable = 0; - bgp_peer_enable(peer); - } - else if (peer->state == bgp_peer_sEstablished) - { - /* disable the peer */ - bgp_peer_stop(peer); - peer_change_status(peer, bgp_peer_sClearing); - bgp_clear_route_all(peer); - } - - /* if the program is terminating then see if this was the last session - * and if so ... die .... + /* If the session is marked "delete_me", do that. + * + * Otherwise, Old session now gone, so re-enable peer if now possible. */ - program_terminate_if_all_disabled(); - return 0; -} + if (session->delete_me) + bgp_session_delete(peer) ; /* NB: this may also delete the peer. */ + else + bgp_peer_enable(peer); +} ; -/* Administrative BGP peer stop event. */ -/* May be called multiple times for the same peer */ -int -bgp_peer_stop (struct peer *peer) +/*------------------------------------------------------------------------------ + * Administrative BGP peer stop event -- stop pEstablished peer. + * + * MUST be pEstablished. + * + * Changes to pClearing and sets off to clear down all routes etc, subject to + * the required NSF. + * + * On exit the peer will be: + * + * - pIdle if the clearing of routes etc completed immediately. + * + * - pClearing if further work for clearing of routes has been scheduled + * for later. + * + * NB: Leaves any Max Prefix Timer running. + * + * Starts Graceful Restart and Stale Route timers iff NSF and at least one + * afi/safi is enabled for NSF. + */ +static void +bgp_peer_stop (struct peer *peer, bool nsf) { - afi_t afi; - safi_t safi; - char orf_name[BUFSIZ]; + bool cleared ; - /* Can't do this in Clearing; events are used for state transitions */ - if (peer->state != bgp_peer_sClearing) - { - /* Delete all existing events of the peer */ - BGP_EVENT_FLUSH (peer); - } + assert(peer->state == bgp_peer_pEstablished) ; - /* Increment Dropped count. */ - if (peer->state == bgp_peer_sEstablished) - { - peer->dropped++; + /* Change state to pClearing. + * + * Turns off all timers. + */ + bgp_peer_change_status(peer, bgp_peer_pClearing) ; - /* bgp log-neighbor-changes of neighbor Down */ - if (bgp_flag_check (peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) - zlog_info ("%%ADJCHANGE: neighbor %s Down %s", peer->host, - peer_down_str [(int) peer->last_reset]); + peer->dropped++ ; + peer->resettime = time (NULL) ; - /* graceful restart */ - if (peer->t_gr_stale) - { - BGP_TIMER_OFF (peer->t_gr_stale); - if (BGP_DEBUG (events, EVENTS)) - zlog_debug ("%s graceful restart stalepath timer stopped", peer->host); - } - if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT)) - { - if (BGP_DEBUG (events, EVENTS)) - { - zlog_debug ("%s graceful restart timer started for %d sec", - peer->host, peer->v_gr_restart); - zlog_debug ("%s graceful restart stalepath timer started for %d sec", - peer->host, peer->bgp->stalepath_time); - } - BGP_TIMER_ON (peer->t_gr_restart, bgp_graceful_restart_timer_expire, - peer->v_gr_restart); - BGP_TIMER_ON (peer->t_gr_stale, bgp_graceful_stale_timer_expire, - peer->bgp->stalepath_time); - } - else - { - UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE); + /* bgp log-neighbor-changes of neighbor Down */ + if (bgp_flag_check (peer->bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) + zlog_info ("%%ADJCHANGE: neighbor %s Down %s", peer->host, + peer_down_str [(int) peer->last_reset]); - for (afi = AFI_IP ; afi < AFI_MAX ; afi++) - for (safi = SAFI_UNICAST ; safi < SAFI_UNICAST_MULTICAST ; safi++) - peer->nsf[afi][safi] = 0; + /* Clear out routes, with NSF if required. + * + * Sets PEER_STATUS_NSF_WAIT iff NSF and at least one afi/safi is enabled + * for NSF. Clears PEER_STATUS_NSF_WAIT otherwise. + */ + cleared = bgp_clear_all_routes (peer, nsf) ; + + /* graceful restart */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT)) + { + if (BGP_DEBUG (events, EVENTS)) + { + zlog_debug ("%s graceful restart timer started for %d sec", + peer->host, peer->v_gr_restart); + zlog_debug ("%s graceful restart stalepath timer started for %d sec", + peer->host, peer->bgp->stalepath_time); } + BGP_TIMER_ON (peer->t_gr_restart, bgp_graceful_restart_timer_expire, + peer->v_gr_restart); + BGP_TIMER_ON (peer->t_gr_stale, bgp_graceful_stale_timer_expire, + peer->bgp->stalepath_time); + } ; - /* set last reset time */ - peer->resettime = time (NULL); - /* Reset uptime. */ - bgp_uptime_reset (peer); + /* Reset uptime. */ + bgp_uptime_reset (peer); #ifdef HAVE_SNMP - bgpTrapBackwardTransition (peer); + bgpTrapBackwardTransition (peer); #endif /* HAVE_SNMP */ - /* Reset uptime. */ - bgp_uptime_reset (peer); + /* Reset peer synctime */ + peer->synctime = 0; - /* Reset peer synctime */ - peer->synctime = 0; - } + /* If completed the clearing of routes, then can now go pIdle */ + if (cleared) + bgp_peer_change_status(peer, bgp_peer_pIdle) ; +} ; + +/*------------------------------------------------------------------------------ + * Clear out any stale routes, cancel any Graceful Restart timers. + * + * NB: may still be pClearing from when peer went down leaving these stale + * routes. + * + * NB: assumes clearing stale routes will complete immediately ! + */ +static void +bgp_peer_clear_all_stale_routes (struct peer *peer) +{ + afi_t afi; + safi_t safi; + + for (afi = AFI_IP ; afi < AFI_MAX ; afi++) + for (safi = SAFI_UNICAST ; safi < SAFI_UNICAST_MULTICAST ; safi++) + if (peer->nsf[afi][safi]) + bgp_clear_stale_route (peer, afi, safi); + + bgp_graceful_restart_timer_cancel(peer) ; + bgp_graceful_stale_timer_cancel(peer) ; + + UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT); +} ; + +/*------------------------------------------------------------------------------ + * If waiting for NSF peer to come back, stop now. + * + * When a session stops -- see bgp_peer_stop(), above -- the peer is set + * PEER_STATUS_NSF_WAIT iff there are now stale routes in the table, waiting + * for peer to come back. + * + * This function terminates that wait and clears out any stale routes, and + * cancels any timers. + * + * Also clears down all NSF flags. + * + * If is PEER_STATUS_NSF_WAIT, MUST be pIdle or pClearing. + * + * NB: may still be pClearing from when peer went down leaving these stale + * routes. + * + * NB: assumes clearing stale routes will complete immediately ! + */ +static void +bgp_peer_nsf_stop (struct peer *peer) +{ + afi_t afi; + safi_t safi; + + if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)) + { + assert( (peer->state == bgp_peer_pIdle) + || (peer->state == bgp_peer_pClearing) ) ; + + bgp_peer_clear_all_stale_routes (peer) ; + } ; + + UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE); + + for (afi = AFI_IP ; afi < AFI_MAX ; afi++) + for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++) + peer->nsf[afi][safi] = 0; +} ; - /* Stop all timers. */ - BGP_TIMER_OFF (peer->t_asorig); - BGP_TIMER_OFF (peer->t_routeadv); +/*------------------------------------------------------------------------------ + * Set the PEER_FLAG_SHUTDOWN flag and also: + * + * - turn off any NSF and related timers. + * + * - turn off any Max Prefix overflow and related timers. + */ +static void +bgp_peer_shutdown(struct peer *peer) +{ + SET_FLAG (peer->flags, PEER_FLAG_SHUTDOWN) ; + + bgp_maximum_prefix_cancel_timer(peer) ; + + bgp_peer_nsf_stop (peer) ; +} ; + +/*------------------------------------------------------------------------------ + * Reset state as peer goes to pIdle. + * + * This tidies things up, ready for session to be enabled again. + * + * NB: can be called any number of times... either to tidy up or to prepare + * for session to be enabled. + */ +static void +bgp_peer_reset_idle(struct peer *peer) +{ + afi_t afi; + safi_t safi; + char orf_name[BUFSIZ]; + + assert(peer->state == bgp_peer_pIdle) ; + + UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE) ; peer->cap = 0 ; for (afi = AFI_IP ; afi < AFI_MAX ; afi++) @@ -430,8 +660,9 @@ bgp_peer_stop (struct peer *peer) { /* Reset all negotiated variables */ peer->afc_nego[afi][safi] = 0; - peer->afc_adv[afi][safi] = 0; + peer->afc_adv[afi][safi] = 0; peer->afc_recv[afi][safi] = 0; + peer->nsf[afi][safi] = 0; /* peer address family capability flags*/ peer->af_cap[afi][safi] = 0; @@ -445,7 +676,7 @@ bgp_peer_stop (struct peer *peer) /* ORF received prefix-filter pnt */ sprintf (orf_name, "%s.%d.%d", peer->host, afi, safi); prefix_bgp_orf_remove_all (orf_name); - } + } ; /* Reset keepalive and holdtime */ if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER)) @@ -457,283 +688,38 @@ bgp_peer_stop (struct peer *peer) { peer->v_keepalive = peer->bgp->default_keepalive; peer->v_holdtime = peer->bgp->default_holdtime; - } - -#if 0 - /* Reset prefix count */ - peer->pcount[AFI_IP][SAFI_UNICAST] = 0; - peer->pcount[AFI_IP][SAFI_MULTICAST] = 0; - peer->pcount[AFI_IP][SAFI_MPLS_VPN] = 0; - peer->pcount[AFI_IP6][SAFI_UNICAST] = 0; - peer->pcount[AFI_IP6][SAFI_MULTICAST] = 0; -#endif - - return 0; -} - -/*------------------------------------------------------------------------------ - * When a bgp_clear_route_all completes, this is called to move the peer state - * on, if required. - */ -extern void -bgp_peer_clearing_completed(struct peer *peer) -{ - /* Flush the event queue and ensure the peer is shut down */ - bgp_peer_stop(peer); - BGP_EVENT_FLUSH (peer); - - if (peer->state == bgp_peer_sClearing) - { - peer_change_status (peer, bgp_peer_sIdle); - /* enable peer if required */ - bgp_peer_enable(peer); - } + } ; } ; -#if 0 -/* Stop all timers for the given peer +/*------------------------------------------------------------------------------ + * Allocate new peer object, implicitly locked. */ -static void -bgp_peer_timers_stop(bgp_peer peer) -{ - BGP_TIMER_OFF(peer->t_asorig) ; - BGP_TIMER_OFF(peer->t_routeadv) ; - - BGP_TIMER_OFF (peer->t_gr_restart); - BGP_TIMER_OFF (peer->t_gr_stale); - BGP_TIMER_OFF (peer->t_pmax_restart); -} ; -#endif - -static void -bgp_timer_set (struct peer *peer) -{ - switch (peer->state) - { - case bgp_peer_sIdle: - /* First entry point of peer's finite state machine. In Idle - status start timer is on unless peer is shutdown or peer is - inactive. All other timer must be turned off */ - BGP_TIMER_OFF (peer->t_asorig); - BGP_TIMER_OFF (peer->t_routeadv); - break; - -#if 0 - case Connect: - /* After start timer is expired, the peer moves to Connnect - status. Make sure start timer is off and connect timer is - on. */ - BGP_TIMER_OFF (peer->t_asorig); - BGP_TIMER_OFF (peer->t_routeadv); - break; - - case Active: - /* Active is waiting connection from remote peer. And if - connect timer is expired, change status to Connect. */ - BGP_TIMER_OFF (peer->t_asorig); - BGP_TIMER_OFF (peer->t_routeadv); - break; - - case OpenSent: - /* OpenSent status. */ - BGP_TIMER_OFF (peer->t_asorig); - BGP_TIMER_OFF (peer->t_routeadv); - break; - - case OpenConfirm: - /* OpenConfirm status. */ - BGP_TIMER_OFF (peer->t_asorig); - BGP_TIMER_OFF (peer->t_routeadv); - break; -#endif - - case bgp_peer_sEstablished: - /* In Established status start and connect timer is turned - off. */ - BGP_TIMER_OFF (peer->t_asorig); - break; - case bgp_peer_sDeleted: - BGP_TIMER_OFF (peer->t_gr_restart); - BGP_TIMER_OFF (peer->t_gr_stale); - BGP_TIMER_OFF (peer->t_pmax_restart); - case bgp_peer_sClearing: - BGP_TIMER_OFF (peer->t_asorig); - BGP_TIMER_OFF (peer->t_routeadv); - break; - default: - assert(0); - } -} - -static int -bgp_routeadv_timer (struct thread *thread) -{ - struct peer *peer; - uint32_t jittered ; - uint32_t jitter ; - - peer = THREAD_ARG (thread); - peer->t_routeadv = NULL; - - if (BGP_DEBUG (fsm, FSM)) - zlog (peer->log, LOG_DEBUG, - "%s [FSM] Timer (routeadv timer expire)", - peer->host); - - peer->synctime = time (NULL); - - 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 ; - - /* TODO: move this to the Routeing Engine qtimer pile. */ - BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer, jittered) ; - - return 0; -} - -/* Reset bgp update timer */ -static void -bgp_uptime_reset (struct peer *peer) -{ - peer->uptime = time (NULL); -} - -/* BGP Peer Down Cause */ -const char *peer_down_str[] = -{ - "", - "Router ID changed", - "Remote AS changed", - "Local AS change", - "Cluster ID changed", - "Confederation identifier changed", - "Confederation peer changed", - "RR client config change", - "RS client config change", - "Update source change", - "Address family activated", - "Admin. shutdown", - "User reset", - "BGP Notification received", - "BGP Notification send", - "Peer closed the session", - "Neighbor deleted", - "Peer-group add member", - "Peer-group delete member", - "Capability changed", - "Passive config change", - "Multihop config change", - "NSF peer closed the session" -}; - -static int -bgp_graceful_restart_timer_expire (struct thread *thread) -{ - struct peer *peer; - afi_t afi; - safi_t safi; - - peer = THREAD_ARG (thread); - peer->t_gr_restart = NULL; - - /* NSF delete stale route */ - for (afi = AFI_IP ; afi < AFI_MAX ; afi++) - for (safi = SAFI_UNICAST ; safi < SAFI_UNICAST_MULTICAST ; safi++) - if (peer->nsf[afi][safi]) - bgp_clear_stale_route (peer, afi, safi); - - UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT); - BGP_TIMER_OFF (peer->t_gr_stale); - - if (BGP_DEBUG (events, EVENTS)) - { - zlog_debug ("%s graceful restart timer expired", peer->host); - zlog_debug ("%s graceful restart stalepath timer stopped", peer->host); - } - - bgp_timer_set (peer); - - return 0; -} - -static int -bgp_graceful_stale_timer_expire (struct thread *thread) -{ - struct peer *peer; - afi_t afi; - safi_t safi; - - peer = THREAD_ARG (thread); - peer->t_gr_stale = NULL; - - if (BGP_DEBUG (events, EVENTS)) - zlog_debug ("%s graceful restart stalepath timer expired", peer->host); - - /* NSF delete stale route */ - for (afi = AFI_IP ; afi < AFI_MAX ; afi++) - for (safi = SAFI_UNICAST ; safi < SAFI_UNICAST_MULTICAST ; safi++) - if (peer->nsf[afi][safi]) - bgp_clear_stale_route (peer, afi, safi); - - return 0; -} - -#if 0 -/* BGP peer is stopped by the error. */ -static int -bgp_stop_with_error (struct peer *peer) -{ - /* Double start timer. */ - peer->v_start *= 2; - - /* Overflow check. */ - if (peer->v_start >= (60 * 2)) - peer->v_start = (60 * 2); - - bgp_stop (peer); - - return 0; -} -#endif - -/* Allocate new peer object, implicitly locked. */ struct peer * -peer_new (struct bgp *bgp) +bgp_peer_new (struct bgp *bgp) { afi_t afi; safi_t safi; struct peer *peer; struct servent *sp; - /* bgp argument is absolutely required */ + /* bgp argument is absolutely required */ assert (bgp); - if (!bgp) - return NULL; + bgp_lock (bgp); - /* Allocate new peer. */ + /* Allocate new peer. */ peer = XCALLOC (MTYPE_BGP_PEER, sizeof (struct peer)); /* Set default value. */ - peer->v_start = BGP_INIT_START_TIMER; + peer->v_start = BGP_INIT_START_TIMER; peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; - peer->v_asorig = BGP_DEFAULT_ASORIGINATE; - peer->state = bgp_peer_sIdle; - peer->ostate = bgp_peer_sIdle; - peer->weight = 0; - peer->password = NULL; - peer->bgp = bgp; - peer = peer_lock (peer); /* initial reference */ - bgp_lock (bgp); + peer->v_asorig = BGP_DEFAULT_ASORIGINATE; + peer->state = bgp_peer_pIdle; + peer->ostate = bgp_peer_pIdle; + peer->weight = 0; + peer->password = NULL; + peer->bgp = bgp; + + peer = bgp_peer_lock (peer); /* initial reference */ /* Set default flags. */ for (afi = AFI_IP; afi < AFI_MAX; afi++) @@ -762,32 +748,41 @@ peer_new (struct bgp *bgp) return peer; } -/* Create new BGP peer. */ +/*------------------------------------------------------------------------------ + * Create new BGP peer -- if an AFI/SAFI is given, activate & enable for that. + * + * Peer starts in pIdle state. + * + * This is creating a PEER_STATUS_REAL_PEER, which is placed on the bgp->peer + * list. + */ struct peer * -peer_create (union sockunion *su, struct bgp *bgp, as_t local_as, - as_t remote_as, afi_t afi, safi_t safi) +bgp_peer_create (union sockunion *su, struct bgp *bgp, as_t local_as, + as_t remote_as, afi_t afi, safi_t safi) { - int active; struct peer *peer; + bool enable = (afi != 0) && (safi != 0) ; + + /* TODO: should bgp_peer_new() set state pNull ?? */ + peer = bgp_peer_new (bgp); /* sets peer->state == pIdle */ - peer = peer_new (bgp); - peer->su = *su; - peer->local_as = local_as; - peer->as = remote_as; - peer->local_id = bgp->router_id; - peer->v_holdtime = bgp->default_holdtime; + peer->su = *su; + peer->local_as = local_as; + peer->as = remote_as; + peer->local_id = bgp->router_id; + peer->v_holdtime = bgp->default_holdtime; peer->v_keepalive = bgp->default_keepalive; if (peer_sort (peer) == BGP_PEER_IBGP) peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; else peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; - peer = peer_lock (peer); /* bgp peer list reference */ + SET_FLAG(peer->sflags, PEER_STATUS_REAL_PEER) ; + peer = bgp_peer_lock (peer); /* bgp peer list reference */ listnode_add_sort (bgp->peer, peer); - /* If "default ipv4-unicast", then this is implicit "activate" */ - active = peer_active (peer); - if (afi && safi) + /* If required, activate given AFI/SAFI -- eg "default ipv4-unicast" */ + if (enable) peer->afc[afi][safi] = 1; /* Last read time set */ @@ -802,70 +797,102 @@ peer_create (union sockunion *su, struct bgp *bgp, as_t local_as, /* Make peer's address string. */ peer->host = sockunion_su2str (su, MTYPE_BGP_PEER_HOST) ; - /* register */ + /* session -- NB: *before* peer is registered, so before any possible + * lookup up by accept() in the BGP Engine + */ + bgp_session_init_new(peer); + + /* register -- NB: *after* peer->session set, so safe */ bgp_peer_index_register(peer, &peer->su); - /* session */ - peer->session = bgp_session_init_new(peer->session, peer); + /* Fire up any timers that should run during pIdle */ + bgp_peer_timers_set (peer); - /* If implicit activate, Set up peer's events and timers. */ - if ((! active) && peer_active (peer)) - { - bgp_timer_set (peer); - bgp_peer_enable (peer); - } ; + /* If require, enable now all is ready */ + if (enable) + bgp_peer_enable(peer) ; return peer; } -/* Delete peer from configuration. +/*------------------------------------------------------------------------------ + * Delete peer from configuration. * * The peer is moved to a dead-end "Deleted" neighbour-state, to allow * it to "cool off" and refcounts to hit 0, at which state it is freed. * - * This function /should/ take care to be idempotent, to guard against - * it being called multiple times through stray events that come in - * that happen to result in this function being called again. That - * said, getting here for a "Deleted" peer is a bug in the neighbour - * FSM. */ int -peer_delete (struct peer *peer) +bgp_peer_delete (struct peer *peer) { int i; afi_t afi; safi_t safi; struct bgp *bgp; struct bgp_filter *filter; - struct listnode *pn; - assert (peer->state != bgp_peer_sDeleted); + bgp = peer->bgp ; - bgp = peer->bgp; + /* Once peer is pDeleting it should be impossible to find in order to + * bgp_peer_delete() it ! + */ + assert (peer->state != bgp_peer_pDeleting); - if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT)) - peer_nsf_stop (peer); + /* If the peer is active, then need to shut it down now. If there are any + * stale routes, flush them now. + * + * If the peer ends up in pClearing state, that implies that some background + * work is required to completely clear this peer. So increment the + * reference count to hold the peer in existence. That will be decremented + * again in bgp_peer_clearing_completed(). + * + * If the peer ends up in sIdle state, that implies that any routes that + * needed clearing have been dealt with. + * + * There may be a session in existence. If so, it must either be sLimping or + * sDisabled. + * + * Changing to pDeleting state turns off all timers. + */ + bgp_peer_down(peer, PEER_DOWN_NEIGHBOR_DELETE) ; - /* If this peer belongs to peer group, clear up the - relationship. */ - if (peer->group) - { - if ((pn = listnode_lookup (peer->group->peer, peer))) - { - peer = peer_unlock (peer); /* group->peer list reference */ - list_delete_node (peer->group->peer, pn); - } - peer->group = NULL; - } + if (peer->state == bgp_peer_pClearing) + bgp_peer_lock (peer) ; + else + assert(peer->state == bgp_peer_pIdle) ; + + bgp_peer_change_status (peer, bgp_peer_pDeleting); - /* Withdraw all information from routing table. We can not use - * BGP_EVENT_ADD (peer, BGP_Stop) at here. Because the event is - * executed after peer structure is deleted. + /* Increment count of peers lingering in pDeleting + * + * The count is used while terminating bgpd -- keeps all the nexuses running + * until this count drops to zero. */ - peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE; - bgp_peer_stop (peer); - peer_change_status (peer, bgp_peer_sDeleted); - bgp_clear_route_all(peer); + bm->peer_linger_count++ ; + + /* If is an rsclient in its own right, remove from rsclient list */ + if (peer_rsclient_active (peer)) + { + struct listnode *pn; + pn = listnode_lookup (bgp->rsclient, peer) ; + + bgp_peer_unlock (peer); /* rsclient list reference */ + list_delete_node (bgp->rsclient, pn); + } ; + + /* If this peer belongs to peer group, clear up the relationship. */ + if (peer->group != NULL) + { + struct listnode *pn; + pn = listnode_lookup (peer->group->peer, peer) ; + + assert(pn != NULL) ; + + bgp_peer_unlock (peer); /* group->peer list reference */ + list_delete_node (peer->group->peer, pn); + + peer->group = NULL; + } ; /* Password configuration */ if (peer->password) @@ -874,36 +901,48 @@ peer_delete (struct peer *peer) peer->password = NULL; } - bgp_timer_set (peer); /* stops all timers for Deleted */ - - /* Delete from all peer list. */ - if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP) - && (pn = listnode_lookup (bgp->peer, peer))) + /* Delete from bgp->peer list, if required. */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_REAL_PEER)) { - peer_unlock (peer); /* bgp peer list reference */ + struct listnode *pn; + pn = listnode_lookup (bgp->peer, peer) ; + + assert(pn != NULL) ; + + bgp_peer_unlock (peer); /* bgp peer list reference */ list_delete_node (bgp->peer, pn); - } + } ; - if (peer_rsclient_active (peer) - && (pn = listnode_lookup (bgp->rsclient, peer))) - { - peer_unlock (peer); /* rsclient list reference */ - list_delete_node (bgp->rsclient, pn); + /* Discard rsclient ribs which are owned by group + * and cross-check rib pointer and PEER_FLAG_RSERVER_CLIENT. + */ + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)) + { + assert(peer->rib[afi][safi] != NULL) ; + if (peer->af_group[afi][safi]) + { + peer->rib[afi][safi] = NULL ; + UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) ; + } ; + } + else + assert(peer->rib[afi][safi] == NULL) ; - /* Clear our own rsclient ribs. */ - for (afi = AFI_IP; afi < AFI_MAX; afi++) - for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) - if (CHECK_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_RSERVER_CLIENT)) - bgp_clear_route_rsclient(peer, afi, safi); - } + /* Can now clear any rsclient ribs. */ + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + if (peer->rib[afi][safi] != NULL) + { + bgp_clear_rsclient_rib(peer, afi, safi) ; + UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) ; + } ; - /* Free RIB for any family in which peer is RSERVER_CLIENT, and is not - member of a peer_group. */ + /* Have now finished with any rsclient ribs */ for (afi = AFI_IP; afi < AFI_MAX; afi++) for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) - if (peer->rib[afi][safi] && ! peer->af_group[afi][safi]) - bgp_table_finish (&peer->rib[afi][safi]); + bgp_table_finish (&peer->rib[afi][safi]) ; /* Buffers. */ if (peer->ibuf) @@ -955,27 +994,89 @@ peer_delete (struct peer *peer) peer->default_rmap[afi][safi].name = NULL; } - peer_unlock (peer); /* initial reference */ + /* Unregister the peer. + * + * NB: the peer can no longer be looked up by its 'name'. + * + * In particular this means that the accept() logic in the BGP Engine + * will conclude that the session should not be accepting connections. + * + * NB: also (currently) releases the peer_id -- which may not be so clever ? + */ + if (peer->index_entry != NULL) + bgp_peer_index_deregister(peer, &peer->su); + + /* Tear down session, if any and if possible. */ + bgp_session_delete(peer) ; + + /* Finally: count down the initial reference, which will delete the peer + * iff everything else has finished with it. + */ + bgp_peer_unlock (peer); /* initial reference */ return 0; +} ; + +/*------------------------------------------------------------------------------ + * increase reference count on a struct peer + */ +struct peer * +bgp_peer_lock (struct peer *peer) +{ + assert (peer && (peer->lock >= 0)); + + peer->lock++; + + return peer; } -void -peer_free (struct peer *peer) +/*------------------------------------------------------------------------------ + * decrease reference count on a struct peer + * + * If is last reference, the structure is freed and NULL returned + */ +struct peer * +bgp_peer_unlock (struct peer *peer) { - assert (peer->state == bgp_peer_sDeleted); + assert (peer && (peer->lock > 0)); - bgp_unlock(peer->bgp); + peer->lock--; - /* this /ought/ to have been done already through bgp_stop earlier, - * but just to be sure.. - */ - bgp_timer_set (peer); - BGP_EVENT_FLUSH (peer); + if (peer->lock == 0) + { +#if 0 + zlog_debug ("unlocked and freeing"); + zlog_backtrace (LOG_DEBUG); +#endif + bgp_peer_free (peer); + return NULL; + } - /* unregister */ - if (peer->index_entry != NULL) - bgp_peer_index_deregister(peer, &peer->su); +#if 0 + if (peer->lock == 1) + { + zlog_debug ("unlocked to 1"); + zlog_backtrace (LOG_DEBUG); + } +#endif + + return peer; +} + +/*------------------------------------------------------------------------------ + * Dismantle and free peer data structure. + */ +static void +bgp_peer_free (struct peer *peer) +{ + struct bgp* bgp ; + + assert (peer->state == bgp_peer_pDeleting); + assert (peer->session == NULL) ; /* session holds a lock on peer */ + + bm->peer_linger_count-- ; + + bgp = peer->bgp ; if (peer->desc) XFREE (MTYPE_PEER_DESC, peer->desc); @@ -991,110 +1092,785 @@ peer_free (struct peer *peer) if (peer->update_if) XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); - if (peer->clear_node_queue) - work_queue_free (peer->clear_node_queue); - - /* session */ - if (peer->session) - bgp_session_free(peer->session); - bgp_sync_delete (peer); - memset (peer, 0, sizeof (struct peer)); + memset (peer, 0, sizeof (struct peer)); + peer->lock = -54321 ; XFREE (MTYPE_BGP_PEER, peer); -} + bgp_unlock(bgp); +} ; + +/*------------------------------------------------------------------------------ + * Enable Peer + * + * This means that something has changed, and session can be started, if no + * session has already started. + * + * So does nothing unless in pIdle, and expects the session state to be: + * + * - sIdle, sDisabled or no session at all -- session enabled if possible + * + * - sEnabled -- session already enabled, so do nothing + * + * - sEstablished -- IMPOSSIBLE + * + * - sLimping -- cannot, yet, start a new session -- that will happen in due + * course. + * + * This means that any change which requires the session to be taken down and + * restarted needs to call bgp_peer_disable(). + * + * The other peer states: + * + * - pEstablished == sEstablished + * + * - pClearing -- cannot restart the peer yet, that will happen in due course. + * + * - pDeleted -- Enable peer makes no sense... asserts invalid. + * + * TODO: assert !pEstablished in bgp_peer_enable ? + */ void -peer_nsf_stop (struct peer *peer) +bgp_peer_enable(bgp_peer peer) { - afi_t afi; - safi_t safi; + switch (peer->state) + { + case bgp_peer_pIdle: + + /* Enable the session unless: + * 1) session is active -- ie sEnabled/sLimping (see above) + * 2) no address family is activated + * 3) the peer has been shutdown + * 4) is dealing with prefix overflow (waiting for timer) + */ + + if (bgp_session_is_active(peer->session)) + assert(peer->session->state != bgp_session_sEstablished) ; + else + { + if (peer_active (peer) + && !CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN) + && !CHECK_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW)) + { + /* enable the session */ + zlog_err ("%s: enabling peer %s:", __func__, peer->host) ; - UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT); - UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_MODE); + bgp_peer_reset_idle(peer) ; /* tidy up */ + bgp_session_enable(peer); + } ; + } ; + break ; - for (afi = AFI_IP ; afi < AFI_MAX ; afi++) - for (safi = SAFI_UNICAST ; safi < SAFI_UNICAST_MULTICAST ; safi++) - peer->nsf[afi][safi] = 0; + case bgp_peer_pEstablished: + break ; - if (peer->t_gr_restart) + case bgp_peer_pClearing: + break ; + + case bgp_peer_pDeleting: + zabort("cannot enable a pDeleting peer") ; + break ; + + default: + zabort("unknown peer->state") ; + break ; + } ; +} ; + +/*------------------------------------------------------------------------------ + * Down Peer -- bring down any existing session and restart if possible. + * + * The following "why_down" values are special: + * + * - PEER_DOWN_NSF_CLOSE_SESSION + * + * causes NSF to be turned on as the peer is stopped and routes are cleared. + * + * - PEER_DOWN_USER_SHUTDOWN + * + * causes the peer to be shutdown -- so won't restart. + * + * - PEER_DOWN_NEIGHBOR_DELETE + * + * prevents the peer from restarting. + * + * If there is an active session, then it must be disabled, sending the given + * notification, or one based on the reason for downing the peer. + * + * If there is no active session, any stale NSF routes will be cleared. + * + * So any session ends up as: + * + * sIdle -- wasn't active and still isn't + * + * sLimping -- was sEnabled or sEstablished, we now wait for BGP Engine + * to complete the disable action and signal when done. + * + * sDisabled -- has previously been disabled. + * + * The result depends on the initial peer state: + * + * 1. bgp_peer_pIdle + * + * Any session that was sEnabled will have been disabled -- and will now + * be sLimping. + * + * The peer will have automatically restarted, if possible. + * + * Noting that PEER_DOWN_USER_SHUTDOWN and PEER_DOWN_NEIGHBOR_DELETE both + * prevent any restart. + * + * 2. bgp_peer_pEstablished + * + * The session will have been disabled -- and will now be sLimping. + * + * See bgp_peer_stop() for the state of the peer. + * + * 3. bgp_peer_pClearing + * + * In this state the session can only be: + * + * sLimping -- session disable has been sent to the BGP Engine. + * sDisabled -- session has been disabled by the BGP Engine + * + * because peer must have been pEstablished immediately before. + * + * Do nothing -- will proceed to pIdle in due course. + * + * 4. bgp_peer_pDeleting + * + * In this state there may be no session at all, or the session can + * only be: + * + * sIdle -- session never got going + * sLimping -- session disable has been sent to the BGP Engine. + * sDisabled -- session has been disabled by the BGP Engine + * + * Do nothing -- peer will be deleted in due course. + */ +void +bgp_peer_down(bgp_peer peer, peer_down_t why_down) +{ + bgp_peer_down_notify(peer, why_down, NULL) ; +} ; + +static void +bgp_peer_down_notify(bgp_peer peer, peer_down_t why_down, + bgp_notify notification) +{ + /* Deal with session (if any). */ + + if (bgp_session_is_active(peer->session)) { - BGP_TIMER_OFF (peer->t_gr_restart); - if (BGP_DEBUG (events, EVENTS)) - zlog_debug ("%s graceful restart timer stopped", peer->host); + if (notification == NULL) + notification = bgp_peer_map_peer_down(why_down) ; + + bgp_notify_set_dup(&peer->session->notification, notification) ; + + bgp_session_disable(peer, notification) ; } - if (peer->t_gr_stale) + else + bgp_notify_free(notification) ; /* Discard unused notification */ + + /* Now worry about the state of the peer */ + + if (why_down == PEER_DOWN_USER_SHUTDOWN) + bgp_peer_shutdown(peer) ; + + if (why_down != PEER_DOWN_NULL) + peer->last_reset = why_down ; + + switch (peer->state) + { + case bgp_peer_pIdle: + assert(!bgp_session_is_active(peer->session) + || (peer->session->state == bgp_session_sLimping)) ; + + bgp_peer_nsf_stop (peer) ; /* flush stale routes, if any */ + + if (why_down != PEER_DOWN_NEIGHBOR_DELETE) + bgp_peer_enable(peer) ; /* Restart if possible. */ + + break ; + + case bgp_peer_pEstablished: + assert(peer->session->state == bgp_session_sLimping) ; + + bgp_peer_stop(peer, why_down == PEER_DOWN_NSF_CLOSE_SESSION) ; + + break ; + + case bgp_peer_pClearing: + assert( (peer->session->state == bgp_session_sLimping) + || (peer->session->state == bgp_session_sDisabled) ) ; + + bgp_peer_nsf_stop (peer) ; /* flush stale routes, if any */ + + break ; + + case bgp_peer_pDeleting: + assert( (peer->session == NULL) + || (peer->session->state == bgp_session_sIdle) + || (peer->session->state == bgp_session_sLimping) + || (peer->session->state == bgp_session_sDisabled) ) ; + break ; + + default: + zabort("unknown peer->state") ; + break ; + } ; +} ; + +/*------------------------------------------------------------------------------ + * Notify the far end that an error has been detected, and close down the + * session. + * + * The session will have been established, so the IdleHoldTime will be extended. + * + * Because it knows no better, the session will be restarted. + */ +extern void +bgp_peer_down_error(struct peer* peer, + bgp_nom_code_t code, bgp_nom_subcode_t subcode) +{ + bgp_peer_down_error_with_data (peer, code, subcode, NULL, 0); +} + +/*------------------------------------------------------------------------------ + * Notify the far end that an error has been detected, and close down the + * session. + * + * Same as above, except that this accepts a data part for the notification + * message. + */ +extern void +bgp_peer_down_error_with_data (struct peer* peer, + bgp_nom_code_t code, bgp_nom_subcode_t subcode, + const u_int8_t* data, size_t datalen) +{ + bgp_notify notification; + notification = bgp_notify_new_with_data(code, subcode, data, datalen); + + bgp_peer_down_notify(peer, bgp_peer_map_notification(notification), + notification) ; +} ; + +/*------------------------------------------------------------------------------ + * Construct notification based on the reason for bringing down the session + * + * Where the session is brought down by the other end, returns NULL. + */ +static bgp_notify +bgp_peer_map_peer_down(peer_down_t why_down) +{ + bgp_nom_code_t code ; + bgp_nom_subcode_t subcode ; + + assert((why_down >= PEER_DOWN_first) && (why_down < PEER_DOWN_count)) ; + + code = BGP_NOMC_CEASE ; /* Default values */ + subcode = BGP_NOMS_UNSPECIFIC ; + + switch(why_down) + { + case PEER_DOWN_NULL: + return NULL ; + + /* Session taken down at this end for some unspecified reason */ + + case PEER_DOWN_UNSPECIFIED: + break ; + + /* Configuration changes that cause a session to be reset. */ + + case PEER_DOWN_CONFIG_CHANGE: + case PEER_DOWN_RID_CHANGE: + case PEER_DOWN_REMOTE_AS_CHANGE: + case PEER_DOWN_LOCAL_AS_CHANGE: + case PEER_DOWN_CLID_CHANGE: + case PEER_DOWN_CONFED_ID_CHANGE: + case PEER_DOWN_CONFED_PEER_CHANGE: + case PEER_DOWN_RR_CLIENT_CHANGE: + case PEER_DOWN_RS_CLIENT_CHANGE: + case PEER_DOWN_UPDATE_SOURCE_CHANGE: + case PEER_DOWN_AF_ACTIVATE: + case PEER_DOWN_RMAP_BIND: + case PEER_DOWN_RMAP_UNBIND: + case PEER_DOWN_CAPABILITY_CHANGE: + case PEER_DOWN_PASSIVE_CHANGE: + case PEER_DOWN_MULTIHOP_CHANGE: + case PEER_DOWN_AF_DEACTIVATE: + case PEER_DOWN_PASSWORD_CHANGE: + case PEER_DOWN_ALLOWAS_IN_CHANGE: + subcode = BGP_NOMS_C_CONFIG ; + break ; + + /* Other actions that cause a session to be reset */ + + case PEER_DOWN_USER_SHUTDOWN: + subcode = BGP_NOMS_C_SHUTDOWN ; + break ; + + case PEER_DOWN_USER_RESET: + subcode = BGP_NOMS_C_RESET ; + break ; + + case PEER_DOWN_NEIGHBOR_DELETE: + subcode = BGP_NOMS_C_DECONFIG ; + break ; + + case PEER_DOWN_INTERFACE_DOWN: + return NULL ; /* nowhere to send a notification ! */ + + /* Errors and problems that cause a session to be reset */ + /* */ + /* SHOULD really have a notification constructed for these, but for */ + /* completeness construct an "unspecified" for these. */ + + case PEER_DOWN_MAX_PREFIX: + subcode = BGP_NOMS_C_MAX_PREF ; + break ; + + case PEER_DOWN_HEADER_ERROR: + code = BGP_NOMC_HEADER ; + break ; + + case PEER_DOWN_OPEN_ERROR: + code = BGP_NOMC_OPEN ; + break ; + + case PEER_DOWN_UPDATE_ERROR: + code = BGP_NOMC_UPDATE ; + break ; + + case PEER_DOWN_HOLD_TIMER: + code = BGP_NOMC_HOLD_EXP ; + break ; + + case PEER_DOWN_FSM_ERROR: + code = BGP_NOMC_FSM ; + break ; + + case PEER_DOWN_DYN_CAP_ERROR: + code = BGP_NOMC_DYN_CAP ; + break ; + + /* Things the far end can do to cause a session to be reset */ + + case PEER_DOWN_NOTIFY_RECEIVED: + return NULL ; /* should not get here ! */ + + case PEER_DOWN_CLOSE_SESSION: + case PEER_DOWN_NSF_CLOSE_SESSION: + return NULL ; /* nowhere to send a notification ! */ + + /* To keep the compiler happy. */ + case PEER_DOWN_count: + break ; /* should have asserted already */ + } ; + + return bgp_notify_new(code, subcode) ; +} ; + +/*------------------------------------------------------------------------------ + * Construct reason for bringing down the session based on the notification + */ +static peer_down_t +bgp_peer_map_notification(bgp_notify notification) +{ + switch (bgp_notify_get_code(notification)) /* deals with NULL */ + { + case BGP_NOMC_UNDEF: + break ; + + case BGP_NOMC_HEADER: + return PEER_DOWN_HEADER_ERROR ; + + case BGP_NOMC_OPEN: + return PEER_DOWN_OPEN_ERROR ; + + case BGP_NOMC_UPDATE: + return PEER_DOWN_UPDATE_ERROR ; + + case BGP_NOMC_HOLD_EXP: + return PEER_DOWN_HOLD_TIMER ; + + case BGP_NOMC_FSM: + return PEER_DOWN_FSM_ERROR ; + + case BGP_NOMC_CEASE: + switch (notification->subcode) + { + case BGP_NOMS_C_MAX_PREF: + return PEER_DOWN_MAX_PREFIX ; + + case BGP_NOMS_C_SHUTDOWN: + return PEER_DOWN_USER_SHUTDOWN ; + + case BGP_NOMS_C_DECONFIG: + return PEER_DOWN_NEIGHBOR_DELETE ; + + case BGP_NOMS_C_RESET: + return PEER_DOWN_USER_RESET ; + + case BGP_NOMS_C_REJECTED: /* should not get here */ + return PEER_DOWN_NULL ; + + case BGP_NOMS_C_CONFIG: + return PEER_DOWN_CONFIG_CHANGE ; + + case BGP_NOMS_C_COLLISION: /* should not get here */ + return PEER_DOWN_NULL ; + + case BGP_NOMS_C_RESOURCES: /* not used */ + return PEER_DOWN_NULL ; + + default: + break ; + } ; + break ; + + case BGP_NOMC_DYN_CAP: + return PEER_DOWN_DYN_CAP_ERROR ; + + default: + break ; + } ; + + return PEER_DOWN_UNSPECIFIED ; +} ; + +/*------------------------------------------------------------------------------ + * Background route clearing has completed. + * + * If there is any background work required to clear routes for a given peer, + * then when that completes, this is called to move the state of the peer + * forwards. + */ +extern void +bgp_peer_clearing_completed(struct peer *peer) +{ + switch (peer->state) + { + case bgp_peer_pIdle: + case bgp_peer_pEstablished: + zabort("invalid peer->state") ; + break ; + + case bgp_peer_pClearing: + bgp_peer_change_status (peer, bgp_peer_pIdle); + bgp_peer_enable(peer); + break ; + + case bgp_peer_pDeleting: + bgp_peer_unlock (peer); /* route clearing "reference" */ + break ; + + default: + zabort("unknown peer->state") ; + break ; + } ; +} ; + +/*------------------------------------------------------------------------------ + * Set new peer state. + * + * If state changes, do dump new state and log state change if required. + * + * In any case, set timers for the new state -- so if state hasn't changed, + * will restart those timers. + */ +static void +bgp_peer_change_status (bgp_peer peer, bgp_peer_state_t new_state) +{ + if (peer->state != new_state) { + bgp_dump_state (peer, peer->state, new_state); + + /* Preserve old status and change into new status. */ + peer->ostate = peer->state ; + peer->state = new_state ; + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s went from %s to %s", peer->host, + LOOKUP (bgp_peer_status_msg, peer->ostate), + LOOKUP (bgp_peer_status_msg, peer->state)) ; + + if (new_state == bgp_peer_pIdle) + bgp_peer_reset_idle(peer) ; /* tidy up */ + } ; + + bgp_peer_timers_set (peer); +} ; + +/*============================================================================== + * Timer handling + */ + +static void +bgp_peer_timers_set (struct peer *peer) +{ + switch (peer->state) + { + case bgp_peer_pIdle: + /* On entry to pIdle the Graceful Restart Timers are left running: + * + * - if no connection is established within the Graceful Restart time, + * then things are no longer graceful, and the stale routes have to + * be thrown away. + * + * - if routes do not thereafter arrive quickly enough, then the + * Graceful Stale time kicks in and stale routes will be thrown away. + */ + BGP_TIMER_OFF (peer->t_routeadv) ; + BGP_TIMER_OFF (peer->t_pmax_restart) ; + break; + + case bgp_peer_pEstablished: + /* On entry to pEstablished only the the Graceful Stale Timer is left + * running. + * + * Any Graceful Restart Timer can be cancelled -- have established in + * time. + */ + BGP_TIMER_OFF (peer->t_routeadv) ; + BGP_TIMER_OFF (peer->t_pmax_restart) ; + + bgp_graceful_restart_timer_cancel(peer) ; + break; + + case bgp_peer_pClearing: + /* On entry to pClearing, turn off all timers. + * + * The Graceful Restart timer should not be running in any case. + * + * If the session is brought down quickly enough, the Graceful Stale + * timer may be running. + */ + BGP_TIMER_OFF (peer->t_routeadv); + BGP_TIMER_OFF (peer->t_pmax_restart); + BGP_TIMER_OFF (peer->t_gr_restart); + + bgp_graceful_stale_timer_cancel(peer) ; + break ; + + case bgp_peer_pDeleting: + /* On entry to pDeleting, turn off all timers. + */ + BGP_TIMER_OFF (peer->t_routeadv); + BGP_TIMER_OFF (peer->t_pmax_restart); + BGP_TIMER_OFF (peer->t_gr_restart); BGP_TIMER_OFF (peer->t_gr_stale); - if (BGP_DEBUG (events, EVENTS)) - zlog_debug ("%s graceful restart stalepath timer stopped", peer->host); - } - bgp_clear_route_all (peer); -} + break; -/* enable the peer */ -void -bgp_peer_enable(bgp_peer peer) + default: + assert(0); + } ; +} ; + +static int +bgp_routeadv_timer (struct thread *thread) { - /* Don't enable the session if: - * 1) the peer not idle, which means not ready yet: clearing, deleting or - * waiting for disable. - * 2) no address family is activated - * 3) the peer has been shutdown - * 4) is dealing with prefix overflow: its timer will enable peer when ready + struct peer *peer; + uint32_t jittered ; + uint32_t jitter ; + + peer = THREAD_ARG (thread); + peer->t_routeadv = NULL; + + if (BGP_DEBUG (fsm, FSM)) + zlog (peer->log, LOG_DEBUG, + "%s [FSM] Timer (routeadv timer expire)", + peer->host); + + peer->synctime = time (NULL); + + 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 ; - if ((peer->state == bgp_peer_sIdle) - && peer_active (peer) - && !CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN) - && !CHECK_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW)) - { - /* enable the session */ - zlog_err ("%s: enabling peer %s:", __func__, peer->host) ; + /* TODO: move this to the Routeing Engine qtimer pile. */ + BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer, jittered) ; - bgp_session_enable(peer); - } + return 0; +} + +/* Reset bgp update timer */ +static void +bgp_uptime_reset (struct peer *peer) +{ + peer->uptime = time (NULL); } -/* disable the peer - * sent notification, disable session +/*------------------------------------------------------------------------------ + * BGP Peer Down Causes mapped to strings */ -void -bgp_peer_disable(bgp_peer peer, bgp_notify notification) +const char *peer_down_str[] = { - 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); + [PEER_DOWN_NULL] = "", + + [PEER_DOWN_UNSPECIFIED] = "Unspecified reason", + + [PEER_DOWN_CONFIG_CHANGE] = "Unspecified config change", + + [PEER_DOWN_RID_CHANGE] = "Router ID changed", + [PEER_DOWN_REMOTE_AS_CHANGE] = "Remote AS changed", + [PEER_DOWN_LOCAL_AS_CHANGE] = "Local AS change", + [PEER_DOWN_CLID_CHANGE] = "Cluster ID changed", + [PEER_DOWN_CONFED_ID_CHANGE] = "Confederation identifier changed", + [PEER_DOWN_CONFED_PEER_CHANGE] = "Confederation peer changed", + [PEER_DOWN_RR_CLIENT_CHANGE] = "RR client config change", + [PEER_DOWN_RS_CLIENT_CHANGE] = "RS client config change", + [PEER_DOWN_UPDATE_SOURCE_CHANGE] = "Update source change", + [PEER_DOWN_AF_ACTIVATE] = "Address family activated", + [PEER_DOWN_RMAP_BIND] = "Peer-group add member", + [PEER_DOWN_RMAP_UNBIND] = "Peer-group delete member", + [PEER_DOWN_CAPABILITY_CHANGE] = "Capability changed", + [PEER_DOWN_PASSIVE_CHANGE] = "Passive config change", + [PEER_DOWN_MULTIHOP_CHANGE] = "Multihop config change", + [PEER_DOWN_AF_DEACTIVATE] = "Address family deactivated", + [PEER_DOWN_PASSWORD_CHANGE] = "MD5 Password changed", + [PEER_DOWN_ALLOWAS_IN_CHANGE] = "Allow AS in changed", + + [PEER_DOWN_USER_SHUTDOWN] = "Admin. shutdown", + [PEER_DOWN_USER_RESET] = "User reset", + [PEER_DOWN_NEIGHBOR_DELETE] = "Neighbor deleted", + + [PEER_DOWN_INTERFACE_DOWN] = "Interface down", + + [PEER_DOWN_MAX_PREFIX] = "Max Prefix Limit exceeded", + + [PEER_DOWN_HEADER_ERROR] = "Error in message header", + [PEER_DOWN_OPEN_ERROR] = "Error in BGP OPEN message", + [PEER_DOWN_UPDATE_ERROR] = "Error in BGP UPDATE message", + [PEER_DOWN_HOLD_TIMER] = "HoldTimer expired", + [PEER_DOWN_FSM_ERROR] = "Error in FSM sequence", + [PEER_DOWN_DYN_CAP_ERROR] = "Error in Dynamic Capability", + + [PEER_DOWN_NOTIFY_RECEIVED] = "Notification received", + [PEER_DOWN_NSF_CLOSE_SESSION] = "NSF peer closed the session", + [PEER_DOWN_CLOSE_SESSION] = "Peer closed the session", +} ; + +CONFIRM(sizeof(peer_down_str) == (PEER_DOWN_count * sizeof(const char*))) ; - bgp_session_disable(peer, notification); +/*------------------------------------------------------------------------------ + * Graceful Restart timer has expired. + * + * MUST be pIdle or pClearing -- transition to pEstablished cancels this timer. + * + * Clears out stale routes and stops the Graceful Restart Stale timer. + * + * Clears down PEER_STATUS_NSF_MODE & PEER_STATUS_NSF_WAIT. + */ +static int +bgp_graceful_restart_timer_expire (struct thread *thread) +{ + struct peer *peer; + + peer = THREAD_ARG (thread); + peer->t_gr_restart = NULL; + + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s graceful restart timer expired", peer->host) ; + + bgp_peer_nsf_stop (peer) ; + + return 0; +} + +/*------------------------------------------------------------------------------ + * Cancel any Graceful Restart timer + * + * NB: does NOT do anything about any stale routes or about any stale timer ! + */ +static void +bgp_graceful_restart_timer_cancel (struct peer* peer) +{ + if (peer->t_gr_restart) + { + BGP_TIMER_OFF (peer->t_gr_restart); + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s graceful restart timer stopped", peer->host); } +} ; + +/*------------------------------------------------------------------------------ + * Graceful Restart Stale timer has expired. + * + * SHOULD be pEstablished, because otherwise the Graceful Restart timer should + * have gone off before this does, and cancelled this. + * + * To be safe, if not pEstablished, then MUST be pIdle or pClearing, so can do + * bgp_peer_nsf_stop (peer). + * + * Clears out stale routes and stops the Graceful Restart Stale timer. + * + * Clears down PEER_STATUS_NSF_MODE & PEER_STATUS_NSF_WAIT. + */ +static int +bgp_graceful_stale_timer_expire (struct thread *thread) +{ + struct peer *peer; + + peer = THREAD_ARG (thread); + peer->t_gr_stale = NULL; + + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s graceful restart stalepath timer expired", peer->host); + + if (peer->state == bgp_peer_pEstablished) + bgp_peer_clear_all_stale_routes(peer) ; else + bgp_peer_nsf_stop(peer) ; + + return 0; +} + +/*------------------------------------------------------------------------------ + * Cancel any Graceful Restart Stale timer + * + * NB: does NOT do anything about any stale routes ! + */ +static void +bgp_graceful_stale_timer_cancel (struct peer* peer) +{ + if (peer->t_gr_stale) { - bgp_notify_free(notification) ; - bgp_peer_stop(peer); + BGP_TIMER_OFF (peer->t_gr_stale); + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s graceful restart stalepath timer stopped", peer->host); } -} +} ; -/* Called after event occurred, this function changes status */ -void -peer_change_status (bgp_peer peer, int status) +#if 0 +/* BGP peer is stopped by the error. */ +static int +bgp_stop_with_error (struct peer *peer) { - bgp_dump_state (peer, peer->state, status); + /* Double start timer. */ + peer->v_start *= 2; - /* Preserve old status and change into new status. */ - peer->ostate = peer->state; - peer->state = status; + /* Overflow check. */ + if (peer->v_start >= (60 * 2)) + peer->v_start = (60 * 2); + + bgp_stop (peer); - if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s went from %s to %s", - peer->host, - LOOKUP (bgp_peer_status_msg, peer->ostate), - LOOKUP (bgp_peer_status_msg, peer->state)); + return 0; } +#endif + /*============================================================================== * For the given interface name, get a suitable address so can bind() before diff --git a/bgpd/bgp_peer.h b/bgpd/bgp_peer.h index 91070377..ab32117c 100644 --- a/bgpd/bgp_peer.h +++ b/bgpd/bgp_peer.h @@ -106,6 +106,72 @@ struct bgp_filter * */ +enum PEER_DOWN { + PEER_DOWN_first = 0, + + PEER_DOWN_NULL = 0, /* Not a PEER_DOWN */ + + /* Session taken down at this end for some unspecified reason */ + + PEER_DOWN_UNSPECIFIED, + + /* Configuration changes that cause a session to be reset. */ + + PEER_DOWN_CONFIG_CHANGE, /* Unspecified config change */ + + PEER_DOWN_RID_CHANGE, /* 'bgp router-id' */ + PEER_DOWN_REMOTE_AS_CHANGE, /* 'neighbor remote-as' */ + PEER_DOWN_LOCAL_AS_CHANGE, /* 'neighbor local-as' */ + PEER_DOWN_CLID_CHANGE, /* 'bgp cluster-id' */ + PEER_DOWN_CONFED_ID_CHANGE, /* 'bgp confederation identifier' */ + PEER_DOWN_CONFED_PEER_CHANGE, /* 'bgp confederation peer' */ + PEER_DOWN_RR_CLIENT_CHANGE, /* 'neighbor route-reflector-client' */ + PEER_DOWN_RS_CLIENT_CHANGE, /* 'neighbor route-server-client' */ + PEER_DOWN_UPDATE_SOURCE_CHANGE, /* 'neighbor update-source' */ + PEER_DOWN_AF_ACTIVATE, /* 'neighbor activate' */ + PEER_DOWN_RMAP_BIND, /* 'neighbor peer-group' */ + PEER_DOWN_RMAP_UNBIND, /* 'no neighbor peer-group' */ + PEER_DOWN_CAPABILITY_CHANGE, /* 'neighbor capability' */ + PEER_DOWN_PASSIVE_CHANGE, /* 'neighbor passive' */ + PEER_DOWN_MULTIHOP_CHANGE, /* 'neighbor multihop' */ + PEER_DOWN_AF_DEACTIVATE, /* 'no neighbor activate' */ + PEER_DOWN_PASSWORD_CHANGE, /* password changed */ + PEER_DOWN_ALLOWAS_IN_CHANGE, /* allowas-in change */ + + /* Other actions that cause a session to be reset */ + + PEER_DOWN_USER_SHUTDOWN, /* 'neighbor shutdown' */ + PEER_DOWN_USER_RESET, /* 'clear ip bgp' */ + PEER_DOWN_NEIGHBOR_DELETE, /* neighbor delete */ + + PEER_DOWN_INTERFACE_DOWN, /* interface reported to be down */ + + /* Errors and problems that cause a session to be reset */ + + PEER_DOWN_MAX_PREFIX, /* max prefix limit exceeded */ + + PEER_DOWN_HEADER_ERROR, /* error in BGP Message header */ + PEER_DOWN_OPEN_ERROR, /* error in BGP OPEN message */ + PEER_DOWN_UPDATE_ERROR, /* error in BGP UPDATE message */ + PEER_DOWN_HOLD_TIMER, /* HoldTimer expired */ + PEER_DOWN_FSM_ERROR, /* error in FSM sequence */ + PEER_DOWN_DYN_CAP_ERROR, /* error in Dynamic Capability */ + + /* Things the far end can do to cause a session to be reset */ + + PEER_DOWN_NOTIFY_RECEIVED, /* notification received */ + PEER_DOWN_CLOSE_SESSION, /* tcp session close */ + PEER_DOWN_NSF_CLOSE_SESSION, /* NSF tcp session close */ + + /* Number of down causes */ + PEER_DOWN_count +} ; + +typedef enum PEER_DOWN peer_down_t ; + + + + struct peer { /* BGP structure. */ @@ -156,35 +222,35 @@ struct peer struct stream *work; /* Status of the peer. */ - bgp_peer_state_t state; /* current state */ - bgp_peer_state_t ostate; /* old state */ + bgp_peer_state_t state; /* current state */ + bgp_peer_state_t ostate; /* old state */ /* Peer index, used for dumping TABLE_DUMP_V2 format */ uint16_t table_dump_index; /* Peer information */ + bgp_peer_index_entry index_entry ; bgp_session session ; /* Current session */ - bgp_peer_index_entry index_entry ; /* and our index entry */ - - int ttl; /* TTL of TCP connection to the peer. */ - char *desc; /* Description of the peer. */ - unsigned short port; /* Destination port for peer */ - char *host; /* Printable address of the peer. */ - union sockunion su; /* Sockunion address of the peer. */ - time_t uptime; /* Last Up/Down time */ - time_t readtime; /* Last read time */ - time_t resettime; /* Last reset time */ - - unsigned int ifindex; /* ifindex of the BGP connection. */ - char *ifname; /* bind interface name. */ + + int ttl; /* TTL of TCP connection to the peer. */ + char *desc; /* Description of the peer. */ + unsigned short port; /* Destination port for peer */ + char *host; /* Printable address of the peer. */ + union sockunion su; /* Sockunion address of the peer. */ + time_t uptime; /* Last Up/Down time */ + time_t readtime; /* Last read time */ + time_t resettime; /* Last reset time */ + + unsigned int ifindex; /* ifindex of the BGP connection. */ + char *ifname; /* bind interface name. */ char *update_if; union sockunion *update_source; struct zlog *log; - union sockunion *su_local; /* Sockunion of local address. */ - union sockunion *su_remote; /* Sockunion of remote address. */ - int shared_network; /* Is this peer shared same network. */ - struct bgp_nexthop nexthop; /* Nexthop */ + union sockunion *su_local; /* Sockunion of local address. */ + union sockunion *su_remote; /* Sockunion of remote address. */ + int shared_network; /* Is this peer shared same network. */ + struct bgp_nexthop nexthop; /* Nexthop */ /* Peer address family configuration. */ u_char afc[AFI_MAX][SAFI_MAX]; @@ -265,13 +331,13 @@ struct peer /* Peer status flags. */ u_int16_t sflags; -#define PEER_STATUS_ACCEPT_PEER (1 << 0) /* accept peer */ -#define PEER_STATUS_PREFIX_OVERFLOW (1 << 1) /* prefix-overflow */ -#define PEER_STATUS_CAPABILITY_OPEN (1 << 2) /* capability open send */ -#define PEER_STATUS_HAVE_ACCEPT (1 << 3) /* accept peer's parent */ -#define PEER_STATUS_GROUP (1 << 4) /* peer-group conf */ -#define PEER_STATUS_NSF_MODE (1 << 5) /* NSF aware peer */ -#define PEER_STATUS_NSF_WAIT (1 << 6) /* wait comeback peer */ +#define PEER_STATUS_REAL_PEER (1 << 0) /* not group conf or peer_self */ +#define PEER_STATUS_PREFIX_OVERFLOW (1 << 1) /* prefix-overflow */ +#define PEER_STATUS_CAPABILITY_OPEN (1 << 2) /* capability open send */ +#define PEER_STATUS_HAVE_ACCEPT (1 << 3) /* accept peer's parent */ +#define PEER_STATUS_GROUP (1 << 4) /* peer-group conf */ +#define PEER_STATUS_NSF_MODE (1 << 5) /* NSF aware peer */ +#define PEER_STATUS_NSF_WAIT (1 << 6) /* wait comeback peer */ /* Peer status af flags (reset in bgp_stop) */ u_int16_t af_sflags[AFI_MAX][SAFI_MAX]; @@ -285,17 +351,17 @@ struct peer /* Default attribute value for the peer. */ u_int32_t config; -#define PEER_CONFIG_WEIGHT (1 << 0) /* Default weight. */ -#define PEER_CONFIG_TIMER (1 << 1) /* keepalive & holdtime */ -#define PEER_CONFIG_CONNECT (1 << 2) /* connect */ -#define PEER_CONFIG_ROUTEADV (1 << 3) /* route advertise */ +#define PEER_CONFIG_WEIGHT (1 << 0) /* Default weight. */ +#define PEER_CONFIG_TIMER (1 << 1) /* keepalive & holdtime */ +#define PEER_CONFIG_CONNECT (1 << 2) /* connect */ +#define PEER_CONFIG_ROUTEADV (1 << 3) /* route advertise */ u_int32_t weight; u_int32_t holdtime; u_int32_t keepalive; u_int32_t connect; u_int32_t routeadv; - /* Timer values. */ + /* Timer values. */ u_int32_t v_start; u_int32_t v_connect; u_int32_t v_holdtime; @@ -305,74 +371,51 @@ struct peer u_int32_t v_pmax_restart; u_int32_t v_gr_restart; - /* Threads. */ + /* Threads. */ struct thread *t_asorig; struct thread *t_routeadv; struct thread *t_pmax_restart; struct thread *t_gr_restart; struct thread *t_gr_stale; - /* workqueues */ - struct work_queue *clear_node_queue; - - /* BGP state count */ - u_int32_t established; /* Established */ - u_int32_t dropped; /* Dropped */ + /* BGP state count */ + u_int32_t established; + u_int32_t dropped; - /* Synchronization list and time. */ + /* Synchronization list and time. */ struct bgp_synchronize *sync[AFI_MAX][SAFI_MAX]; time_t synctime; - /* Send prefix count. */ + /* Send prefix count. */ unsigned long scount[AFI_MAX][SAFI_MAX]; - /* Announcement attribute hash. */ + /* Announcement attribute hash. */ struct hash *hash[AFI_MAX][SAFI_MAX]; - /* Filter structure. */ + /* Filter structure. */ struct bgp_filter filter[AFI_MAX][SAFI_MAX]; - /* ORF Prefix-list */ + /* ORF Prefix-list */ struct prefix_list *orf_plist[AFI_MAX][SAFI_MAX]; - /* Prefix count. */ + /* Prefix count. */ unsigned long pcount[AFI_MAX][SAFI_MAX]; - /* Max prefix count. */ + /* Max prefix count. */ unsigned long pmax[AFI_MAX][SAFI_MAX]; u_char pmax_threshold[AFI_MAX][SAFI_MAX]; u_int16_t pmax_restart[AFI_MAX][SAFI_MAX]; #define MAXIMUM_PREFIX_THRESHOLD_DEFAULT 75 - /* allowas-in. */ + /* allowas-in. */ char allowas_in[AFI_MAX][SAFI_MAX]; - /* peer reset cause */ - char last_reset; -#define PEER_DOWN_RID_CHANGE 1 /* bgp router-id command */ -#define PEER_DOWN_REMOTE_AS_CHANGE 2 /* neighbor remote-as command */ -#define PEER_DOWN_LOCAL_AS_CHANGE 3 /* neighbor local-as command */ -#define PEER_DOWN_CLID_CHANGE 4 /* bgp cluster-id command */ -#define PEER_DOWN_CONFED_ID_CHANGE 5 /* bgp confederation identifier command */ -#define PEER_DOWN_CONFED_PEER_CHANGE 6 /* bgp confederation peer command */ -#define PEER_DOWN_RR_CLIENT_CHANGE 7 /* neighbor route-reflector-client command */ -#define PEER_DOWN_RS_CLIENT_CHANGE 8 /* neighbor route-server-client command */ -#define PEER_DOWN_UPDATE_SOURCE_CHANGE 9 /* neighbor update-source command */ -#define PEER_DOWN_AF_ACTIVATE 10 /* neighbor activate command */ -#define PEER_DOWN_USER_SHUTDOWN 11 /* neighbor shutdown command */ -#define PEER_DOWN_USER_RESET 12 /* clear ip bgp command */ -#define PEER_DOWN_NOTIFY_RECEIVED 13 /* notification received */ -#define PEER_DOWN_NOTIFY_SEND 14 /* notification send */ -#define PEER_DOWN_CLOSE_SESSION 15 /* tcp session close */ -#define PEER_DOWN_NEIGHBOR_DELETE 16 /* neghbor delete */ -#define PEER_DOWN_RMAP_BIND 17 /* neghbor peer-group command */ -#define PEER_DOWN_RMAP_UNBIND 18 /* no neighbor peer-group command */ -#define PEER_DOWN_CAPABILITY_CHANGE 19 /* neighbor capability command */ -#define PEER_DOWN_PASSIVE_CHANGE 20 /* neighbor passive command */ -#define PEER_DOWN_MULTIHOP_CHANGE 21 /* neighbor multihop command */ -#define PEER_DOWN_NSF_CLOSE_SESSION 22 /* NSF tcp session close */ - - /* The kind of route-map Flags.*/ + /* peer reset cause */ + peer_down_t last_reset; + + bgp_notify notification ; + + /* The kind of route-map Flags. */ u_char rmap_type; #define PEER_RMAP_TYPE_IN (1 << 0) /* neighbor route-map in */ #define PEER_RMAP_TYPE_OUT (1 << 1) /* neighbor route-map out */ @@ -387,7 +430,7 @@ struct peer #define BGP_TIMER_ON(T,F,V) \ do { \ - if (!(T) && (peer->state != bgp_peer_sDeleted)) \ + if (!(T) && (peer->state != bgp_peer_pDeleting)) \ THREAD_TIMER_ON(master,(T),(F),peer,(V)); \ } while (0) @@ -399,7 +442,7 @@ struct peer #define BGP_EVENT_ADD(P,E) \ do { \ - if ((P)->state != bgp_peer_sDeleted) \ + if ((P)->state != bgp_peer_pDeleting) \ thread_add_event (master, bgp_event, (P), (E)); \ } while (0) @@ -426,39 +469,38 @@ extern const char *peer_down_str[]; extern void bgp_session_do_event(mqueue_block mqb, mqb_flag_t flag); -void -bgp_peer_reenable(bgp_peer peer, bgp_notify notification); - extern void bgp_peer_enable(bgp_peer peer); extern void -bgp_peer_disable(bgp_peer peer, bgp_notify notification); - -extern int -bgp_peer_stop (struct peer *peer) ; +bgp_peer_down(bgp_peer peer, peer_down_t why_down) ; extern void -bgp_peer_clearing_completed(struct peer *peer) ; +bgp_peer_down_error(struct peer* peer, + bgp_nom_code_t code, bgp_nom_subcode_t subcode) ; +extern void +bgp_peer_down_error_with_data (struct peer* peer, + bgp_nom_code_t code, bgp_nom_subcode_t subcode, + const u_int8_t* data, size_t datalen) ; extern void -peer_change_status (bgp_peer peer, int status); +bgp_peer_clearing_completed(struct peer *peer) ; extern struct peer * -peer_new (struct bgp *bgp); +bgp_peer_new (struct bgp *bgp); extern struct peer * -peer_create (union sockunion *su, struct bgp *bgp, as_t local_as, - as_t remote_as, afi_t afi, safi_t safi); +bgp_peer_create (union sockunion *su, struct bgp *bgp, as_t local_as, + as_t remote_as, afi_t afi, safi_t safi); -extern int -peer_delete (struct peer *peer); +extern struct +peer *bgp_peer_lock (struct peer *) ; -extern void -peer_free (struct peer *peer); +extern struct +peer *bgp_peer_unlock (struct peer *) ; -extern void -peer_nsf_stop (struct peer *peer); +extern int +bgp_peer_delete (struct peer *peer); extern sockunion bgp_peer_get_ifaddress(bgp_peer peer, const char* ifname, pAF_t paf) ; diff --git a/bgpd/bgp_peer_index.c b/bgpd/bgp_peer_index.c index 5bb7f3d0..518a22bc 100644 --- a/bgpd/bgp_peer_index.c +++ b/bgpd/bgp_peer_index.c @@ -24,6 +24,7 @@ #include "bgpd/bgp_peer_index.h" #include "bgpd/bgp_peer.h" #include "bgpd/bgp_session.h" +#include "bgpd/bgp_connection.h" #include "lib/symtab.h" #include "lib/vector.h" @@ -35,17 +36,26 @@ * BGP Peer Index * * When peers are created, they are registered in the bgp_peer_index. When - * they are destroyed, they are removed. This is done by the Routeing Engine. + * they are destroyed, they are removed. This is done by the Routing Engine. * - * The peer index is used by the Routeing Engine to lookup peers either by + * The Peer Index is used by the Routing Engine to lookup peers either by * name (IP address) or by peer_id. * * The BGP Engine needs to lookup sessions when a listening socket accepts a * connection -- first, to decide whether to continue with the connection, and - * second, to tie the connection to the right session. It uses the peer index + * second, to tie the connection to the right session. It uses the Peer Index * to do this. * - * A mutex is used to coordinate access to the index. + * A mutex is used to coordinate access to the index. Only the Routing engine + * makes changes to the Peer Index, so it only needs to lock the mutex when it + * does make changes. The BGP Engine needs to lock the Peer Index whenever it + * accesses it. + * + * The BGP Engine needs the session associated with a given address if and only + * if the session is enabled for accept(), which implies that it is active and + * in the hands of the BGP Engine. To get to the session it needs to step + * via the peer->session pointer, having found the peer via the index. So, + * setting the peer->session pointer is done under the Peer Index Mutex. */ static struct symbol_table bgp_peer_index ; /* lookup by 'name' */ @@ -140,7 +150,7 @@ bgp_peer_index_reset(void) /* Ream out the peer id vector -- checking that all entries are empty */ while ((entry = vector_ream_keep(&bgp_peer_id_index)) != NULL) - passert(entry->peer == NULL) ; + passert((entry->peer == NULL) && (entry->next_free != entry)) ; /* Discard body of symbol table -- must be empty ! */ symbol_table_reset_keep(&bgp_peer_index) ; @@ -192,13 +202,14 @@ bgp_peer_index_register(bgp_peer peer, union sockunion* su) bgp_peer_id_table_make_ids() ; entry = bgp_peer_id_free_head ; - bgp_peer_id_free_head = (void*)entry->accept ; + bgp_peer_id_free_head = entry->next_free ; assert(vector_get_item(&bgp_peer_id_index, entry->id) == entry) ; /* Initialise the entry -- the id is already set */ entry->peer = peer ; - entry->accept = NULL ; + entry->next_free = entry ; + peer->index_entry = entry; /* Insert the new entry into the symbol table. */ @@ -235,7 +246,9 @@ bgp_peer_index_deregister(bgp_peer peer, union sockunion* su) entry = symbol_delete(sym) ; - passert((entry != NULL) && (entry->peer == peer) && (entry->accept == NULL)) ; + passert( (entry != NULL) && (entry->id != bgp_peer_id_null) + && (entry->peer == peer) + && (entry->next_free == entry) ) ; bgp_peer_id_table_free_entry(entry) ; @@ -271,21 +284,47 @@ bgp_peer_index_seek(union sockunion* su) extern bgp_peer_index_entry bgp_peer_index_seek_entry(union sockunion* su) { + bgp_peer_index_entry entry ; + /* Only the Routing Engine can add/delete entries -- so no lock required */ - return symbol_get_value(symbol_seek(&bgp_peer_index, su)) ; + entry = symbol_get_value(symbol_seek(&bgp_peer_index, su)) ; + + if (entry != NULL) + assert((entry->peer != NULL) && (entry->next_free = entry)) ; + + return entry ; } ; /*------------------------------------------------------------------------------ - * Lookup a peer by its address. + * Set peer->session field. * - * Return a pointer to its session iff it is prepared to accept() a connection. + * This is done under the Peer Index Mutex, so that the BGP Engine can step + * from the Peer Index entry, via the peer structure, to the session, which is + * also controlled by that Mutex, in safety. + */ +extern void +bgp_peer_index_set_session(bgp_peer peer, bgp_session session) +{ + BGP_PEER_INDEX_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ + + peer->session = session ; + + BGP_PEER_INDEX_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ +} ; + +/*------------------------------------------------------------------------------ + * Find whether given address is for a known peer, and if so whether it has + * an active session which is prepared to accept() a connection. * * For use by the BGP Engine. * * Returns: bgp_connection if: peer with given address is configured * and: the session is prepared to accept() * + * Note that the session cannot be deleted while it is in a prepared + * to accept state. + * * or: NULL otherwise * * Sets *p_found <=> a peer with the given address is configured. @@ -294,17 +333,25 @@ bgp_peer_index_seek_entry(union sockunion* su) * is initialised NULL when the index entry is created. */ extern bgp_connection -bgp_peer_index_seek_accept(union sockunion* su, int* p_found) +bgp_peer_index_seek_accept(union sockunion* su, bool* p_found) { - bgp_connection accept ; + bgp_connection accept ; bgp_peer_index_entry entry ; BGP_PEER_INDEX_LOCK() ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ entry = symbol_get_value(symbol_seek(&bgp_peer_index, su)) ; - *p_found = (entry != NULL) ; - accept = *p_found ? entry->accept : NULL ; + if (entry != NULL) + { + *p_found = true ; + accept = bgp_connection_query_accept(entry->peer->session) ; + } + else + { + *p_found = false ; + accept = NULL ; + } ; BGP_PEER_INDEX_UNLOCK() ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ @@ -333,10 +380,10 @@ bgp_peer_id_table_free_entry(bgp_peer_index_entry entry) if (bgp_peer_id_free_head == NULL) bgp_peer_id_free_head = entry ; else - bgp_peer_id_free_tail->accept = (void*)entry ; + bgp_peer_id_free_tail->next_free = entry ; bgp_peer_id_free_tail = entry ; - entry->accept = NULL ; /* used as 'next' for free list */ + entry->next_free = NULL ; entry->peer = NULL ; /* only when free ! */ } ; @@ -363,9 +410,9 @@ bgp_peer_id_table_make_ids(void) { confirm(bgp_peer_id_null == 0) ; - entry->id = 0 ; /* should never be used */ - entry->peer = NULL ; /* invalid in use */ - entry->accept = (void*)entry ; /* invalid if not active ! */ + entry->id = 0 ; /* should never be used */ + entry->peer = NULL ; /* invalid in use */ + entry->next_free = NULL ; /* invalid in use */ ++entry ; /* step past id == 0 */ id_new = 1 ; /* avoid setting id == 0 free */ diff --git a/bgpd/bgp_peer_index.h b/bgpd/bgp_peer_index.h index c99ec710..2ce3fb16 100644 --- a/bgpd/bgp_peer_index.h +++ b/bgpd/bgp_peer_index.h @@ -40,20 +40,10 @@ typedef unsigned bgp_peer_id_t ; struct bgp_peer_index_entry { - bgp_peer peer ; /* used by Routing Engine */ + bgp_peer_index_entry next_free ; /* for list of free peer_id's */ + /* points to self if entry is in use */ - /* The accept pointer is used by the listening socket(s) to find the - * session when it is prepared to accept a connection. - * - * This pointer MUST be NULL when not sEnabled or sEstablished. It - * will be set by the BGP Engine when it decides to accept connections, - * and cleared by it otherwise (and when a session stops). - * - * An active session contains a pointer to the peer index entry to - * facilitate this. - */ - - bgp_connection accept ; /* used by BGP Engine */ + bgp_peer peer ; /* NULL if entry is not in use */ bgp_peer_id_t id ; /* maps IP address to peer_id */ } ; @@ -88,8 +78,11 @@ bgp_peer_index_seek(sockunion su) ; extern bgp_peer_index_entry bgp_peer_index_seek_entry(sockunion su) ; +extern void +bgp_peer_index_set_session(bgp_peer peer, bgp_session session) ; + extern bgp_connection -bgp_peer_index_seek_accept(sockunion su, int* p_found) ; +bgp_peer_index_seek_accept(sockunion su, bool* p_found) ; #endif /* _QUAGGA_BGP_PEER_INDEX_H */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index ff7dada3..ca8bee8c 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -205,7 +205,7 @@ bgp_info_add (struct bgp_node *rn, struct bgp_info *ri) bgp_info_lock (ri); bgp_lock_node (rn); - peer_lock (peer); /* bgp_info peer reference */ + bgp_peer_lock (peer); /* bgp_info peer reference */ } /* Do the actual removal of info from RIB, for use by bgp_process @@ -237,7 +237,7 @@ bgp_info_reap (struct bgp_node *rn, struct bgp_info *ri) bgp_info_unlock (ri); /* fewer references to bgp_info */ bgp_unlock_node (rn); /* fewer references to bgp_node */ - peer_unlock (peer); /* fewer references to peer */ + bgp_peer_unlock (peer); /* fewer references to peer */ } void @@ -1434,7 +1434,7 @@ bgp_process_announce_selected (struct peer *peer, struct bgp_info *selected, p = &rn->p; /* Announce route to Established peer. */ - if (peer->state != bgp_peer_sEstablished) + if (peer->state != bgp_peer_pEstablished) return 0; /* Address family configuration check. */ @@ -1493,6 +1493,7 @@ bgp_process_rsclient (struct work_queue *wq, work_queue_item item) struct bgp_info *old_select; struct bgp_info_pair old_and_new; struct listnode *node, *nnode; + struct bgp_table *table ; struct peer *rsclient ; assert(wq->spec.data == item) ; @@ -1503,12 +1504,20 @@ bgp_process_rsclient (struct work_queue *wq, work_queue_item item) return WQ_SUCCESS ; /* hack off queue and prepare to process */ - pq->head = rn->wq_next ; - rn->on_wq = 0 ; - rsclient = rn->table->owner; - afi = rn->table->afi; - safi = rn->table->safi; + dassert((rn->on_wq != 0) && (rn->lock > 0)) ; + + pq->head = rn->wq_next ; + rn->wq_next = NULL ; /* Keep tidy */ + rn->on_wq = 0 ; + + table = rn->table ; + rsclient = table->owner; + afi = table->afi; + safi = table->safi; + + dassert(table->lock > 0) ; + dassert(rsclient->lock > 0) ; /* Best path selection. */ bgp_best_selection (bgp, rn, &old_and_new); @@ -1552,8 +1561,8 @@ bgp_process_rsclient (struct work_queue *wq, work_queue_item item) if (old_select && CHECK_FLAG (old_select->flags, BGP_INFO_REMOVED)) bgp_info_reap (rn, old_select); - bgp_table_unlock (rn->table); bgp_unlock_node (rn); + bgp_table_unlock (table); /* NB: *after* node, in case table is deleted */ bgp_unlock (bgp); if (pq->head == NULL) @@ -1575,6 +1584,7 @@ bgp_process_main (struct work_queue *wq, work_queue_item item) struct bgp_info *old_select; struct bgp_info_pair old_and_new; struct listnode *node, *nnode; + struct bgp_table *table ; struct peer *peer; assert(wq->spec.data == item) ; @@ -1585,11 +1595,18 @@ bgp_process_main (struct work_queue *wq, work_queue_item item) return WQ_SUCCESS ; /* hack off queue and prepare to process */ - pq->head = rn->wq_next ; - rn->on_wq = 0 ; - afi = rn->table->afi; - safi = rn->table->safi; + dassert((rn->on_wq != 0) && (rn->lock > 0)) ; + + pq->head = rn->wq_next ; + rn->wq_next = NULL ; /* Keep tidy */ + rn->on_wq = 0 ; + + table = rn->table ; + afi = table->afi; + safi = table->safi; + + dassert(table->lock > 0) ; p = &rn->p ; @@ -1599,7 +1616,7 @@ bgp_process_main (struct work_queue *wq, work_queue_item item) new_select = old_and_new.new; /* Nothing to do. */ - if (old_select && old_select == new_select) + if (old_select && (old_select == new_select)) { if (! CHECK_FLAG (old_select->flags, BGP_INFO_ATTR_CHANGED)) { @@ -1626,19 +1643,17 @@ bgp_process_main (struct work_queue *wq, work_queue_item item) } /* FIB update. */ - if (safi == SAFI_UNICAST && ! bgp->name && - ! bgp_option_check (BGP_OPT_NO_FIB)) + if ((safi == SAFI_UNICAST) && (bgp->name == NULL) && + ! bgp_option_check (BGP_OPT_NO_FIB)) { - if (new_select - && new_select->type == ZEBRA_ROUTE_BGP - && new_select->sub_type == BGP_ROUTE_NORMAL) + if (new_select && (new_select->type == ZEBRA_ROUTE_BGP) + && (new_select->sub_type == BGP_ROUTE_NORMAL)) bgp_zebra_announce (p, new_select, bgp); else { /* Withdraw the route from the kernel. */ - if (old_select - && old_select->type == ZEBRA_ROUTE_BGP - && old_select->sub_type == BGP_ROUTE_NORMAL) + if (old_select && (old_select->type == ZEBRA_ROUTE_BGP) + && (old_select->sub_type == BGP_ROUTE_NORMAL)) bgp_zebra_withdraw (p, old_select); } } @@ -1650,9 +1665,9 @@ bgp_process_main (struct work_queue *wq, work_queue_item item) /* Finish up */ finish: - bgp_table_unlock (rn->table); - bgp_unlock_node (rn); - bgp_unlock (bgp); + bgp_unlock_node (rn) ; + bgp_table_unlock (table) ; /* NB: *after* node, in case table is deleted */ + bgp_unlock (bgp) ; if (pq->head == NULL) return WQ_SUCCESS ; @@ -1660,6 +1675,9 @@ finish: return WQ_REQUEUE ; } +/*------------------------------------------------------------------------------ + * Delete item from work queue + */ static void bgp_processq_del (struct work_queue *wq, work_queue_item item) { @@ -1670,17 +1688,29 @@ bgp_processq_del (struct work_queue *wq, work_queue_item item) while ((rn = pq->head) != NULL) { - pq->head = rn->wq_next ; - rn->on_wq = 0 ; + struct bgp_table *table ; + + dassert((rn->on_wq != 0) && (rn->lock > 0)) ; + + pq->head = rn->wq_next ; + rn->wq_next = NULL ; /* Keep tidy */ + rn->on_wq = 0 ; + + table = rn->table ; + + dassert(table->lock > 0) ; - bgp_table_unlock (rn->table); bgp_unlock_node (rn); + bgp_table_unlock (table); /* NB: *after* node, in case table is deleted */ bgp_unlock (pq->bgp); } ; wq->spec.data = NULL ; } ; +/*------------------------------------------------------------------------------ + * Create new work queue for given bgp instance and given type of table + */ static work_queue bgp_process_queue_init (struct bgp* bgp, bgp_table_t type) { @@ -1718,6 +1748,10 @@ bgp_process_queue_init (struct bgp* bgp, bgp_table_t type) return *p_wq = wq ; } +/*------------------------------------------------------------------------------ + * Place given route node on appropriate work queue, so that best path + * selection etc. can take place later. + */ void bgp_process (struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi) { @@ -1762,9 +1796,9 @@ bgp_process (struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi) pq = work_queue_item_args(item) ; /* all unlocked when processed or deleted */ + bgp_lock (bgp); bgp_table_lock (rn->table); bgp_lock_node (rn); - bgp_lock (bgp); /* add to the queue */ if (pq->head == NULL) @@ -1780,6 +1814,11 @@ bgp_process (struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi) return; } +/*============================================================================*/ + +/*------------------------------------------------------------------------------ + * Max Prefix Overflow timer expired -- turn off overflow status and enable. + */ static int bgp_maximum_prefix_restart_timer (struct thread *thread) { @@ -1788,15 +1827,42 @@ bgp_maximum_prefix_restart_timer (struct thread *thread) peer = THREAD_ARG (thread); peer->t_pmax_restart = NULL; + assert(CHECK_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW)) ; + if (BGP_DEBUG (events, EVENTS)) zlog_debug ("%s Maximum-prefix restart timer expired, restore peering", peer->host); - peer_clear (peer); + UNSET_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW); + + bgp_peer_enable(peer); return 0; } +/*------------------------------------------------------------------------------ + * If there is an active max prefix restart timer, cancel it now. + * + * NB: clears PEER_STATUS_PREFIX_OVERFLOW, but does NOT enable the peer. + */ +void +bgp_maximum_prefix_cancel_timer (struct peer *peer) +{ + if (peer->t_pmax_restart) + { + assert(CHECK_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW)) ; + + BGP_TIMER_OFF (peer->t_pmax_restart); + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s Maximum-prefix restart timer cancelled", peer->host) ; + } ; + + UNSET_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW) ; +} ; + +/*------------------------------------------------------------------------------ + * Number of prefixes has overflowed. + */ int bgp_maximum_prefix_overflow (struct peer *peer, afi_t afi, safi_t safi, int always) @@ -1814,10 +1880,11 @@ bgp_maximum_prefix_overflow (struct peer *peer, afi_t afi, "%%MAXPFXEXCEED: No. of %s prefix received from %s %ld exceed, " "limit %ld", afi_safi_print (afi, safi), peer->host, peer->pcount[afi][safi], peer->pmax[afi][safi]); + SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_LIMIT); if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING)) - return 0; + return 0; { u_int8_t ndata[7]; @@ -1835,8 +1902,8 @@ bgp_maximum_prefix_overflow (struct peer *peer, afi_t afi, SET_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW); /* Disable the peer, the timer routine will reenable. */ - bgp_notify_send_with_data(peer, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_MAX_PREFIX, ndata, 7); + bgp_peer_down_error_with_data(peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_MAX_PREFIX, ndata, 7); } /* restart timer start */ @@ -1874,7 +1941,10 @@ bgp_maximum_prefix_overflow (struct peer *peer, afi_t afi, return 0; } -/* Unconditionally remove the route from the RIB, without taking +/*============================================================================*/ + +/*------------------------------------------------------------------------------ + * Unconditionally remove the route from the RIB, without taking * damping into consideration (eg, because the session went down) */ static void @@ -2671,7 +2741,7 @@ bgp_announce_route (struct peer *peer, afi_t afi, safi_t safi) struct bgp_node *rn; struct bgp_table *table; - if (peer->state != bgp_peer_sEstablished) + if (peer->state != bgp_peer_pEstablished) return; if (! peer->afc_nego[afi][safi]) @@ -2773,7 +2843,7 @@ bgp_soft_reconfig_in (struct peer *peer, afi_t afi, safi_t safi) struct bgp_node *rn; struct bgp_table *table; - if (peer->state != bgp_peer_sEstablished) + if (peer->state != bgp_peer_pEstablished) return; if (safi != SAFI_MPLS_VPN) @@ -2790,14 +2860,18 @@ bgp_soft_reconfig_in (struct peer *peer, afi_t afi, safi_t safi) * * There are two (quite different) forms of clearing: * - * 1. Normal clearing -- mass withdraw of given client's routes for all + * 1. Normal clearing -- mass withdraw of given peer's routes for all * or individual AFI/SAFI. * + * This is clears the routes *from* the given peer. + * * Note that normal clearing deals with the main RIB and any RS Client * RIBs that may also contain routes. * * 2. RS Client clearing -- dismantling of RS Client RIB for an AFI/SAFI. * + * This clears out the routes *for* the given RS Client. + * *------------------------------------------------------------------------------ * Normal clearing * @@ -2834,9 +2908,20 @@ bgp_soft_reconfig_in (struct peer *peer, afi_t afi, safi_t safi) * The peer's: * * struct bgp_info* routes_head[AFI_MAX][SAFI_MAX] ; + * + * This list threads through every use of all routes which belong to + * the peer, in all RIBs. + * * struct bgp_adj_in* adj_in_head[AFI_MAX][SAFI_MAX] ; + * + * This list threads through every copy of all routes which belong to the + * peer and which have been preserved for soft reconfiguration, in all RIBs. + * * struct bgp_adj_out* adj_out_head[AFI_MAX][SAFI_MAX] ; * + * This list threads through every route which has been selected for the + * peer, in all RIBs. + * * Are maintained for exactly this purpose. * * NB: this is now a linear process, because the lists identify the stuff to @@ -2860,6 +2945,7 @@ bgp_soft_reconfig_in (struct peer *peer, afi_t afi, safi_t safi) * * [The MPLS VPN stuff has a two level RIB, which the above probably doesn't * work for... more work required, here.] + * * TODO: fix bgp_clear_route() and MPLS VPN !! * *------------------------------------------------------------------------------ @@ -2873,28 +2959,226 @@ bgp_soft_reconfig_in (struct peer *peer, afi_t afi, safi_t safi) */ /*------------------------------------------------------------------------------ - * Clear given route -- respecting NSF for peer whose route this is. + * Normal clearing of a a given peer's routes. + * + * The following lists are processed: + * + * * struct bgp_info* routes_head + * + * Walks this and clears each route. + * + * * struct bgp_adj_in* adj_in_head + * * struct bgp_adj_out* adj_out_head + * + * These two are simply emptied out. + * + * NB: in the latest scheme of things this is completed immediately... + * + * ...however, retain the ability for this to kick off background or other + * activity. + * + * Returns: true <=> clearing has completed * - * Will mark the bgp_info as stale, or will remove altogether. If removes the - * route will set the bgp_node to be reprocessed. */ -static void -bgp_clear_this_route(bgp_peer peer, struct bgp_node* rn, struct bgp_info* ri, - afi_t afi, safi_t safi) -{ - assert (rn && peer && ri) ; - assert ((rn == ri->rn) && (peer == ri->peer)) ; - - /* graceful restart STALE flag set. */ - if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT) - && peer->nsf[afi][safi] - && ! CHECK_FLAG (ri->flags, BGP_INFO_STALE) - && ! CHECK_FLAG (ri->flags, BGP_INFO_UNUSEABLE)) - bgp_info_set_flag (rn, ri, BGP_INFO_STALE); - else - bgp_rib_remove (rn, ri, peer, afi, safi); +extern bool +bgp_clear_routes(struct peer *peer, afi_t afi, safi_t safi, bool nsf) +{ + struct bgp_info* ri ; + struct bgp_info* next_ri ; + struct bgp_adj_in* adj_in ; + struct bgp_adj_out* adj_out ; + struct bgp_adj_in** adj_in_head ; + struct bgp_adj_out** adj_out_head ; + + next_ri = peer->routes_head[afi][safi] ; + + /* If NSF requested and nsf configured for this afi/safi, do nsf and + * set flag to indicate that at least one afi/safi may have stale routes. + */ + nsf = nsf && peer->nsf[afi][safi] ; + if (nsf) + SET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT) ; + + /* TODO: fix bgp_clear_route_normal() so can clear an MPLS VPN table.... */ + if (next_ri != NULL) + assert(safi != SAFI_MPLS_VPN) ; + + while (next_ri != NULL) + { + /* The current bgp_info object may vanish, so bank the next */ + ri = next_ri ; + next_ri = ri->routes_next ; + + assert (peer == ri->peer) ; + + if (nsf && ! CHECK_FLAG (ri->flags, BGP_INFO_STALE) + && ! CHECK_FLAG (ri->flags, BGP_INFO_UNUSEABLE)) + bgp_info_set_flag (ri->rn, ri, BGP_INFO_STALE); + else + bgp_rib_remove (ri->rn, ri, peer, afi, safi); + } ; + + /* Empty out all adjacencies */ + adj_in_head = &(peer->adj_in_head[afi][safi]) ; + while ((adj_in = *adj_in_head) != NULL) + { + assert(adj_in->route_prev == NULL) ; + bgp_adj_in_remove (adj_in->rn, adj_in) ; + assert(adj_in != *adj_in_head) ; + } ; + + adj_out_head = &(peer->adj_out_head[afi][safi]) ; + while ((adj_out = *adj_out_head) != NULL) + { + assert(adj_out->route_prev == NULL) ; + bgp_adj_out_remove (adj_out->rn, adj_out, peer, afi, safi) ; + assert(adj_out != *adj_out_head) ; + } ; + + return true ; +} ; + +/*------------------------------------------------------------------------------ + * Normal clearing of given peer for all AFI/SAFI -- respecting NSF if required. + * + * NB: in the latest scheme of things this is completed immediately... + * + * ...however, retain the ability to run this in the background with the + * peer in bgp_peer_pClearing. + * + * Returns: true <=> all clearing completed + * so false => something running in the background. + */ +extern bool +bgp_clear_all_routes (struct peer *peer, bool nsf) +{ + bool completed ; + afi_t afi; + safi_t safi; + + assert(peer->state == bgp_peer_pClearing) ; + + UNSET_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT) ; + + completed = true ; + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + if (!bgp_clear_routes(peer, afi, safi, nsf)) + completed = false ; + + return completed ; +} ; + +/*------------------------------------------------------------------------------ + * Clear Route Server RIB for given AFI/SAFI -- unconditionally + * + * This is used to dismantle a Route Server Client's RIB -- this is removing + * all the routes from all *other* Route Server Clients that have been placed + * in this Clients RIB. + * + * Walks all the nodes in the table and discards all routes, all adj_in and + * all adj_out. + * + * Does nothing if there is no RIB for that AFI/SAFI. + */ +extern void +bgp_clear_rsclient_rib(struct peer* rsclient, afi_t afi, safi_t safi) +{ + struct bgp_node *rn ; + struct bgp_table* table ; + + table = rsclient->rib[afi][safi] ; + + if (table == NULL) + return ; /* Ignore unconfigured afi/safi or similar */ + + /* TODO: fix bgp_clear_rsclient_rib() so that will clear an MPLS VPN table. */ + passert(table->safi != SAFI_MPLS_VPN) ; + + for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) + { + struct bgp_info *ri; + struct bgp_info *next_ri ; + struct bgp_adj_in *ain; + struct bgp_adj_out *aout; + + next_ri = rn->info ; + while(next_ri != NULL) + { + ri = next_ri ; + next_ri = ri->info_next ; /* bank this */ + + bgp_rib_remove (rn, ri, rsclient, table->afi, table->safi); + } ; + + while ((ain = rn->adj_in) != NULL) + { + assert(ain->adj_prev == NULL) ; + bgp_adj_in_remove (rn, ain); + assert(ain != rn->adj_in) ; + } ; + + while ((aout = rn->adj_out) != NULL) + { + assert(aout->adj_prev == NULL) ; + bgp_adj_out_remove (rn, aout, aout->peer, table->afi, table->safi) ; + assert(aout != rn->adj_out) ; + } ; + } + return ; +} + +/*------------------------------------------------------------------------------ + * Walk main RIB and remove any adj_in for given peer. + * + * TODO: walk peer->bgp_adj_in_head[afi][safi] -- but check which table ? + */ +void +bgp_clear_adj_in (struct peer *peer, afi_t afi, safi_t safi) +{ + struct bgp_table *table; + struct bgp_node *rn; + struct bgp_adj_in *ain; + + table = peer->bgp->rib[afi][safi]; + + for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) + for (ain = rn->adj_in; ain ; ain = ain->adj_next) + if (ain->peer == peer) + { + bgp_adj_in_remove (rn, ain); + break; + } } ; +/*------------------------------------------------------------------------------ + * Walk main RIB and remove all stale routes for the given peer. + * + * NB: is required to complete immediately ! + * + * TODO: walk peer->routes_head[afi][safi] + */ +void +bgp_clear_stale_route (struct peer *peer, afi_t afi, safi_t safi) +{ + struct bgp_node *rn; + struct bgp_info *ri; + struct bgp_table *table; + + table = peer->bgp->rib[afi][safi]; + + for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) + { + for (ri = rn->info; ri; ri = ri->info_next) + if (ri->peer == peer) + { + if (CHECK_FLAG (ri->flags, BGP_INFO_STALE)) + bgp_rib_remove (rn, ri, peer, afi, safi); + break; + } + } +} + #if 0 struct bgp_clear_node_queue @@ -2952,14 +3236,14 @@ bgp_clear_node_complete (struct work_queue *wq) /* Flush the event queue and ensure the peer is shut down */ bgp_peer_stop(peer); BGP_EVENT_FLUSH (peer); - if (peer->state == bgp_peer_sClearing) + if (peer->state == bgp_peer_pClearing) { - peer_change_status (peer, bgp_peer_sIdle); + peer_change_status (peer, bgp_peer_pIdle); /* enable peer if required */ bgp_peer_enable(peer); } - peer_unlock (peer); /* bgp_clear_route */ + bgp_peer_unlock (peer); /* bgp_clear_route */ } static void @@ -2985,120 +3269,6 @@ bgp_clear_node_queue_init (struct peer *peer) peer->clear_node_queue->spec.data = peer; } -#endif - -/*------------------------------------------------------------------------------ - * Completely empty the given table which belongs to the given peer. - * - * Used for RS Client RIB clearing. - * - * Walks the table, *unconditionally* deleting all routes. - * - * Deletes any and all adj_in and adj_out. - * - * TODO: fix bgp_clear_route_table() so that will clear an MPLS VPN table.... - */ -static void -bgp_clear_route_table (bgp_peer peer, struct bgp_table* table) -{ - struct bgp_node *rn ; - - if (table == NULL) - return ; /* Ignore unconfigured afi/safi or similar */ - - passert(table->safi != SAFI_MPLS_VPN) ; - - for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) - { - struct bgp_info *ri; - struct bgp_info *next_ri ; - struct bgp_adj_in *ain; - struct bgp_adj_out *aout; - - next_ri = rn->info ; - while(next_ri != NULL) - { - ri = next_ri ; - next_ri = ri->info_next ; /* bank this */ - - bgp_rib_remove (rn, ri, peer, table->afi, table->safi); - } ; - - while ((ain = rn->adj_in) != NULL) - { - assert(ain->adj_prev == NULL) ; - bgp_adj_in_remove (rn, ain); - assert(ain != rn->adj_in) ; - } ; - - while ((aout = rn->adj_out) != NULL) - { - assert(aout->adj_prev == NULL) ; - bgp_adj_out_remove (rn, aout, aout->peer, table->afi, table->safi) ; - assert(aout != rn->adj_out) ; - } ; - } - return ; -} - -/*------------------------------------------------------------------------------ - * Normal clearing of a a given peer's routes. - * - * The following lists are processed: - * - * * struct bgp_info* routes_head - * - * Walks this and clears each route. - * - * * struct bgp_adj_in* adj_in_head - * * struct bgp_adj_out* adj_out_head - * - * These two are simply emptied out. - * - * TODO: fix bgp_clear_route_normal() so that will clear an MPLS VPN table.... - */ -extern void -bgp_clear_route_normal(struct peer *peer, afi_t afi, safi_t safi) -{ - struct bgp_info* ri ; - struct bgp_info* next_ri ; - struct bgp_adj_in* adj_in ; - struct bgp_adj_out* adj_out ; - struct bgp_adj_in** adj_in_head ; - struct bgp_adj_out** adj_out_head ; - - next_ri = peer->routes_head[afi][safi] ; - - assert((safi != SAFI_MPLS_VPN) || (next_ri == NULL)) ; - - while (next_ri != NULL) - { - /* The current bgp_info object may vanish, so bank the next */ - ri = next_ri ; - next_ri = ri->routes_next ; - - bgp_clear_this_route(peer, ri->rn, ri, afi, safi) ; - } ; - - /* Empty out all adjacencies */ - adj_in_head = &(peer->adj_in_head[afi][safi]) ; - while ((adj_in = *adj_in_head) != NULL) - { - assert(adj_in->route_prev == NULL) ; - bgp_adj_in_remove (adj_in->rn, adj_in) ; - assert(adj_in != *adj_in_head) ; - } ; - - adj_out_head = &(peer->adj_out_head[afi][safi]) ; - while ((adj_out = *adj_out_head) != NULL) - { - assert(adj_out->route_prev == NULL) ; - bgp_adj_out_remove (adj_out->rn, adj_out, peer, afi, safi) ; - assert(adj_out != *adj_out_head) ; - } ; -} ; - -#if 0 void bgp_clear_route (struct peer *peer, afi_t afi, safi_t safi) { @@ -3123,7 +3293,7 @@ bgp_clear_route (struct peer *peer, afi_t afi, safi_t safi) * to grow and grow. */ //if (!peer->clear_node_queue->thread) - peer_lock (peer); /* bgp_clear_node_complete */ + bgp_peer_lock (peer); /* bgp_clear_node_complete */ switch (purpose) { @@ -3181,91 +3351,14 @@ bgp_clear_route (struct peer *peer, afi_t afi, safi_t safi) /* The following was in bgp_clear_node_complete */ - peer_unlock (peer); /* bgp_clear_route */ + bgp_peer_unlock (peer); /* bgp_clear_route */ } #endif -/*------------------------------------------------------------------------------ - * Clear Route Server RIB for given AFI/SAFI -- unconditionally - * - * Does nothing if there is no RIB for that AFI/SAFI. - */ -extern void -bgp_clear_route_rsclient(struct peer* rsclient, afi_t afi, safi_t safi) -{ - bgp_clear_route_table (rsclient, rsclient->rib[afi][safi]) ; -} ; - -/*------------------------------------------------------------------------------ - * Normal clearing of given peer for all AFI/SAFI -- respecting NSF. - * - * NB: in the latest scheme of things this is completed immediately... - * - * ...however, retain the ability to run this in the background with the - * peer in bgp_peer_sClearing. - * - * Caller should set state of peer *before* calling this. - */ -extern void -bgp_clear_route_all (struct peer *peer) -{ - afi_t afi; - safi_t safi; - - for (afi = AFI_IP; afi < AFI_MAX; afi++) - for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) - bgp_clear_route_normal(peer, afi, safi); - - bgp_peer_clearing_completed(peer) ; -} ; - -/*------------------------------------------------------------------------------ - * Walk main RIB and remove all adj_in for given peer. - */ -void -bgp_clear_adj_in (struct peer *peer, afi_t afi, safi_t safi) -{ - struct bgp_table *table; - struct bgp_node *rn; - struct bgp_adj_in *ain; - - table = peer->bgp->rib[afi][safi]; - - for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) - for (ain = rn->adj_in; ain ; ain = ain->adj_next) - if (ain->peer == peer) - { - bgp_adj_in_remove (rn, ain); - break; - } -} - -/*------------------------------------------------------------------------------ - * Walk main RIB and remove all stale routes for the given peer. - */ -void -bgp_clear_stale_route (struct peer *peer, afi_t afi, safi_t safi) -{ - struct bgp_node *rn; - struct bgp_info *ri; - struct bgp_table *table; - - table = peer->bgp->rib[afi][safi]; - - for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) - { - for (ri = rn->info; ri; ri = ri->info_next) - if (ri->peer == peer) - { - if (CHECK_FLAG (ri->flags, BGP_INFO_STALE)) - bgp_rib_remove (rn, ri, peer, afi, safi); - break; - } - } -} /*============================================================================*/ -/* Delete all kernel routes. */ +#if 0 +/* Delete all kernel routes. */ void bgp_cleanup_routes (void) { @@ -3296,6 +3389,7 @@ bgp_cleanup_routes (void) bgp_zebra_withdraw (&rn->p, ri); } } +#endif void bgp_reset (void) @@ -3317,7 +3411,7 @@ bgp_nlri_parse (struct peer *peer, struct attr *attr, struct bgp_nlri *packet) int ret; /* Check peer status. */ - if (peer->state != bgp_peer_sEstablished) + if (peer->state != bgp_peer_pEstablished) return 0; pnt = packet->nlri; @@ -3432,8 +3526,8 @@ bgp_nlri_sanity_check (struct peer *peer, int afi, u_char *pnt, plog_err (peer->log, "%s [Error] Update packet error (wrong prefix length %d)", peer->host, prefixlen); - bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_INVAL_NETWORK); + bgp_peer_down_error(peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_INVAL_NETWORK); return -1; } @@ -3446,8 +3540,8 @@ bgp_nlri_sanity_check (struct peer *peer, int afi, u_char *pnt, "%s [Error] Update packet error" " (prefix data overflow prefix size is %d)", peer->host, psize); - bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_INVAL_NETWORK); + bgp_peer_down_error(peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_INVAL_NETWORK); return -1; } @@ -3461,8 +3555,8 @@ bgp_nlri_sanity_check (struct peer *peer, int afi, u_char *pnt, "%s [Error] Update packet error" " (prefix length mismatch with total length)", peer->host); - bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_INVAL_NETWORK); + bgp_peer_down_error(peer, BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_INVAL_NETWORK); return -1; } return 0; diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 97ee39ab..e1e8f18d 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -174,7 +174,7 @@ enum bgp_clear_route_type /* Prototypes. */ extern void bgp_route_init (void); extern void bgp_route_finish (void); -extern void bgp_cleanup_routes (void); + extern void bgp_announce_route (struct peer *, afi_t, safi_t); extern void bgp_announce_route_all (struct peer *); extern void bgp_default_originate (struct peer *, afi_t, safi_t, int); @@ -182,10 +182,11 @@ extern void bgp_soft_reconfig_in (struct peer *, afi_t, safi_t); extern void bgp_soft_reconfig_rsclient (struct peer *, afi_t, safi_t); extern void bgp_check_local_routes_rsclient (struct peer *rsclient, afi_t afi, safi_t safi); -extern void bgp_clear_route_normal(struct peer *peer, afi_t afi, safi_t safi) ; -extern void bgp_clear_route_rsclient(struct peer* rsclient, +extern bool bgp_clear_routes(struct peer *peer, afi_t afi, safi_t safi, + bool nsf) ; +extern void bgp_clear_rsclient_rib(struct peer* rsclient, afi_t afi, safi_t safi) ; -extern void bgp_clear_route_all (struct peer *); +extern bool bgp_clear_all_routes (struct peer *, bool nsf); extern void bgp_clear_adj_in (struct peer *, afi_t, safi_t); extern void bgp_clear_stale_route (struct peer *, afi_t, safi_t); @@ -201,6 +202,7 @@ extern int bgp_nlri_sanity_check (struct peer *, int, u_char *, bgp_size_t); extern int bgp_nlri_parse (struct peer *, struct attr *, struct bgp_nlri *); extern int bgp_maximum_prefix_overflow (struct peer *, afi_t, safi_t, int); +extern void bgp_maximum_prefix_cancel_timer (struct peer *peer) ; extern void bgp_redistribute_add (struct prefix *, struct in_addr *, u_int32_t, u_char); diff --git a/bgpd/bgp_session.c b/bgpd/bgp_session.c index 8d8bfea2..9de8678a 100644 --- a/bgpd/bgp_session.c +++ b/bgpd/bgp_session.c @@ -40,7 +40,6 @@ #include "lib/zassert.h" /* prototypes */ -static int bgp_session_defer_if_limping(bgp_session session); static void bgp_session_do_enable(mqueue_block mqb, mqb_flag_t flag) ; static void bgp_session_do_update_recv(mqueue_block mqb, mqb_flag_t flag); static void bgp_session_do_update_send(mqueue_block mqb, mqb_flag_t flag); @@ -99,7 +98,7 @@ static void bgp_session_do_route_refresh_recv(mqueue_block mqb, mqb_flag_t flag) */ /*------------------------------------------------------------------------------ - * Initialise new session structure -- allocate if required. + * Allocate & initialise new session structure. * * Ties peer and session together. Sets session sIdle, initialises mutex. * @@ -108,30 +107,34 @@ static void bgp_session_do_route_refresh_recv(mqueue_block mqb, mqb_flag_t flag) * NB: if not allocating, the existing session MUST be sIdle/sDisabled OR never * been kissed. * - * NB: in any event, the peer's peer index entry MUST have a NULL accept - * pointer. + * NB: peer MUST NOT have a session set up: + * + * (a) because if there was a session, there would have to be code here + * to worry about its state, and tearing it down etc. + * + * (b) so that do not have to worry about BGP Engine reaching the old + * session while it was being replaced or whatever. */ extern bgp_session -bgp_session_init_new(bgp_session session, bgp_peer peer) +bgp_session_init_new(bgp_peer peer) { + bgp_session session ; + assert(peer->session == NULL) ; - assert(peer->index_entry->accept == NULL) ; - if (session == NULL) - session = XCALLOC(MTYPE_BGP_SESSION, sizeof(struct bgp_session)) ; - else - memset(session, 0, sizeof(struct bgp_session)) ; + session = XCALLOC(MTYPE_BGP_SESSION, sizeof(struct bgp_session)) ; qpt_mutex_init_new(&session->mutex, qpt_mutex_recursive) ; - peer->session = session ; session->peer = peer ; - session->state = bgp_session_sIdle ; + bgp_peer_lock(peer) ; /* Account for the session->peer pointer */ - session->index_entry = peer->index_entry ; + session->state = bgp_session_sIdle ; /* Zeroising the structure has set: * + * delete_me -- 0 -- false + * * event -- bgp_session_null_event * notification -- NULL -- none * err -- 0 -- none @@ -173,25 +176,73 @@ bgp_session_init_new(bgp_session session, bgp_peer peer) * * connections[] -- NULL -- none * active -- false, not yet active + * accept -- false, not yet ready to accept() */ confirm(bgp_session_null_event == 0) ; + /* Once the session is fully initialised, can set peer->session pointer. + * + * NB: this is done last and under the Peer Index Mutex, so that the + * accept() code does not trip over. + */ + bgp_peer_index_set_session(peer, session) ; + return session ; } ; -/* Free session structure +/*============================================================================== + * Routing Engine: delete session for given peer. + * + * This is for use when the peer itself is being deleted. (Peer MUST be in + * pDeleting state.) + * + * Does nothing if there is no session ! + * + * If the session is active, simply sets delete_me flag, which will be honoured + * when the session goes dDisabled. Note, it is the callers responsibility + * to arrange for that to happen. + * + * If the session is not active, it is immediately freed. * + * NB: if the session is freed, the peer may vanish at the same time ! */ -extern bgp_session -bgp_session_free(bgp_session session) +extern void +bgp_session_delete(bgp_peer peer) { + bgp_session session = peer->session ; + if (session == NULL) - return NULL; + return ; /* easy if no session anyway */ + + assert(peer == session->peer) ; + + /* If is active, set flag so that session is deleted when next it becomes + * sDisabled. + */ + if (bgp_session_is_active(session)) + { + session->delete_me = true ; + return ; + } ; + + /*--------------------------------------------------------------------------*/ + /* Proceed to free the session structure. */ + + /* Make sure that the BGP Engine has, in fact, let go of the session. + * + * The LOCK/UNLOCK makes sure that the BGP Engine has unlocked the session. + * + * Without this, the qpt_mutex_destroy() can fail horribly, if the BGP + * Engine sends the disable acknowledge before finally unlocking the session. + */ + BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - assert(!bgp_session_is_active(session)) ; + BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ qpt_mutex_destroy(&session->mutex, 0) ; + /* Proceed to dismantle the session. */ + bgp_notify_unset(&session->notification); bgp_open_state_free(session->open_send); bgp_open_state_free(session->open_recv); @@ -206,12 +257,24 @@ bgp_session_free(bgp_session session) sockunion_unset(&session->su_local) ; sockunion_unset(&session->su_remote) ; + /* Drop the peer->session and session->peer pointers + * + * NB: the peer->session pointer is cleared under the Peer Index Mutex, + * so that the accept() code does not trip over. + * + * NB: at this point it is possible for the peer structure to suddenly + * vanish -- if peer has been deleted, and has been waiting for the + * session to go sDisabled. + */ + bgp_peer_index_set_session(peer, NULL) ; + + session->peer = NULL ; + bgp_peer_unlock(peer) ; /* NB: peer->session == NULL */ + /* Zeroize to catch dangling references asap */ memset(session, 0, sizeof(struct bgp_session)) ; XFREE(MTYPE_BGP_SESSION, session); - - return NULL; -} +} ; /*============================================================================== * Routing Engine: enable session for given peer -- allocate if required. @@ -226,6 +289,8 @@ bgp_session_enable(bgp_peer peer) bgp_session session ; mqueue_block mqb ; + assert(peer->state = bgp_peer_pIdle) ; + /* Set up session if required. Check session if already exists. * * Only the Routing Engine creates sessions, so it is safe to pick up the @@ -238,27 +303,24 @@ bgp_session_enable(bgp_peer peer) session = peer->session ; if (session == NULL) - session = bgp_session_init_new(NULL, peer) ; + session = bgp_session_init_new(peer) ; else { assert(session->peer == peer) ; - /* if session is limping then defer the enable */ - if (bgp_session_defer_if_limping(session)) - return; assert(!bgp_session_is_active(session)) ; } ; /* Initialise what we need to make and run connections */ - session->state = bgp_session_sIdle; - session->defer_enable = 0; - session->flow_control = 0; - session->event = bgp_session_null_event; - bgp_notify_unset(&session->notification); - session->err = 0; - session->ordinal = 0; + session->state = bgp_session_sIdle ; + session->delete_me = false ; + session->flow_control = 0 ; + session->event = bgp_session_null_event ; + bgp_notify_unset(&session->notification) ; + session->err = 0 ; + session->ordinal = 0 ; - session->open_send = bgp_peer_open_state_init_new(session->open_send, peer); - bgp_open_state_unset(&session->open_recv); + session->open_send = bgp_peer_open_state_init_new(session->open_send, peer) ; + bgp_open_state_unset(&session->open_recv) ; session->connect = (peer->flags & PEER_FLAG_PASSIVE) == 0 ; session->listen = 1 ; @@ -306,12 +368,22 @@ bgp_session_enable(bgp_peer peer) session->hold_timer_interval = peer->v_holdtime ; session->keepalive_timer_interval = peer->v_keepalive ; - session->as4 = 0; - session->route_refresh_pre = 0; + session->as4 = false ; + session->route_refresh_pre = false ; + session->orf_prefix_pre = false ; /* su_local set when session Established */ /* su_remote set when session Established */ + /* TODO: check whether session stats should persist */ + memset(&session->stats, 0, sizeof(struct bgp_session_stats)) ; + + memset(&session->connections, 0, + sizeof(bgp_connection) * bgp_connection_count) ; + + session->active = false ; + session->accept = false ; + /* Routeing Engine does the state change now. */ /* Now pass the session to the BGP Engine, which will set about */ @@ -340,7 +412,7 @@ bgp_session_do_enable(mqueue_block mqb, mqb_flag_t flag) BGP_SESSION_LOCK(session) ; /*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/ - session->active = 1 ; + session->active = true ; bgp_fsm_enable_session(session) ; BGP_SESSION_UNLOCK(session) ; /*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/ @@ -352,6 +424,8 @@ bgp_session_do_enable(mqueue_block mqb, mqb_flag_t flag) /*============================================================================== * Routing Engine: disable session for given peer -- if enabled (!). * + * Does nothing if the session is not sEnabled or sEstablished. + * * Passes any bgp_notify to the BGP Engine, which will dispose of it in due * course. * @@ -390,7 +464,6 @@ bgp_session_disable(bgp_peer peer, bgp_notify notification) /* Now change to limping state */ session->state = bgp_session_sLimping; - session->defer_enable = 0; /* Ask the BGP engine to disable the session. * @@ -470,7 +543,10 @@ bgp_session_event(bgp_session session, bgp_session_event_t event, mqueue_block mqb ; if (stopped) - session->active = 0 ; /* ignore updates etc */ + { + session->active = false ; /* ignore updates etc */ + session->accept = false ; /* for completeness */ + } ; mqb = mqb_init_new(NULL, bgp_session_do_event, session) ; @@ -774,6 +850,8 @@ bgp_session_update_recv(bgp_session session, struct stream* buf, bgp_size_t size /*------------------------------------------------------------------------------ * Routing Engine: process incoming update message -- mqb action function. + * + * Discard the update if the session is not sEstablished. */ static void bgp_session_do_update_recv(mqueue_block mqb, mqb_flag_t flag) @@ -781,10 +859,9 @@ bgp_session_do_update_recv(mqueue_block mqb, mqb_flag_t flag) bgp_session session = mqb_get_arg0(mqb) ; struct bgp_session_update_args* args = mqb_get_args(mqb) ; - if ((flag == mqb_action) && (session->state == bgp_session_sEstablished)) + if ( (flag == mqb_action) && (session->state == bgp_session_sEstablished) ) { - bgp_peer peer = session->peer; - + bgp_peer peer = session->peer; stream_free(peer->ibuf); peer->ibuf = args->buf; bgp_update_receive (peer, args->size); @@ -826,8 +903,8 @@ bgp_session_do_route_refresh_recv(mqueue_block mqb, mqb_flag_t flag) struct bgp_session_route_refresh_args* args = mqb_get_args(mqb) ; bgp_session session = mqb_get_arg0(mqb) ; - if ((flag == mqb_action) && (session->state == bgp_session_sEstablished)) - bgp_route_refresh_recv(session->peer, args->rr); + if ( (flag == mqb_action) && (session->state == bgp_session_sEstablished) ) + bgp_route_refresh_recv(session->peer, args->rr) ; bgp_route_refresh_free(args->rr); mqb_free(mqb); @@ -860,9 +937,10 @@ bgp_session_do_XON(mqueue_block mqb, mqb_flag_t flag) { bgp_session session = mqb_get_arg0(mqb) ; - if ((flag == mqb_action) && (session->state == bgp_session_sEstablished)) + if ( (flag == mqb_action) && (session->state == bgp_session_sEstablished) ) { int xoff = (session->flow_control <= 0); + session->flow_control = BGP_XON_REFRESH; if (xoff) bgp_write (session->peer, NULL) ; @@ -923,59 +1001,46 @@ bgp_session_do_set_ttl(mqueue_block mqb, mqb_flag_t flag) */ /*------------------------------------------------------------------------------ - * See if session exists and is active. + * Routing Engine: see if session exists and is active. * - * Ensure that if exists and is not active, that the peer index entry accept - * 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 ! + * If exists then performs a few checks, just to make sure things are straight. * - * NB: accessing Routing Engine "private" variable -- no lock required. + * NB: accessing Routing Engine "private" variable -- no lock required. * - * accessing index_entry when not active -- no lock required. + * checks session->active, only when not active -- no lock required. */ -extern int +extern bool bgp_session_is_active(bgp_session session) { - int active ; + bool active ; if (session == NULL) - active = 0 ; + active = false ; else { - active = ( (session->state == bgp_session_sEnabled) - || (session->state == bgp_session_sEstablished) - || (session->state == bgp_session_sLimping) ) ; - - if (!active) - assert(session->index_entry->accept == NULL) ; + switch (session->state) + { + case bgp_session_sIdle: + case bgp_session_sDisabled: + assert(!session->active) ; + active = false ; + break ; + + case bgp_session_sEnabled: + case bgp_session_sEstablished: + case bgp_session_sLimping: + active = true ; + break ; + + default: + zabort("invalid session->state") ; + } ; } ; return active ; } ; /*------------------------------------------------------------------------------ - * 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 Routing Engine "private" variable -- no lock required. - */ -static int -bgp_session_defer_if_limping(bgp_session session) -{ - int defer_enable = 0 ; - - if (session == NULL) - defer_enable = 0 ; - else - defer_enable = (session->state == bgp_session_sLimping) ; - - return session->defer_enable = defer_enable ; -} ; - -/*------------------------------------------------------------------------------ * Get a copy of the session statistics, copied all at once so * forms a consistent snapshot */ diff --git a/bgpd/bgp_session.h b/bgpd/bgp_session.h index 6f449bec..79b6f1a4 100644 --- a/bgpd/bgp_session.h +++ b/bgpd/bgp_session.h @@ -89,23 +89,22 @@ struct bgp_session_stats struct bgp_session { - /* The following are set when the session is created, and not changed - * thereafter. + /* The following is set when the session is created, and not changed + * thereafter, so do not need to lock the session to access this. */ bgp_peer peer ; /* peer whose session this is */ - bgp_peer_index_entry index_entry ; /* and its index entry */ /* This is a *recursive* mutex */ qpt_mutex_t mutex ; /* for access to the rest */ - /* While sIdle and sStopped: + /* While sIdle and sDisabled -- aka not "active" states: * * the session belongs to the Routing Engine. * * The BGP Engine will not touch a session in these states and the * Routing Engine may do what it likes with it. * - * While sEnabled, sEstablished and sStopping: + * While sEnabled, sEstablished and sLimping -- aka "active" states: * * the session belongs to the BGP Engine. * @@ -119,11 +118,12 @@ struct bgp_session * These are private to the Routing Engine. */ bgp_session_state_t state ; - int defer_enable ; /* set when waiting for stop */ int flow_control ; /* limits number of updates sent by the Routing Engine */ + bool delete_me ; /* when next goes sDisabled */ + /* These are private to the Routing Engine, and are set each time a session * event message is received from the BGP Engine. */ @@ -211,10 +211,15 @@ struct bgp_session * 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 Routing Engine acknowledging that (by disabling the session). + * + * The accept flag is set when the secondary connection is completely ready + * to accept connections. It is cleared otherwise, or when the active flag + * is cleared. */ bgp_connection connections[bgp_connection_count] ; bool active ; + bool accept ; } ; /*============================================================================== @@ -267,7 +272,7 @@ MQB_ARGS_SIZE_OK(bgp_session_end_of_rib_args) ; struct bgp_session_event_args /* to Routeing Engine */ { - bgp_session_event_t event ; + bgp_session_event_t event ; /* what just happened */ bgp_notify notification ; /* sent or received (if any) */ int err ; /* errno if any */ bgp_connection_ord_t ordinal ; /* primary/secondary connection */ @@ -309,10 +314,7 @@ inline static void BGP_SESSION_UNLOCK(bgp_session session) */ extern bgp_session -bgp_session_init_new(bgp_session session, bgp_peer peer) ; - -extern bgp_session -bgp_session_free(bgp_session session); +bgp_session_init_new(bgp_peer peer) ; extern void bgp_session_enable(bgp_peer peer) ; @@ -321,6 +323,9 @@ extern void bgp_session_disable(bgp_peer peer, bgp_notify notification) ; extern void +bgp_session_delete(bgp_peer peer); + +extern void bgp_session_event(bgp_session session, bgp_session_event_t event, bgp_notify notification, int err, @@ -358,7 +363,7 @@ bgp_session_get_stats(bgp_session session, struct bgp_session_stats *stats); * Session data access functions. */ -extern int +extern bool bgp_session_is_active(bgp_session session) ; #endif /* QUAGGA_BGP_SESSION_H */ diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c index 91cab606..a2581e3c 100644 --- a/bgpd/bgp_table.c +++ b/bgpd/bgp_table.c @@ -96,6 +96,7 @@ bgp_node_set (struct bgp_table *table, struct prefix *prefix) static void bgp_node_free (struct bgp_node *node) { + node->lock = -54321 ; XFREE (MTYPE_BGP_NODE, node); } @@ -128,11 +129,17 @@ bgp_table_free (struct bgp_table *rt) continue; } + assert( (node->info == NULL) + && (node->adj_out == NULL) + && (node->adj_in == NULL) + && (node->on_wq == 0) ) ; + tmp_node = node; node = node->parent; tmp_node->table->count--; tmp_node->lock = 0; /* to cause assert if unlocked after this */ + bgp_node_free (tmp_node); if (node != NULL) @@ -152,10 +159,11 @@ bgp_table_free (struct bgp_table *rt) if (rt->owner) { - peer_unlock (rt->owner); + bgp_peer_unlock (rt->owner); rt->owner = NULL; } + rt->lock = -54321 ; XFREE (MTYPE_BGP_TABLE, rt); return; } @@ -371,6 +379,7 @@ bgp_node_delete (struct bgp_node *node) assert (node->lock == 0); assert (node->info == NULL); + assert (node->on_wq == 0) ; if (node->l_left && node->l_right) return; diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 1afc9f94..2a5acb05 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -1353,7 +1353,7 @@ DEFUN (no_neighbor, { peer = peer_lookup (vty->index, &su); if (peer) - peer_delete (peer); + bgp_peer_delete (peer); } return CMD_SUCCESS; @@ -1794,7 +1794,7 @@ DEFUN (no_neighbor_dont_capability_negotiate, static int peer_af_flag_modify_vty (struct vty *vty, const char *peer_str, afi_t afi, - safi_t safi, u_int32_t flag, int set) + safi_t safi, u_int32_t flag, bool set) { int ret; struct peer *peer; @@ -1803,10 +1803,7 @@ peer_af_flag_modify_vty (struct vty *vty, const char *peer_str, afi_t afi, if (! peer) return CMD_WARNING; - if (set) - ret = peer_af_flag_set (peer, afi, safi, flag); - else - ret = peer_af_flag_unset (peer, afi, safi, flag); + ret = peer_af_flag_modify(peer, afi, safi, flag, set); return bgp_vty_return (vty, ret); } @@ -1815,14 +1812,14 @@ static int peer_af_flag_set_vty (struct vty *vty, const char *peer_str, afi_t afi, safi_t safi, u_int32_t flag) { - return peer_af_flag_modify_vty (vty, peer_str, afi, safi, flag, 1); + return peer_af_flag_modify_vty (vty, peer_str, afi, safi, flag, true); } static int peer_af_flag_unset_vty (struct vty *vty, const char *peer_str, afi_t afi, safi_t safi, u_int32_t flag) { - return peer_af_flag_modify_vty (vty, peer_str, afi, safi, flag, 0); + return peer_af_flag_modify_vty (vty, peer_str, afi, safi, flag, false); } /* neighbor capability orf prefix-list. */ @@ -2080,7 +2077,7 @@ peer_rsclient_set_vty (struct vty *vty, const char *peer_str, struct listnode *node, *nnode; struct bgp_filter *pfilter; struct bgp_filter *gfilter; - int locked_and_added = 0; + bool was_active ; bgp = vty->index; @@ -2092,29 +2089,22 @@ peer_rsclient_set_vty (struct vty *vty, const char *peer_str, if ( CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) ) return CMD_SUCCESS; - if ( ! peer_rsclient_active (peer) ) - { - peer = peer_lock (peer); /* rsclient peer list reference */ - listnode_add_sort (bgp->rsclient, peer); - locked_and_added = 1; - } + was_active = peer_rsclient_active(peer) ; ret = peer_af_flag_set (peer, afi, safi, PEER_FLAG_RSERVER_CLIENT); if (ret < 0) - { - if (locked_and_added) - { - listnode_delete (bgp->rsclient, peer); - peer_unlock (peer); /* rsclient peer list reference */ - } + return bgp_vty_return (vty, ret); - return bgp_vty_return (vty, ret); - } + if (!was_active) + { + bgp_peer_lock (peer); /* rsclient peer list reference */ + listnode_add_sort (bgp->rsclient, peer); + } ; peer->rib[afi][safi] = bgp_table_init (afi, safi); peer->rib[afi][safi]->type = BGP_TABLE_RSCLIENT; /* RIB peer reference. Released when table is free'd in bgp_table_free. */ - peer->rib[afi][safi]->owner = peer_lock (peer); + peer->rib[afi][safi]->owner = bgp_peer_lock (peer); /* Check for existing 'network' and 'redistribute' routes. */ bgp_check_local_routes_rsclient (peer, afi, safi); @@ -2181,10 +2171,19 @@ peer_rsclient_unset_vty (struct vty *vty, const char *peer_str, if ( ! peer ) return CMD_WARNING; + assert(bgp == peer->bgp) ; + /* If it is not a RS-Client, don't do anything. */ if ( ! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) ) return CMD_SUCCESS; + /* If this is a Peer Group, then need to undo the relevant rsclient state + * for all the group members. + * + * That means clearing the state flag and the pointer to the shared RIB. + * + * TODO: peer_af_flag_unset PEER_FLAG_RSERVER_CLIENT fails for group members ? + */ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) { group = peer->group; @@ -2201,20 +2200,63 @@ peer_rsclient_unset_vty (struct vty *vty, const char *peer_str, peer = group->conf; } + /* Unset the rsclient flag and remove from rsclient list if no longer a + * distinct rsclient. + * + * NB: this takes care of downing the peer, if required. + */ ret = peer_af_flag_unset (peer, afi, safi, PEER_FLAG_RSERVER_CLIENT); if (ret < 0) return bgp_vty_return (vty, ret); - if ( ! peer_rsclient_active (peer) ) + /* Now tidy up the data structures. */ + peer_rsclient_unset(peer, afi, safi, false) ; + + return CMD_SUCCESS; +} + +/* Have unset rsclient state for a peer that was a distinct rsclient. + * + * Tidy up the data structures. + * + * NB: does not down the peer or deal with other consequences. + */ +void +peer_rsclient_unset(struct peer* peer, int afi, int safi, bool keep_export) +{ + assert(peer->rib[afi][safi] != NULL) ; + + /* If the peer is no longer a distinct rsclient, remove from list of same. */ + if (! peer_rsclient_active (peer)) { - bgp_clear_route_rsclient (peer, afi, safi); - listnode_delete (bgp->rsclient, peer); - peer_unlock (peer); /* peer bgp rsclient reference */ + struct listnode *pn; + pn = listnode_lookup (peer->bgp->rsclient, peer) ; + + assert(pn != NULL) ; + + bgp_peer_unlock (peer); /* peer rsclient reference */ + list_delete_node (peer->bgp->rsclient, pn); } - bgp_table_finish (&peer->rib[bgp_node_afi(vty)][bgp_node_safi(vty)]); + /* Discard the rsclient rib */ + bgp_clear_rsclient_rib (peer, afi, safi); + bgp_table_finish (&peer->rib[afi][safi]); - return CMD_SUCCESS; + /* Discard import policy unconditionally */ + 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; + } + + /* Discard export policy unless should be kept. */ + if (peer->filter[afi][safi].map[RMAP_EXPORT].name && !keep_export) + { + 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; + } } /* neighbor route-server-client. */ @@ -6778,7 +6820,7 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi) vty_out (vty, "%8s", peer_uptime (peer->uptime, timebuf, BGP_UPTIME_LEN)); - if (peer->state == bgp_peer_sEstablished) + if (peer->state == bgp_peer_pEstablished) { vty_out (vty, " %8ld", peer->pcount[afi][safi]); } @@ -7315,7 +7357,7 @@ bgp_show_peer (struct vty *vty, struct peer *p) /* Status. */ vty_out (vty, " BGP state = %s", LOOKUP (bgp_peer_status_msg, p->state)); - if (p->state == bgp_peer_sEstablished) + if (p->state == bgp_peer_pEstablished) vty_out (vty, ", up for %8s", peer_uptime (p->uptime, timebuf, BGP_UPTIME_LEN)); /* TODO: what is state "Active" now? sEnabled? */ @@ -7344,7 +7386,7 @@ bgp_show_peer (struct vty *vty, struct peer *p) } /* Capability. */ - if (p->state == bgp_peer_sEstablished) + if (p->state == bgp_peer_pEstablished) { if (p->cap || p->afc_adv[AFI_IP][SAFI_UNICAST] @@ -7466,7 +7508,7 @@ bgp_show_peer (struct vty *vty, struct peer *p) int eor_receive_af_count = 0; vty_out (vty, " Graceful restart informations:%s", VTY_NEWLINE); - if (p->state == bgp_peer_sEstablished) + if (p->state == bgp_peer_pEstablished) { vty_out (vty, " End-of-RIB send: "); for (afi = AFI_IP ; afi < AFI_MAX ; afi++) diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index c0fc3628..195ccaef 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -167,7 +167,7 @@ bgp_interface_down (int command, struct zclient *zclient, zebra_size_t length) continue; if (ifp == peer_if) - bgp_peer_disable(peer, NULL); + bgp_peer_down(peer, PEER_DOWN_INTERFACE_DOWN); } } } diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 1176d2d0..dfcc7ff0 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -80,9 +80,6 @@ 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) @@ -183,13 +180,7 @@ bgp_router_id_set (struct bgp *bgp, struct in_addr *id) 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); - } + bgp_peer_down(peer, PEER_DOWN_RID_CHANGE) ; } return 0; } @@ -214,12 +205,7 @@ bgp_cluster_id_set (struct bgp *bgp, struct in_addr *cluster_id) 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); - } + bgp_peer_down(peer, PEER_DOWN_CLID_CHANGE) ; } return 0; } @@ -242,12 +228,7 @@ bgp_cluster_id_unset (struct bgp *bgp) 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); - } + bgp_peer_down(peer, PEER_DOWN_CLID_CHANGE) ; } return 0; } @@ -267,7 +248,7 @@ int bgp_timers_unset (struct bgp *bgp) { bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE; - bgp->default_holdtime = BGP_DEFAULT_HOLDTIME; + bgp->default_holdtime = BGP_DEFAULT_HOLDTIME; return 0; } @@ -300,23 +281,20 @@ bgp_confederation_id_set (struct bgp *bgp, as_t as) 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); + bgp_peer_down(peer, PEER_DOWN_CONFED_ID_CHANGE) ; } } else { - /* Not doign confederation before, so reset every non-local + /* Not doing 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); + + bgp_peer_down(peer, PEER_DOWN_CONFED_ID_CHANGE) ; } } } @@ -338,9 +316,7 @@ bgp_confederation_id_unset (struct bgp *bgp) 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); + bgp_peer_down(peer, PEER_DOWN_CONFED_ID_CHANGE) ; } } return 0; @@ -396,9 +372,7 @@ bgp_confederation_peers_add (struct bgp *bgp, as_t as) 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); + bgp_peer_down(peer, PEER_DOWN_CONFED_PEER_CHANGE) ; } } } @@ -447,9 +421,7 @@ bgp_confederation_peers_remove (struct bgp *bgp, as_t as) 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); + bgp_peer_down(peer, PEER_DOWN_CONFED_PEER_CHANGE) ; } } } @@ -535,7 +507,7 @@ peer_af_flag_reset (struct peer *peer, afi_t afi, safi_t safi) filter->aslist[i].name = NULL; } } - for (i = RMAP_IN; i < RMAP_MAX; i++) + for (i = RMAP_IN; i < RMAP_MAX; i++) { if (filter->map[i].name) { @@ -664,59 +636,15 @@ peer_sort (struct peer *peer) } } - -/* 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 = bgp_peer_new (bgp); - peer = peer_lock (peer); /* bgp peer list reference */ + peer = bgp_peer_lock (peer); /* bgp peer list reference */ listnode_add_sort (bgp->peer, peer); return peer; @@ -728,13 +656,6 @@ 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; @@ -772,12 +693,16 @@ peer_as_change (struct peer *peer, as_t as) PEER_FLAG_REFLECTOR_CLIENT); } - /* local-as reset */ + /* 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); } + + /* Down peer. */ + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + bgp_peer_down(peer, PEER_DOWN_REMOTE_AS_CHANGE) ; } /* If peer does not exist, create new one. If peer already exists, @@ -836,14 +761,16 @@ peer_remote_as (struct bgp *bgp, union sockunion *su, as_t *as, else local_as = bgp->as; - /* If this is IPv4 unicast configuration and "no bgp default - ipv4-unicast" is specified. */ + /* TODO: report bug... if is IPv4 unicast, may implicitly activate */ - 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); + /* If this is IPv4 unicast configuration and "no bgp default + ipv4-unicast" is NOT specified. + */ + if (!bgp_flag_check (bgp, BGP_FLAG_NO_DEFAULT_IPV4) + && (afi == AFI_IP) && (safi == SAFI_UNICAST)) + peer = bgp_peer_create (su, bgp, local_as, *as, afi, safi); else - peer = peer_create (su, bgp, local_as, *as, afi, safi); + peer = bgp_peer_create (su, bgp, local_as, *as, 0, 0); } return 0; @@ -853,7 +780,7 @@ peer_remote_as (struct bgp *bgp, union sockunion *su, as_t *as, int peer_activate (struct peer *peer, afi_t afi, safi_t safi) { - int active; + bool was_active; if (peer->afc[afi][safi]) return 0; @@ -863,15 +790,19 @@ peer_activate (struct peer *peer, afi_t afi, safi_t safi) peer->afc[afi][safi] = 1; else { - active = peer_active (peer); - + was_active = peer_active (peer); peer->afc[afi][safi] = 1; - if (! active && peer_active (peer)) + /* If wasn't active, can now enable since now is. + * + * Otherwise, to enable an extra AFI/SAFI need either to use Dynamic + * Capabilities or restart the session. + */ + if (! was_active) bgp_peer_enable (peer); else -#if 0 /* TODO: Dynamic capability */ +#if 0 { if (peer->status == Established) { @@ -890,9 +821,7 @@ peer_activate (struct peer *peer, afi_t afi, safi_t safi) else #endif { - peer->last_reset = PEER_DOWN_AF_ACTIVATE; - bgp_notify_send(peer, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_CONFIG_CHANGE); + bgp_peer_down(peer, PEER_DOWN_AF_ACTIVATE) ; } #if 0 } @@ -905,17 +834,19 @@ peer_activate (struct peer *peer, afi_t afi, safi_t safi) int peer_deactivate (struct peer *peer, afi_t afi, safi_t safi) { - struct peer_group *group; - struct peer *peer1; - struct listnode *node, *nnode; + bool was_rsclient ; if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { + struct peer_group *group; + struct listnode *node, *nnode; + struct peer* group_member ; + group = peer->group; - for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1)) + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, group_member)) { - if (peer1->af_group[afi][safi]) + if (group_member->af_group[afi][safi]) return BGP_ERR_PEER_GROUP_MEMBER_EXISTS; } } @@ -928,53 +859,56 @@ peer_deactivate (struct peer *peer, afi_t afi, safi_t safi) if (! peer->afc[afi][safi]) return 0; - /* De-activate the address family configuration. */ + /* If we arrive here, the peer is either: + * + * - a real peer which is not a group member for this afi/safi + * + * - a group which has no members for this afi/safi. + * + * In which case, if this is an rsclient, it is a distinct rsclient, and the + * rsclient RIB should be discarded. + */ + was_rsclient = CHECK_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_RSERVER_CLIENT) ; + + /* De-activate the address family configuration. */ peer->afc[afi][safi] = 0; peer_af_flag_reset (peer, afi, safi); + /* Tidy up if was rsclient */ + if (was_rsclient) + peer_rsclient_unset(peer, afi, safi, false) ; + + /* Deal with knock on effect on real peer */ if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->state == bgp_peer_sEstablished) - { - /* Unless can dynamically reconfigure: once a session is established, - * turning off an address family requires the session to be dropped - * and restarted. - */ - if (CHECK_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV)) - { - peer->afc_adv[afi][safi] = 0; - peer->afc_nego[afi][safi] = 0; + bool down = true ; - if (peer_active_nego (peer)) - { - bgp_capability_send (peer, afi, safi, - CAPABILITY_CODE_MP, - CAPABILITY_ACTION_UNSET); - bgp_clear_route_normal(peer, afi, safi); - 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); - } - } - else if ((peer->state == bgp_peer_sIdle) && !peer_active (peer)) + if ( (peer->state == bgp_peer_pEstablished) + && CHECK_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV) ) { - /* In sIdle, the BGP Engine may be trying to connect... if no address - * family is now active, need now to shut down the session. - */ - bgp_peer_disable(peer, NULL) ; + /* If can dynamically reconfigure can avoid restarting the session. */ + peer->afc_adv[afi][safi] = 0; + peer->afc_nego[afi][safi] = 0; + + if (peer_active_nego (peer)) + { + bool completed ; + bgp_capability_send (peer, afi, safi, CAPABILITY_CODE_MP, + CAPABILITY_ACTION_UNSET); + completed = bgp_clear_routes(peer, afi, safi, false) ; + /* If clearing routes does not complete, what do we do ? */ + passert(completed) ; + peer->pcount[afi][safi] = 0; + + down = false ; /* don't need to down the peer */ + } ; } ; - } + + if (down) + bgp_peer_down(peer, PEER_DOWN_AF_DEACTIVATE) ; + } ; + return 0; } @@ -1038,7 +972,7 @@ peer_group_get (struct bgp *bgp, const char *name) group->bgp = bgp; group->name = strdup (name); group->peer = list_new (); - group->conf = peer_new (bgp); + group->conf = bgp_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); @@ -1133,6 +1067,7 @@ peer_group2peer_config_copy (struct peer_group *group, struct peer *peer, /* 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); @@ -1336,7 +1271,7 @@ peer_group_delete (struct peer_group *group) for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) { peer->group = NULL; - peer_delete (peer); + bgp_peer_delete (peer); } list_delete (group->peer); @@ -1344,7 +1279,7 @@ peer_group_delete (struct peer_group *group) group->name = NULL; group->conf->group = NULL; - peer_delete (group->conf); + bgp_peer_delete (group->conf); /* Delete from all peer_group list. */ listnode_delete (bgp->group, group); @@ -1366,7 +1301,7 @@ peer_group_remote_as_delete (struct peer_group *group) for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) { peer->group = NULL; - peer_delete (peer); + bgp_peer_delete (peer); } list_delete_all_node (group->peer); @@ -1381,7 +1316,7 @@ 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; + bool first_member ; /* Check peer group's address family. */ if (! group->conf->afc[afi][safi]) @@ -1390,63 +1325,94 @@ peer_group_bind (struct bgp *bgp, union sockunion *su, /* Lookup the peer. */ peer = peer_lookup (bgp, su); - /* Create a new peer. */ + /* Create a new peer -- iff group specifies a remote-as. */ 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 = bgp_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 */ + peer = bgp_peer_lock (peer); /* group->peer list reference */ listnode_add (group->peer, peer); peer_group2peer_config_copy (group, peer, afi, safi); - return 0; + return 0 ; /* Done */ } - /* When the peer already belongs to peer group, check the consistency. */ + assert(bgp == peer->bgp) ; + + /* When the peer already belongs to peer group, check the consistency. + * + * If already belong to a group for the current afi/safi, then must be the + * same group -- cannot change group association by this means. + */ if (peer->af_group[afi][safi]) { if (strcmp (peer->group->name, group->name) != 0) return BGP_ERR_PEER_GROUP_CANT_CHANGE; - return 0; + return 0; /* Done */ } - /* Check current peer group configuration. */ + /* Check current peer group configuration. + * + * Can only belong to one group at a time. All afi/safi which are members of + * a group, must be members of the same group. + */ 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 binding to this group would change the sort of peer, then cannot + * do it. + * + * The group itself may not carry a sort of peer ?? Or something ?? + */ + first_member = false ; + if (! group->conf->as) /* If group does NOT specify a remote AS ? */ + { if (peer_sort (group->conf) == BGP_PEER_INTERNAL) - first_member = 1; + first_member = true ; + else + { + if (peer_sort (group->conf) != peer_sort (peer)) + { + if (as) + *as = peer->as; + return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT; + } + } } + /* Can join the group. + * + * NB: if we get to here, then we know that this afi/safi was NOT a member + * of any group. + * + * Note that this appears to implicitly activate the afi/safi... but does + * not go through the rest of the activation process ??? + * + * TODO: why does implicit activation of afi/safi not do more work ? + */ peer->af_group[afi][safi] = 1; peer->afc[afi][safi] = 1; + + /* If not already a member, add to list of members. */ if (! peer->group) { peer->group = group; - peer = peer_lock (peer); /* group->peer list reference */ + peer = bgp_peer_lock (peer); /* group->peer list reference */ listnode_add (group->peer, peer); } else assert (group && peer->group == group); + /* Further magic stuff to do with peer sort. */ if (first_member) { /* Advertisement-interval reset */ @@ -1467,48 +1433,39 @@ peer_group_bind (struct bgp *bgp, union sockunion *su, } } + /* Worry about rsclient state of the peer for this afi/safi. + * + * A peer cannot be an rsclient separately from its group. + * + * This peer was not previously a member of any group for this afi/safi. + * + * So if the peer is an rsclient for this afi/safi, it had better stop + * that now. + */ 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); + /* Now that we have set peer->af_group for this afi/safi, this peer + * may no longer have any distinct rsclient status, in which case it + * must be removes from list bgp->rsclient. + * + * Note that it must have had distinct rsclient status, because it is + * an rsclient in this afi/safi, and it was not a group member in this + * afi/safi. + * + * We discard any import and export route map, except if the group is + * an rsclient, when we keep the export route map. + */ + UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT) ; - /* Clear our own rsclient rib for this afi/safi. */ - bgp_clear_route_rsclient (peer, afi, safi); - } - - 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_rsclient_unset(peer, afi, safi, + CHECK_FLAG(group->conf->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)) ; } + /* Now deal with the rest of the group configuration. */ 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); + /* And down the peer to push into new state. */ + bgp_peer_down(peer, PEER_DOWN_RMAP_BIND) ; return 0; } @@ -1518,35 +1475,42 @@ 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; + return 0; /* quit if not member of any group */ if (group != peer->group) - return BGP_ERR_PEER_GROUP_MISMATCH; + return BGP_ERR_PEER_GROUP_MISMATCH; /* quit if not member of this group */ + /* So is a member of this group for this afi/safi. + * + * This is an implied deactivation for this peer. That is taken care of in + * bgp_peer_down(). + */ 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; + /* Is member of group, so if it is an rsclient we were using its rsclient + * RIB, which must now forget. + */ + peer->rib[afi][safi] = NULL; + /* If is now not a member of any group */ if (! peer_group_active (peer)) { assert (listnode_lookup (group->peer, peer)); - peer_unlock (peer); /* peer group list reference */ + bgp_peer_unlock (peer); /* peer group list reference */ listnode_delete (group->peer, peer); peer->group = NULL; if (group->conf->as) { - peer_delete (peer); + bgp_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); + bgp_peer_down(peer, PEER_DOWN_RMAP_UNBIND) ; + return 0; } @@ -1562,7 +1526,7 @@ bgp_create (as_t *as, const char *name) return NULL; bgp_lock (bgp); - bgp->peer_self = peer_new (bgp); + bgp->peer_self = bgp_peer_new (bgp); bgp->peer_self->host = XSTRDUP (MTYPE_BGP_PEER_HOST, "Static announcement"); bgp->peer = list_new (); @@ -1710,7 +1674,7 @@ bgp_delete (struct bgp *bgp) bgp_redistribute_unset (bgp, afi, i); for (ALL_LIST_ELEMENTS (bgp->peer, node, next, peer)) - peer_delete (peer); + bgp_peer_delete (peer); for (ALL_LIST_ELEMENTS (bgp->group, node, next, group)) peer_group_delete (group); @@ -1718,7 +1682,7 @@ bgp_delete (struct bgp *bgp) assert (listcount (bgp->rsclient) == 0); if (bgp->peer_self) { - peer_delete(bgp->peer_self); + bgp_peer_delete(bgp->peer_self); bgp->peer_self = NULL; } @@ -1783,8 +1747,7 @@ peer_lookup (struct bgp *bgp, union sockunion *su) 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)) + if (sockunion_same (&peer->su, su)) return peer; } else if (bm->bgp != NULL) @@ -1793,13 +1756,13 @@ peer_lookup (struct bgp *bgp, union sockunion *su) 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)) + if (sockunion_same (&peer->su, su)) return peer; } return NULL; } +#if 0 struct peer * peer_lookup_with_open (union sockunion *su, as_t remote_as, struct in_addr *remote_id, int *as) @@ -1842,6 +1805,7 @@ peer_lookup_with_open (union sockunion *su, as_t remote_as, } return NULL; } +#endif /* If peer is configured at least one address family return 1. */ int @@ -1869,200 +1833,222 @@ peer_active_nego (struct peer *peer) return 0; } -/* peer_flag_change_type. */ +/*------------------------------------------------------------------------------ + * Actions required after a change of state of a peer. + */ enum peer_change_type { - peer_change_none, - peer_change_reset, - peer_change_reset_in, - peer_change_reset_out, -}; - + peer_change_none, /* no further action */ + peer_change_reset, /* drop any existing session and restart */ + peer_change_reset_in, /* if possible, ask for route refresh, + otherwise drop and restart. */ + peer_change_reset_out, /* reannounce everything */ +} ; + +typedef enum peer_change_type peer_change_type_t ; + +/* Perform action after change of state of a peer for the given afi/safi. + * + * If has to down the peer (drop any existing session and restart), then + * requires a peer_down_t to record why. + */ static void peer_change_action (struct peer *peer, afi_t afi, safi_t safi, - enum peer_change_type type) + enum peer_change_type type, + peer_down_t why_changed) { 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); -} + switch (type) + { + case peer_change_none: + break ; + case peer_change_reset: + bgp_peer_down(peer, why_changed) ; + break ; + + case peer_change_reset_in: + if (peer->state == bgp_peer_pEstablished) + { + 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_peer_down(peer, why_changed); + } ; + break ; + + case peer_change_reset_out: + bgp_announce_route (peer, afi, safi) ; /* Does nothing if !pEstablished */ + break ; + + default: + zabort("unknown peer_change_type") ; + break ; + } ; +} ; + +/*------------------------------------------------------------------------------ + * + * + */ struct peer_flag_action { - /* Peer's flag. */ + /* Peer's flag. */ u_int32_t flag; - /* This flag can be set for peer-group member. */ - u_char not_for_member; + /* This flag can be set for peer-group member. */ + bool not_for_member; - /* Action when the flag is changed. */ - enum peer_change_type type; + /* Action when the flag is changed. */ + peer_change_type_t type; - /* Peer down cause */ - u_char peer_down; + /* Peer down cause */ + peer_down_t peer_down; }; +/*------------------------------------------------------------------------------ + * Table of actions for changing peer->flags + * + * NB: change actions are peer_change_none or peer_change_reset ONLY. + * (The peer->flags apply to all afi/safi.) + * + * NB: PEER_FLAG_LOCAL_AS_NO_PREPEND is dealt with eslewhere. + * + * NB: all flags are set/cleared individually. + */ 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 } + { PEER_FLAG_PASSIVE, + false, peer_change_reset, PEER_DOWN_PASSIVE_CHANGE}, + { PEER_FLAG_SHUTDOWN, + false, peer_change_reset, PEER_DOWN_USER_SHUTDOWN }, + { PEER_FLAG_DONT_CAPABILITY, + false, peer_change_none, PEER_DOWN_NULL }, + { PEER_FLAG_OVERRIDE_CAPABILITY, + false, peer_change_none, PEER_DOWN_NULL }, + { PEER_FLAG_STRICT_CAP_MATCH, + false, peer_change_none, PEER_DOWN_NULL }, + { PEER_FLAG_DYNAMIC_CAPABILITY, + false, peer_change_reset, PEER_DOWN_CAPABILITY_CHANGE }, + { PEER_FLAG_DISABLE_CONNECTED_CHECK, + false, peer_change_reset, PEER_DOWN_CONFIG_CHANGE }, + { 0, false, peer_change_none, PEER_DOWN_NULL } }; +/*------------------------------------------------------------------------------ + * Table of actions for changing peer->af_flags + * + * NB: some flags may be set/cleared together, in any combination. + */ 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 } + { PEER_FLAG_NEXTHOP_SELF, + true, peer_change_reset_out, PEER_DOWN_NULL }, + { PEER_FLAG_SEND_COMMUNITY + | PEER_FLAG_SEND_EXT_COMMUNITY, + true, peer_change_reset_out, PEER_DOWN_NULL }, + { PEER_FLAG_SOFT_RECONFIG, + false, peer_change_reset_in, PEER_DOWN_CONFIG_CHANGE }, + { PEER_FLAG_REFLECTOR_CLIENT, + true, peer_change_reset, PEER_DOWN_RR_CLIENT_CHANGE }, + { PEER_FLAG_RSERVER_CLIENT, + true, peer_change_reset, PEER_DOWN_RS_CLIENT_CHANGE }, + { PEER_FLAG_AS_PATH_UNCHANGED + | PEER_FLAG_NEXTHOP_UNCHANGED + | PEER_FLAG_MED_UNCHANGED, + true, peer_change_reset_out, PEER_DOWN_NULL }, + { PEER_FLAG_REMOVE_PRIVATE_AS, + true, peer_change_reset_out, PEER_DOWN_NULL }, + { PEER_FLAG_ALLOWAS_IN, + false, peer_change_reset_in, PEER_DOWN_ALLOWAS_IN_CHANGE }, + { PEER_FLAG_ORF_PREFIX_SM + | PEER_FLAG_ORF_PREFIX_RM, + true, peer_change_reset, PEER_DOWN_CONFIG_CHANGE }, + { PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED, + false, peer_change_reset_out, PEER_DOWN_NULL }, + { 0, false, peer_change_none, PEER_DOWN_NULL } }; -/* 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) +/*------------------------------------------------------------------------------ + * Look up action for given flags. + * + * Returns: address of required peer_flag_action structure, + * or: NULL if no action found for the given combination of flags. + * + * This mechanism is generally used when setting/clearing one flag at a time. + * + * The table may contain entries with more than one flag. In these cases, + * any combination of those flags may be set/cleared together -- any flags + * which are not mentioned are not affected. + * + * The given flags value must either exactly match a single flag entry, or be + * a subset of a multiple flag entry. The table is searched in the order + * given, and proceeds to the end before stopping. + */ +static const struct peer_flag_action* +peer_flag_action_set (const struct peer_flag_action* action, uint32_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++) + while (action->flag != 0) { - 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; - } + if ((action->flag & flag) == flag) + return action ; + action++ ; } - /* 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; + return NULL ; } +/*------------------------------------------------------------------------------ + * Having set or cleared something in peer->flags, make any implied changes. + * + * NB: Clearing PEER_FLAG_SHUTDOWN is a special case + * + * NB: Setting PEER_FLAG_SHUTDOWN -> PEER_DOWN_USER_SHUTDOWN, which is a + * special case in bgp_peer_down. + */ static void -peer_flag_modify_action (struct peer *peer, u_int32_t flag) +peer_flag_modify_action (struct peer *peer, + const struct peer_flag_action* action, bool set) { - if (flag == PEER_FLAG_SHUTDOWN) - { - if (CHECK_FLAG (peer->flags, flag)) - { - if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT)) - peer_nsf_stop (peer); + if (action->type == peer_change_none) + return ; - 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 0 /* TODO: surely no need to peer_nsf_stop() twice ? */ - if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_WAIT)) - peer_nsf_stop (peer); -#endif - bgp_notify_send(peer, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN); - } - else - { - peer->v_start = BGP_INIT_START_TIMER; - bgp_peer_enable(peer); - } - } - else + assert(action->type == peer_change_reset) ; + + if ((action->flag == PEER_FLAG_SHUTDOWN) && !set) { - 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; + peer->v_start = BGP_INIT_START_TIMER; + bgp_peer_enable(peer); - bgp_notify_send(peer, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_CONFIG_CHANGE); - } -} + return ; /* Note */ + } ; -/* Change specified peer flag. */ + bgp_peer_down(peer, action->peer_down) ; +} ; + +/*------------------------------------------------------------------------------ + * Change specified peer->flags flag. + * + * See: peer_flag_action_list above. + */ static int -peer_flag_modify (struct peer *peer, u_int32_t flag, int set) +peer_flag_modify (struct peer *peer, u_int32_t flag, bool set) { - int found; - int size; struct peer_group *group; struct listnode *node, *nnode; - struct peer_flag_action action; + const 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); + action = peer_flag_action_set (peer_flag_action_list, flag) ; /* No flag action is found. */ - if (! found) + if (action == NULL) return BGP_ERR_INVALID_FLAG; /* Not for peer-group member. */ - if (action.not_for_member && peer_group_active (peer)) + 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 @@ -2097,9 +2083,7 @@ peer_flag_modify (struct peer *peer, u_int32_t flag, int set) if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (action.type == peer_change_reset) - peer_flag_modify_action (peer, flag); - + peer_flag_modify_action (peer, action, set); return 0; } @@ -2119,22 +2103,27 @@ peer_flag_modify (struct peer *peer, u_int32_t flag, int set) else UNSET_FLAG (peer->flags, flag); - if (action.type == peer_change_reset) - peer_flag_modify_action (peer, flag); + peer_flag_modify_action (peer, action, set); } return 0; } +/*------------------------------------------------------------------------------ + * Set specified peer->flags flag. + */ int peer_flag_set (struct peer *peer, u_int32_t flag) { - return peer_flag_modify (peer, flag, 1); + return peer_flag_modify (peer, flag, true); } +/*------------------------------------------------------------------------------ + * Clear specified peer->flags flag. + */ int peer_flag_unset (struct peer *peer, u_int32_t flag) { - return peer_flag_modify (peer, flag, 0); + return peer_flag_modify (peer, flag, false); } static int @@ -2145,23 +2134,43 @@ peer_is_group_member (struct peer *peer, afi_t afi, safi_t safi) return 0; } -static int +/*------------------------------------------------------------------------------ + * Having set or cleared something in peer->af_flags, make any implied changes. + * + * NB: clearing PEER_FLAG_SOFT_RECONFIG is a special case. + */ +static void +peer_af_flag_modify_action (struct peer *peer, afi_t afi, safi_t safi, + const struct peer_flag_action* action, bool set) +{ + if ((action->flag == PEER_FLAG_SOFT_RECONFIG) && !set) + { + if (peer->state == bgp_peer_pEstablished) + bgp_clear_adj_in (peer, afi, safi); + } + else + { + peer_change_action (peer, afi, safi, action->type, action->peer_down) ; + } ; +} ; + +/*------------------------------------------------------------------------------ + * Change specified peer->af_flags flag. + * + * See: peer_af_flag_action_list above. + */ +int peer_af_flag_modify (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag, - int set) + bool 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); + const struct peer_flag_action* action; - found = peer_flag_action_set (peer_af_flag_action_list, size, &action, flag); + action = peer_flag_action_set (peer_af_flag_action_list, flag); /* No flag action is found. */ - if (! found) + if (action == NULL) return BGP_ERR_INVALID_FLAG; /* Adress family must be activated. */ @@ -2169,17 +2178,16 @@ peer_af_flag_modify (struct peer *peer, afi_t afi, safi_t safi, u_int32_t flag, return BGP_ERR_PEER_INACTIVE; /* Not for peer-group member. */ - if (action.not_for_member && peer_is_group_member (peer, afi, safi)) + 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) + 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) + 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 @@ -2191,7 +2199,7 @@ peer_af_flag_modify (struct peer *peer, afi_t afi, safi_t safi, u_int32_t 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) + 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; @@ -2202,82 +2210,54 @@ peer_af_flag_modify (struct peer *peer, afi_t afi, safi_t safi, u_int32_t 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) + /* Execute action. */ + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - 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_af_flag_modify_action(peer, afi, safi, action, set) ; + return 0 ; + } ; /* Peer group member updates. */ - if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + group = peer->group; + + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) { - group = peer->group; + if (! peer->af_group[afi][safi]) + continue; - 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) == flag) - continue; + if (! set && ! CHECK_FLAG (peer->af_flags[afi][safi], 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 (set) - SET_FLAG (peer->af_flags[afi][safi], flag); - else - UNSET_FLAG (peer->af_flags[afi][safi], flag); + peer_af_flag_modify_action(peer, afi, safi, action, set) ; + } ; - 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; } +/*------------------------------------------------------------------------------ + * Set specified peer->af_flags flag. + */ 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); + return peer_af_flag_modify (peer, afi, safi, flag, true); } +/*------------------------------------------------------------------------------ + * Clear specified peer->af_flags flag. + */ 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); + return peer_af_flag_modify (peer, afi, safi, flag, false); } /* EBGP multihop configuration. */ @@ -2405,9 +2385,7 @@ peer_update_source_if_set (struct peer *peer, const char *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); + bgp_peer_down(peer, PEER_DOWN_UPDATE_SOURCE_CHANGE) ; return 0; } @@ -2432,9 +2410,7 @@ peer_update_source_if_set (struct peer *peer, const char *ifname) 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); + bgp_peer_down(peer, PEER_DOWN_UPDATE_SOURCE_CHANGE) ; } return 0; } @@ -2464,9 +2440,7 @@ peer_update_source_addr_set (struct peer *peer, union sockunion *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); + bgp_peer_down(peer, PEER_DOWN_UPDATE_SOURCE_CHANGE) ; return 0; } @@ -2490,9 +2464,7 @@ peer_update_source_addr_set (struct peer *peer, union sockunion *su) 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); + bgp_peer_down(peer, PEER_DOWN_UPDATE_SOURCE_CHANGE) ; } return 0; } @@ -2536,9 +2508,7 @@ peer_update_source_unset (struct peer *peer) 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); + bgp_peer_down(peer, PEER_DOWN_UPDATE_SOURCE_CHANGE) ; return 0; } @@ -2561,9 +2531,7 @@ peer_update_source_unset (struct peer *peer) peer->update_if = NULL; } - peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE; - bgp_notify_send(peer, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_CONFIG_CHANGE); + bgp_peer_down(peer, PEER_DOWN_UPDATE_SOURCE_CHANGE) ; } return 0; } @@ -2600,7 +2568,7 @@ peer_default_originate_set (struct peer *peer, afi_t afi, safi_t safi, if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->state == bgp_peer_sEstablished && peer->afc_nego[afi][safi]) + if (peer->state == bgp_peer_pEstablished && peer->afc_nego[afi][safi]) bgp_default_originate (peer, afi, safi, 0); return 0; } @@ -2619,7 +2587,7 @@ peer_default_originate_set (struct peer *peer, afi_t afi, safi_t safi, peer->default_rmap[afi][safi].map = route_map_lookup_by_name (rmap); } - if (peer->state == bgp_peer_sEstablished && peer->afc_nego[afi][safi]) + if (peer->state == bgp_peer_pEstablished && peer->afc_nego[afi][safi]) bgp_default_originate (peer, afi, safi, 0); } return 0; @@ -2651,7 +2619,7 @@ peer_default_originate_unset (struct peer *peer, afi_t afi, safi_t safi) if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->state == bgp_peer_sEstablished && peer->afc_nego[afi][safi]) + if (peer->state == bgp_peer_pEstablished && peer->afc_nego[afi][safi]) bgp_default_originate (peer, afi, safi, 1); return 0; } @@ -2667,7 +2635,7 @@ peer_default_originate_unset (struct peer *peer, afi_t afi, safi_t safi) 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]) + if (peer->state == bgp_peer_pEstablished && peer->afc_nego[afi][safi]) bgp_default_originate (peer, afi, safi, 1); } return 0; @@ -2908,7 +2876,8 @@ peer_allowas_in_set (struct peer *peer, afi_t afi, safi_t safi, int 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); + peer_change_action (peer, afi, safi, peer_change_reset_in, + PEER_DOWN_ALLOWAS_IN_CHANGE) ; } if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) @@ -2921,7 +2890,8 @@ peer_allowas_in_set (struct peer *peer, afi_t afi, safi_t safi, int 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); + peer_change_action (peer, afi, safi, peer_change_reset_in, + PEER_DOWN_ALLOWAS_IN_CHANGE) ; } } @@ -2985,10 +2955,8 @@ peer_local_as_set (struct peer *peer, as_t as, int 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; + bgp_peer_down(peer, PEER_DOWN_LOCAL_AS_CHANGE) ; + return 0 ; } group = peer->group; @@ -3000,9 +2968,7 @@ peer_local_as_set (struct peer *peer, as_t as, int 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); + bgp_peer_down(peer, PEER_DOWN_LOCAL_AS_CHANGE) ; } return 0; @@ -3025,9 +2991,7 @@ peer_local_as_unset (struct peer *peer) 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); + bgp_peer_down(peer, PEER_DOWN_LOCAL_AS_CHANGE) ; return 0; } @@ -3037,9 +3001,7 @@ peer_local_as_unset (struct peer *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); + bgp_peer_down(peer, PEER_DOWN_LOCAL_AS_CHANGE) ; } return 0; } @@ -3066,8 +3028,7 @@ peer_password_set (struct peer *peer, const char *password) if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - bgp_notify_send(peer, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_CONFIG_CHANGE); + bgp_peer_down(peer, PEER_DOWN_PASSWORD_CHANGE) ; return BGP_SUCCESS; } @@ -3081,8 +3042,7 @@ peer_password_set (struct peer *peer, const char *password) peer->password = XSTRDUP(MTYPE_PEER_PASSWORD, password); - bgp_notify_send(peer, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_CONFIG_CHANGE); + bgp_peer_down(peer, PEER_DOWN_PASSWORD_CHANGE) ; } return ret; @@ -3104,13 +3064,14 @@ peer_password_unset (struct peer *peer) && 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); + { + XFREE (MTYPE_PEER_PASSWORD, peer->password); + /* sets peer->password NULL */ + + bgp_peer_down(peer, PEER_DOWN_PASSWORD_CHANGE) ; + } ; - peer->password = NULL; return 0; } @@ -3122,11 +3083,9 @@ peer_password_unset (struct peer *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; + /* sets peer->password NULL */ + bgp_peer_down(peer, PEER_DOWN_PASSWORD_CHANGE) ; } return 0; @@ -3840,30 +3799,14 @@ peer_maximum_prefix_unset (struct peer *peer, afi_t afi, safi_t safi) 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); - } + /* Overrides any Max Prefix issues. */ + bgp_maximum_prefix_cancel_timer (peer) ; - /* Beware we may still be clearing, if so the end of - * clearing will enable the peer */ - bgp_peer_enable(peer); + /* Overrides any idle hold timer */ + peer->v_start = BGP_INIT_START_TIMER; - return 0; - } + bgp_peer_down(peer, PEER_DOWN_USER_RESET) ; - peer->v_start = BGP_INIT_START_TIMER; - bgp_notify_send(peer, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_ADMIN_RESET); - } return 0; } @@ -3871,7 +3814,7 @@ int peer_clear_soft (struct peer *peer, afi_t afi, safi_t safi, enum bgp_clear_type stype) { - if (peer->state == bgp_peer_sEstablished) + if (peer->state == bgp_peer_pEstablished) return 0; if (! peer->afc[afi][safi]) @@ -3922,7 +3865,8 @@ peer_clear_soft (struct peer *peer, afi_t afi, safi_t safi, } } - if (stype == BGP_CLEAR_SOFT_IN || stype == BGP_CLEAR_SOFT_BOTH + 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. @@ -4446,11 +4390,8 @@ bgp_config_write_family (struct vty *vty, struct bgp *bgp, afi_t afi, { 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); - } + bgp_config_write_family_header (vty, afi, safi, &write); + bgp_config_write_peer (vty, bgp, peer, afi, safi); } } if (write) @@ -4621,10 +4562,7 @@ bgp_config_write (struct vty *vty) /* 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); - } + bgp_config_write_peer (vty, bgp, peer, AFI_IP, SAFI_UNICAST); /* Distance configuration. */ bgp_config_write_distance (vty, bgp); @@ -4657,11 +4595,18 @@ bgp_master_init (void) bm = &bgp_master; bm->bgp = list_new (); - bm->port = BGP_PORT_DEFAULT; bm->master = thread_master_create (); + bm->port = BGP_PORT_DEFAULT; bm->start_time = time (NULL); -} + /* Implicitly: + * + * address = NULL -- no special listen address + * options = 0 -- no options set + * as2_speaker = false -- as4 speaker by default + * peer_linger_count = 0 -- no peers lingering + */ +} ; void bgp_init (void) @@ -4707,6 +4652,12 @@ bgp_init (void) #endif /* HAVE_SNMP */ } +/*------------------------------------------------------------------------------ + * If not terminating, reset all peers now + * + * If + * + */ void bgp_terminate (int terminating, int retain_mode) { @@ -4715,75 +4666,22 @@ bgp_terminate (int terminating, int retain_mode) 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)) - { - if (retain_mode) - bgp_peer_disable(peer, NULL); - else if (terminating) - { - peer_flag_set(peer, PEER_FLAG_SHUTDOWN); - /* Belt and braces, probably redundant */ - bgp_notify_send(peer, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN); - } - else - bgp_notify_send(peer, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_ADMIN_RESET); - } - - if (!retain_mode) - bgp_cleanup_routes (); - - for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp)) + /* If we are retaining, then turn off changes to the FIB. */ + if (retain_mode) { - if (bgp->process_main_queue) - { - work_queue_free (bgp->process_main_queue); - bgp->process_main_queue = NULL; - } - if (bgp->process_rsclient_queue) - { - work_queue_free (bgp->process_rsclient_queue); - bgp->process_rsclient_queue = NULL; - } - } - - /* if no sessions were enabled then need to check here */ - program_terminate_if_all_disabled(); -} + assert(terminating) ; /* Can only retain when terminating */ + bgp_option_set(BGP_OPT_NO_FIB) ; + } ; -/* 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 bgp instances... */ 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); -} + { + /* ...delete or down all peers. */ + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + if (terminating) + bgp_peer_delete(peer) ; + else + bgp_peer_down(peer, PEER_DOWN_USER_RESET) ; + } ; +} ; diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index cd92576a..7a53c511 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -35,19 +35,17 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA /* BGP master for system wide configurations and variables. */ struct bgp_master { - /* BGP instance list. */ + /* BGP instance list. */ struct list *bgp; - /* BGP thread master. */ + /* BGP thread master. */ struct thread_master *master; - /* BGP port number. */ - u_int16_t port; - - /* Listener address */ + /* Listener address & port number */ char *address; + u_int16_t port; - /* BGP start time. */ + /* BGP start time. */ time_t start_time; /* Various BGP global configuration. */ @@ -58,6 +56,9 @@ struct bgp_master /* Do not announce AS4 */ bool as2_speaker ; + + /* Peers lingering in pDeleting */ + unsigned peer_linger_count ; }; /* BGP instance structure. */ @@ -69,7 +70,7 @@ struct bgp /* Name of this BGP instance. */ char *name; - /* Reference count to allow peer_delete to finish after bgp_delete */ + /* Reference count to allow bgp_peer_delete to finish after bgp_delete */ int lock; /* Self peer. */ @@ -423,8 +424,6 @@ extern struct peer_group *peer_group_lookup (struct bgp *, const char *); extern struct peer_group *peer_group_get (struct bgp *, const char *); extern struct peer *peer_lookup_with_open (union sockunion *, as_t, struct in_addr *, int *); -extern struct peer *peer_lock (struct peer *); -extern struct peer *peer_unlock (struct peer *); extern int peer_sort (struct peer *peer); extern int peer_active (struct peer *); extern int peer_active_nego (struct peer *); @@ -474,13 +473,15 @@ extern int peer_rsclient_active (struct peer *); extern int peer_remote_as (struct bgp *, union sockunion *, as_t *, afi_t, safi_t); extern int peer_group_remote_as (struct bgp *, const char *, as_t *); -extern int peer_delete (struct peer *peer); extern int peer_group_delete (struct peer_group *); extern int peer_group_remote_as_delete (struct peer_group *); extern int peer_activate (struct peer *, afi_t, safi_t); extern int peer_deactivate (struct peer *, afi_t, safi_t); +extern void peer_rsclient_unset(struct peer* peer, int afi, int safi, + bool keep_export) ; + extern int peer_group_bind (struct bgp *, union sockunion *, struct peer_group *, afi_t, safi_t, as_t *); extern int peer_group_unbind (struct bgp *, struct peer *, struct peer_group *, @@ -491,6 +492,8 @@ extern int peer_flag_unset (struct peer *, u_int32_t); extern int peer_af_flag_set (struct peer *, afi_t, safi_t, u_int32_t); extern int peer_af_flag_unset (struct peer *, afi_t, safi_t, u_int32_t); +extern int peer_af_flag_modify (struct peer *, afi_t, safi_t, u_int32_t, + bool set) ; extern int peer_af_flag_check (struct peer *, afi_t, safi_t, u_int32_t); extern int peer_ebgp_multihop_set (struct peer *, int); diff --git a/configure.ac b/configure.ac index 85784080..3507a120 100755 --- a/configure.ac +++ b/configure.ac @@ -8,7 +8,7 @@ ## $Id$ AC_PREREQ(2.53) -AC_INIT(Quagga, 0.99.15ex01, [http://bugzilla.quagga.net]) +AC_INIT(Quagga, 0.99.15ex02, [http://bugzilla.quagga.net]) AC_CONFIG_SRCDIR(lib/zebra.h) AC_CONFIG_MACRO_DIR([m4]) diff --git a/lib/plist.c b/lib/plist.c index 41868e96..85ff35f8 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -700,18 +700,24 @@ prefix_list_entry_lookup_seq(struct prefix_list *plist, int seq, int* result) * * Returns index of an entry in the prefix_list or the dup_cache, and sets: * - * cache -- NULL if not using dup_cache for this prefix_list. - * The index and result values refer to the main prefix_list. + * cache -- NULL if not using dup_cache for this prefix_list. + * The index and result values refer to the main prefix_list. * - * -- address of the cache (a vector). - * The index and result values refer to the cache. << NB + * result < 0 -- not found. prefix list is empty, index == 0 + * result == 0 -- found. index is of the prefix list entry + * result > 0 -- not found. prefix is not empty, index == last * - * result < 0 -- not found. index returned is of first entry in the - * prefix list, and this sequence number comes - * before it. (Or list is empty.) - * result == 0 -- found. index is of the entry found. - * result > 0 -- not found. index returned is of the entry with the largest - * sequence number smaller than the given one. + * -- address of the cache (a vector). + * The index and result values refer to the *cache*. << NB + * + * result < 0 -- not found. index == 0, prefix value given is + * less than first entry in the *cache* + * result == 0 -- found. index is of the *cache* entry + * result > 0 -- not found. index is of entry in the *cache* + * with the largest prefix value less + * than the given prefix value. + * + * Note that the cache is never empty. */ static vector_index prefix_list_entry_lookup_val(struct prefix_list *plist, @@ -749,7 +755,9 @@ prefix_list_entry_lookup_val(struct prefix_list *plist, if (plist->cmp(&pe, &temp) == 0) return i ; /* Found ! */ } ; - *result = 1 ; /* Not found. */ + + *result = (vector_end(&plist->list) == 0) ? -1 : +1 ; + /* Not found. */ return i ; } ; } ; @@ -855,16 +863,21 @@ prefix_list_entry_insert(struct prefix_list *plist, i = prefix_list_entry_lookup_seq(plist, temp->seq, &ret) ; else { - int last_seq = 0 ; + int last_seq ; i = vector_end(&plist->list) ; - if (i != 0) + if (i == 0) + { + last_seq = 0 ; /* initial value for empty list */ + ret = -1 ; /* insert before first item of empty list */ + } + else { - --i ; /* step back to last entry */ + --i ; /* step back to last entry */ pe = vector_get_item(&plist->list, i) ; - last_seq = pe->seq ; + last_seq = pe->seq ; + ret = 1 ; /* insert after last entry */ } ; temp->seq = (((last_seq + 5 - 1) / 5) * 5) + 5 ; - ret = 1 ; /* insert after last entry (if any) */ } ; /* If we found it with same sequence number, then replace entry, diff --git a/lib/qpnexus.c b/lib/qpnexus.c index 59dcc535..3b014be2 100644 --- a/lib/qpnexus.c +++ b/lib/qpnexus.c @@ -53,7 +53,7 @@ static void qpn_in_thread_init(qpn_nexus qpn); * Returns the qpn_nexus. */ extern qpn_nexus -qpn_init_new(qpn_nexus qpn, int main_thread) +qpn_init_new(qpn_nexus qpn, bool main_thread) { if (qpn == NULL) qpn = XCALLOC(MTYPE_QPN_NEXUS, sizeof(struct qpn_nexus)) ; @@ -309,13 +309,19 @@ qpn_in_thread_init(qpn_nexus qpn) } /*------------------------------------------------------------------------------ - * Ask the thread to terminate itself quickly and cleanly + * Ask the thread to terminate itself quickly and cleanly. + * + * Does nothing if terminate already set. */ void qpn_terminate(qpn_nexus qpn) { - qpn->terminate = 1; - /* wake up any pselect */ - if (qpthreads_enabled) - qpt_thread_signal(qpn->thread_id, SIGMQUEUE); + if (!qpn->terminate) + { + qpn->terminate = true ; + + /* wake up any pselect */ + if (qpthreads_enabled) + qpt_thread_signal(qpn->thread_id, SIGMQUEUE); + } ; } diff --git a/lib/qpnexus.h b/lib/qpnexus.h index f4195a4d..0cf5a824 100644 --- a/lib/qpnexus.h +++ b/lib/qpnexus.h @@ -75,10 +75,10 @@ typedef struct qpn_nexus* qpn_nexus ; struct qpn_nexus { /* set true to terminate the thread (eventually) */ - int terminate; + bool terminate; /* true if this is the main thread */ - int main_thread; + bool main_thread; /* thread ID */ qpt_thread_t thread_id; @@ -146,7 +146,7 @@ struct qpn_nexus * Functions */ -extern qpn_nexus qpn_init_new(qpn_nexus qpn, int main_thread); +extern qpn_nexus qpn_init_new(qpn_nexus qpn, bool main_thread); extern void qpn_add_hook_function(qpn_hook_list list, void* hook) ; extern void qpn_exec(qpn_nexus qpn); extern void qpn_terminate(qpn_nexus qpn); diff --git a/tests/bgp_capability_test.c b/tests/bgp_capability_test.c index 8f265870..cae9a12d 100644 --- a/tests/bgp_capability_test.c +++ b/tests/bgp_capability_test.c @@ -664,7 +664,7 @@ main (void) parse_test (peer, &opt_params[i++], OPT_PARAM); SET_FLAG (peer->cap, PEER_CAP_DYNAMIC_ADV); - peer->state = bgp_peer_sEstablished; + peer->state = bgp_peer_pEstablished; i = 0; while (dynamic_cap_msgs[i].name) |