Page MenuHomeFreeBSD

D48550.diff
No OneTemporary

D48550.diff

diff --git a/include/rpc/svc.h b/include/rpc/svc.h
--- a/include/rpc/svc.h
+++ b/include/rpc/svc.h
@@ -455,6 +455,12 @@
*/
extern SVCXPRT *svcunixfd_create(int, u_int, u_int);
+/*
+ * netlink(4) server creation. To be used to service requests that
+ * originate from an in-kernel client.
+ */
+extern SVCXPRT *svc_nl_create(const char *);
+
/*
* Memory based rpc (for speed check and testing)
*/
diff --git a/lib/libc/rpc/Makefile.inc b/lib/libc/rpc/Makefile.inc
--- a/lib/libc/rpc/Makefile.inc
+++ b/lib/libc/rpc/Makefile.inc
@@ -8,7 +8,7 @@
rpc_callmsg.c rpc_generic.c rpc_soc.c rpcb_clnt.c rpcb_prot.c \
rpcb_st_xdr.c rpcsec_gss_stub.c svc.c svc_auth.c svc_dg.c \
svc_auth_unix.c svc_generic.c svc_raw.c svc_run.c svc_simple.c \
- svc_vc.c
+ svc_vc.c svc_nl.c
# Secure-RPC
SRCS+= auth_time.c auth_des.c authdes_prot.c des_crypt.c des_soft.c \
diff --git a/lib/libc/rpc/Symbol.map b/lib/libc/rpc/Symbol.map
--- a/lib/libc/rpc/Symbol.map
+++ b/lib/libc/rpc/Symbol.map
@@ -199,6 +199,10 @@
__rpc_get_local_uid;
};
+FBSD_1.8 {
+ svc_nl_create;
+};
+
FBSDprivate_1.0 {
__des_crypt_LOCAL;
__key_encryptsession_pk_LOCAL;
diff --git a/lib/libc/rpc/rpc_generic.c b/lib/libc/rpc/rpc_generic.c
--- a/lib/libc/rpc/rpc_generic.c
+++ b/lib/libc/rpc/rpc_generic.c
@@ -95,7 +95,8 @@
{ "udp6", AF_INET6, IPPROTO_UDP },
{ "tcp6", AF_INET6, IPPROTO_TCP },
#endif
- { "local", AF_LOCAL, 0 }
+ { "local", AF_LOCAL, 0 },
+ { "netlink", AF_NETLINK, 0 },
};
#if 0
diff --git a/lib/libc/rpc/svc_nl.c b/lib/libc/rpc/svc_nl.c
new file mode 100644
--- /dev/null
+++ b/lib/libc/rpc/svc_nl.c
@@ -0,0 +1,300 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Gleb Smirnoff <glebius@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <rpc/rpc.h>
+#include <rpc/clnt_nl.h>
+
+#include <netlink/netlink.h>
+#include <netlink/netlink_generic.h>
+#include <netlink/netlink_snl.h>
+#include <netlink/netlink_snl_generic.h>
+
+#include "rpc_com.h"
+
+/*
+ * RPC server to serve a kernel RPC client(s) over netlink(4). See clnt_nl.c
+ * in sys/rpc as the counterpart.
+ *
+ * Upon creation the client will seek for specified multicast group within the
+ * generic netlink family named "rpc". Then it would listen for incoming
+ * messages, process them and send replies over the same netlink socket.
+ * See clnt_nl.c for more transport protocol implementation details.
+ */
+
+static void svc_nl_destroy(SVCXPRT *);
+static bool_t svc_nl_recv(SVCXPRT *, struct rpc_msg *);
+static bool_t svc_nl_reply(SVCXPRT *, struct rpc_msg *);
+static enum xprt_stat svc_nl_stat(SVCXPRT *);
+static bool_t svc_nl_getargs(SVCXPRT *, xdrproc_t, void *);
+static bool_t svc_nl_freeargs(SVCXPRT *, xdrproc_t, void *);
+
+static struct xp_ops nl_ops = {
+ .xp_recv = svc_nl_recv,
+ .xp_reply = svc_nl_reply,
+ .xp_stat = svc_nl_stat,
+ .xp_getargs = svc_nl_getargs,
+ .xp_freeargs = svc_nl_freeargs,
+ .xp_destroy = svc_nl_destroy,
+};
+
+struct nl_softc {
+ struct snl_state snl;
+ XDR xdrs;
+ struct nlmsghdr *hdr;
+ size_t mlen;
+ enum xprt_stat stat;
+ uint32_t xid;
+ uint32_t group;
+ uint16_t family;
+ u_int errline;
+ int error;
+};
+
+SVCXPRT *
+svc_nl_create(const char *service)
+{
+ static struct sockaddr_nl snl_null = {
+ .nl_len = sizeof(struct sockaddr_nl),
+ .nl_family = PF_NETLINK,
+ };
+ struct nl_softc *sc;
+ SVCXPRT *xprt;
+ void *buf = NULL;
+ uint16_t family;
+ ssize_t len = 1024;
+
+ if ((sc = calloc(1, sizeof(struct nl_softc))) == NULL)
+ return (NULL);
+ if (!snl_init(&sc->snl, NETLINK_GENERIC) || (sc->group =
+ snl_get_genl_mcast_group(&sc->snl, "rpc", service, &family)) == 0)
+ goto fail;
+ if (setsockopt(sc->snl.fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
+ &sc->group, sizeof(sc->group)) == -1)
+ goto fail;
+ if ((buf = malloc(len)) == NULL)
+ goto fail;
+ if ((xprt = svc_xprt_alloc()) == NULL)
+ goto fail;
+
+ sc->hdr = buf,
+ sc->mlen = len,
+ sc->stat = XPRT_IDLE,
+ sc->family = family;
+
+ xprt->xp_fd = sc->snl.fd,
+ xprt->xp_p1 = sc,
+ xprt->xp_ops = &nl_ops,
+ xprt->xp_rtaddr = (struct netbuf){
+ .maxlen = sizeof(struct sockaddr_nl),
+ .len = sizeof(struct sockaddr_nl),
+ .buf = &snl_null,
+ };
+ xprt_register(xprt);
+
+ return (xprt);
+fail:
+ free(buf);
+ snl_free(&sc->snl);
+ free(sc);
+ return (NULL);
+}
+
+static void
+svc_nl_destroy(SVCXPRT *xprt)
+{
+ struct nl_softc *sc = xprt->xp_p1;
+
+ snl_free(&sc->snl);
+ free(sc->hdr);
+ free(xprt->xp_p1);
+ svc_xprt_free(xprt);
+}
+
+#define DIE(sc) do { \
+ (sc)->stat = XPRT_DIED; \
+ (sc)->errline = __LINE__; \
+ (sc)->error = errno; \
+ return (FALSE); \
+} while (0)
+
+struct nl_request_parsed {
+ uint32_t group;
+ struct nlattr *data;
+};
+static const struct snl_attr_parser rpcnl_attr_parser[] = {
+#define OUT(field) offsetof(struct nl_request_parsed, field)
+ { .type = RPCNL_REQUEST_GROUP, .off = OUT(group),
+ .cb = snl_attr_get_uint32 },
+ { .type = RPCNL_REQUEST_BODY, .off = OUT(data), .cb = snl_attr_get_nla },
+#undef OUT
+};
+SNL_DECLARE_GENL_PARSER(request_parser, rpcnl_attr_parser);
+
+static bool_t
+svc_nl_recv(SVCXPRT *xprt, struct rpc_msg *msg)
+{
+ struct nl_request_parsed req;
+ struct nl_softc *sc = xprt->xp_p1;
+ struct nlmsghdr *hdr = sc->hdr;
+
+ switch (sc->stat) {
+ case XPRT_IDLE:
+ if (recv(xprt->xp_fd, hdr, sizeof(struct nlmsghdr),
+ MSG_PEEK) != sizeof(struct nlmsghdr))
+ DIE(sc);
+ break;
+ case XPRT_MOREREQS:
+ sc->stat = XPRT_IDLE;
+ break;
+ case XPRT_DIED:
+ return (FALSE);
+ }
+
+ if (sc->mlen < hdr->nlmsg_len) {
+ if ((hdr = sc->hdr = realloc(hdr, hdr->nlmsg_len)) == NULL)
+ DIE(sc);
+ else
+ sc->mlen = hdr->nlmsg_len;
+ }
+ if (read(xprt->xp_fd, hdr, hdr->nlmsg_len) != hdr->nlmsg_len)
+ DIE(sc);
+
+ if (hdr->nlmsg_type != sc->family)
+ return (FALSE);
+
+ if (((struct genlmsghdr *)(hdr + 1))->cmd != RPCNL_REQUEST)
+ return (FALSE);
+
+ if (!snl_parse_nlmsg(NULL, hdr, &request_parser, &req))
+ return (FALSE);
+
+ if (req.group != sc->group)
+ return (FALSE);
+
+ xdrmem_create(&sc->xdrs, NLA_DATA(req.data), NLA_DATA_LEN(req.data),
+ XDR_DECODE);
+ if (xdr_callmsg(&sc->xdrs, msg)) {
+ /* XXX: assert that xid == nlmsg_seq? */
+ sc->xid = msg->rm_xid;
+ return (TRUE);
+ } else
+ return (FALSE);
+}
+
+static bool_t
+svc_nl_reply(SVCXPRT *xprt, struct rpc_msg *msg)
+{
+ struct nl_softc *sc = xprt->xp_p1;
+ struct snl_state snl;
+ struct snl_writer nw;
+ struct nlattr *body;
+ bool_t rv;
+
+ msg->rm_xid = sc->xid;
+
+ if (__predict_false(!snl_clone(&snl, &sc->snl)))
+ return (FALSE);
+ snl_init_writer(&sc->snl, &nw);
+ snl_create_genl_msg_request(&nw, sc->family, RPCNL_REPLY);
+ snl_add_msg_attr_u32(&nw, RPCNL_REPLY_GROUP, sc->group);
+ body = snl_reserve_msg_attr_raw(&nw, RPCNL_REPLY_BODY, RPC_MAXDATASIZE);
+
+ xdrmem_create(&sc->xdrs, (char *)(body + 1), RPC_MAXDATASIZE,
+ XDR_ENCODE);
+
+ if (msg->rm_reply.rp_stat == MSG_ACCEPTED &&
+ msg->rm_reply.rp_acpt.ar_stat == SUCCESS) {
+ xdrproc_t xdr_proc;
+ char *xdr_where;
+ u_int pos;
+
+ xdr_proc = msg->acpted_rply.ar_results.proc;
+ xdr_where = msg->acpted_rply.ar_results.where;
+ msg->acpted_rply.ar_results.proc = (xdrproc_t) xdr_void;
+ msg->acpted_rply.ar_results.where = NULL;
+
+ pos = xdr_getpos(&sc->xdrs);
+ if (!xdr_replymsg(&sc->xdrs, msg) ||
+ !SVCAUTH_WRAP(&SVC_AUTH(xprt), &sc->xdrs, xdr_proc,
+ xdr_where)) {
+ xdr_setpos(&sc->xdrs, pos);
+ rv = FALSE;
+ } else
+ rv = TRUE;
+ } else
+ rv = xdr_replymsg(&sc->xdrs, msg);
+
+ if (rv) {
+ /* snl_finalize_msg() really doesn't work for us */
+ body->nla_len = sizeof(struct nlattr) + xdr_getpos(&sc->xdrs);
+ nw.hdr->nlmsg_len = ((char *)body - (char *)nw.hdr) +
+ body->nla_len;
+ nw.hdr->nlmsg_type = sc->family;
+ nw.hdr->nlmsg_flags = NLM_F_REQUEST;
+ nw.hdr->nlmsg_seq = sc->xid;
+ if (write(xprt->xp_fd, nw.hdr, nw.hdr->nlmsg_len) !=
+ nw.hdr->nlmsg_len)
+ DIE(sc);
+ }
+
+ snl_free(&snl);
+
+ return (rv);
+}
+
+static enum xprt_stat
+svc_nl_stat(SVCXPRT *xprt)
+{
+ struct nl_softc *sc = xprt->xp_p1;
+
+ if (sc->stat == XPRT_IDLE &&
+ recv(xprt->xp_fd, sc->hdr, sizeof(struct nlmsghdr),
+ MSG_PEEK | MSG_DONTWAIT) == sizeof(struct nlmsghdr))
+ sc->stat = XPRT_MOREREQS;
+
+ return (sc->stat);
+}
+
+static bool_t
+svc_nl_getargs(SVCXPRT *xprt, xdrproc_t xdr_args, void *args_ptr)
+{
+ struct nl_softc *sc = xprt->xp_p1;
+
+ return (SVCAUTH_UNWRAP(&SVC_AUTH(xprt), &sc->xdrs, xdr_args, args_ptr));
+}
+
+static bool_t
+svc_nl_freeargs(SVCXPRT *xprt, xdrproc_t xdr_args, void *args_ptr)
+{
+ struct nl_softc *sc = xprt->xp_p1;
+
+ sc->xdrs.x_op = XDR_FREE;
+ return ((*xdr_args)(&sc->xdrs, args_ptr));
+}

File Metadata

Mime Type
text/plain
Expires
Mon, Feb 3, 7:58 PM (20 h, 50 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16441601
Default Alt Text
D48550.diff (9 KB)

Event Timeline