1c5d671b7SGleb Smirnoff /*-
2c5d671b7SGleb Smirnoff * SPDX-License-Identifier: BSD-2-Clause
3c5d671b7SGleb Smirnoff *
4c5d671b7SGleb Smirnoff * Copyright (c) 2025 Gleb Smirnoff <glebius@FreeBSD.org>
5c5d671b7SGleb Smirnoff *
6c5d671b7SGleb Smirnoff * Redistribution and use in source and binary forms, with or without
7c5d671b7SGleb Smirnoff * modification, are permitted provided that the following conditions
8c5d671b7SGleb Smirnoff * are met:
9c5d671b7SGleb Smirnoff * 1. Redistributions of source code must retain the above copyright
10c5d671b7SGleb Smirnoff * notice, this list of conditions and the following disclaimer.
11c5d671b7SGleb Smirnoff * 2. Redistributions in binary form must reproduce the above copyright
12c5d671b7SGleb Smirnoff * notice, this list of conditions and the following disclaimer in the
13c5d671b7SGleb Smirnoff * documentation and/or other materials provided with the distribution.
14c5d671b7SGleb Smirnoff *
15c5d671b7SGleb Smirnoff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16c5d671b7SGleb Smirnoff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17c5d671b7SGleb Smirnoff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18c5d671b7SGleb Smirnoff * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19c5d671b7SGleb Smirnoff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20c5d671b7SGleb Smirnoff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21c5d671b7SGleb Smirnoff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22c5d671b7SGleb Smirnoff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23c5d671b7SGleb Smirnoff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24c5d671b7SGleb Smirnoff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25c5d671b7SGleb Smirnoff * SUCH DAMAGE.
26c5d671b7SGleb Smirnoff */
27c5d671b7SGleb Smirnoff
28c5d671b7SGleb Smirnoff #include <stdlib.h>
29c5d671b7SGleb Smirnoff #include <unistd.h>
30c5d671b7SGleb Smirnoff #include <errno.h>
31*c62ae124SGleb Smirnoff #include <pthread.h>
32c5d671b7SGleb Smirnoff #include <rpc/rpc.h>
33c5d671b7SGleb Smirnoff #include <rpc/clnt_nl.h>
34c5d671b7SGleb Smirnoff
35c5d671b7SGleb Smirnoff #include <netlink/netlink.h>
36c5d671b7SGleb Smirnoff #include <netlink/netlink_generic.h>
37c5d671b7SGleb Smirnoff #include <netlink/netlink_snl.h>
38c5d671b7SGleb Smirnoff #include <netlink/netlink_snl_generic.h>
39c5d671b7SGleb Smirnoff
40c5d671b7SGleb Smirnoff #include "rpc_com.h"
41*c62ae124SGleb Smirnoff #include "libc_private.h"
42c5d671b7SGleb Smirnoff
43c5d671b7SGleb Smirnoff /*
44c5d671b7SGleb Smirnoff * RPC server to serve a kernel RPC client(s) over netlink(4). See clnt_nl.c
45c5d671b7SGleb Smirnoff * in sys/rpc as the counterpart.
46c5d671b7SGleb Smirnoff *
47c5d671b7SGleb Smirnoff * Upon creation the client will seek for specified multicast group within the
48c5d671b7SGleb Smirnoff * generic netlink family named "rpc". Then it would listen for incoming
49c5d671b7SGleb Smirnoff * messages, process them and send replies over the same netlink socket.
50c5d671b7SGleb Smirnoff * See clnt_nl.c for more transport protocol implementation details.
51c5d671b7SGleb Smirnoff */
52c5d671b7SGleb Smirnoff
53c5d671b7SGleb Smirnoff static void svc_nl_destroy(SVCXPRT *);
54c5d671b7SGleb Smirnoff static bool_t svc_nl_recv(SVCXPRT *, struct rpc_msg *);
55c5d671b7SGleb Smirnoff static bool_t svc_nl_reply(SVCXPRT *, struct rpc_msg *);
56c5d671b7SGleb Smirnoff static enum xprt_stat svc_nl_stat(SVCXPRT *);
57c5d671b7SGleb Smirnoff static bool_t svc_nl_getargs(SVCXPRT *, xdrproc_t, void *);
58c5d671b7SGleb Smirnoff static bool_t svc_nl_freeargs(SVCXPRT *, xdrproc_t, void *);
59*c62ae124SGleb Smirnoff static bool_t svc_nl_control(SVCXPRT *, const u_int, void *);
60c5d671b7SGleb Smirnoff
61c5d671b7SGleb Smirnoff static struct xp_ops nl_ops = {
62c5d671b7SGleb Smirnoff .xp_recv = svc_nl_recv,
63c5d671b7SGleb Smirnoff .xp_reply = svc_nl_reply,
64c5d671b7SGleb Smirnoff .xp_stat = svc_nl_stat,
65c5d671b7SGleb Smirnoff .xp_getargs = svc_nl_getargs,
66c5d671b7SGleb Smirnoff .xp_freeargs = svc_nl_freeargs,
67c5d671b7SGleb Smirnoff .xp_destroy = svc_nl_destroy,
68c5d671b7SGleb Smirnoff };
69*c62ae124SGleb Smirnoff static struct xp_ops2 nl_ops2 = {
70*c62ae124SGleb Smirnoff .xp_control = svc_nl_control,
71*c62ae124SGleb Smirnoff };
72c5d671b7SGleb Smirnoff
73c5d671b7SGleb Smirnoff struct nl_softc {
74c5d671b7SGleb Smirnoff struct snl_state snl;
75c5d671b7SGleb Smirnoff XDR xdrs;
76c5d671b7SGleb Smirnoff struct nlmsghdr *hdr;
77*c62ae124SGleb Smirnoff pthread_key_t xidkey;
78c5d671b7SGleb Smirnoff size_t mlen;
79c5d671b7SGleb Smirnoff enum xprt_stat stat;
80c5d671b7SGleb Smirnoff uint32_t xid;
81c5d671b7SGleb Smirnoff uint32_t group;
82c5d671b7SGleb Smirnoff uint16_t family;
83c5d671b7SGleb Smirnoff u_int errline;
84c5d671b7SGleb Smirnoff int error;
85c5d671b7SGleb Smirnoff };
86c5d671b7SGleb Smirnoff
87c5d671b7SGleb Smirnoff SVCXPRT *
svc_nl_create(const char * service)88c5d671b7SGleb Smirnoff svc_nl_create(const char *service)
89c5d671b7SGleb Smirnoff {
90c5d671b7SGleb Smirnoff static struct sockaddr_nl snl_null = {
91c5d671b7SGleb Smirnoff .nl_len = sizeof(struct sockaddr_nl),
92c5d671b7SGleb Smirnoff .nl_family = PF_NETLINK,
93c5d671b7SGleb Smirnoff };
94c5d671b7SGleb Smirnoff struct nl_softc *sc;
95c5d671b7SGleb Smirnoff SVCXPRT *xprt;
96c5d671b7SGleb Smirnoff void *buf = NULL;
97c5d671b7SGleb Smirnoff uint16_t family;
98c5d671b7SGleb Smirnoff ssize_t len = 1024;
99c5d671b7SGleb Smirnoff
100c5d671b7SGleb Smirnoff if ((sc = calloc(1, sizeof(struct nl_softc))) == NULL)
101c5d671b7SGleb Smirnoff return (NULL);
102c5d671b7SGleb Smirnoff if (!snl_init(&sc->snl, NETLINK_GENERIC) || (sc->group =
103c5d671b7SGleb Smirnoff snl_get_genl_mcast_group(&sc->snl, "rpc", service, &family)) == 0)
104c5d671b7SGleb Smirnoff goto fail;
105c5d671b7SGleb Smirnoff if (setsockopt(sc->snl.fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
106c5d671b7SGleb Smirnoff &sc->group, sizeof(sc->group)) == -1)
107c5d671b7SGleb Smirnoff goto fail;
108c5d671b7SGleb Smirnoff if ((buf = malloc(len)) == NULL)
109c5d671b7SGleb Smirnoff goto fail;
110c5d671b7SGleb Smirnoff if ((xprt = svc_xprt_alloc()) == NULL)
111c5d671b7SGleb Smirnoff goto fail;
112c5d671b7SGleb Smirnoff
113c5d671b7SGleb Smirnoff sc->hdr = buf,
114c5d671b7SGleb Smirnoff sc->mlen = len,
115c5d671b7SGleb Smirnoff sc->stat = XPRT_IDLE,
116c5d671b7SGleb Smirnoff sc->family = family;
117c5d671b7SGleb Smirnoff
118*c62ae124SGleb Smirnoff if (__isthreaded &&
119*c62ae124SGleb Smirnoff (pthread_key_create(&sc->xidkey, NULL) != 0 ||
120*c62ae124SGleb Smirnoff pthread_setspecific(sc->xidkey, &sc->xid) != 0))
121*c62ae124SGleb Smirnoff goto fail;
122*c62ae124SGleb Smirnoff
123c5d671b7SGleb Smirnoff xprt->xp_fd = sc->snl.fd,
124c5d671b7SGleb Smirnoff xprt->xp_p1 = sc,
125c5d671b7SGleb Smirnoff xprt->xp_ops = &nl_ops,
126*c62ae124SGleb Smirnoff xprt->xp_ops2 = &nl_ops2,
127c5d671b7SGleb Smirnoff xprt->xp_rtaddr = (struct netbuf){
128c5d671b7SGleb Smirnoff .maxlen = sizeof(struct sockaddr_nl),
129c5d671b7SGleb Smirnoff .len = sizeof(struct sockaddr_nl),
130c5d671b7SGleb Smirnoff .buf = &snl_null,
131c5d671b7SGleb Smirnoff };
132c5d671b7SGleb Smirnoff xprt_register(xprt);
133c5d671b7SGleb Smirnoff
134c5d671b7SGleb Smirnoff return (xprt);
135c5d671b7SGleb Smirnoff fail:
136c5d671b7SGleb Smirnoff free(buf);
137c5d671b7SGleb Smirnoff snl_free(&sc->snl);
138c5d671b7SGleb Smirnoff free(sc);
139c5d671b7SGleb Smirnoff return (NULL);
140c5d671b7SGleb Smirnoff }
141c5d671b7SGleb Smirnoff
142c5d671b7SGleb Smirnoff static void
svc_nl_destroy(SVCXPRT * xprt)143c5d671b7SGleb Smirnoff svc_nl_destroy(SVCXPRT *xprt)
144c5d671b7SGleb Smirnoff {
145c5d671b7SGleb Smirnoff struct nl_softc *sc = xprt->xp_p1;
146c5d671b7SGleb Smirnoff
147c5d671b7SGleb Smirnoff snl_free(&sc->snl);
148c5d671b7SGleb Smirnoff free(sc->hdr);
149c5d671b7SGleb Smirnoff free(xprt->xp_p1);
150c5d671b7SGleb Smirnoff svc_xprt_free(xprt);
151c5d671b7SGleb Smirnoff }
152c5d671b7SGleb Smirnoff
153c5d671b7SGleb Smirnoff #define DIE(sc) do { \
154c5d671b7SGleb Smirnoff (sc)->stat = XPRT_DIED; \
155c5d671b7SGleb Smirnoff (sc)->errline = __LINE__; \
156c5d671b7SGleb Smirnoff (sc)->error = errno; \
157c5d671b7SGleb Smirnoff return (FALSE); \
158c5d671b7SGleb Smirnoff } while (0)
159c5d671b7SGleb Smirnoff
160c5d671b7SGleb Smirnoff struct nl_request_parsed {
161c5d671b7SGleb Smirnoff uint32_t group;
162c5d671b7SGleb Smirnoff struct nlattr *data;
163c5d671b7SGleb Smirnoff };
164c5d671b7SGleb Smirnoff static const struct snl_attr_parser rpcnl_attr_parser[] = {
165c5d671b7SGleb Smirnoff #define OUT(field) offsetof(struct nl_request_parsed, field)
166c5d671b7SGleb Smirnoff { .type = RPCNL_REQUEST_GROUP, .off = OUT(group),
167c5d671b7SGleb Smirnoff .cb = snl_attr_get_uint32 },
168c5d671b7SGleb Smirnoff { .type = RPCNL_REQUEST_BODY, .off = OUT(data), .cb = snl_attr_get_nla },
169c5d671b7SGleb Smirnoff #undef OUT
170c5d671b7SGleb Smirnoff };
171c5d671b7SGleb Smirnoff SNL_DECLARE_GENL_PARSER(request_parser, rpcnl_attr_parser);
172c5d671b7SGleb Smirnoff
173c5d671b7SGleb Smirnoff static bool_t
svc_nl_recv(SVCXPRT * xprt,struct rpc_msg * msg)174c5d671b7SGleb Smirnoff svc_nl_recv(SVCXPRT *xprt, struct rpc_msg *msg)
175c5d671b7SGleb Smirnoff {
176c5d671b7SGleb Smirnoff struct nl_request_parsed req;
177c5d671b7SGleb Smirnoff struct nl_softc *sc = xprt->xp_p1;
178c5d671b7SGleb Smirnoff struct nlmsghdr *hdr = sc->hdr;
179c5d671b7SGleb Smirnoff
180c5d671b7SGleb Smirnoff switch (sc->stat) {
181c5d671b7SGleb Smirnoff case XPRT_IDLE:
182c5d671b7SGleb Smirnoff if (recv(xprt->xp_fd, hdr, sizeof(struct nlmsghdr),
183c5d671b7SGleb Smirnoff MSG_PEEK) != sizeof(struct nlmsghdr))
184c5d671b7SGleb Smirnoff DIE(sc);
185c5d671b7SGleb Smirnoff break;
186c5d671b7SGleb Smirnoff case XPRT_MOREREQS:
187c5d671b7SGleb Smirnoff sc->stat = XPRT_IDLE;
188c5d671b7SGleb Smirnoff break;
189c5d671b7SGleb Smirnoff case XPRT_DIED:
190c5d671b7SGleb Smirnoff return (FALSE);
191c5d671b7SGleb Smirnoff }
192c5d671b7SGleb Smirnoff
193c5d671b7SGleb Smirnoff if (sc->mlen < hdr->nlmsg_len) {
194c5d671b7SGleb Smirnoff if ((hdr = sc->hdr = realloc(hdr, hdr->nlmsg_len)) == NULL)
195c5d671b7SGleb Smirnoff DIE(sc);
196c5d671b7SGleb Smirnoff else
197c5d671b7SGleb Smirnoff sc->mlen = hdr->nlmsg_len;
198c5d671b7SGleb Smirnoff }
199c5d671b7SGleb Smirnoff if (read(xprt->xp_fd, hdr, hdr->nlmsg_len) != hdr->nlmsg_len)
200c5d671b7SGleb Smirnoff DIE(sc);
201c5d671b7SGleb Smirnoff
202c5d671b7SGleb Smirnoff if (hdr->nlmsg_type != sc->family)
203c5d671b7SGleb Smirnoff return (FALSE);
204c5d671b7SGleb Smirnoff
205c5d671b7SGleb Smirnoff if (((struct genlmsghdr *)(hdr + 1))->cmd != RPCNL_REQUEST)
206c5d671b7SGleb Smirnoff return (FALSE);
207c5d671b7SGleb Smirnoff
208c5d671b7SGleb Smirnoff if (!snl_parse_nlmsg(NULL, hdr, &request_parser, &req))
209c5d671b7SGleb Smirnoff return (FALSE);
210c5d671b7SGleb Smirnoff
211c5d671b7SGleb Smirnoff if (req.group != sc->group)
212c5d671b7SGleb Smirnoff return (FALSE);
213c5d671b7SGleb Smirnoff
214c5d671b7SGleb Smirnoff xdrmem_create(&sc->xdrs, NLA_DATA(req.data), NLA_DATA_LEN(req.data),
215c5d671b7SGleb Smirnoff XDR_DECODE);
216c5d671b7SGleb Smirnoff if (xdr_callmsg(&sc->xdrs, msg)) {
217c5d671b7SGleb Smirnoff /* XXX: assert that xid == nlmsg_seq? */
218c5d671b7SGleb Smirnoff sc->xid = msg->rm_xid;
219c5d671b7SGleb Smirnoff return (TRUE);
220c5d671b7SGleb Smirnoff } else
221c5d671b7SGleb Smirnoff return (FALSE);
222c5d671b7SGleb Smirnoff }
223c5d671b7SGleb Smirnoff
224*c62ae124SGleb Smirnoff /*
225*c62ae124SGleb Smirnoff * Reenterable reply method. Note that both the softc and xprt are declared
226*c62ae124SGleb Smirnoff * const. The qualifier for xprt is commented out to match the library
227*c62ae124SGleb Smirnoff * prototype. If doing any substantial changes to the function please
228*c62ae124SGleb Smirnoff * temporarily uncomment the const for xprt and check your changes.
229*c62ae124SGleb Smirnoff *
230*c62ae124SGleb Smirnoff * Applications that want to use svc_nl_reply in a spawned thread context
231*c62ae124SGleb Smirnoff * should do the following hacks in self:
232*c62ae124SGleb Smirnoff * 1) - Create xprt with svc_nl_create() with libc in threaded mode, e.g.
233*c62ae124SGleb Smirnoff * at least one pthread_create() shall happen before svc_nl_create().
234*c62ae124SGleb Smirnoff * - After xprt creation query it for the pthread_key_t with the
235*c62ae124SGleb Smirnoff * SVCNL_GET_XIDKEY control and save this key.
236*c62ae124SGleb Smirnoff * 2) In the RPC function body that wants to become multithreaded:
237*c62ae124SGleb Smirnoff * - Make a copy of the arguments and of the xid with help of
238*c62ae124SGleb Smirnoff * pthread_getspecific() using the key.
239*c62ae124SGleb Smirnoff * - pthread_create() the worker function, pointing it at the copy of
240*c62ae124SGleb Smirnoff * arguments and xid.
241*c62ae124SGleb Smirnoff * - return FALSE, so that RPC generated code doesn't do anything.
242*c62ae124SGleb Smirnoff * 3) In the spawned thread:
243*c62ae124SGleb Smirnoff * - Use arguments provided in the copy by the parent.
244*c62ae124SGleb Smirnoff * - Allocate appropriately typed result on stack.
245*c62ae124SGleb Smirnoff * - *** do the actual work ***
246*c62ae124SGleb Smirnoff * - Populate the on-stack result same way as pointed result is populated
247*c62ae124SGleb Smirnoff * in a regular RPC function.
248*c62ae124SGleb Smirnoff * - Point the thread specific storage to the copy of xid provided by the
249*c62ae124SGleb Smirnoff * parent with help of pthread_setspecific().
250*c62ae124SGleb Smirnoff * - Call svc_sendreply() just like the rpcgen(1) generated code does.
251*c62ae124SGleb Smirnoff *
252*c62ae124SGleb Smirnoff * If all done correctly svc_nl_reply() will use thread specific xid for
253*c62ae124SGleb Smirnoff * a call that was processed asynchronously and most recent xid when entered
254*c62ae124SGleb Smirnoff * synchronously. So you can make only some methods of your application
255*c62ae124SGleb Smirnoff * reentrable, keeping others as is.
256*c62ae124SGleb Smirnoff */
257*c62ae124SGleb Smirnoff
258c5d671b7SGleb Smirnoff static bool_t
svc_nl_reply(SVCXPRT * xprt,struct rpc_msg * msg)259*c62ae124SGleb Smirnoff svc_nl_reply(/* const */ SVCXPRT *xprt, struct rpc_msg *msg)
260c5d671b7SGleb Smirnoff {
261*c62ae124SGleb Smirnoff const struct nl_softc *sc = xprt->xp_p1;
262c5d671b7SGleb Smirnoff struct snl_state snl;
263c5d671b7SGleb Smirnoff struct snl_writer nw;
264*c62ae124SGleb Smirnoff XDR xdrs;
265c5d671b7SGleb Smirnoff struct nlattr *body;
266c5d671b7SGleb Smirnoff bool_t rv;
267c5d671b7SGleb Smirnoff
268*c62ae124SGleb Smirnoff msg->rm_xid = __isthreaded ?
269*c62ae124SGleb Smirnoff *(uint32_t *)pthread_getspecific(sc->xidkey) :
270*c62ae124SGleb Smirnoff sc->xid;
271c5d671b7SGleb Smirnoff
272c5d671b7SGleb Smirnoff if (__predict_false(!snl_clone(&snl, &sc->snl)))
273c5d671b7SGleb Smirnoff return (FALSE);
274*c62ae124SGleb Smirnoff snl_init_writer(&snl, &nw);
275c5d671b7SGleb Smirnoff snl_create_genl_msg_request(&nw, sc->family, RPCNL_REPLY);
276c5d671b7SGleb Smirnoff snl_add_msg_attr_u32(&nw, RPCNL_REPLY_GROUP, sc->group);
277c5d671b7SGleb Smirnoff body = snl_reserve_msg_attr_raw(&nw, RPCNL_REPLY_BODY, RPC_MAXDATASIZE);
278c5d671b7SGleb Smirnoff
279*c62ae124SGleb Smirnoff xdrmem_create(&xdrs, (char *)(body + 1), RPC_MAXDATASIZE, XDR_ENCODE);
280c5d671b7SGleb Smirnoff
281c5d671b7SGleb Smirnoff if (msg->rm_reply.rp_stat == MSG_ACCEPTED &&
282c5d671b7SGleb Smirnoff msg->rm_reply.rp_acpt.ar_stat == SUCCESS) {
283c5d671b7SGleb Smirnoff xdrproc_t xdr_proc;
284c5d671b7SGleb Smirnoff char *xdr_where;
285c5d671b7SGleb Smirnoff u_int pos;
286c5d671b7SGleb Smirnoff
287c5d671b7SGleb Smirnoff xdr_proc = msg->acpted_rply.ar_results.proc;
288c5d671b7SGleb Smirnoff xdr_where = msg->acpted_rply.ar_results.where;
289c5d671b7SGleb Smirnoff msg->acpted_rply.ar_results.proc = (xdrproc_t) xdr_void;
290c5d671b7SGleb Smirnoff msg->acpted_rply.ar_results.where = NULL;
291c5d671b7SGleb Smirnoff
292*c62ae124SGleb Smirnoff pos = xdr_getpos(&xdrs);
293*c62ae124SGleb Smirnoff if (!xdr_replymsg(&xdrs, msg) ||
294*c62ae124SGleb Smirnoff !SVCAUTH_WRAP(&SVC_AUTH(xprt), &xdrs, xdr_proc,
295c5d671b7SGleb Smirnoff xdr_where)) {
296*c62ae124SGleb Smirnoff xdr_setpos(&xdrs, pos);
297c5d671b7SGleb Smirnoff rv = FALSE;
298c5d671b7SGleb Smirnoff } else
299c5d671b7SGleb Smirnoff rv = TRUE;
300c5d671b7SGleb Smirnoff } else
301*c62ae124SGleb Smirnoff rv = xdr_replymsg(&xdrs, msg);
302c5d671b7SGleb Smirnoff
303c5d671b7SGleb Smirnoff if (rv) {
304c5d671b7SGleb Smirnoff /* snl_finalize_msg() really doesn't work for us */
305*c62ae124SGleb Smirnoff body->nla_len = sizeof(struct nlattr) + xdr_getpos(&xdrs);
306c5d671b7SGleb Smirnoff nw.hdr->nlmsg_len = ((char *)body - (char *)nw.hdr) +
307c5d671b7SGleb Smirnoff body->nla_len;
308c5d671b7SGleb Smirnoff nw.hdr->nlmsg_type = sc->family;
309c5d671b7SGleb Smirnoff nw.hdr->nlmsg_flags = NLM_F_REQUEST;
310*c62ae124SGleb Smirnoff nw.hdr->nlmsg_seq = msg->rm_xid;
311c5d671b7SGleb Smirnoff if (write(xprt->xp_fd, nw.hdr, nw.hdr->nlmsg_len) !=
312c5d671b7SGleb Smirnoff nw.hdr->nlmsg_len)
313*c62ae124SGleb Smirnoff DIE(__DECONST(struct nl_softc *, sc));
314c5d671b7SGleb Smirnoff }
315c5d671b7SGleb Smirnoff
316c5d671b7SGleb Smirnoff snl_free(&snl);
317c5d671b7SGleb Smirnoff
318c5d671b7SGleb Smirnoff return (rv);
319c5d671b7SGleb Smirnoff }
320c5d671b7SGleb Smirnoff
321*c62ae124SGleb Smirnoff static bool_t
svc_nl_control(SVCXPRT * xprt,const u_int req,void * v)322*c62ae124SGleb Smirnoff svc_nl_control(SVCXPRT *xprt, const u_int req, void *v)
323*c62ae124SGleb Smirnoff {
324*c62ae124SGleb Smirnoff struct nl_softc *sc = xprt->xp_p1;
325*c62ae124SGleb Smirnoff
326*c62ae124SGleb Smirnoff switch (req) {
327*c62ae124SGleb Smirnoff case SVCNL_GET_XIDKEY:
328*c62ae124SGleb Smirnoff if (!__isthreaded) {
329*c62ae124SGleb Smirnoff /*
330*c62ae124SGleb Smirnoff * Report to application that it had created xprt not
331*c62ae124SGleb Smirnoff * in threaded mode, but definitly plans to use it with
332*c62ae124SGleb Smirnoff * threads. If it tries so, it would very likely crash.
333*c62ae124SGleb Smirnoff */
334*c62ae124SGleb Smirnoff errno = EDOOFUS;
335*c62ae124SGleb Smirnoff DIE(sc);
336*c62ae124SGleb Smirnoff };
337*c62ae124SGleb Smirnoff *(pthread_key_t *)v = sc->xidkey;
338*c62ae124SGleb Smirnoff return (TRUE);
339*c62ae124SGleb Smirnoff default:
340*c62ae124SGleb Smirnoff return (FALSE);
341*c62ae124SGleb Smirnoff }
342*c62ae124SGleb Smirnoff }
343*c62ae124SGleb Smirnoff
344c5d671b7SGleb Smirnoff static enum xprt_stat
svc_nl_stat(SVCXPRT * xprt)345c5d671b7SGleb Smirnoff svc_nl_stat(SVCXPRT *xprt)
346c5d671b7SGleb Smirnoff {
347c5d671b7SGleb Smirnoff struct nl_softc *sc = xprt->xp_p1;
348c5d671b7SGleb Smirnoff
349c5d671b7SGleb Smirnoff if (sc->stat == XPRT_IDLE &&
350c5d671b7SGleb Smirnoff recv(xprt->xp_fd, sc->hdr, sizeof(struct nlmsghdr),
351c5d671b7SGleb Smirnoff MSG_PEEK | MSG_DONTWAIT) == sizeof(struct nlmsghdr))
352c5d671b7SGleb Smirnoff sc->stat = XPRT_MOREREQS;
353c5d671b7SGleb Smirnoff
354c5d671b7SGleb Smirnoff return (sc->stat);
355c5d671b7SGleb Smirnoff }
356c5d671b7SGleb Smirnoff
357c5d671b7SGleb Smirnoff static bool_t
svc_nl_getargs(SVCXPRT * xprt,xdrproc_t xdr_args,void * args_ptr)358c5d671b7SGleb Smirnoff svc_nl_getargs(SVCXPRT *xprt, xdrproc_t xdr_args, void *args_ptr)
359c5d671b7SGleb Smirnoff {
360c5d671b7SGleb Smirnoff struct nl_softc *sc = xprt->xp_p1;
361c5d671b7SGleb Smirnoff
362c5d671b7SGleb Smirnoff return (SVCAUTH_UNWRAP(&SVC_AUTH(xprt), &sc->xdrs, xdr_args, args_ptr));
363c5d671b7SGleb Smirnoff }
364c5d671b7SGleb Smirnoff
365c5d671b7SGleb Smirnoff static bool_t
svc_nl_freeargs(SVCXPRT * xprt,xdrproc_t xdr_args,void * args_ptr)366c5d671b7SGleb Smirnoff svc_nl_freeargs(SVCXPRT *xprt, xdrproc_t xdr_args, void *args_ptr)
367c5d671b7SGleb Smirnoff {
368c5d671b7SGleb Smirnoff struct nl_softc *sc = xprt->xp_p1;
369c5d671b7SGleb Smirnoff
370c5d671b7SGleb Smirnoff sc->xdrs.x_op = XDR_FREE;
371c5d671b7SGleb Smirnoff return ((*xdr_args)(&sc->xdrs, args_ptr));
372c5d671b7SGleb Smirnoff }
373