X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/d7e50217d7adf6e52786a38bcaa4cd698cb9a79e..3a60a9f5b85abb8c2cf24e1926c5c7b3f608a5e2:/bsd/kern/netboot.c diff --git a/bsd/kern/netboot.c b/bsd/kern/netboot.c index ce58ad619..2555875b7 100644 --- a/bsd/kern/netboot.c +++ b/bsd/kern/netboot.c @@ -1,24 +1,21 @@ /* - * Copyright (c) 2001 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2001-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. + * 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. 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 + * This 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. + * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the + * License for the specific language governing rights and limitations + * under the License. * * @APPLE_LICENSE_HEADER_END@ */ @@ -33,13 +30,14 @@ #include #include #include -#include -#include +#include +#include #include #include -#include +#include #include #include +#include #include #include #include @@ -48,16 +46,19 @@ #include #include #include +#include +#include +#include //#include - -extern dev_t rootdev; /* device of the root */ 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(); /* nfs_vfsops.c */ +extern int nfs_mountroot(void); /* nfs_vfsops.c */ extern int (*mountroot)(void); extern unsigned char rootdevice[]; @@ -66,24 +67,19 @@ 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" @@ -93,6 +89,20 @@ 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] @@ -242,6 +252,7 @@ static __inline__ boolean_t parse_netboot_path(char * path, struct in_addr * iaddr_p, char * * host, char * * mount_dir, char * * image_path) { + static char tmp[MAX_IPv4_STR_LEN]; /* Danger - not thread safe */ char * start; char * colon; @@ -275,7 +286,7 @@ 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); } @@ -304,13 +315,13 @@ 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"); } } @@ -345,10 +356,12 @@ netboot_info_init(struct in_addr iaddr) 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); + FREE_ZONE(vndevice, MAXPATHLEN, M_NAMEI); info = (struct netboot_info *)kalloc(sizeof(*info)); bzero(info, sizeof(*info)); @@ -358,6 +371,8 @@ netboot_info_init(struct in_addr iaddr) /* 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) { @@ -380,7 +395,7 @@ netboot_info_init(struct in_addr iaddr) 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] != '/') { @@ -412,7 +427,7 @@ netboot_info_init(struct in_addr iaddr) printf("netboot: root path uses unrecognized format\n"); } } - _FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI); + FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI); return (info); } @@ -487,12 +502,12 @@ 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"); } } @@ -526,9 +541,13 @@ 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 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) { @@ -547,59 +566,78 @@ inet_aifaddr(struct socket * so, char * name, const struct in_addr * addr, } 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(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 }; @@ -609,10 +647,10 @@ netboot_mountroot() struct proc * 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(); @@ -662,19 +700,56 @@ 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); + 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); @@ -691,7 +766,6 @@ failed: if (so != NULL) { soclose(so); } - thread_funnel_switch(NETWORK_FUNNEL, KERNEL_FUNNEL); return (error); } @@ -730,20 +804,24 @@ netboot_setup(struct proc * p) if (error == 0 && rootvnode != NULL) { struct vnode *tvp; struct vnode *newdp; + struct vfs_context context; + + context.vc_proc = p; + context.vc_ucred = proc_ucred(p); /* XXX kauth_cred_get() ??? proxy */ /* Get the vnode for '/'. Set fdp->fd_fd.fd_cdir to reference it. */ - if (VFS_ROOT(mountlist.cqh_last, &newdp)) + if (VFS_ROOT(mountlist.tqh_last, &newdp, &context)) 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); @@ -751,7 +829,7 @@ netboot_setup(struct proc * p) } int -netboot_root() +netboot_root(void) { return (S_netboot); }