Home | History | Annotate | Download | only in librpcsoc
      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