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