X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/43866e378188c25dd1e2208016ab3cbeb086ae6c..316670eb35587141e969394ae8537d66b9211e80:/bsd/kern/netboot.c?ds=inline diff --git a/bsd/kern/netboot.c b/bsd/kern/netboot.c index ce58ad619..dd238f066 100644 --- a/bsd/kern/netboot.c +++ b/bsd/kern/netboot.c @@ -1,16 +1,19 @@ /* - * Copyright (c) 2001 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2001-2010 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2003 Apple Computer, 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. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. + * 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 @@ -20,7 +23,7 @@ * Please see the License for the specific language governing rights and * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* @@ -33,14 +36,16 @@ #include #include #include -#include -#include +#include +#include #include #include -#include +#include #include #include +#include #include +#include #include #include #include @@ -48,16 +53,18 @@ #include #include #include +#include +#include +#include +#include +#include +#include //#include - -extern dev_t rootdev; /* device of the root */ 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[]; @@ -66,32 +73,21 @@ static int S_netboot = 0; 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); #define IP_FORMAT "%d.%d.%d.%d" #define IP_CH(ip) ((u_char *)ip) @@ -116,29 +112,10 @@ struct netboot_info { 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: @@ -152,7 +129,7 @@ inet_aton(char * cp, struct in_addr * pin) * "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; @@ -239,9 +216,10 @@ find_colon(char * str) * 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; @@ -275,12 +253,12 @@ parse_netboot_path(char * path, struct in_addr * iaddr_p, char * * host, (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') { @@ -304,29 +282,29 @@ get_root_path(char * root_path) 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; } @@ -336,32 +314,47 @@ get_root_path(char * root_path) } +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 (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 (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 (root_path == NULL) + panic("netboot_info_init: M_NAMEI zone exhausted"); + 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; @@ -374,14 +367,14 @@ netboot_info_init(struct in_addr iaddr) 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; - + boolean_t needs_slash = FALSE; + info->image_path_length = strlen(image_path) + 1; if (image_path[0] != '/') { needs_slash = TRUE; @@ -389,11 +382,12 @@ netboot_info_init(struct in_addr iaddr) } 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); } @@ -401,18 +395,29 @@ netboot_info_init(struct in_addr iaddr) } 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); + FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI); return (info); } @@ -431,6 +436,9 @@ netboot_info_free(struct netboot_info * * info_p) 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; @@ -466,8 +474,8 @@ netboot_rootpath(struct in_addr * server_ip, 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); } @@ -487,31 +495,31 @@ get_ip_parameters(struct in_addr * iaddr_p, struct in_addr * netmask_p, 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; @@ -522,98 +530,97 @@ get_ip_parameters(struct in_addr * iaddr_p, struct in_addr * netmask_p, } 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, 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; - 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)); + error = rtrequest_scoped(cmd, (struct sockaddr *)&dst, + (struct sockaddr *)&gw, (struct sockaddr *)&mask, flags, NULL, ifscope); - return (rtrequest(RTM_ADD, (struct sockaddr *)&dst, (struct sockaddr *)&gw, - (struct sockaddr *)&mask, flags, NULL)); + return (error); + +} + +static int +default_route_add(struct in_addr router, boolean_t proxy_arp) +{ + 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, IFSCOPE_NONE)); +} + +static int +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, ifscope)); } static struct ifnet * -find_interface() +find_interface(void) { struct ifnet * ifp = NULL; + dlil_if_lock(); 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(); } + dlil_if_unlock(); 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) { @@ -621,7 +628,8 @@ netboot_mountroot() 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 */ @@ -638,10 +646,10 @@ netboot_mountroot() /* 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; } } @@ -662,22 +670,60 @@ netboot_mountroot() /* 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, + ifp->if_index); + 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, ifp->if_index); + 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) { @@ -691,67 +737,37 @@ failed: 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 == 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; - - /* Get the vnode for '/'. Set fdp->fd_fd.fd_cdir to reference it. */ - if (VFS_ROOT(mountlist.cqh_last, &newdp)) - panic("netboot_setup: cannot find root vnode"); - VREF(newdp); - tvp = rootvnode; - vrele(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; - } + done: netboot_info_free(&S_netboot_info_p); return (error); } int -netboot_root() +netboot_root(void) { return (S_netboot); }