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