/*
- * Copyright (c) 2001-2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2001-2013 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#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 <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/dhcp_options.h>
-#include <pexpert/pexpert.h>
#include <kern/kern_types.h>
#include <kern/kalloc.h>
+#include <sys/netboot.h>
+#include <sys/imageboot.h>
+#include <pexpert/pexpert.h>
//#include <libkern/libkern.h>
extern struct filedesc filedesc0;
-extern int strncmp(const char *,const char *, size_t);
-extern unsigned long strtoul(const char *, char **, int);
-extern char * strchr(const char *str, int ch);
-
extern int nfs_mountroot(void); /* nfs_vfsops.c */
extern int (*mountroot)(void);
IOBSDRegistryEntryGetData(void * entry, const char * property_name,
int * packet_length);
-extern int vndevice_root_image(const char * path, char devname[],
- dev_t * dev_p);
-extern int di_root_image(const char *path, char devname[], dev_t *dev_p);
-
#define BOOTP_RESPONSE "bootp-response"
#define BSDP_RESPONSE "bsdp-response"
#define DHCP_RESPONSE "dhcp-response"
-extern int
-bootp(struct ifnet * ifp, struct in_addr * iaddr_p, int max_retry,
- struct in_addr * netmask_p, struct in_addr * router_p,
- struct proc * procp);
-
-
-/* forward declarations */
-int inet_aton(char * cp, struct in_addr * pin);
-
-boolean_t netboot_iaddr(struct in_addr * iaddr_p);
-boolean_t netboot_rootpath(struct in_addr * server_ip,
- char * name, int name_len,
- char * path, int path_len);
-int netboot_setup(struct proc * p);
-int netboot_mountroot(void);
-int netboot_root(void);
-
-
-
#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]
char * image_path;
int image_path_length;
NetBootImageType image_type;
- boolean_t use_hdix;
+ char * second_image_path;
+ int second_image_path_length;
};
-int
-inet_aton(char * cp, struct in_addr * pin)
-{
- u_char * b = (char *)pin;
- int i;
- char * p;
-
- for (p = cp, i = 0; i < 4; i++) {
- u_long l = strtoul(p, 0, 0);
- if (l > 255)
- return (FALSE);
- b[i] = l;
- p = strchr(p, '.');
- if (i < 3 && p == NULL)
- return (FALSE);
- p++;
- }
- return (TRUE);
-}
-
/*
* Function: parse_booter_path
* Purpose:
* "17.202.16.17:seaport:/release/.images/Image9/CurrentHera"
*/
static __inline__ boolean_t
-parse_booter_path(char * path, struct in_addr * iaddr_p, char * * host,
+parse_booter_path(char * path, struct in_addr * iaddr_p, char const * * host,
char * * mount_dir, char * * image_path)
{
char * start;
* 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 * * host,
+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 */
+ static char tmp[MAX_IPv4_STR_LEN]; /* Danger - not thread safe */
char * start;
char * colon;
}
static boolean_t
-parse_image_path(char * path, struct in_addr * iaddr_p, char * * host,
+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') {
if (pkt != NULL) {
int len;
dhcpol_t options;
- char * path;
- struct dhcp * reply;
+ const char * path;
+ const struct dhcp * reply;
- reply = (struct dhcp *)pkt;
- (void)dhcpol_parse_packet(&options, reply, pkt_len, NULL);
+ reply = (const struct dhcp *)pkt;
+ (void)dhcpol_parse_packet(&options, reply, pkt_len);
- path = (char *)dhcpol_find(&options,
- dhcptag_root_path_e, &len, NULL);
+ path = (const char *)dhcpol_find(&options,
+ dhcptag_root_path_e, &len, NULL);
if (path) {
- bcopy(path, root_path, len);
+ memcpy(root_path, path, len);
root_path[len] = '\0';
found = TRUE;
}
}
+static void
+save_path(char * * str_p, int * length_p, char * path)
+{
+ *length_p = strlen(path) + 1;
+ *str_p = (char *)kalloc(*length_p);
+ strlcpy(*str_p, path, *length_p);
+ return;
+}
+
static struct netboot_info *
netboot_info_init(struct in_addr iaddr)
{
- struct netboot_info * info;
+ boolean_t have_root_path = FALSE;
+ struct netboot_info * info = NULL;
char * root_path = NULL;
- boolean_t use_hdix = TRUE;
- char * vndevice = NULL;
-
- MALLOC_ZONE(vndevice, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
- if (vndevice == NULL)
- panic("netboot_info_init: M_NAMEI zone exhausted");
- if (PE_parse_boot_arg("vndevice", vndevice) == TRUE) {
- use_hdix = FALSE;
- }
- FREE_ZONE(vndevice, MAXPATHLEN, M_NAMEI);
info = (struct netboot_info *)kalloc(sizeof(*info));
bzero(info, sizeof(*info));
info->client_ip = iaddr;
info->image_type = kNetBootImageTypeUnknown;
- info->use_hdix = use_hdix;
/* check for a booter-specified path then a NetBoot path */
MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
if (root_path == NULL)
panic("netboot_info_init: M_NAMEI zone exhausted");
- if (PE_parse_boot_arg("rp", root_path) == TRUE
- || PE_parse_boot_arg("rootpath", root_path) == TRUE
- || get_root_path(root_path) == TRUE) {
- char * server_name = NULL;
+ 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;
info->server_name = (char *)kalloc(info->server_name_length);
info->mount_point_length = strlen(mount_point) + 1;
info->mount_point = (char *)kalloc(info->mount_point_length);
- strcpy(info->server_name, server_name);
- strcpy(info->mount_point, mount_point);
+ strlcpy(info->server_name, server_name, info->server_name_length);
+ strlcpy(info->mount_point, mount_point, info->mount_point_length);
- printf("Server %s Mount %s",
+ printf("netboot: NFS Server %s Mount %s",
server_name, info->mount_point);
if (image_path != NULL) {
boolean_t needs_slash = FALSE;
-
+
info->image_path_length = strlen(image_path) + 1;
if (image_path[0] != '/') {
needs_slash = TRUE;
}
info->image_path = (char *)kalloc(info->image_path_length);
if (needs_slash) {
- info->image_path[0] = '/';
- strcpy(info->image_path + 1, image_path);
- }
- else {
- strcpy(info->image_path, image_path);
+ info->image_path[0] = '/';
+ strlcpy(info->image_path + 1, image_path,
+ info->image_path_length - 1);
+ } else {
+ strlcpy(info->image_path, image_path,
+ info->image_path_length);
}
printf(" Image %s", info->image_path);
}
}
else if (strncmp(root_path, kNetBootRootPathPrefixHTTP,
strlen(kNetBootRootPathPrefixHTTP)) == 0) {
- /* only HDIX supports HTTP */
info->image_type = kNetBootImageTypeHTTP;
- info->use_hdix = TRUE;
- info->image_path_length = strlen(root_path) + 1;
- info->image_path = (char *)kalloc(info->image_path_length);
- strcpy(info->image_path, root_path);
+ 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);
+ }
}
FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
return (info);
if (info->image_path) {
kfree(info->image_path, info->image_path_length);
}
+ if (info->second_image_path) {
+ kfree(info->second_image_path, info->second_image_path_length);
+ }
kfree(info, sizeof(*info));
}
*info_p = NULL;
path_len, S_netboot_info_p->mount_point_length);
return (FALSE);
}
- strcpy(path, S_netboot_info_p->mount_point);
- strncpy(name, S_netboot_info_p->server_name, name_len);
+ 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);
}
}
}
if (pkt != NULL) {
- struct in_addr * ip;
+ const struct in_addr * ip;
int len;
dhcpol_t options;
- struct dhcp * reply;
+ const struct dhcp * reply;
- reply = (struct dhcp *)pkt;
- (void)dhcpol_parse_packet(&options, reply, pkt_len, NULL);
+ reply = (const struct dhcp *)pkt;
+ (void)dhcpol_parse_packet(&options, reply, pkt_len);
*iaddr_p = reply->dp_yiaddr;
- ip = (struct in_addr *)
+ ip = (const struct in_addr *)
dhcpol_find(&options,
dhcptag_subnet_mask_e, &len, NULL);
if (ip) {
*netmask_p = *ip;
}
- ip = (struct in_addr *)
+ ip = (const struct in_addr *)
dhcpol_find(&options, dhcptag_router_e, &len, NULL);
if (ip) {
*router_p = *ip;
return (pkt != NULL);
}
-static int
-inet_aifaddr(struct socket * so, char * name, const struct in_addr * addr,
- const struct in_addr * mask,
- const struct in_addr * broadcast)
-{
- struct sockaddr blank_sin;
- struct ifaliasreq ifra;
-
- bzero(&blank_sin, sizeof(blank_sin));
- blank_sin.sa_len = sizeof(blank_sin);
- blank_sin.sa_family = AF_INET;
-
- bzero(&ifra, sizeof(ifra));
- strncpy(ifra.ifra_name, name, sizeof(ifra.ifra_name));
- if (addr) {
- ifra.ifra_addr = blank_sin;
- ((struct sockaddr_in *)&ifra.ifra_addr)->sin_addr = *addr;
- }
- if (mask) {
- ifra.ifra_mask = blank_sin;
- ((struct sockaddr_in *)&ifra.ifra_mask)->sin_addr = *mask;
- }
- if (broadcast) {
- ifra.ifra_broadaddr = blank_sin;
- ((struct sockaddr_in *)&ifra.ifra_broadaddr)->sin_addr = *broadcast;
- }
- return (ifioctl(so, SIOCAIFADDR, (caddr_t)&ifra, current_proc()));
-}
-
static int
route_cmd(int cmd, struct in_addr d, struct in_addr g,
- struct in_addr m, u_long more_flags)
+ struct in_addr m, uint32_t more_flags, unsigned int ifscope)
{
struct sockaddr_in dst;
- u_long flags = RTF_UP | RTF_STATIC;
+ int error;
+ uint32_t flags = RTF_UP | RTF_STATIC;
struct sockaddr_in gw;
struct sockaddr_in mask;
mask.sin_family = AF_INET;
mask.sin_addr = m;
- return (rtrequest(cmd, (struct sockaddr *)&dst, (struct sockaddr *)&gw,
- (struct sockaddr *)&mask, flags, NULL));
+ 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)
{
- u_long flags = 0;
+ uint32_t flags = 0;
struct in_addr zeroes = { 0 };
if (proxy_arp == FALSE) {
flags |= RTF_GATEWAY;
}
- return (route_cmd(RTM_ADD, zeroes, router, zeroes, flags));
+ return (route_cmd(RTM_ADD, zeroes, router, zeroes, flags, IFSCOPE_NONE));
}
static int
-host_route_delete(struct in_addr host)
+host_route_delete(struct in_addr host, unsigned int ifscope)
{
struct in_addr zeroes = { 0 };
- return (route_cmd(RTM_DELETE, host, zeroes, zeroes, RTF_HOST));
+ return (route_cmd(RTM_DELETE, host, zeroes, zeroes, RTF_HOST, ifscope));
}
static struct ifnet *
{
struct ifnet * ifp = NULL;
+ dlil_if_lock();
if (rootdevice[0]) {
- ifp = ifunit(rootdevice);
+ ifp = ifunit((char *)rootdevice);
}
if (ifp == NULL) {
ifnet_head_lock_shared();
break;
ifnet_head_done();
}
+ dlil_if_unlock();
return (ifp);
}
+static const struct sockaddr_in blank_sin = {
+ sizeof(struct sockaddr_in),
+ AF_INET,
+ 0,
+ { 0 },
+ { 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)
{
struct ifreq ifr;
struct ifnet * ifp;
struct in_addr netmask = { 0 };
- struct proc * procp = current_proc();
+ proc_t procp = current_proc();
struct in_addr router = { 0 };
struct socket * so = NULL;
unsigned int try;
bzero(&ifr, sizeof(ifr));
-
/* find the interface */
ifp = find_interface();
if (ifp == NULL) {
error = ENXIO;
goto failed;
}
- sprintf(ifr.ifr_name, "%s%d", ifp->if_name, ifp->if_unit);
+ 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 */
/* grab information from the registry */
if (get_ip_parameters(&iaddr, &netmask, &router) == FALSE) {
- /* use BOOTP to retrieve IP address, netmask and router */
- error = bootp(ifp, &iaddr, 32, &netmask, &router, procp);
- if (error) {
- printf("netboot: BOOTP failed %d\n", error);
- goto failed;
- }
+ printf("netboot: can't retrieve IP parameters\n");
+ goto failed;
}
printf("netboot: IP address " IP_FORMAT, IP_LIST(&iaddr));
if (netmask.s_addr) {
/* NOT REACHED */
case EHOSTDOWN:
/* remove the server's arp entry */
- error = host_route_delete(S_netboot_info_p->server_ip);
+ error = host_route_delete(S_netboot_info_p->server_ip,
+ ifp->if_index);
if (error) {
printf("netboot: host_route_delete(" IP_FORMAT
") failed %d\n",
}
break;
case EHOSTUNREACH:
- error = host_route_delete(router);
+ error = host_route_delete(router, ifp->if_index);
if (error) {
printf("netboot: host_route_delete(" IP_FORMAT
") failed %d\n", IP_LIST(&router), error);
}
break;
case kNetBootImageTypeHTTP:
- error = netboot_setup(procp);
+ error = netboot_setup();
break;
}
if (error == 0) {
}
int
-netboot_setup(struct proc * p)
+netboot_setup()
{
- dev_t dev;
int error = 0;
if (S_netboot_info_p == NULL
|| S_netboot_info_p->image_path == NULL) {
goto done;
}
- if (S_netboot_info_p->use_hdix) {
- printf("netboot_setup: calling di_root_image\n");
- error = di_root_image(S_netboot_info_p->image_path,
- rootdevice, &dev);
- if (error) {
- printf("netboot_setup: di_root_image: failed %d\n", error);
- goto done;
- }
+ printf("netboot_setup: calling imageboot_mount_image\n");
+ error = imageboot_mount_image(S_netboot_info_p->image_path, -1);
+ if (error != 0) {
+ printf("netboot: failed to mount root image, %d\n", error);
}
- else {
- printf("netboot_setup: calling vndevice_root_image\n");
- error = vndevice_root_image(S_netboot_info_p->image_path,
- rootdevice, &dev);
- if (error) {
- printf("netboot_setup: vndevice_root_image: failed %d\n", error);
- goto done;
+ else if (S_netboot_info_p->second_image_path != NULL) {
+ error = imageboot_mount_image(S_netboot_info_p->second_image_path, 0);
+ if (error != 0) {
+ printf("netboot: failed to mount second root image, %d\n", error);
}
}
- rootdev = dev;
- mountroot = NULL;
- printf("netboot: root device 0x%x\n", rootdev);
- error = vfs_mountroot();
- if (error == 0 && rootvnode != NULL) {
- struct vnode *tvp;
- struct vnode *newdp;
- struct vfs_context context;
-
- context.vc_proc = p;
- context.vc_ucred = kauth_cred_proc_ref(p); /* XXX kauth_cred_get() ??? proxy */
-
- /* Get the vnode for '/'. Set fdp->fd_fd.fd_cdir to reference it. */
- if (VFS_ROOT(mountlist.tqh_last, &newdp, &context))
- panic("netboot_setup: cannot find root vnode");
- vnode_ref(newdp);
- vnode_put(newdp);
- tvp = rootvnode;
- vnode_rele(tvp);
- filedesc0.fd_cdir = newdp;
- rootvnode = newdp;
- mount_list_lock();
- TAILQ_REMOVE(&mountlist, TAILQ_FIRST(&mountlist), mnt_list);
- mount_list_unlock();
- mountlist.tqh_first->mnt_flag |= MNT_ROOTFS;
- kauth_cred_unref(&context.vc_ucred);
- }
+
done:
netboot_info_free(&S_netboot_info_p);
return (error);