Home | History | Annotate | Download | only in usr.sbin
      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 2005 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <stdio.h>
     30 #include <stdlib.h>
     31 #include <unistd.h>
     32 #include <string.h>
     33 #include <locale.h>
     34 #include <sys/utsname.h>
     35 #include <sys/systeminfo.h>
     36 #include <netdb.h>
     37 #include <sys/types.h>
     38 #include <sys/param.h>
     39 #include <sys/errno.h>
     40 #include <sys/file.h>
     41 #include <sys/ioctl.h>
     42 #include <sys/signal.h>
     43 #include <sys/wait.h>
     44 #include <sys/time.h>
     45 #include <sys/socket.h>
     46 #include <sys/stropts.h>
     47 #include <sys/resource.h>
     48 #include <net/if.h>
     49 #include <net/if_arp.h>
     50 #include <sys/stream.h>
     51 #include <net/route.h>
     52 #include <netinet/in.h>
     53 #include <arpa/inet.h>
     54 #include <netinet/if_ether.h>
     55 #include <netinet/ip_var.h>
     56 #include <netinet/udp.h>
     57 #include <netinet/udp_var.h>
     58 #include <rpc/rpc.h>
     59 #include <rpcsvc/bootparam_prot.h>
     60 
     61 #define	MAXIFS	256
     62 
     63 /* command line flags */
     64 int		debug = 0;		/* do debug printfs */
     65 int		echo_host = 0;		/* just echo hostname, don't set it */
     66 int		verbose = 0;		/* do verbose printfs */
     67 int		safe = 0;		/* don't change anything */
     68 int		multiple = 0;		/* take multiple replies */
     69 
     70 static ulong_t	if_netmask;
     71 
     72 void		notsupported(), usage(), bp_whoami();
     73 int		get_ifdata();		/* get IP addr, subnet mask from IF */
     74 extern char	*inet_ntoa();
     75 extern int	getopt(), setdomainname();
     76 
     77 struct prototab {
     78 	char *name;
     79 	void (*func)();
     80 } prototab[] = {
     81 	{ "bootparams", bp_whoami },
     82 	{ "bootp", notsupported },
     83 	{ 0, 0 }
     84 };
     85 
     86 
     87 
     88 /*
     89  * usage: hostconfig [-p <protocol>] [-v] [-n] [-h] [<ifname>] [-f <hostname>]
     90  *
     91  * options:
     92  *	-d		Debug mode.
     93  * 	-v		Verbose mode.
     94  *	-n		Don't change anything.
     95  *	-h		Don't set hostname, just echo to standard out.
     96  *	-m		Wait for multiple answers (best used with the "-n"
     97  *			and "-v" flags).
     98  *	-f <hostname>	Fake mode - get bootparams for <hostname> (also
     99  *			best used with the "-n" and "-v" flags).
    100  *	<ifname>	Use IP address of <interface> in whoami request.
    101  *
    102  * If no interface name is specified, bp_whoami will cycle through the
    103  * interfaces, using the IP address of each in turn until an answer is
    104  * received.  Note that rpc_broadcast() broadcasts the RPC call on all
    105  * interfaces, so the <ifname> argument doesn't restrict the request
    106  * to that interface, it just uses that interface to determine the IP
    107  * address to put into the request.  If "-f <hostname>" is specified,
    108  * we put the IP address of <hostname> in the whoami request.  Otherwise,
    109  * we put the IP address of the interface in the whoami request.
    110  *
    111  */
    112 
    113 
    114 int
    115 main(argc, argv)
    116 	int argc;
    117 	char **argv;
    118 {
    119 	struct ifreq *reqbuf;
    120 	struct ifreq *ifr;
    121 	struct ifconf ifc;
    122 	struct in_addr targetaddr;
    123 	struct hostent *hp;
    124 	char *targethost = NULL;
    125 	char *cmdname;
    126 	int c;
    127 	int n;
    128 	struct prototab *ptp;
    129 	void (*protofunc)() = NULL;
    130 	int numifs;
    131 	unsigned bufsize;
    132 
    133 	extern char *optarg;
    134 	extern int optind;
    135 
    136 	cmdname = argv[0];
    137 
    138 	while ((c = getopt(argc, argv, "dhvnmf:p:")) != -1) {
    139 
    140 		switch ((char)c) {
    141 		case 'd':
    142 			debug++;
    143 			break;
    144 
    145 		case 'h':
    146 			echo_host++;
    147 			break;
    148 		case 'v':
    149 			verbose++;
    150 			break;
    151 
    152 		case 'm':
    153 			multiple++;
    154 			break;
    155 
    156 		case 'n':
    157 			safe++;
    158 			break;
    159 
    160 		case 'f':
    161 			targethost = optarg;
    162 			break;
    163 
    164 		case 'p':
    165 			protofunc = NULL;
    166 			for (ptp = &prototab[0]; ptp->func; ptp++)
    167 				if (strcmp(optarg, ptp->name) == 0) {
    168 					protofunc = ptp->func;
    169 					break;
    170 				}
    171 			if (protofunc == NULL)
    172 				usage(cmdname);
    173 			break;
    174 
    175 		case '?':
    176 			usage(cmdname);
    177 		}
    178 	}
    179 
    180 	if (protofunc == NULL)
    181 		usage(cmdname);
    182 
    183 	if (targethost) {
    184 		/* we are faking it */
    185 		if (debug)
    186 			fprintf(stdout, "targethost = %s\n", targethost);
    187 
    188 		if ((hp = gethostbyname(targethost)) == NULL) {
    189 			if ((targetaddr.s_addr = inet_addr(targethost)) ==
    190 			    (ulong_t)(-1)) {
    191 				(void) fprintf(stderr,
    192 					"%s: cannot get IP address for %s\n",
    193 					cmdname, targethost);
    194 				return (1);
    195 			}
    196 		} else {
    197 			if (hp->h_length != sizeof (targetaddr)) {
    198 				(void) fprintf(stderr,
    199 					"%s: cannot find host entry for %s\n",
    200 					cmdname, targethost);
    201 				return (1);
    202 			} else
    203 				(void) memcpy(&targetaddr.s_addr, hp->h_addr,
    204 				    sizeof (targetaddr));
    205 		}
    206 	} else
    207 		targetaddr.s_addr = 0;
    208 
    209 	if (optind < argc) {
    210 		/* interface names were specified */
    211 		for (; optind < argc; optind++) {
    212 			if (debug)
    213 				fprintf(stdout, "Trying arg %s\n",
    214 					argv[optind]);
    215 			(*protofunc)(argv[optind], targetaddr);
    216 		}
    217 	} else {
    218 		/* no interface names specified - try them all */
    219 		int ifcount = 0;	/* count of useable interfaces */
    220 		int s;
    221 
    222 		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    223 			perror("socket");
    224 			return (1);
    225 		}
    226 #ifdef SIOCGIFNUM
    227 		if (ioctl(s, SIOCGIFNUM, (char *)&numifs) < 0) {
    228 			numifs = MAXIFS;
    229 		}
    230 #else
    231 		numifs = MAXIFS;
    232 #endif
    233 		bufsize = numifs * sizeof (struct ifreq);
    234 		reqbuf = (struct ifreq *)malloc(bufsize);
    235 		if (reqbuf == NULL) {
    236 			fprintf(stderr, "out of memory\n");
    237 			return (1);
    238 		}
    239 		ifc.ifc_buf = (caddr_t)&reqbuf[0];
    240 		ifc.ifc_len = bufsize;
    241 		if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0) {
    242 			perror("ioctl(SIOCGIFCONF)");
    243 			return (1);
    244 		}
    245 		ifr = ifc.ifc_req;
    246 		n = ifc.ifc_len/sizeof (struct ifreq);
    247 		for (; n > 0; n--, ifr++) {
    248 			if (ioctl(s, SIOCGIFFLAGS, (char *)ifr) < 0) {
    249 				perror("ioctl(SIOCGIFFLAGS)");
    250 				return (1);
    251 			}
    252 			if ((ifr->ifr_flags & IFF_LOOPBACK) ||
    253 			    !(ifr->ifr_flags & IFF_BROADCAST) ||
    254 			    !(ifr->ifr_flags & IFF_UP) ||
    255 			    (ifr->ifr_flags & IFF_NOARP) ||
    256 			    (ifr->ifr_flags & IFF_POINTOPOINT)) {
    257 				if (debug)
    258 					fprintf(stdout, "If %s not suitable\n",
    259 						ifr->ifr_name);
    260 				continue;
    261 			} else {
    262 				if (debug)
    263 					fprintf(stdout, "Trying device %s\n",
    264 						ifr->ifr_name);
    265 				(*protofunc)(ifr->ifr_name,  targetaddr);
    266 				ifcount++;
    267 			}
    268 		}
    269 		if (verbose && ifcount == 0) {
    270 			fprintf(stderr, "No useable interfaces found.\n");
    271 			return (1);
    272 		}
    273 		(void) close(s);
    274 		(void) free((char *)reqbuf);
    275 	}
    276 	return (0);
    277 }
    278 
    279 
    280 void
    281 add_default_route(router_addr)
    282 	struct in_addr router_addr;
    283 {
    284 	struct rtentry route;
    285 	struct sockaddr_in *sin;
    286 	int s;
    287 
    288 	(void) memset(&route, 0, sizeof (route));
    289 
    290 	/* route destination is "default" - zero */
    291 	/* LINTED - alignment OK (32bit) */
    292 	sin = (struct sockaddr_in *)&route.rt_dst;
    293 	sin->sin_family = AF_INET;
    294 
    295 	/* LINTED - alignment OK (32bit) */
    296 	sin = (struct sockaddr_in *)&route.rt_gateway;
    297 	sin->sin_family = AF_INET;
    298 	sin->sin_addr.s_addr = router_addr.s_addr;
    299 
    300 	route.rt_flags = RTF_GATEWAY | RTF_UP;
    301 
    302 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    303 		perror("socket");
    304 		return;
    305 	}
    306 	if (ioctl(s, SIOCADDRT, (char *)&route) == -1) {
    307 		perror("add default route");
    308 		return;
    309 	}
    310 	(void) close(s);
    311 }
    312 
    313 
    314 int
    315 bpanswer(res, nb)
    316 	struct bp_whoami_res *res;
    317 	struct netbuf *nb;
    318 {
    319 	struct in_addr router_addr;
    320 	static int set;
    321 	int len;
    322 	char errbuf[MAX_MACHINE_NAME + 28];
    323 	/* MAX_MACHINE_NAME + strlen ("sysinfo(SI_SET_HOSTNAME)()") + null */
    324 
    325 	(void) memcpy(&router_addr, &res->router_address.bp_address_u.ip_addr,
    326 	    sizeof (router_addr));
    327 
    328 	if (verbose) {
    329 		struct sockaddr_in *addr;
    330 
    331 		if (nb) {
    332 			/* LINTED - alignment (32bit) OK */
    333 			addr = (struct sockaddr_in *)nb->buf;
    334 			fprintf(stdout, "From [%s]: ",
    335 			    inet_ntoa(addr->sin_addr));
    336 		} else {
    337 			fprintf(stdout, "Reply:\\t\\t");
    338 		}
    339 		fprintf(stdout, "hostname = %s\n", res->client_name);
    340 		fprintf(stdout, "\t\typdomain = %s\n", res->domain_name);
    341 		fprintf(stdout, "\t\trouter = %s\n", inet_ntoa(router_addr));
    342 	}
    343 
    344 	if (!safe && !set) {
    345 		/*
    346 		 * Stuff the values from the RPC reply into the kernel.
    347 		 * Only allow one pass through this code; There's no reason
    348 		 * why all replies should tweak the kernel.
    349 		 */
    350 		set++;
    351 
    352 		len = strlen(res->client_name);
    353 		if (len != 0) {
    354 			if (!echo_host) {
    355 				if (sysinfo(SI_SET_HOSTNAME, res->client_name,
    356 				    len) < 0) {
    357 					(void) snprintf(errbuf, sizeof (errbuf),
    358 					    "sysinfo(SI_SET_HOSTNAME)(%s)",
    359 					    res->client_name);
    360 					perror(errbuf);
    361 				}
    362 			} else
    363 				(void) fprintf(stdout, "%s\n",
    364 				    res->client_name);
    365 		}
    366 
    367 		len = strlen(res->domain_name);
    368 		if (len != 0) {
    369 			if (setdomainname(res->domain_name, len) == -1) {
    370 				(void) snprintf(errbuf, sizeof (errbuf),
    371 				    "setdomainname(%s)", res->domain_name);
    372 				perror(errbuf);
    373 			}
    374 		}
    375 
    376 		/* we really should validate this router value */
    377 		if (router_addr.s_addr != 0)
    378 			add_default_route(router_addr);
    379 	}
    380 
    381 	if (multiple)
    382 		return (NULL);
    383 
    384 	/* our job is done */
    385 	exit(0);
    386 	/* NOTREACHED */
    387 }
    388 
    389 void
    390 bp_whoami(device, addr)
    391 	char *device;
    392 	struct in_addr addr;
    393 {
    394 	struct bp_whoami_arg req;
    395 	struct bp_whoami_res res;
    396 	struct in_addr lookupaddr;
    397 	enum clnt_stat stat;
    398 	int val = 1;
    399 
    400 	if (debug)
    401 		fprintf(stdout, "bp_whoami on interface %s addr %s\n", device,
    402 		    inet_ntoa(addr));
    403 
    404 	if (addr.s_addr ==  0) {
    405 		if (get_ifdata(device, &lookupaddr, &if_netmask) == -1)
    406 			exit(1);
    407 	} else
    408 		(void) memcpy(&lookupaddr, &addr, sizeof (addr));
    409 
    410 	lookupaddr.s_addr = ntohl(lookupaddr.s_addr);
    411 
    412 	if (debug)
    413 		fprintf(stdout, "lookup address is %s\n",
    414 			inet_ntoa(lookupaddr));
    415 
    416 	(void) memset(&req, 0, sizeof (req));
    417 	(void) memset(&res, 0, sizeof (res));
    418 
    419 	req.client_address.address_type = IP_ADDR_TYPE;
    420 	(void) memcpy(&req.client_address.bp_address_u.ip_addr, &lookupaddr,
    421 	    sizeof (lookupaddr));
    422 
    423 	/*
    424 	 * Broadcast using portmap version number 2  ONLY to
    425 	 * prevent broadcast storm
    426 	 */
    427 
    428 	(void) __rpc_control(CLCR_SET_LOWVERS, &val);
    429 
    430 	stat = rpc_broadcast(BOOTPARAMPROG, BOOTPARAMVERS, BOOTPARAMPROC_WHOAMI,
    431 	    xdr_bp_whoami_arg, (caddr_t)&req, xdr_bp_whoami_res, (caddr_t)&res,
    432 	    (resultproc_t)bpanswer, "udp");
    433 
    434 	/* Now try version 3 as well */
    435 
    436 	val = 0;
    437 	(void) __rpc_control(CLCR_SET_LOWVERS, &val);
    438 
    439 	stat = rpc_broadcast(BOOTPARAMPROG, BOOTPARAMVERS,
    440 	    BOOTPARAMPROC_WHOAMI, xdr_bp_whoami_arg, (caddr_t)&req,
    441 	    xdr_bp_whoami_res, (caddr_t)&res, (resultproc_t)bpanswer, "udp");
    442 
    443 	if (stat != RPC_SUCCESS) {
    444 		clnt_perrno(stat);
    445 		exit(1);
    446 	}
    447 }
    448 
    449 
    450 /*
    451  * Get IP address of an interface.  As long as we are looking, get the
    452  * netmask as well.
    453  */
    454 int
    455 get_ifdata(dev, ipp, maskp)
    456 	char *dev;
    457 	ulong_t *ipp, *maskp;
    458 {
    459 	struct ifreq ifr;
    460 	/* LINTED - alignment OK (32bit) */
    461 	struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
    462 	int s;
    463 
    464 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    465 		perror("socket");
    466 		return (-1);
    467 	}
    468 
    469 	if (strlcpy(ifr.ifr_name, dev, sizeof (ifr.ifr_name)) >=
    470 		sizeof (ifr.ifr_name)) {
    471 			(void) fprintf(stderr, "Device name too long %s\n",
    472 			    dev);
    473 			return (-1);
    474 	}
    475 
    476 	if (ipp) {
    477 		if (ioctl(s, SIOCGIFADDR, (caddr_t)&ifr) < 0) {
    478 			perror("ioctl(SIOCGIFADDR)");
    479 			return (-1);
    480 		}
    481 		*ipp = ntohl(sin->sin_addr.s_addr);
    482 
    483 		if (debug)
    484 			(void) fprintf(stderr, "Interface '%s' address %s\n",
    485 			    dev, inet_ntoa(sin->sin_addr));
    486 	}
    487 
    488 	if (maskp) {
    489 		if (ioctl(s, SIOCGIFNETMASK, (caddr_t)&ifr) < 0) {
    490 			perror("SIOCGIFNETMASK");
    491 			return (-1);
    492 		}
    493 		*maskp = ntohl(sin->sin_addr.s_addr);
    494 
    495 		if (debug)
    496 			(void) fprintf(stderr,
    497 				"Interface '%s' subnet mask %s\n", dev,
    498 				inet_ntoa(sin->sin_addr));
    499 	}
    500 
    501 	(void) close(s);
    502 	return (0);
    503 }
    504 
    505 void
    506 notsupported()
    507 {
    508 	fprintf(stderr, "requested protocol is not supported\n");
    509 	exit(1);
    510 }
    511 
    512 void
    513 usage(cmdname)
    514 	char *cmdname;
    515 {
    516 	(void) fprintf(stderr, "usage: %s [-v] [-n] [-m] [-h] [<ifname>] "
    517 	    "[-f <hostname>] -p bootparams|bootp\n", cmdname);
    518 	(void) fflush(stderr);
    519 	exit(1);
    520 }
    521