/*
- * Copyright (c) 2001 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2001-2006 Apple Computer, Inc. All rights reserved.
*
- * @APPLE_LICENSE_HEADER_START@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License"). You may not use this file except in compliance with the
- * License. Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
+ * 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.
*
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * 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 OR NON-INFRINGEMENT. Please see the
- * License for the specific language governing rights and limitations
- * under the License.
+ * 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_LICENSE_HEADER_END@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/*
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/ioctl.h>
-#include <sys/proc.h>
-#include <sys/mount.h>
+#include <sys/proc_internal.h>
+#include <sys/mount_internal.h>
#include <sys/mbuf.h>
#include <sys/filedesc.h>
-#include <sys/vnode.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 <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/dhcp_options.h>
+#include <netinet/in_dhcp.h>
+#include <kern/kern_types.h>
+#include <kern/kalloc.h>
-//#include <libkern/libkern.h>
+#include <pexpert/pexpert.h>
-extern dev_t rootdev; /* device of the root */
+//#include <libkern/libkern.h>
extern struct filedesc filedesc0;
-extern char * strchr(const char *str, int ch);
-
-extern int nfs_mountroot(); /* nfs_vfsops.c */
+extern int nfs_mountroot(void); /* nfs_vfsops.c */
extern int (*mountroot)(void);
extern unsigned char rootdevice[];
static struct netboot_info * S_netboot_info_p;
void *
-IOBSDRegistryEntryForDeviceTree(char * path);
+IOBSDRegistryEntryForDeviceTree(const char * path);
void
IOBSDRegistryEntryRelease(void * entry);
const void *
-IOBSDRegistryEntryGetData(void * entry, char * property_name,
+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);
-
-static boolean_t path_getfile __P((char * image_path,
- struct sockaddr_in * sin_p,
- char * serv_name, char * pathname));
-
#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(void);
+int netboot_mountroot(void);
+int netboot_root(void);
+
+
#define IP_FORMAT "%d.%d.%d.%d"
#define IP_CH(ip) ((u_char *)ip)
int
inet_aton(char * cp, struct in_addr * pin)
{
- u_char * b = (char *)pin;
+ u_char * b = (u_char *)pin;
int i;
char * p;
* "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 */
char * start;
char * colon;
(void)find_colon(start);
*image_path = start;
}
- *host = inet_ntoa(*iaddr_p);
+ *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 * * 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') {
return (FALSE);
}
pkt = IOBSDRegistryEntryGetData(entry, BSDP_RESPONSE, &pkt_len);
- if (pkt != NULL && pkt_len >= sizeof(struct dhcp)) {
+ 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 >= sizeof(struct dhcp)) {
+ 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;
- 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;
}
char * vndevice = NULL;
MALLOC_ZONE(vndevice, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
- if (PE_parse_boot_arg("vndevice", vndevice) == TRUE) {
+ if (vndevice == NULL)
+ panic("netboot_info_init: M_NAMEI zone exhausted");
+ if (PE_parse_boot_argn("vndevice", vndevice, MAXPATHLEN) == TRUE) {
use_hdix = FALSE;
}
- _FREE_ZONE(vndevice, MAXPATHLEN, M_NAMEI);
+ FREE_ZONE(vndevice, MAXPATHLEN, M_NAMEI);
info = (struct netboot_info *)kalloc(sizeof(*info));
bzero(info, sizeof(*info));
/* check for a booter-specified path then a NetBoot path */
MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
- if (PE_parse_boot_arg("rp", root_path) == TRUE
- || PE_parse_boot_arg("rootpath", root_path) == TRUE
+ if (root_path == NULL)
+ panic("netboot_info_init: M_NAMEI zone exhausted");
+ if (PE_parse_boot_argn("rp", root_path, MAXPATHLEN) == TRUE
+ || PE_parse_boot_argn("rootpath", root_path, MAXPATHLEN) == TRUE
|| get_root_path(root_path) == TRUE) {
- char * server_name = NULL;
+ 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",
server_name, info->mount_point);
if (image_path != NULL) {
- boolean_t needs_slash;
+ boolean_t needs_slash = FALSE;
info->image_path_length = strlen(image_path) + 1;
if (image_path[0] != '/') {
}
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);
}
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);
+ strlcpy(info->image_path, root_path, info->image_path_length);
}
else {
printf("netboot: root path uses unrecognized format\n");
}
}
- _FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
+ FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
return (info);
}
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);
}
return (FALSE);
}
pkt = IOBSDRegistryEntryGetData(entry, DHCP_RESPONSE, &pkt_len);
- if (pkt != NULL && pkt_len >= sizeof(struct dhcp)) {
+ 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 >= sizeof(struct dhcp)) {
+ if (pkt != NULL && pkt_len >= (int)sizeof(struct dhcp)) {
printf("netboot: retrieving IP information from BOOTP response\n");
}
}
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;
}
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 = { sizeof(blank_sin), AF_INET };
- struct ifaliasreq ifra;
-
- 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
-default_route_add(struct in_addr router, boolean_t proxy_arp)
+route_cmd(int cmd, struct in_addr d, struct in_addr g,
+ struct in_addr m, u_long more_flags)
{
struct sockaddr_in dst;
u_long flags = RTF_UP | RTF_STATIC;
struct sockaddr_in gw;
struct sockaddr_in mask;
- if (proxy_arp == FALSE) {
- flags |= RTF_GATEWAY;
- }
+ flags |= more_flags;
- /* dest 0.0.0.0 */
+ /* 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 = router;
+ gw.sin_addr = g;
- /* mask 0.0.0.0 */
+ /* mask */
bzero(&mask, sizeof(mask));
mask.sin_len = sizeof(mask);
mask.sin_family = AF_INET;
+ mask.sin_addr = m;
- printf("netboot: adding default route " IP_FORMAT "\n",
- IP_LIST(&router));
-
- return (rtrequest(RTM_ADD, (struct sockaddr *)&dst, (struct sockaddr *)&gw,
+ return (rtrequest(cmd, (struct sockaddr *)&dst, (struct sockaddr *)&gw,
(struct sockaddr *)&mask, flags, NULL));
}
+static int
+default_route_add(struct in_addr router, boolean_t proxy_arp)
+{
+ u_long flags = 0;
+ struct in_addr zeroes = { 0 };
+
+ if (proxy_arp == FALSE) {
+ flags |= RTF_GATEWAY;
+ }
+ return (route_cmd(RTM_ADD, zeroes, router, zeroes, flags));
+}
+
+static int
+host_route_delete(struct in_addr host)
+{
+ struct in_addr zeroes = { 0 };
+
+ return (route_cmd(RTM_DELETE, host, zeroes, zeroes, RTF_HOST));
+}
+
static struct ifnet *
-find_interface()
+find_interface(void)
{
struct ifnet * ifp = NULL;
if (rootdevice[0]) {
- ifp = ifunit(rootdevice);
+ ifp = ifunit((char *)rootdevice);
}
if (ifp == NULL) {
- TAILQ_FOREACH(ifp, &ifnet, if_link)
- if ((ifp->if_flags &
- (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0)
- break;
+ ifnet_head_lock_shared();
+ TAILQ_FOREACH(ifp, &ifnet_head, if_link)
+ if ((ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0)
+ break;
+ ifnet_head_done();
}
return (ifp);
}
int
-netboot_mountroot()
+netboot_mountroot(void)
{
int error = 0;
struct in_addr iaddr = { 0 };
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));
- thread_funnel_switch(KERNEL_FUNNEL, NETWORK_FUNNEL);
-
/* 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%d", ifp->if_name,
+ ifp->if_unit);
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);
+ /* use DHCP to retrieve IP address, netmask and router */
+ error = dhcp(ifp, &iaddr, 64, &netmask, &router, procp);
if (error) {
- printf("netboot: BOOTP failed %d\n", error);
+ printf("netboot: DHCP failed %d\n", error);
goto failed;
}
}
/* 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);
- thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
S_netboot_info_p = netboot_info_init(iaddr);
switch (S_netboot_info_p->image_type) {
default:
case kNetBootImageTypeNFS:
- error = nfs_mountroot();
+ for (try = 1; TRUE; try++) {
+ error = nfs_mountroot();
+ if (error == 0) {
+ break;
+ }
+ printf("netboot: nfs_mountroot() attempt %u failed; "
+ "clearing ARP entry and trying again\n", try);
+ /*
+ * error is either EHOSTDOWN or EHOSTUNREACH, which likely means
+ * that the port we're plugged into has spanning tree enabled,
+ * and either the router or the server can't answer our ARP
+ * requests. Clear the incomplete ARP entry by removing the
+ * appropriate route, depending on the error code:
+ * EHOSTDOWN NFS server's route
+ * EHOSTUNREACH router's route
+ */
+ switch (error) {
+ default:
+ /* NOT REACHED */
+ case EHOSTDOWN:
+ /* remove the server's arp entry */
+ error = host_route_delete(S_netboot_info_p->server_ip);
+ if (error) {
+ printf("netboot: host_route_delete(" IP_FORMAT
+ ") failed %d\n",
+ IP_LIST(&S_netboot_info_p->server_ip), error);
+ }
+ break;
+ case EHOSTUNREACH:
+ error = host_route_delete(router);
+ if (error) {
+ printf("netboot: host_route_delete(" IP_FORMAT
+ ") failed %d\n", IP_LIST(&router), error);
+ }
+ break;
+ }
+ }
break;
case kNetBootImageTypeHTTP:
- error = netboot_setup(procp);
+ error = netboot_setup();
break;
}
if (error == 0) {
if (so != NULL) {
soclose(so);
}
- thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL);
return (error);
}
int
-netboot_setup(struct proc * p)
+netboot_setup()
{
dev_t dev;
int error = 0;
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);
+ (char *)rootdevice, &dev);
if (error) {
printf("netboot_setup: di_root_image: failed %d\n", error);
goto done;
else {
printf("netboot_setup: calling vndevice_root_image\n");
error = vndevice_root_image(S_netboot_info_p->image_path,
- rootdevice, &dev);
+ (char *)rootdevice, &dev);
if (error) {
printf("netboot_setup: vndevice_root_image: failed %d\n", error);
goto done;
struct vnode *newdp;
/* Get the vnode for '/'. Set fdp->fd_fd.fd_cdir to reference it. */
- if (VFS_ROOT(mountlist.cqh_last, &newdp))
+ if (VFS_ROOT(TAILQ_LAST(&mountlist,mntlist), &newdp, vfs_context_kernel()))
panic("netboot_setup: cannot find root vnode");
- VREF(newdp);
+ vnode_ref(newdp);
+ vnode_put(newdp);
tvp = rootvnode;
- vrele(tvp);
+ vnode_rele(tvp);
filedesc0.fd_cdir = newdp;
rootvnode = newdp;
- simple_lock(&mountlist_slock);
- CIRCLEQ_REMOVE(&mountlist, CIRCLEQ_FIRST(&mountlist), mnt_list);
- simple_unlock(&mountlist_slock);
- VOP_UNLOCK(rootvnode, 0, p);
- mountlist.cqh_first->mnt_flag |= MNT_ROOTFS;
+ mount_list_lock();
+ TAILQ_REMOVE(&mountlist, TAILQ_FIRST(&mountlist), mnt_list);
+ mount_list_unlock();
+ mountlist.tqh_first->mnt_flag |= MNT_ROOTFS;
}
done:
netboot_info_free(&S_netboot_info_p);
}
int
-netboot_root()
+netboot_root(void)
{
return (S_netboot);
}