4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
40 #pragma ident "%Z%%M% %I% %E% SMI"
44 * Server side for UDP/IP based RPC. (Does some caching in the hopes of
45 * achieving execute-at-most-once semantics.)
49 #include <rpc/clnt_soc.h>
50 #include <sys/socket.h>
57 #define rpc_buffer(xprt) ((xprt)->xp_p1)
59 static struct xp_ops
*svcudp_ops();
62 extern SVCXPRT
*svc_xprt_alloc();
63 extern void svc_xprt_free();
64 extern int _socket(int, int, int);
65 extern int _bind(int, const struct sockaddr
*, int);
66 extern int _getsockname(int, struct sockaddr
*, int *);
67 extern int _listen(int, int);
68 extern int _accept(int, struct sockaddr
*, int *);
69 extern int bindresvport(int, struct sockaddr_in
*);
70 extern int _recvfrom(int, char *, int, int,
71 struct sockaddr
*, int *);
72 extern int _sendto(int, const char *, int, int,
73 const struct sockaddr
*, int);
75 static int cache_get(SVCXPRT
*, struct rpc_msg
*,
77 static void cache_set(SVCXPRT
*, uint_t
);
83 u_int su_iosz
; /* byte size of send.recv buffer */
84 uint32_t su_xid
; /* transaction id */
85 XDR su_xdrs
; /* XDR handle */
86 char su_verfbody
[MAX_AUTH_BYTES
]; /* verifier body */
87 char * su_cache
; /* cached data, NULL if no cache */
89 #define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2))
93 * xprt = svcudp_create(sock);
95 * If sock<0 then a socket is created, else sock is used.
96 * If the socket, sock is not bound to a port then svcudp_create
97 * binds it to an arbitrary port. In any (successful) case,
98 * xprt->xp_sock is the registered socket number and xprt->xp_port is the
99 * associated port number.
100 * Once *xprt is initialized, it is registered as a transporter;
101 * see (svc.h, xprt_register).
102 * The routines returns NULL if a problem occurred.
105 svcudp_bufcreate(sock
, sendsz
, recvsz
)
107 u_int sendsz
, recvsz
;
109 bool_t madesock
= FALSE
;
110 register SVCXPRT
*xprt
;
111 register struct svcudp_data
*su
;
112 struct sockaddr_in addr
;
113 int len
= sizeof (struct sockaddr_in
);
115 if (sock
== RPC_ANYSOCK
) {
116 if ((sock
= _socket(AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
)) < 0) {
117 (void) syslog(LOG_ERR
, "svcudp_create: socket",
118 " creation problem: %m");
119 return ((SVCXPRT
*)NULL
);
123 memset((char *)&addr
, 0, sizeof (addr
));
124 addr
.sin_family
= AF_INET
;
125 if (bindresvport(sock
, &addr
)) {
127 (void) _bind(sock
, (struct sockaddr
*)&addr
, len
);
129 if (_getsockname(sock
, (struct sockaddr
*)&addr
, &len
) != 0) {
130 (void) syslog(LOG_ERR
, "svcudp_create -",
131 " cannot getsockname: %m");
134 return ((SVCXPRT
*)NULL
);
136 xprt
= svc_xprt_alloc();
138 (void) syslog(LOG_ERR
, "svcudp_create: out of memory");
141 return ((SVCXPRT
*)NULL
);
143 su
= (struct svcudp_data
*)mem_alloc(sizeof (*su
));
145 (void) syslog(LOG_ERR
, "svcudp_create: out of memory");
149 return ((SVCXPRT
*)NULL
);
151 su
->su_iosz
= ((MAX(sendsz
, recvsz
) + 3) / 4) * 4;
152 if ((rpc_buffer(xprt
) = (char *)mem_alloc(su
->su_iosz
)) == NULL
) {
153 (void) syslog(LOG_ERR
, "svcudp_create: out of memory");
154 mem_free((char *) su
, sizeof (*su
));
158 return ((SVCXPRT
*)NULL
);
161 &(su
->su_xdrs
), rpc_buffer(xprt
), su
->su_iosz
, XDR_DECODE
);
163 xprt
->xp_p2
= (caddr_t
)su
;
164 xprt
->xp_netid
= NULL
;
165 xprt
->xp_verf
.oa_base
= su
->su_verfbody
;
166 xprt
->xp_ops
= svcudp_ops();
167 xprt
->xp_port
= ntohs(addr
.sin_port
);
168 xprt
->xp_sock
= sock
;
169 xprt
->xp_rtaddr
.buf
= &xprt
->xp_raddr
[0];
179 return (svcudp_bufcreate(sock
, UDPMSGSIZE
, UDPMSGSIZE
));
182 static enum xprt_stat
191 svcudp_recv(xprt
, msg
)
192 register SVCXPRT
*xprt
;
195 register struct svcudp_data
*su
= su_data(xprt
);
196 register XDR
*xdrs
= &(su
->su_xdrs
);
202 xprt
->xp_addrlen
= sizeof (struct sockaddr_in
);
203 rlen
= _recvfrom(xprt
->xp_sock
, rpc_buffer(xprt
), (int) su
->su_iosz
,
204 0, (struct sockaddr
*)&(xprt
->xp_raddr
), &(xprt
->xp_addrlen
));
205 if (rlen
== -1 && errno
== EINTR
)
207 if (rlen
< 4*sizeof (uint32_t))
209 xdrs
->x_op
= XDR_DECODE
;
211 if (! xdr_callmsg(xdrs
, msg
))
213 su
->su_xid
= msg
->rm_xid
;
214 if (su
->su_cache
!= NULL
) {
215 if (cache_get(xprt
, msg
, &reply
, &replylen
)) {
216 (void) _sendto(xprt
->xp_sock
, reply
, (int) replylen
, 0,
217 (struct sockaddr
*) &xprt
->xp_raddr
,
226 svcudp_reply(xprt
, msg
)
227 register SVCXPRT
*xprt
;
230 register struct svcudp_data
*su
= su_data(xprt
);
231 register XDR
*xdrs
= &(su
->su_xdrs
);
233 register bool_t stat
= FALSE
;
235 xdrs
->x_op
= XDR_ENCODE
;
237 msg
->rm_xid
= su
->su_xid
;
238 if (xdr_replymsg(xdrs
, msg
)) {
239 slen
= (int)XDR_GETPOS(xdrs
);
240 if (_sendto(xprt
->xp_sock
, rpc_buffer(xprt
), slen
, 0,
241 (struct sockaddr
*)&(xprt
->xp_raddr
), xprt
->xp_addrlen
)
244 if (su
->su_cache
&& slen
>= 0) {
245 (void) cache_set(xprt
, (uint_t
) slen
);
253 svcudp_getargs(xprt
, xdr_args
, args_ptr
)
259 return ((*xdr_args
)(&(su_data(xprt
)->su_xdrs
), args_ptr
));
263 svcudp_freeargs(xprt
, xdr_args
, args_ptr
)
268 register XDR
*xdrs
= &(su_data(xprt
)->su_xdrs
);
270 xdrs
->x_op
= XDR_FREE
;
271 return ((*xdr_args
)(xdrs
, args_ptr
));
276 register SVCXPRT
*xprt
;
278 register struct svcudp_data
*su
= su_data(xprt
);
280 xprt_unregister(xprt
);
281 (void) close(xprt
->xp_sock
);
282 XDR_DESTROY(&(su
->su_xdrs
));
283 mem_free(rpc_buffer(xprt
), su
->su_iosz
);
284 mem_free((caddr_t
)su
, sizeof (struct svcudp_data
));
289 /* **********this could be a separate file********************* */
292 * Fifo cache for udp server
293 * Copies pointers to reply buffers into fifo cache
294 * Buffers are sent again if retransmissions are detected.
297 #define SPARSENESS 4 /* 75% sparse */
299 #define ALLOC(type, size) \
300 (type *) mem_alloc((unsigned) (sizeof (type) * (size)))
302 #define BZERO(addr, type, size) \
303 memset((char *) (addr), 0, sizeof (type) * (int) (size))
305 #define FREE(addr, type, size) \
306 (void) mem_free((char *) (addr), (sizeof (type) * (size)))
309 * An entry in the cache
311 typedef struct cache_node
*cache_ptr
;
314 * Index into cache is xid, proc, vers, prog and address
320 struct sockaddr_in cache_addr
;
322 * The cached reply and length
325 uint32_t cache_replylen
;
327 * Next node on the list, if there is a collision
329 cache_ptr cache_next
;
338 uint32_t uc_size
; /* size of cache */
339 cache_ptr
*uc_entries
; /* hash table of entries in cache */
340 cache_ptr
*uc_fifo
; /* fifo list of entries in cache */
341 uint32_t uc_nextvictim
; /* points to next victim in fifo list */
342 uint32_t uc_prog
; /* saved program number */
343 uint32_t uc_vers
; /* saved version number */
344 uint32_t uc_proc
; /* saved procedure number */
345 struct sockaddr_in uc_addr
; /* saved caller's address */
350 * the hashing function
352 #define CACHE_LOC(transp, xid) \
353 (xid % (SPARSENESS*((struct udp_cache *) \
354 su_data(transp)->su_cache)->uc_size))
358 * Enable use of the cache.
359 * Note: there is no disable.
362 svcudp_enablecache(transp
, size
)
366 struct svcudp_data
*su
= su_data(transp
);
367 struct udp_cache
*uc
;
369 if (su
->su_cache
!= NULL
) {
370 (void) syslog(LOG_ERR
, "enablecache: cache already enabled");
373 uc
= ALLOC(struct udp_cache
, 1);
375 (void) syslog(LOG_ERR
, "enablecache: could not allocate cache");
379 uc
->uc_nextvictim
= 0;
380 uc
->uc_entries
= ALLOC(cache_ptr
, size
* SPARSENESS
);
381 if (uc
->uc_entries
== NULL
) {
382 (void) syslog(LOG_ERR
, "enablecache: could not",
383 " allocate cache data");
384 FREE(uc
, struct udp_cache
, 1);
387 BZERO(uc
->uc_entries
, cache_ptr
, size
* SPARSENESS
);
388 uc
->uc_fifo
= ALLOC(cache_ptr
, size
);
389 if (uc
->uc_fifo
== NULL
) {
390 (void) syslog(LOG_ERR
, "enablecache: could not",
391 " allocate cache fifo");
392 FREE((char *)uc
->uc_entries
, cache_ptr
, size
* SPARSENESS
);
393 FREE((char *)uc
, struct udp_cache
, 1);
396 BZERO(uc
->uc_fifo
, cache_ptr
, size
);
397 su
->su_cache
= (char *) uc
;
403 * Set an entry in the cache
406 cache_set(xprt
, replylen
)
410 register cache_ptr victim
;
411 register cache_ptr
*vicp
;
412 register struct svcudp_data
*su
= su_data(xprt
);
413 struct udp_cache
*uc
= (struct udp_cache
*) su
->su_cache
;
418 * Find space for the new entry, either by
419 * reusing an old entry, or by mallocing a new one
421 victim
= uc
->uc_fifo
[uc
->uc_nextvictim
];
422 if (victim
!= NULL
) {
423 loc
= CACHE_LOC(xprt
, victim
->cache_xid
);
424 for (vicp
= &uc
->uc_entries
[loc
];
425 *vicp
!= NULL
&& *vicp
!= victim
;
426 vicp
= &(*vicp
)->cache_next
)
429 (void) syslog(LOG_ERR
, "cache_set: victim not found");
432 *vicp
= victim
->cache_next
; /* remote from cache */
433 newbuf
= victim
->cache_reply
;
435 victim
= ALLOC(struct cache_node
, 1);
436 if (victim
== NULL
) {
437 (void) syslog(LOG_ERR
, "cache_set: victim alloc",
441 newbuf
= (char *)mem_alloc(su
->su_iosz
);
442 if (newbuf
== NULL
) {
443 (void) syslog(LOG_ERR
, "cache_set: could not",
444 " allocate new rpc_buffer");
445 FREE(victim
, struct cache_node
, 1);
453 victim
->cache_replylen
= replylen
;
454 victim
->cache_reply
= rpc_buffer(xprt
);
455 rpc_buffer(xprt
) = newbuf
;
456 xdrmem_create(&(su
->su_xdrs
), rpc_buffer(xprt
),
457 su
->su_iosz
, XDR_ENCODE
);
458 victim
->cache_xid
= su
->su_xid
;
459 victim
->cache_proc
= uc
->uc_proc
;
460 victim
->cache_vers
= uc
->uc_vers
;
461 victim
->cache_prog
= uc
->uc_prog
;
462 victim
->cache_addr
= uc
->uc_addr
;
463 loc
= CACHE_LOC(xprt
, victim
->cache_xid
);
464 victim
->cache_next
= uc
->uc_entries
[loc
];
465 uc
->uc_entries
[loc
] = victim
;
466 uc
->uc_fifo
[uc
->uc_nextvictim
++] = victim
;
467 uc
->uc_nextvictim
%= uc
->uc_size
;
471 * Try to get an entry from the cache
472 * return 1 if found, 0 if not found
475 cache_get(xprt
, msg
, replyp
, replylenp
)
482 register cache_ptr ent
;
483 register struct svcudp_data
*su
= su_data(xprt
);
484 register struct udp_cache
*uc
= (struct udp_cache
*) su
->su_cache
;
486 #define EQADDR(a1, a2) \
487 (memcmp((char *)&a1, (char *)&a2, sizeof (a1)) == 0)
489 loc
= CACHE_LOC(xprt
, su
->su_xid
);
490 for (ent
= uc
->uc_entries
[loc
]; ent
!= NULL
; ent
= ent
->cache_next
) {
491 if (ent
->cache_xid
== su
->su_xid
&&
492 ent
->cache_proc
== uc
->uc_proc
&&
493 ent
->cache_vers
== uc
->uc_vers
&&
494 ent
->cache_prog
== uc
->uc_prog
&&
495 EQADDR(ent
->cache_addr
, uc
->uc_addr
)) {
496 *replyp
= ent
->cache_reply
;
497 *replylenp
= ent
->cache_replylen
;
502 * Failed to find entry
503 * Remember a few things so we can do a set later
505 uc
->uc_proc
= msg
->rm_call
.cb_proc
;
506 uc
->uc_vers
= msg
->rm_call
.cb_vers
;
507 uc
->uc_prog
= msg
->rm_call
.cb_prog
;
508 memcpy((char *)&uc
->uc_addr
, (char *)&xprt
->xp_raddr
,
509 sizeof (struct sockaddr_in
));
513 static struct xp_ops
*
516 static struct xp_ops ops
;
518 if (ops
.xp_recv
== NULL
) {
519 ops
.xp_recv
= svcudp_recv
;
520 ops
.xp_stat
= svcudp_stat
;
521 ops
.xp_getargs
= svcudp_getargs
;
522 ops
.xp_reply
= svcudp_reply
;
523 ops
.xp_freeargs
= svcudp_freeargs
;
524 ops
.xp_destroy
= svcudp_destroy
;