1/* 2 * Copyright (c) 2001-2010 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29/* 30 * History: 31 * 14 December, 2001 Dieter Siegmund (dieter@apple.com) 32 * - created 33 */ 34#include <sys/param.h> 35#include <sys/systm.h> 36#include <sys/kernel.h> 37#include <sys/conf.h> 38#include <sys/ioctl.h> 39#include <sys/proc_internal.h> 40#include <sys/mount_internal.h> 41#include <sys/mbuf.h> 42#include <sys/filedesc.h> 43#include <sys/vnode_internal.h> 44#include <sys/malloc.h> 45#include <sys/socket.h> 46#include <sys/socketvar.h> 47#include <sys/reboot.h> 48#include <sys/kauth.h> 49#include <net/if.h> 50#include <net/if_dl.h> 51#include <net/if_types.h> 52#include <net/route.h> 53#include <netinet/in.h> 54#include <netinet/if_ether.h> 55#include <netinet/dhcp_options.h> 56#include <netinet/in_dhcp.h> 57 58#include <kern/kern_types.h> 59#include <kern/kalloc.h> 60#include <sys/netboot.h> 61#include <sys/imageboot.h> 62#include <pexpert/pexpert.h> 63 64//#include <libkern/libkern.h> 65extern struct filedesc filedesc0; 66 67extern int nfs_mountroot(void); /* nfs_vfsops.c */ 68extern int (*mountroot)(void); 69 70extern unsigned char rootdevice[]; 71 72static int S_netboot = 0; 73static struct netboot_info * S_netboot_info_p; 74 75void * 76IOBSDRegistryEntryForDeviceTree(const char * path); 77 78void 79IOBSDRegistryEntryRelease(void * entry); 80 81const void * 82IOBSDRegistryEntryGetData(void * entry, const char * property_name, 83 int * packet_length); 84 85#define BOOTP_RESPONSE "bootp-response" 86#define BSDP_RESPONSE "bsdp-response" 87#define DHCP_RESPONSE "dhcp-response" 88 89/* forward declarations */ 90int inet_aton(char * cp, struct in_addr * pin); 91 92#define IP_FORMAT "%d.%d.%d.%d" 93#define IP_CH(ip) ((u_char *)ip) 94#define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3] 95 96#define kNetBootRootPathPrefixNFS "nfs:" 97#define kNetBootRootPathPrefixHTTP "http:" 98 99typedef enum { 100 kNetBootImageTypeUnknown = 0, 101 kNetBootImageTypeNFS = 1, 102 kNetBootImageTypeHTTP = 2, 103} NetBootImageType; 104 105struct netboot_info { 106 struct in_addr client_ip; 107 struct in_addr server_ip; 108 char * server_name; 109 int server_name_length; 110 char * mount_point; 111 int mount_point_length; 112 char * image_path; 113 int image_path_length; 114 NetBootImageType image_type; 115 char * second_image_path; 116 int second_image_path_length; 117}; 118 119/* 120 * Function: parse_booter_path 121 * Purpose: 122 * Parse a string of the form: 123 * "<IP>:<host>:<mount>[:<image_path>]" 124 * into the given ip address, host, mount point, and optionally, image_path. 125 * 126 * Note: 127 * The passed in string is modified i.e. ':' is replaced by '\0'. 128 * Example: 129 * "17.202.16.17:seaport:/release/.images/Image9/CurrentHera" 130 */ 131static __inline__ boolean_t 132parse_booter_path(char * path, struct in_addr * iaddr_p, char const * * host, 133 char * * mount_dir, char * * image_path) 134{ 135 char * start; 136 char * colon; 137 138 /* IP address */ 139 start = path; 140 colon = strchr(start, ':'); 141 if (colon == NULL) { 142 return (FALSE); 143 } 144 *colon = '\0'; 145 if (inet_aton(start, iaddr_p) != 1) { 146 return (FALSE); 147 } 148 149 /* host */ 150 start = colon + 1; 151 colon = strchr(start, ':'); 152 if (colon == NULL) { 153 return (FALSE); 154 } 155 *colon = '\0'; 156 *host = start; 157 158 /* mount */ 159 start = colon + 1; 160 colon = strchr(start, ':'); 161 *mount_dir = start; 162 if (colon == NULL) { 163 *image_path = NULL; 164 } 165 else { 166 /* image path */ 167 *colon = '\0'; 168 start = colon + 1; 169 *image_path = start; 170 } 171 return (TRUE); 172} 173 174/* 175 * Function: find_colon 176 * Purpose: 177 * Find the next unescaped instance of the colon character. 178 * If a colon is escaped (preceded by a backslash '\' character), 179 * shift the string over by one character to overwrite the backslash. 180 */ 181static __inline__ char * 182find_colon(char * str) 183{ 184 char * start = str; 185 char * colon; 186 187 while ((colon = strchr(start, ':')) != NULL) { 188 char * dst; 189 char * src; 190 191 if (colon == start) { 192 break; 193 } 194 if (colon[-1] != '\\') 195 break; 196 for (dst = colon - 1, src = colon; *dst != '\0'; dst++, src++) { 197 *dst = *src; 198 } 199 start = colon; 200 } 201 return (colon); 202} 203 204/* 205 * Function: parse_netboot_path 206 * Purpose: 207 * Parse a string of the form: 208 * "nfs:<IP>:<mount>[:<image_path>]" 209 * into the given ip address, host, mount point, and optionally, image_path. 210 * Notes: 211 * - the passed in string is modified i.e. ':' is replaced by '\0' 212 * - literal colons must be escaped with a backslash 213 * 214 * Examples: 215 * nfs:17.202.42.112:/Library/NetBoot/NetBootSP0:Jaguar/Jaguar.dmg 216 * nfs:17.202.42.112:/Volumes/Foo\:/Library/NetBoot/NetBootSP0:Jaguar/Jaguar.dmg 217 */ 218static __inline__ boolean_t 219parse_netboot_path(char * path, struct in_addr * iaddr_p, char const * * host, 220 char * * mount_dir, char * * image_path) 221{ 222 static char tmp[MAX_IPv4_STR_LEN]; /* Danger - not thread safe */ 223 char * start; 224 char * colon; 225 226 if (strncmp(path, kNetBootRootPathPrefixNFS, 227 strlen(kNetBootRootPathPrefixNFS)) != 0) { 228 return (FALSE); 229 } 230 231 /* IP address */ 232 start = path + strlen(kNetBootRootPathPrefixNFS); 233 colon = strchr(start, ':'); 234 if (colon == NULL) { 235 return (FALSE); 236 } 237 *colon = '\0'; 238 if (inet_aton(start, iaddr_p) != 1) { 239 return (FALSE); 240 } 241 242 /* mount point */ 243 start = colon + 1; 244 colon = find_colon(start); 245 *mount_dir = start; 246 if (colon == NULL) { 247 *image_path = NULL; 248 } 249 else { 250 /* image path */ 251 *colon = '\0'; 252 start = colon + 1; 253 (void)find_colon(start); 254 *image_path = start; 255 } 256 *host = inet_ntop(AF_INET, iaddr_p, tmp, sizeof(tmp)); 257 return (TRUE); 258} 259 260static boolean_t 261parse_image_path(char * path, struct in_addr * iaddr_p, char const * * host, 262 char * * mount_dir, char * * image_path) 263{ 264 if (path[0] >= '0' && path[0] <= '9') { 265 return (parse_booter_path(path, iaddr_p, host, mount_dir, 266 image_path)); 267 } 268 return (parse_netboot_path(path, iaddr_p, host, mount_dir, 269 image_path)); 270} 271 272static boolean_t 273get_root_path(char * root_path) 274{ 275 void * entry; 276 boolean_t found = FALSE; 277 const void * pkt; 278 int pkt_len; 279 280 entry = IOBSDRegistryEntryForDeviceTree("/chosen"); 281 if (entry == NULL) { 282 return (FALSE); 283 } 284 pkt = IOBSDRegistryEntryGetData(entry, BSDP_RESPONSE, &pkt_len); 285 if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) { 286 printf("netboot: retrieving root path from BSDP response\n"); 287 } 288 else { 289 pkt = IOBSDRegistryEntryGetData(entry, BOOTP_RESPONSE, 290 &pkt_len); 291 if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) { 292 printf("netboot: retrieving root path from BOOTP response\n"); 293 } 294 } 295 if (pkt != NULL) { 296 int len; 297 dhcpol_t options; 298 const char * path; 299 const struct dhcp * reply; 300 301 reply = (const struct dhcp *)pkt; 302 (void)dhcpol_parse_packet(&options, reply, pkt_len); 303 304 path = (const char *)dhcpol_find(&options, 305 dhcptag_root_path_e, &len, NULL); 306 if (path) { 307 memcpy(root_path, path, len); 308 root_path[len] = '\0'; 309 found = TRUE; 310 } 311 } 312 IOBSDRegistryEntryRelease(entry); 313 return (found); 314 315} 316 317static void 318save_path(char * * str_p, int * length_p, char * path) 319{ 320 *length_p = strlen(path) + 1; 321 *str_p = (char *)kalloc(*length_p); 322 strlcpy(*str_p, path, *length_p); 323 return; 324} 325 326static struct netboot_info * 327netboot_info_init(struct in_addr iaddr) 328{ 329 boolean_t have_root_path = FALSE; 330 struct netboot_info * info = NULL; 331 char * root_path = NULL; 332 333 info = (struct netboot_info *)kalloc(sizeof(*info)); 334 bzero(info, sizeof(*info)); 335 info->client_ip = iaddr; 336 info->image_type = kNetBootImageTypeUnknown; 337 338 /* check for a booter-specified path then a NetBoot path */ 339 MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK); 340 if (root_path == NULL) 341 panic("netboot_info_init: M_NAMEI zone exhausted"); 342 if (PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) == TRUE 343 || PE_parse_boot_argn("rp", root_path, MAXPATHLEN) == TRUE 344 || PE_parse_boot_argn("rootpath", root_path, MAXPATHLEN) == TRUE) { 345 if (imageboot_format_is_valid(root_path)) { 346 printf("netboot_info_init: rp0='%s' isn't a network path," 347 " ignoring\n", root_path); 348 } 349 else { 350 have_root_path = TRUE; 351 } 352 } 353 if (have_root_path == FALSE) { 354 have_root_path = get_root_path(root_path); 355 } 356 if (have_root_path) { 357 const char * server_name = NULL; 358 char * mount_point = NULL; 359 char * image_path = NULL; 360 struct in_addr server_ip; 361 362 if (parse_image_path(root_path, &server_ip, &server_name, 363 &mount_point, &image_path)) { 364 info->image_type = kNetBootImageTypeNFS; 365 info->server_ip = server_ip; 366 info->server_name_length = strlen(server_name) + 1; 367 info->server_name = (char *)kalloc(info->server_name_length); 368 info->mount_point_length = strlen(mount_point) + 1; 369 info->mount_point = (char *)kalloc(info->mount_point_length); 370 strlcpy(info->server_name, server_name, info->server_name_length); 371 strlcpy(info->mount_point, mount_point, info->mount_point_length); 372 373 printf("netboot: NFS Server %s Mount %s", 374 server_name, info->mount_point); 375 if (image_path != NULL) { 376 boolean_t needs_slash = FALSE; 377 378 info->image_path_length = strlen(image_path) + 1; 379 if (image_path[0] != '/') { 380 needs_slash = TRUE; 381 info->image_path_length++; 382 } 383 info->image_path = (char *)kalloc(info->image_path_length); 384 if (needs_slash) { 385 info->image_path[0] = '/'; 386 strlcpy(info->image_path + 1, image_path, 387 info->image_path_length - 1); 388 } else { 389 strlcpy(info->image_path, image_path, 390 info->image_path_length); 391 } 392 printf(" Image %s", info->image_path); 393 } 394 printf("\n"); 395 } 396 else if (strncmp(root_path, kNetBootRootPathPrefixHTTP, 397 strlen(kNetBootRootPathPrefixHTTP)) == 0) { 398 info->image_type = kNetBootImageTypeHTTP; 399 save_path(&info->image_path, &info->image_path_length, 400 root_path); 401 printf("netboot: HTTP URL %s\n", info->image_path); 402 } 403 else { 404 printf("netboot: root path uses unrecognized format\n"); 405 } 406 407 /* check for image-within-image */ 408 if (info->image_path != NULL) { 409 if (PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN) 410 || PE_parse_boot_argn("rp1", root_path, MAXPATHLEN)) { 411 /* rp1/root-dmg is the second-level image */ 412 save_path(&info->second_image_path, &info->second_image_path_length, 413 root_path); 414 } 415 } 416 if (info->second_image_path != NULL) { 417 printf("netboot: nested image %s\n", info->second_image_path); 418 } 419 } 420 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI); 421 return (info); 422} 423 424static void 425netboot_info_free(struct netboot_info * * info_p) 426{ 427 struct netboot_info * info = *info_p; 428 429 if (info) { 430 if (info->mount_point) { 431 kfree(info->mount_point, info->mount_point_length); 432 } 433 if (info->server_name) { 434 kfree(info->server_name, info->server_name_length); 435 } 436 if (info->image_path) { 437 kfree(info->image_path, info->image_path_length); 438 } 439 if (info->second_image_path) { 440 kfree(info->second_image_path, info->second_image_path_length); 441 } 442 kfree(info, sizeof(*info)); 443 } 444 *info_p = NULL; 445 return; 446} 447 448boolean_t 449netboot_iaddr(struct in_addr * iaddr_p) 450{ 451 if (S_netboot_info_p == NULL) 452 return (FALSE); 453 454 *iaddr_p = S_netboot_info_p->client_ip; 455 return (TRUE); 456} 457 458boolean_t 459netboot_rootpath(struct in_addr * server_ip, 460 char * name, int name_len, 461 char * path, int path_len) 462{ 463 if (S_netboot_info_p == NULL) 464 return (FALSE); 465 466 name[0] = '\0'; 467 path[0] = '\0'; 468 469 if (S_netboot_info_p->mount_point_length == 0) { 470 return (FALSE); 471 } 472 if (path_len < S_netboot_info_p->mount_point_length) { 473 printf("netboot: path too small %d < %d\n", 474 path_len, S_netboot_info_p->mount_point_length); 475 return (FALSE); 476 } 477 strlcpy(path, S_netboot_info_p->mount_point, path_len); 478 strlcpy(name, S_netboot_info_p->server_name, name_len); 479 *server_ip = S_netboot_info_p->server_ip; 480 return (TRUE); 481} 482 483 484static boolean_t 485get_ip_parameters(struct in_addr * iaddr_p, struct in_addr * netmask_p, 486 struct in_addr * router_p) 487{ 488 void * entry; 489 const void * pkt; 490 int pkt_len; 491 492 493 entry = IOBSDRegistryEntryForDeviceTree("/chosen"); 494 if (entry == NULL) { 495 return (FALSE); 496 } 497 pkt = IOBSDRegistryEntryGetData(entry, DHCP_RESPONSE, &pkt_len); 498 if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) { 499 printf("netboot: retrieving IP information from DHCP response\n"); 500 } 501 else { 502 pkt = IOBSDRegistryEntryGetData(entry, BOOTP_RESPONSE, &pkt_len); 503 if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) { 504 printf("netboot: retrieving IP information from BOOTP response\n"); 505 } 506 } 507 if (pkt != NULL) { 508 const struct in_addr * ip; 509 int len; 510 dhcpol_t options; 511 const struct dhcp * reply; 512 513 reply = (const struct dhcp *)pkt; 514 (void)dhcpol_parse_packet(&options, reply, pkt_len); 515 *iaddr_p = reply->dp_yiaddr; 516 ip = (const struct in_addr *) 517 dhcpol_find(&options, 518 dhcptag_subnet_mask_e, &len, NULL); 519 if (ip) { 520 *netmask_p = *ip; 521 } 522 ip = (const struct in_addr *) 523 dhcpol_find(&options, dhcptag_router_e, &len, NULL); 524 if (ip) { 525 *router_p = *ip; 526 } 527 } 528 IOBSDRegistryEntryRelease(entry); 529 return (pkt != NULL); 530} 531 532static int 533route_cmd(int cmd, struct in_addr d, struct in_addr g, 534 struct in_addr m, uint32_t more_flags, unsigned int ifscope) 535{ 536 struct sockaddr_in dst; 537 int error; 538 uint32_t flags = RTF_UP | RTF_STATIC; 539 struct sockaddr_in gw; 540 struct sockaddr_in mask; 541 542 flags |= more_flags; 543 544 /* destination */ 545 bzero((caddr_t)&dst, sizeof(dst)); 546 dst.sin_len = sizeof(dst); 547 dst.sin_family = AF_INET; 548 dst.sin_addr = d; 549 550 /* gateway */ 551 bzero((caddr_t)&gw, sizeof(gw)); 552 gw.sin_len = sizeof(gw); 553 gw.sin_family = AF_INET; 554 gw.sin_addr = g; 555 556 /* mask */ 557 bzero(&mask, sizeof(mask)); 558 mask.sin_len = sizeof(mask); 559 mask.sin_family = AF_INET; 560 mask.sin_addr = m; 561 562 error = rtrequest_scoped(cmd, (struct sockaddr *)&dst, 563 (struct sockaddr *)&gw, (struct sockaddr *)&mask, flags, NULL, ifscope); 564 565 return (error); 566 567} 568 569static int 570default_route_add(struct in_addr router, boolean_t proxy_arp) 571{ 572 uint32_t flags = 0; 573 struct in_addr zeroes = { 0 }; 574 575 if (proxy_arp == FALSE) { 576 flags |= RTF_GATEWAY; 577 } 578 return (route_cmd(RTM_ADD, zeroes, router, zeroes, flags, IFSCOPE_NONE)); 579} 580 581static int 582host_route_delete(struct in_addr host, unsigned int ifscope) 583{ 584 struct in_addr zeroes = { 0 }; 585 586 return (route_cmd(RTM_DELETE, host, zeroes, zeroes, RTF_HOST, ifscope)); 587} 588 589static struct ifnet * 590find_interface(void) 591{ 592 struct ifnet * ifp = NULL; 593 594 dlil_if_lock(); 595 if (rootdevice[0]) { 596 ifp = ifunit((char *)rootdevice); 597 } 598 if (ifp == NULL) { 599 ifnet_head_lock_shared(); 600 TAILQ_FOREACH(ifp, &ifnet_head, if_link) 601 if ((ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0) 602 break; 603 ifnet_head_done(); 604 } 605 dlil_if_unlock(); 606 return (ifp); 607} 608 609int 610netboot_mountroot(void) 611{ 612 int error = 0; 613 struct in_addr iaddr = { 0 }; 614 struct ifreq ifr; 615 struct ifnet * ifp; 616 struct in_addr netmask = { 0 }; 617 proc_t procp = current_proc(); 618 struct in_addr router = { 0 }; 619 struct socket * so = NULL; 620 unsigned int try; 621 622 bzero(&ifr, sizeof(ifr)); 623 624 /* find the interface */ 625 ifp = find_interface(); 626 if (ifp == NULL) { 627 printf("netboot: no suitable interface\n"); 628 error = ENXIO; 629 goto failed; 630 } 631 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", ifp->if_name, 632 ifp->if_unit); 633 printf("netboot: using network interface '%s'\n", ifr.ifr_name); 634 635 /* bring it up */ 636 if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0) { 637 printf("netboot: socreate, error=%d\n", error); 638 goto failed; 639 } 640 ifr.ifr_flags = ifp->if_flags | IFF_UP; 641 error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ifr, procp); 642 if (error) { 643 printf("netboot: SIFFLAGS, error=%d\n", error); 644 goto failed; 645 } 646 647 /* grab information from the registry */ 648 if (get_ip_parameters(&iaddr, &netmask, &router) == FALSE) { 649 /* use DHCP to retrieve IP address, netmask and router */ 650 error = dhcp(ifp, &iaddr, 64, &netmask, &router, procp); 651 if (error) { 652 printf("netboot: DHCP failed %d\n", error); 653 goto failed; 654 } 655 } 656 printf("netboot: IP address " IP_FORMAT, IP_LIST(&iaddr)); 657 if (netmask.s_addr) { 658 printf(" netmask " IP_FORMAT, IP_LIST(&netmask)); 659 } 660 if (router.s_addr) { 661 printf(" router " IP_FORMAT, IP_LIST(&router)); 662 } 663 printf("\n"); 664 error = inet_aifaddr(so, ifr.ifr_name, &iaddr, &netmask, NULL); 665 if (error) { 666 printf("netboot: inet_aifaddr failed, %d\n", error); 667 goto failed; 668 } 669 if (router.s_addr == 0) { 670 /* enable proxy arp if we don't have a router */ 671 router.s_addr = iaddr.s_addr; 672 } 673 printf("netboot: adding default route " IP_FORMAT "\n", 674 IP_LIST(&router)); 675 error = default_route_add(router, router.s_addr == iaddr.s_addr); 676 if (error) { 677 printf("netboot: default_route_add failed %d\n", error); 678 } 679 680 soclose(so); 681 682 S_netboot_info_p = netboot_info_init(iaddr); 683 switch (S_netboot_info_p->image_type) { 684 default: 685 case kNetBootImageTypeNFS: 686 for (try = 1; TRUE; try++) { 687 error = nfs_mountroot(); 688 if (error == 0) { 689 break; 690 } 691 printf("netboot: nfs_mountroot() attempt %u failed; " 692 "clearing ARP entry and trying again\n", try); 693 /* 694 * error is either EHOSTDOWN or EHOSTUNREACH, which likely means 695 * that the port we're plugged into has spanning tree enabled, 696 * and either the router or the server can't answer our ARP 697 * requests. Clear the incomplete ARP entry by removing the 698 * appropriate route, depending on the error code: 699 * EHOSTDOWN NFS server's route 700 * EHOSTUNREACH router's route 701 */ 702 switch (error) { 703 default: 704 /* NOT REACHED */ 705 case EHOSTDOWN: 706 /* remove the server's arp entry */ 707 error = host_route_delete(S_netboot_info_p->server_ip, 708 ifp->if_index); 709 if (error) { 710 printf("netboot: host_route_delete(" IP_FORMAT 711 ") failed %d\n", 712 IP_LIST(&S_netboot_info_p->server_ip), error); 713 } 714 break; 715 case EHOSTUNREACH: 716 error = host_route_delete(router, ifp->if_index); 717 if (error) { 718 printf("netboot: host_route_delete(" IP_FORMAT 719 ") failed %d\n", IP_LIST(&router), error); 720 } 721 break; 722 } 723 } 724 break; 725 case kNetBootImageTypeHTTP: 726 error = netboot_setup(); 727 break; 728 } 729 if (error == 0) { 730 S_netboot = 1; 731 } 732 else { 733 S_netboot = 0; 734 } 735 return (error); 736failed: 737 if (so != NULL) { 738 soclose(so); 739 } 740 return (error); 741} 742 743int 744netboot_setup() 745{ 746 int error = 0; 747 748 if (S_netboot_info_p == NULL 749 || S_netboot_info_p->image_path == NULL) { 750 goto done; 751 } 752 printf("netboot_setup: calling imageboot_mount_image\n"); 753 error = imageboot_mount_image(S_netboot_info_p->image_path, -1); 754 if (error != 0) { 755 printf("netboot: failed to mount root image, %d\n", error); 756 } 757 else if (S_netboot_info_p->second_image_path != NULL) { 758 error = imageboot_mount_image(S_netboot_info_p->second_image_path, 0); 759 if (error != 0) { 760 printf("netboot: failed to mount second root image, %d\n", error); 761 } 762 } 763 764 done: 765 netboot_info_free(&S_netboot_info_p); 766 return (error); 767} 768 769int 770netboot_root(void) 771{ 772 return (S_netboot); 773} 774