xref: /titanic_50/usr/src/lib/librdc/common/netaddrs.c (revision 69b3e10436272b0970d58743de375d0dd61046ce)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <locale.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <memory.h>
30 #include <varargs.h>
31 #include <unistd.h>
32 #include <ctype.h>
33 #include <stdlib.h>
34 #include <signal.h>
35 #include <sys/param.h>
36 #include <rpc/rpc.h>
37 #include <errno.h>
38 #include <sys/stat.h>
39 #include <netdb.h>
40 #include <sys/pathconf.h>
41 #include <netdir.h>
42 #include <netconfig.h>
43 #include <sys/sockio.h>
44 #include <net/if.h>
45 #include <syslog.h>
46 #include <netinet/in.h>
47 #include <nfs/nfs_sec.h>
48 #include <strings.h>
49 #include <sys/nsctl/rdc_prot.h>
50 #include <nsctl.h>
51 
52 #include "librdc.h"
53 
54 #define	MAXIFS 32
55 
56 /* number of transports to try */
57 #define	MNT_PREF_LISTLEN	2
58 #define	FIRST_TRY		1
59 #define	SECOND_TRY		2
60 
61 
62 int
63 Is_ipv6present(void)
64 {
65 #ifdef AF_INET6
66 	int sock;
67 	struct lifnum lifn;
68 
69 	sock = socket(AF_INET6, SOCK_DGRAM, 0);
70 	if (sock < 0)
71 		return (0);
72 
73 	lifn.lifn_family = AF_INET6;
74 	lifn.lifn_flags = 0;
75 	if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) {
76 		close(sock);
77 		return (0);
78 	}
79 	close(sock);
80 	if (lifn.lifn_count == 0)
81 		return (0);
82 	return (1);
83 #else
84 	return (0);
85 #endif
86 }
87 
88 /*
89  * The following is stolen from autod_nfs.c
90  */
91 static void
92 getmyaddrs(struct ifconf *ifc)
93 {
94 	int sock;
95 	int numifs;
96 	char *buf;
97 	int family;
98 
99 	ifc->ifc_buf = NULL;
100 	ifc->ifc_len = 0;
101 
102 #ifdef AF_INET6
103 	family = AF_INET6;
104 #else
105 	family = AF_INET;
106 #endif
107 	if ((sock = socket(family, SOCK_DGRAM, 0)) < 0) {
108 #ifdef DEBUG
109 		perror("getmyaddrs(): socket");
110 #endif
111 		return;
112 	}
113 
114 	if (ioctl(sock, SIOCGIFNUM, (char *)&numifs) < 0) {
115 #ifdef DEBUG
116 		perror("getmyaddrs(): SIOCGIFNUM");
117 #endif
118 		numifs = MAXIFS;
119 	}
120 
121 	buf = (char *)malloc(numifs * sizeof (struct ifreq));
122 	if (buf == NULL) {
123 #ifdef DEBUG
124 		fprintf(stderr, "getmyaddrs(): malloc failed\n");
125 #endif
126 		(void) close(sock);
127 		return;
128 	}
129 
130 	ifc->ifc_buf = buf;
131 	ifc->ifc_len = numifs * sizeof (struct ifreq);
132 
133 	if (ioctl(sock, SIOCGIFCONF, (char *)ifc) < 0) {
134 #ifdef DEBUG
135 		perror("getmyaddrs(): SIOCGIFCONF");
136 #else
137 		;
138 		/*EMPTY*/
139 #endif
140 	}
141 
142 	(void) close(sock);
143 }
144 
145 int
146 self_check(char *hostname)
147 {
148 	int n;
149 	struct sockaddr_in *s1, *s2;
150 	struct ifreq *ifr;
151 	struct nd_hostserv hs;
152 	struct nd_addrlist *retaddrs;
153 	struct netconfig *nconfp;
154 	struct ifconf *ifc;
155 	int retval;
156 
157 	ifc = malloc(sizeof (struct ifconf));
158 	if (ifc == NULL)
159 		return (0);
160 	memset((char *)ifc, 0, sizeof (struct ifconf));
161 	getmyaddrs(ifc);
162 	/*
163 	 * Get the IP address for hostname
164 	 */
165 	nconfp = getnetconfigent("udp");
166 	if (nconfp == NULL) {
167 #ifdef DEBUG
168 		fprintf(stderr, "self_check(): getnetconfigent failed\n");
169 #endif
170 		retval = 0;
171 		goto out;
172 	}
173 	hs.h_host = hostname;
174 	hs.h_serv = "rpcbind";
175 	if (netdir_getbyname(nconfp, &hs, &retaddrs) != ND_OK) {
176 		freenetconfigent(nconfp);
177 		retval = 0;
178 		goto out;
179 	}
180 	freenetconfigent(nconfp);
181 	/* LINTED pointer alignment */
182 	s1 = (struct sockaddr_in *)retaddrs->n_addrs->buf;
183 
184 	/*
185 	 * Now compare it against the list of
186 	 * addresses for the interfaces on this
187 	 * host.
188 	 */
189 	ifr = ifc->ifc_req;
190 	n = ifc->ifc_len / sizeof (struct ifreq);
191 	s2 = NULL;
192 	for (; n > 0; n--, ifr++) {
193 		if (ifr->ifr_addr.sa_family != AF_INET)
194 			continue;
195 
196 		/* LINTED pointer alignment */
197 		s2 = (struct sockaddr_in *)&ifr->ifr_addr;
198 
199 		if (memcmp((char *)&s2->sin_addr,
200 			(char *)&s1->sin_addr, sizeof (s1->sin_addr)) == 0) {
201 			netdir_free((void *)retaddrs, ND_ADDRLIST);
202 			retval = 1;
203 			goto out;	/* it's me */
204 		}
205 	}
206 	netdir_free((void *)retaddrs, ND_ADDRLIST);
207 	retval = 0;
208 
209 out:
210 	if (ifc->ifc_buf != NULL)
211 		free(ifc->ifc_buf);
212 	free(ifc);
213 	return (retval);
214 }
215 
216 
217 int
218 convert_nconf_to_knconf(struct netconfig *nconf, struct knetconfig *knconf)
219 {
220 	struct stat sb;
221 
222 	if (stat(nconf->nc_device, &sb) < 0) {
223 		(void) syslog(LOG_ERR, "can't find device for transport %s\n",
224 				nconf->nc_device);
225 		return (-1);
226 	}
227 #ifdef DEBUG_ADDR
228 	printf("lib knconf %x %s %s %x\n", nconf->nc_semantics,
229 		nconf->nc_protofmly, nconf->nc_proto, sb.st_rdev);
230 #endif
231 
232 	knconf->knc_semantics = nconf->nc_semantics;
233 	knconf->knc_protofmly = nconf->nc_protofmly;
234 	knconf->knc_proto = nconf->nc_proto;
235 	knconf->knc_rdev = sb.st_rdev;
236 
237 	return (0);
238 }
239 
240 struct hostent *
241 gethost_byname(const char *name)
242 {
243 	int errnum;
244 #ifdef AF_INET6
245 	return (getipnodebyname(name, AF_INET6, AI_DEFAULT, &errnum));
246 #else /* !AF_INET6 */
247 	return (gethostbyname(name));
248 #endif /* AF_INET6 */
249 }
250 
251 int
252 gethost_netaddrs(char *fromhost, char *tohost,
253 	char *fromnetaddr, char *tonetaddr)
254 {
255 	struct hostent *host;
256 	int j;
257 	int errnum;
258 
259 #ifdef AF_INET6
260 	host = getipnodebyname(fromhost, AF_INET6, AI_DEFAULT, &errnum);
261 	if (host == NULL) {
262 #ifdef DEBUG
263 		(void) fprintf(stderr, dgettext("sndr",
264 		    "Could not find host %s"), fromhost);
265 #endif
266 		return (-1);
267 	}
268 	for (j = 0; j < host->h_length; j++)
269 		fromnetaddr[j] = host->h_addr[j];
270 	freehostent(host);
271 #else /* !AF_INET6 */
272 	host = gethostbyname(fromhost);
273 	if (host == NULL) {
274 #ifdef DEBUG
275 		(void) fprintf(stderr, dgettext("sndr",
276 		    "Could not find host %s"), fromhost);
277 #endif
278 		return (-1);
279 	}
280 
281 	if (host->h_length < 4) {
282 #ifdef DEBUG
283 		fprintf(stderr, "host->h_length(%d) < 4!\n", host->h_length);
284 #endif
285 		return (-1);
286 	}
287 
288 	for (j = 0; j < host->h_length; j++)
289 		fromnetaddr[j] = host->h_addr[j];
290 #endif /* AF_INET6 */
291 
292 #ifdef AF_INET6
293 	host = getipnodebyname(tohost, AF_INET6, AI_DEFAULT, &errnum);
294 	if (host == NULL) {
295 #ifdef DEBUG
296 		(void) fprintf(stderr, dgettext("sndr",
297 		    "Could not find host %s"), tohost);
298 #endif
299 		return (-1);
300 	}
301 	for (j = 0; j < host->h_length; j++)
302 		tonetaddr[j] = host->h_addr[j];
303 	freehostent(host);
304 #else /* !AF_INET6 */
305 	host = gethostbyname(tohost);
306 	if (host == NULL) {
307 #ifdef DEBUG
308 		(void) fprintf(stderr, dgettext("sndr",
309 		    "Could not find host %s"), tohost);
310 #endif
311 		return (-1);
312 	}
313 
314 	if (host->h_length < 4) {
315 #ifdef DEBUG
316 		fprintf(stderr, "host->h_length(%d) < 4!\n", host->h_length);
317 #endif
318 		return (-1);
319 	}
320 
321 	for (j = 0; j < host->h_length; j++)
322 		tonetaddr[j] = host->h_addr[j];
323 #endif /* AF_INET6 */
324 	return (0);
325 }
326 
327 /*
328  * Get the network address on "hostname" for program "prog"
329  * with version "vers" by using the nconf configuration data
330  * passed in.
331  *
332  * If the address of a netconfig pointer is null then
333  * information is not sufficient and no netbuf will be returned.
334  *
335  * Finally, ping the null procedure of that service.
336  *
337  */
338 static struct netbuf *
339 get_the_addr(char *hostname, ulong_t prog, ulong_t vers,
340 	struct netconfig *nconf, ushort_t port, struct t_info *tinfo,
341 	int portmap)
342 {
343 	struct netbuf *nb = NULL;
344 	struct t_bind *tbind = NULL;
345 	CLIENT *cl = NULL;
346 	struct timeval tv;
347 	int fd = -1;
348 	AUTH *ah = NULL;
349 
350 	if (nconf == NULL)
351 		return (NULL);
352 
353 	if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) == -1)
354 		    goto done;
355 
356 	/* LINTED pointer alignment */
357 	if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR)) == NULL)
358 		goto done;
359 
360 	if (portmap) { /* contact rpcbind */
361 		if (rpcb_getaddr(prog, vers, nconf, &tbind->addr,
362 		    hostname) == FALSE) {
363 			goto done;
364 		}
365 
366 		if (port) {
367 			if (strcmp(nconf->nc_protofmly, NC_INET) == 0)
368 			    /* LINTED pointer alignment */
369 			    ((struct sockaddr_in *)tbind->addr.buf)->sin_port
370 					= port;
371 #ifdef NC_INET6
372 			else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
373 			    /* LINTED pointer alignment */
374 			    ((struct sockaddr_in6 *)tbind->addr.buf)->sin6_port
375 					= port;
376 #endif
377 		}
378 
379 		/* Simon -- we never use the client we create?! */
380 		cl = clnt_tli_create(fd, nconf, &tbind->addr, prog, vers, 0, 0);
381 		if (cl == NULL)
382 			goto done;
383 
384 		ah = authsys_create_default();
385 		if (ah != NULL)
386 			cl->cl_auth = ah;
387 
388 		tv.tv_sec = 5;
389 		tv.tv_usec = 0;
390 
391 		(void) clnt_control(cl, CLSET_TIMEOUT, (char *)&tv);
392 	} else { /* create our own address and skip rpcbind */
393 		struct netbuf *nb;
394 		struct hostent *hp;
395 		int j;
396 		int errnum;
397 		unsigned short family;
398 		nb = &(tbind->addr);
399 
400 #ifdef AF_INET6
401 		if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
402 			hp = getipnodebyname(hostname, AF_INET6, 0, &errnum);
403 			family = AF_INET6;
404 			nb->len = nb->maxlen = sizeof (struct sockaddr_in6);
405 		} else {
406 			hp = getipnodebyname(hostname, AF_INET, 0, &errnum);
407 			family = AF_INET;
408 			nb->len = nb->maxlen = sizeof (struct sockaddr_in);
409 		}
410 		if (hp == NULL) {
411 #ifdef DEBUG_ADDR
412 				(void) fprintf(stderr, dgettext("sndr",
413 				    "Could not find host %s\n"), hostname);
414 #endif
415 				goto done;
416 		}
417 		nb->buf = (char *)calloc(1, nb->maxlen);
418 		if (nb->buf == NULL) {
419 			(void) printf(dgettext("sndr", "no memory\n"));
420 			goto done;
421 		}
422 
423 		if (family == AF_INET) {
424 			for (j = 0; j < hp->h_length; j++)
425 				nb->buf[j+4] = hp->h_addr[j];
426 			/* LINTED pointer alignment */
427 			((struct sockaddr_in *)(nb->buf))->sin_port = port;
428 			/* LINTED pointer alignment */
429 			((struct sockaddr_in *)(nb->buf))->sin_family = AF_INET;
430 		} else {
431 			for (j = 0; j < hp->h_length; j++)
432 				nb->buf[j+8] = hp->h_addr[j];
433 			/* LINTED pointer alignment */
434 			((struct sockaddr_in6 *)(nb->buf))->sin6_port = port;
435 			/* LINTED pointer alignment */
436 			((struct sockaddr_in6 *)(nb->buf))->sin6_family =
437 			    AF_INET6;
438 		}
439 		freehostent(hp);
440 #else
441 		hp = gethostbyname(hostname);
442 		if (hp == NULL) {
443 #ifdef DEBUG
444 			(void) fprintf(stderr, dgettext("sndr",
445 			    "Could not find host %s"), hostname);
446 #endif
447 			goto done;
448 		}
449 
450 		nb->len = nb->maxlen = sizeof (struct sockaddr_in);
451 		nb->buf = (char *)calloc(1, nb->maxlen);
452 		if (nb->buf == NULL) {
453 			(void) printf(dgettext("sndr", "no memory\n"));
454 			free(nb);
455 			nb = NULL;
456 			goto done;
457 		}
458 
459 		for (j = 0; j < hp->h_length; j++)
460 			nb->buf[j+4] = hp->h_addr[j];
461 
462 		if (hp->h_addrtype == AF_INET) {
463 			((struct sockaddr_in *)(nb->buf))->sin_port = port;
464 			((struct sockaddr_in *)(nb->buf))->sin_family = AF_INET;
465 		}
466 #endif
467 	}
468 
469 	/*
470 	 * Make a copy of the netbuf to return
471 	 */
472 	nb = (struct netbuf *)calloc(1, sizeof (*nb));
473 	if (nb == NULL) {
474 		(void) printf(dgettext("sndr", "no memory\n"));
475 		goto done;
476 	}
477 
478 	*nb = tbind->addr;	/* structure copy */
479 
480 	nb->buf = (char *)calloc(1, nb->maxlen);
481 	if (nb->buf == NULL) {
482 		(void) printf(dgettext("sndr", "no memory\n"));
483 		free(nb);
484 		nb = NULL;
485 		goto done;
486 	}
487 
488 	(void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len);
489 
490 done:
491 	if (cl) {
492 		if (ah != NULL) {
493 		    AUTH_DESTROY(cl->cl_auth);
494 		    cl->cl_auth = NULL;
495 		}
496 
497 		clnt_destroy(cl);
498 		cl = NULL;
499 	}
500 
501 	if (tbind) {
502 		t_free((char *)tbind, T_BIND);
503 		tbind = NULL;
504 	}
505 
506 	if (fd >= 0)
507 		(void) t_close(fd);
508 	return (nb);
509 }
510 
511 /*
512  * Get a network address on "hostname" for program "prog"
513  * with version "vers".  If the port number is specified (non zero)
514  * then try for a TCP/UDP transport and set the port number of the
515  * resulting IP address.
516  *
517  * If the address of a netconfig pointer was passed and
518  * if it's not null, use it as the netconfig otherwise
519  * assign the address of the netconfig that was used to
520  * establish contact with the service.
521  * If portmap is false, we return a similiar address and we do not
522  * contact rpcbind
523  *
524  */
525 struct netbuf *
526 get_addr(char *hostname, ulong_t prog, ulong_t vers, struct netconfig **nconfp,
527 	char *proto, char *srvport, struct t_info *tinfo, int portmap)
528 {
529 	struct netbuf *nb = NULL;
530 	struct netconfig *nconf = NULL;
531 	NCONF_HANDLE *nc = NULL;
532 	int nthtry = FIRST_TRY;
533 	struct servent *svp;
534 	ushort_t port;
535 
536 	/*
537 	 * First lets get the requested port
538 	 */
539 
540 	if ((svp = getservbyname(srvport, proto)) == NULL)
541 		goto done;
542 	port = svp->s_port;
543 	/*
544 	 * No nconf passed in.
545 	 *
546 	 * Try to get a nconf from /etc/netconfig filtered by
547 	 * the NETPATH environment variable.
548 	 * First search for COTS, second for CLTS unless proto
549 	 * is specified.  When we retry, we reset the
550 	 * netconfig list so that we would search the whole list
551 	 * all over again.
552 	 */
553 	if ((nc = setnetpath()) == NULL)
554 		goto done;
555 
556 	/*
557 	 * If proto is specified, then only search for the match,
558 	 * otherwise try COTS first, if failed, try CLTS.
559 	 */
560 	if (proto) {
561 		while (nconf = getnetpath(nc)) {
562 			if (strcmp(nconf->nc_netid, proto) == 0) {
563 				/*
564 				 * If the port number is specified then TCP/UDP
565 				 * is needed. Otherwise any cots/clts will do.
566 				 */
567 				if (port == 0)
568 					break;
569 
570 				if ((strcmp(nconf->nc_protofmly, NC_INET) == 0
571 #ifdef NC_INET6
572 				/* CSTYLED */
573 				|| strcmp(nconf->nc_protofmly, NC_INET6) == 0
574 #endif
575 				/* CSTYLED */
576 				) &&
577 				(strcmp(nconf->nc_proto, NC_TCP) == 0 ||
578 				strcmp(nconf->nc_proto, NC_UDP) == 0))
579 					break;
580 				else {
581 					nconf = NULL;
582 					break;
583 				}
584 			}
585 		}
586 		if (nconf == NULL)
587 			goto done;
588 		if ((nb = get_the_addr(hostname, prog, vers, nconf, port,
589 				tinfo, portmap)) == NULL) {
590 			goto done;
591 		}
592 	} else {
593 retry:
594 		while (nconf = getnetpath(nc)) {
595 			if (nconf->nc_flag & NC_VISIBLE) {
596 			    if (nthtry == FIRST_TRY) {
597 				if ((nconf->nc_semantics == NC_TPI_COTS_ORD) ||
598 					(nconf->nc_semantics == NC_TPI_COTS)) {
599 				    if (port == 0)
600 					break;
601 				    if ((strcmp(nconf->nc_protofmly,
602 					NC_INET) == 0
603 #ifdef NC_INET6
604 					/* CSTYLED */
605 					|| strcmp(nconf->nc_protofmly,
606 					NC_INET6) == 0
607 #endif
608 					/* CSTYLED */
609 					) &&
610 					(strcmp(nconf->nc_proto, NC_TCP) == 0))
611 					break;
612 				}
613 			    }
614 			}
615 		} /* while */
616 		if (nconf == NULL) {
617 			if (++nthtry <= MNT_PREF_LISTLEN) {
618 				endnetpath(nc);
619 				if ((nc = setnetpath()) == NULL)
620 					goto done;
621 				goto retry;
622 			} else
623 				goto done;
624 		} else {
625 			if ((nb = get_the_addr(hostname, prog, vers, nconf,
626 			    port, tinfo, portmap)) == NULL) {
627 				/*
628 				 * Continue the same search path in the
629 				 * netconfig db until no more matched
630 				 * nconf (nconf == NULL).
631 				 */
632 				goto retry;
633 			}
634 #ifdef AF_INET6
635 			if ((nb->len == 8) &&
636 			    (strcmp(nconf->nc_protofmly, NC_INET6) == 0)) {
637 				/*
638 				 * We have a mismatch in the netconfig retry
639 				 */
640 				free(nb);
641 				goto retry;
642 			}
643 #endif
644 		}
645 	}
646 
647 	/*
648 	 * Got nconf and nb.  Now dup the netconfig structure (nconf)
649 	 * and return it thru nconfp.
650 	 */
651 	*nconfp = getnetconfigent(nconf->nc_netid);
652 	if (*nconfp == NULL) {
653 		syslog(LOG_ERR, "no memory\n");
654 		free(nb);
655 		nb = NULL;
656 	}
657 done:
658 	if (nc)
659 		endnetpath(nc);
660 	return (nb);
661 }
662 
663 
664 /* return values as for nsc_check_release() */
665 int
666 rdc_check_release(char **reqd)
667 {
668 	/* librdc.so must be built on the runtime OS release */
669 	return (nsc_check_release(BUILD_REV_STR, NULL, reqd));
670 }
671