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