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