mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-04 06:55:18 +00:00
bfdd: support connecting to BFD data plane
Add option to connect to a data plane server instead of receiving connections. Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
This commit is contained in:
parent
319d840c02
commit
6655b43d51
@ -762,10 +762,11 @@ int ptm_bfd_notify(struct bfd_session *bs, uint8_t notify_state);
|
||||
/**
|
||||
* Initialize BFD data plane infrastructure for distributed BFD implementation.
|
||||
*
|
||||
* \param sa listening socket address.
|
||||
* \param salen listening socket address structure length.
|
||||
* \param sa socket address.
|
||||
* \param salen socket address structure length.
|
||||
* \param client `true` means connecting socket, `false` listening socket.
|
||||
*/
|
||||
void bfd_dplane_init(const struct sockaddr *sa, socklen_t salen);
|
||||
void bfd_dplane_init(const struct sockaddr *sa, socklen_t salen, bool client);
|
||||
|
||||
/**
|
||||
* Attempts to delegate the BFD session liveness detection to hardware.
|
||||
|
18
bfdd/bfdd.c
18
bfdd/bfdd.c
@ -204,6 +204,7 @@ static void
|
||||
distributed_bfd_init(const char *arg)
|
||||
{
|
||||
char *sptr, *saux;
|
||||
bool is_client = false;
|
||||
size_t slen;
|
||||
socklen_t salen;
|
||||
char addr[64];
|
||||
@ -235,11 +236,17 @@ distributed_bfd_init(const char *arg)
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
|
||||
/* Fill the address information. */
|
||||
if (strcmp(type, "unix") == 0) {
|
||||
if (strcmp(type, "unix") == 0 || strcmp(type, "unixc") == 0) {
|
||||
if (strcmp(type, "unixc") == 0)
|
||||
is_client = true;
|
||||
|
||||
salen = sizeof(sa.sun);
|
||||
sa.sun.sun_family = AF_UNIX;
|
||||
strlcpy(sa.sun.sun_path, addr, sizeof(sa.sun.sun_path));
|
||||
} else if (strcmp(type, "ipv4") == 0) {
|
||||
} else if (strcmp(type, "ipv4") == 0 || strcmp(type, "ipv4c") == 0) {
|
||||
if (strcmp(type, "ipv4c") == 0)
|
||||
is_client = true;
|
||||
|
||||
salen = sizeof(sa.sin);
|
||||
sa.sin.sin_family = AF_INET;
|
||||
|
||||
@ -255,7 +262,10 @@ distributed_bfd_init(const char *arg)
|
||||
if (inet_pton(AF_INET, addr, &sa.sin.sin_addr) != 1)
|
||||
errx(1, "%s: inet_pton: invalid address %s", __func__,
|
||||
addr);
|
||||
} else if (strcmp(type, "ipv6") == 0) {
|
||||
} else if (strcmp(type, "ipv6") == 0 || strcmp(type, "ipv6c") == 0) {
|
||||
if (strcmp(type, "ipv6c") == 0)
|
||||
is_client = true;
|
||||
|
||||
salen = sizeof(sa.sin6);
|
||||
sa.sin6.sin6_family = AF_INET6;
|
||||
|
||||
@ -295,7 +305,7 @@ distributed_bfd_init(const char *arg)
|
||||
}
|
||||
|
||||
/* Initialize BFD data plane listening socket. */
|
||||
bfd_dplane_init((struct sockaddr *)&sa, salen);
|
||||
bfd_dplane_init((struct sockaddr *)&sa, salen, is_client);
|
||||
}
|
||||
|
||||
static void bg_init(void)
|
||||
|
219
bfdd/dplane.c
219
bfdd/dplane.c
@ -22,6 +22,7 @@
|
||||
#include <zebra.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
@ -35,6 +36,7 @@
|
||||
#include <time.h>
|
||||
|
||||
#include "lib/hook.h"
|
||||
#include "lib/network.h"
|
||||
#include "lib/printfrr.h"
|
||||
#include "lib/stream.h"
|
||||
#include "lib/thread.h"
|
||||
@ -52,6 +54,19 @@ DEFINE_MTYPE_STATIC(BFDD, BFDD_DPLANE_CTX, "Data plane client allocated memory")
|
||||
struct bfd_dplane_ctx {
|
||||
/** Client file descriptor. */
|
||||
int sock;
|
||||
/** Is this a connected or accepted? */
|
||||
bool client;
|
||||
/** Is the socket still connecting? */
|
||||
bool connecting;
|
||||
/** Client/server address. */
|
||||
union {
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_in sin;
|
||||
struct sockaddr_in6 sin6;
|
||||
struct sockaddr_un sun;
|
||||
} addr;
|
||||
/** Address length. */
|
||||
socklen_t addrlen;
|
||||
/** Data plane current last used ID. */
|
||||
uint16_t last_id;
|
||||
|
||||
@ -63,6 +78,8 @@ struct bfd_dplane_ctx {
|
||||
struct thread *inbufev;
|
||||
/** Output event data. */
|
||||
struct thread *outbufev;
|
||||
/** Connection event. */
|
||||
struct thread *connectev;
|
||||
|
||||
/** Amount of bytes read. */
|
||||
uint64_t in_bytes;
|
||||
@ -89,6 +106,8 @@ struct bfd_dplane_ctx {
|
||||
*/
|
||||
typedef void (*bfd_dplane_expect_cb)(struct bfddp_message *msg, void *arg);
|
||||
|
||||
static int bfd_dplane_client_connect(struct thread *t);
|
||||
static bool bfd_dplane_client_connecting(struct bfd_dplane_ctx *bdc);
|
||||
static void bfd_dplane_ctx_free(struct bfd_dplane_ctx *bdc);
|
||||
static int _bfd_dplane_add_session(struct bfd_dplane_ctx *bdc,
|
||||
struct bfd_session *bs);
|
||||
@ -310,7 +329,14 @@ static ssize_t bfd_dplane_flush(struct bfd_dplane_ctx *bdc)
|
||||
|
||||
static int bfd_dplane_write(struct thread *t)
|
||||
{
|
||||
bfd_dplane_flush(THREAD_ARG(t));
|
||||
struct bfd_dplane_ctx *bdc = THREAD_ARG(t);
|
||||
|
||||
/* Handle connection stage. */
|
||||
if (bdc->connecting && bfd_dplane_client_connecting(bdc))
|
||||
return 0;
|
||||
|
||||
bfd_dplane_flush(bdc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -395,6 +421,10 @@ static int bfd_dplane_enqueue(struct bfd_dplane_ctx *bdc, const void *buf,
|
||||
{
|
||||
size_t rlen;
|
||||
|
||||
/* Handle not connected yet client. */
|
||||
if (bdc->client && bdc->sock == -1)
|
||||
return -1;
|
||||
|
||||
/* Not enough space. */
|
||||
if (buflen > STREAM_WRITEABLE(bdc->outbuf)) {
|
||||
bdc->out_fullev++;
|
||||
@ -626,6 +656,11 @@ static struct bfd_dplane_ctx *bfd_dplane_ctx_new(int sock)
|
||||
bdc->sock = sock;
|
||||
bdc->inbuf = stream_new(BFD_DPLANE_CLIENT_BUF_SIZE);
|
||||
bdc->outbuf = stream_new(BFD_DPLANE_CLIENT_BUF_SIZE);
|
||||
|
||||
/* If not socket ready, skip read and session registration. */
|
||||
if (sock == -1)
|
||||
return bdc;
|
||||
|
||||
thread_add_read(master, bfd_dplane_read, bdc, sock, &bdc->inbufev);
|
||||
|
||||
/* Register all unattached sessions. */
|
||||
@ -654,6 +689,25 @@ static void bfd_dplane_ctx_free(struct bfd_dplane_ctx *bdc)
|
||||
zlog_debug("%s: terminating data plane client %d", __func__,
|
||||
bdc->sock);
|
||||
|
||||
/* Client mode has special treatment. */
|
||||
if (bdc->client) {
|
||||
/* Disable connection event if any. */
|
||||
THREAD_OFF(bdc->connectev);
|
||||
|
||||
/* Normal treatment on shutdown. */
|
||||
if (bglobal.bg_shutdown)
|
||||
goto free_resources;
|
||||
|
||||
/* Attempt reconnection. */
|
||||
socket_close(&bdc->sock);
|
||||
THREAD_OFF(bdc->inbufev);
|
||||
THREAD_OFF(bdc->outbufev);
|
||||
thread_add_timer(master, bfd_dplane_client_connect, bdc, 3,
|
||||
&bdc->connectev);
|
||||
return;
|
||||
}
|
||||
|
||||
free_resources:
|
||||
/* Remove from the list of attached data planes. */
|
||||
TAILQ_REMOVE(&bglobal.bg_dplaneq, bdc, entry);
|
||||
|
||||
@ -810,6 +864,146 @@ reschedule_and_return:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Data plane connecting socket.
|
||||
*/
|
||||
static void _bfd_dplane_client_bootstrap(struct bfd_dplane_ctx *bdc)
|
||||
{
|
||||
bdc->connecting = false;
|
||||
|
||||
/* Clean up buffers. */
|
||||
stream_reset(bdc->inbuf);
|
||||
stream_reset(bdc->outbuf);
|
||||
|
||||
/* Ask for read notifications. */
|
||||
thread_add_read(master, bfd_dplane_read, bdc, bdc->sock, &bdc->inbufev);
|
||||
|
||||
/* Remove all sessions then register again to send them all. */
|
||||
bfd_key_iterate(_bfd_session_unregister_dplane, bdc);
|
||||
bfd_key_iterate(_bfd_session_register_dplane, bdc);
|
||||
}
|
||||
|
||||
static bool bfd_dplane_client_connecting(struct bfd_dplane_ctx *bdc)
|
||||
{
|
||||
int rv;
|
||||
socklen_t rvlen = sizeof(rv);
|
||||
|
||||
/* Make sure `errno` is reset, then test `getsockopt` success. */
|
||||
errno = 0;
|
||||
if (getsockopt(bdc->sock, SOL_SOCKET, SO_ERROR, &rv, &rvlen) == -1)
|
||||
rv = -1;
|
||||
|
||||
/* Connection successful. */
|
||||
if (rv == 0) {
|
||||
if (bglobal.debug_dplane)
|
||||
zlog_debug("%s: connected to server: %d", __func__,
|
||||
bdc->sock);
|
||||
|
||||
_bfd_dplane_client_bootstrap(bdc);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (rv) {
|
||||
case EINTR:
|
||||
case EAGAIN:
|
||||
case EALREADY:
|
||||
case EINPROGRESS:
|
||||
/* non error, wait more. */
|
||||
return true;
|
||||
|
||||
default:
|
||||
zlog_warn("%s: connection failed: %s", __func__,
|
||||
strerror(errno));
|
||||
bfd_dplane_ctx_free(bdc);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static int bfd_dplane_client_connect(struct thread *t)
|
||||
{
|
||||
struct bfd_dplane_ctx *bdc = THREAD_ARG(t);
|
||||
int rv, sock;
|
||||
socklen_t rvlen = sizeof(rv);
|
||||
|
||||
/* Allocate new socket. */
|
||||
sock = socket(bdc->addr.sa.sa_family, SOCK_STREAM, 0);
|
||||
if (sock == -1) {
|
||||
zlog_warn("%s: failed to initialize socket: %s", __func__,
|
||||
strerror(errno));
|
||||
goto reschedule_connect;
|
||||
}
|
||||
|
||||
/* Set non blocking socket. */
|
||||
set_nonblocking(sock);
|
||||
|
||||
/* Set 'no delay' (disables nagle algorithm) for IPv4/IPv6. */
|
||||
rv = 1;
|
||||
if (bdc->addr.sa.sa_family != AF_UNIX
|
||||
&& setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &rv, rvlen) == -1)
|
||||
zlog_warn("%s: TCP_NODELAY: %s", __func__, strerror(errno));
|
||||
|
||||
/* Attempt to connect. */
|
||||
rv = connect(sock, &bdc->addr.sa, bdc->addrlen);
|
||||
if (rv == -1 && (errno != EINPROGRESS && errno != EAGAIN)) {
|
||||
zlog_warn("%s: data plane connection failed: %s", __func__,
|
||||
strerror(errno));
|
||||
goto reschedule_connect;
|
||||
}
|
||||
|
||||
bdc->sock = sock;
|
||||
if (rv == -1) {
|
||||
if (bglobal.debug_dplane)
|
||||
zlog_debug("%s: server connection in progress: %d",
|
||||
__func__, sock);
|
||||
|
||||
/* If we are not connected yet, ask for write notifications. */
|
||||
bdc->connecting = true;
|
||||
thread_add_write(master, bfd_dplane_write, bdc, bdc->sock,
|
||||
&bdc->outbufev);
|
||||
} else {
|
||||
if (bglobal.debug_dplane)
|
||||
zlog_debug("%s: server connection: %d", __func__, sock);
|
||||
|
||||
/* Otherwise just start accepting data. */
|
||||
_bfd_dplane_client_bootstrap(bdc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
reschedule_connect:
|
||||
THREAD_OFF(bdc->inbufev);
|
||||
THREAD_OFF(bdc->outbufev);
|
||||
socket_close(&sock);
|
||||
thread_add_timer(master, bfd_dplane_client_connect, bdc, 3,
|
||||
&bdc->connectev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bfd_dplane_client_init(const struct sockaddr *sa, socklen_t salen)
|
||||
{
|
||||
struct bfd_dplane_ctx *bdc;
|
||||
|
||||
/* Allocate context and copy address for reconnection. */
|
||||
bdc = bfd_dplane_ctx_new(-1);
|
||||
if (salen <= sizeof(bdc->addr)) {
|
||||
memcpy(&bdc->addr, sa, salen);
|
||||
bdc->addrlen = sizeof(bdc->addr);
|
||||
} else {
|
||||
memcpy(&bdc->addr, sa, sizeof(bdc->addr));
|
||||
bdc->addrlen = sizeof(bdc->addr);
|
||||
zlog_warn("%s: server address truncated (from %d to %d)",
|
||||
__func__, salen, bdc->addrlen);
|
||||
}
|
||||
|
||||
bdc->client = true;
|
||||
|
||||
thread_add_timer(master, bfd_dplane_client_connect, bdc, 0,
|
||||
&bdc->connectev);
|
||||
|
||||
/* Insert into data plane lists. */
|
||||
TAILQ_INSERT_TAIL(&bglobal.bg_dplaneq, bdc, entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Termination phase of the distributed BFD infrastructure: free all allocated
|
||||
* resources.
|
||||
@ -835,12 +1029,27 @@ static int bfd_dplane_finish_late(void)
|
||||
/*
|
||||
* Data plane exported functions.
|
||||
*/
|
||||
void bfd_dplane_init(const struct sockaddr *sa, socklen_t salen)
|
||||
void bfd_dplane_init(const struct sockaddr *sa, socklen_t salen, bool client)
|
||||
{
|
||||
int sock;
|
||||
|
||||
zlog_info("initializing distributed BFD");
|
||||
|
||||
/* Initialize queue header. */
|
||||
TAILQ_INIT(&bglobal.bg_dplaneq);
|
||||
|
||||
/* Initialize listening socket. */
|
||||
bglobal.bg_dplane_sock = -1;
|
||||
|
||||
/* Observe shutdown events. */
|
||||
hook_register(frr_fini, bfd_dplane_finish_late);
|
||||
|
||||
/* Handle client mode. */
|
||||
if (client) {
|
||||
bfd_dplane_client_init(sa, salen);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Data plane socket creation:
|
||||
* - Set REUSEADDR option for taking over previously open socket.
|
||||
@ -883,12 +1092,6 @@ void bfd_dplane_init(const struct sockaddr *sa, socklen_t salen)
|
||||
bglobal.bg_dplane_sock = sock;
|
||||
thread_add_read(master, bfd_dplane_accept, &bglobal, sock,
|
||||
&bglobal.bg_dplane_sockev);
|
||||
|
||||
/* Initialize queue header. */
|
||||
TAILQ_INIT(&bglobal.bg_dplaneq);
|
||||
|
||||
/* Observe shutdown events. */
|
||||
hook_register(frr_fini, bfd_dplane_finish_late);
|
||||
}
|
||||
|
||||
int bfd_dplane_add_session(struct bfd_session *bs)
|
||||
|
@ -73,6 +73,12 @@ may also be specified (:ref:`common-invocation-options`).
|
||||
|
||||
(if ommited the default port is ``50700``).
|
||||
|
||||
It is also possible to operate in client mode (instead of listening for
|
||||
connections). To connect to a data plane server append the letter 'c' to
|
||||
the protocol, example:
|
||||
|
||||
--dplaneaddr ipv4c:127.0.0.1
|
||||
|
||||
.. note::
|
||||
|
||||
When using UNIX sockets don't forget to check the file permissions
|
||||
|
Loading…
Reference in New Issue
Block a user