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 /* 41 * svc_udp.c, 42 * Server side for UDP/IP based RPC. (Does some caching in the hopes of 43 * achieving execute-at-most-once semantics.) 44 */ 45 46 #include <rpc/rpc.h> 47 #include <rpc/clnt_soc.h> 48 #include <sys/socket.h> 49 #include <errno.h> 50 #include <syslog.h> 51 #include <malloc.h> 52 #include <stdio.h> 53 54 55 #define rpc_buffer(xprt) ((xprt)->xp_p1) 56 57 static struct xp_ops *svcudp_ops(); 58 59 extern int errno; 60 extern SVCXPRT *svc_xprt_alloc(); 61 extern void svc_xprt_free(); 62 extern int _socket(int, int, int); 63 extern int _bind(int, const struct sockaddr *, int); 64 extern int _getsockname(int, struct sockaddr *, int *); 65 extern int _listen(int, int); 66 extern int _accept(int, struct sockaddr *, int *); 67 extern int bindresvport(int, struct sockaddr_in *); 68 extern int _recvfrom(int, char *, int, int, 69 struct sockaddr *, int *); 70 extern int _sendto(int, const char *, int, int, 71 const struct sockaddr *, int); 72 73 static int cache_get(SVCXPRT *, struct rpc_msg *, 74 char **, uint_t *); 75 static void cache_set(SVCXPRT *, uint_t); 76 77 /* 78 * kept in xprt->xp_p2 79 */ 80 struct svcudp_data { 81 u_int su_iosz; /* byte size of send.recv buffer */ 82 uint32_t su_xid; /* transaction id */ 83 XDR su_xdrs; /* XDR handle */ 84 char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */ 85 char * su_cache; /* cached data, NULL if no cache */ 86 }; 87 #define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2)) 88 89 /* 90 * Usage: 91 * xprt = svcudp_create(sock); 92 * 93 * If sock<0 then a socket is created, else sock is used. 94 * If the socket, sock is not bound to a port then svcudp_create 95 * binds it to an arbitrary port. In any (successful) case, 96 * xprt->xp_sock is the registered socket number and xprt->xp_port is the 97 * associated port number. 98 * Once *xprt is initialized, it is registered as a transporter; 99 * see (svc.h, xprt_register). 100 * The routines returns NULL if a problem occurred. 101 */ 102 SVCXPRT * 103 svcudp_bufcreate(sock, sendsz, recvsz) 104 register int sock; 105 u_int sendsz, recvsz; 106 { 107 bool_t madesock = FALSE; 108 register SVCXPRT *xprt; 109 register struct svcudp_data *su; 110 struct sockaddr_in addr; 111 int len = sizeof (struct sockaddr_in); 112 113 if (sock == RPC_ANYSOCK) { 114 if ((sock = _socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { 115 (void) syslog(LOG_ERR, "svcudp_create: socket", 116 " creation problem: %m"); 117 return ((SVCXPRT *)NULL); 118 } 119 madesock = TRUE; 120 } 121 memset((char *)&addr, 0, sizeof (addr)); 122 addr.sin_family = AF_INET; 123 if (bindresvport(sock, &addr)) { 124 addr.sin_port = 0; 125 (void) _bind(sock, (struct sockaddr *)&addr, len); 126 } 127 if (_getsockname(sock, (struct sockaddr *)&addr, &len) != 0) { 128 (void) syslog(LOG_ERR, "svcudp_create -", 129 " cannot getsockname: %m"); 130 if (madesock) 131 (void) close(sock); 132 return ((SVCXPRT *)NULL); 133 } 134 xprt = svc_xprt_alloc(); 135 if (xprt == NULL) { 136 (void) syslog(LOG_ERR, "svcudp_create: out of memory"); 137 if (madesock) 138 (void) close(sock); 139 return ((SVCXPRT *)NULL); 140 } 141 su = (struct svcudp_data *)mem_alloc(sizeof (*su)); 142 if (su == NULL) { 143 (void) syslog(LOG_ERR, "svcudp_create: out of memory"); 144 svc_xprt_free(xprt); 145 if (madesock) 146 (void) close(sock); 147 return ((SVCXPRT *)NULL); 148 } 149 su->su_iosz = ((MAX(sendsz, recvsz) + 3) / 4) * 4; 150 if ((rpc_buffer(xprt) = (char *)mem_alloc(su->su_iosz)) == NULL) { 151 (void) syslog(LOG_ERR, "svcudp_create: out of memory"); 152 mem_free((char *) su, sizeof (*su)); 153 svc_xprt_free(xprt); 154 if (madesock) 155 (void) close(sock); 156 return ((SVCXPRT *)NULL); 157 } 158 xdrmem_create( 159 &(su->su_xdrs), rpc_buffer(xprt), su->su_iosz, XDR_DECODE); 160 su->su_cache = NULL; 161 xprt->xp_p2 = (caddr_t)su; 162 xprt->xp_netid = NULL; 163 xprt->xp_verf.oa_base = su->su_verfbody; 164 xprt->xp_ops = svcudp_ops(); 165 xprt->xp_port = ntohs(addr.sin_port); 166 xprt->xp_sock = sock; 167 xprt->xp_rtaddr.buf = &xprt->xp_raddr[0]; 168 xprt_register(xprt); 169 return (xprt); 170 } 171 172 SVCXPRT * 173 svcudp_create(sock) 174 int sock; 175 { 176 177 return (svcudp_bufcreate(sock, UDPMSGSIZE, UDPMSGSIZE)); 178 } 179 180 static enum xprt_stat 181 svcudp_stat(xprt) 182 SVCXPRT *xprt; 183 { 184 185 return (XPRT_IDLE); 186 } 187 188 static bool_t 189 svcudp_recv(xprt, msg) 190 register SVCXPRT *xprt; 191 struct rpc_msg *msg; 192 { 193 register struct svcudp_data *su = su_data(xprt); 194 register XDR *xdrs = &(su->su_xdrs); 195 register int rlen; 196 char *reply; 197 uint_t replylen; 198 199 again: 200 xprt->xp_addrlen = sizeof (struct sockaddr_in); 201 rlen = _recvfrom(xprt->xp_sock, rpc_buffer(xprt), (int) su->su_iosz, 202 0, (struct sockaddr *)&(xprt->xp_raddr), &(xprt->xp_addrlen)); 203 if (rlen == -1 && errno == EINTR) 204 goto again; 205 if (rlen < 4*sizeof (uint32_t)) 206 return (FALSE); 207 xdrs->x_op = XDR_DECODE; 208 XDR_SETPOS(xdrs, 0); 209 if (! xdr_callmsg(xdrs, msg)) 210 return (FALSE); 211 su->su_xid = msg->rm_xid; 212 if (su->su_cache != NULL) { 213 if (cache_get(xprt, msg, &reply, &replylen)) { 214 (void) _sendto(xprt->xp_sock, reply, (int) replylen, 0, 215 (struct sockaddr *) &xprt->xp_raddr, 216 xprt->xp_addrlen); 217 return (TRUE); 218 } 219 } 220 return (TRUE); 221 } 222 223 static bool_t 224 svcudp_reply(xprt, msg) 225 register SVCXPRT *xprt; 226 struct rpc_msg *msg; 227 { 228 register struct svcudp_data *su = su_data(xprt); 229 register XDR *xdrs = &(su->su_xdrs); 230 register int slen; 231 register bool_t stat = FALSE; 232 233 xdrs->x_op = XDR_ENCODE; 234 XDR_SETPOS(xdrs, 0); 235 msg->rm_xid = su->su_xid; 236 if (xdr_replymsg(xdrs, msg)) { 237 slen = (int)XDR_GETPOS(xdrs); 238 if (_sendto(xprt->xp_sock, rpc_buffer(xprt), slen, 0, 239 (struct sockaddr *)&(xprt->xp_raddr), xprt->xp_addrlen) 240 == slen) { 241 stat = TRUE; 242 if (su->su_cache && slen >= 0) { 243 (void) cache_set(xprt, (uint_t) slen); 244 } 245 } 246 } 247 return (stat); 248 } 249 250 static bool_t 251 svcudp_getargs(xprt, xdr_args, args_ptr) 252 SVCXPRT *xprt; 253 xdrproc_t xdr_args; 254 caddr_t args_ptr; 255 { 256 257 return ((*xdr_args)(&(su_data(xprt)->su_xdrs), args_ptr)); 258 } 259 260 static bool_t 261 svcudp_freeargs(xprt, xdr_args, args_ptr) 262 SVCXPRT *xprt; 263 xdrproc_t xdr_args; 264 caddr_t args_ptr; 265 { 266 register XDR *xdrs = &(su_data(xprt)->su_xdrs); 267 268 xdrs->x_op = XDR_FREE; 269 return ((*xdr_args)(xdrs, args_ptr)); 270 } 271 272 static void 273 svcudp_destroy(xprt) 274 register SVCXPRT *xprt; 275 { 276 register struct svcudp_data *su = su_data(xprt); 277 278 xprt_unregister(xprt); 279 (void) close(xprt->xp_sock); 280 XDR_DESTROY(&(su->su_xdrs)); 281 mem_free(rpc_buffer(xprt), su->su_iosz); 282 mem_free((caddr_t)su, sizeof (struct svcudp_data)); 283 svc_xprt_free(xprt); 284 } 285 286 287 /* **********this could be a separate file********************* */ 288 289 /* 290 * Fifo cache for udp server 291 * Copies pointers to reply buffers into fifo cache 292 * Buffers are sent again if retransmissions are detected. 293 */ 294 295 #define SPARSENESS 4 /* 75% sparse */ 296 297 #define ALLOC(type, size) \ 298 (type *) mem_alloc((unsigned) (sizeof (type) * (size))) 299 300 #define BZERO(addr, type, size) \ 301 memset((char *) (addr), 0, sizeof (type) * (int) (size)) 302 303 #define FREE(addr, type, size) \ 304 (void) mem_free((char *) (addr), (sizeof (type) * (size))) 305 306 /* 307 * An entry in the cache 308 */ 309 typedef struct cache_node *cache_ptr; 310 struct cache_node { 311 /* 312 * Index into cache is xid, proc, vers, prog and address 313 */ 314 uint32_t cache_xid; 315 uint32_t cache_proc; 316 uint32_t cache_vers; 317 uint32_t cache_prog; 318 struct sockaddr_in cache_addr; 319 /* 320 * The cached reply and length 321 */ 322 char * cache_reply; 323 uint32_t cache_replylen; 324 /* 325 * Next node on the list, if there is a collision 326 */ 327 cache_ptr cache_next; 328 }; 329 330 331 332 /* 333 * The entire cache 334 */ 335 struct udp_cache { 336 uint32_t uc_size; /* size of cache */ 337 cache_ptr *uc_entries; /* hash table of entries in cache */ 338 cache_ptr *uc_fifo; /* fifo list of entries in cache */ 339 uint32_t uc_nextvictim; /* points to next victim in fifo list */ 340 uint32_t uc_prog; /* saved program number */ 341 uint32_t uc_vers; /* saved version number */ 342 uint32_t uc_proc; /* saved procedure number */ 343 struct sockaddr_in uc_addr; /* saved caller's address */ 344 }; 345 346 347 /* 348 * the hashing function 349 */ 350 #define CACHE_LOC(transp, xid) \ 351 (xid % (SPARSENESS*((struct udp_cache *) \ 352 su_data(transp)->su_cache)->uc_size)) 353 354 355 /* 356 * Enable use of the cache. 357 * Note: there is no disable. 358 */ 359 int 360 svcudp_enablecache(transp, size) 361 SVCXPRT *transp; 362 uint_t size; 363 { 364 struct svcudp_data *su = su_data(transp); 365 struct udp_cache *uc; 366 367 if (su->su_cache != NULL) { 368 (void) syslog(LOG_ERR, "enablecache: cache already enabled"); 369 return (0); 370 } 371 uc = ALLOC(struct udp_cache, 1); 372 if (uc == NULL) { 373 (void) syslog(LOG_ERR, "enablecache: could not allocate cache"); 374 return (0); 375 } 376 uc->uc_size = size; 377 uc->uc_nextvictim = 0; 378 uc->uc_entries = ALLOC(cache_ptr, size * SPARSENESS); 379 if (uc->uc_entries == NULL) { 380 (void) syslog(LOG_ERR, "enablecache: could not", 381 " allocate cache data"); 382 FREE(uc, struct udp_cache, 1); 383 return (0); 384 } 385 BZERO(uc->uc_entries, cache_ptr, size * SPARSENESS); 386 uc->uc_fifo = ALLOC(cache_ptr, size); 387 if (uc->uc_fifo == NULL) { 388 (void) syslog(LOG_ERR, "enablecache: could not", 389 " allocate cache fifo"); 390 FREE((char *)uc->uc_entries, cache_ptr, size * SPARSENESS); 391 FREE((char *)uc, struct udp_cache, 1); 392 return (0); 393 } 394 BZERO(uc->uc_fifo, cache_ptr, size); 395 su->su_cache = (char *) uc; 396 return (1); 397 } 398 399 400 /* 401 * Set an entry in the cache 402 */ 403 static void 404 cache_set(xprt, replylen) 405 SVCXPRT *xprt; 406 uint_t replylen; 407 { 408 register cache_ptr victim; 409 register cache_ptr *vicp; 410 register struct svcudp_data *su = su_data(xprt); 411 struct udp_cache *uc = (struct udp_cache *) su->su_cache; 412 u_int loc; 413 char *newbuf; 414 415 /* 416 * Find space for the new entry, either by 417 * reusing an old entry, or by mallocing a new one 418 */ 419 victim = uc->uc_fifo[uc->uc_nextvictim]; 420 if (victim != NULL) { 421 loc = CACHE_LOC(xprt, victim->cache_xid); 422 for (vicp = &uc->uc_entries[loc]; 423 *vicp != NULL && *vicp != victim; 424 vicp = &(*vicp)->cache_next) 425 ; 426 if (*vicp == NULL) { 427 (void) syslog(LOG_ERR, "cache_set: victim not found"); 428 return; 429 } 430 *vicp = victim->cache_next; /* remote from cache */ 431 newbuf = victim->cache_reply; 432 } else { 433 victim = ALLOC(struct cache_node, 1); 434 if (victim == NULL) { 435 (void) syslog(LOG_ERR, "cache_set: victim alloc", 436 " failed"); 437 return; 438 } 439 newbuf = (char *)mem_alloc(su->su_iosz); 440 if (newbuf == NULL) { 441 (void) syslog(LOG_ERR, "cache_set: could not", 442 " allocate new rpc_buffer"); 443 FREE(victim, struct cache_node, 1); 444 return; 445 } 446 } 447 448 /* 449 * Store it away 450 */ 451 victim->cache_replylen = replylen; 452 victim->cache_reply = rpc_buffer(xprt); 453 rpc_buffer(xprt) = newbuf; 454 xdrmem_create(&(su->su_xdrs), rpc_buffer(xprt), 455 su->su_iosz, XDR_ENCODE); 456 victim->cache_xid = su->su_xid; 457 victim->cache_proc = uc->uc_proc; 458 victim->cache_vers = uc->uc_vers; 459 victim->cache_prog = uc->uc_prog; 460 victim->cache_addr = uc->uc_addr; 461 loc = CACHE_LOC(xprt, victim->cache_xid); 462 victim->cache_next = uc->uc_entries[loc]; 463 uc->uc_entries[loc] = victim; 464 uc->uc_fifo[uc->uc_nextvictim++] = victim; 465 uc->uc_nextvictim %= uc->uc_size; 466 } 467 468 /* 469 * Try to get an entry from the cache 470 * return 1 if found, 0 if not found 471 */ 472 static int 473 cache_get(xprt, msg, replyp, replylenp) 474 SVCXPRT *xprt; 475 struct rpc_msg *msg; 476 char **replyp; 477 uint_t *replylenp; 478 { 479 u_int loc; 480 register cache_ptr ent; 481 register struct svcudp_data *su = su_data(xprt); 482 register struct udp_cache *uc = (struct udp_cache *) su->su_cache; 483 484 #define EQADDR(a1, a2) \ 485 (memcmp((char *)&a1, (char *)&a2, sizeof (a1)) == 0) 486 487 loc = CACHE_LOC(xprt, su->su_xid); 488 for (ent = uc->uc_entries[loc]; ent != NULL; ent = ent->cache_next) { 489 if (ent->cache_xid == su->su_xid && 490 ent->cache_proc == uc->uc_proc && 491 ent->cache_vers == uc->uc_vers && 492 ent->cache_prog == uc->uc_prog && 493 EQADDR(ent->cache_addr, uc->uc_addr)) { 494 *replyp = ent->cache_reply; 495 *replylenp = ent->cache_replylen; 496 return (1); 497 } 498 } 499 /* 500 * Failed to find entry 501 * Remember a few things so we can do a set later 502 */ 503 uc->uc_proc = msg->rm_call.cb_proc; 504 uc->uc_vers = msg->rm_call.cb_vers; 505 uc->uc_prog = msg->rm_call.cb_prog; 506 memcpy((char *)&uc->uc_addr, (char *)&xprt->xp_raddr, 507 sizeof (struct sockaddr_in)); 508 return (0); 509 } 510 511 static struct xp_ops * 512 svcudp_ops() 513 { 514 static struct xp_ops ops; 515 516 if (ops.xp_recv == NULL) { 517 ops.xp_recv = svcudp_recv; 518 ops.xp_stat = svcudp_stat; 519 ops.xp_getargs = svcudp_getargs; 520 ops.xp_reply = svcudp_reply; 521 ops.xp_freeargs = svcudp_freeargs; 522 ops.xp_destroy = svcudp_destroy; 523 } 524 return (&ops); 525 } 526