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