]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/netboot.c
xnu-1699.26.8.tar.gz
[apple/xnu.git] / bsd / kern / netboot.c
1 /*
2 * Copyright (c) 2001-2012 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>
65 extern struct filedesc filedesc0;
66
67 extern int nfs_mountroot(void); /* nfs_vfsops.c */
68 extern int (*mountroot)(void);
69
70 extern unsigned char rootdevice[];
71
72 static int S_netboot = 0;
73 static struct netboot_info * S_netboot_info_p;
74
75 void *
76 IOBSDRegistryEntryForDeviceTree(const char * path);
77
78 void
79 IOBSDRegistryEntryRelease(void * entry);
80
81 const void *
82 IOBSDRegistryEntryGetData(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 */
90 int 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
99 typedef enum {
100 kNetBootImageTypeUnknown = 0,
101 kNetBootImageTypeNFS = 1,
102 kNetBootImageTypeHTTP = 2,
103 } NetBootImageType;
104
105 struct 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 */
131 static __inline__ boolean_t
132 parse_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 */
181 static __inline__ char *
182 find_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 */
218 static __inline__ boolean_t
219 parse_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
260 static boolean_t
261 parse_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
272 static boolean_t
273 get_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
317 static void
318 save_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
326 static struct netboot_info *
327 netboot_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
424 static void
425 netboot_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
448 boolean_t
449 netboot_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
458 boolean_t
459 netboot_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
484 static boolean_t
485 get_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
532 static int
533 route_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
569 static int
570 default_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
581 static int
582 host_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
589 static struct ifnet *
590 find_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
609 int
610 netboot_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);
736 failed:
737 if (so != NULL) {
738 soclose(so);
739 }
740 return (error);
741 }
742
743 int
744 netboot_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
769 int
770 netboot_root(void)
771 {
772 return (S_netboot);
773 }