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