703 lines
18 KiB
C
703 lines
18 KiB
C
/*
|
|
* Copyright (c) 2001-2019 Apple Inc. All rights reserved.
|
|
*
|
|
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
|
|
*
|
|
* This file contains Original Code and/or Modifications of Original Code
|
|
* as defined in and that are subject to the Apple Public Source License
|
|
* Version 2.0 (the 'License'). You may not use this file except in
|
|
* compliance with the License. The rights granted to you under the License
|
|
* may not be used to create, or enable the creation or redistribution of,
|
|
* unlawful or unlicensed copies of an Apple operating system, or to
|
|
* circumvent, violate, or enable the circumvention or violation of, any
|
|
* terms of an Apple operating system software license agreement.
|
|
*
|
|
* Please obtain a copy of the License at
|
|
* http://www.opensource.apple.com/apsl/ and read it before using this file.
|
|
*
|
|
* The Original Code and all software distributed under the License are
|
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
|
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
|
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
|
* Please see the License for the specific language governing rights and
|
|
* limitations under the License.
|
|
*
|
|
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
|
|
*/
|
|
|
|
/*
|
|
* History:
|
|
* 14 December, 2001 Dieter Siegmund (dieter@apple.com)
|
|
* - created
|
|
*/
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/proc_internal.h>
|
|
#include <sys/mount_internal.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/filedesc.h>
|
|
#include <sys/vnode_internal.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/socketvar.h>
|
|
#include <sys/reboot.h>
|
|
#include <sys/kauth.h>
|
|
#include <net/if.h>
|
|
#include <net/if_dl.h>
|
|
#include <net/if_types.h>
|
|
#include <net/route.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/if_ether.h>
|
|
#include <netinet/dhcp_options.h>
|
|
|
|
#include <kern/kern_types.h>
|
|
#include <kern/kalloc.h>
|
|
#include <sys/netboot.h>
|
|
#include <sys/imageboot.h>
|
|
#include <pexpert/pexpert.h>
|
|
|
|
extern int (*mountroot)(void);
|
|
|
|
extern unsigned char rootdevice[];
|
|
|
|
static int S_netboot = 0;
|
|
static struct netboot_info * S_netboot_info_p;
|
|
|
|
void *
|
|
IOBSDRegistryEntryForDeviceTree(const char * path);
|
|
|
|
void
|
|
IOBSDRegistryEntryRelease(void * entry);
|
|
|
|
const void *
|
|
IOBSDRegistryEntryGetData(void * entry, const char * property_name,
|
|
int * packet_length);
|
|
|
|
#define BOOTP_RESPONSE "bootp-response"
|
|
#define BSDP_RESPONSE "bsdp-response"
|
|
#define DHCP_RESPONSE "dhcp-response"
|
|
|
|
#define IP_FORMAT "%d.%d.%d.%d"
|
|
#define IP_CH(ip) ((u_char *)ip)
|
|
#define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
|
|
|
|
#define kNetBootRootPathPrefixNFS "nfs:"
|
|
#define kNetBootRootPathPrefixHTTP "http:"
|
|
|
|
typedef enum {
|
|
kNetBootImageTypeUnknown = 0,
|
|
kNetBootImageTypeNFS = 1, // Deprecated
|
|
kNetBootImageTypeHTTP = 2,
|
|
} NetBootImageType;
|
|
|
|
struct netboot_info {
|
|
struct in_addr client_ip;
|
|
struct in_addr server_ip;
|
|
char * server_name;
|
|
size_t server_name_length;
|
|
char * mount_point;
|
|
size_t mount_point_length;
|
|
char * image_path;
|
|
size_t image_path_length;
|
|
NetBootImageType image_type;
|
|
char * second_image_path;
|
|
size_t second_image_path_length;
|
|
};
|
|
|
|
/*
|
|
* Function: parse_booter_path
|
|
* Purpose:
|
|
* Parse a string of the form:
|
|
* "<IP>:<host>:<mount>[:<image_path>]"
|
|
* into the given ip address, host, mount point, and optionally, image_path.
|
|
*
|
|
* Note:
|
|
* The passed in string is modified i.e. ':' is replaced by '\0'.
|
|
* Example:
|
|
* "17.202.16.17:seaport:/release/.images/Image9/CurrentHera"
|
|
*/
|
|
static __inline__ boolean_t
|
|
parse_booter_path(char * path, struct in_addr * iaddr_p, char const * * host,
|
|
char * * mount_dir, char * * image_path)
|
|
{
|
|
char * start;
|
|
char * colon;
|
|
|
|
/* IP address */
|
|
start = path;
|
|
colon = strchr(start, ':');
|
|
if (colon == NULL) {
|
|
return FALSE;
|
|
}
|
|
*colon = '\0';
|
|
if (inet_aton(start, iaddr_p) != 1) {
|
|
return FALSE;
|
|
}
|
|
|
|
/* host */
|
|
start = colon + 1;
|
|
colon = strchr(start, ':');
|
|
if (colon == NULL) {
|
|
return FALSE;
|
|
}
|
|
*colon = '\0';
|
|
*host = start;
|
|
|
|
/* mount */
|
|
start = colon + 1;
|
|
colon = strchr(start, ':');
|
|
*mount_dir = start;
|
|
if (colon == NULL) {
|
|
*image_path = NULL;
|
|
} else {
|
|
/* image path */
|
|
*colon = '\0';
|
|
start = colon + 1;
|
|
*image_path = start;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Function: find_colon
|
|
* Purpose:
|
|
* Find the next unescaped instance of the colon character.
|
|
* If a colon is escaped (preceded by a backslash '\' character),
|
|
* shift the string over by one character to overwrite the backslash.
|
|
*/
|
|
static __inline__ char *
|
|
find_colon(char * str)
|
|
{
|
|
char * start = str;
|
|
char * colon;
|
|
|
|
while ((colon = strchr(start, ':')) != NULL) {
|
|
char * dst;
|
|
char * src;
|
|
|
|
if (colon == start) {
|
|
break;
|
|
}
|
|
if (colon[-1] != '\\') {
|
|
break;
|
|
}
|
|
for (dst = colon - 1, src = colon; *dst != '\0'; dst++, src++) {
|
|
*dst = *src;
|
|
}
|
|
start = colon;
|
|
}
|
|
return colon;
|
|
}
|
|
|
|
/*
|
|
* Function: parse_netboot_path
|
|
* Purpose:
|
|
* Parse a string of the form:
|
|
* "nfs:<IP>:<mount>[:<image_path>]"
|
|
* into the given ip address, host, mount point, and optionally, image_path.
|
|
* Notes:
|
|
* - the passed in string is modified i.e. ':' is replaced by '\0'
|
|
* - literal colons must be escaped with a backslash
|
|
*
|
|
* Examples:
|
|
* nfs:17.202.42.112:/Library/NetBoot/NetBootSP0:Jaguar/Jaguar.dmg
|
|
* nfs:17.202.42.112:/Volumes/Foo\:/Library/NetBoot/NetBootSP0:Jaguar/Jaguar.dmg
|
|
*/
|
|
static __inline__ boolean_t
|
|
parse_netboot_path(char * path, struct in_addr * iaddr_p, char const * * host,
|
|
char * * mount_dir, char * * image_path)
|
|
{
|
|
static char tmp[MAX_IPv4_STR_LEN]; /* Danger - not thread safe */
|
|
char * start;
|
|
char * colon;
|
|
|
|
if (strncmp(path, kNetBootRootPathPrefixNFS,
|
|
strlen(kNetBootRootPathPrefixNFS)) != 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
/* IP address */
|
|
start = path + strlen(kNetBootRootPathPrefixNFS);
|
|
colon = strchr(start, ':');
|
|
if (colon == NULL) {
|
|
return FALSE;
|
|
}
|
|
*colon = '\0';
|
|
if (inet_aton(start, iaddr_p) != 1) {
|
|
return FALSE;
|
|
}
|
|
|
|
/* mount point */
|
|
start = colon + 1;
|
|
colon = find_colon(start);
|
|
*mount_dir = start;
|
|
if (colon == NULL) {
|
|
*image_path = NULL;
|
|
} else {
|
|
/* image path */
|
|
*colon = '\0';
|
|
start = colon + 1;
|
|
(void)find_colon(start);
|
|
*image_path = start;
|
|
}
|
|
*host = inet_ntop(AF_INET, iaddr_p, tmp, sizeof(tmp));
|
|
return TRUE;
|
|
}
|
|
|
|
static boolean_t
|
|
parse_image_path(char * path, struct in_addr * iaddr_p, char const * * host,
|
|
char * * mount_dir, char * * image_path)
|
|
{
|
|
if (path[0] >= '0' && path[0] <= '9') {
|
|
return parse_booter_path(path, iaddr_p, host, mount_dir,
|
|
image_path);
|
|
}
|
|
return parse_netboot_path(path, iaddr_p, host, mount_dir,
|
|
image_path);
|
|
}
|
|
|
|
static boolean_t
|
|
get_root_path(char * root_path)
|
|
{
|
|
void * entry;
|
|
boolean_t found = FALSE;
|
|
const void * pkt;
|
|
int pkt_len;
|
|
|
|
entry = IOBSDRegistryEntryForDeviceTree("/chosen");
|
|
if (entry == NULL) {
|
|
return FALSE;
|
|
}
|
|
pkt = IOBSDRegistryEntryGetData(entry, BSDP_RESPONSE, &pkt_len);
|
|
if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) {
|
|
printf("netboot: retrieving root path from BSDP response\n");
|
|
} else {
|
|
pkt = IOBSDRegistryEntryGetData(entry, BOOTP_RESPONSE,
|
|
&pkt_len);
|
|
if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) {
|
|
printf("netboot: retrieving root path from BOOTP response\n");
|
|
}
|
|
}
|
|
if (pkt != NULL) {
|
|
int len;
|
|
dhcpol_t options;
|
|
const char * path;
|
|
const struct dhcp * reply;
|
|
|
|
reply = (const struct dhcp *)pkt;
|
|
(void)dhcpol_parse_packet(&options, reply, pkt_len);
|
|
|
|
path = (const char *)dhcpol_find(&options,
|
|
dhcptag_root_path_e, &len, NULL);
|
|
if (path) {
|
|
memcpy(root_path, path, len);
|
|
root_path[len] = '\0';
|
|
found = TRUE;
|
|
}
|
|
}
|
|
IOBSDRegistryEntryRelease(entry);
|
|
return found;
|
|
}
|
|
|
|
static void
|
|
save_path(char * * str_p, size_t * length_p, char * path)
|
|
{
|
|
*length_p = strlen(path) + 1;
|
|
*str_p = kalloc_data(*length_p, Z_WAITOK);
|
|
strlcpy(*str_p, path, *length_p);
|
|
return;
|
|
}
|
|
|
|
static struct netboot_info *
|
|
netboot_info_init(struct in_addr iaddr)
|
|
{
|
|
boolean_t have_root_path = FALSE;
|
|
struct netboot_info * info = NULL;
|
|
char * root_path = NULL;
|
|
|
|
info = (struct netboot_info *)kalloc_type(struct netboot_info, Z_WAITOK | Z_ZERO);
|
|
info->client_ip = iaddr;
|
|
info->image_type = kNetBootImageTypeUnknown;
|
|
|
|
/* check for a booter-specified path then a NetBoot path */
|
|
root_path = zalloc(ZV_NAMEI);
|
|
|
|
if (PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) == TRUE
|
|
|| PE_parse_boot_argn("rp", root_path, MAXPATHLEN) == TRUE
|
|
|| PE_parse_boot_argn("rootpath", root_path, MAXPATHLEN) == TRUE) {
|
|
if (imageboot_format_is_valid(root_path)) {
|
|
printf("netboot_info_init: rp0='%s' isn't a network path,"
|
|
" ignoring\n", root_path);
|
|
} else {
|
|
have_root_path = TRUE;
|
|
}
|
|
}
|
|
if (have_root_path == FALSE) {
|
|
have_root_path = get_root_path(root_path);
|
|
}
|
|
if (have_root_path) {
|
|
const char * server_name = NULL;
|
|
char * mount_point = NULL;
|
|
char * image_path = NULL;
|
|
struct in_addr server_ip;
|
|
|
|
if (parse_image_path(root_path, &server_ip, &server_name,
|
|
&mount_point, &image_path)) {
|
|
/* kNetBootImageTypeNFS is deprecated */
|
|
printf("netboot: NFS boot is deprecated\n");
|
|
} else if (strncmp(root_path, kNetBootRootPathPrefixHTTP,
|
|
strlen(kNetBootRootPathPrefixHTTP)) == 0) {
|
|
info->image_type = kNetBootImageTypeHTTP;
|
|
save_path(&info->image_path, &info->image_path_length,
|
|
root_path);
|
|
printf("netboot: HTTP URL %s\n", info->image_path);
|
|
} else {
|
|
printf("netboot: root path uses unrecognized format\n");
|
|
}
|
|
|
|
/* check for image-within-image */
|
|
if (info->image_path != NULL) {
|
|
if (PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN)
|
|
|| PE_parse_boot_argn("rp1", root_path, MAXPATHLEN)) {
|
|
/* rp1/root-dmg is the second-level image */
|
|
save_path(&info->second_image_path, &info->second_image_path_length,
|
|
root_path);
|
|
}
|
|
}
|
|
if (info->second_image_path != NULL) {
|
|
printf("netboot: nested image %s\n", info->second_image_path);
|
|
}
|
|
}
|
|
zfree(ZV_NAMEI, root_path);
|
|
return info;
|
|
}
|
|
|
|
static void
|
|
netboot_info_free(struct netboot_info * * info_p)
|
|
{
|
|
struct netboot_info * info = *info_p;
|
|
|
|
if (info) {
|
|
kfree_data(info->mount_point, info->mount_point_length);
|
|
kfree_data(info->server_name, info->server_name_length);
|
|
kfree_data(info->image_path, info->image_path_length);
|
|
kfree_data(info->second_image_path,
|
|
info->second_image_path_length);
|
|
kfree_type(struct netboot_info, info);
|
|
}
|
|
*info_p = NULL;
|
|
}
|
|
|
|
boolean_t
|
|
netboot_iaddr(struct in_addr * iaddr_p)
|
|
{
|
|
if (S_netboot_info_p == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
*iaddr_p = S_netboot_info_p->client_ip;
|
|
return TRUE;
|
|
}
|
|
|
|
boolean_t
|
|
netboot_rootpath(struct in_addr * server_ip,
|
|
char * name, size_t name_len,
|
|
char * path, size_t path_len)
|
|
{
|
|
if (S_netboot_info_p == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
name[0] = '\0';
|
|
path[0] = '\0';
|
|
|
|
if (S_netboot_info_p->mount_point_length == 0) {
|
|
return FALSE;
|
|
}
|
|
if (path_len < S_netboot_info_p->mount_point_length) {
|
|
printf("netboot: path too small %zu < %zu\n",
|
|
path_len, S_netboot_info_p->mount_point_length);
|
|
return FALSE;
|
|
}
|
|
strlcpy(path, S_netboot_info_p->mount_point, path_len);
|
|
strlcpy(name, S_netboot_info_p->server_name, name_len);
|
|
*server_ip = S_netboot_info_p->server_ip;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static boolean_t
|
|
get_ip_parameters(struct in_addr * iaddr_p, struct in_addr * netmask_p,
|
|
struct in_addr * router_p)
|
|
{
|
|
void * entry;
|
|
const void * pkt;
|
|
int pkt_len;
|
|
|
|
|
|
entry = IOBSDRegistryEntryForDeviceTree("/chosen");
|
|
if (entry == NULL) {
|
|
return FALSE;
|
|
}
|
|
pkt = IOBSDRegistryEntryGetData(entry, DHCP_RESPONSE, &pkt_len);
|
|
if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) {
|
|
printf("netboot: retrieving IP information from DHCP response\n");
|
|
} else {
|
|
pkt = IOBSDRegistryEntryGetData(entry, BOOTP_RESPONSE, &pkt_len);
|
|
if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) {
|
|
printf("netboot: retrieving IP information from BOOTP response\n");
|
|
}
|
|
}
|
|
if (pkt != NULL) {
|
|
const struct in_addr * ip;
|
|
int len;
|
|
dhcpol_t options;
|
|
const struct dhcp * reply;
|
|
|
|
reply = (const struct dhcp *)pkt;
|
|
(void)dhcpol_parse_packet(&options, reply, pkt_len);
|
|
*iaddr_p = reply->dp_yiaddr;
|
|
ip = (const struct in_addr *)
|
|
dhcpol_find(&options,
|
|
dhcptag_subnet_mask_e, &len, NULL);
|
|
if (ip) {
|
|
*netmask_p = *ip;
|
|
}
|
|
ip = (const struct in_addr *)
|
|
dhcpol_find(&options, dhcptag_router_e, &len, NULL);
|
|
if (ip) {
|
|
*router_p = *ip;
|
|
}
|
|
}
|
|
IOBSDRegistryEntryRelease(entry);
|
|
return pkt != NULL;
|
|
}
|
|
|
|
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)
|
|
{
|
|
struct sockaddr_in dst;
|
|
int error;
|
|
uint32_t flags = RTF_UP | RTF_STATIC;
|
|
struct sockaddr_in gw;
|
|
struct sockaddr_in mask;
|
|
|
|
flags |= more_flags;
|
|
|
|
/* destination */
|
|
bzero((caddr_t)&dst, sizeof(dst));
|
|
dst.sin_len = sizeof(dst);
|
|
dst.sin_family = AF_INET;
|
|
dst.sin_addr = d;
|
|
|
|
/* gateway */
|
|
bzero((caddr_t)&gw, sizeof(gw));
|
|
gw.sin_len = sizeof(gw);
|
|
gw.sin_family = AF_INET;
|
|
gw.sin_addr = g;
|
|
|
|
/* mask */
|
|
bzero(&mask, sizeof(mask));
|
|
mask.sin_len = sizeof(mask);
|
|
mask.sin_family = AF_INET;
|
|
mask.sin_addr = m;
|
|
|
|
error = rtrequest_scoped(cmd, (struct sockaddr *)&dst,
|
|
(struct sockaddr *)&gw, (struct sockaddr *)&mask, flags, NULL, ifscope);
|
|
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
default_route_add(struct in_addr router, boolean_t proxy_arp)
|
|
{
|
|
uint32_t flags = 0;
|
|
struct in_addr zeroes = { .s_addr = 0 };
|
|
|
|
if (proxy_arp == FALSE) {
|
|
flags |= RTF_GATEWAY;
|
|
}
|
|
return route_cmd(RTM_ADD, zeroes, router, zeroes, flags, IFSCOPE_NONE);
|
|
}
|
|
|
|
static struct ifnet *
|
|
find_interface(void)
|
|
{
|
|
struct ifnet * ifp = NULL;
|
|
|
|
dlil_if_lock();
|
|
if (rootdevice[0]) {
|
|
ifp = ifunit((char *)rootdevice);
|
|
}
|
|
if (ifp == NULL) {
|
|
ifnet_head_lock_shared();
|
|
TAILQ_FOREACH(ifp, &ifnet_head, if_link)
|
|
if ((ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) == 0) {
|
|
break;
|
|
}
|
|
ifnet_head_done();
|
|
}
|
|
dlil_if_unlock();
|
|
return ifp;
|
|
}
|
|
|
|
static const struct sockaddr_in blank_sin = {
|
|
.sin_len = sizeof(struct sockaddr_in),
|
|
.sin_family = AF_INET,
|
|
.sin_port = 0,
|
|
.sin_addr = { .s_addr = 0 },
|
|
.sin_zero = { 0, 0, 0, 0, 0, 0, 0, 0 }
|
|
};
|
|
|
|
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)
|
|
{
|
|
struct ifaliasreq ifra;
|
|
|
|
bzero(&ifra, sizeof(ifra));
|
|
strlcpy(ifra.ifra_name, name, sizeof(ifra.ifra_name));
|
|
if (addr) {
|
|
*((struct sockaddr_in *)(void *)&ifra.ifra_addr) = blank_sin;
|
|
((struct sockaddr_in *)(void *)&ifra.ifra_addr)->sin_addr = *addr;
|
|
}
|
|
if (mask) {
|
|
*((struct sockaddr_in *)(void *)&ifra.ifra_mask) = blank_sin;
|
|
((struct sockaddr_in *)(void *)&ifra.ifra_mask)->sin_addr = *mask;
|
|
}
|
|
if (broadcast) {
|
|
*((struct sockaddr_in *)(void *)&ifra.ifra_broadaddr) = blank_sin;
|
|
((struct sockaddr_in *)(void *)&ifra.ifra_broadaddr)->sin_addr = *broadcast;
|
|
}
|
|
return ifioctl(so, SIOCAIFADDR, (caddr_t)&ifra, current_proc());
|
|
}
|
|
|
|
|
|
int
|
|
netboot_mountroot(void)
|
|
{
|
|
int error = 0;
|
|
struct in_addr iaddr = { .s_addr = 0 };
|
|
struct ifreq ifr;
|
|
struct ifnet * ifp;
|
|
struct in_addr netmask = { .s_addr = 0 };
|
|
proc_t procp = current_proc();
|
|
struct in_addr router = { .s_addr = 0 };
|
|
struct socket * so = NULL;
|
|
|
|
bzero(&ifr, sizeof(ifr));
|
|
|
|
/* find the interface */
|
|
ifp = find_interface();
|
|
if (ifp == NULL) {
|
|
printf("netboot: no suitable interface\n");
|
|
error = ENXIO;
|
|
goto failed;
|
|
}
|
|
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", if_name(ifp));
|
|
printf("netboot: using network interface '%s'\n", ifr.ifr_name);
|
|
|
|
/* bring it up */
|
|
if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0) {
|
|
printf("netboot: socreate, error=%d\n", error);
|
|
goto failed;
|
|
}
|
|
ifr.ifr_flags = ifp->if_flags | IFF_UP;
|
|
error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ifr, procp);
|
|
if (error) {
|
|
printf("netboot: SIFFLAGS, error=%d\n", error);
|
|
goto failed;
|
|
}
|
|
|
|
/* grab information from the registry */
|
|
if (get_ip_parameters(&iaddr, &netmask, &router) == FALSE) {
|
|
printf("netboot: can't retrieve IP parameters\n");
|
|
goto failed;
|
|
}
|
|
OS_ANALYZER_SUPPRESS("12641116") printf("netboot: IP address " IP_FORMAT, IP_LIST(&iaddr));
|
|
if (netmask.s_addr) {
|
|
printf(" netmask " IP_FORMAT, IP_LIST(&netmask));
|
|
}
|
|
if (router.s_addr) {
|
|
printf(" router " IP_FORMAT, IP_LIST(&router));
|
|
}
|
|
printf("\n");
|
|
error = inet_aifaddr(so, ifr.ifr_name, &iaddr, &netmask, NULL);
|
|
if (error) {
|
|
printf("netboot: inet_aifaddr failed, %d\n", error);
|
|
goto failed;
|
|
}
|
|
if (router.s_addr == 0) {
|
|
/* enable proxy arp if we don't have a router */
|
|
router.s_addr = iaddr.s_addr;
|
|
}
|
|
printf("netboot: adding default route " IP_FORMAT "\n",
|
|
IP_LIST(&router));
|
|
error = default_route_add(router, router.s_addr == iaddr.s_addr);
|
|
if (error) {
|
|
printf("netboot: default_route_add failed %d\n", error);
|
|
}
|
|
|
|
soclose(so);
|
|
|
|
S_netboot_info_p = netboot_info_init(iaddr);
|
|
switch (S_netboot_info_p->image_type) {
|
|
default:
|
|
case kNetBootImageTypeNFS:
|
|
/* kNetBootImageTypeNFS is deprecated */
|
|
error = ENOTSUP;
|
|
break;
|
|
case kNetBootImageTypeHTTP:
|
|
error = netboot_setup();
|
|
break;
|
|
}
|
|
if (error == 0) {
|
|
S_netboot = 1;
|
|
} else {
|
|
S_netboot = 0;
|
|
}
|
|
return error;
|
|
failed:
|
|
if (so != NULL) {
|
|
soclose(so);
|
|
}
|
|
return error;
|
|
}
|
|
|
|
int
|
|
netboot_setup(void)
|
|
{
|
|
int error = 0;
|
|
|
|
if (S_netboot_info_p == NULL
|
|
|| S_netboot_info_p->image_path == NULL) {
|
|
goto done;
|
|
}
|
|
printf("netboot_setup: calling imageboot_mount_image\n");
|
|
error = imageboot_mount_image(S_netboot_info_p->image_path, -1, IMAGEBOOT_DMG);
|
|
if (error != 0) {
|
|
printf("netboot: failed to mount root image, %d\n", error);
|
|
} else if (S_netboot_info_p->second_image_path != NULL) {
|
|
error = imageboot_mount_image(S_netboot_info_p->second_image_path, 0, IMAGEBOOT_DMG);
|
|
if (error != 0) {
|
|
printf("netboot: failed to mount second root image, %d\n", error);
|
|
}
|
|
}
|
|
|
|
done:
|
|
netboot_info_free(&S_netboot_info_p);
|
|
return error;
|
|
}
|
|
|
|
int
|
|
netboot_root(void)
|
|
{
|
|
return S_netboot;
|
|
}
|