1 /* @(#)svc_udp.c 2.2 88/07/29 4.0 RPCSRC */
3 * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
4 * unrestricted use provided that this legend is included on all tape
5 * media and as a part of the software program in whole or part. Users
6 * may copy or modify Sun RPC without charge, but are not authorized
7 * to license or distribute it to anyone else except as part of a product or
8 * program developed by the user.
10 * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
11 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
12 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
14 * Sun RPC is provided with no support and without any obligation on the
15 * part of Sun Microsystems, Inc. to assist in its use, correction,
16 * modification or enhancement.
18 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
19 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
20 * OR ANY PART THEREOF.
22 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
23 * or profits or other special, indirect and consequential damages, even if
24 * Sun has been advised of the possibility of such damages.
26 * Sun Microsystems, Inc.
28 * Mountain View, California 94043
30 #if !defined(lint) && defined(SCCSIDS)
31 static char sccsid
[] = "@(#)svc_udp.c 1.24 87/08/11 Copyr 1984 Sun Micro";
36 * Server side for UDP/IP based RPC. (Does some caching in the hopes of
37 * achieving execute-at-most-once semantics.)
39 * Copyright (C) 1984, Sun Microsystems, Inc.
46 #include <sys/socket.h>
56 # include <libio/iolibio.h>
59 #define rpc_buffer(xprt) ((xprt)->xp_p1)
61 #define MAX(a, b) ((a > b) ? a : b)
64 static bool_t
svcudp_recv (SVCXPRT
*, struct rpc_msg
*);
65 static bool_t
svcudp_reply (SVCXPRT
*, struct rpc_msg
*);
66 static enum xprt_stat
svcudp_stat (SVCXPRT
*);
67 static bool_t
svcudp_getargs (SVCXPRT
*, xdrproc_t
, caddr_t
);
68 static bool_t
svcudp_freeargs (SVCXPRT
*, xdrproc_t
, caddr_t
);
69 static void svcudp_destroy (SVCXPRT
*);
71 static const struct xp_ops svcudp_op
=
81 static int cache_get (SVCXPRT
*, struct rpc_msg
*, char **replyp
,
83 static void cache_set (SVCXPRT
*xprt
, u_long replylen
);
90 u_int su_iosz
; /* byte size of send.recv buffer */
91 u_long su_xid
; /* transaction id */
92 XDR su_xdrs
; /* XDR handle */
93 char su_verfbody
[MAX_AUTH_BYTES
]; /* verifier body */
94 char *su_cache
; /* cached data, NULL if no cache */
96 #define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2))
100 * xprt = svcudp_create(sock);
102 * If sock<0 then a socket is created, else sock is used.
103 * If the socket, sock is not bound to a port then svcudp_create
104 * binds it to an arbitrary port. In any (successful) case,
105 * xprt->xp_sock is the registered socket number and xprt->xp_port is the
106 * associated port number.
107 * Once *xprt is initialized, it is registered as a transporter;
108 * see (svc.h, xprt_register).
109 * The routines returns NULL if a problem occurred.
112 svcudp_bufcreate (sock
, sendsz
, recvsz
)
114 u_int sendsz
, recvsz
;
116 bool_t madesock
= FALSE
;
118 struct svcudp_data
*su
;
119 struct sockaddr_in addr
;
120 socklen_t len
= sizeof (struct sockaddr_in
);
124 if (sock
== RPC_ANYSOCK
)
126 if ((sock
= __socket (AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
)) < 0)
128 perror (_("svcudp_create: socket creation problem"));
129 return (SVCXPRT
*) NULL
;
133 __bzero ((char *) &addr
, sizeof (addr
));
134 addr
.sin_family
= AF_INET
;
135 if (bindresvport (sock
, &addr
))
138 (void) __bind (sock
, (struct sockaddr
*) &addr
, len
);
140 if (__getsockname (sock
, (struct sockaddr
*) &addr
, &len
) != 0)
142 perror (_("svcudp_create - cannot getsockname"));
144 (void) __close (sock
);
145 return (SVCXPRT
*) NULL
;
147 xprt
= (SVCXPRT
*) mem_alloc (sizeof (SVCXPRT
));
148 su
= (struct svcudp_data
*) mem_alloc (sizeof (*su
));
149 buf
= mem_alloc (((MAX (sendsz
, recvsz
) + 3) / 4) * 4);
150 if (xprt
== NULL
|| su
== NULL
|| buf
== NULL
)
153 if (_IO_fwide (stderr
, 0) > 0)
154 (void) __fwprintf (stderr
, L
"%s", _("svcudp_create: out of memory\n"));
157 (void) fputs (_("svcudp_create: out of memory\n"), stderr
);
158 mem_free (xprt
, sizeof (SVCXPRT
));
159 mem_free (su
, sizeof (*su
));
160 mem_free (buf
, ((MAX (sendsz
, recvsz
) + 3) / 4) * 4);
163 su
->su_iosz
= ((MAX (sendsz
, recvsz
) + 3) / 4) * 4;
164 rpc_buffer (xprt
) = buf
;
165 INTUSE(xdrmem_create
) (&(su
->su_xdrs
), rpc_buffer (xprt
), su
->su_iosz
,
168 xprt
->xp_p2
= (caddr_t
) su
;
169 xprt
->xp_verf
.oa_base
= su
->su_verfbody
;
170 xprt
->xp_ops
= &svcudp_op
;
171 xprt
->xp_port
= ntohs (addr
.sin_port
);
172 xprt
->xp_sock
= sock
;
175 if ((sizeof (struct iovec
) + sizeof (struct msghdr
)
176 + sizeof(struct cmsghdr
) + sizeof (struct in_pktinfo
))
177 > sizeof (xprt
->xp_pad
))
180 if (_IO_fwide (stderr
, 0) > 0)
181 (void) __fwprintf (stderr
, L
"%s",
182 _("svcudp_create: xp_pad is too small for IP_PKTINFO\n"));
185 (void) fputs (_("svcudp_create: xp_pad is too small for IP_PKTINFO\n"),
190 if (__setsockopt (sock
, SOL_IP
, IP_PKTINFO
, (void *) &pad
,
192 /* Set the padding to all 1s. */
196 /* Clear the padding. */
198 memset (&xprt
->xp_pad
[0], pad
, sizeof (xprt
->xp_pad
));
200 xprt_register (xprt
);
203 INTDEF (svcudp_bufcreate
)
209 return INTUSE(svcudp_bufcreate
) (sock
, UDPMSGSIZE
, UDPMSGSIZE
);
211 INTDEF (svcudp_create
)
213 static enum xprt_stat
222 svcudp_recv (xprt
, msg
)
226 struct svcudp_data
*su
= su_data (xprt
);
227 XDR
*xdrs
= &(su
->su_xdrs
);
233 /* It is very tricky when you have IP aliases. We want to make sure
234 that we are sending the packet from the IP address where the
235 incoming packet is addressed to. H.J. */
238 struct msghdr
*mesgp
;
242 /* FIXME -- should xp_addrlen be a size_t? */
243 len
= (socklen_t
) sizeof(struct sockaddr_in
);
245 iovp
= (struct iovec
*) &xprt
->xp_pad
[0];
246 mesgp
= (struct msghdr
*) &xprt
->xp_pad
[sizeof (struct iovec
)];
247 if (mesgp
->msg_iovlen
)
249 iovp
->iov_base
= rpc_buffer (xprt
);
250 iovp
->iov_len
= su
->su_iosz
;
251 mesgp
->msg_iov
= iovp
;
252 mesgp
->msg_iovlen
= 1;
253 mesgp
->msg_name
= &(xprt
->xp_raddr
);
254 mesgp
->msg_namelen
= len
;
255 mesgp
->msg_control
= &xprt
->xp_pad
[sizeof (struct iovec
)
256 + sizeof (struct msghdr
)];
257 mesgp
->msg_controllen
= sizeof(xprt
->xp_pad
)
258 - sizeof (struct iovec
) - sizeof (struct msghdr
);
259 rlen
= __recvmsg (xprt
->xp_sock
, mesgp
, 0);
262 struct cmsghdr
*cmsg
;
263 len
= mesgp
->msg_namelen
;
264 cmsg
= CMSG_FIRSTHDR (mesgp
);
266 || CMSG_NXTHDR (mesgp
, cmsg
) != NULL
267 || cmsg
->cmsg_level
!= SOL_IP
268 || cmsg
->cmsg_type
!= IP_PKTINFO
269 || cmsg
->cmsg_len
< (sizeof (struct cmsghdr
)
270 + sizeof (struct in_pktinfo
)))
272 /* Not a simple IP_PKTINFO, ignore it. */
273 mesgp
->msg_control
= NULL
;
274 mesgp
->msg_controllen
= 0;
278 /* It was a simple IP_PKTIFO as we expected, discard the
280 struct in_pktinfo
*pkti
= (struct in_pktinfo
*) CMSG_DATA (cmsg
);
281 pkti
->ipi_ifindex
= 0;
287 rlen
= __recvfrom (xprt
->xp_sock
, rpc_buffer (xprt
),
288 (int) su
->su_iosz
, 0,
289 (struct sockaddr
*) &(xprt
->xp_raddr
), &len
);
290 xprt
->xp_addrlen
= len
;
291 if (rlen
== -1 && errno
== EINTR
)
293 if (rlen
< 16) /* < 4 32-bit ints? */
295 xdrs
->x_op
= XDR_DECODE
;
296 XDR_SETPOS (xdrs
, 0);
297 if (!INTUSE(xdr_callmsg
) (xdrs
, msg
))
299 su
->su_xid
= msg
->rm_xid
;
300 if (su
->su_cache
!= NULL
)
302 if (cache_get (xprt
, msg
, &reply
, &replylen
))
305 if (mesgp
->msg_iovlen
)
307 iovp
->iov_base
= reply
;
308 iovp
->iov_len
= replylen
;
309 (void) __sendmsg (xprt
->xp_sock
, mesgp
, 0);
313 (void) __sendto (xprt
->xp_sock
, reply
, (int) replylen
, 0,
314 (struct sockaddr
*) &xprt
->xp_raddr
, len
);
322 svcudp_reply (xprt
, msg
)
326 struct svcudp_data
*su
= su_data (xprt
);
327 XDR
*xdrs
= &(su
->su_xdrs
);
332 struct msghdr
*mesgp
;
335 xdrs
->x_op
= XDR_ENCODE
;
336 XDR_SETPOS (xdrs
, 0);
337 msg
->rm_xid
= su
->su_xid
;
338 if (INTUSE(xdr_replymsg
) (xdrs
, msg
))
340 slen
= (int) XDR_GETPOS (xdrs
);
342 mesgp
= (struct msghdr
*) &xprt
->xp_pad
[sizeof (struct iovec
)];
343 if (mesgp
->msg_iovlen
)
345 iovp
= (struct iovec
*) &xprt
->xp_pad
[0];
346 iovp
->iov_base
= rpc_buffer (xprt
);
347 iovp
->iov_len
= slen
;
348 sent
= __sendmsg (xprt
->xp_sock
, mesgp
, 0);
352 sent
= __sendto (xprt
->xp_sock
, rpc_buffer (xprt
), slen
, 0,
353 (struct sockaddr
*) &(xprt
->xp_raddr
),
358 if (su
->su_cache
&& slen
>= 0)
360 cache_set (xprt
, (u_long
) slen
);
368 svcudp_getargs (xprt
, xdr_args
, args_ptr
)
374 return (*xdr_args
) (&(su_data (xprt
)->su_xdrs
), args_ptr
);
378 svcudp_freeargs (xprt
, xdr_args
, args_ptr
)
383 XDR
*xdrs
= &(su_data (xprt
)->su_xdrs
);
385 xdrs
->x_op
= XDR_FREE
;
386 return (*xdr_args
) (xdrs
, args_ptr
);
390 svcudp_destroy (xprt
)
393 struct svcudp_data
*su
= su_data (xprt
);
395 xprt_unregister (xprt
);
396 (void) __close (xprt
->xp_sock
);
397 XDR_DESTROY (&(su
->su_xdrs
));
398 mem_free (rpc_buffer (xprt
), su
->su_iosz
);
399 mem_free ((caddr_t
) su
, sizeof (struct svcudp_data
));
400 mem_free ((caddr_t
) xprt
, sizeof (SVCXPRT
));
404 /***********this could be a separate file*********************/
407 * Fifo cache for udp server
408 * Copies pointers to reply buffers into fifo cache
409 * Buffers are sent again if retransmissions are detected.
412 #define SPARSENESS 4 /* 75% sparse */
415 # define CACHE_PERROR(msg) \
416 if (_IO_fwide (stderr, 0) > 0) \
417 (void) __fwprintf(stderr, L"%s\n", msg); \
419 (void) fprintf(stderr, "%s\n", msg)
421 # define CACHE_PERROR(msg) \
422 (void) fprintf(stderr,"%s\n", msg)
425 #define ALLOC(type, size) \
426 (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
428 #define BZERO(addr, type, size) \
429 __bzero((char *) addr, sizeof(type) * (int) (size))
432 * An entry in the cache
434 typedef struct cache_node
*cache_ptr
;
438 * Index into cache is xid, proc, vers, prog and address
444 struct sockaddr_in cache_addr
;
446 * The cached reply and length
449 u_long cache_replylen
;
451 * Next node on the list, if there is a collision
453 cache_ptr cache_next
;
463 u_long uc_size
; /* size of cache */
464 cache_ptr
*uc_entries
; /* hash table of entries in cache */
465 cache_ptr
*uc_fifo
; /* fifo list of entries in cache */
466 u_long uc_nextvictim
; /* points to next victim in fifo list */
467 u_long uc_prog
; /* saved program number */
468 u_long uc_vers
; /* saved version number */
469 u_long uc_proc
; /* saved procedure number */
470 struct sockaddr_in uc_addr
; /* saved caller's address */
475 * the hashing function
477 #define CACHE_LOC(transp, xid) \
478 (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
482 * Enable use of the cache.
483 * Note: there is no disable.
486 svcudp_enablecache (SVCXPRT
*transp
, u_long size
)
488 struct svcudp_data
*su
= su_data (transp
);
489 struct udp_cache
*uc
;
491 if (su
->su_cache
!= NULL
)
493 CACHE_PERROR (_("enablecache: cache already enabled"));
496 uc
= ALLOC (struct udp_cache
, 1);
499 CACHE_PERROR (_("enablecache: could not allocate cache"));
503 uc
->uc_nextvictim
= 0;
504 uc
->uc_entries
= ALLOC (cache_ptr
, size
* SPARSENESS
);
505 if (uc
->uc_entries
== NULL
)
507 CACHE_PERROR (_("enablecache: could not allocate cache data"));
510 BZERO (uc
->uc_entries
, cache_ptr
, size
* SPARSENESS
);
511 uc
->uc_fifo
= ALLOC (cache_ptr
, size
);
512 if (uc
->uc_fifo
== NULL
)
514 CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
517 BZERO (uc
->uc_fifo
, cache_ptr
, size
);
518 su
->su_cache
= (char *) uc
;
524 * Set an entry in the cache
527 cache_set (SVCXPRT
*xprt
, u_long replylen
)
531 struct svcudp_data
*su
= su_data (xprt
);
532 struct udp_cache
*uc
= (struct udp_cache
*) su
->su_cache
;
537 * Find space for the new entry, either by
538 * reusing an old entry, or by mallocing a new one
540 victim
= uc
->uc_fifo
[uc
->uc_nextvictim
];
543 loc
= CACHE_LOC (xprt
, victim
->cache_xid
);
544 for (vicp
= &uc
->uc_entries
[loc
];
545 *vicp
!= NULL
&& *vicp
!= victim
;
546 vicp
= &(*vicp
)->cache_next
)
550 CACHE_PERROR (_("cache_set: victim not found"));
553 *vicp
= victim
->cache_next
; /* remote from cache */
554 newbuf
= victim
->cache_reply
;
558 victim
= ALLOC (struct cache_node
, 1);
561 CACHE_PERROR (_("cache_set: victim alloc failed"));
564 newbuf
= mem_alloc (su
->su_iosz
);
567 CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer"));
575 victim
->cache_replylen
= replylen
;
576 victim
->cache_reply
= rpc_buffer (xprt
);
577 rpc_buffer (xprt
) = newbuf
;
578 INTUSE(xdrmem_create
) (&(su
->su_xdrs
), rpc_buffer (xprt
), su
->su_iosz
,
580 victim
->cache_xid
= su
->su_xid
;
581 victim
->cache_proc
= uc
->uc_proc
;
582 victim
->cache_vers
= uc
->uc_vers
;
583 victim
->cache_prog
= uc
->uc_prog
;
584 victim
->cache_addr
= uc
->uc_addr
;
585 loc
= CACHE_LOC (xprt
, victim
->cache_xid
);
586 victim
->cache_next
= uc
->uc_entries
[loc
];
587 uc
->uc_entries
[loc
] = victim
;
588 uc
->uc_fifo
[uc
->uc_nextvictim
++] = victim
;
589 uc
->uc_nextvictim
%= uc
->uc_size
;
593 * Try to get an entry from the cache
594 * return 1 if found, 0 if not found
597 cache_get (xprt
, msg
, replyp
, replylenp
)
605 struct svcudp_data
*su
= su_data (xprt
);
606 struct udp_cache
*uc
= (struct udp_cache
*) su
->su_cache
;
608 #define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
610 loc
= CACHE_LOC (xprt
, su
->su_xid
);
611 for (ent
= uc
->uc_entries
[loc
]; ent
!= NULL
; ent
= ent
->cache_next
)
613 if (ent
->cache_xid
== su
->su_xid
&&
614 ent
->cache_proc
== uc
->uc_proc
&&
615 ent
->cache_vers
== uc
->uc_vers
&&
616 ent
->cache_prog
== uc
->uc_prog
&&
617 EQADDR (ent
->cache_addr
, uc
->uc_addr
))
619 *replyp
= ent
->cache_reply
;
620 *replylenp
= ent
->cache_replylen
;
625 * Failed to find entry
626 * Remember a few things so we can do a set later
628 uc
->uc_proc
= msg
->rm_call
.cb_proc
;
629 uc
->uc_vers
= msg
->rm_call
.cb_vers
;
630 uc
->uc_prog
= msg
->rm_call
.cb_prog
;
631 memcpy (&uc
->uc_addr
, &xprt
->xp_raddr
, sizeof (uc
->uc_addr
));