The branch main has been updated by glebius:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=c5d671b711c34fb9eec55531663182feedd93f87

commit c5d671b711c34fb9eec55531663182feedd93f87
Author:     Gleb Smirnoff <gleb...@freebsd.org>
AuthorDate: 2025-02-01 01:02:00 +0000
Commit:     Gleb Smirnoff <gleb...@freebsd.org>
CommitDate: 2025-02-01 09:00:25 +0000

    libc/rpc: add userland side RPC server over netlink(4)
    
    To be used by NFS related daemons that provide RPC services to the kernel.
    Some implementation details inside the new svc_nl.c.
    
    Reviewed by:            rmacklem
    Differential Revision:  https://reviews.freebsd.org/D48550
---
 include/rpc/svc.h          |   6 +
 lib/libc/rpc/Makefile.inc  |   2 +-
 lib/libc/rpc/Symbol.map    |   4 +
 lib/libc/rpc/rpc_generic.c |   3 +-
 lib/libc/rpc/svc_nl.c      | 300 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 313 insertions(+), 2 deletions(-)

diff --git a/include/rpc/svc.h b/include/rpc/svc.h
index d4a84384bb92..deb626ad7c1d 100644
--- a/include/rpc/svc.h
+++ b/include/rpc/svc.h
@@ -455,6 +455,12 @@ extern SVCXPRT *svc_fd_create(const int, const u_int, 
const u_int);
  */
 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
index 5ee4ffc256a2..87963d10eec1 100644
--- a/lib/libc/rpc/Makefile.inc
+++ b/lib/libc/rpc/Makefile.inc
@@ -8,7 +8,7 @@ SRCS+=  auth_none.c auth_unix.c authunix_prot.c bindresvport.c 
clnt_bcast.c \
        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
index e4fecb83ec66..105d6fb6b54e 100644
--- a/lib/libc/rpc/Symbol.map
+++ b/lib/libc/rpc/Symbol.map
@@ -199,6 +199,10 @@ FBSD_1.0 {
        __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
index d8b8e2cdf333..0e563f6a5996 100644
--- a/lib/libc/rpc/rpc_generic.c
+++ b/lib/libc/rpc/rpc_generic.c
@@ -95,7 +95,8 @@ static const struct netid_af na_cvt[] = {
        { "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
index 000000000000..f866acaf3015
--- /dev/null
+++ b/lib/libc/rpc/svc_nl.c
@@ -0,0 +1,300 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Gleb Smirnoff <gleb...@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));
+}

Reply via email to