]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/netboot.c
xnu-792.10.96.tar.gz
[apple/xnu.git] / bsd / kern / netboot.c
CommitLineData
9bccf70c 1/*
91447636 2 * Copyright (c) 2001-2004 Apple Computer, Inc. All rights reserved.
9bccf70c
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
37839358
A
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
9bccf70c 11 *
37839358
A
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
9bccf70c
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
37839358
A
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
9bccf70c
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23/*
24 * History:
25 * 14 December, 2001 Dieter Siegmund (dieter@apple.com)
26 * - created
27 */
28#include <sys/param.h>
29#include <sys/systm.h>
30#include <sys/kernel.h>
31#include <sys/conf.h>
32#include <sys/ioctl.h>
91447636
A
33#include <sys/proc_internal.h>
34#include <sys/mount_internal.h>
9bccf70c
A
35#include <sys/mbuf.h>
36#include <sys/filedesc.h>
91447636 37#include <sys/vnode_internal.h>
9bccf70c
A
38#include <sys/malloc.h>
39#include <sys/socket.h>
4a249263 40#include <sys/socketvar.h>
9bccf70c
A
41#include <sys/reboot.h>
42#include <net/if.h>
43#include <net/if_dl.h>
44#include <net/if_types.h>
45#include <net/route.h>
46#include <netinet/in.h>
47#include <netinet/if_ether.h>
48#include <netinet/dhcp_options.h>
4a249263 49#include <pexpert/pexpert.h>
9bccf70c 50
91447636
A
51#include <kern/kern_types.h>
52#include <kern/kalloc.h>
53
9bccf70c 54//#include <libkern/libkern.h>
9bccf70c
A
55extern struct filedesc filedesc0;
56
4a249263
A
57extern int strncmp(const char *,const char *, size_t);
58extern unsigned long strtoul(const char *, char **, int);
9bccf70c
A
59extern char * strchr(const char *str, int ch);
60
4a249263 61extern int nfs_mountroot(void); /* nfs_vfsops.c */
9bccf70c
A
62extern int (*mountroot)(void);
63
64extern unsigned char rootdevice[];
65
66static int S_netboot = 0;
67static struct netboot_info * S_netboot_info_p;
68
69void *
4a249263 70IOBSDRegistryEntryForDeviceTree(const char * path);
9bccf70c
A
71
72void
73IOBSDRegistryEntryRelease(void * entry);
74
75const void *
4a249263 76IOBSDRegistryEntryGetData(void * entry, const char * property_name,
9bccf70c
A
77 int * packet_length);
78
79extern int vndevice_root_image(const char * path, char devname[],
80 dev_t * dev_p);
81extern int di_root_image(const char *path, char devname[], dev_t *dev_p);
82
9bccf70c
A
83#define BOOTP_RESPONSE "bootp-response"
84#define BSDP_RESPONSE "bsdp-response"
85#define DHCP_RESPONSE "dhcp-response"
86
87extern int
88bootp(struct ifnet * ifp, struct in_addr * iaddr_p, int max_retry,
89 struct in_addr * netmask_p, struct in_addr * router_p,
90 struct proc * procp);
91
4a249263
A
92
93/* forward declarations */
94int inet_aton(char * cp, struct in_addr * pin);
95
96boolean_t netboot_iaddr(struct in_addr * iaddr_p);
97boolean_t netboot_rootpath(struct in_addr * server_ip,
98 char * name, int name_len,
99 char * path, int path_len);
100int netboot_setup(struct proc * p);
101int netboot_mountroot(void);
102int netboot_root(void);
103
104
105
9bccf70c
A
106#define IP_FORMAT "%d.%d.%d.%d"
107#define IP_CH(ip) ((u_char *)ip)
108#define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
109
110#define kNetBootRootPathPrefixNFS "nfs:"
111#define kNetBootRootPathPrefixHTTP "http:"
112
113typedef enum {
114 kNetBootImageTypeUnknown = 0,
115 kNetBootImageTypeNFS = 1,
116 kNetBootImageTypeHTTP = 2,
117} NetBootImageType;
118
119struct netboot_info {
120 struct in_addr client_ip;
121 struct in_addr server_ip;
122 char * server_name;
123 int server_name_length;
124 char * mount_point;
125 int mount_point_length;
126 char * image_path;
127 int image_path_length;
128 NetBootImageType image_type;
129 boolean_t use_hdix;
130};
131
132int
133inet_aton(char * cp, struct in_addr * pin)
134{
135 u_char * b = (char *)pin;
136 int i;
137 char * p;
138
139 for (p = cp, i = 0; i < 4; i++) {
140 u_long l = strtoul(p, 0, 0);
141 if (l > 255)
142 return (FALSE);
143 b[i] = l;
144 p = strchr(p, '.');
145 if (i < 3 && p == NULL)
146 return (FALSE);
147 p++;
148 }
149 return (TRUE);
150}
151
152/*
153 * Function: parse_booter_path
154 * Purpose:
155 * Parse a string of the form:
156 * "<IP>:<host>:<mount>[:<image_path>]"
157 * into the given ip address, host, mount point, and optionally, image_path.
158 *
159 * Note:
160 * The passed in string is modified i.e. ':' is replaced by '\0'.
161 * Example:
162 * "17.202.16.17:seaport:/release/.images/Image9/CurrentHera"
163 */
164static __inline__ boolean_t
165parse_booter_path(char * path, struct in_addr * iaddr_p, char * * host,
166 char * * mount_dir, char * * image_path)
167{
168 char * start;
169 char * colon;
170
171 /* IP address */
172 start = path;
173 colon = strchr(start, ':');
174 if (colon == NULL) {
175 return (FALSE);
176 }
177 *colon = '\0';
178 if (inet_aton(start, iaddr_p) != 1) {
179 return (FALSE);
180 }
181
182 /* host */
183 start = colon + 1;
184 colon = strchr(start, ':');
185 if (colon == NULL) {
186 return (FALSE);
187 }
188 *colon = '\0';
189 *host = start;
190
191 /* mount */
192 start = colon + 1;
193 colon = strchr(start, ':');
194 *mount_dir = start;
195 if (colon == NULL) {
196 *image_path = NULL;
197 }
198 else {
199 /* image path */
200 *colon = '\0';
201 start = colon + 1;
202 *image_path = start;
203 }
204 return (TRUE);
205}
206
207/*
208 * Function: find_colon
209 * Purpose:
210 * Find the next unescaped instance of the colon character.
211 * If a colon is escaped (preceded by a backslash '\' character),
212 * shift the string over by one character to overwrite the backslash.
213 */
214static __inline__ char *
215find_colon(char * str)
216{
217 char * start = str;
218 char * colon;
219
220 while ((colon = strchr(start, ':')) != NULL) {
221 char * dst;
222 char * src;
223
224 if (colon == start) {
225 break;
226 }
227 if (colon[-1] != '\\')
228 break;
229 for (dst = colon - 1, src = colon; *dst != '\0'; dst++, src++) {
230 *dst = *src;
231 }
232 start = colon;
233 }
234 return (colon);
235}
236
237/*
238 * Function: parse_netboot_path
239 * Purpose:
240 * Parse a string of the form:
241 * "nfs:<IP>:<mount>[:<image_path>]"
242 * into the given ip address, host, mount point, and optionally, image_path.
243 * Notes:
244 * - the passed in string is modified i.e. ':' is replaced by '\0'
245 * - literal colons must be escaped with a backslash
246 *
247 * Examples:
248 * nfs:17.202.42.112:/Library/NetBoot/NetBootSP0:Jaguar/Jaguar.dmg
249 * nfs:17.202.42.112:/Volumes/Foo\:/Library/NetBoot/NetBootSP0:Jaguar/Jaguar.dmg
250 */
251static __inline__ boolean_t
252parse_netboot_path(char * path, struct in_addr * iaddr_p, char * * host,
253 char * * mount_dir, char * * image_path)
254{
91447636 255 static char tmp[MAX_IPv4_STR_LEN]; /* Danger - not thread safe */
9bccf70c
A
256 char * start;
257 char * colon;
258
259 if (strncmp(path, kNetBootRootPathPrefixNFS,
260 strlen(kNetBootRootPathPrefixNFS)) != 0) {
261 return (FALSE);
262 }
263
264 /* IP address */
265 start = path + strlen(kNetBootRootPathPrefixNFS);
266 colon = strchr(start, ':');
267 if (colon == NULL) {
268 return (FALSE);
269 }
270 *colon = '\0';
271 if (inet_aton(start, iaddr_p) != 1) {
272 return (FALSE);
273 }
274
275 /* mount point */
276 start = colon + 1;
277 colon = find_colon(start);
278 *mount_dir = start;
279 if (colon == NULL) {
280 *image_path = NULL;
281 }
282 else {
283 /* image path */
284 *colon = '\0';
285 start = colon + 1;
286 (void)find_colon(start);
287 *image_path = start;
288 }
91447636 289 *host = inet_ntop(AF_INET, iaddr_p, tmp, sizeof(tmp));
9bccf70c
A
290 return (TRUE);
291}
292
293static boolean_t
294parse_image_path(char * path, struct in_addr * iaddr_p, char * * host,
295 char * * mount_dir, char * * image_path)
296{
297 if (path[0] >= '0' && path[0] <= '9') {
298 return (parse_booter_path(path, iaddr_p, host, mount_dir,
299 image_path));
300 }
301 return (parse_netboot_path(path, iaddr_p, host, mount_dir,
302 image_path));
303}
304
305static boolean_t
306get_root_path(char * root_path)
307{
308 void * entry;
309 boolean_t found = FALSE;
310 const void * pkt;
311 int pkt_len;
312
313 entry = IOBSDRegistryEntryForDeviceTree("/chosen");
314 if (entry == NULL) {
315 return (FALSE);
316 }
317 pkt = IOBSDRegistryEntryGetData(entry, BSDP_RESPONSE, &pkt_len);
4a249263 318 if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) {
9bccf70c
A
319 printf("netboot: retrieving root path from BSDP response\n");
320 }
321 else {
322 pkt = IOBSDRegistryEntryGetData(entry, BOOTP_RESPONSE,
323 &pkt_len);
4a249263 324 if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) {
9bccf70c
A
325 printf("netboot: retrieving root path from BOOTP response\n");
326 }
327 }
328 if (pkt != NULL) {
329 int len;
330 dhcpol_t options;
331 char * path;
332 struct dhcp * reply;
333
334 reply = (struct dhcp *)pkt;
335 (void)dhcpol_parse_packet(&options, reply, pkt_len, NULL);
336
337 path = (char *)dhcpol_find(&options,
338 dhcptag_root_path_e, &len, NULL);
339 if (path) {
340 bcopy(path, root_path, len);
341 root_path[len] = '\0';
342 found = TRUE;
343 }
344 }
345 IOBSDRegistryEntryRelease(entry);
346 return (found);
347
348}
349
350static struct netboot_info *
351netboot_info_init(struct in_addr iaddr)
352{
353 struct netboot_info * info;
354 char * root_path = NULL;
355 boolean_t use_hdix = TRUE;
356 char * vndevice = NULL;
357
358 MALLOC_ZONE(vndevice, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
91447636
A
359 if (vndevice == NULL)
360 panic("netboot_info_init: M_NAMEI zone exhausted");
9bccf70c
A
361 if (PE_parse_boot_arg("vndevice", vndevice) == TRUE) {
362 use_hdix = FALSE;
363 }
55e303ae 364 FREE_ZONE(vndevice, MAXPATHLEN, M_NAMEI);
9bccf70c
A
365
366 info = (struct netboot_info *)kalloc(sizeof(*info));
367 bzero(info, sizeof(*info));
368 info->client_ip = iaddr;
369 info->image_type = kNetBootImageTypeUnknown;
370 info->use_hdix = use_hdix;
371
372 /* check for a booter-specified path then a NetBoot path */
373 MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
91447636
A
374 if (root_path == NULL)
375 panic("netboot_info_init: M_NAMEI zone exhausted");
9bccf70c
A
376 if (PE_parse_boot_arg("rp", root_path) == TRUE
377 || PE_parse_boot_arg("rootpath", root_path) == TRUE
378 || get_root_path(root_path) == TRUE) {
379 char * server_name = NULL;
380 char * mount_point = NULL;
381 char * image_path = NULL;
382 struct in_addr server_ip;
383
384 if (parse_image_path(root_path, &server_ip, &server_name,
385 &mount_point, &image_path)) {
386 info->image_type = kNetBootImageTypeNFS;
387 info->server_ip = server_ip;
388 info->server_name_length = strlen(server_name) + 1;
389 info->server_name = (char *)kalloc(info->server_name_length);
390 info->mount_point_length = strlen(mount_point) + 1;
391 info->mount_point = (char *)kalloc(info->mount_point_length);
392 strcpy(info->server_name, server_name);
393 strcpy(info->mount_point, mount_point);
394
395 printf("Server %s Mount %s",
396 server_name, info->mount_point);
397 if (image_path != NULL) {
4a249263 398 boolean_t needs_slash = FALSE;
9bccf70c
A
399
400 info->image_path_length = strlen(image_path) + 1;
401 if (image_path[0] != '/') {
402 needs_slash = TRUE;
403 info->image_path_length++;
404 }
405 info->image_path = (char *)kalloc(info->image_path_length);
406 if (needs_slash) {
407 info->image_path[0] = '/';
408 strcpy(info->image_path + 1, image_path);
409 }
410 else {
411 strcpy(info->image_path, image_path);
412 }
413 printf(" Image %s", info->image_path);
414 }
415 printf("\n");
416 }
417 else if (strncmp(root_path, kNetBootRootPathPrefixHTTP,
418 strlen(kNetBootRootPathPrefixHTTP)) == 0) {
419 /* only HDIX supports HTTP */
420 info->image_type = kNetBootImageTypeHTTP;
421 info->use_hdix = TRUE;
422 info->image_path_length = strlen(root_path) + 1;
423 info->image_path = (char *)kalloc(info->image_path_length);
424 strcpy(info->image_path, root_path);
425 }
426 else {
427 printf("netboot: root path uses unrecognized format\n");
428 }
429 }
55e303ae 430 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
9bccf70c
A
431 return (info);
432}
433
434static void
435netboot_info_free(struct netboot_info * * info_p)
436{
437 struct netboot_info * info = *info_p;
438
439 if (info) {
440 if (info->mount_point) {
91447636 441 kfree(info->mount_point, info->mount_point_length);
9bccf70c
A
442 }
443 if (info->server_name) {
91447636 444 kfree(info->server_name, info->server_name_length);
9bccf70c
A
445 }
446 if (info->image_path) {
91447636 447 kfree(info->image_path, info->image_path_length);
9bccf70c 448 }
91447636 449 kfree(info, sizeof(*info));
9bccf70c
A
450 }
451 *info_p = NULL;
452 return;
453}
454
455boolean_t
456netboot_iaddr(struct in_addr * iaddr_p)
457{
458 if (S_netboot_info_p == NULL)
459 return (FALSE);
460
461 *iaddr_p = S_netboot_info_p->client_ip;
462 return (TRUE);
463}
464
465boolean_t
466netboot_rootpath(struct in_addr * server_ip,
467 char * name, int name_len,
468 char * path, int path_len)
469{
470 if (S_netboot_info_p == NULL)
471 return (FALSE);
472
473 name[0] = '\0';
474 path[0] = '\0';
475
476 if (S_netboot_info_p->mount_point_length == 0) {
477 return (FALSE);
478 }
479 if (path_len < S_netboot_info_p->mount_point_length) {
480 printf("netboot: path too small %d < %d\n",
481 path_len, S_netboot_info_p->mount_point_length);
482 return (FALSE);
483 }
484 strcpy(path, S_netboot_info_p->mount_point);
485 strncpy(name, S_netboot_info_p->server_name, name_len);
486 *server_ip = S_netboot_info_p->server_ip;
487 return (TRUE);
488}
489
490
491static boolean_t
492get_ip_parameters(struct in_addr * iaddr_p, struct in_addr * netmask_p,
493 struct in_addr * router_p)
494{
495 void * entry;
496 const void * pkt;
497 int pkt_len;
498
499
500 entry = IOBSDRegistryEntryForDeviceTree("/chosen");
501 if (entry == NULL) {
502 return (FALSE);
503 }
504 pkt = IOBSDRegistryEntryGetData(entry, DHCP_RESPONSE, &pkt_len);
4a249263 505 if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) {
9bccf70c
A
506 printf("netboot: retrieving IP information from DHCP response\n");
507 }
508 else {
509 pkt = IOBSDRegistryEntryGetData(entry, BOOTP_RESPONSE, &pkt_len);
4a249263 510 if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) {
9bccf70c
A
511 printf("netboot: retrieving IP information from BOOTP response\n");
512 }
513 }
514 if (pkt != NULL) {
515 struct in_addr * ip;
516 int len;
517 dhcpol_t options;
518 struct dhcp * reply;
519
520 reply = (struct dhcp *)pkt;
521 (void)dhcpol_parse_packet(&options, reply, pkt_len, NULL);
522 *iaddr_p = reply->dp_yiaddr;
523 ip = (struct in_addr *)
524 dhcpol_find(&options,
525 dhcptag_subnet_mask_e, &len, NULL);
526 if (ip) {
527 *netmask_p = *ip;
528 }
529 ip = (struct in_addr *)
530 dhcpol_find(&options, dhcptag_router_e, &len, NULL);
531 if (ip) {
532 *router_p = *ip;
533 }
534 }
535 IOBSDRegistryEntryRelease(entry);
536 return (pkt != NULL);
537}
538
539static int
540inet_aifaddr(struct socket * so, char * name, const struct in_addr * addr,
541 const struct in_addr * mask,
542 const struct in_addr * broadcast)
543{
4a249263 544 struct sockaddr blank_sin;
9bccf70c
A
545 struct ifaliasreq ifra;
546
4a249263
A
547 bzero(&blank_sin, sizeof(blank_sin));
548 blank_sin.sa_len = sizeof(blank_sin);
549 blank_sin.sa_family = AF_INET;
550
9bccf70c
A
551 bzero(&ifra, sizeof(ifra));
552 strncpy(ifra.ifra_name, name, sizeof(ifra.ifra_name));
553 if (addr) {
554 ifra.ifra_addr = blank_sin;
555 ((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr = *addr;
556 }
557 if (mask) {
558 ifra.ifra_mask = blank_sin;
559 ((struct sockaddr_in *)&ifra.ifra_mask)->sin_addr = *mask;
560 }
561 if (broadcast) {
562 ifra.ifra_broadaddr = blank_sin;
563 ((struct sockaddr_in *)&ifra.ifra_broadaddr)->sin_addr = *broadcast;
564 }
565 return (ifioctl(so, SIOCAIFADDR, (caddr_t)&ifra, current_proc()));
566}
567
568static int
4a249263
A
569route_cmd(int cmd, struct in_addr d, struct in_addr g,
570 struct in_addr m, u_long more_flags)
9bccf70c
A
571{
572 struct sockaddr_in dst;
573 u_long flags = RTF_UP | RTF_STATIC;
574 struct sockaddr_in gw;
575 struct sockaddr_in mask;
576
4a249263 577 flags |= more_flags;
9bccf70c 578
4a249263 579 /* destination */
9bccf70c
A
580 bzero((caddr_t)&dst, sizeof(dst));
581 dst.sin_len = sizeof(dst);
582 dst.sin_family = AF_INET;
4a249263 583 dst.sin_addr = d;
9bccf70c
A
584
585 /* gateway */
586 bzero((caddr_t)&gw, sizeof(gw));
587 gw.sin_len = sizeof(gw);
588 gw.sin_family = AF_INET;
4a249263 589 gw.sin_addr = g;
9bccf70c 590
4a249263 591 /* mask */
9bccf70c
A
592 bzero(&mask, sizeof(mask));
593 mask.sin_len = sizeof(mask);
594 mask.sin_family = AF_INET;
4a249263 595 mask.sin_addr = m;
9bccf70c 596
4a249263 597 return (rtrequest(cmd, (struct sockaddr *)&dst, (struct sockaddr *)&gw,
9bccf70c
A
598 (struct sockaddr *)&mask, flags, NULL));
599}
600
4a249263
A
601static int
602default_route_add(struct in_addr router, boolean_t proxy_arp)
603{
604 u_long flags = 0;
605 struct in_addr zeroes = { 0 };
606
607 if (proxy_arp == FALSE) {
608 flags |= RTF_GATEWAY;
609 }
610 return (route_cmd(RTM_ADD, zeroes, router, zeroes, flags));
611}
612
613static int
614host_route_delete(struct in_addr host)
615{
616 struct in_addr zeroes = { 0 };
617
618 return (route_cmd(RTM_DELETE, host, zeroes, zeroes, RTF_HOST));
619}
620
9bccf70c 621static struct ifnet *
4a249263 622find_interface(void)
9bccf70c
A
623{
624 struct ifnet * ifp = NULL;
625
626 if (rootdevice[0]) {
91447636 627 ifp = ifunit(rootdevice);
9bccf70c
A
628 }
629 if (ifp == NULL) {
91447636
A
630 ifnet_head_lock_shared();
631 TAILQ_FOREACH(ifp, &ifnet_head, if_link)
632 if ((ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0)
633 break;
634 ifnet_head_done();
9bccf70c
A
635 }
636 return (ifp);
637}
638
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 };
647 struct proc * procp = current_proc();
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
655 /* find the interface */
656 ifp = find_interface();
657 if (ifp == NULL) {
658 printf("netboot: no suitable interface\n");
659 error = ENXIO;
660 goto failed;
661 }
662 sprintf(ifr.ifr_name, "%s%d", ifp->if_name, ifp->if_unit);
663 printf("netboot: using network interface '%s'\n", ifr.ifr_name);
664
665 /* bring it up */
666 if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0) {
667 printf("netboot: socreate, error=%d\n", error);
668 goto failed;
669 }
670 ifr.ifr_flags = ifp->if_flags | IFF_UP;
671 error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ifr, procp);
672 if (error) {
673 printf("netboot: SIFFLAGS, error=%d\n", error);
674 goto failed;
675 }
676
677 /* grab information from the registry */
678 if (get_ip_parameters(&iaddr, &netmask, &router) == FALSE) {
679 /* use BOOTP to retrieve IP address, netmask and router */
680 error = bootp(ifp, &iaddr, 32, &netmask, &router, procp);
681 if (error) {
682 printf("netboot: BOOTP failed %d\n", error);
683 goto failed;
684 }
685 }
686 printf("netboot: IP address " IP_FORMAT, IP_LIST(&iaddr));
687 if (netmask.s_addr) {
688 printf(" netmask " IP_FORMAT, IP_LIST(&netmask));
689 }
690 if (router.s_addr) {
691 printf(" router " IP_FORMAT, IP_LIST(&router));
692 }
693 printf("\n");
694 error = inet_aifaddr(so, ifr.ifr_name, &iaddr, &netmask, NULL);
695 if (error) {
696 printf("netboot: inet_aifaddr failed, %d\n", error);
697 goto failed;
698 }
699 if (router.s_addr == 0) {
700 /* enable proxy arp if we don't have a router */
701 router.s_addr = iaddr.s_addr;
702 }
4a249263
A
703 printf("netboot: adding default route " IP_FORMAT "\n",
704 IP_LIST(&router));
9bccf70c
A
705 error = default_route_add(router, router.s_addr == iaddr.s_addr);
706 if (error) {
707 printf("netboot: default_route_add failed %d\n", error);
708 }
709
710 soclose(so);
9bccf70c
A
711
712 S_netboot_info_p = netboot_info_init(iaddr);
713 switch (S_netboot_info_p->image_type) {
714 default:
715 case kNetBootImageTypeNFS:
4a249263
A
716 for (try = 1; TRUE; try++) {
717 error = nfs_mountroot();
718 if (error == 0) {
719 break;
720 }
721 printf("netboot: nfs_mountroot() attempt %u failed; "
722 "clearing ARP entry and trying again\n", try);
723 /*
724 * error is either EHOSTDOWN or EHOSTUNREACH, which likely means
725 * that the port we're plugged into has spanning tree enabled,
726 * and either the router or the server can't answer our ARP
727 * requests. Clear the incomplete ARP entry by removing the
728 * appropriate route, depending on the error code:
729 * EHOSTDOWN NFS server's route
730 * EHOSTUNREACH router's route
731 */
732 switch (error) {
733 default:
734 /* NOT REACHED */
735 case EHOSTDOWN:
736 /* remove the server's arp entry */
737 error = host_route_delete(S_netboot_info_p->server_ip);
738 if (error) {
739 printf("netboot: host_route_delete(" IP_FORMAT
740 ") failed %d\n",
741 IP_LIST(&S_netboot_info_p->server_ip), error);
742 }
743 break;
744 case EHOSTUNREACH:
745 error = host_route_delete(router);
746 if (error) {
747 printf("netboot: host_route_delete(" IP_FORMAT
748 ") failed %d\n", IP_LIST(&router), error);
749 }
750 break;
751 }
752 }
9bccf70c
A
753 break;
754 case kNetBootImageTypeHTTP:
755 error = netboot_setup(procp);
756 break;
757 }
758 if (error == 0) {
759 S_netboot = 1;
760 }
761 else {
762 S_netboot = 0;
763 }
764 return (error);
765failed:
766 if (so != NULL) {
767 soclose(so);
768 }
9bccf70c
A
769 return (error);
770}
771
772int
773netboot_setup(struct proc * p)
774{
775 dev_t dev;
776 int error = 0;
777
778 if (S_netboot_info_p == NULL
779 || S_netboot_info_p->image_path == NULL) {
780 goto done;
781 }
782 if (S_netboot_info_p->use_hdix) {
783 printf("netboot_setup: calling di_root_image\n");
784 error = di_root_image(S_netboot_info_p->image_path,
785 rootdevice, &dev);
786 if (error) {
787 printf("netboot_setup: di_root_image: failed %d\n", error);
788 goto done;
789 }
790 }
791 else {
792 printf("netboot_setup: calling vndevice_root_image\n");
793 error = vndevice_root_image(S_netboot_info_p->image_path,
794 rootdevice, &dev);
795 if (error) {
796 printf("netboot_setup: vndevice_root_image: failed %d\n", error);
797 goto done;
798 }
799 }
800 rootdev = dev;
801 mountroot = NULL;
802 printf("netboot: root device 0x%x\n", rootdev);
803 error = vfs_mountroot();
804 if (error == 0 && rootvnode != NULL) {
805 struct vnode *tvp;
806 struct vnode *newdp;
91447636
A
807 struct vfs_context context;
808
809 context.vc_proc = p;
810 context.vc_ucred = proc_ucred(p); /* XXX kauth_cred_get() ??? proxy */
9bccf70c
A
811
812 /* Get the vnode for '/'. Set fdp->fd_fd.fd_cdir to reference it. */
91447636 813 if (VFS_ROOT(mountlist.tqh_last, &newdp, &context))
9bccf70c 814 panic("netboot_setup: cannot find root vnode");
91447636
A
815 vnode_ref(newdp);
816 vnode_put(newdp);
9bccf70c 817 tvp = rootvnode;
91447636 818 vnode_rele(tvp);
9bccf70c
A
819 filedesc0.fd_cdir = newdp;
820 rootvnode = newdp;
91447636
A
821 mount_list_lock();
822 TAILQ_REMOVE(&mountlist, TAILQ_FIRST(&mountlist), mnt_list);
823 mount_list_unlock();
824 mountlist.tqh_first->mnt_flag |= MNT_ROOTFS;
9bccf70c
A
825 }
826 done:
827 netboot_info_free(&S_netboot_info_p);
828 return (error);
829}
830
831int
4a249263 832netboot_root(void)
9bccf70c
A
833{
834 return (S_netboot);
835}