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