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
main(argc,argv)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
add_default_route(router_addr)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
bpanswer(res,nb)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
bp_whoami(device,addr)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
get_ifdata(dev,ipp,maskp)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
notsupported()506 notsupported()
507 {
508 fprintf(stderr, "requested protocol is not supported\n");
509 exit(1);
510 }
511
512 void
usage(cmdname)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