summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorChristian Hopps <chopps@labn.net>2023-04-29 13:20:18 +0200
committerChristian Hopps <chopps@labn.net>2023-05-28 11:13:22 +0200
commit99564edc43ecaf099ede84f7fa84b104f2b950b1 (patch)
treed1e1aa057dafefc5153aeb73b8027131da9bacc3 /lib
parentlib: msg: refactor common connection code from mgmtd (diff)
downloadfrr-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.c150
-rw-r--r--lib/mgmt_msg.h50
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 */