0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-11-14 15:18:44 +00:00

BGP now has its own loop

This commit is contained in:
Maria Matejka 2023-04-02 19:35:35 +02:00
parent 836e857b30
commit e33902e15c
4 changed files with 104 additions and 42 deletions

View File

@ -126,13 +126,13 @@
#include "bgp.h" #include "bgp.h"
static void bgp_listen_create(void *); static void bgp_listen_create(void *);
static list STATIC_LIST_INIT(bgp_sockets); /* Global list of listening sockets */ static list STATIC_LIST_INIT(bgp_sockets); /* Global list of listening sockets */
static list STATIC_LIST_INIT(bgp_listen_pending); /* Global list of listening socket open requests */ static list STATIC_LIST_INIT(bgp_listen_pending); /* Global list of listening socket open requests */
static event bgp_listen_event = { .hook = bgp_listen_create }; static event bgp_listen_event = { .hook = bgp_listen_create };
DOMAIN(rtable) bgp_listen_domain;
static void bgp_connect(struct bgp_proto *p); static void bgp_connect(struct bgp_proto *p);
static void bgp_active(struct bgp_proto *p); static void bgp_active(struct bgp_proto *p);
@ -151,6 +151,13 @@ static void bgp_graceful_restart_feed(struct bgp_channel *c);
static inline int static inline int
bgp_setup_auth(struct bgp_proto *p, int enable) bgp_setup_auth(struct bgp_proto *p, int enable)
{ {
/* Beware. This is done from main_birdloop and protocol birdloop is NOT ENTERED.
* Anyway, we are only accessing:
* - protocol config which can be changed only from main_birdloop (reconfig)
* - protocol listen socket which is always driven by main_birdloop
* - protocol name which is set on reconfig
*/
if (p->cf->password && p->listen.sock) if (p->cf->password && p->listen.sock)
{ {
ip_addr prefix = p->cf->remote_ip; ip_addr prefix = p->cf->remote_ip;
@ -184,16 +191,21 @@ bgp_setup_auth(struct bgp_proto *p, int enable)
static void static void
bgp_close(struct bgp_proto *p) bgp_close(struct bgp_proto *p)
{ {
LOCK_DOMAIN(rtable, bgp_listen_domain);
struct bgp_listen_request *req = &p->listen; struct bgp_listen_request *req = &p->listen;
struct bgp_socket *bs = req->sock; struct bgp_socket *bs = req->sock;
ASSERT(bs); if (bs)
{
req->sock = NULL; req->sock = NULL;
rem_node(&req->n); rem_node(&req->n);
if (EMPTY_LIST(bs->requests)) if (bs && EMPTY_LIST(bs->requests))
ev_schedule(&bgp_listen_event); ev_send(&global_event_list, &bgp_listen_event);
}
UNLOCK_DOMAIN(rtable, bgp_listen_domain);
} }
/** /**
@ -208,6 +220,8 @@ bgp_close(struct bgp_proto *p)
static void static void
bgp_open(struct bgp_proto *p) bgp_open(struct bgp_proto *p)
{ {
LOCK_DOMAIN(rtable, bgp_listen_domain);
struct bgp_listen_request *req = &p->listen; struct bgp_listen_request *req = &p->listen;
/* We assume that cf->iface is defined iff cf->local_ip is link-local */ /* We assume that cf->iface is defined iff cf->local_ip is link-local */
req->iface = p->cf->strict_bind ? p->cf->iface : NULL; req->iface = p->cf->strict_bind ? p->cf->iface : NULL;
@ -220,17 +234,28 @@ bgp_open(struct bgp_proto *p)
BGP_TRACE(D_EVENTS, "Requesting listen socket at %I%J port %u", req->addr, req->iface, req->port); BGP_TRACE(D_EVENTS, "Requesting listen socket at %I%J port %u", req->addr, req->iface, req->port);
add_tail(&bgp_listen_pending, &req->n); add_tail(&bgp_listen_pending, &req->n);
ev_schedule(&bgp_listen_event); ev_send(&global_event_list, &bgp_listen_event);
UNLOCK_DOMAIN(rtable, bgp_listen_domain);
} }
static void static void
bgp_listen_create(void *_ UNUSED) bgp_listen_create(void *_ UNUSED)
{ {
ASSERT_DIE(birdloop_inside(&main_birdloop));
uint flag_mask = SKF_FREEBIND; uint flag_mask = SKF_FREEBIND;
struct bgp_listen_request *req; while (1) {
WALK_LIST_FIRST(req, bgp_listen_pending) LOCK_DOMAIN(rtable, bgp_listen_domain);
if (EMPTY_LIST(bgp_listen_pending))
{ {
UNLOCK_DOMAIN(rtable, bgp_listen_domain);
break;
}
/* Get the first request to match */
struct bgp_listen_request *req = HEAD(bgp_listen_pending);
struct bgp_proto *p = SKIP_BACK(struct bgp_proto, listen, req); struct bgp_proto *p = SKIP_BACK(struct bgp_proto, listen, req);
rem_node(&req->n); rem_node(&req->n);
@ -249,6 +274,8 @@ bgp_listen_create(void *_ UNUSED)
BGP_TRACE(D_EVENTS, "Found a listening socket: %p", bs); BGP_TRACE(D_EVENTS, "Found a listening socket: %p", bs);
else else
{ {
/* Allocating new socket from global protocol pool.
* We can do this in main_birdloop. */
sock *sk = sk_new(proto_pool); sock *sk = sk_new(proto_pool);
sk->type = SK_TCP_PASSIVE; sk->type = SK_TCP_PASSIVE;
sk->ttl = 255; sk->ttl = 255;
@ -268,8 +295,9 @@ bgp_listen_create(void *_ UNUSED)
sk_log_error(sk, p->p.name); sk_log_error(sk, p->p.name);
log(L_ERR "%s: Cannot open listening socket", p->p.name); log(L_ERR "%s: Cannot open listening socket", p->p.name);
rfree(sk); rfree(sk);
bgp_initiate_disable(p, BEM_NO_SOCKET); UNLOCK_DOMAIN(rtable, bgp_listen_domain);
bgp_initiate_disable(p, BEM_NO_SOCKET);
continue; continue;
} }
@ -283,17 +311,25 @@ bgp_listen_create(void *_ UNUSED)
BGP_TRACE(D_EVENTS, "Created new listening socket: %p", bs); BGP_TRACE(D_EVENTS, "Created new listening socket: %p", bs);
} }
add_tail(&bs->requests, &req->n);
req->sock = bs; req->sock = bs;
add_tail(&bs->requests, &req->n);
if (bgp_setup_auth(p, 1) < 0) if (bgp_setup_auth(p, 1) < 0)
{ {
bgp_close(p); rem_node(&req->n);
req->sock = NULL;
UNLOCK_DOMAIN(rtable, bgp_listen_domain);
bgp_initiate_disable(p, BEM_INVALID_MD5); bgp_initiate_disable(p, BEM_INVALID_MD5);
continue;
} }
UNLOCK_DOMAIN(rtable, bgp_listen_domain);
} }
/* Cleanup leftover listening sockets */ /* Cleanup leftover listening sockets */
LOCK_DOMAIN(rtable, bgp_listen_domain);
struct bgp_socket *bs; struct bgp_socket *bs;
node *nxt; node *nxt;
WALK_LIST_DELSAFE(bs, nxt, bgp_sockets) WALK_LIST_DELSAFE(bs, nxt, bgp_sockets)
@ -303,6 +339,7 @@ bgp_listen_create(void *_ UNUSED)
rem_node(&bs->n); rem_node(&bs->n);
mb_free(bs); mb_free(bs);
} }
UNLOCK_DOMAIN(rtable, bgp_listen_domain);
} }
static inline struct bgp_channel * static inline struct bgp_channel *
@ -328,6 +365,9 @@ bgp_startup(struct bgp_proto *p)
if (p->postponed_sk) if (p->postponed_sk)
{ {
/* Apply postponed incoming connection */ /* Apply postponed incoming connection */
sk_unmain(p->postponed_sk);
sk_start(p->postponed_sk);
bgp_setup_conn(p, &p->incoming_conn); bgp_setup_conn(p, &p->incoming_conn);
bgp_setup_sk(&p->incoming_conn, p->postponed_sk); bgp_setup_sk(&p->incoming_conn, p->postponed_sk);
bgp_send_open(&p->incoming_conn); bgp_send_open(&p->incoming_conn);
@ -354,7 +394,7 @@ bgp_initiate(struct bgp_proto *p)
{ {
p->start_state = BSS_DELAY; p->start_state = BSS_DELAY;
BGP_TRACE(D_EVENTS, "Startup delayed by %d seconds due to errors", p->startup_delay); BGP_TRACE(D_EVENTS, "Startup delayed by %d seconds due to errors", p->startup_delay);
bgp_start_timer(p->startup_timer, p->startup_delay); bgp_start_timer(p, p->startup_timer, p->startup_delay);
} }
else else
bgp_startup(p); bgp_startup(p);
@ -363,13 +403,19 @@ bgp_initiate(struct bgp_proto *p)
static void static void
bgp_initiate_disable(struct bgp_proto *p, int err_val) bgp_initiate_disable(struct bgp_proto *p, int err_val)
{ {
PROTO_LOCKED_FROM_MAIN(&p->p)
{
/* The protocol may be already down for another reason.
* Shutdown the protocol only if it isn't already shutting down. */
switch (p->p.proto_state)
{
case PS_START:
case PS_UP:
p->p.disabled = 1; p->p.disabled = 1;
bgp_store_error(p, NULL, BE_MISC, err_val); bgp_store_error(p, NULL, BE_MISC, err_val);
bgp_stop(p, err_val, NULL, 0);
p->neigh = NULL; }
proto_notify_state(&p->p, PS_DOWN); }
return;
} }
/** /**
@ -382,14 +428,14 @@ bgp_initiate_disable(struct bgp_proto *p, int err_val)
* timers. * timers.
*/ */
void void
bgp_start_timer(timer *t, uint value) bgp_start_timer(struct bgp_proto *p, timer *t, uint value)
{ {
if (value) if (value)
{ {
/* The randomization procedure is specified in RFC 4271 section 10 */ /* The randomization procedure is specified in RFC 4271 section 10 */
btime time = value S; btime time = value S;
btime randomize = random() % ((time / 4) + 1); btime randomize = random() % ((time / 4) + 1);
tm_start(t, time - randomize); tm_start_in(t, time - randomize, p->p.loop);
} }
else else
tm_stop(t); tm_stop(t);
@ -419,6 +465,7 @@ bgp_close_conn(struct bgp_conn *conn)
rfree(conn->tx_ev); rfree(conn->tx_ev);
conn->tx_ev = NULL; conn->tx_ev = NULL;
rfree(conn->sk); rfree(conn->sk);
conn->sk = NULL; conn->sk = NULL;
@ -564,7 +611,7 @@ bgp_stop(struct bgp_proto *p, int subcode, byte *data, uint len)
if (c->ptx) if (c->ptx)
bgp_free_pending_tx(c); bgp_free_pending_tx(c);
ev_schedule(p->event); proto_send_event(&p->p, p->event);
} }
static inline void static inline void
@ -755,7 +802,7 @@ bgp_conn_enter_close_state(struct bgp_conn *conn)
conn->sk->rx_hook = NULL; conn->sk->rx_hook = NULL;
/* Timeout for CLOSE state, if we cannot send notification soon then we just hangup */ /* Timeout for CLOSE state, if we cannot send notification soon then we just hangup */
bgp_start_timer(conn->hold_timer, 10); bgp_start_timer(p, conn->hold_timer, 10);
if (os == BS_ESTABLISHED) if (os == BS_ESTABLISHED)
bgp_conn_leave_established_state(p); bgp_conn_leave_established_state(p);
@ -769,7 +816,7 @@ bgp_conn_enter_idle_state(struct bgp_conn *conn)
bgp_close_conn(conn); bgp_close_conn(conn);
bgp_conn_set_state(conn, BS_IDLE); bgp_conn_set_state(conn, BS_IDLE);
ev_schedule(p->event); proto_send_event(&p->p, p->event);
if (os == BS_ESTABLISHED) if (os == BS_ESTABLISHED)
bgp_conn_leave_established_state(p); bgp_conn_leave_established_state(p);
@ -840,7 +887,7 @@ bgp_handle_graceful_restart(struct bgp_proto *p)
ASSERT(p->gr_active_num > 0); ASSERT(p->gr_active_num > 0);
proto_notify_state(&p->p, PS_START); proto_notify_state(&p->p, PS_START);
tm_start(p->gr_timer, p->conn->remote_caps->gr_time S); tm_start_in(p->gr_timer, p->conn->remote_caps->gr_time S, p->p.loop);
} }
static void static void
@ -954,7 +1001,7 @@ bgp_graceful_restart_timeout(timer *t)
/* Channel is in GR, and supports LLGR -> start LLGR */ /* Channel is in GR, and supports LLGR -> start LLGR */
c->gr_active = BGP_GRS_LLGR; c->gr_active = BGP_GRS_LLGR;
tm_start(c->stale_timer, c->stale_time S); tm_start_in(c->stale_timer, c->stale_time S, p->p.loop);
bgp_graceful_restart_feed(c); bgp_graceful_restart_feed(c);
} }
} }
@ -1028,7 +1075,7 @@ bgp_send_open(struct bgp_conn *conn)
bgp_prepare_capabilities(conn); bgp_prepare_capabilities(conn);
bgp_schedule_packet(conn, NULL, PKT_OPEN); bgp_schedule_packet(conn, NULL, PKT_OPEN);
bgp_conn_set_state(conn, BS_OPENSENT); bgp_conn_set_state(conn, BS_OPENSENT);
bgp_start_timer(conn->hold_timer, conn->bgp->cf->initial_hold_time); bgp_start_timer(conn->bgp, conn->hold_timer, conn->bgp->cf->initial_hold_time);
} }
static void static void
@ -1105,7 +1152,7 @@ bgp_hold_timeout(timer *t)
and perhaps just not processed BGP packets in time. */ and perhaps just not processed BGP packets in time. */
if (sk_rx_ready(conn->sk) > 0) if (sk_rx_ready(conn->sk) > 0)
bgp_start_timer(conn->hold_timer, 10); bgp_start_timer(p, conn->hold_timer, 10);
else if ((conn->state == BS_ESTABLISHED) && p->llgr_ready) else if ((conn->state == BS_ESTABLISHED) && p->llgr_ready)
{ {
BGP_TRACE(D_EVENTS, "Hold timer expired"); BGP_TRACE(D_EVENTS, "Hold timer expired");
@ -1165,7 +1212,7 @@ bgp_active(struct bgp_proto *p)
BGP_TRACE(D_EVENTS, "Connect delayed by %d seconds", delay); BGP_TRACE(D_EVENTS, "Connect delayed by %d seconds", delay);
bgp_setup_conn(p, conn); bgp_setup_conn(p, conn);
bgp_conn_set_state(conn, BS_ACTIVE); bgp_conn_set_state(conn, BS_ACTIVE);
bgp_start_timer(conn->connect_timer, delay); bgp_start_timer(p, conn->connect_timer, delay);
} }
/** /**
@ -1196,6 +1243,7 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c
s->tos = IP_PREC_INTERNET_CONTROL; s->tos = IP_PREC_INTERNET_CONTROL;
s->password = p->cf->password; s->password = p->cf->password;
s->tx_hook = bgp_connected; s->tx_hook = bgp_connected;
s->flags = SKF_THREAD;
BGP_TRACE(D_EVENTS, "Connecting to %I%J from local address %I%J", BGP_TRACE(D_EVENTS, "Connecting to %I%J from local address %I%J",
s->daddr, ipa_is_link_local(s->daddr) ? p->cf->iface : NULL, s->daddr, ipa_is_link_local(s->daddr) ? p->cf->iface : NULL,
s->saddr, ipa_is_link_local(s->saddr) ? s->iface : NULL); s->saddr, ipa_is_link_local(s->saddr) ? s->iface : NULL);
@ -1212,7 +1260,7 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c
goto err; goto err;
DBG("BGP: Waiting for connect success\n"); DBG("BGP: Waiting for connect success\n");
bgp_start_timer(conn->connect_timer, p->cf->connect_retry_time); bgp_start_timer(p, conn->connect_timer, p->cf->connect_retry_time);
return; return;
err: err:
@ -1239,6 +1287,8 @@ bgp_find_proto(sock *sk)
/* sk->iface is valid only if src or dst address is link-local */ /* sk->iface is valid only if src or dst address is link-local */
int link = ipa_is_link_local(sk->saddr) || ipa_is_link_local(sk->daddr); int link = ipa_is_link_local(sk->saddr) || ipa_is_link_local(sk->daddr);
LOCK_DOMAIN(rtable, bgp_listen_domain);
WALK_LIST(req, bs->requests) WALK_LIST(req, bs->requests)
{ {
struct bgp_proto *p = SKIP_BACK(struct bgp_proto, listen, req); struct bgp_proto *p = SKIP_BACK(struct bgp_proto, listen, req);
@ -1257,6 +1307,7 @@ bgp_find_proto(sock *sk)
} }
} }
UNLOCK_DOMAIN(rtable, bgp_listen_domain);
return best; return best;
} }
@ -1275,6 +1326,8 @@ bgp_find_proto(sock *sk)
static int static int
bgp_incoming_connection(sock *sk, uint dummy UNUSED) bgp_incoming_connection(sock *sk, uint dummy UNUSED)
{ {
ASSERT_DIE(birdloop_inside(&main_birdloop));
struct bgp_proto *p; struct bgp_proto *p;
int acc, hops; int acc, hops;
@ -1288,6 +1341,8 @@ bgp_incoming_connection(sock *sk, uint dummy UNUSED)
return 0; return 0;
} }
birdloop_enter(p->p.loop);
/* /*
* BIRD should keep multiple incoming connections in OpenSent state (for * BIRD should keep multiple incoming connections in OpenSent state (for
* details RFC 4271 8.2.1 par 3), but it keeps just one. Duplicate incoming * details RFC 4271 8.2.1 par 3), but it keeps just one. Duplicate incoming
@ -1317,7 +1372,7 @@ bgp_incoming_connection(sock *sk, uint dummy UNUSED)
if (!acc) if (!acc)
{ {
rfree(sk); rfree(sk);
return 0; goto leave;
} }
hops = p->cf->multihop ? : 1; hops = p->cf->multihop ? : 1;
@ -1342,19 +1397,24 @@ bgp_incoming_connection(sock *sk, uint dummy UNUSED)
p = bgp_spawn(p, sk->daddr); p = bgp_spawn(p, sk->daddr);
p->postponed_sk = sk; p->postponed_sk = sk;
rmove(sk, p->p.pool); rmove(sk, p->p.pool);
return 0; goto leave;
} }
rmove(sk, p->p.pool); rmove(sk, p->p.pool);
sk_reloop(sk, p->p.loop);
bgp_setup_conn(p, &p->incoming_conn); bgp_setup_conn(p, &p->incoming_conn);
bgp_setup_sk(&p->incoming_conn, sk); bgp_setup_sk(&p->incoming_conn, sk);
bgp_send_open(&p->incoming_conn); bgp_send_open(&p->incoming_conn);
return 0; goto leave;
err: err:
sk_log_error(sk, p->p.name); sk_log_error(sk, p->p.name);
log(L_ERR "%s: Incoming connection aborted", p->p.name); log(L_ERR "%s: Incoming connection aborted", p->p.name);
rfree(sk); rfree(sk);
leave:
birdloop_leave(p->p.loop);
return 0; return 0;
} }
@ -1679,7 +1739,7 @@ bgp_start(struct proto *P)
.hook = bgp_start_locked, .hook = bgp_start_locked,
.data = p, .data = p,
}; };
lock->target = &global_event_list; lock->target = proto_event_list(P);
/* For dynamic BGP, we use inst 1 to avoid collisions with regular BGP */ /* For dynamic BGP, we use inst 1 to avoid collisions with regular BGP */
if (bgp_is_dynamic(p)) if (bgp_is_dynamic(p))
@ -2776,4 +2836,5 @@ void bgp_build(void)
{ {
proto_build(&proto_bgp); proto_build(&proto_bgp);
bgp_register_attrs(); bgp_register_attrs();
bgp_listen_domain = DOMAIN_NEW(rtable, "BGP Listen Sockets");
} }

View File

@ -547,7 +547,7 @@ bgp_parse_error(struct bgp_parse_state *s, uint subcode)
} }
void bgp_start_timer(timer *t, uint value); void bgp_start_timer(struct bgp_proto *p, timer *t, uint value);
void bgp_check_config(struct bgp_config *c); void bgp_check_config(struct bgp_config *c);
void bgp_error(struct bgp_conn *c, unsigned code, unsigned subcode, byte *data, int len); void bgp_error(struct bgp_conn *c, unsigned code, unsigned subcode, byte *data, int len);
void bgp_close_conn(struct bgp_conn *c); void bgp_close_conn(struct bgp_conn *c);

View File

@ -52,6 +52,7 @@ proto: bgp_proto '}' ;
bgp_proto_start: proto_start BGP { bgp_proto_start: proto_start BGP {
this_proto = proto_config_new(&proto_bgp, $1); this_proto = proto_config_new(&proto_bgp, $1);
this_proto->loop_order = DOMAIN_ORDER(proto);
BGP_CFG->local_port = BGP_PORT; BGP_CFG->local_port = BGP_PORT;
BGP_CFG->remote_port = BGP_PORT; BGP_CFG->remote_port = BGP_PORT;
BGP_CFG->multihop = -1; /* undefined */ BGP_CFG->multihop = -1; /* undefined */

View File

@ -973,7 +973,7 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
conn->hold_time, conn->keepalive_time, p->remote_as, p->remote_id, conn->as4_session); conn->hold_time, conn->keepalive_time, p->remote_as, p->remote_id, conn->as4_session);
bgp_schedule_packet(conn, NULL, PKT_KEEPALIVE); bgp_schedule_packet(conn, NULL, PKT_KEEPALIVE);
bgp_start_timer(conn->hold_timer, conn->hold_time); bgp_start_timer(p, conn->hold_timer, conn->hold_time);
bgp_conn_enter_openconfirm_state(conn); bgp_conn_enter_openconfirm_state(conn);
} }
@ -2572,7 +2572,7 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, uint len)
if (conn->state != BS_ESTABLISHED) if (conn->state != BS_ESTABLISHED)
{ bgp_error(conn, 5, fsm_err_subcode[conn->state], NULL, 0); return; } { bgp_error(conn, 5, fsm_err_subcode[conn->state], NULL, 0); return; }
bgp_start_timer(conn->hold_timer, conn->hold_time); bgp_start_timer(p, conn->hold_timer, conn->hold_time);
struct lp_state tmpp; struct lp_state tmpp;
lp_save(tmp_linpool, &tmpp); lp_save(tmp_linpool, &tmpp);
@ -2913,7 +2913,7 @@ bgp_fire_tx(struct bgp_conn *conn)
{ {
conn->packets_to_send &= ~(1 << PKT_KEEPALIVE); conn->packets_to_send &= ~(1 << PKT_KEEPALIVE);
BGP_TRACE(D_PACKETS, "Sending KEEPALIVE"); BGP_TRACE(D_PACKETS, "Sending KEEPALIVE");
bgp_start_timer(conn->keepalive_timer, conn->keepalive_time); bgp_start_timer(p, conn->keepalive_timer, conn->keepalive_time);
return bgp_send(conn, PKT_KEEPALIVE, BGP_HEADER_LENGTH); return bgp_send(conn, PKT_KEEPALIVE, BGP_HEADER_LENGTH);
} }
else while (conn->channels_to_send) else while (conn->channels_to_send)
@ -3002,7 +3002,7 @@ bgp_schedule_packet(struct bgp_conn *conn, struct bgp_channel *c, int type)
conn->packets_to_send |= 1 << type; conn->packets_to_send |= 1 << type;
if ((conn->sk->tpos == conn->sk->tbuf) && !ev_active(conn->tx_ev)) if ((conn->sk->tpos == conn->sk->tbuf) && !ev_active(conn->tx_ev))
ev_schedule(conn->tx_ev); proto_send_event(&p->p, conn->tx_ev);
} }
void void
bgp_kick_tx(void *vconn) bgp_kick_tx(void *vconn)
@ -3219,7 +3219,7 @@ bgp_rx_keepalive(struct bgp_conn *conn)
struct bgp_proto *p = conn->bgp; struct bgp_proto *p = conn->bgp;
BGP_TRACE(D_PACKETS, "Got KEEPALIVE"); BGP_TRACE(D_PACKETS, "Got KEEPALIVE");
bgp_start_timer(conn->hold_timer, conn->hold_time); bgp_start_timer(p, conn->hold_timer, conn->hold_time);
if (conn->state == BS_OPENCONFIRM) if (conn->state == BS_OPENCONFIRM)
{ bgp_conn_enter_established_state(conn); return; } { bgp_conn_enter_established_state(conn); return; }