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 2004 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 <sys/types.h> 30 #include <stdlib.h> 31 #include <dhcp_impl.h> 32 #include <netinet/inetutil.h> 33 #include <sys/systeminfo.h> 34 #include <netinet/in.h> 35 #include <strings.h> 36 #include <net/if.h> 37 #include <libdevinfo.h> 38 #include <sys/isa_defs.h> 39 #include <unistd.h> 40 #include <fcntl.h> 41 #include <netdb.h> 42 #include <alloca.h> 43 #include <stdio.h> 44 #include <sys/sockio.h> 45 #include <sys/statvfs.h> 46 #include <sys/utsname.h> 47 #include <bootinfo.h> 48 #include <bootinfo_aux.h> 49 50 #define MAXIFS 256 /* default max number of interfaces */ 51 52 /* 53 * Callback structure used when walking the device tree. 54 */ 55 typedef struct { 56 char *cb_path; /* device path we want to match */ 57 di_node_t cb_node; /* found leaf node of device path */ 58 } cb_t; 59 60 /* 61 * Handles on devinfo stuff. 62 */ 63 static di_node_t root_node = DI_NODE_NIL; 64 static di_prom_handle_t phdl = DI_PROM_HANDLE_NIL; 65 66 /* 67 * Root filesystem type string. 68 */ 69 static char *rootfs_type = NULL; 70 71 /* 72 * Handles on DHCP's packet list and interface-name. 73 */ 74 static PKT_LIST *dhcp_pl = NULL; 75 static char dhcp_ifn[IFNAMSIZ + 1]; 76 77 /* 78 * Deallocate dhcp_pl. 79 */ 80 static void 81 dhcp_info_end(void) 82 { 83 if (dhcp_pl != NULL) { 84 free(dhcp_pl->pkt); 85 free(dhcp_pl); 86 } 87 dhcp_pl = NULL; 88 dhcp_ifn[0] = '\0'; 89 } 90 91 /* 92 * Determine whether the kernel has a cached DHCP ACK, and if so 93 * initialize dhcp_pl and dhcp_ifn. 94 */ 95 static boolean_t 96 dhcp_info_init(void) 97 { 98 boolean_t ret = B_FALSE; 99 char dummy; 100 char *dhcack = NULL; 101 long dhcacksz; 102 char *ackp; 103 104 /* 105 * See whether the kernel has a cached DHCP ACK, and if so get it. 106 * If there is no DHCP ACK, then the returned length is equal to 107 * the size of an empty string. 108 */ 109 if ((dhcacksz = sysinfo(SI_DHCP_CACHE, &dummy, 110 sizeof (dummy))) == sizeof ("")) { 111 return (B_TRUE); 112 } 113 if ((dhcack = malloc(dhcacksz)) == NULL) { 114 goto cleanup; 115 } 116 if ((dhcp_pl = calloc(1, sizeof (PKT_LIST))) == NULL) { 117 goto cleanup; 118 } 119 (void) sysinfo(SI_DHCP_CACHE, (caddr_t)dhcack, dhcacksz); 120 121 /* 122 * The first IFNAMSIZ bytes are reserved for the interface name; 123 * the ACK follows. 124 */ 125 ackp = &dhcack[IFNAMSIZ]; 126 127 /* 128 * Convert and scan the options. 129 */ 130 dhcp_pl->len = strlen(ackp) / 2; 131 if ((dhcp_pl->pkt = malloc(dhcp_pl->len)) == NULL) { 132 goto cleanup; 133 } 134 if (hexascii_to_octet(ackp, dhcp_pl->len * 2, 135 dhcp_pl->pkt, &dhcp_pl->len) != 0) { 136 goto cleanup; 137 } 138 if (dhcp_options_scan(dhcp_pl, B_TRUE) != 0) { 139 goto cleanup; 140 } 141 142 /* 143 * Set the interface-name. 144 */ 145 (void) strlcpy(dhcp_ifn, dhcack, sizeof (dhcp_ifn)); 146 147 ret = B_TRUE; 148 cleanup: 149 if (!ret) { 150 dhcp_info_end(); 151 } 152 if (dhcack != NULL) { 153 free(dhcack); 154 } 155 156 return (ret); 157 } 158 159 /* 160 * Deallocate devinfo stuff. 161 */ 162 static void 163 destroy_snapshot(void) 164 { 165 if (phdl != DI_PROM_HANDLE_NIL) { 166 di_prom_fini(phdl); 167 } 168 phdl = DI_PROM_HANDLE_NIL; 169 170 if (root_node != DI_NODE_NIL) { 171 di_fini(root_node); 172 } 173 root_node = DI_NODE_NIL; 174 } 175 176 /* 177 * Take a snapshot of the device tree, i.e. get a devinfo handle and 178 * a PROM handle. 179 */ 180 static boolean_t 181 snapshot_devtree(void) 182 { 183 /* 184 * Deallocate any existing devinfo stuff first. 185 */ 186 destroy_snapshot(); 187 188 if ((root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL || 189 (phdl = di_prom_init()) == DI_PROM_HANDLE_NIL) { 190 destroy_snapshot(); 191 return (B_FALSE); 192 } 193 194 return (B_TRUE); 195 } 196 197 /* 198 * Get the value of the named property on the named node in root. 199 */ 200 static char * 201 get_prop(const char *nodename, const char *propname, size_t *lenp) 202 { 203 di_node_t node; 204 di_prom_prop_t pp; 205 char *val = NULL; 206 int len; 207 208 /* 209 * Locate nodename within '/'. 210 */ 211 for (node = di_child_node(root_node); 212 node != DI_NODE_NIL; 213 node = di_sibling_node(node)) { 214 if (strcmp(di_node_name(node), nodename) == 0) { 215 break; 216 } 217 } 218 if (node == DI_NODE_NIL) { 219 return (NULL); 220 } 221 222 /* 223 * Scan all properties of /nodename for the 'propname' property. 224 */ 225 for (pp = di_prom_prop_next(phdl, node, DI_PROM_PROP_NIL); 226 pp != DI_PROM_PROP_NIL; 227 pp = di_prom_prop_next(phdl, node, pp)) { 228 if (strcmp(propname, di_prom_prop_name(pp)) == 0) { 229 break; 230 } 231 } 232 if (pp == DI_PROM_PROP_NIL) { 233 return (NULL); 234 } 235 236 /* 237 * Found the property; copy out its length and return its value. 238 */ 239 len = di_prom_prop_data(pp, (uchar_t **)&val); 240 if (lenp != NULL) { 241 *lenp = len; 242 } 243 return (val); 244 } 245 246 /* 247 * Strip any trailing arguments from a device path. 248 * Returned memory must be freed by caller. 249 */ 250 static char * 251 strip_args(char *path, size_t len) 252 { 253 char *stripped_path = NULL; 254 255 if (path != NULL && len != 0 && 256 (stripped_path = calloc(len + 1, sizeof (char))) != NULL) { 257 char *p; 258 259 (void) memcpy(stripped_path, path, len); 260 if ((p = strchr(stripped_path, ':')) != NULL) { 261 *p = '\0'; 262 } 263 } 264 return (stripped_path); 265 } 266 267 /* 268 * Return the "bootpath" property (sans arguments) from /chosen. 269 * Returned memory must be freed by caller. 270 */ 271 static char * 272 get_bootpath(void) 273 { 274 char *path; 275 size_t len; 276 277 path = get_prop("chosen", "bootpath", &len); 278 return (strip_args(path, len)); 279 } 280 281 /* 282 * Return the "net" property (sans arguments) from /aliases. 283 * Returned memory must be freed by caller. 284 */ 285 static char * 286 get_netalias(void) 287 { 288 char *path; 289 size_t len; 290 291 path = get_prop("aliases", "net", &len); 292 return (strip_args(path, len)); 293 } 294 295 /* 296 * Callback used by path2node(). 297 */ 298 static int 299 p2n_cb(di_node_t node, void *arg) 300 { 301 int ret = DI_WALK_CONTINUE; 302 cb_t *cbp = arg; 303 char *phys_path = di_devfs_path(node); 304 305 if (strcmp(cbp->cb_path, phys_path) == 0) { 306 cbp->cb_node = node; 307 ret = DI_WALK_TERMINATE; 308 } 309 di_devfs_path_free(phys_path); 310 311 return (ret); 312 } 313 314 /* 315 * Map a device path to its matching di_node_t. 316 */ 317 static di_node_t 318 path2node(char *path) 319 { 320 cb_t cb; 321 322 cb.cb_path = path; 323 cb.cb_node = DI_NODE_NIL; 324 325 (void) di_walk_node(root_node, DI_WALK_CLDFIRST, &cb, p2n_cb); 326 327 return (cb.cb_node); 328 } 329 330 /* 331 * Check whether node corresponds to a network device. 332 */ 333 static boolean_t 334 is_network_device(di_node_t node) 335 { 336 char *type; 337 338 return (di_prom_prop_lookup_strings(phdl, node, 339 "device_type", &type) > 0 && strcmp(type, "network") == 0); 340 } 341 342 /* 343 * Initialise bootmisc with the rootfs-type. 344 */ 345 static boolean_t 346 rootfs_type_init(void) 347 { 348 static struct statvfs vfs; 349 350 if (statvfs("/", &vfs) >= 0) { 351 if (strncmp(vfs.f_basetype, "nfs", sizeof ("nfs") - 1) == 0) { 352 vfs.f_basetype[sizeof ("nfs") - 1] = '\0'; 353 } 354 rootfs_type = vfs.f_basetype; 355 } 356 357 return (rootfs_type != NULL && bi_put_bootmisc(BI_ROOTFS_TYPE, 358 rootfs_type, strlen(rootfs_type) + 1)); 359 } 360 361 /* 362 * Initialise bootmisc with the interface-name of the primary network device, 363 * and the net-config-strategy employed in configuring that device. 364 */ 365 static boolean_t 366 netif_init(char *ifn, char *ncs) 367 { 368 return (bi_put_bootmisc(BI_INTERFACE_NAME, ifn, strlen(ifn) + 1) && 369 bi_put_bootmisc(BI_NET_CONFIG_STRATEGY, ncs, strlen(ncs) + 1)); 370 } 371 372 /* 373 * Determine whether the interface was configured manually. 374 */ 375 static boolean_t 376 manual_if_init(void) 377 { 378 boolean_t ret = B_FALSE; 379 char *ncs; 380 char *devpath; 381 di_node_t node; 382 int instance; 383 char *drvname; 384 char ifname[IFNAMSIZ + 1]; 385 386 /* 387 * If net-config-strategy isn't "manual", don't go any further. 388 */ 389 if ((ncs = get_prop("chosen", BI_NET_CONFIG_STRATEGY, NULL)) == NULL || 390 strcmp(ncs, "manual") != 0) { 391 return (B_FALSE); 392 } 393 394 /* 395 * First check the 'bootpath' property of /chosen to see whether 396 * it specifies the path of a network device; if so, use this. 397 */ 398 if ((devpath = get_bootpath()) == NULL || 399 (node = path2node(devpath)) == DI_NODE_NIL || 400 !is_network_device(node)) { 401 /* 402 * Must have been booted from CD-ROM or disk; attempt to 403 * use the path defined by the 'net' property of /aliases. 404 */ 405 free(devpath); 406 if ((devpath = get_netalias()) == NULL || 407 (node = path2node(devpath)) == DI_NODE_NIL || 408 !is_network_device(node)) { 409 goto cleanup; 410 } 411 } 412 413 /* 414 * Get the driver name and instance number of this node. 415 * We may have to load the driver. 416 */ 417 if ((drvname = di_driver_name(node)) == NULL) { 418 goto cleanup; 419 } 420 if ((instance = di_instance(node)) == -1) { 421 di_node_t tmp; 422 423 /* 424 * Attempt to load the driver, create a new snapshot of the 425 * (possibly changed) device tree and re-compute our node. 426 */ 427 if ((tmp = di_init_driver(drvname, 0)) != DI_NODE_NIL) { 428 di_fini(tmp); 429 430 if (!snapshot_devtree() || 431 (node = path2node(devpath)) == DI_NODE_NIL) { 432 goto cleanup; 433 } 434 } 435 instance = di_instance(node); 436 } 437 438 /* 439 * Construct the interface name. 440 */ 441 if (instance == -1) { 442 (void) snprintf(ifname, sizeof (ifname), 443 "%s", di_driver_name(node)); 444 } else { 445 (void) snprintf(ifname, sizeof (ifname), 446 "%s%d", di_driver_name(node), instance); 447 } 448 449 ret = netif_init(ifname, "manual"); 450 cleanup: 451 free(devpath); 452 return (ret); 453 } 454 455 /* 456 * Determine whether the interface was configured via DHCP. 457 */ 458 static boolean_t 459 dhcp_if_init(void) 460 { 461 return (strlen(dhcp_ifn) != 0 && netif_init(dhcp_ifn, "dhcp")); 462 } 463 464 static boolean_t 465 bootmisc_init(void) 466 { 467 return (rootfs_type_init() && 468 (manual_if_init() || dhcp_if_init())); 469 } 470 471 472 /* 473 * Functions dealing with bootinfo initialization/cleanup. 474 */ 475 boolean_t 476 bi_init_bootinfo(void) 477 { 478 if (snapshot_devtree() && dhcp_info_init() && bootmisc_init()) { 479 return (B_TRUE); 480 } 481 bi_end_bootinfo(); 482 return (B_FALSE); 483 } 484 485 void 486 bi_end_bootinfo(void) 487 { 488 destroy_snapshot(); 489 dhcp_info_end(); 490 } 491 492 /* 493 * Function dealing with /chosen data. 494 */ 495 boolean_t 496 bi_get_chosen_prop(const char *name, void *valbuf, size_t *vallenp) 497 { 498 char *val; 499 size_t buflen = *vallenp; 500 501 if ((val = get_prop("chosen", name, vallenp)) == NULL) { 502 return (B_FALSE); 503 } 504 if (*vallenp <= buflen) { 505 (void) memcpy(valbuf, val, *vallenp); 506 } 507 508 return (B_TRUE); 509 } 510 511 /* 512 * Function dealing with DHCP data. 513 */ 514 boolean_t 515 bi_get_dhcp_info(uchar_t optcat, uint16_t optcode, uint16_t optsize, 516 void *valbuf, size_t *vallenp) 517 { 518 return (dhcp_getinfo_pl(dhcp_pl, 519 optcat, optcode, optsize, valbuf, vallenp)); 520 } 521