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