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
)
152 (void) __fxprintf (NULL
, "%s: %s",
153 "svcudp_create", _("out of memory\n"));
154 mem_free (xprt
, sizeof (SVCXPRT
));
155 mem_free (su
, sizeof (*su
));
156 mem_free (buf
, ((MAX (sendsz
, recvsz
) + 3) / 4) * 4);
159 su
->su_iosz
= ((MAX (sendsz
, recvsz
) + 3) / 4) * 4;
160 rpc_buffer (xprt
) = buf
;
161 INTUSE(xdrmem_create
) (&(su
->su_xdrs
), rpc_buffer (xprt
), su
->su_iosz
,
164 xprt
->xp_p2
= (caddr_t
) su
;
165 xprt
->xp_verf
.oa_base
= su
->su_verfbody
;
166 xprt
->xp_ops
= &svcudp_op
;
167 xprt
->xp_port
= ntohs (addr
.sin_port
);
168 xprt
->xp_sock
= sock
;
171 if ((sizeof (struct iovec
) + sizeof (struct msghdr
)
172 + sizeof(struct cmsghdr
) + sizeof (struct in_pktinfo
))
173 > sizeof (xprt
->xp_pad
))
175 (void) __fxprintf (NULL
,"%s", _("\
176 svcudp_create: xp_pad is too small for IP_PKTINFO\n"));
180 if (__setsockopt (sock
, SOL_IP
, IP_PKTINFO
, (void *) &pad
,
182 /* Set the padding to all 1s. */
186 /* Clear the padding. */
188 memset (&xprt
->xp_pad
[0], pad
, sizeof (xprt
->xp_pad
));
190 xprt_register (xprt
);
193 INTDEF (svcudp_bufcreate
)
199 return INTUSE(svcudp_bufcreate
) (sock
, UDPMSGSIZE
, UDPMSGSIZE
);
201 INTDEF (svcudp_create
)
203 static enum xprt_stat
212 svcudp_recv (xprt
, msg
)
216 struct svcudp_data
*su
= su_data (xprt
);
217 XDR
*xdrs
= &(su
->su_xdrs
);
223 /* It is very tricky when you have IP aliases. We want to make sure
224 that we are sending the packet from the IP address where the
225 incoming packet is addressed to. H.J. */
228 struct msghdr
*mesgp
;
232 /* FIXME -- should xp_addrlen be a size_t? */
233 len
= (socklen_t
) sizeof(struct sockaddr_in
);
235 iovp
= (struct iovec
*) &xprt
->xp_pad
[0];
236 mesgp
= (struct msghdr
*) &xprt
->xp_pad
[sizeof (struct iovec
)];
237 if (mesgp
->msg_iovlen
)
239 iovp
->iov_base
= rpc_buffer (xprt
);
240 iovp
->iov_len
= su
->su_iosz
;
241 mesgp
->msg_iov
= iovp
;
242 mesgp
->msg_iovlen
= 1;
243 mesgp
->msg_name
= &(xprt
->xp_raddr
);
244 mesgp
->msg_namelen
= len
;
245 mesgp
->msg_control
= &xprt
->xp_pad
[sizeof (struct iovec
)
246 + sizeof (struct msghdr
)];
247 mesgp
->msg_controllen
= sizeof(xprt
->xp_pad
)
248 - sizeof (struct iovec
) - sizeof (struct msghdr
);
249 rlen
= __recvmsg (xprt
->xp_sock
, mesgp
, 0);
252 struct cmsghdr
*cmsg
;
253 len
= mesgp
->msg_namelen
;
254 cmsg
= CMSG_FIRSTHDR (mesgp
);
256 || CMSG_NXTHDR (mesgp
, cmsg
) != NULL
257 || cmsg
->cmsg_level
!= SOL_IP
258 || cmsg
->cmsg_type
!= IP_PKTINFO
259 || cmsg
->cmsg_len
< (sizeof (struct cmsghdr
)
260 + sizeof (struct in_pktinfo
)))
262 /* Not a simple IP_PKTINFO, ignore it. */
263 mesgp
->msg_control
= NULL
;
264 mesgp
->msg_controllen
= 0;
268 /* It was a simple IP_PKTIFO as we expected, discard the
270 struct in_pktinfo
*pkti
= (struct in_pktinfo
*) CMSG_DATA (cmsg
);
271 pkti
->ipi_ifindex
= 0;
277 rlen
= __recvfrom (xprt
->xp_sock
, rpc_buffer (xprt
),
278 (int) su
->su_iosz
, 0,
279 (struct sockaddr
*) &(xprt
->xp_raddr
), &len
);
280 xprt
->xp_addrlen
= len
;
281 if (rlen
== -1 && errno
== EINTR
)
283 if (rlen
< 16) /* < 4 32-bit ints? */
285 xdrs
->x_op
= XDR_DECODE
;
286 XDR_SETPOS (xdrs
, 0);
287 if (!INTUSE(xdr_callmsg
) (xdrs
, msg
))
289 su
->su_xid
= msg
->rm_xid
;
290 if (su
->su_cache
!= NULL
)
292 if (cache_get (xprt
, msg
, &reply
, &replylen
))
295 if (mesgp
->msg_iovlen
)
297 iovp
->iov_base
= reply
;
298 iovp
->iov_len
= replylen
;
299 (void) __sendmsg (xprt
->xp_sock
, mesgp
, 0);
303 (void) __sendto (xprt
->xp_sock
, reply
, (int) replylen
, 0,
304 (struct sockaddr
*) &xprt
->xp_raddr
, len
);
312 svcudp_reply (xprt
, msg
)
316 struct svcudp_data
*su
= su_data (xprt
);
317 XDR
*xdrs
= &(su
->su_xdrs
);
322 struct msghdr
*mesgp
;
325 xdrs
->x_op
= XDR_ENCODE
;
326 XDR_SETPOS (xdrs
, 0);
327 msg
->rm_xid
= su
->su_xid
;
328 if (INTUSE(xdr_replymsg
) (xdrs
, msg
))
330 slen
= (int) XDR_GETPOS (xdrs
);
332 mesgp
= (struct msghdr
*) &xprt
->xp_pad
[sizeof (struct iovec
)];
333 if (mesgp
->msg_iovlen
)
335 iovp
= (struct iovec
*) &xprt
->xp_pad
[0];
336 iovp
->iov_base
= rpc_buffer (xprt
);
337 iovp
->iov_len
= slen
;
338 sent
= __sendmsg (xprt
->xp_sock
, mesgp
, 0);
342 sent
= __sendto (xprt
->xp_sock
, rpc_buffer (xprt
), slen
, 0,
343 (struct sockaddr
*) &(xprt
->xp_raddr
),
348 if (su
->su_cache
&& slen
>= 0)
350 cache_set (xprt
, (u_long
) slen
);
358 svcudp_getargs (xprt
, xdr_args
, args_ptr
)
364 return (*xdr_args
) (&(su_data (xprt
)->su_xdrs
), args_ptr
);
368 svcudp_freeargs (xprt
, xdr_args
, args_ptr
)
373 XDR
*xdrs
= &(su_data (xprt
)->su_xdrs
);
375 xdrs
->x_op
= XDR_FREE
;
376 return (*xdr_args
) (xdrs
, args_ptr
);
380 svcudp_destroy (xprt
)
383 struct svcudp_data
*su
= su_data (xprt
);
385 xprt_unregister (xprt
);
386 (void) __close (xprt
->xp_sock
);
387 XDR_DESTROY (&(su
->su_xdrs
));
388 mem_free (rpc_buffer (xprt
), su
->su_iosz
);
389 mem_free ((caddr_t
) su
, sizeof (struct svcudp_data
));
390 mem_free ((caddr_t
) xprt
, sizeof (SVCXPRT
));
394 /***********this could be a separate file*********************/
397 * Fifo cache for udp server
398 * Copies pointers to reply buffers into fifo cache
399 * Buffers are sent again if retransmissions are detected.
402 #define SPARSENESS 4 /* 75% sparse */
404 #define CACHE_PERROR(msg) \
405 (void) __fxprintf(NULL, "%s\n", msg)
407 #define ALLOC(type, size) \
408 (type *) mem_alloc((unsigned) (sizeof(type) * (size)))
410 #define CALLOC(type, size) \
411 (type *) calloc (sizeof (type), size)
414 * An entry in the cache
416 typedef struct cache_node
*cache_ptr
;
420 * Index into cache is xid, proc, vers, prog and address
426 struct sockaddr_in cache_addr
;
428 * The cached reply and length
431 u_long cache_replylen
;
433 * Next node on the list, if there is a collision
435 cache_ptr cache_next
;
445 u_long uc_size
; /* size of cache */
446 cache_ptr
*uc_entries
; /* hash table of entries in cache */
447 cache_ptr
*uc_fifo
; /* fifo list of entries in cache */
448 u_long uc_nextvictim
; /* points to next victim in fifo list */
449 u_long uc_prog
; /* saved program number */
450 u_long uc_vers
; /* saved version number */
451 u_long uc_proc
; /* saved procedure number */
452 struct sockaddr_in uc_addr
; /* saved caller's address */
457 * the hashing function
459 #define CACHE_LOC(transp, xid) \
460 (xid % (SPARSENESS*((struct udp_cache *) su_data(transp)->su_cache)->uc_size))
464 * Enable use of the cache.
465 * Note: there is no disable.
468 svcudp_enablecache (SVCXPRT
*transp
, u_long size
)
470 struct svcudp_data
*su
= su_data (transp
);
471 struct udp_cache
*uc
;
473 if (su
->su_cache
!= NULL
)
475 CACHE_PERROR (_("enablecache: cache already enabled"));
478 uc
= ALLOC (struct udp_cache
, 1);
481 CACHE_PERROR (_("enablecache: could not allocate cache"));
485 uc
->uc_nextvictim
= 0;
486 uc
->uc_entries
= CALLOC (cache_ptr
, size
* SPARSENESS
);
487 if (uc
->uc_entries
== NULL
)
489 mem_free (uc
, sizeof (struct udp_cache
));
490 CACHE_PERROR (_("enablecache: could not allocate cache data"));
493 uc
->uc_fifo
= CALLOC (cache_ptr
, size
);
494 if (uc
->uc_fifo
== NULL
)
496 mem_free (uc
->uc_entries
, size
* SPARSENESS
);
497 mem_free (uc
, sizeof (struct udp_cache
));
498 CACHE_PERROR (_("enablecache: could not allocate cache fifo"));
501 su
->su_cache
= (char *) uc
;
507 * Set an entry in the cache
510 cache_set (SVCXPRT
*xprt
, u_long replylen
)
514 struct svcudp_data
*su
= su_data (xprt
);
515 struct udp_cache
*uc
= (struct udp_cache
*) su
->su_cache
;
520 * Find space for the new entry, either by
521 * reusing an old entry, or by mallocing a new one
523 victim
= uc
->uc_fifo
[uc
->uc_nextvictim
];
526 loc
= CACHE_LOC (xprt
, victim
->cache_xid
);
527 for (vicp
= &uc
->uc_entries
[loc
];
528 *vicp
!= NULL
&& *vicp
!= victim
;
529 vicp
= &(*vicp
)->cache_next
)
533 CACHE_PERROR (_("cache_set: victim not found"));
536 *vicp
= victim
->cache_next
; /* remote from cache */
537 newbuf
= victim
->cache_reply
;
541 victim
= ALLOC (struct cache_node
, 1);
544 CACHE_PERROR (_("cache_set: victim alloc failed"));
547 newbuf
= mem_alloc (su
->su_iosz
);
550 mem_free (victim
, sizeof (struct cache_node
));
551 CACHE_PERROR (_("cache_set: could not allocate new rpc_buffer"));
559 victim
->cache_replylen
= replylen
;
560 victim
->cache_reply
= rpc_buffer (xprt
);
561 rpc_buffer (xprt
) = newbuf
;
562 INTUSE(xdrmem_create
) (&(su
->su_xdrs
), rpc_buffer (xprt
), su
->su_iosz
,
564 victim
->cache_xid
= su
->su_xid
;
565 victim
->cache_proc
= uc
->uc_proc
;
566 victim
->cache_vers
= uc
->uc_vers
;
567 victim
->cache_prog
= uc
->uc_prog
;
568 victim
->cache_addr
= uc
->uc_addr
;
569 loc
= CACHE_LOC (xprt
, victim
->cache_xid
);
570 victim
->cache_next
= uc
->uc_entries
[loc
];
571 uc
->uc_entries
[loc
] = victim
;
572 uc
->uc_fifo
[uc
->uc_nextvictim
++] = victim
;
573 uc
->uc_nextvictim
%= uc
->uc_size
;
577 * Try to get an entry from the cache
578 * return 1 if found, 0 if not found
581 cache_get (xprt
, msg
, replyp
, replylenp
)
589 struct svcudp_data
*su
= su_data (xprt
);
590 struct udp_cache
*uc
= (struct udp_cache
*) su
->su_cache
;
592 #define EQADDR(a1, a2) (memcmp((char*)&a1, (char*)&a2, sizeof(a1)) == 0)
594 loc
= CACHE_LOC (xprt
, su
->su_xid
);
595 for (ent
= uc
->uc_entries
[loc
]; ent
!= NULL
; ent
= ent
->cache_next
)
597 if (ent
->cache_xid
== su
->su_xid
&&
598 ent
->cache_proc
== uc
->uc_proc
&&
599 ent
->cache_vers
== uc
->uc_vers
&&
600 ent
->cache_prog
== uc
->uc_prog
&&
601 EQADDR (ent
->cache_addr
, uc
->uc_addr
))
603 *replyp
= ent
->cache_reply
;
604 *replylenp
= ent
->cache_replylen
;
609 * Failed to find entry
610 * Remember a few things so we can do a set later
612 uc
->uc_proc
= msg
->rm_call
.cb_proc
;
613 uc
->uc_vers
= msg
->rm_call
.cb_vers
;
614 uc
->uc_prog
= msg
->rm_call
.cb_prog
;
615 memcpy (&uc
->uc_addr
, &xprt
->xp_raddr
, sizeof (uc
->uc_addr
));