0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2025-01-03 07:31:54 +00:00

BGP: opening and closing listening socket asynchronously

This commit is contained in:
Maria Matejka 2023-02-05 19:54:06 +01:00
parent 597a4b33a7
commit 0bb04d5390
2 changed files with 147 additions and 100 deletions

View File

@ -127,7 +127,11 @@
#include "bgp.h"
static void bgp_listen_create(void *);
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 event bgp_listen_event = { .hook = bgp_listen_create };
static void bgp_connect(struct bgp_proto *p);
@ -139,73 +143,33 @@ static void bgp_update_bfd(struct bgp_proto *p, const struct bfd_options *bfd);
static int bgp_incoming_connection(sock *sk, uint dummy UNUSED);
static void bgp_listen_sock_err(sock *sk UNUSED, int err);
static void bgp_initiate_disable(struct bgp_proto *p, int err_val);
/**
* bgp_open - open a BGP instance
* @p: BGP instance
*
* This function allocates and configures shared BGP resources, mainly listening
* sockets. Should be called as the last step during initialization (when lock
* is acquired and neighbor is ready). When error, caller should change state to
* PS_DOWN and return immediately.
*/
static int
bgp_open(struct bgp_proto *p)
static inline int
bgp_setup_auth(struct bgp_proto *p, int enable)
{
struct bgp_socket *bs = NULL;
struct iface *ifa = p->cf->strict_bind ? p->cf->iface : NULL;
ip_addr addr = p->cf->strict_bind ? p->cf->local_ip :
(p->ipv4 ? IPA_NONE4 : IPA_NONE6);
uint port = p->cf->local_port;
uint flags = p->cf->free_bind ? SKF_FREEBIND : 0;
uint flag_mask = SKF_FREEBIND;
/* We assume that cf->iface is defined iff cf->local_ip is link-local */
WALK_LIST(bs, bgp_sockets)
if (ipa_equal(bs->sk->saddr, addr) &&
(bs->sk->sport == port) &&
(bs->sk->iface == ifa) &&
(bs->sk->vrf == p->p.vrf) &&
((bs->sk->flags & flag_mask) == flags))
if (p->cf->password && p->listen.sock)
{
bs->uc++;
p->sock = bs;
return 0;
ip_addr prefix = p->cf->remote_ip;
int pxlen = -1;
if (p->cf->remote_range)
{
prefix = net_prefix(p->cf->remote_range);
pxlen = net_pxlen(p->cf->remote_range);
}
sock *sk = sk_new(proto_pool);
sk->type = SK_TCP_PASSIVE;
sk->ttl = 255;
sk->saddr = addr;
sk->sport = port;
sk->iface = ifa;
sk->vrf = p->p.vrf;
sk->flags = flags;
sk->tos = IP_PREC_INTERNET_CONTROL;
sk->rbsize = BGP_RX_BUFFER_SIZE;
sk->tbsize = BGP_TX_BUFFER_SIZE;
sk->rx_hook = bgp_incoming_connection;
sk->err_hook = bgp_listen_sock_err;
int rv = sk_set_md5_auth(p->listen.sock->sk,
p->cf->local_ip, prefix, pxlen, p->cf->iface,
enable ? p->cf->password : NULL, p->cf->setkey);
if (sk_open(sk) < 0)
goto err;
bs = mb_allocz(proto_pool, sizeof(struct bgp_socket));
bs->sk = sk;
bs->uc = 1;
p->sock = bs;
sk->data = bs;
add_tail(&bgp_sockets, &bs->n);
if (rv < 0)
sk_log_error(p->listen.sock->sk, p->p.name);
return rv;
}
else
return 0;
err:
sk_log_error(sk, p->p.name);
log(L_ERR "%s: Cannot open listening socket", p->p.name);
rfree(sk);
return -1;
}
/**
@ -217,43 +181,119 @@ err:
static void
bgp_close(struct bgp_proto *p)
{
struct bgp_socket *bs = p->sock;
struct bgp_listen_request *req = &p->listen;
struct bgp_socket *bs = req->sock;
ASSERT(bs && bs->uc);
ASSERT(bs);
if (--bs->uc)
return;
req->sock = NULL;
rem_node(&req->n);
if (EMPTY_LIST(bs->requests))
ev_schedule(&bgp_listen_event);
}
/**
* bgp_open - open a BGP instance
* @p: BGP instance
*
* This function allocates and configures shared BGP resources, mainly listening
* sockets. Should be called as the last step during initialization (when lock
* is acquired and neighbor is ready). When error, caller should change state to
* PS_DOWN and return immediately.
*/
static void
bgp_open(struct bgp_proto *p)
{
struct bgp_listen_request *req = &p->listen;
/* 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->vrf = p->p.vrf;
req->addr = p->cf->strict_bind ? p->cf->local_ip :
(p->ipv4 ? IPA_NONE4 : IPA_NONE6);
req->port = p->cf->local_port;
req->flags = p->cf->free_bind ? SKF_FREEBIND : 0;
add_tail(&bgp_listen_pending, &req->n);
ev_schedule(&bgp_listen_event);
}
static void
bgp_listen_create(void *_ UNUSED)
{
uint flag_mask = SKF_FREEBIND;
struct bgp_listen_request *req;
WALK_LIST_FIRST(req, bgp_listen_pending)
{
struct bgp_proto *p = SKIP_BACK(struct bgp_proto, listen, req);
rem_node(&req->n);
/* First try to find existing socket */
struct bgp_socket *bs;
WALK_LIST(bs, bgp_sockets)
if (ipa_equal(bs->sk->saddr, req->addr) &&
(bs->sk->sport == req->port) &&
(bs->sk->iface == req->iface) &&
(bs->sk->vrf == req->vrf) &&
((bs->sk->flags & flag_mask) == req->flags))
break;
/* Not found any */
if (!NODE_VALID(bs))
{
sock *sk = sk_new(proto_pool);
sk->type = SK_TCP_PASSIVE;
sk->ttl = 255;
sk->saddr = req->addr;
sk->sport = req->port;
sk->iface = req->iface;
sk->vrf = req->vrf;
sk->flags = req->flags;
sk->tos = IP_PREC_INTERNET_CONTROL;
sk->rbsize = BGP_RX_BUFFER_SIZE;
sk->tbsize = BGP_TX_BUFFER_SIZE;
sk->rx_hook = bgp_incoming_connection;
sk->err_hook = bgp_listen_sock_err;
if (sk_open(sk) < 0)
{
sk_log_error(sk, p->p.name);
log(L_ERR "%s: Cannot open listening socket", p->p.name);
rfree(sk);
bgp_initiate_disable(p, BEM_NO_SOCKET);
continue;
}
bs = mb_allocz(proto_pool, sizeof(struct bgp_socket));
bs->sk = sk;
sk->data = bs;
init_list(&bs->requests);
add_tail(&bgp_sockets, &bs->n);
}
add_tail(&bs->requests, &req->n);
req->sock = bs;
if (bgp_setup_auth(p, 1) < 0)
{
bgp_close(p);
bgp_initiate_disable(p, BEM_INVALID_MD5);
}
}
/* Cleanup leftover listening sockets */
struct bgp_socket *bs;
node *nxt;
WALK_LIST_DELSAFE(bs, nxt, bgp_sockets)
if (EMPTY_LIST(bs->requests))
{
rfree(bs->sk);
rem_node(&bs->n);
mb_free(bs);
}
static inline int
bgp_setup_auth(struct bgp_proto *p, int enable)
{
if (p->cf->password)
{
ip_addr prefix = p->cf->remote_ip;
int pxlen = -1;
if (p->cf->remote_range)
{
prefix = net_prefix(p->cf->remote_range);
pxlen = net_pxlen(p->cf->remote_range);
}
int rv = sk_set_md5_auth(p->sock->sk,
p->cf->local_ip, prefix, pxlen, p->cf->iface,
enable ? p->cf->password : NULL, p->cf->setkey);
if (rv < 0)
sk_log_error(p->sock->sk, p->p.name);
return rv;
}
else
return 0;
}
static inline struct bgp_channel *
@ -296,13 +336,7 @@ bgp_startup_timeout(timer *t)
static void
bgp_initiate(struct bgp_proto *p)
{
int err_val;
if (bgp_open(p) < 0)
{ err_val = BEM_NO_SOCKET; goto err1; }
if (bgp_setup_auth(p, 1) < 0)
{ err_val = BEM_INVALID_MD5; goto err2; }
bgp_open(p);
if (p->cf->bfd)
bgp_update_bfd(p, p->cf->bfd);
@ -315,12 +349,11 @@ bgp_initiate(struct bgp_proto *p)
}
else
bgp_startup(p);
}
return;
err2:
bgp_close(p);
err1:
static void
bgp_initiate_disable(struct bgp_proto *p, int err_val)
{
p->p.disabled = 1;
bgp_store_error(p, NULL, BE_MISC, err_val);
@ -1146,12 +1179,15 @@ static struct bgp_proto *
bgp_find_proto(sock *sk)
{
struct bgp_proto *best = NULL;
struct bgp_proto *p;
struct bgp_socket *bs = sk->data;
struct bgp_listen_request *req;
/* 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);
WALK_LIST(p, proto_list)
WALK_LIST(req, bs->requests)
{
struct bgp_proto *p = SKIP_BACK(struct bgp_proto, listen, req);
if ((p->p.proto == &proto_bgp) &&
(ipa_equal(p->remote_ip, sk->daddr) || bgp_is_dynamic(p)) &&
(!p->cf->remote_range || ipa_in_netX(sk->daddr, p->cf->remote_range)) &&
@ -1165,6 +1201,7 @@ bgp_find_proto(sock *sk)
if (!bgp_is_dynamic(p))
break;
}
}
return best;
}

View File

@ -266,8 +266,8 @@ struct bgp_caps {
struct bgp_socket {
node n; /* Node in global bgp_sockets */
list requests; /* Listen requests */
sock *sk; /* Real listening socket */
u32 uc; /* Use count */
};
struct bgp_stats {
@ -302,6 +302,16 @@ struct bgp_conn {
uint hold_time, keepalive_time; /* Times calculated from my and neighbor's requirements */
};
struct bgp_listen_request {
node n; /* Node in bgp_socket / pending list */
struct bgp_socket *sock; /* Assigned socket */
ip_addr addr;
struct iface *iface;
struct iface *vrf;
uint port;
uint flags;
};
struct bgp_proto {
struct proto p;
const struct bgp_config *cf; /* Shortcut to BGP configuration */
@ -333,7 +343,7 @@ struct bgp_proto {
struct bgp_conn incoming_conn; /* Incoming connection we have neither accepted nor rejected yet */
struct object_lock *lock; /* Lock for neighbor connection */
struct neighbor *neigh; /* Neighbor entry corresponding to remote ip, NULL if multihop */
struct bgp_socket *sock; /* Shared listening socket */
struct bgp_listen_request listen; /* Shared listening socket */
struct bfd_request *bfd_req; /* BFD request, if BFD is used */
struct birdsock *postponed_sk; /* Postponed incoming socket for dynamic BGP */
struct bgp_stats stats; /* BGP statistics */