diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 4ac8201f749c..ed735df87308 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -1271,7 +1271,8 @@ void bgp_fsm_change_status(struct peer_connection *connection, * Clearing * (or Deleted). */ - if (!work_queue_is_scheduled(peer->clear_node_queue) && + if (!CHECK_FLAG(peer->flags, PEER_FLAG_CLEARING_BATCH) && + !work_queue_is_scheduled(peer->clear_node_queue) && status != Deleted) BGP_EVENT_ADD(connection, Clearing_Completed); } diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c index 9e9251c85459..c7aa946f4463 100644 --- a/bgpd/bgp_io.c +++ b/bgpd/bgp_io.c @@ -100,7 +100,6 @@ void bgp_reads_off(struct peer_connection *connection) event_cancel_async(fpt->master, &connection->t_read, NULL); EVENT_OFF(connection->t_process_packet); - EVENT_OFF(connection->t_process_packet_error); UNSET_FLAG(connection->thread_flags, PEER_THREAD_READS_ON); } @@ -252,8 +251,7 @@ static void bgp_process_reads(struct event *thread) /* Handle the error in the main pthread, include the * specific state change from 'bgp_read'. */ - event_add_event(bm->master, bgp_packet_process_error, connection, - code, &connection->t_process_packet_error); + bgp_enqueue_conn_err(peer->bgp, connection, code); goto done; } diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index 1f76945da3e0..517e34feba84 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -136,4 +136,6 @@ DECLARE_MTYPE(BGP_SOFT_VERSION); DECLARE_MTYPE(BGP_EVPN_OVERLAY); +DECLARE_MTYPE(CLEARING_BATCH); + #endif /* _QUAGGA_BGP_MEMORY_H */ diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index a76a300c11bc..8ce0cdf89fd0 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -4196,37 +4196,3 @@ void bgp_send_delayed_eor(struct bgp *bgp) for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) bgp_write_proceed_actions(peer); } - -/* - * Task callback to handle socket error encountered in the io pthread. We avoid - * having the io pthread try to enqueue fsm events or mess with the peer - * struct. - */ -void bgp_packet_process_error(struct event *thread) -{ - struct peer_connection *connection; - struct peer *peer; - int code; - - connection = EVENT_ARG(thread); - peer = connection->peer; - code = EVENT_VAL(thread); - - if (bgp_debug_neighbor_events(peer)) - zlog_debug("%s [Event] BGP error %d on fd %d", peer->host, code, - connection->fd); - - /* Closed connection or error on the socket */ - if (peer_established(connection)) { - if ((CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART) - || CHECK_FLAG(peer->flags, - PEER_FLAG_GRACEFUL_RESTART_HELPER)) - && CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) { - peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; - SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); - } else - peer->last_reset = PEER_DOWN_CLOSE_SESSION; - } - - bgp_event_update(connection, code); -} diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h index c266b17266ec..3f7106fd37ed 100644 --- a/bgpd/bgp_packet.h +++ b/bgpd/bgp_packet.h @@ -75,8 +75,6 @@ extern void bgp_process_packet(struct event *event); extern void bgp_send_delayed_eor(struct bgp *bgp); -/* Task callback to handle socket error encountered in the io pthread */ -void bgp_packet_process_error(struct event *thread); extern struct bgp_notify bgp_notify_decapsulate_hard_reset(struct bgp_notify *notify); extern bool bgp_has_graceful_restart_notification(struct peer *peer); diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 72e798a7e2c9..a5f287a0688a 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -78,6 +78,9 @@ #include "bgpd/bgp_route_clippy.c" +/* Memory for batched clearing of peers from the RIB */ +DEFINE_MTYPE(BGPD, CLEARING_BATCH, "Clearing batch"); + DEFINE_HOOK(bgp_snmp_update_stats, (struct bgp_dest *rn, struct bgp_path_info *pi, bool added), (rn, pi, added)); @@ -6186,11 +6189,240 @@ void bgp_clear_route(struct peer *peer, afi_t afi, safi_t safi) peer_unlock(peer); } +/* + * Callback scheduled to process prefixes/dests for batch clearing; the + * dests were found via a rib walk. + * The one-peer version of this uses a per-peer workqueue to manage + * rescheduling, but we're just using a fixed limit here. + */ + +/* Limit the number of dests we'll process per callback */ +#define BGP_CLEARING_BATCH_MAX_DESTS 100 + +static void bgp_clear_batch_dests_task(struct event *event) +{ + struct bgp_clearing_info *cinfo = EVENT_ARG(event); + struct bgp_dest *dest; + struct bgp_path_info *pi; + struct bgp_table *table; + struct bgp *bgp; + afi_t afi; + safi_t safi; + int counter = 0; + + bgp = cinfo->bgp; + +next_dest: + + dest = bgp_clearing_batch_next_dest(cinfo); + if (dest == NULL) + goto done; + + table = bgp_dest_table(dest); + afi = table->afi; + safi = table->safi; + + /* Have to check every path: it is possible that we have multiple paths + * for a prefix from a peer if that peer is using AddPath. + */ + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { + if (!bgp_clearing_batch_check_peer(cinfo, pi->peer)) + continue; + + /* graceful restart STALE flag set. */ + if (((CHECK_FLAG(pi->peer->sflags, PEER_STATUS_NSF_WAIT) + && pi->peer->nsf[afi][safi]) + || CHECK_FLAG(pi->peer->af_sflags[afi][safi], + PEER_STATUS_ENHANCED_REFRESH)) + && !CHECK_FLAG(pi->flags, BGP_PATH_STALE) + && !CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE)) + bgp_path_info_set_flag(dest, pi, BGP_PATH_STALE); + else { + /* If this is an EVPN route, process for + * un-import. */ + if (safi == SAFI_EVPN) + bgp_evpn_unimport_route( + bgp, afi, safi, + bgp_dest_get_prefix(dest), pi); + /* Handle withdraw for VRF route-leaking and L3VPN */ + if (SAFI_UNICAST == safi + && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF || + bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { + vpn_leak_from_vrf_withdraw(bgp_get_default(), + bgp, pi); + } + if (SAFI_MPLS_VPN == safi && + bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) { + vpn_leak_to_vrf_withdraw(pi); + } + + bgp_rib_remove(dest, pi, pi->peer, afi, safi); + } + } + + /* Unref this dest and table */ + bgp_dest_unlock_node(dest); + bgp_table_unlock(bgp_dest_table(dest)); + + counter++; + if (counter < BGP_CLEARING_BATCH_MAX_DESTS) + goto next_dest; + +done: + + /* If there are still dests to process, reschedule. */ + if (bgp_clearing_batch_dests_present(cinfo)) { + if (bgp_debug_neighbor_events(NULL)) + zlog_debug("%s: Batch %p: Rescheduled after processing %d dests", + __func__, cinfo, counter); + + event_add_event(bm->master, bgp_clear_batch_dests_task, cinfo, + 0, &cinfo->t_sched); + } else { + if (bgp_debug_neighbor_events(NULL)) + zlog_debug("%s: Batch %p: Done after processing %d dests", + __func__, cinfo, counter); + bgp_clearing_batch_completed(cinfo); + } + + return; +} + +/* + * Walk a single table for batch peer clearing processing + */ +static void clear_batch_table_helper(struct bgp_clearing_info *cinfo, + struct bgp_table *table) +{ + struct bgp_dest *dest; + bool force = (cinfo->bgp->process_queue == NULL); + uint32_t examined = 0, queued = 0; + + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { + struct bgp_path_info *pi, *next; + struct bgp_adj_in *ain; + struct bgp_adj_in *ain_next; + + examined++; + + ain = dest->adj_in; + while (ain) { + ain_next = ain->next; + + if (bgp_clearing_batch_check_peer(cinfo, ain->peer)) + bgp_adj_in_remove(&dest, ain); + + ain = ain_next; + + assert(dest != NULL); + } + + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = next) { + next = pi->next; + if (!bgp_clearing_batch_check_peer(cinfo, pi->peer)) + continue; + + queued++; + + if (force) { + bgp_path_info_reap(dest, pi); + } else { + /* Unlocked after processing */ + bgp_table_lock(bgp_dest_table(dest)); + bgp_dest_lock_node(dest); + + bgp_clearing_batch_add_dest(cinfo, dest); + break; + } + } + } + + if (examined > 0) { + if (bgp_debug_neighbor_events(NULL)) + zlog_debug("%s: %s/%s: examined %u, queued %u", + __func__, afi2str(table->afi), + safi2str(table->safi), examined, queued); + } +} + +/* + * RIB-walking helper for batch clearing work: walk all tables, identify + * dests that are affected by the peers in the batch, enqueue the dests for + * async processing. + */ +static void clear_batch_rib_helper(struct bgp_clearing_info *cinfo) +{ + afi_t afi; + safi_t safi; + struct bgp_dest *dest; + struct bgp_table *table; + + FOREACH_AFI_SAFI (afi, safi) { + /* Identify table to be examined */ + if (safi != SAFI_MPLS_VPN && safi != SAFI_ENCAP && + safi != SAFI_EVPN) { + table = cinfo->bgp->rib[afi][safi]; + if (!table) + continue; + + clear_batch_table_helper(cinfo, table); + } else { + for (dest = bgp_table_top(cinfo->bgp->rib[afi][safi]); + dest; dest = bgp_route_next(dest)) { + table = bgp_dest_get_bgp_table_info(dest); + if (!table) + continue; + + /* TODO -- record the tables we've seen + * and don't repeat any? + */ + + clear_batch_table_helper(cinfo, table); + } + } + } +} + +/* + * Identify prefixes that need to be cleared for a batch of peers in 'cinfo'. + * The actual clearing processing will be done async... + */ +void bgp_clear_route_batch(struct bgp_clearing_info *cinfo) +{ + if (bgp_debug_neighbor_events(NULL)) + zlog_debug("%s: BGP %s, batch %p", __func__, + cinfo->bgp->name_pretty, cinfo); + + /* Walk the rib, checking the peers in the batch */ + clear_batch_rib_helper(cinfo); + + /* If we found some prefixes, schedule a task to begin work. */ + if (bgp_clearing_batch_dests_present(cinfo)) + event_add_event(bm->master, bgp_clear_batch_dests_task, cinfo, + 0, &cinfo->t_sched); + + /* NB -- it's the caller's job to clean up, release refs, etc. if + * we didn't find any dests + */ +} + void bgp_clear_route_all(struct peer *peer) { afi_t afi; safi_t safi; + /* We may be able to batch multiple peers' clearing work: check + * and see. + */ + if (bgp_clearing_batch_add_peer(peer->bgp, peer)) { + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s: peer %pBP batched", __func__, peer); + return; + } + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s: peer %pBP", __func__, peer); + FOREACH_AFI_SAFI (afi, safi) bgp_clear_route(peer, afi, safi); diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 1df0ffd300e1..8c1b4a104365 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -746,6 +746,9 @@ extern void bgp_soft_reconfig_table_task_cancel(const struct bgp *bgp, extern bool bgp_soft_reconfig_in(struct peer *peer, afi_t afi, safi_t safi); extern void bgp_clear_route(struct peer *, afi_t, safi_t); extern void bgp_clear_route_all(struct peer *); +/* Clear routes for a batch of peers */ +void bgp_clear_route_batch(struct bgp_clearing_info *cinfo); + extern void bgp_clear_adj_in(struct peer *, afi_t, safi_t); extern void bgp_clear_stale_route(struct peer *, afi_t, safi_t); extern void bgp_set_stale_route(struct peer *peer, afi_t afi, safi_t safi); @@ -899,9 +902,6 @@ extern bool subgroup_announce_check(struct bgp_dest *dest, const struct prefix *p, struct attr *attr, struct attr *post_attr); -extern void bgp_peer_clear_node_queue_drain_immediate(struct peer *peer); -extern void bgp_process_queues_drain_immediate(void); - /* for encap/vpn */ extern struct bgp_dest *bgp_safi_node_lookup(struct bgp_table *table, safi_t safi, diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index a8431bee978a..d579fde40cb6 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -87,6 +87,22 @@ DEFINE_QOBJ_TYPE(peer); DEFINE_HOOK(bgp_inst_delete, (struct bgp *bgp), (bgp)); DEFINE_HOOK(bgp_instance_state, (struct bgp *bgp), (bgp)); +/* Peers with connection error/failure, per bgp instance */ +DECLARE_DLIST(bgp_peer_conn_errlist, struct peer_connection, conn_err_link); + +/* List of info about peers that are being cleared from BGP RIBs in a batch */ +DECLARE_DLIST(bgp_clearing_info, struct bgp_clearing_info, link); + +/* List of dests that need to be processed in a clearing batch */ +DECLARE_LIST(bgp_clearing_destlist, struct bgp_clearing_dest, link); + +/* Hash of peers in clearing info object */ +static int peer_clearing_hash_cmp(const struct peer *p1, const struct peer *p2); +static uint32_t peer_clearing_hashfn(const struct peer *p1); + +DECLARE_HASH(bgp_clearing_hash, struct peer, clear_hash_link, + peer_clearing_hash_cmp, peer_clearing_hashfn); + /* BGP process wide configuration. */ static struct bgp_master bgp_master; @@ -2710,6 +2726,9 @@ int peer_delete(struct peer *peer) assert(peer->connection->status != Deleted); + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s: peer %pBP", __func__, peer); + bgp = peer->bgp; accept_peer = CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER); @@ -2727,6 +2746,13 @@ int peer_delete(struct peer *peer) PEER_THREAD_READS_ON)); assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON)); + /* Ensure the peer is removed from the connection error list */ + frr_with_mutex (&bgp->peer_errs_mtx) { + if (bgp_peer_conn_errlist_anywhere(peer->connection)) + bgp_peer_conn_errlist_del(&bgp->peer_conn_errlist, + peer->connection); + } + if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)) peer_nsf_stop(peer); @@ -3634,6 +3660,11 @@ static struct bgp *bgp_create(as_t *as, const char *name, memset(&bgp->ebgprequirespolicywarning, 0, sizeof(bgp->ebgprequirespolicywarning)); + /* Init peer connection error info */ + pthread_mutex_init(&bgp->peer_errs_mtx, NULL); + bgp_peer_conn_errlist_init(&bgp->peer_conn_errlist); + bgp_clearing_info_init(&bgp->clearing_list); + return bgp; } @@ -4006,6 +4037,8 @@ int bgp_delete(struct bgp *bgp) struct bgp_table *dest_table = NULL; struct graceful_restart_info *gr_info; uint32_t cnt_before, cnt_after; + struct bgp_clearing_info *cinfo; + struct peer_connection *connection; assert(bgp); @@ -4030,6 +4063,10 @@ int bgp_delete(struct bgp *bgp) zlog_debug("Zebra Announce Fifo cleanup count before %u and after %u during BGP %s deletion", cnt_before, cnt_after, bgp->name_pretty); + /* Cleanup for peer connection batching */ + while ((cinfo = bgp_clearing_info_first(&bgp->clearing_list)) != NULL) + bgp_clearing_batch_completed(cinfo); + bgp_soft_reconfig_table_task_cancel(bgp, NULL, NULL); /* make sure we withdraw any exported routes */ @@ -4050,6 +4087,8 @@ int bgp_delete(struct bgp *bgp) EVENT_OFF(bgp->t_maxmed_onstartup); EVENT_OFF(bgp->t_update_delay); EVENT_OFF(bgp->t_establish_wait); + /* Cancel peer connection errors event */ + EVENT_OFF(bgp->t_conn_errors); /* Set flag indicating bgp instance delete in progress */ SET_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS); @@ -4128,6 +4167,18 @@ int bgp_delete(struct bgp *bgp) if (i != ZEBRA_ROUTE_BGP) bgp_redistribute_unset(bgp, afi, i, 0); + /* Clear list of peers with connection errors - each + * peer will need to check again, in case the io pthread is racing + * with us, but this batch cleanup should make the per-peer check + * cheaper. + */ + frr_with_mutex (&bgp->peer_errs_mtx) { + do { + connection = bgp_peer_conn_errlist_pop( + &bgp->peer_conn_errlist); + } while (connection != NULL); + } + /* Free peers and peer-groups. */ for (ALL_LIST_ELEMENTS(bgp->group, node, next, group)) peer_group_delete(group); @@ -4143,7 +4194,6 @@ int bgp_delete(struct bgp *bgp) } update_bgp_group_free(bgp); - /* TODO - Other memory may need to be freed - e.g., NHT */ #ifdef ENABLE_BGP_VNC @@ -4312,6 +4362,9 @@ void bgp_free(struct bgp *bgp) bgp_srv6_cleanup(bgp); bgp_confederation_id_unset(bgp); + bgp_peer_conn_errlist_init(&bgp->peer_conn_errlist); + pthread_mutex_destroy(&bgp->peer_errs_mtx); + for (int i = 0; i < bgp->confed_peers_cnt; i++) XFREE(MTYPE_BGP_NAME, bgp->confed_peers[i].as_pretty); @@ -8945,6 +8998,351 @@ void bgp_gr_apply_running_config(void) } } +/* Hash of peers in clearing info object */ +static int peer_clearing_hash_cmp(const struct peer *p1, const struct peer *p2) +{ + if (p1 == p2) + return 0; + else if (p1 < p2) + return -1; + else + return 1; +} + +static uint32_t peer_clearing_hashfn(const struct peer *p1) +{ + return (uint32_t)((intptr_t)p1 & 0xffffffffULL); +} + +/* + * Free a clearing batch: this really just does the memory cleanup; the + * clearing code is expected to manage the peer, dest, table, etc refcounts + */ +static void bgp_clearing_batch_free(struct bgp *bgp, + struct bgp_clearing_info **pinfo) +{ + struct bgp_clearing_info *cinfo = *pinfo; + struct bgp_clearing_dest *destinfo; + + if (bgp_clearing_info_anywhere(cinfo)) + bgp_clearing_info_del(&bgp->clearing_list, cinfo); + + while ((destinfo = bgp_clearing_destlist_pop(&cinfo->destlist)) != NULL) + XFREE(MTYPE_CLEARING_BATCH, destinfo); + + bgp_clearing_hash_fini(&cinfo->peers); + + XFREE(MTYPE_CLEARING_BATCH, *pinfo); +} + +/* + * Done with a peer that was part of a clearing batch + */ +static void bgp_clearing_peer_done(struct peer *peer) +{ + /* Tickle FSM to start moving again */ + BGP_EVENT_ADD(peer->connection, Clearing_Completed); + + peer_unlock(peer); /* bgp_clear_route */ +} + +/* + * Initialize a new batch struct for clearing peer(s) from the RIB + */ +static void bgp_clearing_batch_begin(struct bgp *bgp) +{ + struct bgp_clearing_info *cinfo; + + cinfo = XCALLOC(MTYPE_CLEARING_BATCH, sizeof(struct bgp_clearing_info)); + + cinfo->bgp = bgp; + + /* Init hash of peers and list of dests */ + bgp_clearing_hash_init(&cinfo->peers); + bgp_clearing_destlist_init(&cinfo->destlist); + + /* Batch is open for more peers */ + SET_FLAG(cinfo->flags, BGP_CLEARING_INFO_FLAG_OPEN); + + bgp_clearing_info_add_head(&bgp->clearing_list, cinfo); +} + +/* + * Close a batch of clearing peers, and begin working on the RIB + */ +static void bgp_clearing_batch_end(struct bgp *bgp) +{ + struct bgp_clearing_info *cinfo; + + cinfo = bgp_clearing_info_first(&bgp->clearing_list); + + assert(cinfo != NULL); + assert(CHECK_FLAG(cinfo->flags, BGP_CLEARING_INFO_FLAG_OPEN)); + + /* Batch is closed */ + UNSET_FLAG(cinfo->flags, BGP_CLEARING_INFO_FLAG_OPEN); + + /* If we have no peers to examine, just discard the batch info */ + if (bgp_clearing_hash_count(&cinfo->peers) == 0) { + bgp_clearing_batch_free(bgp, &cinfo); + return; + } + + /* Do a RIB walk for the current batch. If it finds dests/prefixes + * to work on, this will schedule a task to process + * the dests/prefixes in the batch. + */ + bgp_clear_route_batch(cinfo); + + /* If we found no prefixes/dests, just discard the batch, + * remembering that we're holding a ref for each peer. + */ + if (bgp_clearing_destlist_count(&cinfo->destlist) == 0) { + bgp_clearing_batch_completed(cinfo); + } +} + +/* Check whether a dest's peer is relevant to a clearing batch */ +bool bgp_clearing_batch_check_peer(struct bgp_clearing_info *cinfo, + const struct peer *peer) +{ + struct peer *p; + + p = bgp_clearing_hash_find(&cinfo->peers, peer); + return (p != NULL); +} + +/* + * Check whether a clearing batch has any dests to process + */ +bool bgp_clearing_batch_dests_present(struct bgp_clearing_info *cinfo) +{ + return (bgp_clearing_destlist_count(&cinfo->destlist) > 0); +} + +/* + * Done with a peer clearing batch; deal with refcounts, free memory + */ +void bgp_clearing_batch_completed(struct bgp_clearing_info *cinfo) +{ + struct peer *peer; + struct bgp_dest *dest; + struct bgp_clearing_dest *destinfo; + + /* Ensure event is not scheduled */ + event_cancel_event(bm->master, &cinfo->t_sched); + + /* Remove all peers and un-ref */ + while ((peer = bgp_clearing_hash_pop(&cinfo->peers)) != NULL) + bgp_clearing_peer_done(peer); + + /* Remove any dests/prefixes and unlock (should have been done + * by processing, so this is belt-and-suspenders) + */ + destinfo = bgp_clearing_destlist_pop(&cinfo->destlist); + if (destinfo) { + dest = destinfo->dest; + XFREE(MTYPE_CLEARING_BATCH, destinfo); + + bgp_dest_unlock_node(dest); + bgp_table_unlock(bgp_dest_table(dest)); + } + + /* Free memory */ + bgp_clearing_batch_free(cinfo->bgp, &cinfo); +} + +/* + * Add a prefix/dest to a clearing batch + */ +void bgp_clearing_batch_add_dest(struct bgp_clearing_info *cinfo, + struct bgp_dest *dest) +{ + struct bgp_clearing_dest *destinfo; + + destinfo = XCALLOC(MTYPE_CLEARING_BATCH, + sizeof(struct bgp_clearing_dest)); + + destinfo->dest = dest; + bgp_clearing_destlist_add_tail(&cinfo->destlist, destinfo); +} + +/* + * Return the next dest for batch clear processing + */ +struct bgp_dest *bgp_clearing_batch_next_dest(struct bgp_clearing_info *cinfo) +{ + struct bgp_clearing_dest *destinfo; + struct bgp_dest *dest = NULL; + + destinfo = bgp_clearing_destlist_pop(&cinfo->destlist); + if (destinfo) { + dest = destinfo->dest; + XFREE(MTYPE_CLEARING_BATCH, destinfo); + } + + return dest; +} + +/* If a clearing batch is available for 'peer', add it and return 'true', + * else return 'false'. + */ +bool bgp_clearing_batch_add_peer(struct bgp *bgp, struct peer *peer) +{ + struct bgp_clearing_info *cinfo; + + cinfo = bgp_clearing_info_first(&bgp->clearing_list); + + if (cinfo && CHECK_FLAG(cinfo->flags, BGP_CLEARING_INFO_FLAG_OPEN)) { + if (!CHECK_FLAG(peer->flags, PEER_FLAG_CLEARING_BATCH)) { + /* Add a peer ref */ + peer_lock(peer); + + bgp_clearing_hash_add(&cinfo->peers, peer); + SET_FLAG(peer->flags, PEER_FLAG_CLEARING_BATCH); + } + return true; + } + + return false; +} + +/* + * Task callback in the main pthread to handle socket errors + * encountered in the io pthread. We avoid having the io pthread try + * to enqueue fsm events or mess with the peer struct. + */ + +/* TODO -- should this be configurable? */ +/* Max number of peers to process without rescheduling */ +#define BGP_CONN_ERROR_DEQUEUE_MAX 10 + +static void bgp_process_conn_error(struct event *event) +{ + struct bgp *bgp; + struct peer *peer; + struct peer_connection *connection; + int counter = 0; + size_t list_count = 0; + bool more_p = false; + + bgp = EVENT_ARG(event); + + frr_with_mutex (&bgp->peer_errs_mtx) { + connection = bgp_peer_conn_errlist_pop(&bgp->peer_conn_errlist); + + list_count = + bgp_peer_conn_errlist_count(&bgp->peer_conn_errlist); + } + + /* If we have multiple peers with errors, try to batch some + * clearing work. + */ + if (list_count > 0) + bgp_clearing_batch_begin(bgp); + + /* Dequeue peers from the error list */ + while (connection != NULL) { + peer = connection->peer; + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s [Event] BGP error %d on fd %d", + peer->host, connection->connection_errcode, + connection->fd); + + /* Closed connection or error on the socket */ + if (peer_established(connection)) { + if ((CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART) + || CHECK_FLAG(peer->flags, + PEER_FLAG_GRACEFUL_RESTART_HELPER)) + && CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) { + peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; + SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); + } else + peer->last_reset = PEER_DOWN_CLOSE_SESSION; + } + + /* No need for keepalives, if enabled */ + bgp_keepalives_off(peer->connection); + + /* Drive into state-machine changes */ + bgp_event_update(connection, connection->connection_errcode); + + counter++; + if (counter >= BGP_CONN_ERROR_DEQUEUE_MAX) + break; + + connection = bgp_dequeue_conn_err(bgp, &more_p); + } + + /* Reschedule event if necessary */ + if (more_p) + bgp_conn_err_reschedule(bgp); + + /* Done with a clearing batch */ + if (list_count > 0) + bgp_clearing_batch_end(bgp); + + if (bgp_debug_neighbor_events(NULL)) + zlog_debug("%s: dequeued and processed %d peers", __func__, + counter); +} + +/* + * Enqueue a connection with an error to be handled in the main pthread; + * this is called from the io pthread. + */ +int bgp_enqueue_conn_err(struct bgp *bgp, struct peer_connection *connection, + int errcode) +{ + frr_with_mutex (&bgp->peer_errs_mtx) { + connection->connection_errcode = errcode; + + /* Careful not to double-enqueue */ + if (!bgp_peer_conn_errlist_anywhere(connection)) { + bgp_peer_conn_errlist_add_tail(&bgp->peer_conn_errlist, + connection); + } + } + /* Ensure an event is scheduled */ + event_add_event(bm->master, bgp_process_conn_error, bgp, 0, + &bgp->t_conn_errors); + return 0; +} + +/* + * Dequeue a connection that encountered a connection error; signal whether there + * are more queued peers. + */ +struct peer_connection *bgp_dequeue_conn_err(struct bgp *bgp, bool *more_p) +{ + struct peer_connection *connection = NULL; + bool more = false; + + frr_with_mutex (&bgp->peer_errs_mtx) { + connection = bgp_peer_conn_errlist_pop(&bgp->peer_conn_errlist); + + if (bgp_peer_conn_errlist_const_first( + &bgp->peer_conn_errlist) != NULL) + more = true; + } + + if (more_p) + *more_p = more; + + return connection; +} + +/* + * Reschedule the connection error event - probably after processing + * some of the peers on the list. + */ +void bgp_conn_err_reschedule(struct bgp *bgp) +{ + event_add_event(bm->master, bgp_process_conn_error, bgp, 0, + &bgp->t_conn_errors); +} + printfrr_ext_autoreg_p("BP", printfrr_bp); static ssize_t printfrr_bp(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index f123188ae8c4..843e4b405c42 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -375,6 +375,55 @@ struct as_confed { struct bgp_mplsvpn_nh_label_bind_cache; PREDECL_RBTREE_UNIQ(bgp_mplsvpn_nh_label_bind_cache); +/* List of peers that have connection errors in the io pthread */ +PREDECL_DLIST(bgp_peer_conn_errlist); + +/* List of info about peers that are being cleared from BGP RIBs in a batch */ +PREDECL_DLIST(bgp_clearing_info); + +/* Hash of peers in clearing info object */ +PREDECL_HASH(bgp_clearing_hash); + +/* List of dests that need to be processed in a clearing batch */ +PREDECL_LIST(bgp_clearing_destlist); + +struct bgp_clearing_dest { + struct bgp_dest *dest; + struct bgp_clearing_destlist_item link; +}; + +/* Info about a batch of peers that need to be cleared from the RIB. + * If many peers need to be cleared, we process them in batches, taking + * one walk through the RIB for each batch. This is only used for "all" + * afi/safis, typically when processing peer connection errors. + */ +struct bgp_clearing_info { + /* Owning bgp instance */ + struct bgp *bgp; + + /* Hash of peers */ + struct bgp_clearing_hash_head peers; + + /* Flags */ + uint32_t flags; + + /* List of dests - wrapped by a small wrapper struct */ + struct bgp_clearing_destlist_head destlist; + + /* Event to schedule/reschedule processing */ + struct event *t_sched; + + /* TODO -- id, serial number, for debugging/logging? */ + + /* TODO -- info for rescheduling the RIB walk? future? */ + + /* Linkage for list of batches per bgp */ + struct bgp_clearing_info_item link; +}; + +/* Batch is open, new peers can be added */ +#define BGP_CLEARING_INFO_FLAG_OPEN (1 << 0) + /* BGP instance structure. */ struct bgp { /* AS number of this BGP instance. */ @@ -854,6 +903,21 @@ struct bgp { uint16_t tcp_keepalive_intvl; uint16_t tcp_keepalive_probes; + /* List of peers that have connection errors in the IO pthread */ + struct bgp_peer_conn_errlist_head peer_conn_errlist; + + /* Mutex that guards the connection-errors list */ + pthread_mutex_t peer_errs_mtx; + + /* Event indicating that there have been connection errors; this + * is typically signalled in the IO pthread; it's handled in the + * main pthread. + */ + struct event *t_conn_errors; + + /* List of batches of peers being cleared from BGP RIBs */ + struct bgp_clearing_info_head clearing_list; + struct timeval ebgprequirespolicywarning; #define FIFTEENMINUTE2USEC (int64_t)15 * 60 * 1000000 @@ -1229,10 +1293,15 @@ struct peer_connection { struct event *t_routeadv; struct event *t_process_packet; - struct event *t_process_packet_error; struct event *t_stop_with_notify; + /* Linkage for list connections with errors, from IO pthread */ + struct bgp_peer_conn_errlist_item conn_err_link; + + /* Connection error code */ + uint16_t connection_errcode; + union sockunion su; #define BGP_CONNECTION_SU_UNSPEC(connection) \ (connection->su.sa.sa_family == AF_UNSPEC) @@ -1520,6 +1589,8 @@ struct peer { #define PEER_FLAG_AS_LOOP_DETECTION (1ULL << 38) /* as path loop detection */ #define PEER_FLAG_EXTENDED_LINK_BANDWIDTH (1ULL << 39) #define PEER_FLAG_DUAL_AS (1ULL << 40) +/* Peer is part of a batch clearing its routes */ +#define PEER_FLAG_CLEARING_BATCH (1ULL << 41) /* *GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART @@ -1916,6 +1987,9 @@ struct peer { /* Add-Path Paths-Limit */ struct addpath_paths_limit addpath_paths_limit[AFI_MAX][SAFI_MAX]; + /* Linkage for hash of clearing peers being cleared in a batch */ + struct bgp_clearing_hash_item clear_hash_link; + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(peer); @@ -2552,6 +2626,11 @@ void bgp_gr_apply_running_config(void); int bgp_global_gr_init(struct bgp *bgp); int bgp_peer_gr_init(struct peer *peer); +/* APIs for the per-bgp peer connection error list */ +int bgp_enqueue_conn_err(struct bgp *bgp, struct peer_connection *connection, + int errcode); +struct peer_connection *bgp_dequeue_conn_err(struct bgp *bgp, bool *more_p); +void bgp_conn_err_reschedule(struct bgp *bgp); #define BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(_bgp, _peer_list) \ do { \ @@ -2880,6 +2959,23 @@ extern void srv6_function_free(struct bgp_srv6_function *func); extern void bgp_session_reset_safe(struct peer *peer, struct listnode **nnode); +/* If a clearing batch is available for 'peer', add it and return 'true', + * else return 'false'. + */ +bool bgp_clearing_batch_add_peer(struct bgp *bgp, struct peer *peer); +/* Add a prefix/dest to a clearing batch */ +void bgp_clearing_batch_add_dest(struct bgp_clearing_info *cinfo, + struct bgp_dest *dest); +/* Check whether a dest's peer is relevant to a clearing batch */ +bool bgp_clearing_batch_check_peer(struct bgp_clearing_info *cinfo, + const struct peer *peer); +/* Check whether a clearing batch has any dests to process */ +bool bgp_clearing_batch_dests_present(struct bgp_clearing_info *cinfo); +/* Returns the next dest for batch clear processing */ +struct bgp_dest *bgp_clearing_batch_next_dest(struct bgp_clearing_info *cinfo); +/* Done with a peer clearing batch; deal with refcounts, free memory */ +void bgp_clearing_batch_completed(struct bgp_clearing_info *cinfo); + #ifdef _FRR_ATTRIBUTE_PRINTFRR /* clang-format off */ #pragma FRR printfrr_ext "%pBP" (struct peer *) diff --git a/tests/topotests/bgp_peer_shut/__init__.py b/tests/topotests/bgp_peer_shut/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_peer_shut/bgp-peer-shut.dot b/tests/topotests/bgp_peer_shut/bgp-peer-shut.dot new file mode 100644 index 000000000000..290b31509df4 --- /dev/null +++ b/tests/topotests/bgp_peer_shut/bgp-peer-shut.dot @@ -0,0 +1,206 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph ospf_ecmp_iBGP_topo1 { + label="bgp peer shut - eBGP peer shut"; + labelloc="t"; + + # Routers + r1 [ + label="r1\nrtr-id 10.0.255.1/32", + shape=doubleoctagon, + fillcolor="#f08080", + style=filled, + ]; + + # 4 Switches for eBGP Peers + s1 [ + label="s1\n10.0.1.0/24", + shape=oval, + fillcolor="#d0e0d0", + style=filled, + ]; + s2 [ + label="s2\n10.0.2.0/24", + shape=oval, + fillcolor="#d0e0d0", + style=filled, + ]; + s3 [ + label="s3\n10.0.3.0/24", + shape=oval, + fillcolor="#d0e0d0", + style=filled, + ]; + s4 [ + label="s4\n10.0.4.0/24", + shape=oval, + fillcolor="#d0e0d0", + style=filled, + ]; + + # 20 ExaBGP Peers AS 101...120 + peer1 [ + label="eBGP peer1\nAS99\nrtr-id 10.0.1.101/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer2 [ + label="eBGP peer2\nAS99\nrtr-id 10.0.1.102/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer3 [ + label="eBGP peer3\nAS99\nrtr-id 10.0.1.103/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer4 [ + label="eBGP peer4\nAS99\nrtr-id 10.0.1.104/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer5 [ + label="eBGP peer5\nAS99\nrtr-id 10.0.1.105/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer6 [ + label="eBGP peer6\nAS99\nrtr-id 10.0.2.106/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer7 [ + label="eBGP peer7\nAS99\nrtr-id 10.0.2.107/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer8 [ + label="eBGP peer8\nAS99\nrtr-id 10.0.2.108/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer9 [ + label="eBGP peer9\nAS99\nrtr-id 10.0.2.109/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer10 [ + label="eBGP peer10\nAS99\nrtr-id 10.0.2.110/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer11 [ + label="eBGP peer11\nAS111\nrtr-id 10.0.3.111/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer12 [ + label="eBGP peer12\nAS112\nrtr-id 10.0.3.112/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer13 [ + label="eBGP peer13\nAS113\nrtr-id 10.0.3.113/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer14 [ + label="eBGP peer14\nAS114\nrtr-id 10.0.3.114/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer15 [ + label="eBGP peer15\nAS115\nrtr-id 10.0.3.115/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer16 [ + label="eBGP peer16\nAS116\nrtr-id 10.0.4.116/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer17 [ + label="eBGP peer17\nAS117\nrtr-id 10.0.4.117/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer18 [ + label="eBGP peer18\nAS118\nrtr-id 10.0.4.118/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer19 [ + label="eBGP peer19\nAS119\nrtr-id 10.0.4.119/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + peer20 [ + label="eBGP peer20\nAS120\nrtr-id 10.0.4.120/32", + shape=rectangle, + fillcolor="#eee3d3", + style=filled, + ]; + + # Connections + r1 -- s1 [label="eth0\n.1"]; + r1 -- s2 [label="eth1\n.1"]; + r1 -- s3 [label="eth2\n.1"]; + r1 -- s4 [label="eth3\n.1"]; + + peer1 -- s1 [label="eth0\n.101"]; + peer2 -- s1 [label="eth0\n.102"]; + peer3 -- s1 [label="eth0\n.103"]; + peer4 -- s1 [label="eth0\n.104"]; + peer5 -- s1 [label="eth0\n.105"]; + peer6 -- s2 [label="eth0\n.106"]; + peer7 -- s2 [label="eth0\n.107"]; + peer8 -- s2 [label="eth0\n.108"]; + peer9 -- s2 [label="eth0\n.109"]; + peer10 -- s2 [label="eth0\n.110"]; + peer11 -- s3 [label="eth0\n.111"]; + peer12 -- s3 [label="eth0\n.112"]; + peer13 -- s3 [label="eth0\n.113"]; + peer14 -- s3 [label="eth0\n.114"]; + peer15 -- s3 [label="eth0\n.115"]; + peer16 -- s4 [label="eth0\n.116"]; + peer17 -- s4 [label="eth0\n.117"]; + peer18 -- s4 [label="eth0\n.118"]; + peer19 -- s4 [label="eth0\n.119"]; + peer20 -- s4 [label="eth0\n.120"]; + + # Arrange network to make cleaner diagram + { rank=same peer1 peer2 peer3 peer4 peer5 } -- s1 -- { rank=same peer6 peer7 peer8 peer9 peer10 } -- s2 + -- { rank=same peer11 peer12 peer13 peer14 peer15 } -- s3 -- { rank=same peer16 peer17 peer18 peer19 peer20 } -- s4 + -- { rank=same r1 } [style=invis] +} diff --git a/tests/topotests/bgp_peer_shut/bgp-peer-shut.pdf b/tests/topotests/bgp_peer_shut/bgp-peer-shut.pdf new file mode 100644 index 000000000000..fff66bb39bcf Binary files /dev/null and b/tests/topotests/bgp_peer_shut/bgp-peer-shut.pdf differ diff --git a/tests/topotests/bgp_peer_shut/exabgp.env b/tests/topotests/bgp_peer_shut/exabgp.env new file mode 100644 index 000000000000..ec978c66e76e --- /dev/null +++ b/tests/topotests/bgp_peer_shut/exabgp.env @@ -0,0 +1,55 @@ + +[exabgp.api] +ack = false +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' +##daemonize = false + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_peer_shut/peer1/exa-send.py b/tests/topotests/bgp_peer_shut/peer1/exa-send.py new file mode 100755 index 000000000000..1a8e6bfe2ffd --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer1/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_peer_shut/peer1/exabgp.cfg b/tests/topotests/bgp_peer_shut/peer1/exabgp.cfg new file mode 100644 index 000000000000..97a024ce9620 --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer1/exabgp.cfg @@ -0,0 +1,18 @@ +process announce-routes { + run /etc/exabgp/exa-send.py 1 10; + encoder text; +} + +process receive-routes { + run /etc/exabgp/exa-receive.py 1; + encoder text; +} + +neighbor 10.0.1.1 { + router-id 10.0.1.101; + local-address 10.0.1.101; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} +} diff --git a/tests/topotests/bgp_peer_shut/peer10/exa-send.py b/tests/topotests/bgp_peer_shut/peer10/exa-send.py new file mode 100755 index 000000000000..1a8e6bfe2ffd --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer10/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_peer_shut/peer10/exabgp.cfg b/tests/topotests/bgp_peer_shut/peer10/exabgp.cfg new file mode 100644 index 000000000000..e318162a1faf --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer10/exabgp.cfg @@ -0,0 +1,18 @@ +process announce-routes { + run /etc/exabgp/exa-send.py 10 10; + encoder text; +} + +process receive-routes { + run /etc/exabgp/exa-receive.py 10; + encoder text; +} + +neighbor 10.0.2.1 { + router-id 10.0.2.110; + local-address 10.0.2.110; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} +} diff --git a/tests/topotests/bgp_peer_shut/peer11/exa-send.py b/tests/topotests/bgp_peer_shut/peer11/exa-send.py new file mode 100755 index 000000000000..1a8e6bfe2ffd --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer11/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_peer_shut/peer11/exabgp.cfg b/tests/topotests/bgp_peer_shut/peer11/exabgp.cfg new file mode 100644 index 000000000000..ea5bdcce804a --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer11/exabgp.cfg @@ -0,0 +1,18 @@ +process announce-routes { + run /etc/exabgp/exa-send.py 11 10; + encoder text; +} + +process receive-routes { + run /etc/exabgp/exa-receive.py 11; + encoder text; +} + +neighbor 10.0.3.1 { + router-id 10.0.3.111; + local-address 10.0.3.111; + local-as 111; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} +} diff --git a/tests/topotests/bgp_peer_shut/peer12/exa-send.py b/tests/topotests/bgp_peer_shut/peer12/exa-send.py new file mode 100755 index 000000000000..1a8e6bfe2ffd --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer12/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_peer_shut/peer12/exabgp.cfg b/tests/topotests/bgp_peer_shut/peer12/exabgp.cfg new file mode 100644 index 000000000000..81fb5035f756 --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer12/exabgp.cfg @@ -0,0 +1,18 @@ +process announce-routes { + run /etc/exabgp/exa-send.py 12 10; + encoder text; +} + +process receive-routes { + run /etc/exabgp/exa-receive.py 12; + encoder text; +} + +neighbor 10.0.3.1 { + router-id 10.0.3.112; + local-address 10.0.3.112; + local-as 112; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} +} diff --git a/tests/topotests/bgp_peer_shut/peer13/exa-send.py b/tests/topotests/bgp_peer_shut/peer13/exa-send.py new file mode 100755 index 000000000000..1a8e6bfe2ffd --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer13/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_peer_shut/peer13/exabgp.cfg b/tests/topotests/bgp_peer_shut/peer13/exabgp.cfg new file mode 100644 index 000000000000..40078411ec10 --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer13/exabgp.cfg @@ -0,0 +1,18 @@ +process announce-routes { + run /etc/exabgp/exa-send.py 13 10; + encoder text; +} + +process receive-routes { + run /etc/exabgp/exa-receive.py 13; + encoder text; +} + +neighbor 10.0.3.1 { + router-id 10.0.3.113; + local-address 10.0.3.113; + local-as 113; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} +} diff --git a/tests/topotests/bgp_peer_shut/peer14/exa-send.py b/tests/topotests/bgp_peer_shut/peer14/exa-send.py new file mode 100755 index 000000000000..1a8e6bfe2ffd --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer14/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_peer_shut/peer14/exabgp.cfg b/tests/topotests/bgp_peer_shut/peer14/exabgp.cfg new file mode 100644 index 000000000000..afb8741636d1 --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer14/exabgp.cfg @@ -0,0 +1,18 @@ +process announce-routes { + run /etc/exabgp/exa-send.py 14 10; + encoder text; +} + +process receive-routes { + run /etc/exabgp/exa-receive.py 14; + encoder text; +} + +neighbor 10.0.3.1 { + router-id 10.0.3.114; + local-address 10.0.3.114; + local-as 114; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} +} diff --git a/tests/topotests/bgp_peer_shut/peer15/exa-send.py b/tests/topotests/bgp_peer_shut/peer15/exa-send.py new file mode 100755 index 000000000000..1a8e6bfe2ffd --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer15/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_peer_shut/peer15/exabgp.cfg b/tests/topotests/bgp_peer_shut/peer15/exabgp.cfg new file mode 100644 index 000000000000..9a4ca7f695b8 --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer15/exabgp.cfg @@ -0,0 +1,18 @@ +process announce-routes { + run /etc/exabgp/exa-send.py 15 10; + encoder text; +} + +process receive-routes { + run /etc/exabgp/exa-receive.py 15; + encoder text; +} + +neighbor 10.0.3.1 { + router-id 10.0.3.115; + local-address 10.0.3.115; + local-as 115; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} +} diff --git a/tests/topotests/bgp_peer_shut/peer16/exa-send.py b/tests/topotests/bgp_peer_shut/peer16/exa-send.py new file mode 100755 index 000000000000..1a8e6bfe2ffd --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer16/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_peer_shut/peer16/exabgp.cfg b/tests/topotests/bgp_peer_shut/peer16/exabgp.cfg new file mode 100644 index 000000000000..a0bb88a3f3a3 --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer16/exabgp.cfg @@ -0,0 +1,18 @@ +process announce-routes { + run /etc/exabgp/exa-send.py 16 10; + encoder text; +} + +process receive-routes { + run /etc/exabgp/exa-receive.py 16; + encoder text; +} + +neighbor 10.0.4.1 { + router-id 10.0.4.116; + local-address 10.0.4.116; + local-as 116; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} +} diff --git a/tests/topotests/bgp_peer_shut/peer17/exa-send.py b/tests/topotests/bgp_peer_shut/peer17/exa-send.py new file mode 100755 index 000000000000..1a8e6bfe2ffd --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer17/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_peer_shut/peer17/exabgp.cfg b/tests/topotests/bgp_peer_shut/peer17/exabgp.cfg new file mode 100644 index 000000000000..870d33d45599 --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer17/exabgp.cfg @@ -0,0 +1,18 @@ +process announce-routes { + run /etc/exabgp/exa-send.py 17 10; + encoder text; +} + +process receive-routes { + run /etc/exabgp/exa-receive.py 17; + encoder text; +} + +neighbor 10.0.4.1 { + router-id 10.0.4.117; + local-address 10.0.4.117; + local-as 117; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} +} diff --git a/tests/topotests/bgp_peer_shut/peer18/exa-send.py b/tests/topotests/bgp_peer_shut/peer18/exa-send.py new file mode 100755 index 000000000000..1a8e6bfe2ffd --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer18/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_peer_shut/peer18/exabgp.cfg b/tests/topotests/bgp_peer_shut/peer18/exabgp.cfg new file mode 100644 index 000000000000..c5a1ca62e416 --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer18/exabgp.cfg @@ -0,0 +1,18 @@ +process announce-routes { + run /etc/exabgp/exa-send.py 18 10; + encoder text; +} + +process receive-routes { + run /etc/exabgp/exa-receive.py 18; + encoder text; +} + +neighbor 10.0.4.1 { + router-id 10.0.4.118; + local-address 10.0.4.118; + local-as 118; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} +} diff --git a/tests/topotests/bgp_peer_shut/peer19/exa-send.py b/tests/topotests/bgp_peer_shut/peer19/exa-send.py new file mode 100755 index 000000000000..1a8e6bfe2ffd --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer19/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_peer_shut/peer19/exabgp.cfg b/tests/topotests/bgp_peer_shut/peer19/exabgp.cfg new file mode 100644 index 000000000000..f662ccf93e2b --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer19/exabgp.cfg @@ -0,0 +1,18 @@ +process announce-routes { + run /etc/exabgp/exa-send.py 19 10; + encoder text; +} + +process receive-routes { + run /etc/exabgp/exa-receive.py 19; + encoder text; +} + +neighbor 10.0.4.1 { + router-id 10.0.4.119; + local-address 10.0.4.119; + local-as 119; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} +} diff --git a/tests/topotests/bgp_peer_shut/peer2/exa-send.py b/tests/topotests/bgp_peer_shut/peer2/exa-send.py new file mode 100755 index 000000000000..1a8e6bfe2ffd --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer2/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_peer_shut/peer2/exabgp.cfg b/tests/topotests/bgp_peer_shut/peer2/exabgp.cfg new file mode 100644 index 000000000000..673d6ec0eef9 --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer2/exabgp.cfg @@ -0,0 +1,18 @@ +process announce-routes { + run /etc/exabgp/exa-send.py 2 10; + encoder text; +} + +process receive-routes { + run /etc/exabgp/exa-receive.py 2; + encoder text; +} + +neighbor 10.0.1.1 { + router-id 10.0.1.102; + local-address 10.0.1.102; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} +} diff --git a/tests/topotests/bgp_peer_shut/peer20/exa-send.py b/tests/topotests/bgp_peer_shut/peer20/exa-send.py new file mode 100755 index 000000000000..1a8e6bfe2ffd --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer20/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_peer_shut/peer20/exabgp.cfg b/tests/topotests/bgp_peer_shut/peer20/exabgp.cfg new file mode 100644 index 000000000000..5ea2c91de142 --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer20/exabgp.cfg @@ -0,0 +1,18 @@ +process announce-routes { + run /etc/exabgp/exa-send.py 20 10; + encoder text; +} + +process receive-routes { + run /etc/exabgp/exa-receive.py 20; + encoder text; +} + +neighbor 10.0.4.1 { + router-id 10.0.4.120; + local-address 10.0.4.120; + local-as 120; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} +} diff --git a/tests/topotests/bgp_peer_shut/peer3/exa-send.py b/tests/topotests/bgp_peer_shut/peer3/exa-send.py new file mode 100755 index 000000000000..1a8e6bfe2ffd --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer3/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_peer_shut/peer3/exabgp.cfg b/tests/topotests/bgp_peer_shut/peer3/exabgp.cfg new file mode 100644 index 000000000000..47a25cab223c --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer3/exabgp.cfg @@ -0,0 +1,18 @@ +process announce-routes { + run /etc/exabgp/exa-send.py 3 10; + encoder text; +} + +process receive-routes { + run /etc/exabgp/exa-receive.py 3; + encoder text; +} + +neighbor 10.0.1.1 { + router-id 10.0.1.103; + local-address 10.0.1.103; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} +} diff --git a/tests/topotests/bgp_peer_shut/peer4/exa-send.py b/tests/topotests/bgp_peer_shut/peer4/exa-send.py new file mode 100755 index 000000000000..1a8e6bfe2ffd --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer4/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_peer_shut/peer4/exabgp.cfg b/tests/topotests/bgp_peer_shut/peer4/exabgp.cfg new file mode 100644 index 000000000000..376ac5f37dca --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer4/exabgp.cfg @@ -0,0 +1,18 @@ +process announce-routes { + run /etc/exabgp/exa-send.py 4 10; + encoder text; +} + +process receive-routes { + run /etc/exabgp/exa-receive.py 4; + encoder text; +} + +neighbor 10.0.1.1 { + router-id 10.0.1.104; + local-address 10.0.1.104; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} +} diff --git a/tests/topotests/bgp_peer_shut/peer5/exa-send.py b/tests/topotests/bgp_peer_shut/peer5/exa-send.py new file mode 100755 index 000000000000..1a8e6bfe2ffd --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer5/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_peer_shut/peer5/exabgp.cfg b/tests/topotests/bgp_peer_shut/peer5/exabgp.cfg new file mode 100644 index 000000000000..878d728de645 --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer5/exabgp.cfg @@ -0,0 +1,18 @@ +process announce-routes { + run /etc/exabgp/exa-send.py 5 10; + encoder text; +} + +process receive-routes { + run /etc/exabgp/exa-receive.py 5; + encoder text; +} + +neighbor 10.0.1.1 { + router-id 10.0.1.105; + local-address 10.0.1.105; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} +} diff --git a/tests/topotests/bgp_peer_shut/peer6/exa-send.py b/tests/topotests/bgp_peer_shut/peer6/exa-send.py new file mode 100755 index 000000000000..1a8e6bfe2ffd --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer6/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_peer_shut/peer6/exabgp.cfg b/tests/topotests/bgp_peer_shut/peer6/exabgp.cfg new file mode 100644 index 000000000000..fc702674cf48 --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer6/exabgp.cfg @@ -0,0 +1,18 @@ +process announce-routes { + run /etc/exabgp/exa-send.py 6 10; + encoder text; +} + +process receive-routes { + run /etc/exabgp/exa-receive.py 6; + encoder text; +} + +neighbor 10.0.2.1 { + router-id 10.0.2.106; + local-address 10.0.2.106; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} +} diff --git a/tests/topotests/bgp_peer_shut/peer7/exa-send.py b/tests/topotests/bgp_peer_shut/peer7/exa-send.py new file mode 100755 index 000000000000..1a8e6bfe2ffd --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer7/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_peer_shut/peer7/exabgp.cfg b/tests/topotests/bgp_peer_shut/peer7/exabgp.cfg new file mode 100644 index 000000000000..09827a89024b --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer7/exabgp.cfg @@ -0,0 +1,18 @@ +process announce-routes { + run /etc/exabgp/exa-send.py 7 10; + encoder text; +} + +process receive-routes { + run /etc/exabgp/exa-receive.py 7; + encoder text; +} + +neighbor 10.0.2.1 { + router-id 10.0.2.107; + local-address 10.0.2.107; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} +} diff --git a/tests/topotests/bgp_peer_shut/peer8/exa-send.py b/tests/topotests/bgp_peer_shut/peer8/exa-send.py new file mode 100755 index 000000000000..1a8e6bfe2ffd --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer8/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_peer_shut/peer8/exabgp.cfg b/tests/topotests/bgp_peer_shut/peer8/exabgp.cfg new file mode 100644 index 000000000000..37c01dabb790 --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer8/exabgp.cfg @@ -0,0 +1,18 @@ +process announce-routes { + run /etc/exabgp/exa-send.py 8 10; + encoder text; +} + +process receive-routes { + run /etc/exabgp/exa-receive.py 8; + encoder text; +} + +neighbor 10.0.2.1 { + router-id 10.0.2.108; + local-address 10.0.2.108; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} +} diff --git a/tests/topotests/bgp_peer_shut/peer9/exa-send.py b/tests/topotests/bgp_peer_shut/peer9/exa-send.py new file mode 100755 index 000000000000..1a8e6bfe2ffd --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer9/exa-send.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +""" +exa-send.py: Send a few testroutes with ExaBGP +""" + +from sys import stdout, argv +from time import sleep + +sleep(5) + +# 1st arg is peer number +# 2nd arg is number of routes to send +peer = int(argv[1]) +numRoutes = int(argv[2]) +if peer <= 10: + asnum = 99 +else: + asnum = peer + 100 + +# Announce numRoutes equal routes per PE - different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n" + % (i, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100) + ) + stdout.flush() + +# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS +for i in range(0, numRoutes): + stdout.write( + "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum) + ) + stdout.flush() + +# Announce 2 different route per peer +stdout.write( + "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100) +) +stdout.write( + "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n" + % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum) +) +stdout.flush() + +# Loop endlessly to allow ExaBGP to continue running +while True: + sleep(1) diff --git a/tests/topotests/bgp_peer_shut/peer9/exabgp.cfg b/tests/topotests/bgp_peer_shut/peer9/exabgp.cfg new file mode 100644 index 000000000000..8fb5c58b6b97 --- /dev/null +++ b/tests/topotests/bgp_peer_shut/peer9/exabgp.cfg @@ -0,0 +1,18 @@ +process announce-routes { + run /etc/exabgp/exa-send.py 9 10; + encoder text; +} + +process receive-routes { + run /etc/exabgp/exa-receive.py 9; + encoder text; +} + +neighbor 10.0.2.1 { + router-id 10.0.2.109; + local-address 10.0.2.109; + local-as 99; + peer-as 100; + capability {graceful-restart;} + api {processes [ announce-routes, receive-routes ];} +} diff --git a/tests/topotests/bgp_peer_shut/r1/bgpd.conf b/tests/topotests/bgp_peer_shut/r1/bgpd.conf new file mode 100644 index 000000000000..49981ac58968 --- /dev/null +++ b/tests/topotests/bgp_peer_shut/r1/bgpd.conf @@ -0,0 +1,51 @@ +! +hostname r1 +log file bgpd.log +! +router bgp 100 + bgp router-id 10.0.255.1 + bgp bestpath as-path multipath-relax + no bgp ebgp-requires-policy + neighbor 10.0.1.101 remote-as 99 + neighbor 10.0.1.101 timers 3 10 + neighbor 10.0.1.102 remote-as 99 + neighbor 10.0.1.102 timers 3 10 + neighbor 10.0.1.103 remote-as 99 + neighbor 10.0.1.103 timers 3 10 + neighbor 10.0.1.104 remote-as 99 + neighbor 10.0.1.104 timers 3 10 + neighbor 10.0.1.105 remote-as 99 + neighbor 10.0.1.105 timers 3 10 + neighbor 10.0.2.106 remote-as 99 + neighbor 10.0.2.106 timers 3 10 + neighbor 10.0.2.107 remote-as 99 + neighbor 10.0.2.107 timers 3 10 + neighbor 10.0.2.108 remote-as 99 + neighbor 10.0.2.108 timers 3 10 + neighbor 10.0.2.109 remote-as 99 + neighbor 10.0.2.109 timers 3 10 + neighbor 10.0.2.110 remote-as 99 + neighbor 10.0.2.110 timers 3 10 + neighbor 10.0.3.111 remote-as 111 + neighbor 10.0.3.111 timers 3 10 + neighbor 10.0.3.112 remote-as 112 + neighbor 10.0.3.112 timers 3 10 + neighbor 10.0.3.113 remote-as 113 + neighbor 10.0.3.113 timers 3 10 + neighbor 10.0.3.114 remote-as 114 + neighbor 10.0.3.114 timers 3 10 + neighbor 10.0.3.115 remote-as 115 + neighbor 10.0.3.115 timers 3 10 + neighbor 10.0.4.116 remote-as 116 + neighbor 10.0.4.116 timers 3 10 + neighbor 10.0.4.117 remote-as 117 + neighbor 10.0.4.117 timers 3 10 + neighbor 10.0.4.118 remote-as 118 + neighbor 10.0.4.118 timers 3 10 + neighbor 10.0.4.119 remote-as 119 + neighbor 10.0.4.119 timers 3 10 + neighbor 10.0.4.120 remote-as 120 + neighbor 10.0.4.120 timers 3 10 + ! +! + diff --git a/tests/topotests/bgp_peer_shut/r1/summary.txt b/tests/topotests/bgp_peer_shut/r1/summary.txt new file mode 100644 index 000000000000..68de28a35be5 --- /dev/null +++ b/tests/topotests/bgp_peer_shut/r1/summary.txt @@ -0,0 +1,131 @@ +{ +"ipv4Unicast":{ + "routerId":"10.0.255.1", + "as":100, + "vrfName":"default", + "peerCount":20, + "peers":{ + "10.0.1.101":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.1.102":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.1.103":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.1.104":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.1.105":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.2.106":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.2.107":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.2.108":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.2.109":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.2.110":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.3.111":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.3.112":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.3.113":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.3.114":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.3.115":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.4.116":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.4.117":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.4.118":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.4.119":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + }, + "10.0.4.120":{ + "outq":0, + "inq":0, + "pfxRcd":42, + "state":"Established" + } + }, + "totalPeers":20 +} +} diff --git a/tests/topotests/bgp_peer_shut/r1/zebra.conf b/tests/topotests/bgp_peer_shut/r1/zebra.conf new file mode 100644 index 000000000000..77c76cd4907a --- /dev/null +++ b/tests/topotests/bgp_peer_shut/r1/zebra.conf @@ -0,0 +1,16 @@ +! +hostname r1 +log file zebra.log +! +interface r1-eth0 + ip address 10.0.1.1/24 +! +interface r1-eth1 + ip address 10.0.2.1/24 +! +interface r1-eth2 + ip address 10.0.3.1/24 +! +interface r1-eth3 + ip address 10.0.4.1/24 +! diff --git a/tests/topotests/bgp_peer_shut/test_bgp_peer_shut.py b/tests/topotests/bgp_peer_shut/test_bgp_peer_shut.py new file mode 100644 index 000000000000..17533b604747 --- /dev/null +++ b/tests/topotests/bgp_peer_shut/test_bgp_peer_shut.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: ISC + +# +# test_bgp_peer_shut.py +# Part of FRR Topology Tests +# +# Copyright (c) 2017, 2024 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_bgp_peer_shut.py: Test BGP peer shutdown +""" + +import json +import functools +import os +import sys +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + + +pytestmark = [pytest.mark.bgpd] + + +total_ebgp_peers = 20 + +##################################################### +# +# Network Topology Definition +# +##################################################### + + +def build_topo(tgen): + router = tgen.add_router("r1") + + # Setup Switches - 1 switch per 5 peering routers + for swNum in range(1, (total_ebgp_peers + 4) // 5 + 1): + switch = tgen.add_switch("s{}".format(swNum)) + switch.add_link(router) + + # Add 'total_ebgp_peers' number of eBGP ExaBGP neighbors + for peerNum in range(1, total_ebgp_peers + 1): + swNum = (peerNum - 1) // 5 + 1 + + peer_ip = "10.0.{}.{}".format(swNum, peerNum + 100) + peer_route = "via 10.0.{}.1".format(swNum) + peer = tgen.add_exabgp_peer( + "peer{}".format(peerNum), ip=peer_ip, defaultRoute=peer_route + ) + + switch = tgen.gears["s{}".format(swNum)] + switch.add_link(peer) + + +##################################################### +# +# Tests starting +# +##################################################### + + +def setup_module(module): + tgen = Topogen(build_topo, module.__name__) + tgen.start_topology() + + # Starting Routers + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + router.start() + + # Starting Hosts and init ExaBGP on each of them + topotest.sleep(10, "starting BGP on all {} peers".format(total_ebgp_peers)) + peer_list = tgen.exabgp_peers() + for pname, peer in peer_list.items(): + peer_dir = os.path.join(CWD, pname) + env_file = os.path.join(CWD, "exabgp.env") + peer.start(peer_dir, env_file) + logger.info(pname) + + +def teardown_module(module): + del module + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_convergence(): + "Test for BGP topology convergence" + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Expected result + router = tgen.gears["r1"] + reffile = os.path.join(CWD, "r1/summary.txt") + + expected = json.loads(open(reffile).read()) + + def _output_summary_cmp(router, cmd, data): + """ + Runs `cmd` that returns JSON data (normally the command ends + with 'json') and compare with `data` contents. + """ + output = router.vtysh_cmd(cmd, isjson=True) + return topotest.json_cmp(output, data) + + test_func = functools.partial( + _output_summary_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=1.0) + assertmsg = "BGP router network did not converge" + assert res is None, assertmsg + + +def test_bgp_peer_shut(): + "Test shutdown of all exabgp peers" + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("=== Checking for bgp routes ===") + + expect = { + "routerId": "10.0.255.1", + "routes": {}, + } + + for net in range(1, 5): + for subnet in range(0, 10): + netkey = "10.20{}.{}.0/24".format(net, subnet) + expect["routes"][netkey] = [] + for _ in range(0, 10): + peer = {"multipath": True, "valid": True} + expect["routes"][netkey].append(peer) + + test_func = functools.partial( + topotest.router_json_cmp, tgen.gears["r1"], "show ip bgp json", expect + ) + _, res = topotest.run_and_expect(test_func, None, count=40, wait=0.5) + assertmsg = 'expected multipath routes in "show ip bgp" output' + assert res is None, assertmsg + + logger.info("=== Stopping all dynamic peers ===") + + # Stop the dynamic peers + epeers = tgen.exabgp_peers() + for key in epeers: + epeers[key].stop() + + # for debugging + #tgen.cli() + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args))