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 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 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 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 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 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 506 notsupported() 507 { 508 fprintf(stderr, "requested protocol is not supported\n"); 509 exit(1); 510 } 511 512 void 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