]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/kern/netboot.c
xnu-4570.41.2.tar.gz
[apple/xnu.git] / bsd / kern / netboot.c
index cd594de26a0547a94416b4430e8cf7d6b7710fe8..e8bdddb34f618145b1219e2bcd8800f2c27723e6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001-2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2001-2013 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -45,6 +45,7 @@
 #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);
 
@@ -82,33 +81,10 @@ const 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]
@@ -132,29 +108,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:
@@ -168,7 +125,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;
@@ -255,10 +212,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 */
+    static char        tmp[MAX_IPv4_STR_LEN];  /* Danger - not thread safe */
     char *     start;
     char *     colon;
 
@@ -297,7 +254,7 @@ parse_netboot_path(char * path, struct in_addr * iaddr_p, char * * host,
 }
 
 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') {
@@ -334,16 +291,16 @@ get_root_path(char * root_path)
     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;
        }
@@ -353,36 +310,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 (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;
@@ -395,14 +363,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 = FALSE;
-
+               
                info->image_path_length = strlen(image_path) + 1;
                if (image_path[0] != '/') {
                    needs_slash = TRUE;
@@ -410,11 +378,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);
            }
@@ -422,16 +391,27 @@ 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);
     return (info);
@@ -452,6 +432,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;
@@ -487,8 +470,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);
 }
@@ -518,21 +501,21 @@ get_ip_parameters(struct in_addr * iaddr_p, struct in_addr * netmask_p,
        }
     }
     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;
@@ -542,41 +525,13 @@ get_ip_parameters(struct in_addr * iaddr_p, struct in_addr * netmask_p,
     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;
     
@@ -600,28 +555,31 @@ route_cmd(int cmd, struct in_addr d, struct in_addr g,
     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 *
@@ -629,8 +587,9 @@ find_interface(void)
 {
     struct ifnet *             ifp = NULL;
 
+    dlil_if_lock();
     if (rootdevice[0]) {
-               ifp = ifunit(rootdevice);
+               ifp = ifunit((char *)rootdevice);
     }
     if (ifp == NULL) {
                ifnet_head_lock_shared();
@@ -639,9 +598,44 @@ find_interface(void)
                                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)
 {
@@ -650,14 +644,13 @@ 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) {
@@ -665,7 +658,7 @@ netboot_mountroot(void)
        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 */
@@ -682,12 +675,8 @@ netboot_mountroot(void)
 
     /* 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) {
@@ -740,7 +729,8 @@ netboot_mountroot(void)
                /* 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", 
@@ -748,7 +738,7 @@ netboot_mountroot(void)
                }
                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);
@@ -758,7 +748,7 @@ netboot_mountroot(void)
        }
        break;
     case kNetBootImageTypeHTTP:
-       error = netboot_setup(procp);
+       error = netboot_setup();
        break;
     }
     if (error == 0) {
@@ -776,60 +766,26 @@ failed:
 }
 
 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);