xref: /titanic_51/usr/src/uts/common/rpc/rpc_subr.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
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 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 /* SVr4.0 1.1 */
33 
34 /*
35  * Miscellaneous support routines for kernel implementation of RPC.
36  */
37 
38 #include <sys/param.h>
39 #include <sys/t_lock.h>
40 #include <sys/user.h>
41 #include <sys/vnode.h>
42 #include <sys/stream.h>
43 #include <sys/stropts.h>
44 #include <sys/strsubr.h>
45 #include <sys/socket.h>
46 #include <sys/tihdr.h>
47 #include <sys/timod.h>
48 #include <sys/tiuser.h>
49 #include <sys/systm.h>
50 #include <sys/cmn_err.h>
51 #include <sys/debug.h>
52 #include <netinet/in.h>
53 #include <rpc/types.h>
54 #include <rpc/auth.h>
55 #include <rpc/clnt.h>
56 #include <rpc/rpcb_prot.h>
57 #include <rpc/pmap_prot.h>
58 
59 static int strtoi(char *, char **);
60 static void grow_netbuf(struct netbuf *, size_t);
61 static void loopb_u2t(const char *, struct netbuf *);
62 
63 #define	RPC_PMAP_TIMEOUT	15
64 
65 /*
66  * Kernel level debugging aid. The global variable "rpclog" is a bit
67  * mask which allows various types of debugging messages to be printed
68  * out.
69  *
70  *	rpclog & 1 	will cause actual failures to be printed.
71  *	rpclog & 2	will cause informational messages to be
72  *			printed on the client side of rpc.
73  *	rpclog & 4	will cause informational messages to be
74  *			printed on the server side of rpc.
75  *	rpclog & 8	will cause informational messages for rare events to be
76  *			printed on the client side of rpc.
77  *	rpclog & 16	will cause informational messages for rare events to be
78  *			printed on the server side of rpc.
79  *	rpclog & 32	will cause informational messages for rare events to be
80  *			printed on the common client/server code paths of rpc.
81  *	rpclog & 64	will cause informational messages for manipulation
82  *			client-side COTS dispatch list to be printed.
83  */
84 
85 uint_t rpclog = 0;
86 
87 
88 void
89 rpc_poptimod(vnode_t *vp)
90 {
91 	int error, isfound, ret;
92 
93 	error = strioctl(vp, I_FIND, (intptr_t)"timod", 0, K_TO_K, kcred,
94 	    &isfound);
95 	if (error) {
96 		RPCLOG(1, "rpc_poptimod: I_FIND strioctl error %d\n", error);
97 		return;
98 	}
99 	if (isfound) {
100 		/*
101 		 * Pop timod module
102 		 */
103 		error = strioctl(vp, I_POP, 0, 0, K_TO_K, kcred, &ret);
104 		if (error) {
105 			RPCLOG(1, "rpc_poptimod: I_POP strioctl error %d\n",
106 			    error);
107 			return;
108 		}
109 	}
110 }
111 
112 /*
113  * Return a port number from a sockaddr_in expressed in universal address
114  * format.  Note that this routine does not work for address families other
115  * than INET.  Eventually, we should replace this routine with one that
116  * contacts the rpcbind running locally.
117  */
118 int
119 rpc_uaddr2port(int af, char *addr)
120 {
121 	int p1;
122 	int p2;
123 	char *next, *p;
124 
125 	if (af == AF_INET) {
126 		/*
127 		 * A struct sockaddr_in expressed in universal address
128 		 * format looks like:
129 		 *
130 		 *	"IP.IP.IP.IP.PORT[top byte].PORT[bottom byte]"
131 		 *
132 		 * Where each component expresses as a character,
133 		 * the corresponding part of the IP address
134 		 * and port number.
135 		 * Thus 127.0.0.1, port 2345 looks like:
136 		 *
137 		 *	49 50 55 46 48 46 48 46 49 46 57 46 52 49
138 		 *	1  2  7  .  0  .  0  .  1  .  9  .  4  1
139 		 *
140 		 * 2345 = 929base16 = 9.32+9 = 9.41
141 		 */
142 		(void) strtoi(addr, &next);
143 		(void) strtoi(next, &next);
144 		(void) strtoi(next, &next);
145 		(void) strtoi(next, &next);
146 		p1 = strtoi(next, &next);
147 		p2 = strtoi(next, &next);
148 
149 	} else if (af == AF_INET6) {
150 		/*
151 		 * An IPv6 address is expressed in following two formats
152 		 * fec0:A02::2:202:4FCD or
153 		 * ::10.9.2.1
154 		 * An universal address will have porthi.portlo appended to
155 		 * v6 address. So always look for the last two dots when
156 		 * extracting port number.
157 		 */
158 		next = addr;
159 		while (next = strchr(next, '.')) {
160 			p = ++next;
161 			next = strchr(next, '.');
162 			next++;
163 		}
164 		p1 = strtoi(p, &p);
165 		p2 = strtoi(p, &p);
166 		RPCLOG(1, "rpc_uaddr2port: IPv6 port %d\n", ((p1 << 8) + p2));
167 	}
168 
169 	return ((p1 << 8) + p2);
170 }
171 
172 /*
173  * Modified strtol(3).  Should we be using mi_strtol() instead?
174  */
175 static int
176 strtoi(char *str, char **ptr)
177 {
178 	int c;
179 	int val;
180 
181 	for (val = 0, c = *str++; c >= '0' && c <= '9'; c = *str++) {
182 		val *= 10;
183 		val += c - '0';
184 	}
185 	*ptr = str;
186 	return (val);
187 }
188 
189 /*
190  * Utilities for manipulating netbuf's.
191  *
192  * Note that loopback addresses are not null-terminated, so these utilities
193  * typically use the strn* string routines.
194  */
195 
196 /*
197  * Utilities to patch a port number (for NC_INET protocols) or a
198  *	port name (for NC_LOOPBACK) into a network address.
199  */
200 
201 
202 /*
203  * PSARC 2003/523 Contract Private Interface
204  * put_inet_port
205  * Changes must be reviewed by Solaris File Sharing
206  * Changes must be communicated to contract-2003-523@sun.com
207  */
208 void
209 put_inet_port(struct netbuf *addr, ushort_t port)
210 {
211 	/*
212 	 * Easy - we always patch an unsigned short on top of an
213 	 * unsigned short.  No changes to addr's len or maxlen are
214 	 * necessary.
215 	 */
216 	((struct sockaddr_in *)(addr->buf))->sin_port = port;
217 }
218 
219 void
220 put_inet6_port(struct netbuf *addr, ushort_t port)
221 {
222 	((struct sockaddr_in6 *)(addr->buf))->sin6_port = port;
223 }
224 
225 void
226 put_loopback_port(struct netbuf *addr, char *port)
227 {
228 	char *dot;
229 	char *newbuf;
230 	int newlen;
231 
232 
233 	/*
234 	 * We must make sure the addr has enough space for us,
235 	 * patch in `port', and then adjust addr's len and maxlen
236 	 * to reflect the change.
237 	 */
238 	if ((dot = strnrchr(addr->buf, '.', addr->len)) == (char *)NULL)
239 		return;
240 
241 	newlen = (int)((dot - addr->buf + 1) + strlen(port));
242 	if (newlen > addr->maxlen) {
243 		newbuf = kmem_zalloc(newlen, KM_SLEEP);
244 		bcopy(addr->buf, newbuf, addr->len);
245 		kmem_free(addr->buf, addr->maxlen);
246 		addr->buf = newbuf;
247 		addr->len = addr->maxlen = newlen;
248 		dot = strnrchr(addr->buf, '.', addr->len);
249 	} else {
250 		addr->len = newlen;
251 	}
252 
253 	(void) strncpy(++dot, port, strlen(port));
254 }
255 
256 /*
257  * Convert a loopback universal address to a loopback transport address.
258  */
259 static void
260 loopb_u2t(const char *ua, struct netbuf *addr)
261 {
262 	size_t stringlen = strlen(ua) + 1;
263 	const char *univp;		/* ptr into universal addr */
264 	char *transp;			/* ptr into transport addr */
265 
266 	/* Make sure the netbuf will be big enough. */
267 	if (addr->maxlen < stringlen) {
268 		grow_netbuf(addr, stringlen);
269 	}
270 
271 	univp = ua;
272 	transp = addr->buf;
273 	while (*univp != NULL) {
274 		if (*univp == '\\' && *(univp+1) == '\\') {
275 			*transp = '\\';
276 			univp += 2;
277 		} else if (*univp == '\\') {
278 			/* octal character */
279 			*transp = (((*(univp+1) - '0') & 3) << 6) +
280 			    (((*(univp+2) - '0') & 7) << 3) +
281 			    ((*(univp+3) - '0') & 7);
282 			univp += 4;
283 		} else {
284 			*transp = *univp;
285 			univp++;
286 		}
287 		transp++;
288 	}
289 
290 	addr->len = (unsigned int)(transp - addr->buf);
291 	ASSERT(addr->len <= addr->maxlen);
292 }
293 
294 /*
295  * Make sure the given netbuf has a maxlen at least as big as the given
296  * length.
297  */
298 static void
299 grow_netbuf(struct netbuf *nb, size_t length)
300 {
301 	char *newbuf;
302 
303 	if (nb->maxlen >= length)
304 		return;
305 
306 	newbuf = kmem_zalloc(length, KM_SLEEP);
307 	bcopy(nb->buf, newbuf, nb->len);
308 	kmem_free(nb->buf, nb->maxlen);
309 	nb->buf = newbuf;
310 	nb->maxlen = (unsigned int)length;
311 }
312 
313 
314 /*
315  * Try to get the address for the desired service by using the rpcbind
316  * protocol.  Ignores signals.  If addr is a loopback address, it is
317  * expected to be initialized to "<hostname>.".
318  */
319 
320 enum clnt_stat
321 rpcbind_getaddr(struct knetconfig *config, rpcprog_t prog, rpcvers_t vers,
322     struct netbuf *addr)
323 {
324 	char *ua = NULL;
325 	enum clnt_stat status;
326 	RPCB parms;
327 	struct timeval tmo;
328 	CLIENT *client = NULL;
329 	k_sigset_t oldmask;
330 	k_sigset_t newmask;
331 	ushort_t port;
332 
333 	/*
334 	 * Call rpcbind (local or remote) to get an address we can use
335 	 * in an RPC client handle.
336 	 */
337 	tmo.tv_sec = RPC_PMAP_TIMEOUT;
338 	tmo.tv_usec = 0;
339 	parms.r_prog = prog;
340 	parms.r_vers = vers;
341 	parms.r_addr = parms.r_owner = "";
342 
343 	if (strcmp(config->knc_protofmly, NC_INET) == 0) {
344 		if (strcmp(config->knc_proto, NC_TCP) == 0)
345 			parms.r_netid = "tcp";
346 		else
347 			parms.r_netid = "udp";
348 		put_inet_port(addr, htons(PMAPPORT));
349 	} else if (strcmp(config->knc_protofmly, NC_INET6) == 0) {
350 		if (strcmp(config->knc_proto, NC_TCP) == 0)
351 			parms.r_netid = "tcp6";
352 		else
353 			parms.r_netid = "udp6";
354 		put_inet6_port(addr, htons(PMAPPORT));
355 	} else if (strcmp(config->knc_protofmly, NC_LOOPBACK) == 0) {
356 		ASSERT(strnrchr(addr->buf, '.', addr->len) != NULL);
357 		if (config->knc_semantics == NC_TPI_COTS_ORD)
358 			parms.r_netid = "ticotsord";
359 		else if (config->knc_semantics == NC_TPI_COTS)
360 			parms.r_netid = "ticots";
361 		else
362 			parms.r_netid = "ticlts";
363 
364 		put_loopback_port(addr, "rpc");
365 	} else {
366 		status = RPC_UNKNOWNPROTO;
367 		goto out;
368 	}
369 
370 	/*
371 	 * Mask signals for the duration of the handle creation and
372 	 * RPC calls.  This allows relatively normal operation with a
373 	 * signal already posted to our thread (e.g., when we are
374 	 * sending an NLM_CANCEL in response to catching a signal).
375 	 *
376 	 * Any further exit paths from this routine must restore
377 	 * the original signal mask.
378 	 */
379 	sigfillset(&newmask);
380 	sigreplace(&newmask, &oldmask);
381 
382 	if (clnt_tli_kcreate(config, addr, RPCBPROG,
383 	    RPCBVERS, 0, 0, CRED(), &client)) {
384 		status = RPC_TLIERROR;
385 		sigreplace(&oldmask, (k_sigset_t *)NULL);
386 		goto out;
387 	}
388 
389 	client->cl_nosignal = 1;
390 	if ((status = CLNT_CALL(client, RPCBPROC_GETADDR,
391 	    xdr_rpcb, (char *)&parms,
392 	    xdr_wrapstring, (char *)&ua,
393 	    tmo)) != RPC_SUCCESS) {
394 		sigreplace(&oldmask, (k_sigset_t *)NULL);
395 		goto out;
396 	}
397 
398 	sigreplace(&oldmask, (k_sigset_t *)NULL);
399 
400 	if (ua == NULL || *ua == NULL) {
401 		status = RPC_PROGNOTREGISTERED;
402 		goto out;
403 	}
404 
405 	/*
406 	 * Convert the universal address to the transport address.
407 	 * Theoretically, we should call the local rpcbind to translate
408 	 * from the universal address to the transport address, but it gets
409 	 * complicated (e.g., there's no direct way to tell rpcbind that we
410 	 * want an IP address instead of a loopback address).  Note that
411 	 * the transport address is potentially host-specific, so we can't
412 	 * just ask the remote rpcbind, because it might give us the wrong
413 	 * answer.
414 	 */
415 	if (strcmp(config->knc_protofmly, NC_INET) == 0) {
416 		port = rpc_uaddr2port(AF_INET, ua);
417 		put_inet_port(addr, ntohs(port));
418 	} else if (strcmp(config->knc_protofmly, NC_INET6) == 0) {
419 		port = rpc_uaddr2port(AF_INET6, ua);
420 		put_inet6_port(addr, ntohs(port));
421 	} else if (strcmp(config->knc_protofmly, NC_LOOPBACK) == 0) {
422 		loopb_u2t(ua, addr);
423 	} else {
424 		/* "can't happen" - should have been checked for above */
425 		cmn_err(CE_PANIC, "rpcbind_getaddr: bad protocol family");
426 	}
427 
428 out:
429 	if (client != NULL) {
430 		auth_destroy(client->cl_auth);
431 		clnt_destroy(client);
432 	}
433 	if (ua != NULL)
434 		xdr_free(xdr_wrapstring, (char *)&ua);
435 	return (status);
436 }
437 
438 static const char *tpiprims[] = {
439 	"T_CONN_REQ      0        connection request",
440 	"T_CONN_RES      1        connection response",
441 	"T_DISCON_REQ    2        disconnect request",
442 	"T_DATA_REQ      3        data request",
443 	"T_EXDATA_REQ    4        expedited data request",
444 	"T_INFO_REQ      5        information request",
445 	"T_BIND_REQ      6        bind request",
446 	"T_UNBIND_REQ    7        unbind request",
447 	"T_UNITDATA_REQ  8        unitdata request",
448 	"T_OPTMGMT_REQ   9        manage options req",
449 	"T_ORDREL_REQ    10       orderly release req",
450 	"T_CONN_IND      11       connection indication",
451 	"T_CONN_CON      12       connection confirmation",
452 	"T_DISCON_IND    13       disconnect indication",
453 	"T_DATA_IND      14       data indication",
454 	"T_EXDATA_IND    15       expeditied data indication",
455 	"T_INFO_ACK      16       information acknowledgment",
456 	"T_BIND_ACK      17       bind acknowledment",
457 	"T_ERROR_ACK     18       error acknowledgment",
458 	"T_OK_ACK        19       ok acknowledgment",
459 	"T_UNITDATA_IND  20       unitdata indication",
460 	"T_UDERROR_IND   21       unitdata error indication",
461 	"T_OPTMGMT_ACK   22       manage options ack",
462 	"T_ORDREL_IND    23       orderly release ind"
463 };
464 
465 
466 const char *
467 rpc_tpiprim2name(uint_t prim)
468 {
469 	if (prim > (sizeof (tpiprims) / sizeof (tpiprims[0]) - 1))
470 		return ("unknown primitive");
471 
472 	return (tpiprims[prim]);
473 }
474 
475 static const char *tpierrs[] = {
476 	"error zero      0",
477 	"TBADADDR        1        incorrect addr format",
478 	"TBADOPT         2        incorrect option format",
479 	"TACCES          3        incorrect permissions",
480 	"TBADF           4        illegal transport fd",
481 	"TNOADDR         5        couldn't allocate addr",
482 	"TOUTSTATE       6        out of state",
483 	"TBADSEQ         7        bad call sequnce number",
484 	"TSYSERR         8        system error",
485 	"TLOOK           9        event requires attention",
486 	"TBADDATA        10       illegal amount of data",
487 	"TBUFOVFLW       11       buffer not large enough",
488 	"TFLOW           12       flow control",
489 	"TNODATA         13       no data",
490 	"TNODIS          14       discon_ind not found on q",
491 	"TNOUDERR        15       unitdata error not found",
492 	"TBADFLAG        16       bad flags",
493 	"TNOREL          17       no ord rel found on q",
494 	"TNOTSUPPORT     18       primitive not supported",
495 	"TSTATECHNG      19       state is in process of changing"
496 };
497 
498 
499 const char *
500 rpc_tpierr2name(uint_t err)
501 {
502 	if (err > (sizeof (tpierrs) / sizeof (tpierrs[0]) - 1))
503 		return ("unknown error");
504 
505 	return (tpierrs[err]);
506 }
507 
508 /*
509  * derive  the code from user land inet_top6
510  * convert IPv6 binary address into presentation (printable) format
511  */
512 #define	INADDRSZ	4
513 #define	IN6ADDRSZ	16
514 #define	INT16SZ	2
515 const char *
516 kinet_ntop6(src, dst, size)
517 	uchar_t *src;
518 	char *dst;
519 	size_t size;
520 {
521 	/*
522 	 * Note that int32_t and int16_t need only be "at least" large enough
523 	 * to contain a value of the specified size.  On some systems, like
524 	 * Crays, there is no such thing as an integer variable with 16 bits.
525 	 * Keep this in mind if you think this function should have been coded
526 	 * to use pointer overlays.  All the world's not a VAX.
527 	 */
528 	char tmp[sizeof ("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
529 	char *tp;
530 	struct { int base, len; } best, cur;
531 	uint_t words[IN6ADDRSZ / INT16SZ];
532 	int i;
533 	size_t len; /* this is used to track the sprintf len */
534 
535 	/*
536 	 * Preprocess:
537 	 * Copy the input (bytewise) array into a wordwise array.
538 	 * Find the longest run of 0x00's in src[] for :: shorthanding.
539 	 */
540 
541 	bzero(words, sizeof (words));
542 	for (i = 0; i < IN6ADDRSZ; i++)
543 		words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
544 	best.base = -1;
545 	cur.base = -1;
546 
547 	for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
548 		if (words[i] == 0) {
549 			if (cur.base == -1)
550 				cur.base = i, cur.len = 1;
551 			else
552 				cur.len++;
553 		} else {
554 			if (cur.base != -1) {
555 				if (best.base == -1 || cur.len > best.len)
556 					best = cur;
557 				cur.base = -1;
558 			}
559 		}
560 	}
561 	if (cur.base != -1) {
562 		if (best.base == -1 || cur.len > best.len)
563 			best = cur;
564 	}
565 
566 	if (best.base != -1 && best.len < 2)
567 		best.base = -1;
568 
569 	/*
570 	 * Format the result.
571 	 */
572 	tp = tmp;
573 	for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
574 		/* Are we inside the best run of 0x00's? */
575 		if (best.base != -1 && i >= best.base &&
576 		    i < (best.base + best.len)) {
577 			if (i == best.base)
578 				*tp++ = ':';
579 			continue;
580 		}
581 		/* Are we following an initial run of 0x00s or any real hex? */
582 		if (i != 0)
583 			*tp++ = ':';
584 		(void) sprintf(tp, "%x", words[i]);
585 		len = strlen(tp);
586 		tp += len;
587 	}
588 	/* Was it a trailing run of 0x00's? */
589 	if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ))
590 		*tp++ = ':';
591 	*tp++ = '\0';
592 
593 	/*
594 	 * Check for overflow, copy, and we're done.
595 	 */
596 	if ((int)(tp - tmp) > size) {
597 		return (NULL);
598 	}
599 	(void) strcpy(dst, tmp);
600 	return (dst);
601 }
602