Implement BGP Send hold timer according to draft-ietf-idr-bgp-sendholdtimer.
The Send hold timer drops the session if the neighbor is sending keepalives,
but does not receive our messages, causing the TCP connection to stall.
negotiation. If the proposed hold time would lead to a lower value of
the keepalive time, the session is rejected with error. Default: none.
+ <tag><label id="bgp-send-hold-time">send hold time <m/number/</tag>
+ Maximum time in seconds betweeen successfull transmissions of BGP messages.
+ Send hold timer drops the session if the neighbor is sending keepalives,
+ but does not receive our messages, causing the TCP connection to stall.
+ This may happen due to malfunctioning or overwhelmed neighbor. See
+ <HTMLURL URL="https://datatracker.ietf.org/doc/draft-ietf-idr-bgp-sendholdtimer/"
+ name="draft-ietf-idr-bgp-sendholdtimer"> for more details.
+
+ Like the option <cf/keepalive time/, the effective value depends on the
+ negotiated hold time, as it is scaled to maintain proportion between the
+ send hold time and the keepalive time. If it is set to zero, the timer
+ is disabled. Default: double of the hold timer limit.
+
+ The option <cf/disable rx/ is intended only for testing this feature and
+ should not be used anywhere else. It discards received messages and
+ disables the hold timer.
+
<tag><label id="bgp-connect-delay-time">connect delay time <m/number/</tag>
Delay in seconds between protocol startup and the first attempt to
connect. Default: 5 seconds.
conn->keepalive_timer = NULL;
rfree(conn->hold_timer);
conn->hold_timer = NULL;
+ rfree(conn->send_hold_timer);
+ conn->send_hold_timer = NULL;
rfree(conn->tx_ev);
conn->tx_ev = NULL;
rfree(conn->sk);
p->channel_map[c->index] = c;
}
+ /* Breaking rx_hook for simulating receive problem */
+ if (p->cf->disable_rx)
+ {
+ conn->sk->rx_hook = NULL;
+ tm_stop(conn->hold_timer);
+ }
+
/* proto_notify_state() will likely call bgp_feed_begin(), setting c->feed_state */
bgp_conn_set_state(conn, BS_ESTABLISHED);
ev_run(conn->tx_ev);
}
+void
+bgp_send_hold_timeout(timer *t)
+{
+ struct bgp_conn *conn = t->data;
+ struct bgp_proto *p = conn->bgp;
+
+ if (conn->state == BS_CLOSE)
+ return;
+
+ /* Error codes not yet assigned by IANA */
+ uint code = 4;
+ uint subcode = 1;
+
+ /* Like bgp_error() but without NOTIFICATION */
+ bgp_log_error(p, BE_BGP_TX, "Error", code, subcode, NULL, 0);
+ bgp_store_error(p, conn, BE_BGP_TX, (code << 16) | subcode);
+ bgp_conn_enter_idle_state(conn);
+ bgp_update_startup_delay(p);
+ bgp_stop(p, 0, NULL, 0);
+}
+
static void
bgp_setup_conn(struct bgp_proto *p, struct bgp_conn *conn)
{
conn->connect_timer = tm_new_init(p->p.pool, bgp_connect_timeout, conn, 0, 0);
conn->hold_timer = tm_new_init(p->p.pool, bgp_hold_timeout, conn, 0, 0);
conn->keepalive_timer = tm_new_init(p->p.pool, bgp_keepalive_timeout, conn, 0, 0);
+ conn->send_hold_timer = tm_new_init(p->p.pool, bgp_send_hold_timeout, conn, 0, 0);
conn->tx_ev = ev_new_init(p->p.pool, bgp_kick_tx, conn);
}
tm_remains(p->conn->hold_timer), p->conn->hold_time);
cli_msg(-1006, " Keepalive timer: %t/%u",
tm_remains(p->conn->keepalive_timer), p->conn->keepalive_time);
- }
+ cli_msg(-1006, " Send hold timer: %t/%u",
+ tm_remains(p->conn->send_hold_timer), p->conn->send_hold_time);
+}
#if 0
struct bgp_stats *s = &p->stats;
int setkey; /* Set MD5 password to system SA/SP database */
u8 local_role; /* Set peering role with neighbor [RFC 9234] */
int require_roles; /* Require configured roles on both sides */
+ int send_hold_time;
+ int disable_rx; /* Stop reading messages after handshake (for simulating error) */
/* Times below are in seconds */
unsigned gr_time; /* Graceful restart timeout */
unsigned llgr_time; /* Long-lived graceful restart stale time */
timer *connect_timer;
timer *hold_timer;
timer *keepalive_timer;
+ timer *send_hold_timer;
event *tx_ev;
u32 packets_to_send; /* Bitmap of packet types to be sent */
u32 channels_to_send; /* Bitmap of channels with packets to be sent */
int notify_code, notify_subcode, notify_size;
byte *notify_data;
- uint hold_time, keepalive_time; /* Times calculated from my and neighbor's requirements */
+ uint hold_time, keepalive_time, send_hold_time; /* Times calculated from my and neighbor's requirements */
};
struct bgp_proto {
void bgp_conn_enter_established_state(struct bgp_conn *conn);
void bgp_conn_enter_close_state(struct bgp_conn *conn);
void bgp_conn_enter_idle_state(struct bgp_conn *conn);
+void broke_bgp_listening(struct channel *C);
void bgp_handle_graceful_restart(struct bgp_proto *p);
void bgp_graceful_restart_done(struct bgp_channel *c);
void bgp_refresh_begin(struct bgp_channel *c);
LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL, SETS,
DYNAMIC, RANGE, NAME, DIGITS, BGP_AIGP, AIGP, ORIGINATE, COST, ENFORCE,
FIRST, FREE, VALIDATE, BASE, ROLE, ROLES, PEER, PROVIDER, CUSTOMER,
- RS_SERVER, RS_CLIENT, REQUIRE, BGP_OTC, GLOBAL)
+ RS_SERVER, RS_CLIENT, REQUIRE, BGP_OTC, GLOBAL, SEND)
%type <i> bgp_nh
%type <i32> bgp_afi
BGP_CFG->local_role = BGP_ROLE_UNDEFINED;
BGP_CFG->dynamic_name = "dynbgp";
BGP_CFG->check_link = -1;
+ BGP_CFG->send_hold_time = -1;
}
;
| bgp_proto CONNECT RETRY TIME expr ';' { BGP_CFG->connect_retry_time = $5; }
| bgp_proto KEEPALIVE TIME expr ';' { BGP_CFG->keepalive_time = $4; if (($4<1) || ($4>65535)) cf_error("Keepalive time must be in range 1-65535"); }
| bgp_proto MIN KEEPALIVE TIME expr ';' { BGP_CFG->min_keepalive_time = $5; }
+ | bgp_proto SEND HOLD TIME expr';' { BGP_CFG->send_hold_time = $5; }
| bgp_proto ERROR FORGET TIME expr ';' { BGP_CFG->error_amnesia_time = $5; }
| bgp_proto ERROR WAIT TIME expr ',' expr ';' { BGP_CFG->error_delay_time_min = $5; BGP_CFG->error_delay_time_max = $7; }
| bgp_proto DISABLE AFTER ERROR bool ';' { BGP_CFG->disable_after_error = $5; }
| bgp_proto ENFORCE FIRST AS bool ';' { BGP_CFG->enforce_first_as = $5; }
| bgp_proto LOCAL ROLE bgp_role_name ';' { BGP_CFG->local_role = $4; }
| bgp_proto REQUIRE ROLES bool ';' { BGP_CFG->require_roles = $4; }
+ | bgp_proto DISABLE RX bool ';' { BGP_CFG->disable_rx = $4; }
;
bgp_afi:
(p->cf->keepalive_time * hold_time / p->cf->hold_time) :
hold_time / 3;
+ uint send_hold_time = (p->cf->send_hold_time >= 0) ?
+ (p->cf->send_hold_time * hold_time / p->cf->hold_time) :
+ 2 * hold_time;
+
/* Keepalive time might be rounded down to zero */
if (hold_time && !keepalive_time)
keepalive_time = 1;
/* Update our local variables */
conn->hold_time = hold_time;
conn->keepalive_time = keepalive_time;
+ conn->send_hold_time = send_hold_time;
conn->as4_session = conn->local_caps->as4_support && caps->as4_support;
conn->ext_messages = conn->local_caps->ext_messages && caps->ext_messages;
p->remote_id = id;
bgp_schedule_packet(conn, NULL, PKT_KEEPALIVE);
bgp_start_timer(conn->hold_timer, conn->hold_time);
+ bgp_start_timer(conn->send_hold_timer, conn->send_hold_time);
bgp_conn_enter_openconfirm_state(conn);
}
put_u16(buf+16, len);
buf[18] = type;
- return sk_send(sk, len);
+ int success = sk_send(sk, len);
+ if (success && ((conn->state == BS_ESTABLISHED) || (conn->state == BS_OPENCONFIRM)))
+ bgp_start_timer(conn->send_hold_timer, conn->send_hold_time);
+
+ return success;
}
/**
{
struct bgp_conn *conn = sk->data;
+ /* Pending message was passed to kernel */
+ if ((conn->state == BS_ESTABLISHED) || (conn->state == BS_OPENCONFIRM))
+ bgp_start_timer(conn->send_hold_timer, conn->send_hold_time);
+
DBG("BGP: TX hook\n");
uint max = 1024;
while (--max && (bgp_fire_tx(conn) > 0))
{ 3, 10, "Invalid network field" },
{ 3, 11, "Malformed AS_PATH" },
{ 4, 0, "Hold timer expired" },
+ { 4, 1, "Send hold timer expired" }, /* Provisional [draft-ietf-idr-bgp-sendholdtimer] */
{ 5, 0, "Finite state machine error" }, /* Subcodes are according to [RFC6608] */
{ 5, 1, "Unexpected message in OpenSent state" },
{ 5, 2, "Unexpected message in OpenConfirm state" },