1    	
2    	/*
3    	 * Copyright (c) 2009, Sun Microsystems, Inc.
4    	 * Copyright (c) 2012-2018 Red Hat, Inc. and/or its affiliates.
5    	 * All rights reserved.
6    	 *
7    	 * Redistribution and use in source and binary forms, with or without
8    	 * modification, are permitted provided that the following conditions are met:
9    	 * - Redistributions of source code must retain the above copyright notice,
10   	 *   this list of conditions and the following disclaimer.
11   	 * - Redistributions in binary form must reproduce the above copyright notice,
12   	 *   this list of conditions and the following disclaimer in the documentation
13   	 *   and/or other materials provided with the distribution.
14   	 * - Neither the name of Sun Microsystems, Inc. nor the names of its
15   	 *   contributors may be used to endorse or promote products derived
16   	 *   from this software without specific prior written permission.
17   	 *
18   	 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19   	 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20   	 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21   	 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22   	 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23   	 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24   	 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25   	 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26   	 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27   	 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28   	 * POSSIBILITY OF SUCH DAMAGE.
29   	 */
30   	
31   	/*
32   	 * Copyright (c) 1986-1991 by Sun Microsystems Inc.
33   	 */
34   	
35   	#include "config.h"
36   	
37   	/*
38   	 * svc_dg.c, Server side for connectionless RPC.
39   	 */
40   	#include <sys/cdefs.h>
41   	#include <sys/types.h>
42   	#include <sys/socket.h>
43   	#include <sys/param.h>
44   	#include <sys/poll.h>
45   	#include <rpc/types.h>
46   	#include <misc/portable.h>
47   	#include <rpc/rpc.h>
48   	#include <rpc/svc_auth.h>
49   	#include <errno.h>
50   	#include <unistd.h>
51   	#include <stdio.h>
52   	#include <stdlib.h>
53   	#include <string.h>
54   	#include <netconfig.h>
55   	#include <err.h>
56   	
57   	#include "rpc_com.h"
58   	#include "svc_internal.h"
59   	#include "svc_xprt.h"
60   	#include <rpc/svc_rqst.h>
61   	#include <misc/city.h>
62   	#include <rpc/rpc_cksum.h>
63   	
64   	#ifndef MAX
65   	#define MAX(a, b) (((a) > (b)) ? (a) : (b))
66   	#endif
67   	
68   	static void svc_dg_rendezvous_ops(SVCXPRT *);
69   	static void svc_dg_override_ops(SVCXPRT *, SVCXPRT *);
70   	
71   	static void svc_dg_enable_pktinfo(int, const struct __rpc_sockinfo *);
72   	static int svc_dg_store_pktinfo(struct msghdr *, SVCXPRT *);
73   	
74   	/*
75   	 * Usage:
76   	 * xprt = svc_dg_ncreate(sock, sendsize, recvsize);
77   	 *
78   	 * If recvsize or sendsize are 0 suitable,
79   	 * system defaults are chosen.
80   	 * If a problem occurred, this routine returns NULL.
81   	 */
82   	static void
83   	svc_dg_xprt_free(struct svc_dg_xprt *su)
84   	{
85   		XDR_DESTROY(su->su_dr.ioq.xdrs);
86   		rpc_dplx_rec_destroy(&su->su_dr);
87   		mem_free(su, sizeof(struct svc_dg_xprt) + su->su_dr.maxrec);
88   	}
89   	
90   	static struct svc_dg_xprt *
91   	svc_dg_xprt_zalloc(size_t iosz)
92   	{
93   		struct svc_dg_xprt *su = mem_zalloc(sizeof(struct svc_dg_xprt) + iosz);
94   	
95   		/* Init SVCXPRT locks, etc */
96   		rpc_dplx_rec_init(&su->su_dr);
97   		/* Extra ref to match TCP */
98   		SVC_REF(&su->su_dr.xprt, SVC_REF_FLAG_NONE);
99   		xdr_ioq_setup(&su->su_dr.ioq);
100  		return (su);
101  	}
102  	
103  	static void
104  	svc_dg_xprt_setup(SVCXPRT **sxpp)
105  	{
106  		if (unlikely(*sxpp)) {
107  			svc_dg_xprt_free(su_data(*sxpp));
108  			*sxpp = NULL;
109  		} else {
110  			struct svc_dg_xprt *su = svc_dg_xprt_zalloc(0);
111  	
112  			*sxpp = &su->su_dr.xprt;
113  		}
114  	}
115  	
116  	SVCXPRT *
117  	svc_dg_ncreatef(const int fd, const u_int sendsz, const u_int recvsz,
118  			const uint32_t flags)
119  	{
120  		SVCXPRT *xprt;
121  		struct rpc_dplx_rec *rec;
122  		struct svc_dg_xprt *su;
123  		struct __rpc_sockinfo si;
124  		u_int recvsize;
125  		u_int sendsize;
126  		u_int xp_flags;
127  		int rc;
128  	
129  		/* atomically find or create shared fd state; ref+1; locked */
130  		xprt = svc_xprt_lookup(fd, svc_dg_xprt_setup);
131  		if (!xprt) {
132  			__warnx(TIRPC_DEBUG_FLAG_ERROR,
133  				"%s: fd %d svc_xprt_lookup failed",
134  				__func__, fd);
135  			return (NULL);
136  		}
137  		rec = REC_XPRT(xprt);
138  	
139  		xp_flags = atomic_postset_uint16_t_bits(&xprt->xp_flags, flags
140  							| SVC_XPRT_FLAG_INITIALIZED);
141  		if ((xp_flags & SVC_XPRT_FLAG_INITIALIZED)) {
142  			rpc_dplx_rui(rec);
143  			XPRT_TRACE(xprt, __func__, __func__, __LINE__);
144  			return (xprt);
145  		}
146  	
147  		if (!__rpc_fd2sockinfo(fd, &si)) {
148  			atomic_clear_uint16_t_bits(&xprt->xp_flags,
149  						   SVC_XPRT_FLAG_INITIALIZED);
150  			rpc_dplx_rui(rec);
151  			__warnx(TIRPC_DEBUG_FLAG_ERROR,
152  				"%s: fd %d could not get transport information",
153  				__func__, fd);
154  			return (NULL);
155  		}
156  		/*
157  		 * Find the receive and the send size
158  		 */
159  		sendsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)sendsz);
160  		recvsize = __rpc_get_t_size(si.si_af, si.si_proto, (int)recvsz);
161  		if ((sendsize == 0) || (recvsize == 0)) {
162  			atomic_clear_uint16_t_bits(&xprt->xp_flags,
163  						   SVC_XPRT_FLAG_INITIALIZED);
164  			rpc_dplx_rui(rec);
165  			__warnx(TIRPC_DEBUG_FLAG_ERROR,
166  				"%s: fd %d transport does not support data transfer",
167  				__func__, fd);
168  			return (NULL);
169  		}
170  	
171  		__rpc_address_setup(&xprt->xp_local);
172  		rc = getsockname(fd, xprt->xp_local.nb.buf, &xprt->xp_local.nb.len);
173  		if (rc < 0) {
174  			atomic_clear_uint16_t_bits(&xprt->xp_flags,
175  						   SVC_XPRT_FLAG_INITIALIZED);
176  			rpc_dplx_rui(rec);
177  			__warnx(TIRPC_DEBUG_FLAG_ERROR,
178  				"%s: fd %d getsockname failed (%d)",
179  				 __func__, fd, rc);
180  			return (NULL);
181  		}
182  	
183  		/*
184  		 * Should be multiple of 4 for XDR.
185  		 */
186  		su = DG_DR(rec);
187  		su->su_dr.sendsz = ((sendsize + 3) / 4) * 4;
188  		su->su_dr.recvsz = ((recvsize + 3) / 4) * 4;
189  		su->su_dr.maxrec = ((MAX(sendsize, recvsize) + 3) / 4) * 4;
190  	
191  		/* duplex streams are not used by the rendezvous transport */
192  		xdrmem_create(su->su_dr.ioq.xdrs, NULL, 0, XDR_ENCODE);
193  	
194  		svc_dg_rendezvous_ops(xprt);
195  	
196  		/* Enable reception of IP*_PKTINFO control msgs */
197  		svc_dg_enable_pktinfo(fd, &si);
198  	
199  		/* Conditional register */
200  		if ((!(__svc_params->flags & SVC_FLAG_NOREG_XPRTS)
201  		     && !(flags & SVC_CREATE_FLAG_XPRT_NOREG))
202  		    || (flags & SVC_CREATE_FLAG_XPRT_DOREG))
203  			svc_rqst_evchan_reg(__svc_params->ev_u.evchan.id, xprt,
204  					    RPC_DPLX_LOCKED |
205  					    SVC_RQST_FLAG_CHAN_AFFINITY);
206  	
207  		/* release */
208  		rpc_dplx_rui(rec);
209  		XPRT_TRACE(xprt, __func__, __func__, __LINE__);
210  	
211  	#if defined(HAVE_BLKIN)
212  		__rpc_set_blkin_endpoint(xprt, "svc_dg");
213  	#endif
214  	
215  		return (xprt);
216  	}
217  	
218  	 /*ARGSUSED*/
219  	static enum xprt_stat
220  	svc_dg_stat(SVCXPRT *xprt)
221  	{
222  		return SVC_STAT(xprt->xp_parent);
223  	}
224  	
225  	static enum xprt_stat
226  	svc_dg_rendezvous(SVCXPRT *xprt)
227  	{
228  		struct svc_dg_xprt *req_su = su_data(xprt);
229  		struct svc_dg_xprt *su = svc_dg_xprt_zalloc(req_su->su_dr.maxrec);
230  		SVCXPRT *newxprt = &su->su_dr.xprt;
231  		struct sockaddr *sp = (struct sockaddr *)&newxprt->xp_remote.ss;
232  		struct msghdr *mesgp;
233  		struct timespec now;
234  		struct iovec iov;
235  		ssize_t rlen;
236  	
237  		newxprt->xp_fd = xprt->xp_fd;
238  		newxprt->xp_flags = SVC_XPRT_FLAG_INITIAL | SVC_XPRT_FLAG_INITIALIZED;
239  	
240  		(void)clock_gettime(CLOCK_MONOTONIC_FAST, &now);
241  		su->su_dr.call_xid = __RPC_GETXID(&now);
242  		su->su_dr.sendsz = req_su->su_dr.sendsz;
243  		su->su_dr.recvsz = req_su->su_dr.recvsz;
244  		su->su_dr.maxrec = req_su->su_dr.maxrec;
245  		svc_dg_override_ops(newxprt, xprt);
246  	
247  	 again:
248  		iov.iov_base = &su[1];
249  		iov.iov_len = su->su_dr.maxrec;
250  		mesgp = &su->su_msghdr;
251  		memset(mesgp, 0, sizeof(*mesgp));
252  		mesgp->msg_iov = &iov;
253  		mesgp->msg_iovlen = 1;
254  		mesgp->msg_name = sp;
255  		sp->sa_family = (sa_family_t) 0xffff;
256  		mesgp->msg_namelen = sizeof(struct sockaddr_storage);
257  		mesgp->msg_control = su->su_cmsg;
258  		mesgp->msg_controllen = sizeof(su->su_cmsg);
259  	
260  		rlen = recvmsg(newxprt->xp_fd, mesgp, 0);
261  	
262  		if (sp->sa_family == (sa_family_t) 0xffff) {
263  			svc_dg_xprt_free(su);
264  			return (XPRT_DIED);
265  		}
266  	
267  		if (rlen == -1 && errno == EINTR)
268  			goto again;
269  		if (rlen == -1 || (rlen < (ssize_t) (4 * sizeof(u_int32_t)))) {
270  			svc_dg_xprt_free(su);
271  			return (XPRT_DIED);
272  		}
273  	
274  		if (unlikely(svc_rqst_rearm_events(xprt))) {
275  			__warnx(TIRPC_DEBUG_FLAG_ERROR,
276  				"%s: %p fd %d svc_rqst_rearm_events failed (will set dead)",
277  				__func__, xprt, xprt->xp_fd);
278  			svc_dg_xprt_free(su);
279  			return (XPRT_DIED);
280  		}
281  	
282  		__rpc_address_setup(&newxprt->xp_local);
283  		__rpc_address_setup(&newxprt->xp_remote);
284  		newxprt->xp_remote.nb.len = mesgp->msg_namelen;
285  	
286  		/* Check whether there's an IP_PKTINFO or IP6_PKTINFO control message.
287  		 * If yes, preserve it for svc_dg_reply; otherwise just zap any cmsgs */
288  		if (!svc_dg_store_pktinfo(mesgp, newxprt)) {
289  			mesgp->msg_control = NULL;
290  			mesgp->msg_controllen = 0;
291  			newxprt->xp_local.nb.len = 0;
292  		}
293  		XPRT_TRACE(newxprt, __func__, __func__, __LINE__);
294  	
295  	#if defined(HAVE_BLKIN)
296  		__rpc_set_blkin_endpoint(newxprt, "svc_dg");
297  	#endif
298  	
299  		xdrmem_create(su->su_dr.ioq.xdrs, iov.iov_base, iov.iov_len,
300  			      XDR_DECODE);
301  	
302  		SVC_REF(xprt, SVC_REF_FLAG_NONE);
303  		newxprt->xp_parent = xprt;
304  		return (xprt->xp_dispatch.rendezvous_cb(newxprt));
305  	}
306  	
307  	static enum xprt_stat
308  	svc_dg_recv(SVCXPRT *xprt)
309  	{
310  		enum xprt_stat stat;
311  	
312  		/* pass the xdrs to user to store in struct svc_req, as most of
313  		 * the work has already been done on rendezvous
314  		 */
315  		stat = svc_request(xprt, REC_XPRT(xprt)->ioq.xdrs);
316  	
(1) Event cond_false: Condition "xprt->xp_flags & (96 /* 0x20 | 0x40 */)", taking false branch.
317  		if (xprt->xp_flags & SVC_XPRT_FLAG_DESTROYED)
(2) Event if_end: End of if statement.
318  			return (XPRT_DESTROYED);
319  	
320  		/* Only after checking SVC_XPRT_FLAG_DESTROYED:
321  		 * because SVC_DESTROY() has decremented already.
322  		 */
(3) Event unlock: "svc_destroy_it" unlocks "xprt->xp_lock". [details]
Also see events: [double_unlock]
323  		SVC_DESTROY(xprt);
(4) Event double_unlock: "svc_release_it" unlocks "xprt->xp_lock" while it is unlocked. [details]
Also see events: [unlock]
324  		SVC_RELEASE(xprt, SVC_RELEASE_FLAG_NONE);
325  		return (stat);
326  	}
327  	
328  	static enum xprt_stat
329  	svc_dg_decode(struct svc_req *req)
330  	{
331  		XDR *xdrs = req->rq_xdrs;
332  		SVCXPRT *xprt = req->rq_xprt;
333  	
334  		xdrs->x_op = XDR_DECODE;
335  		XDR_SETPOS(xdrs, 0);
336  		rpc_msg_init(&req->rq_msg);
337  	
338  		if (!xdr_dplx_decode(xdrs, &req->rq_msg)) {
339  			__warnx(TIRPC_DEBUG_FLAG_ERROR,
340  				"%s: %p fd %d failed (will set dead)",
341  				__func__, xprt, xprt->xp_fd);
342  			return (XPRT_DIED);
343  		}
344  	
345  		/* in order of likelihood */
346  		if (req->rq_msg.rm_direction == CALL) {
347  			/* an ordinary call header */
348  			return xprt->xp_dispatch.process_cb(req);
349  		}
350  	
351  		if (req->rq_msg.rm_direction == REPLY) {
352  			/* reply header (xprt OK) */
353  			return clnt_req_process_reply(xprt->xp_parent, req);
354  		}
355  	
356  		__warnx(TIRPC_DEBUG_FLAG_WARN,
357  			"%s: %p fd %d failed direction %" PRIu32
358  			" (will set dead)",
359  			__func__, __func__, xprt, xprt->xp_fd,
360  			req->rq_msg.rm_direction);
361  		return (XPRT_DIED);
362  	}
363  	
364  	static void
365  	svc_dg_checksum(struct svc_req *req, void *data, size_t length)
366  	{
367  		req->rq_cksum =
368  	#if 1
369  		/* CithHash64 is -substantially- faster than crc32c from FreeBSD
370  		 * SCTP, so prefer it until fast crc32c bests it */
371  			CityHash64WithSeed(data, MIN(256, length), 103);
372  	#else
373  			calculate_crc32c(0, data, MIN(256, length));
374  	#endif
375  	}
376  	
377  	static enum xprt_stat
378  	svc_dg_reply(struct svc_req *req)
379  	{
380  		SVCXPRT *xprt = req->rq_xprt;
381  		struct rpc_dplx_rec *rec = REC_XPRT(xprt);
382  		XDR *xdrs = rec->ioq.xdrs;
383  		struct svc_dg_xprt *su = DG_DR(rec);
384  		struct msghdr *msg = &su->su_msghdr;
385  		struct iovec iov;
386  		size_t slen;
387  	
388  		if (!xprt->xp_remote.nb.len) {
389  			__warnx(TIRPC_DEBUG_FLAG_WARN,
390  				"%s: %p fd %d has no remote address",
391  				__func__, xprt, xprt->xp_fd);
392  			return (XPRT_IDLE);
393  		}
394  		xdrs->x_op = XDR_ENCODE;
395  		XDR_SETPOS(xdrs, 0);
396  	
397  		if (!xdr_reply_encode(xdrs, &req->rq_msg)) {
398  			__warnx(TIRPC_DEBUG_FLAG_ERROR,
399  				"%s: %p fd %d xdr_reply_encode failed (will set dead)",
400  				__func__, xprt, xprt->xp_fd);
401  			return (XPRT_DIED);
402  		}
403  	
404  		if (req->rq_msg.rm_reply.rp_stat == MSG_ACCEPTED
405  		 && req->rq_msg.rm_reply.rp_acpt.ar_stat == SUCCESS
406  		 && req->rq_auth
407  		 && !SVCAUTH_WRAP(req, xdrs)) {
408  			__warnx(TIRPC_DEBUG_FLAG_ERROR,
409  				"%s: %p fd %d SVCAUTH_WRAP failed (will set dead)",
410  				__func__, xprt, xprt->xp_fd);
411  			return (XPRT_DIED);
412  		}
413  		iov.iov_base = &su[1];
414  		iov.iov_len = slen = XDR_GETPOS(xdrs);
415  		msg->msg_iov = &iov;
416  		msg->msg_iovlen = 1;
417  		msg->msg_name = (struct sockaddr *)&xprt->xp_remote.ss;
418  		msg->msg_namelen = xprt->xp_remote.nb.len;
419  		/* cmsg already set in svc_dg_rendezvous */
420  	
421  		if (sendmsg(xprt->xp_fd, msg, 0) != (ssize_t) slen) {
422  			__warnx(TIRPC_DEBUG_FLAG_ERROR,
423  				"%s: %p fd %d sendmsg failed (will set dead)",
424  				__func__, xprt, xprt->xp_fd);
425  			return (XPRT_DIED);
426  		}
427  	
428  		return (XPRT_IDLE);
429  	}
430  	
431  	static void
432  	svc_dg_destroy_task(struct work_pool_entry *wpe)
433  	{
434  		struct rpc_dplx_rec *rec =
435  				opr_containerof(wpe, struct rpc_dplx_rec, ioq.ioq_wpe);
436  		SVCXPRT *xprt = &rec->xprt;
437  		uint16_t xp_flags;
438  	
439  		__warnx(TIRPC_DEBUG_FLAG_REFCNT,
440  			"%s() %p fd %d xp_refcnt %" PRId32,
441  			__func__, xprt, xprt->xp_fd, xprt->xp_refcnt);
442  	
443  		if (rec->xprt.xp_refcnt) {
444  			/* instead of nanosleep */
445  			work_pool_submit(&svc_work_pool, &(rec->ioq.ioq_wpe));
446  			return;
447  		}
448  	
449  		xp_flags = atomic_postclear_uint16_t_bits(&rec->xprt.xp_flags,
450  							  SVC_XPRT_FLAG_CLOSE);
451  		if ((xp_flags & SVC_XPRT_FLAG_CLOSE)
452  		    && rec->xprt.xp_fd != RPC_ANYFD) {
453  			(void)close(rec->xprt.xp_fd);
454  			rec->xprt.xp_fd = RPC_ANYFD;
455  		}
456  	
457  		if (rec->xprt.xp_ops->xp_free_user_data)
458  			rec->xprt.xp_ops->xp_free_user_data(&rec->xprt);
459  	
460  		if (rec->xprt.xp_tp)
461  			mem_free(rec->xprt.xp_tp, 0);
462  		if (rec->xprt.xp_netid)
463  			mem_free(rec->xprt.xp_netid, 0);
464  	
465  		if (rec->xprt.xp_parent)
466  			SVC_RELEASE(rec->xprt.xp_parent, SVC_RELEASE_FLAG_NONE);
467  	
468  		svc_dg_xprt_free(DG_DR(rec));
469  	}
470  	
471  	static void
472  	svc_dg_destroy_it(SVCXPRT *xprt, u_int flags, const char *tag, const int line)
473  	{
474  		struct timespec ts = {
475  			.tv_sec = 0,
476  			.tv_nsec = 0,
477  		};
478  	
479  		if (!xprt->xp_parent) {
480  			/* only original parent is registered */
481  			svc_rqst_xprt_unregister(xprt, flags);
482  		}
483  	
484  		__warnx(TIRPC_DEBUG_FLAG_REFCNT,
485  			"%s() %p fd %d xp_refcnt %" PRId32 " @%s:%d",
486  			__func__, xprt, xprt->xp_fd, xprt->xp_refcnt, tag, line);
487  	
488  		while (atomic_postset_uint16_t_bits(&(REC_XPRT(xprt)->ioq.ioq_s.qflags),
489  						    IOQ_FLAG_WORKING)
490  		       & IOQ_FLAG_WORKING) {
491  			nanosleep(&ts, NULL);
492  		}
493  	
494  		REC_XPRT(xprt)->ioq.ioq_wpe.fun = svc_dg_destroy_task;
495  		work_pool_submit(&svc_work_pool, &(REC_XPRT(xprt)->ioq.ioq_wpe));
496  	}
497  	
498  	static void
499  	svc_dg_destroy(SVCXPRT *xprt, u_int flags, const char *tag, const int line)
500  	{
501  		svc_dg_destroy_it(xprt, flags, tag, line);
502  	}
503  	
504  	extern mutex_t ops_lock;
505  	
506  	 /*ARGSUSED*/
507  	static bool
508  	svc_dg_control(SVCXPRT *xprt, const u_int rq, void *in)
509  	{
510  		switch (rq) {
511  		case SVCGET_XP_FLAGS:
512  			*(u_int *) in = xprt->xp_flags;
513  			break;
514  		case SVCSET_XP_FLAGS:
515  			xprt->xp_flags = *(u_int *) in;
516  			break;
517  		case SVCGET_XP_FREE_USER_DATA:
518  			mutex_lock(&ops_lock);
519  			*(svc_xprt_fun_t *) in = xprt->xp_ops->xp_free_user_data;
520  			mutex_unlock(&ops_lock);
521  			break;
522  		case SVCSET_XP_FREE_USER_DATA:
523  			mutex_lock(&ops_lock);
524  			xprt->xp_ops->xp_free_user_data = *(svc_xprt_fun_t) in;
525  			mutex_unlock(&ops_lock);
526  			break;
527  		default:
528  			return (false);
529  		}
530  		return (true);
531  	}
532  	
533  	static void
534  	svc_dg_override_ops(SVCXPRT *xprt, SVCXPRT *rendezvous)
535  	{
536  		static struct xp_ops ops;
537  	
538  		/* VARIABLES PROTECTED BY ops_lock: ops, xp_type */
539  		mutex_lock(&ops_lock);
540  	
541  		/* Fill in type of service */
542  		xprt->xp_type = XPRT_UDP;
543  	
544  		if (ops.xp_recv == NULL) {
545  			ops.xp_recv = svc_dg_recv;
546  			ops.xp_stat = svc_dg_stat;
547  			ops.xp_decode = svc_dg_decode;
548  			ops.xp_reply = svc_dg_reply;
549  			ops.xp_checksum = svc_dg_checksum;
550  			ops.xp_destroy = svc_dg_destroy;
551  			ops.xp_control = svc_dg_control;
552  			ops.xp_free_user_data = NULL;	/* no default */
553  		}
554  		svc_override_ops(&ops, rendezvous);
555  		xprt->xp_ops = &ops;
556  		mutex_unlock(&ops_lock);
557  	}
558  	
559  	static void
560  	svc_dg_rendezvous_ops(SVCXPRT *xprt)
561  	{
562  		static struct xp_ops ops;
563  	
564  		mutex_lock(&ops_lock);
565  	
566  		xprt->xp_type = XPRT_UDP_RENDEZVOUS;
567  	
568  		if (ops.xp_recv == NULL) {
569  			ops.xp_recv = svc_dg_rendezvous;
570  			ops.xp_stat = svc_rendezvous_stat;
571  			ops.xp_decode = (svc_req_fun_t)abort;
572  			ops.xp_reply = (svc_req_fun_t)abort;
573  			ops.xp_checksum = NULL;		/* not used */
574  			ops.xp_destroy = svc_dg_destroy_it;
575  			ops.xp_control = svc_dg_control;
576  			ops.xp_free_user_data = NULL;	/* no default */
577  		}
578  		xprt->xp_ops = &ops;
579  		mutex_unlock(&ops_lock);
580  	}
581  	
582  	/*
583  	 * Enable reception of PKTINFO control messages
584  	 */
585  	void
586  	svc_dg_enable_pktinfo(int fd, const struct __rpc_sockinfo *si)
587  	{
588  		int val = 1;
589  	
590  		switch (si->si_af) {
591  		case AF_INET:
592  	#ifdef SOL_IP
593  			(void)setsockopt(fd, SOL_IP, IP_PKTINFO, &val, sizeof(val));
594  	#endif
595  			break;
596  	
597  		case AF_INET6:
598  	#ifdef SOL_IP
599  			(void)setsockopt(fd, SOL_IP, IP_PKTINFO, &val, sizeof(val));
600  	#endif
601  	#ifdef SOL_IPV6
602  			(void)setsockopt(fd, SOL_IPV6, IPV6_RECVPKTINFO,
603  					&val, sizeof(val));
604  	#endif
605  			break;
606  		}
607  	}
608  	
609  	static int
610  	svc_dg_store_in_pktinfo(struct cmsghdr *cmsg, SVCXPRT *xprt)
611  	{
612  		if (cmsg->cmsg_level == SOL_IP &&
613  		    cmsg->cmsg_type == IP_PKTINFO &&
614  		    cmsg->cmsg_len >= CMSG_LEN(sizeof(struct in_pktinfo))) {
615  			struct in_pktinfo *pkti = (struct in_pktinfo *)
616  							CMSG_DATA(cmsg);
617  			struct sockaddr_in *daddr = (struct sockaddr_in *)
618  							&xprt->xp_local.ss;
619  	
620  			daddr->sin_family = AF_INET;
621  	#ifdef __FreeBSD__
622  			daddr->sin_addr = pkti->ipi_addr;
623  	#else
624  			daddr->sin_addr.s_addr = pkti->ipi_spec_dst.s_addr;
625  	#endif
626  			xprt->xp_local.nb.len = sizeof(struct sockaddr_in);
627  			return 1;
628  		} else {
629  			return 0;
630  		}
631  	}
632  	
633  	static int
634  	svc_dg_store_in6_pktinfo(struct cmsghdr *cmsg, SVCXPRT *xprt)
635  	{
636  		if (cmsg->cmsg_level == SOL_IPV6 &&
637  		    cmsg->cmsg_type == IPV6_PKTINFO &&
638  		    cmsg->cmsg_len >= CMSG_LEN(sizeof(struct in6_pktinfo))) {
639  			struct in6_pktinfo *pkti = (struct in6_pktinfo *)
640  							CMSG_DATA(cmsg);
641  			struct sockaddr_in6 *daddr = (struct sockaddr_in6 *)
642  							&xprt->xp_local.ss;
643  	
644  			daddr->sin6_family = AF_INET6;
645  			daddr->sin6_addr = pkti->ipi6_addr;
646  			daddr->sin6_scope_id = pkti->ipi6_ifindex;
647  			xprt->xp_local.nb.len = sizeof(struct sockaddr_in6);
648  			return 1;
649  		} else {
650  			return 0;
651  		}
652  	}
653  	
654  	/*
655  	 * When given a control message received from the socket
656  	 * layer, check whether it contains valid PKTINFO data.
657  	 * If so, store the data in the request.
658  	 */
659  	static int
660  	svc_dg_store_pktinfo(struct msghdr *msg, SVCXPRT *xprt)
661  	{
662  		struct cmsghdr *cmsg;
663  	
664  		if (!msg->msg_name)
665  			return 0;
666  	
667  		if (msg->msg_flags & MSG_CTRUNC)
668  			return 0;
669  	
670  		cmsg = CMSG_FIRSTHDR(msg);
671  		if (cmsg == NULL || CMSG_NXTHDR(msg, cmsg) != NULL)
672  			return 0;
673  	
674  		switch (((struct sockaddr *)msg->msg_name)->sa_family) {
675  		case AF_INET:
676  	#ifdef SOL_IP
677  			if (svc_dg_store_in_pktinfo(cmsg, xprt))
678  				return 1;
679  	#endif
680  			break;
681  	
682  		case AF_INET6:
683  	#ifdef SOL_IP
684  			/* Handle IPv4 PKTINFO as well on IPV6 interface */
685  			if (svc_dg_store_in_pktinfo(cmsg, xprt))
686  				return 1;
687  	#endif
688  	#ifdef SOL_IPV6
689  			if (svc_dg_store_in6_pktinfo(cmsg, xprt))
690  				return 1;
691  	#endif
692  			break;
693  	
694  		default:
695  			break;
696  		}
697  	
698  		return 0;
699  	}
700