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