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