From nobody Sat Feb 01 09:02:05 2025 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4YlRbL0WSyz5mbHG; Sat, 01 Feb 2025 09:02:06 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R11" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4YlRbK4KXfz40h9; Sat, 01 Feb 2025 09:02:05 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1738400525; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=95mLk6Ila/QsvTTDntIco4irL1nNZ7P00XBHjL8fsFI=; b=ytb8hbt01NijQXkXlZHOGbHV6XlsnEByGLEhSXPT4wGhmBoSFNXQLSL6rX2vwrQJZFcK0B 3RZ3ZZ6ucfE9eGQNeNpG4NmsMfKeWUwwRQ8M/HaxVAdxTKILrnkSf198gzUq1RHhtswbWv mgs3wZzsCHJIslmA9zCAyeUPgRXeBFUCpDTB/TGB7zydOdlBq7yrUfpcur+vW7RgmOo9yk FhIXCgythK69cElOGMc815Ev5AyJ+/6eW8V3FGpN+CYV4MzdSQOOIdK9yL5Q0RPs3QYUEH V3HRPJYPFX97SR67NODuzCfVdoN8+wohVjaS5L6CHn//zQVyWFxELlBr0zaJaA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1738400525; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=95mLk6Ila/QsvTTDntIco4irL1nNZ7P00XBHjL8fsFI=; b=kV9f+rrz3ZAzVoysaAJ7Px/ErOz3gWAy7ttPEgUFTNtM5GgYHgKfiVOehc57UHNmhXVN3F c5MUp7cBk+srYOieOFtLn9h43fxTyYNELKt2oOhwJ1ghfZs1ym6cnNlyN4o/vOdYa4+ToS gfL/GAzXPPxFl48nAp6NLH6aM0cFecXidBBBZRB4/JzOpUmLP+lFH2tC/gtAIdnYP6AQjl GXvrc80Dfcuy1yj0YK43W/ls7SGwmHCXoYYjyd6WXcboOuRLWZ0PlybNFe6EhfTH5y8DIL oMn8b5tD5GGOLBfqgcTfjuaB6dGEXOY0hOgC6J/V7MfkpMqiZ8OpfNa6Gi9i3w== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1738400525; a=rsa-sha256; cv=none; b=g/1LdjRAvNazPV+AFp/63XEeYdt2mH8NU9m+I73gB1sbbdQbF4p8U4RVCVMMt1h2seTWVh QVApGzT1MxudbLoejegkSX+NvH4o/aJiE53An1cgVM6T35wPkx5T5z90suCHHdQwkYIG+A Ib5CCogzJi53MJ1+5+pimJrERY0FKAiFn0+QgAQDo3E8xOTUd4l1a+i8SL/ep23HMuq9Sy HeMwymEkGJHIiIEj+uZQ7RlccLu9e5Um1uORW4YDgQMVC4UkuPP9LUgG5WA7DyGcf2XOY2 YJjsO2ZWBRIoHyE91Yzj30yG6WGyxsHJPvwkwRTTRrkxUOV7mLy8R29YIrEaCQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4YlRbK3tSczwtX; Sat, 01 Feb 2025 09:02:05 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 511925bH047829; Sat, 1 Feb 2025 09:02:05 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 51192534047826; Sat, 1 Feb 2025 09:02:05 GMT (envelope-from git) Date: Sat, 1 Feb 2025 09:02:05 GMT Message-Id: <202502010902.51192534047826@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Gleb Smirnoff Subject: git: c5d671b711c3 - main - libc/rpc: add userland side RPC server over netlink(4) List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: glebius X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: c5d671b711c34fb9eec55531663182feedd93f87 Auto-Submitted: auto-generated The branch main has been updated by glebius: URL: https://cgit.FreeBSD.org/src/commit/?id=c5d671b711c34fb9eec55531663182feedd93f87 commit c5d671b711c34fb9eec55531663182feedd93f87 Author: Gleb Smirnoff AuthorDate: 2025-02-01 01:02:00 +0000 Commit: Gleb Smirnoff 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 + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#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)); +}