1 /*
2 * CDDL HEADER START
3 *
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
7 * with the License.
8 *
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.
13 *
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]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29
30 /*
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
33 * All Rights Reserved
34 *
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
37 * contributors.
38 */
39
40 #pragma ident "%Z%%M% %I% %E% SMI"
41
42 /*
43 * svc_udp.c,
44 * Server side for UDP/IP based RPC. (Does some caching in the hopes of
45 * achieving execute-at-most-once semantics.)
46 */
47
48 #include <rpc/rpc.h>
49 #include <rpc/clnt_soc.h>
50 #include <sys/socket.h>
51 #include <errno.h>
52 #include <syslog.h>
53 #include <malloc.h>
54 #include <stdio.h>
55
56
57 #define rpc_buffer(xprt) ((xprt)->xp_p1)
58
59 static struct xp_ops *svcudp_ops();
60
61 extern int errno;
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);
74
75 static int cache_get(SVCXPRT *, struct rpc_msg *,
76 char **, uint_t *);
77 static void cache_set(SVCXPRT *, uint_t);
78
79 /*
80 * kept in xprt->xp_p2
81 */
82 struct svcudp_data {
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 */
88 };
89 #define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2))
90
91 /*
92 * Usage:
93 * xprt = svcudp_create(sock);
94 *
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.
103 */
104 SVCXPRT *
svcudp_bufcreate(sock,sendsz,recvsz)105 svcudp_bufcreate(sock, sendsz, recvsz)
106 register int sock;
107 u_int sendsz, recvsz;
108 {
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);
114
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);
120 }
121 madesock = TRUE;
122 }
123 memset((char *)&addr, 0, sizeof (addr));
124 addr.sin_family = AF_INET;
125 if (bindresvport(sock, &addr)) {
126 addr.sin_port = 0;
127 (void) _bind(sock, (struct sockaddr *)&addr, len);
128 }
129 if (_getsockname(sock, (struct sockaddr *)&addr, &len) != 0) {
130 (void) syslog(LOG_ERR, "svcudp_create -",
131 " cannot getsockname: %m");
132 if (madesock)
133 (void) close(sock);
134 return ((SVCXPRT *)NULL);
135 }
136 xprt = svc_xprt_alloc();
137 if (xprt == NULL) {
138 (void) syslog(LOG_ERR, "svcudp_create: out of memory");
139 if (madesock)
140 (void) close(sock);
141 return ((SVCXPRT *)NULL);
142 }
143 su = (struct svcudp_data *)mem_alloc(sizeof (*su));
144 if (su == NULL) {
145 (void) syslog(LOG_ERR, "svcudp_create: out of memory");
146 svc_xprt_free(xprt);
147 if (madesock)
148 (void) close(sock);
149 return ((SVCXPRT *)NULL);
150 }
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));
155 svc_xprt_free(xprt);
156 if (madesock)
157 (void) close(sock);
158 return ((SVCXPRT *)NULL);
159 }
160 xdrmem_create(
161 &(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_DECODE);
162 su->su_cache = NULL;
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];
170 xprt_register(xprt);
171 return (xprt);
172 }
173
174 SVCXPRT *
svcudp_create(sock)175 svcudp_create(sock)
176 int sock;
177 {
178
179 return (svcudp_bufcreate(sock, UDPMSGSIZE, UDPMSGSIZE));
180 }
181
182 static enum xprt_stat
svcudp_stat(xprt)183 svcudp_stat(xprt)
184 SVCXPRT *xprt;
185 {
186
187 return (XPRT_IDLE);
188 }
189
190 static bool_t
svcudp_recv(xprt,msg)191 svcudp_recv(xprt, msg)
192 register SVCXPRT *xprt;
193 struct rpc_msg *msg;
194 {
195 register struct svcudp_data *su = su_data(xprt);
196 register XDR *xdrs = &(su->su_xdrs);
197 register int rlen;
198 char *reply;
199 uint_t replylen;
200
201 again:
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)
206 goto again;
207 if (rlen < 4*sizeof (uint32_t))
208 return (FALSE);
209 xdrs->x_op = XDR_DECODE;
210 XDR_SETPOS(xdrs, 0);
211 if (! xdr_callmsg(xdrs, msg))
212 return (FALSE);
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,
218 xprt->xp_addrlen);
219 return (TRUE);
220 }
221 }
222 return (TRUE);
223 }
224
225 static bool_t
svcudp_reply(xprt,msg)226 svcudp_reply(xprt, msg)
227 register SVCXPRT *xprt;
228 struct rpc_msg *msg;
229 {
230 register struct svcudp_data *su = su_data(xprt);
231 register XDR *xdrs = &(su->su_xdrs);
232 register int slen;
233 register bool_t stat = FALSE;
234
235 xdrs->x_op = XDR_ENCODE;
236 XDR_SETPOS(xdrs, 0);
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)
242 == slen) {
243 stat = TRUE;
244 if (su->su_cache && slen >= 0) {
245 (void) cache_set(xprt, (uint_t) slen);
246 }
247 }
248 }
249 return (stat);
250 }
251
252 static bool_t
svcudp_getargs(xprt,xdr_args,args_ptr)253 svcudp_getargs(xprt, xdr_args, args_ptr)
254 SVCXPRT *xprt;
255 xdrproc_t xdr_args;
256 caddr_t args_ptr;
257 {
258
259 return ((*xdr_args)(&(su_data(xprt)->su_xdrs), args_ptr));
260 }
261
262 static bool_t
svcudp_freeargs(xprt,xdr_args,args_ptr)263 svcudp_freeargs(xprt, xdr_args, args_ptr)
264 SVCXPRT *xprt;
265 xdrproc_t xdr_args;
266 caddr_t args_ptr;
267 {
268 register XDR *xdrs = &(su_data(xprt)->su_xdrs);
269
270 xdrs->x_op = XDR_FREE;
271 return ((*xdr_args)(xdrs, args_ptr));
272 }
273
274 static void
svcudp_destroy(xprt)275 svcudp_destroy(xprt)
276 register SVCXPRT *xprt;
277 {
278 register struct svcudp_data *su = su_data(xprt);
279
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));
285 svc_xprt_free(xprt);
286 }
287
288
289 /* **********this could be a separate file********************* */
290
291 /*
292 * Fifo cache for udp server
293 * Copies pointers to reply buffers into fifo cache
294 * Buffers are sent again if retransmissions are detected.
295 */
296
297 #define SPARSENESS 4 /* 75% sparse */
298
299 #define ALLOC(type, size) \
300 (type *) mem_alloc((unsigned) (sizeof (type) * (size)))
301
302 #define BZERO(addr, type, size) \
303 memset((char *) (addr), 0, sizeof (type) * (int) (size))
304
305 #define FREE(addr, type, size) \
306 (void) mem_free((char *) (addr), (sizeof (type) * (size)))
307
308 /*
309 * An entry in the cache
310 */
311 typedef struct cache_node *cache_ptr;
312 struct cache_node {
313 /*
314 * Index into cache is xid, proc, vers, prog and address
315 */
316 uint32_t cache_xid;
317 uint32_t cache_proc;
318 uint32_t cache_vers;
319 uint32_t cache_prog;
320 struct sockaddr_in cache_addr;
321 /*
322 * The cached reply and length
323 */
324 char * cache_reply;
325 uint32_t cache_replylen;
326 /*
327 * Next node on the list, if there is a collision
328 */
329 cache_ptr cache_next;
330 };
331
332
333
334 /*
335 * The entire cache
336 */
337 struct udp_cache {
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 */
346 };
347
348
349 /*
350 * the hashing function
351 */
352 #define CACHE_LOC(transp, xid) \
353 (xid % (SPARSENESS*((struct udp_cache *) \
354 su_data(transp)->su_cache)->uc_size))
355
356
357 /*
358 * Enable use of the cache.
359 * Note: there is no disable.
360 */
361 int
svcudp_enablecache(transp,size)362 svcudp_enablecache(transp, size)
363 SVCXPRT *transp;
364 uint_t size;
365 {
366 struct svcudp_data *su = su_data(transp);
367 struct udp_cache *uc;
368
369 if (su->su_cache != NULL) {
370 (void) syslog(LOG_ERR, "enablecache: cache already enabled");
371 return (0);
372 }
373 uc = ALLOC(struct udp_cache, 1);
374 if (uc == NULL) {
375 (void) syslog(LOG_ERR, "enablecache: could not allocate cache");
376 return (0);
377 }
378 uc->uc_size = size;
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);
385 return (0);
386 }
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);
394 return (0);
395 }
396 BZERO(uc->uc_fifo, cache_ptr, size);
397 su->su_cache = (char *) uc;
398 return (1);
399 }
400
401
402 /*
403 * Set an entry in the cache
404 */
405 static void
cache_set(xprt,replylen)406 cache_set(xprt, replylen)
407 SVCXPRT *xprt;
408 uint_t replylen;
409 {
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;
414 u_int loc;
415 char *newbuf;
416
417 /*
418 * Find space for the new entry, either by
419 * reusing an old entry, or by mallocing a new one
420 */
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)
427 ;
428 if (*vicp == NULL) {
429 (void) syslog(LOG_ERR, "cache_set: victim not found");
430 return;
431 }
432 *vicp = victim->cache_next; /* remote from cache */
433 newbuf = victim->cache_reply;
434 } else {
435 victim = ALLOC(struct cache_node, 1);
436 if (victim == NULL) {
437 (void) syslog(LOG_ERR, "cache_set: victim alloc",
438 " failed");
439 return;
440 }
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);
446 return;
447 }
448 }
449
450 /*
451 * Store it away
452 */
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;
468 }
469
470 /*
471 * Try to get an entry from the cache
472 * return 1 if found, 0 if not found
473 */
474 static int
cache_get(xprt,msg,replyp,replylenp)475 cache_get(xprt, msg, replyp, replylenp)
476 SVCXPRT *xprt;
477 struct rpc_msg *msg;
478 char **replyp;
479 uint_t *replylenp;
480 {
481 u_int loc;
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;
485
486 #define EQADDR(a1, a2) \
487 (memcmp((char *)&a1, (char *)&a2, sizeof (a1)) == 0)
488
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;
498 return (1);
499 }
500 }
501 /*
502 * Failed to find entry
503 * Remember a few things so we can do a set later
504 */
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));
510 return (0);
511 }
512
513 static struct xp_ops *
svcudp_ops()514 svcudp_ops()
515 {
516 static struct xp_ops ops;
517
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;
525 }
526 return (&ops);
527 }
528