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