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 * 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 * 175 svcudp_create(sock) 176 int sock; 177 { 178 179 return (svcudp_bufcreate(sock, UDPMSGSIZE, UDPMSGSIZE)); 180 } 181 182 static enum xprt_stat 183 svcudp_stat(xprt) 184 SVCXPRT *xprt; 185 { 186 187 return (XPRT_IDLE); 188 } 189 190 static bool_t 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 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 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 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 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 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 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 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 * 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