xref: /xnu-8796.101.5/bsd/kern/netboot.c (revision aca3beaa3dfbd42498b42c5e5ce20a938e6554e5)
1 /*
2  * Copyright (c) 2001-2019 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
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
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 /*
30  * History:
31  * 14 December, 2001	Dieter Siegmund ([email protected])
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>
39 #include <sys/proc_internal.h>
40 #include <sys/mount_internal.h>
41 #include <sys/mbuf.h>
42 #include <sys/filedesc.h>
43 #include <sys/vnode_internal.h>
44 #include <sys/malloc.h>
45 #include <sys/socket.h>
46 #include <sys/socketvar.h>
47 #include <sys/reboot.h>
48 #include <sys/kauth.h>
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>
56 
57 #include <kern/kern_types.h>
58 #include <kern/kalloc.h>
59 #include <sys/netboot.h>
60 #include <sys/imageboot.h>
61 #include <pexpert/pexpert.h>
62 
63 extern int (*mountroot)(void);
64 
65 extern unsigned char    rootdevice[];
66 
67 static int                      S_netboot = 0;
68 static struct netboot_info *    S_netboot_info_p;
69 
70 void *
71 IOBSDRegistryEntryForDeviceTree(const char * path);
72 
73 void
74 IOBSDRegistryEntryRelease(void * entry);
75 
76 const void *
77 IOBSDRegistryEntryGetData(void * entry, const char * property_name,
78     int * packet_length);
79 
80 #define BOOTP_RESPONSE  "bootp-response"
81 #define BSDP_RESPONSE   "bsdp-response"
82 #define DHCP_RESPONSE   "dhcp-response"
83 
84 #define IP_FORMAT       "%d.%d.%d.%d"
85 #define IP_CH(ip)       ((u_char *)ip)
86 #define IP_LIST(ip)     IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
87 
88 #define kNetBootRootPathPrefixNFS       "nfs:"
89 #define kNetBootRootPathPrefixHTTP      "http:"
90 
91 typedef enum {
92 	kNetBootImageTypeUnknown = 0,
93 	kNetBootImageTypeNFS = 1, // Deprecated
94 	kNetBootImageTypeHTTP = 2,
95 } NetBootImageType;
96 
97 struct netboot_info {
98 	struct in_addr      client_ip;
99 	struct in_addr      server_ip;
100 	char *              server_name;
101 	size_t              server_name_length;
102 	char *              mount_point;
103 	size_t              mount_point_length;
104 	char *              image_path;
105 	size_t              image_path_length;
106 	NetBootImageType    image_type;
107 	char *              second_image_path;
108 	size_t              second_image_path_length;
109 };
110 
111 /*
112  * Function: parse_booter_path
113  * Purpose:
114  *   Parse a string of the form:
115  *        "<IP>:<host>:<mount>[:<image_path>]"
116  *   into the given ip address, host, mount point, and optionally, image_path.
117  *
118  * Note:
119  *   The passed in string is modified i.e. ':' is replaced by '\0'.
120  * Example:
121  *   "17.202.16.17:seaport:/release/.images/Image9/CurrentHera"
122  */
123 static __inline__ boolean_t
parse_booter_path(char * path,struct in_addr * iaddr_p,char const ** host,char ** mount_dir,char ** image_path)124 parse_booter_path(char * path, struct in_addr * iaddr_p, char const * * host,
125     char * * mount_dir, char * * image_path)
126 {
127 	char *      start;
128 	char *      colon;
129 
130 	/* IP address */
131 	start = path;
132 	colon = strchr(start, ':');
133 	if (colon == NULL) {
134 		return FALSE;
135 	}
136 	*colon = '\0';
137 	if (inet_aton(start, iaddr_p) != 1) {
138 		return FALSE;
139 	}
140 
141 	/* host */
142 	start = colon + 1;
143 	colon = strchr(start, ':');
144 	if (colon == NULL) {
145 		return FALSE;
146 	}
147 	*colon = '\0';
148 	*host = start;
149 
150 	/* mount */
151 	start = colon + 1;
152 	colon = strchr(start, ':');
153 	*mount_dir = start;
154 	if (colon == NULL) {
155 		*image_path = NULL;
156 	} else {
157 		/* image path */
158 		*colon = '\0';
159 		start = colon + 1;
160 		*image_path = start;
161 	}
162 	return TRUE;
163 }
164 
165 /*
166  * Function: find_colon
167  * Purpose:
168  *   Find the next unescaped instance of the colon character.
169  *   If a colon is escaped (preceded by a backslash '\' character),
170  *   shift the string over by one character to overwrite the backslash.
171  */
172 static __inline__ char *
find_colon(char * str)173 find_colon(char * str)
174 {
175 	char * start = str;
176 	char * colon;
177 
178 	while ((colon = strchr(start, ':')) != NULL) {
179 		char * dst;
180 		char * src;
181 
182 		if (colon == start) {
183 			break;
184 		}
185 		if (colon[-1] != '\\') {
186 			break;
187 		}
188 		for (dst = colon - 1, src = colon; *dst != '\0'; dst++, src++) {
189 			*dst = *src;
190 		}
191 		start = colon;
192 	}
193 	return colon;
194 }
195 
196 /*
197  * Function: parse_netboot_path
198  * Purpose:
199  *   Parse a string of the form:
200  *        "nfs:<IP>:<mount>[:<image_path>]"
201  *   into the given ip address, host, mount point, and optionally, image_path.
202  * Notes:
203  * - the passed in string is modified i.e. ':' is replaced by '\0'
204  * - literal colons must be escaped with a backslash
205  *
206  * Examples:
207  * nfs:17.202.42.112:/Library/NetBoot/NetBootSP0:Jaguar/Jaguar.dmg
208  * nfs:17.202.42.112:/Volumes/Foo\:/Library/NetBoot/NetBootSP0:Jaguar/Jaguar.dmg
209  */
210 static __inline__ boolean_t
parse_netboot_path(char * path,struct in_addr * iaddr_p,char const ** host,char ** mount_dir,char ** image_path)211 parse_netboot_path(char * path, struct in_addr * iaddr_p, char const * * host,
212     char * * mount_dir, char * * image_path)
213 {
214 	static char tmp[MAX_IPv4_STR_LEN]; /* Danger - not thread safe */
215 	char *      start;
216 	char *      colon;
217 
218 	if (strncmp(path, kNetBootRootPathPrefixNFS,
219 	    strlen(kNetBootRootPathPrefixNFS)) != 0) {
220 		return FALSE;
221 	}
222 
223 	/* IP address */
224 	start = path + strlen(kNetBootRootPathPrefixNFS);
225 	colon = strchr(start, ':');
226 	if (colon == NULL) {
227 		return FALSE;
228 	}
229 	*colon = '\0';
230 	if (inet_aton(start, iaddr_p) != 1) {
231 		return FALSE;
232 	}
233 
234 	/* mount point */
235 	start = colon + 1;
236 	colon = find_colon(start);
237 	*mount_dir = start;
238 	if (colon == NULL) {
239 		*image_path = NULL;
240 	} else {
241 		/* image path */
242 		*colon = '\0';
243 		start = colon + 1;
244 		(void)find_colon(start);
245 		*image_path = start;
246 	}
247 	*host = inet_ntop(AF_INET, iaddr_p, tmp, sizeof(tmp));
248 	return TRUE;
249 }
250 
251 static boolean_t
parse_image_path(char * path,struct in_addr * iaddr_p,char const ** host,char ** mount_dir,char ** image_path)252 parse_image_path(char * path, struct in_addr * iaddr_p, char const * * host,
253     char * * mount_dir, char * * image_path)
254 {
255 	if (path[0] >= '0' && path[0] <= '9') {
256 		return parse_booter_path(path, iaddr_p, host, mount_dir,
257 		           image_path);
258 	}
259 	return parse_netboot_path(path, iaddr_p, host, mount_dir,
260 	           image_path);
261 }
262 
263 static boolean_t
get_root_path(char * root_path)264 get_root_path(char * root_path)
265 {
266 	void *              entry;
267 	boolean_t           found = FALSE;
268 	const void *        pkt;
269 	int                 pkt_len;
270 
271 	entry = IOBSDRegistryEntryForDeviceTree("/chosen");
272 	if (entry == NULL) {
273 		return FALSE;
274 	}
275 	pkt = IOBSDRegistryEntryGetData(entry, BSDP_RESPONSE, &pkt_len);
276 	if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) {
277 		printf("netboot: retrieving root path from BSDP response\n");
278 	} else {
279 		pkt = IOBSDRegistryEntryGetData(entry, BOOTP_RESPONSE,
280 		    &pkt_len);
281 		if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) {
282 			printf("netboot: retrieving root path from BOOTP response\n");
283 		}
284 	}
285 	if (pkt != NULL) {
286 		int                     len;
287 		dhcpol_t                options;
288 		const char *            path;
289 		const struct dhcp *     reply;
290 
291 		reply = (const struct dhcp *)pkt;
292 		(void)dhcpol_parse_packet(&options, reply, pkt_len);
293 
294 		path = (const char *)dhcpol_find(&options,
295 		    dhcptag_root_path_e, &len, NULL);
296 		if (path) {
297 			memcpy(root_path, path, len);
298 			root_path[len] = '\0';
299 			found = TRUE;
300 		}
301 	}
302 	IOBSDRegistryEntryRelease(entry);
303 	return found;
304 }
305 
306 static void
save_path(char ** str_p,size_t * length_p,char * path)307 save_path(char * * str_p, size_t * length_p, char * path)
308 {
309 	*length_p = strlen(path) + 1;
310 	*str_p = kalloc_data(*length_p, Z_WAITOK);
311 	strlcpy(*str_p, path, *length_p);
312 	return;
313 }
314 
315 static struct netboot_info *
netboot_info_init(struct in_addr iaddr)316 netboot_info_init(struct in_addr iaddr)
317 {
318 	boolean_t                   have_root_path = FALSE;
319 	struct netboot_info *       info = NULL;
320 	char *                      root_path = NULL;
321 
322 	info = (struct netboot_info *)kalloc_type(struct netboot_info, Z_WAITOK | Z_ZERO);
323 	info->client_ip = iaddr;
324 	info->image_type = kNetBootImageTypeUnknown;
325 
326 	/* check for a booter-specified path then a NetBoot path */
327 	root_path = zalloc(ZV_NAMEI);
328 
329 	if (PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) == TRUE
330 	    || PE_parse_boot_argn("rp", root_path, MAXPATHLEN) == TRUE
331 	    || PE_parse_boot_argn("rootpath", root_path, MAXPATHLEN) == TRUE) {
332 		if (imageboot_format_is_valid(root_path)) {
333 			printf("netboot_info_init: rp0='%s' isn't a network path,"
334 			    " ignoring\n", root_path);
335 		} else {
336 			have_root_path = TRUE;
337 		}
338 	}
339 	if (have_root_path == FALSE) {
340 		have_root_path = get_root_path(root_path);
341 	}
342 	if (have_root_path) {
343 		const char * server_name = NULL;
344 		char * mount_point = NULL;
345 		char * image_path = NULL;
346 		struct in_addr  server_ip;
347 
348 		if (parse_image_path(root_path, &server_ip, &server_name,
349 		    &mount_point, &image_path)) {
350 			/* kNetBootImageTypeNFS is deprecated */
351 			printf("netboot: NFS boot is deprecated\n");
352 		} else if (strncmp(root_path, kNetBootRootPathPrefixHTTP,
353 		    strlen(kNetBootRootPathPrefixHTTP)) == 0) {
354 			info->image_type = kNetBootImageTypeHTTP;
355 			save_path(&info->image_path, &info->image_path_length,
356 			    root_path);
357 			printf("netboot: HTTP URL %s\n", info->image_path);
358 		} else {
359 			printf("netboot: root path uses unrecognized format\n");
360 		}
361 
362 		/* check for image-within-image */
363 		if (info->image_path != NULL) {
364 			if (PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN)
365 			    || PE_parse_boot_argn("rp1", root_path, MAXPATHLEN)) {
366 				/* rp1/root-dmg is the second-level image */
367 				save_path(&info->second_image_path, &info->second_image_path_length,
368 				    root_path);
369 			}
370 		}
371 		if (info->second_image_path != NULL) {
372 			printf("netboot: nested image %s\n", info->second_image_path);
373 		}
374 	}
375 	zfree(ZV_NAMEI, root_path);
376 	return info;
377 }
378 
379 static void
netboot_info_free(struct netboot_info ** info_p)380 netboot_info_free(struct netboot_info * * info_p)
381 {
382 	struct netboot_info * info = *info_p;
383 
384 	if (info) {
385 		kfree_data(info->mount_point, info->mount_point_length);
386 		kfree_data(info->server_name, info->server_name_length);
387 		kfree_data(info->image_path, info->image_path_length);
388 		kfree_data(info->second_image_path,
389 		    info->second_image_path_length);
390 		kfree_type(struct netboot_info, info);
391 	}
392 	*info_p = NULL;
393 }
394 
395 boolean_t
netboot_iaddr(struct in_addr * iaddr_p)396 netboot_iaddr(struct in_addr * iaddr_p)
397 {
398 	if (S_netboot_info_p == NULL) {
399 		return FALSE;
400 	}
401 
402 	*iaddr_p = S_netboot_info_p->client_ip;
403 	return TRUE;
404 }
405 
406 boolean_t
netboot_rootpath(struct in_addr * server_ip,char * name,size_t name_len,char * path,size_t path_len)407 netboot_rootpath(struct in_addr * server_ip,
408     char * name, size_t name_len,
409     char * path, size_t path_len)
410 {
411 	if (S_netboot_info_p == NULL) {
412 		return FALSE;
413 	}
414 
415 	name[0] = '\0';
416 	path[0] = '\0';
417 
418 	if (S_netboot_info_p->mount_point_length == 0) {
419 		return FALSE;
420 	}
421 	if (path_len < S_netboot_info_p->mount_point_length) {
422 		printf("netboot: path too small %zu < %zu\n",
423 		    path_len, S_netboot_info_p->mount_point_length);
424 		return FALSE;
425 	}
426 	strlcpy(path, S_netboot_info_p->mount_point, path_len);
427 	strlcpy(name, S_netboot_info_p->server_name, name_len);
428 	*server_ip = S_netboot_info_p->server_ip;
429 	return TRUE;
430 }
431 
432 
433 static boolean_t
get_ip_parameters(struct in_addr * iaddr_p,struct in_addr * netmask_p,struct in_addr * router_p)434 get_ip_parameters(struct in_addr * iaddr_p, struct in_addr * netmask_p,
435     struct in_addr * router_p)
436 {
437 	void *              entry;
438 	const void *        pkt;
439 	int                 pkt_len;
440 
441 
442 	entry = IOBSDRegistryEntryForDeviceTree("/chosen");
443 	if (entry == NULL) {
444 		return FALSE;
445 	}
446 	pkt = IOBSDRegistryEntryGetData(entry, DHCP_RESPONSE, &pkt_len);
447 	if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) {
448 		printf("netboot: retrieving IP information from DHCP response\n");
449 	} else {
450 		pkt = IOBSDRegistryEntryGetData(entry, BOOTP_RESPONSE, &pkt_len);
451 		if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) {
452 			printf("netboot: retrieving IP information from BOOTP response\n");
453 		}
454 	}
455 	if (pkt != NULL) {
456 		const struct in_addr *  ip;
457 		int                     len;
458 		dhcpol_t                options;
459 		const struct dhcp *     reply;
460 
461 		reply = (const struct dhcp *)pkt;
462 		(void)dhcpol_parse_packet(&options, reply, pkt_len);
463 		*iaddr_p = reply->dp_yiaddr;
464 		ip = (const struct in_addr *)
465 		    dhcpol_find(&options,
466 		    dhcptag_subnet_mask_e, &len, NULL);
467 		if (ip) {
468 			*netmask_p = *ip;
469 		}
470 		ip = (const struct in_addr *)
471 		    dhcpol_find(&options, dhcptag_router_e, &len, NULL);
472 		if (ip) {
473 			*router_p = *ip;
474 		}
475 	}
476 	IOBSDRegistryEntryRelease(entry);
477 	return pkt != NULL;
478 }
479 
480 static int
route_cmd(int cmd,struct in_addr d,struct in_addr g,struct in_addr m,uint32_t more_flags,unsigned int ifscope)481 route_cmd(int cmd, struct in_addr d, struct in_addr g,
482     struct in_addr m, uint32_t more_flags, unsigned int ifscope)
483 {
484 	struct sockaddr_in          dst;
485 	int                         error;
486 	uint32_t                    flags = RTF_UP | RTF_STATIC;
487 	struct sockaddr_in          gw;
488 	struct sockaddr_in          mask;
489 
490 	flags |= more_flags;
491 
492 	/* destination */
493 	bzero((caddr_t)&dst, sizeof(dst));
494 	dst.sin_len = sizeof(dst);
495 	dst.sin_family = AF_INET;
496 	dst.sin_addr = d;
497 
498 	/* gateway */
499 	bzero((caddr_t)&gw, sizeof(gw));
500 	gw.sin_len = sizeof(gw);
501 	gw.sin_family = AF_INET;
502 	gw.sin_addr = g;
503 
504 	/* mask */
505 	bzero(&mask, sizeof(mask));
506 	mask.sin_len = sizeof(mask);
507 	mask.sin_family = AF_INET;
508 	mask.sin_addr = m;
509 
510 	error = rtrequest_scoped(cmd, (struct sockaddr *)&dst,
511 	    (struct sockaddr *)&gw, (struct sockaddr *)&mask, flags, NULL, ifscope);
512 
513 	return error;
514 }
515 
516 static int
default_route_add(struct in_addr router,boolean_t proxy_arp)517 default_route_add(struct in_addr router, boolean_t proxy_arp)
518 {
519 	uint32_t                    flags = 0;
520 	struct in_addr              zeroes = { .s_addr = 0 };
521 
522 	if (proxy_arp == FALSE) {
523 		flags |= RTF_GATEWAY;
524 	}
525 	return route_cmd(RTM_ADD, zeroes, router, zeroes, flags, IFSCOPE_NONE);
526 }
527 
528 static struct ifnet *
find_interface(void)529 find_interface(void)
530 {
531 	struct ifnet *              ifp = NULL;
532 
533 	dlil_if_lock();
534 	if (rootdevice[0]) {
535 		ifp = ifunit((char *)rootdevice);
536 	}
537 	if (ifp == NULL) {
538 		ifnet_head_lock_shared();
539 		TAILQ_FOREACH(ifp, &ifnet_head, if_link)
540 		if ((ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) == 0) {
541 			break;
542 		}
543 		ifnet_head_done();
544 	}
545 	dlil_if_unlock();
546 	return ifp;
547 }
548 
549 static const struct sockaddr_in blank_sin = {
550 	.sin_len = sizeof(struct sockaddr_in),
551 	.sin_family = AF_INET,
552 	.sin_port = 0,
553 	.sin_addr = { .s_addr = 0 },
554 	.sin_zero = { 0, 0, 0, 0, 0, 0, 0, 0 }
555 };
556 
557 static int
inet_aifaddr(struct socket * so,const char * name,const struct in_addr * addr,const struct in_addr * mask,const struct in_addr * broadcast)558 inet_aifaddr(struct socket * so, const char * name,
559     const struct in_addr * addr,
560     const struct in_addr * mask,
561     const struct in_addr * broadcast)
562 {
563 	struct ifaliasreq   ifra;
564 
565 	bzero(&ifra, sizeof(ifra));
566 	strlcpy(ifra.ifra_name, name, sizeof(ifra.ifra_name));
567 	if (addr) {
568 		*((struct sockaddr_in *)(void *)&ifra.ifra_addr) = blank_sin;
569 		((struct sockaddr_in *)(void *)&ifra.ifra_addr)->sin_addr = *addr;
570 	}
571 	if (mask) {
572 		*((struct sockaddr_in *)(void *)&ifra.ifra_mask) = blank_sin;
573 		((struct sockaddr_in *)(void *)&ifra.ifra_mask)->sin_addr = *mask;
574 	}
575 	if (broadcast) {
576 		*((struct sockaddr_in *)(void *)&ifra.ifra_broadaddr) = blank_sin;
577 		((struct sockaddr_in *)(void *)&ifra.ifra_broadaddr)->sin_addr = *broadcast;
578 	}
579 	return ifioctl(so, SIOCAIFADDR, (caddr_t)&ifra, current_proc());
580 }
581 
582 
583 int
netboot_mountroot(void)584 netboot_mountroot(void)
585 {
586 	int                         error = 0;
587 	struct in_addr              iaddr = { .s_addr = 0 };
588 	struct ifreq                ifr;
589 	struct ifnet *              ifp;
590 	struct in_addr              netmask = { .s_addr = 0 };
591 	proc_t                      procp = current_proc();
592 	struct in_addr              router = { .s_addr = 0 };
593 	struct socket *             so = NULL;
594 
595 	bzero(&ifr, sizeof(ifr));
596 
597 	/* find the interface */
598 	ifp = find_interface();
599 	if (ifp == NULL) {
600 		printf("netboot: no suitable interface\n");
601 		error = ENXIO;
602 		goto failed;
603 	}
604 	snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", if_name(ifp));
605 	printf("netboot: using network interface '%s'\n", ifr.ifr_name);
606 
607 	/* bring it up */
608 	if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0) {
609 		printf("netboot: socreate, error=%d\n", error);
610 		goto failed;
611 	}
612 	ifr.ifr_flags = ifp->if_flags | IFF_UP;
613 	error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ifr, procp);
614 	if (error) {
615 		printf("netboot: SIFFLAGS, error=%d\n", error);
616 		goto failed;
617 	}
618 
619 	/* grab information from the registry */
620 	if (get_ip_parameters(&iaddr, &netmask, &router) == FALSE) {
621 		printf("netboot: can't retrieve IP parameters\n");
622 		goto failed;
623 	}
624 	OS_ANALYZER_SUPPRESS("12641116") printf("netboot: IP address " IP_FORMAT, IP_LIST(&iaddr));
625 	if (netmask.s_addr) {
626 		printf(" netmask " IP_FORMAT, IP_LIST(&netmask));
627 	}
628 	if (router.s_addr) {
629 		printf(" router " IP_FORMAT, IP_LIST(&router));
630 	}
631 	printf("\n");
632 	error = inet_aifaddr(so, ifr.ifr_name, &iaddr, &netmask, NULL);
633 	if (error) {
634 		printf("netboot: inet_aifaddr failed, %d\n", error);
635 		goto failed;
636 	}
637 	if (router.s_addr == 0) {
638 		/* enable proxy arp if we don't have a router */
639 		router.s_addr = iaddr.s_addr;
640 	}
641 	printf("netboot: adding default route " IP_FORMAT "\n",
642 	    IP_LIST(&router));
643 	error = default_route_add(router, router.s_addr == iaddr.s_addr);
644 	if (error) {
645 		printf("netboot: default_route_add failed %d\n", error);
646 	}
647 
648 	soclose(so);
649 
650 	S_netboot_info_p = netboot_info_init(iaddr);
651 	switch (S_netboot_info_p->image_type) {
652 	default:
653 	case kNetBootImageTypeNFS:
654 		/* kNetBootImageTypeNFS is deprecated */
655 		error = ENOTSUP;
656 		break;
657 	case kNetBootImageTypeHTTP:
658 		error = netboot_setup();
659 		break;
660 	}
661 	if (error == 0) {
662 		S_netboot = 1;
663 	} else {
664 		S_netboot = 0;
665 	}
666 	return error;
667 failed:
668 	if (so != NULL) {
669 		soclose(so);
670 	}
671 	return error;
672 }
673 
674 int
netboot_setup(void)675 netboot_setup(void)
676 {
677 	int         error = 0;
678 
679 	if (S_netboot_info_p == NULL
680 	    || S_netboot_info_p->image_path == NULL) {
681 		goto done;
682 	}
683 	printf("netboot_setup: calling imageboot_mount_image\n");
684 	error = imageboot_mount_image(S_netboot_info_p->image_path, -1, IMAGEBOOT_DMG);
685 	if (error != 0) {
686 		printf("netboot: failed to mount root image, %d\n", error);
687 	} else if (S_netboot_info_p->second_image_path != NULL) {
688 		error = imageboot_mount_image(S_netboot_info_p->second_image_path, 0, IMAGEBOOT_DMG);
689 		if (error != 0) {
690 			printf("netboot: failed to mount second root image, %d\n", error);
691 		}
692 	}
693 
694 done:
695 	netboot_info_free(&S_netboot_info_p);
696 	return error;
697 }
698 
699 int
netboot_root(void)700 netboot_root(void)
701 {
702 	return S_netboot;
703 }
704