diff options
author | Christian Hopps <chopps@labn.net> | 2023-04-29 13:20:18 +0200 |
---|---|---|
committer | Christian Hopps <chopps@labn.net> | 2023-05-28 11:13:22 +0200 |
commit | 99564edc43ecaf099ede84f7fa84b104f2b950b1 (patch) | |
tree | d1e1aa057dafefc5153aeb73b8027131da9bacc3 /lib | |
parent | lib: msg: refactor common connection code from mgmtd (diff) | |
download | frr-99564edc43ecaf099ede84f7fa84b104f2b950b1.tar.xz frr-99564edc43ecaf099ede84f7fa84b104f2b950b1.zip |
lib: mgmtd: add server-side connection code to mgmt_msg
Signed-off-by: Christian Hopps <chopps@labn.net>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/mgmt_msg.c | 150 | ||||
-rw-r--r-- | lib/mgmt_msg.h | 50 |
2 files changed, 188 insertions, 12 deletions
diff --git a/lib/mgmt_msg.c b/lib/mgmt_msg.c index 967606d20..03e896a08 100644 --- a/lib/mgmt_msg.c +++ b/lib/mgmt_msg.c @@ -7,6 +7,7 @@ * Copyright (c) 2023, LabN Consulting, L.L.C. */ #include <zebra.h> +#include "debug.h" #include "network.h" #include "sockopt.h" #include "stream.h" @@ -24,6 +25,8 @@ #define MGMT_MSG_ERR(ms, fmt, ...) \ zlog_err("%s: %s: " fmt, (ms)->idtag, __func__, ##__VA_ARGS__) +DEFINE_MTYPE(LIB, MSG_CONN, "msg connection state"); + /** * Read data from a socket into streams containing 1 or more full msgs headed by * mgmt_msg_hdr which contain API messages (currently protobuf). @@ -641,17 +644,128 @@ void msg_client_cleanup(struct msg_client *client) msg_conn_cleanup(&client->conn); } + +/* + * Server-side connections + */ + +static void msg_server_accept(struct event *event) +{ + struct msg_server *server = EVENT_ARG(event); + int fd; + union sockunion su; + + if (server->fd < 0) + return; + + /* We continue hearing server listen socket. */ + event_add_read(server->loop, msg_server_accept, server, server->fd, + &server->listen_ev); + + memset(&su, 0, sizeof(union sockunion)); + + /* We can handle IPv4 or IPv6 socket. */ + fd = sockunion_accept(server->fd, &su); + if (fd < 0) { + zlog_err("Failed to accept %s client connection: %s", + server->idtag, safe_strerror(errno)); + return; + } + set_nonblocking(fd); + set_cloexec(fd); + + DEBUGD(server->debug, "Accepted new %s connection", server->idtag); + + server->create(fd, &su); +} + +int msg_server_init(struct msg_server *server, const char *sopath, + struct event_loop *loop, + struct msg_conn *(*create)(int fd, union sockunion *su), + const char *idtag, struct debug *debug) +{ + int ret; + int sock; + struct sockaddr_un addr; + mode_t old_mask; + + memset(server, 0, sizeof(*server)); + server->fd = -1; + + sock = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC); + if (sock < 0) { + zlog_err("Failed to create %s server socket: %s", server->idtag, + safe_strerror(errno)); + goto fail; + } + + addr.sun_family = AF_UNIX, + strlcpy(addr.sun_path, sopath, sizeof(addr.sun_path)); + unlink(addr.sun_path); + old_mask = umask(0077); + ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr)); + if (ret < 0) { + zlog_err("Failed to bind %s server socket to '%s': %s", + server->idtag, addr.sun_path, safe_strerror(errno)); + umask(old_mask); + goto fail; + } + umask(old_mask); + + ret = listen(sock, MGMTD_MAX_CONN); + if (ret < 0) { + zlog_err("Failed to listen on %s server socket: %s", + server->idtag, safe_strerror(errno)); + goto fail; + } + + server->fd = sock; + server->loop = loop; + server->sopath = strdup(sopath); + server->idtag = strdup(idtag); + server->create = create; + server->debug = debug; + + event_add_read(server->loop, msg_server_accept, server, server->fd, + &server->listen_ev); + + + DEBUGD(debug, "Started %s server, listening on %s", idtag, sopath); + return 0; + +fail: + if (sock >= 0) + close(sock); + server->fd = -1; + return -1; +} + +void msg_server_cleanup(struct msg_server *server) +{ + DEBUGD(server->debug, "Closing %s server", server->idtag); + + if (server->listen_ev) + EVENT_OFF(server->listen_ev); + if (server->fd >= 0) + close(server->fd); + free((char *)server->sopath); + free((char *)server->idtag); + + memset(server, 0, sizeof(*server)); + server->fd = -1; +} + /* * Initialize and start reading from the accepted socket * * notify_connect - only called for disconnect i.e., connected == false */ -void mgmt_msg_server_accept_init( - struct msg_conn *conn, struct event_loop *tm, int fd, - int (*notify_disconnect)(struct msg_conn *conn), - void (*handle_msg)(uint8_t version, uint8_t *data, size_t len, - struct msg_conn *conn), - size_t max_read, size_t max_write, size_t max_size, const char *idtag) +void msg_conn_accept_init(struct msg_conn *conn, struct event_loop *tm, int fd, + int (*notify_disconnect)(struct msg_conn *conn), + void (*handle_msg)(uint8_t version, uint8_t *data, + size_t len, struct msg_conn *conn), + size_t max_read, size_t max_write, size_t max_size, + const char *idtag) { conn->loop = tm; conn->fd = fd; @@ -669,3 +783,27 @@ void mgmt_msg_server_accept_init( setsockopt_so_sendbuf(conn->fd, MSG_CONN_SEND_BUF_SIZE); setsockopt_so_recvbuf(conn->fd, MSG_CONN_RECV_BUF_SIZE); } + +struct msg_conn * +msg_server_conn_create(struct event_loop *tm, int fd, + int (*notify_disconnect)(struct msg_conn *conn), + void (*handle_msg)(uint8_t version, uint8_t *data, + size_t len, struct msg_conn *conn), + size_t max_read, size_t max_write, size_t max_size, + void *user, const char *idtag) +{ + struct msg_conn *conn = XMALLOC(MTYPE_MSG_CONN, sizeof(*conn)); + memset(conn, 0, sizeof(*conn)); + msg_conn_accept_init(conn, tm, fd, notify_disconnect, handle_msg, + max_read, max_write, max_size, idtag); + conn->user = user; + return conn; +} + +void msg_server_conn_delete(struct msg_conn *conn) +{ + if (!conn) + return; + msg_conn_cleanup(conn); + XFREE(MTYPE_MSG_CONN, conn); +} diff --git a/lib/mgmt_msg.h b/lib/mgmt_msg.h index 71ff03caf..79b1e44c1 100644 --- a/lib/mgmt_msg.h +++ b/lib/mgmt_msg.h @@ -7,9 +7,12 @@ #ifndef _MGMT_MSG_H #define _MGMT_MSG_H +#include "memory.h" #include "stream.h" #include "frrevent.h" +DECLARE_MTYPE(MSG_CONN); + /* * Messages on the stream start with a marker that encodes a version octet. */ @@ -92,6 +95,7 @@ struct msg_conn { int (*notify_disconnect)(struct msg_conn *conn); void (*handle_msg)(uint8_t version, uint8_t *data, size_t len, struct msg_conn *conn); + void *user; bool is_client; bool debug; }; @@ -144,12 +148,46 @@ extern void msg_client_init(struct msg_client *client, struct event_loop *tm, /* * Server-side Connections */ +#define MGMTD_MAX_CONN 32 -extern void mgmt_msg_server_accept_init( - struct msg_conn *client, struct event_loop *tm, int fd, - int (*notify_disconnect)(struct msg_conn *conn), - void (*handle_msg)(uint8_t version, uint8_t *data, size_t len, - struct msg_conn *conn), - size_t max_read, size_t max_write, size_t max_size, const char *idtag); +struct msg_server { + int fd; + struct event_loop *loop; + struct event *listen_ev; + const char *sopath; + const char *idtag; + struct msg_conn *(*create)(int fd, union sockunion *su); + struct debug *debug; +}; + +extern int msg_server_init(struct msg_server *server, const char *sopath, + struct event_loop *loop, + struct msg_conn *(*create)(int fd, + union sockunion *su), + const char *idtag, struct debug *debug); +extern void msg_server_cleanup(struct msg_server *server); + +/* + * `notify_disconnect` is not called when the user `msg_conn_cleanup` is + * called for a client which is currently connected. The socket is closed + * but there is no notification. + */ +struct msg_conn * +msg_server_conn_create(struct event_loop *tm, int fd, + int (*notify_disconnect)(struct msg_conn *conn), + void (*handle_msg)(uint8_t version, uint8_t *data, + size_t len, struct msg_conn *conn), + size_t max_read, size_t max_write, size_t max_size, + void *user, const char *idtag); + +extern void msg_server_conn_delete(struct msg_conn *conn); + +extern void +msg_conn_accept_init(struct msg_conn *conn, struct event_loop *tm, int fd, + int (*notify_disconnect)(struct msg_conn *conn), + void (*handle_msg)(uint8_t version, uint8_t *data, + size_t len, struct msg_conn *conn), + size_t max_read, size_t max_write, size_t max_size, + const char *idtag); #endif /* _MGMT_MSG_H */ |