diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index dd31a9b7cb..ce665feb4e 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -67,12 +67,14 @@ static const char *const bgp_event_str[] = { "BGP_Start", "BGP_Stop", "TCP_connection_open", + "TCP_connection_open_w_delay", "TCP_connection_closed", "TCP_connection_open_failed", "TCP_fatal_error", "ConnectRetry_timer_expired", "Hold_Timer_expired", "KeepAlive_timer_expired", + "DelayOpen_timer_expired", "Receive_OPEN_message", "Receive_KEEPALIVE_message", "Receive_UPDATE_message", @@ -92,6 +94,7 @@ int bgp_event(struct thread *); static int bgp_start_timer(struct thread *); static int bgp_connect_timer(struct thread *); static int bgp_holdtime_timer(struct thread *); +static int bgp_delayopen_timer(struct thread *); /* BGP FSM functions. */ static int bgp_start(struct peer *); @@ -174,10 +177,12 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) BGP_TIMER_OFF(peer->t_routeadv); BGP_TIMER_OFF(peer->t_connect); + BGP_TIMER_OFF(peer->t_delayopen); BGP_TIMER_OFF(peer->t_connect_check_r); BGP_TIMER_OFF(peer->t_connect_check_w); BGP_TIMER_OFF(from_peer->t_routeadv); BGP_TIMER_OFF(from_peer->t_connect); + BGP_TIMER_OFF(from_peer->t_delayopen); BGP_TIMER_OFF(from_peer->t_connect_check_r); BGP_TIMER_OFF(from_peer->t_connect_check_w); BGP_TIMER_OFF(from_peer->t_process_packet); @@ -233,6 +238,7 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) peer->v_holdtime = from_peer->v_holdtime; peer->v_keepalive = from_peer->v_keepalive; peer->v_routeadv = from_peer->v_routeadv; + peer->v_delayopen = from_peer->v_delayopen; peer->v_gr_restart = from_peer->v_gr_restart; peer->cap = from_peer->cap; status = peer->status; @@ -364,6 +370,7 @@ void bgp_timer_set(struct peer *peer) BGP_TIMER_OFF(peer->t_holdtime); bgp_keepalives_off(peer); BGP_TIMER_OFF(peer->t_routeadv); + BGP_TIMER_OFF(peer->t_delayopen); break; case Connect: @@ -371,8 +378,13 @@ void bgp_timer_set(struct peer *peer) status. Make sure start timer is off and connect timer is on. */ BGP_TIMER_OFF(peer->t_start); - BGP_TIMER_ON(peer->t_connect, bgp_connect_timer, - peer->v_connect); + if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER_DELAYOPEN)) + BGP_TIMER_ON(peer->t_connect, bgp_connect_timer, + (peer->v_delayopen + peer->v_connect)); + else + BGP_TIMER_ON(peer->t_connect, bgp_connect_timer, + peer->v_connect); + BGP_TIMER_OFF(peer->t_holdtime); bgp_keepalives_off(peer); BGP_TIMER_OFF(peer->t_routeadv); @@ -387,8 +399,13 @@ void bgp_timer_set(struct peer *peer) || CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)) { BGP_TIMER_OFF(peer->t_connect); } else { - BGP_TIMER_ON(peer->t_connect, bgp_connect_timer, - peer->v_connect); + if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER_DELAYOPEN)) + BGP_TIMER_ON( + peer->t_connect, bgp_connect_timer, + (peer->v_delayopen + peer->v_connect)); + else + BGP_TIMER_ON(peer->t_connect, bgp_connect_timer, + peer->v_connect); } BGP_TIMER_OFF(peer->t_holdtime); bgp_keepalives_off(peer); @@ -407,6 +424,7 @@ void bgp_timer_set(struct peer *peer) } bgp_keepalives_off(peer); BGP_TIMER_OFF(peer->t_routeadv); + BGP_TIMER_OFF(peer->t_delayopen); break; case OpenConfirm: @@ -425,6 +443,7 @@ void bgp_timer_set(struct peer *peer) bgp_keepalives_on(peer); } BGP_TIMER_OFF(peer->t_routeadv); + BGP_TIMER_OFF(peer->t_delayopen); break; case Established: @@ -432,6 +451,7 @@ void bgp_timer_set(struct peer *peer) off. */ BGP_TIMER_OFF(peer->t_start); BGP_TIMER_OFF(peer->t_connect); + BGP_TIMER_OFF(peer->t_delayopen); /* Same as OpenConfirm, if holdtime is zero then both holdtime and keepalive must be turned off. */ @@ -455,6 +475,7 @@ void bgp_timer_set(struct peer *peer) BGP_TIMER_OFF(peer->t_holdtime); bgp_keepalives_off(peer); BGP_TIMER_OFF(peer->t_routeadv); + BGP_TIMER_OFF(peer->t_delayopen); break; case BGP_STATUS_MAX: flog_err(EC_LIB_DEVELOPMENT, @@ -488,6 +509,10 @@ static int bgp_connect_timer(struct thread *thread) peer = THREAD_ARG(thread); + /* stop the DelayOpenTimer if it is running */ + if (peer->t_delayopen) + BGP_TIMER_OFF(peer->t_delayopen); + assert(!peer->t_write); assert(!peer->t_read); @@ -564,6 +589,23 @@ int bgp_routeadv_timer(struct thread *thread) return 0; } +/* RFC 4271 DelayOpenTimer */ +int bgp_delayopen_timer(struct thread *thread) +{ + struct peer *peer; + + peer = THREAD_ARG(thread); + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s [FSM] Timer (DelayOpentimer expire)", + peer->host); + + THREAD_VAL(thread) = DelayOpen_timer_expired; + bgp_event(thread); /* bgp_event unlocks peer */ + + return 0; +} + /* BGP Peer Down Cause */ const char *const peer_down_str[] = {"", "Router ID changed", @@ -1299,6 +1341,7 @@ int bgp_stop(struct peer *peer) BGP_TIMER_OFF(peer->t_connect); BGP_TIMER_OFF(peer->t_holdtime); BGP_TIMER_OFF(peer->t_routeadv); + BGP_TIMER_OFF(peer->t_delayopen); /* Clear input and output buffer. */ frr_with_mutex(&peer->io_mtx) { @@ -1357,6 +1400,12 @@ int bgp_stop(struct peer *peer) peer->v_holdtime = peer->bgp->default_holdtime; } + /* Reset DelayOpenTime */ + if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER_DELAYOPEN)) + peer->v_delayopen = peer->delayopen; + else + peer->v_delayopen = peer->bgp->default_delayopen; + peer->update_time = 0; /* Until we are sure that there is no problem about prefix count @@ -1469,7 +1518,10 @@ static int bgp_connect_check(struct thread *thread) /* When status is 0 then TCP connection is established. */ if (status == 0) { - BGP_EVENT_ADD(peer, TCP_connection_open); + if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER_DELAYOPEN)) + BGP_EVENT_ADD(peer, TCP_connection_open_w_delay); + else + BGP_EVENT_ADD(peer, TCP_connection_open); return 1; } else { if (bgp_debug_neighbor_events(peer)) @@ -1516,11 +1568,63 @@ static int bgp_connect_success(struct peer *peer) zlog_debug("%s passive open", peer->host); } + /* Send an open message */ bgp_open_send(peer); return 0; } +/* TCP connection open with RFC 4271 optional session attribute DelayOpen flag + * set. + */ +static int bgp_connect_success_w_delayopen(struct peer *peer) +{ + if (peer->fd < 0) { + flog_err(EC_BGP_CONNECT, "%s: peer's fd is negative value %d", + __func__, peer->fd); + bgp_stop(peer); + return -1; + } + + if (bgp_getsockname(peer) < 0) { + flog_err_sys(EC_LIB_SOCKET, + "%s: bgp_getsockname(): failed for peer %s, fd %d", + __func__, peer->host, peer->fd); + bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR, + bgp_fsm_error_subcode(peer->status)); + bgp_writes_on(peer); + return -1; + } + + bgp_reads_on(peer); + + if (bgp_debug_neighbor_events(peer)) { + char buf1[SU_ADDRSTRLEN]; + + if (!CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER)) + zlog_debug("%s open active, local address %s", + peer->host, + sockunion2str(peer->su_local, buf1, + SU_ADDRSTRLEN)); + else + zlog_debug("%s passive open", peer->host); + } + + /* set the DelayOpenTime to the inital value */ + peer->v_delayopen = peer->delayopen; + + /* Start the DelayOpenTimer if it is not already running */ + if (!peer->t_delayopen) + BGP_TIMER_ON(peer->t_delayopen, bgp_delayopen_timer, + peer->v_delayopen); + + if (bgp_debug_neighbor_events(peer)) + zlog_debug("%s [FSM] BGP OPEN message delayed for %d seconds", + peer->host, peer->delayopen); + + return 0; +} + /* TCP connect fail */ static int bgp_connect_fail(struct peer *peer) { @@ -1535,7 +1639,8 @@ static int bgp_connect_fail(struct peer *peer) } /* This function is the first starting point of all BGP connection. It - try to connect to remote peer with non-blocking IO. */ + * try to connect to remote peer with non-blocking IO. + */ int bgp_start(struct peer *peer) { int status; @@ -1633,6 +1738,7 @@ int bgp_start(struct peer *peer) zlog_debug( "%s [FSM] Connect immediately success, fd %d", peer->host, peer->fd); + BGP_EVENT_ADD(peer, TCP_connection_open); break; case connect_in_progress: @@ -1682,6 +1788,10 @@ static int bgp_reconnect(struct peer *peer) static int bgp_fsm_open(struct peer *peer) { + /* If DelayOpen is active, we may still need to send an open message */ + if ((peer->status == Connect) || (peer->status == Active)) + bgp_open_send(peer); + /* Send keepalive and make keepalive timer */ bgp_keepalive_send(peer); @@ -1709,6 +1819,21 @@ static int bgp_fsm_holdtime_expire(struct peer *peer) return bgp_stop_with_notify(peer, BGP_NOTIFY_HOLD_ERR, 0); } +/* RFC 4271 DelayOpenTimer_Expires event */ +static int bgp_fsm_delayopen_timer_expire(struct peer *peer) +{ + /* Stop the DelayOpenTimer */ + BGP_TIMER_OFF(peer->t_delayopen); + + /* Send open message to peer */ + bgp_open_send(peer); + + /* Set the HoldTimer to a large value (4 minutes) */ + peer->v_holdtime = 245; + + return 0; +} + /* Start the selection deferral timer thread for the specified AFI, SAFI */ static int bgp_start_deferral_timer(struct bgp *bgp, afi_t afi, safi_t safi, struct graceful_restart_info *gr_info) @@ -2089,12 +2214,14 @@ static const struct { {bgp_start, Connect}, /* BGP_Start */ {bgp_stop, Idle}, /* BGP_Stop */ {bgp_stop, Idle}, /* TCP_connection_open */ + {bgp_stop, Idle}, /* TCP_connection_open_w_delay */ {bgp_stop, Idle}, /* TCP_connection_closed */ {bgp_ignore, Idle}, /* TCP_connection_open_failed */ {bgp_stop, Idle}, /* TCP_fatal_error */ {bgp_ignore, Idle}, /* ConnectRetry_timer_expired */ {bgp_ignore, Idle}, /* Hold_Timer_expired */ {bgp_ignore, Idle}, /* KeepAlive_timer_expired */ + {bgp_ignore, Idle}, /* DelayOpen_timer_expired */ {bgp_ignore, Idle}, /* Receive_OPEN_message */ {bgp_ignore, Idle}, /* Receive_KEEPALIVE_message */ {bgp_ignore, Idle}, /* Receive_UPDATE_message */ @@ -2106,46 +2233,56 @@ static const struct { {bgp_ignore, Connect}, /* BGP_Start */ {bgp_stop, Idle}, /* BGP_Stop */ {bgp_connect_success, OpenSent}, /* TCP_connection_open */ + {bgp_connect_success_w_delayopen, + Connect}, /* TCP_connection_open_w_delay */ {bgp_stop, Idle}, /* TCP_connection_closed */ {bgp_connect_fail, Active}, /* TCP_connection_open_failed */ {bgp_connect_fail, Idle}, /* TCP_fatal_error */ {bgp_reconnect, Connect}, /* ConnectRetry_timer_expired */ {bgp_fsm_exeption, Idle}, /* Hold_Timer_expired */ {bgp_fsm_exeption, Idle}, /* KeepAlive_timer_expired */ - {bgp_fsm_exeption, Idle}, /* Receive_OPEN_message */ - {bgp_fsm_exeption, Idle}, /* Receive_KEEPALIVE_message */ - {bgp_fsm_exeption, Idle}, /* Receive_UPDATE_message */ - {bgp_stop, Idle}, /* Receive_NOTIFICATION_message */ - {bgp_fsm_exeption, Idle}, /* Clearing_Completed */ + {bgp_fsm_delayopen_timer_expire, + OpenSent}, /* DelayOpen_timer_expired */ + {bgp_fsm_open, OpenConfirm}, /* Receive_OPEN_message */ + {bgp_fsm_exeption, Idle}, /* Receive_KEEPALIVE_message */ + {bgp_fsm_exeption, Idle}, /* Receive_UPDATE_message */ + {bgp_stop, Idle}, /* Receive_NOTIFICATION_message */ + {bgp_fsm_exeption, Idle}, /* Clearing_Completed */ }, { /* Active, */ {bgp_ignore, Active}, /* BGP_Start */ {bgp_stop, Idle}, /* BGP_Stop */ {bgp_connect_success, OpenSent}, /* TCP_connection_open */ + {bgp_connect_success_w_delayopen, + Active}, /* TCP_connection_open_w_delay */ {bgp_stop, Idle}, /* TCP_connection_closed */ {bgp_ignore, Active}, /* TCP_connection_open_failed */ {bgp_fsm_exeption, Idle}, /* TCP_fatal_error */ {bgp_start, Connect}, /* ConnectRetry_timer_expired */ {bgp_fsm_exeption, Idle}, /* Hold_Timer_expired */ {bgp_fsm_exeption, Idle}, /* KeepAlive_timer_expired */ - {bgp_fsm_exeption, Idle}, /* Receive_OPEN_message */ - {bgp_fsm_exeption, Idle}, /* Receive_KEEPALIVE_message */ - {bgp_fsm_exeption, Idle}, /* Receive_UPDATE_message */ - {bgp_fsm_exeption, Idle}, /* Receive_NOTIFICATION_message */ - {bgp_fsm_exeption, Idle}, /* Clearing_Completed */ + {bgp_fsm_delayopen_timer_expire, + OpenSent}, /* DelayOpen_timer_expired */ + {bgp_fsm_open, OpenConfirm}, /* Receive_OPEN_message */ + {bgp_fsm_exeption, Idle}, /* Receive_KEEPALIVE_message */ + {bgp_fsm_exeption, Idle}, /* Receive_UPDATE_message */ + {bgp_fsm_exeption, Idle}, /* Receive_NOTIFICATION_message */ + {bgp_fsm_exeption, Idle}, /* Clearing_Completed */ }, { /* OpenSent, */ {bgp_ignore, OpenSent}, /* BGP_Start */ {bgp_stop, Idle}, /* BGP_Stop */ {bgp_stop, Active}, /* TCP_connection_open */ + {bgp_fsm_exeption, Idle}, /* TCP_connection_open_w_delay */ {bgp_stop, Active}, /* TCP_connection_closed */ {bgp_stop, Active}, /* TCP_connection_open_failed */ {bgp_stop, Active}, /* TCP_fatal_error */ {bgp_fsm_exeption, Idle}, /* ConnectRetry_timer_expired */ {bgp_fsm_holdtime_expire, Idle}, /* Hold_Timer_expired */ {bgp_fsm_exeption, Idle}, /* KeepAlive_timer_expired */ + {bgp_fsm_exeption, Idle}, /* DelayOpen_timer_expired */ {bgp_fsm_open, OpenConfirm}, /* Receive_OPEN_message */ {bgp_fsm_event_error, Idle}, /* Receive_KEEPALIVE_message */ {bgp_fsm_event_error, Idle}, /* Receive_UPDATE_message */ @@ -2157,12 +2294,14 @@ static const struct { {bgp_ignore, OpenConfirm}, /* BGP_Start */ {bgp_stop, Idle}, /* BGP_Stop */ {bgp_stop, Idle}, /* TCP_connection_open */ + {bgp_fsm_exeption, Idle}, /* TCP_connection_open_w_delay */ {bgp_stop, Idle}, /* TCP_connection_closed */ {bgp_stop, Idle}, /* TCP_connection_open_failed */ {bgp_stop, Idle}, /* TCP_fatal_error */ {bgp_fsm_exeption, Idle}, /* ConnectRetry_timer_expired */ {bgp_fsm_holdtime_expire, Idle}, /* Hold_Timer_expired */ {bgp_ignore, OpenConfirm}, /* KeepAlive_timer_expired */ + {bgp_fsm_exeption, Idle}, /* DelayOpen_timer_expired */ {bgp_fsm_exeption, Idle}, /* Receive_OPEN_message */ {bgp_establish, Established}, /* Receive_KEEPALIVE_message */ {bgp_fsm_exeption, Idle}, /* Receive_UPDATE_message */ @@ -2174,12 +2313,14 @@ static const struct { {bgp_ignore, Established}, /* BGP_Start */ {bgp_stop, Clearing}, /* BGP_Stop */ {bgp_stop, Clearing}, /* TCP_connection_open */ + {bgp_fsm_exeption, Idle}, /* TCP_connection_open_w_delay */ {bgp_stop, Clearing}, /* TCP_connection_closed */ {bgp_stop, Clearing}, /* TCP_connection_open_failed */ {bgp_stop, Clearing}, /* TCP_fatal_error */ {bgp_stop, Clearing}, /* ConnectRetry_timer_expired */ {bgp_fsm_holdtime_expire, Clearing}, /* Hold_Timer_expired */ {bgp_ignore, Established}, /* KeepAlive_timer_expired */ + {bgp_fsm_exeption, Idle}, /* DelayOpen_timer_expired */ {bgp_stop, Clearing}, /* Receive_OPEN_message */ {bgp_fsm_keepalive, Established}, /* Receive_KEEPALIVE_message */ @@ -2193,12 +2334,14 @@ static const struct { {bgp_ignore, Clearing}, /* BGP_Start */ {bgp_stop, Clearing}, /* BGP_Stop */ {bgp_stop, Clearing}, /* TCP_connection_open */ + {bgp_stop, Clearing}, /* TCP_connection_open_w_delay */ {bgp_stop, Clearing}, /* TCP_connection_closed */ {bgp_stop, Clearing}, /* TCP_connection_open_failed */ {bgp_stop, Clearing}, /* TCP_fatal_error */ {bgp_stop, Clearing}, /* ConnectRetry_timer_expired */ {bgp_stop, Clearing}, /* Hold_Timer_expired */ {bgp_stop, Clearing}, /* KeepAlive_timer_expired */ + {bgp_stop, Clearing}, /* DelayOpen_timer_expired */ {bgp_stop, Clearing}, /* Receive_OPEN_message */ {bgp_stop, Clearing}, /* Receive_KEEPALIVE_message */ {bgp_stop, Clearing}, /* Receive_UPDATE_message */ @@ -2210,12 +2353,14 @@ static const struct { {bgp_ignore, Deleted}, /* BGP_Start */ {bgp_ignore, Deleted}, /* BGP_Stop */ {bgp_ignore, Deleted}, /* TCP_connection_open */ + {bgp_ignore, Deleted}, /* TCP_connection_open_w_delay */ {bgp_ignore, Deleted}, /* TCP_connection_closed */ {bgp_ignore, Deleted}, /* TCP_connection_open_failed */ {bgp_ignore, Deleted}, /* TCP_fatal_error */ {bgp_ignore, Deleted}, /* ConnectRetry_timer_expired */ {bgp_ignore, Deleted}, /* Hold_Timer_expired */ {bgp_ignore, Deleted}, /* KeepAlive_timer_expired */ + {bgp_ignore, Deleted}, /* DelayOpen_timer_expired */ {bgp_ignore, Deleted}, /* Receive_OPEN_message */ {bgp_ignore, Deleted}, /* Receive_KEEPALIVE_message */ {bgp_ignore, Deleted}, /* Receive_UPDATE_message */ diff --git a/bgpd/bgp_nb_config.c b/bgpd/bgp_nb_config.c index 4cc92eed54..6e7a1b650c 100644 --- a/bgpd/bgp_nb_config.c +++ b/bgpd/bgp_nb_config.c @@ -1425,8 +1425,8 @@ int bgp_global_global_config_timers_hold_time_modify( keepalive = yang_dnode_get_uint16(args->dnode, "../keepalive"); holdtime = yang_dnode_get_uint16(args->dnode, NULL); - bgp_timers_set(bgp, keepalive, holdtime, - DFLT_BGP_CONNECT_RETRY); + bgp_timers_set(bgp, keepalive, holdtime, DFLT_BGP_CONNECT_RETRY, + BGP_DEFAULT_DELAYOPEN); break; } @@ -1466,8 +1466,8 @@ int bgp_global_global_config_timers_keepalive_modify( keepalive = yang_dnode_get_uint16(args->dnode, NULL); holdtime = yang_dnode_get_uint16(args->dnode, "../hold-time"); - bgp_timers_set(bgp, keepalive, holdtime, - DFLT_BGP_CONNECT_RETRY); + bgp_timers_set(bgp, keepalive, holdtime, DFLT_BGP_CONNECT_RETRY, + BGP_DEFAULT_DELAYOPEN); break; } diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index fcbdb2969f..be2c474493 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -457,8 +457,16 @@ static int bgp_accept(struct thread *thread) BGP_TIMER_OFF( peer1->t_start); /* created in peer_create() */ - if (peer_active(peer1)) - BGP_EVENT_ADD(peer1, TCP_connection_open); + if (peer_active(peer1)) { + if (CHECK_FLAG(peer1->flags, + PEER_FLAG_TIMER_DELAYOPEN)) + BGP_EVENT_ADD( + peer1, + TCP_connection_open_w_delay); + else + BGP_EVENT_ADD(peer1, + TCP_connection_open); + } return 0; } @@ -595,7 +603,10 @@ static int bgp_accept(struct thread *thread) } if (peer_active(peer)) { - BGP_EVENT_ADD(peer, TCP_connection_open); + if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER_DELAYOPEN)) + BGP_EVENT_ADD(peer, TCP_connection_open_w_delay); + else + BGP_EVENT_ADD(peer, TCP_connection_open); } return 0; diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 03494f7a1f..563a1faf96 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -461,7 +461,7 @@ int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name, if (ret == BGP_CREATED) { bgp_timers_set(*bgp, DFLT_BGP_KEEPALIVE, DFLT_BGP_HOLDTIME, - DFLT_BGP_CONNECT_RETRY); + DFLT_BGP_CONNECT_RETRY, BGP_DEFAULT_DELAYOPEN); if (DFLT_BGP_IMPORT_CHECK) SET_FLAG((*bgp)->flags, BGP_FLAG_IMPORT_CHECK); @@ -7373,6 +7373,54 @@ DEFUN_YANG (no_neighbor_timers_connect, return nb_cli_apply_changes(vty, base_xpath); } +DEFPY (neighbor_timers_delayopen, + neighbor_timers_delayopen_cmd, + "neighbor $neighbor timers delayopen (1-240)$interval", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP per neighbor timers\n" + "RFC 4271 DelayOpenTimer\n" + "DelayOpenTime timer interval\n") +{ + struct peer *peer; + + peer = peer_and_group_lookup_vty(vty, neighbor); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + if (!interval) { + if (peer_timers_delayopen_unset(peer)) + return CMD_WARNING_CONFIG_FAILED; + } else { + if (peer_timers_delayopen_set(peer, interval)) + return CMD_WARNING_CONFIG_FAILED; + } + + return CMD_SUCCESS; +} + +DEFPY (no_neighbor_timers_delayopen, + no_neighbor_timers_delayopen_cmd, + "no neighbor $neighbor timers delayopen [(0-65535)]", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "BGP per neighbor timers\n" + "RFC 4271 DelayOpenTimer\n" + "DelayOpenTime timer interval\n") +{ + struct peer *peer; + + peer = peer_and_group_lookup_vty(vty, neighbor); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + if (peer_timers_delayopen_unset(peer)) + return CMD_WARNING_CONFIG_FAILED; + + return CMD_SUCCESS; +} + DEFUN_YANG (neighbor_advertise_interval, neighbor_advertise_interval_cmd, "neighbor advertisement-interval (0-600)", @@ -12607,6 +12655,12 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, json_object_int_add(json_neigh, "bgpTimerKeepAliveIntervalMsecs", p->v_keepalive * 1000); + if (CHECK_FLAG(p->flags, PEER_FLAG_TIMER_DELAYOPEN)) { + json_object_int_add(json_neigh, + "bgpTimerDelayOpenTimeMsecs", + p->v_delayopen * 1000); + } + if (CHECK_FLAG(p->flags, PEER_FLAG_TIMER)) { json_object_int_add(json_neigh, "bgpTimerConfiguredHoldTimeMsecs", @@ -12686,6 +12740,10 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, vty_out(vty, ", keepalive interval is %d seconds\n", bgp->default_keepalive); } + if (CHECK_FLAG(p->flags, PEER_FLAG_TIMER_DELAYOPEN)) + vty_out(vty, + " Configured DelayOpenTime is %d seconds\n", + p->delayopen); } /* Capability. */ if (p->status == Established) { @@ -16402,6 +16460,18 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp, vty_out(vty, " neighbor %s timers connect %u\n", addr, peer->bgp->default_connect_retry); + /* timers delayopen */ + if (peergroup_flag_check(peer, PEER_FLAG_TIMER_DELAYOPEN)) + vty_out(vty, " neighbor %s timers delayopen %u\n", addr, + peer->delayopen); + /* Save config even though flag is not set if default values have been + * changed + */ + else if (!peer_group_active(peer) && !peer->delayopen + && peer->bgp->default_delayopen != BGP_DEFAULT_DELAYOPEN) + vty_out(vty, " neighbor %s timers delayopen %u\n", addr, + peer->bgp->default_delayopen); + /* capability dynamic */ if (peergroup_flag_check(peer, PEER_FLAG_DYNAMIC_CAPABILITY)) vty_out(vty, " neighbor %s capability dynamic\n", addr); @@ -18261,6 +18331,10 @@ void bgp_vty_init(void) install_element(BGP_NODE, &neighbor_timers_connect_cmd); install_element(BGP_NODE, &no_neighbor_timers_connect_cmd); + /* "neighbor timers delayopen" commands. */ + install_element(BGP_NODE, &neighbor_timers_delayopen_cmd); + install_element(BGP_NODE, &no_neighbor_timers_delayopen_cmd); + /* "neighbor advertisement-interval" commands. */ install_element(BGP_NODE, &neighbor_advertise_interval_cmd); install_element(BGP_NODE, &no_neighbor_advertise_interval_cmd); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 82ce0c3882..f1454aaee8 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -491,12 +491,13 @@ time_t bgp_clock(void) /* BGP timer configuration. */ void bgp_timers_set(struct bgp *bgp, uint32_t keepalive, uint32_t holdtime, - uint32_t connect_retry) + uint32_t connect_retry, uint32_t delayopen) { bgp->default_keepalive = (keepalive < holdtime / 3 ? keepalive : holdtime / 3); bgp->default_holdtime = holdtime; bgp->default_connect_retry = connect_retry; + bgp->default_delayopen = delayopen; } /* mostly for completeness - CLI uses its own defaults */ @@ -505,6 +506,7 @@ void bgp_timers_unset(struct bgp *bgp) bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE; bgp->default_holdtime = BGP_DEFAULT_HOLDTIME; bgp->default_connect_retry = BGP_DEFAULT_CONNECT_RETRY; + bgp->default_delayopen = BGP_DEFAULT_DELAYOPEN; } /* BGP confederation configuration. */ @@ -1410,10 +1412,12 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src) peer_dst->holdtime = peer_src->holdtime; peer_dst->keepalive = peer_src->keepalive; peer_dst->connect = peer_src->connect; + peer_dst->delayopen = peer_src->delayopen; peer_dst->v_holdtime = peer_src->v_holdtime; peer_dst->v_keepalive = peer_src->v_keepalive; peer_dst->routeadv = peer_src->routeadv; peer_dst->v_routeadv = peer_src->v_routeadv; + peer_dst->v_delayopen = peer_src->v_delayopen; /* password apply */ if (peer_src->password && !peer_dst->password) @@ -2577,6 +2581,14 @@ static void peer_group2peer_config_copy(struct peer_group *group, peer->v_connect = peer->bgp->default_connect_retry; } + if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_TIMER_DELAYOPEN)) { + PEER_ATTR_INHERIT(peer, group, delayopen); + if (CHECK_FLAG(conf->flags, PEER_FLAG_TIMER_DELAYOPEN)) + peer->v_delayopen = conf->delayopen; + else + peer->v_delayopen = peer->bgp->default_delayopen; + } + /* advertisement-interval apply */ if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_ROUTEADV)) { PEER_ATTR_INHERIT(peer, group, routeadv); @@ -4018,6 +4030,7 @@ static const struct peer_flag_action peer_flag_action_list[] = { {PEER_FLAG_ROUTEADV, 0, peer_change_none}, {PEER_FLAG_TIMER, 0, peer_change_none}, {PEER_FLAG_TIMER_CONNECT, 0, peer_change_none}, + {PEER_FLAG_TIMER_DELAYOPEN, 0, peer_change_none}, {PEER_FLAG_PASSWORD, 0, peer_change_none}, {PEER_FLAG_LOCAL_AS, 0, peer_change_none}, {PEER_FLAG_LOCAL_AS_NO_PREPEND, 0, peer_change_none}, @@ -5403,6 +5416,90 @@ int peer_advertise_interval_unset(struct peer *peer) return 0; } +/* set the peers RFC 4271 DelayOpen session attribute flag and DelayOpenTimer + * interval + */ +int peer_timers_delayopen_set(struct peer *peer, uint32_t delayopen) +{ + struct peer *member; + struct listnode *node; + + /* Set peers session attribute flag and timer interval. */ + peer_flag_set(peer, PEER_FLAG_TIMER_DELAYOPEN); + peer->delayopen = delayopen; + peer->v_delayopen = delayopen; + + /* Skip group mechanics for regular peers. */ + if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) + return 0; + + /* Set flag and configuration on all peer-group members, unless they are + * explicitely overriding peer-group configuration. + */ + for (ALL_LIST_ELEMENTS_RO(peer->group->peer, node, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->flags_override, + PEER_FLAG_TIMER_DELAYOPEN)) + continue; + + /* Set session attribute flag and timer intervals on peer-group + * member. + */ + SET_FLAG(member->flags, PEER_FLAG_TIMER_DELAYOPEN); + member->delayopen = delayopen; + member->v_delayopen = delayopen; + } + + return 0; +} + +/* unset the peers RFC 4271 DelayOpen session attribute flag and reset the + * DelayOpenTimer interval to the default value. + */ +int peer_timers_delayopen_unset(struct peer *peer) +{ + struct peer *member; + struct listnode *node; + + /* Inherit configuration from peer-group if peer is member. */ + if (peer_group_active(peer)) { + peer_flag_inherit(peer, PEER_FLAG_TIMER_DELAYOPEN); + PEER_ATTR_INHERIT(peer, peer->group, delayopen); + } else { + /* Otherwise remove session attribute flag and set timer + * interval to default value. + */ + peer_flag_unset(peer, PEER_FLAG_TIMER_DELAYOPEN); + peer->delayopen = peer->bgp->default_delayopen; + } + + /* Set timer value to zero */ + peer->v_delayopen = 0; + + /* Skip peer-group mechanics for regular peers. */ + if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) + return 0; + + /* Remove flag and configuration from all peer-group members, unless + * they are explicitely overriding peer-group configuration. + */ + for (ALL_LIST_ELEMENTS_RO(peer->group->peer, node, member)) { + /* Skip peers with overridden configuration. */ + if (CHECK_FLAG(member->flags_override, + PEER_FLAG_TIMER_DELAYOPEN)) + continue; + + /* Remove session attribute flag, reset the timer interval to + * the default value and set the timer value to zero. + */ + UNSET_FLAG(member->flags, PEER_FLAG_TIMER_DELAYOPEN); + member->delayopen = peer->bgp->default_delayopen; + member->v_delayopen = 0; + } + + return 0; +} + /* neighbor interface */ void peer_interface_set(struct peer *peer, const char *str) { diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 965a35b345..e867159fa6 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -560,6 +560,7 @@ struct bgp { uint32_t default_holdtime; uint32_t default_keepalive; uint32_t default_connect_retry; + uint32_t default_delayopen; /* BGP graceful restart */ uint32_t restart_time; @@ -904,12 +905,14 @@ enum bgp_fsm_events { BGP_Start = 1, BGP_Stop, TCP_connection_open, + TCP_connection_open_w_delay, TCP_connection_closed, TCP_connection_open_failed, TCP_fatal_error, ConnectRetry_timer_expired, Hold_Timer_expired, KeepAlive_timer_expired, + DelayOpen_timer_expired, Receive_OPEN_message, Receive_KEEPALIVE_message, Receive_UPDATE_message, @@ -1166,6 +1169,8 @@ struct peer { *and PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT */ +#define PEER_FLAG_TIMER_DELAYOPEN (1 << 27) /* delayopen timer */ + struct bgp_peer_gr PEER_GR_FSM[BGP_PEER_GR_MODE][BGP_PEER_GR_EVENT_CMD]; enum peer_mode peer_gr_present_state; /* Non stop forwarding afi-safi count for BGP gr feature*/ @@ -1259,6 +1264,7 @@ struct peer { _Atomic uint32_t keepalive; _Atomic uint32_t connect; _Atomic uint32_t routeadv; + _Atomic uint32_t delayopen; /* Timer values. */ _Atomic uint32_t v_start; @@ -1266,6 +1272,7 @@ struct peer { _Atomic uint32_t v_holdtime; _Atomic uint32_t v_keepalive; _Atomic uint32_t v_routeadv; + _Atomic uint32_t v_delayopen; _Atomic uint32_t v_pmax_restart; _Atomic uint32_t v_gr_restart; @@ -1278,6 +1285,7 @@ struct peer { struct thread *t_connect; struct thread *t_holdtime; struct thread *t_routeadv; + struct thread *t_delayopen; struct thread *t_pmax_restart; struct thread *t_gr_restart; struct thread *t_gr_stale; @@ -1673,6 +1681,9 @@ struct bgp_nlri { #define BGP_DEFAULT_EBGP_ROUTEADV 0 #define BGP_DEFAULT_IBGP_ROUTEADV 0 +/* BGP RFC 4271 DelayOpenTime default value */ +#define BGP_DEFAULT_DELAYOPEN 120 + /* BGP default local preference. */ #define BGP_DEFAULT_LOCAL_PREF 100 @@ -1877,7 +1888,7 @@ extern int bgp_confederation_peers_add(struct bgp *, as_t); extern int bgp_confederation_peers_remove(struct bgp *, as_t); extern void bgp_timers_set(struct bgp *, uint32_t keepalive, uint32_t holdtime, - uint32_t connect_retry); + uint32_t connect_retry, uint32_t delayopen); extern void bgp_timers_unset(struct bgp *); extern int bgp_default_local_preference_set(struct bgp *, uint32_t); @@ -1953,6 +1964,9 @@ extern int peer_timers_connect_unset(struct peer *); extern int peer_advertise_interval_set(struct peer *, uint32_t); extern int peer_advertise_interval_unset(struct peer *); +extern int peer_timers_delayopen_set(struct peer *peer, uint32_t delayopen); +extern int peer_timers_delayopen_unset(struct peer *peer); + extern void peer_interface_set(struct peer *, const char *); extern void peer_interface_unset(struct peer *); diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index a00b994988..73c286631a 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -1596,6 +1596,15 @@ Configuring Peers peer in question. This number is between 0 and 600 seconds, with the default advertisement interval being 0. +.. index:: [no] neighbor PEER timers delayopen (1-240) +.. clicmd:: [no] neighbor PEER timers delayopen (1-240) + + This command allows the user enable the + `RFC 4271 ` DelayOpenTimer with the + specified interval or disable it with the negating command for the peer. By + default, the DelayOpenTimer is disabled. The timer interval may be set to a + duration of 1 to 240 seconds. + Displaying Information about Peers ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/topotests/bgp_features/r1/bgp_delayopen_neighbor.json b/tests/topotests/bgp_features/r1/bgp_delayopen_neighbor.json new file mode 100644 index 0000000000..5caaeabfb5 --- /dev/null +++ b/tests/topotests/bgp_features/r1/bgp_delayopen_neighbor.json @@ -0,0 +1,6 @@ +{ + "192.168.101.2":{ + "remoteAs":65100, + "bgpTimerDelayOpenTimeMsecs":240000 + } +} diff --git a/tests/topotests/bgp_features/r1/bgp_delayopen_summary_established.json b/tests/topotests/bgp_features/r1/bgp_delayopen_summary_established.json new file mode 100644 index 0000000000..3ab3588937 --- /dev/null +++ b/tests/topotests/bgp_features/r1/bgp_delayopen_summary_established.json @@ -0,0 +1,11 @@ +{ +"ipv4Unicast":{ + "as":65000, + "peers":{ + "192.168.101.2":{ + "remoteAs":65100, + "state":"Established" + } + } +} +} diff --git a/tests/topotests/bgp_features/r1/bgp_delayopen_summary_shutdown.json b/tests/topotests/bgp_features/r1/bgp_delayopen_summary_shutdown.json new file mode 100644 index 0000000000..9a41236dc1 --- /dev/null +++ b/tests/topotests/bgp_features/r1/bgp_delayopen_summary_shutdown.json @@ -0,0 +1,11 @@ +{ +"ipv4Unicast":{ + "as":65000, + "peers":{ + "192.168.101.2":{ + "remoteAs":65100, + "state":"Idle (Admin)" + } + } +} +} diff --git a/tests/topotests/bgp_features/r2/bgp_delayopen_neighbor.json b/tests/topotests/bgp_features/r2/bgp_delayopen_neighbor.json new file mode 100644 index 0000000000..a74da030d4 --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgp_delayopen_neighbor.json @@ -0,0 +1,6 @@ +{ + "192.168.201.2":{ + "remoteAs":65200, + "bgpTimerDelayOpenTimeMsecs":60000 + } +} diff --git a/tests/topotests/bgp_features/r2/bgp_delayopen_summary_connect.json b/tests/topotests/bgp_features/r2/bgp_delayopen_summary_connect.json new file mode 100644 index 0000000000..c2b42ec9a3 --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgp_delayopen_summary_connect.json @@ -0,0 +1,11 @@ +{ +"ipv4Unicast":{ + "as":65000, + "peers":{ + "192.168.201.2":{ + "remoteAs":65200, + "state":"Connect" + } + } +} +} diff --git a/tests/topotests/bgp_features/r2/bgp_delayopen_summary_established.json b/tests/topotests/bgp_features/r2/bgp_delayopen_summary_established.json new file mode 100644 index 0000000000..77b6944314 --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgp_delayopen_summary_established.json @@ -0,0 +1,11 @@ +{ +"ipv4Unicast":{ + "as":65000, + "peers":{ + "192.168.201.2":{ + "remoteAs":65200, + "state":"Established" + } + } +} +} diff --git a/tests/topotests/bgp_features/r2/bgp_delayopen_summary_shutdown.json b/tests/topotests/bgp_features/r2/bgp_delayopen_summary_shutdown.json new file mode 100644 index 0000000000..8f9476ad96 --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgp_delayopen_summary_shutdown.json @@ -0,0 +1,11 @@ +{ +"ipv4Unicast":{ + "as":65000, + "peers":{ + "192.168.201.2":{ + "remoteAs":65200, + "state":"Idle (Admin)" + } + } +} +} diff --git a/tests/topotests/bgp_features/r4/bgp_delayopen_summary_established.json b/tests/topotests/bgp_features/r4/bgp_delayopen_summary_established.json new file mode 100644 index 0000000000..85caf55e76 --- /dev/null +++ b/tests/topotests/bgp_features/r4/bgp_delayopen_summary_established.json @@ -0,0 +1,11 @@ +{ +"ipv4Unicast":{ + "as":65100, + "peers":{ + "192.168.101.1":{ + "remoteAs":65000, + "state":"Established" + } + } +} +} diff --git a/tests/topotests/bgp_features/r4/bgp_delayopen_summary_shutdown.json b/tests/topotests/bgp_features/r4/bgp_delayopen_summary_shutdown.json new file mode 100644 index 0000000000..cf784d817d --- /dev/null +++ b/tests/topotests/bgp_features/r4/bgp_delayopen_summary_shutdown.json @@ -0,0 +1,11 @@ +{ +"ipv4Unicast":{ + "as":65100, + "peers":{ + "192.168.101.1":{ + "remoteAs":65000, + "state":"Idle (Admin)" + } + } +} +} diff --git a/tests/topotests/bgp_features/r5/bgp_delayopen_neighbor.json b/tests/topotests/bgp_features/r5/bgp_delayopen_neighbor.json new file mode 100644 index 0000000000..4b97254f24 --- /dev/null +++ b/tests/topotests/bgp_features/r5/bgp_delayopen_neighbor.json @@ -0,0 +1,6 @@ +{ + "192.168.201.1":{ + "remoteAs":65000, + "bgpTimerDelayOpenTimeMsecs":30000 + } +} diff --git a/tests/topotests/bgp_features/r5/bgp_delayopen_summary_connect.json b/tests/topotests/bgp_features/r5/bgp_delayopen_summary_connect.json new file mode 100644 index 0000000000..d7b4e77e96 --- /dev/null +++ b/tests/topotests/bgp_features/r5/bgp_delayopen_summary_connect.json @@ -0,0 +1,11 @@ +{ +"ipv4Unicast":{ + "as":65200, + "peers":{ + "192.168.201.1":{ + "remoteAs":65000, + "state":"Connect" + } + } +} +} diff --git a/tests/topotests/bgp_features/r5/bgp_delayopen_summary_established.json b/tests/topotests/bgp_features/r5/bgp_delayopen_summary_established.json new file mode 100644 index 0000000000..15cfb19653 --- /dev/null +++ b/tests/topotests/bgp_features/r5/bgp_delayopen_summary_established.json @@ -0,0 +1,11 @@ +{ +"ipv4Unicast":{ + "as":65200, + "peers":{ + "192.168.201.1":{ + "remoteAs":65000, + "state":"Established" + } + } +} +} diff --git a/tests/topotests/bgp_features/r5/bgp_delayopen_summary_shutdown.json b/tests/topotests/bgp_features/r5/bgp_delayopen_summary_shutdown.json new file mode 100644 index 0000000000..94acebab96 --- /dev/null +++ b/tests/topotests/bgp_features/r5/bgp_delayopen_summary_shutdown.json @@ -0,0 +1,11 @@ +{ +"ipv4Unicast":{ + "as":65200, + "peers":{ + "192.168.201.1":{ + "remoteAs":65000, + "state":"Idle (Admin)" + } + } +} +} diff --git a/tests/topotests/bgp_features/test_bgp_features.py b/tests/topotests/bgp_features/test_bgp_features.py index 9dce9d668b..bc821bd7a0 100644 --- a/tests/topotests/bgp_features/test_bgp_features.py +++ b/tests/topotests/bgp_features/test_bgp_features.py @@ -32,6 +32,7 @@ import os import sys import pytest import re +import time # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) @@ -745,6 +746,215 @@ def test_bgp_disable_norib_routes(): # tgen.mininet_cli() +def test_bgp_delayopen_without(): + "Optional test of BGP functionality and behaviour without DelayOpenTimer enabled to establish a reference for following tests" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # part 1: no delay r1 <=> no delay r4 + logger.info("Starting optional test of BGP functionality without DelayOpenTimer enabled to establish a reference for following tests") + + # 1.1 enable peering shutdown + logger.info("Enable shutdown of peering between r1 and r4") + tgen.net["r1"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.2 shutdown"') + tgen.net["r4"].cmd('vtysh -c "conf t" -c "router bgp 65100" -c "neighbor 192.168.101.1 shutdown"') + + # 1.2 wait for peers to shut down (poll output) + for router_num in [1, 4]: + logger.info("Checking BGP summary after enabling shutdown of peering on r{}".format(router_num)) + router = tgen.gears["r{}".format(router_num)] + reffile = os.path.join(CWD, "r{}/bgp_delayopen_summary_shutdown.json".format(router_num)) + expected = json.loads(open(reffile).read()) + test_func = functools.partial(topotest.router_json_cmp, router, "show ip bgp summary json", expected) + _, res = topotest.run_and_expect(test_func, None, count=3, wait=1) + assertmsg = "BGP session on r{} did not shut down peer".format(router_num) + assert res is None, assertmsg + + # 1.3 disable peering shutdown + logger.info("Disable shutdown of peering between r1 and r4") + tgen.net["r1"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.2 shutdown"') + tgen.net["r4"].cmd('vtysh -c "conf t" -c "router bgp 65100" -c "no neighbor 192.168.101.1 shutdown"') + + # 1.4 wait for peers to establish connection (poll output) + for router_num in [1, 4]: + logger.info("Checking BGP summary after disabling shutdown of peering on r{}".format(router_num)) + router = tgen.gears["r{}".format(router_num)] + reffile = os.path.join(CWD, "r{}/bgp_delayopen_summary_established.json".format(router_num)) + expected = json.loads(open(reffile).read()) + test_func = functools.partial(topotest.router_json_cmp, router, "show ip bgp summary json", expected) + _, res = topotest.run_and_expect(test_func, None, count=5, wait=1) + assertmsg = "BGP session on r{} did not establish a connection with peer".format(router_num) + assert res is None, assertmsg + + #tgen.mininet_cli() + + # end test_bgp_delayopen_without + + +def test_bgp_delayopen_singular(): + "Test of BGP functionality and behaviour with DelayOpenTimer enabled on one side of the peering" + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # part 2: delay 240s r1 <=> no delay r4 + logger.info("Starting test of BGP functionality and behaviour with DelayOpenTimer enabled on one side of the peering") + + # 2.1 enable peering shutdown + logger.info("Enable shutdown of peering between r1 and r4") + tgen.net["r1"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.2 shutdown"') + tgen.net["r4"].cmd('vtysh -c "conf t" -c "router bgp 65100" -c "neighbor 192.168.101.1 shutdown"') + + # 2.2 wait for peers to shut down (poll output) + for router_num in [1, 4]: + logger.info("Checking BGP summary after disabling shutdown of peering on r{}".format(router_num)) + router = tgen.gears["r{}".format(router_num)] + reffile = os.path.join(CWD, "r{}/bgp_delayopen_summary_shutdown.json".format(router_num)) + expected = json.loads(open(reffile).read()) + test_func = functools.partial(topotest.router_json_cmp, router, "show ip bgp summary json", expected) + _, res = topotest.run_and_expect(test_func, None, count=3, wait=1) + assertmsg = "BGP session on r{} did not shut down peer".format(router_num) + assert res is None, assertmsg + + # 2.3 set delayopen on R1 to 240 + logger.info("Setting DelayOpenTime for neighbor r4 to 240 seconds on r1") + tgen.net["r1"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.2 timers delayopen 240"') + + # 2.4 check config (poll output) + logger.info("Checking BGP neighbor configuration after setting DelayOpenTime on r1") + router = tgen.gears["r1"] + reffile = os.path.join(CWD, "r1/bgp_delayopen_neighbor.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial(topotest.router_json_cmp, router, "show bgp neighbors json", expected) + _, res = topotest.run_and_expect(test_func, None, count=3, wait=1) + assertmsg = "BGP session on r1 failed to set DelayOpenTime for r4" + assert res is None, assertmsg + + # 2.5 disable peering shutdown + logger.info("Disable shutdown of peering between r1 and r4") + tgen.net["r1"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.2 shutdown"') + tgen.net["r4"].cmd('vtysh -c "conf t" -c "router bgp 65100" -c "no neighbor 192.168.101.1 shutdown"') + + # 2.6 wait for peers to establish connection (poll output) + for router_num in [1, 4]: + logger.info("Checking BGP summary after disabling shutdown of peering on r{}".format(router_num)) + router = tgen.gears["r{}".format(router_num)] + reffile = os.path.join(CWD, "r{}/bgp_delayopen_summary_established.json".format(router_num)) + expected = json.loads(open(reffile).read()) + test_func = functools.partial(topotest.router_json_cmp, router, "show ip bgp summary json", expected) + _, res = topotest.run_and_expect(test_func, None, count=5, wait=1) + assertmsg = "BGP session on r{} did not establish a connection with peer".format(router_num) + assert res is None, assertmsg + + # 2.7 unset delayopen on R1 + logger.info("Disabling DelayOpenTimer for neighbor r4 on r1") + tgen.net["r1"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.2 timers delayopen"') + + # 2.8 check config (poll output) + logger.info("Checking BGP neighbor configuration after disabling DelayOpenTimer on r1") + delayopen_cfg = tgen.net["r1"].cmd('vtysh -c "show bgp neighbors json" | grep "DelayOpenTimeMsecs"').rstrip() + assertmsg = "BGP session on r1 failed disable DelayOpenTimer for peer r4" + assert delayopen_cfg == "", assertmsg + + #tgen.mininet_cli() + + # end test_bgp_delayopen_singular + + +def test_bgp_delayopen_dual(): + "Test of BGP functionality and behaviour with DelayOpenTimer enabled on both sides of the peering with different timer intervals" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # part 3: delay 60s R2 <=> delay 30s R5 + logger.info("Starting test of BGP functionality and behaviour with DelayOpenTimer enabled on both sides of the peering with different timer intervals") + + # 3.1 enable peering shutdown + logger.info("Enable shutdown of peering between r2 and r5") + tgen.net["r2"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.201.2 shutdown"') + tgen.net["r5"].cmd('vtysh -c "conf t" -c "router bgp 65200" -c "neighbor 192.168.201.1 shutdown"') + + # 3.2 wait for peers to shut down (pool output) + for router_num in [2, 5]: + logger.info("Checking BGP summary after disabling shutdown of peering on r{}".format(router_num)) + router = tgen.gears["r{}".format(router_num)] + reffile = os.path.join(CWD, "r{}/bgp_delayopen_summary_shutdown.json".format(router_num)) + expected = json.loads(open(reffile).read()) + test_func = functools.partial(topotest.router_json_cmp, router, "show ip bgp summary json", expected) + _, res = topotest.run_and_expect(test_func, None, count=3, wait=1) + assertmsg = "BGP session on r{} did not shut down peer".format(router_num) + assert res is None, assertmsg + + # 3.3 set delayopen on R2 to 60s and on R5 to 30s + logger.info("Setting DelayOpenTime for neighbor r5 to 60 seconds on r2") + tgen.net["r2"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.201.2 timers delayopen 60"') + logger.info("Setting DelayOpenTime for neighbor r2 to 30 seconds on r5") + tgen.net["r5"].cmd('vtysh -c "conf t" -c "router bgp 65200" -c "neighbor 192.168.201.1 timers delayopen 30"') + + # 3.4 check config (poll output) + for router_num in [2, 5]: + logger.info("Checking BGP neighbor configuration after setting DelayOpenTime on r{}i".format(router_num)) + router = tgen.gears["r{}".format(router_num)] + reffile = os.path.join(CWD, "r{}/bgp_delayopen_neighbor.json".format(router_num)) + expected = json.loads(open(reffile).read()) + test_func = functools.partial(topotest.router_json_cmp, router, "show bgp neighbors json", expected) + _, res = topotest.run_and_expect(test_func, None, count=3, wait=1) + assertmsg = "BGP session on r{} failed to set DelayOpenTime".format(router_num) + assert res is None, assertmsg + + ## 3.5 disable peering shutdown + logger.info("Disable shutdown of peering between r2 and r5") + tgen.net["r2"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.201.2 shutdown"') + tgen.net["r5"].cmd('vtysh -c "conf t" -c "router bgp 65200" -c "no neighbor 192.168.201.1 shutdown"') + + ## 3.6 wait for peers to reach connect or active state (poll output) + delay_start = int(time.time()) + for router_num in [2, 5]: + logger.info("Checking BGP summary after disabling shutdown of peering on r{}".format(router_num)) + router = tgen.gears["r{}".format(router_num)] + reffile = os.path.join(CWD, "r{}/bgp_delayopen_summary_connect.json".format(router_num)) + expected = json.loads(open(reffile).read()) + test_func = functools.partial(topotest.router_json_cmp, router, "show ip bgp summary json", expected) + _, res = topotest.run_and_expect(test_func, None, count=3, wait=1) + assertmsg = "BGP session on r{} did not enter Connect state with peer".format(router_num) + assert res is None, assertmsg + + ## 3.7 wait for peers to establish connection (poll output) + for router_num in [2, 5]: + logger.info("Checking BGP summary after disabling shutdown of peering on r{}".format(router_num)) + router = tgen.gears["r{}".format(router_num)] + reffile = os.path.join(CWD, "r{}/bgp_delayopen_summary_established.json".format(router_num)) + expected = json.loads(open(reffile).read()) + test_func = functools.partial(topotest.router_json_cmp, router, "show ip bgp summary json", expected) + _, res = topotest.run_and_expect(test_func, None, count=35, wait=1) + assertmsg = "BGP session on r{} did not establish a connection with peer".format(router_num) + assert res is None, assertmsg + + delay_stop = int(time.time()) + assertmsg = "BGP peering between r2 and r5 was established before DelayOpenTimer (30sec) on r2 could expire" + assert (delay_stop - delay_start) > 30, assertmsg + + # 3.8 unset delayopen on R2 and R5 + logger.info("Disabling DelayOpenTimer for neighbor r5 on r2") + tgen.net["r2"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.201.2 timers delayopen"') + logger.info("Disabling DelayOpenTimer for neighbor r2 on r5") + tgen.net["r5"].cmd('vtysh -c "conf t" -c "router bgp 65200" -c "no neighbor 192.168.201.1 timers delayopen"') + + # 3.9 check config (poll output) + for router_num in [2, 5]: + logger.info("Checking BGP neighbor configuration after disabling DelayOpenTimer on r{}".format(router_num)) + delayopen_cfg = tgen.net["r{}".format(router_num)].cmd('vtysh -c "show bgp neighbors json" | grep "DelayOpenTimeMsecs"').rstrip() + assertmsg = "BGP session on r{} failed disable DelayOpenTimer".format(router_num) + assert delayopen_cfg == "", assertmsg + + #tgen.mininet_cli() + + # end test_bgp_delayopen_dual + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args))