xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/hostconfig.c (revision ed5289f91b9bf164dccd6c75398362be77a4478d)
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