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 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 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 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 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 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 502 notsupported() 503 { 504 fprintf(stderr, "requested protocol is not supported\n"); 505 exit(1); 506 } 507 508 void 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