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