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