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