Index: sys/fs/nfsclient/nfs_clvfsops.c =================================================================== --- sys/fs/nfsclient/nfs_clvfsops.c +++ sys/fs/nfsclient/nfs_clvfsops.c @@ -40,6 +40,8 @@ #include "opt_bootp.h" #include "opt_nfsroot.h" +#include "opt_inet.h" +#include "opt_inet6.h" #include #include @@ -161,8 +163,8 @@ * isn't used in that case. */ #if !defined(NFS_ROOT) -struct nfs_diskless nfs_diskless = { { { 0 } } }; -struct nfsv3_diskless nfsv3_diskless = { { { 0 } } }; +struct nfs_diskless nfs_diskless = { AF_UNSPEC, NULL, { { 0 } } }; +struct nfsv3_diskless nfsv3_diskless = { AF_UNSPEC, NULL, { { 0 } } }; int nfs_diskless_valid = 0; #endif @@ -173,14 +175,14 @@ SYSCTL_STRING(_vfs_nfs, OID_AUTO, diskless_rootpath, CTLFLAG_RD, nfsv3_diskless.root_hostnam, 0, "Path to nfs root"); +/* XXX-BZ unused and not correct anymore. */ SYSCTL_OPAQUE(_vfs_nfs, OID_AUTO, diskless_rootaddr, CTLFLAG_RD, &nfsv3_diskless.root_saddr, sizeof(nfsv3_diskless.root_saddr), "%Ssockaddr_in", "Diskless root nfs address"); void newnfsargs_ntoh(struct nfs_args *); -static int nfs_mountdiskless(char *, - struct sockaddr_in *, struct nfs_args *, +static int nfs_mountdiskless(struct nfsv3_diskless *nd, struct thread *, struct vnode **, struct mount *); static void nfs_convert_diskless(void); static void nfs_convert_oargs(struct nfs_args *args, @@ -254,10 +256,33 @@ nfs_convert_diskless(void) { - bcopy(&nfs_diskless.myif, &nfsv3_diskless.myif, - sizeof(struct ifaliasreq)); - bcopy(&nfs_diskless.mygateway, &nfsv3_diskless.mygateway, - sizeof(struct sockaddr_in)); + switch (nfs_diskless.sa_family) { +#ifdef INET + case PF_INET: + bcopy(&nfs_diskless.myif, &nfsv3_diskless.myif, + sizeof(struct ifaliasreq)); + bcopy(&nfs_diskless.mygateway, &nfsv3_diskless.mygateway, + sizeof(struct sockaddr_in)); + bcopy(&nfs_diskless.root_saddr,&nfsv3_diskless.root_saddr, + sizeof(struct sockaddr_in)); + break; +#endif +#ifdef INET6 + case PF_INET6: + bcopy(&nfs_diskless.myif6, &nfsv3_diskless.myif6, + sizeof(nfs_diskless.myif6)); + bcopy(&nfs_diskless.mygateway6, &nfsv3_diskless.mygateway6, + sizeof(nfs_diskless.mygateway6)); + bcopy(&nfs_diskless.root_saddr6,&nfsv3_diskless.root_saddr6, + sizeof(nfs_diskless.root_saddr6)); + break; +#endif + default: + panic("%s: are you trying to trick me? sa_family %u\n", + __func__, nfs_diskless._mygateway.sa.sa_family); + } + nfsv3_diskless.sa_family = nfs_diskless.sa_family; + nfsv3_diskless.ifp = nfs_diskless.ifp; nfs_convert_oargs(&nfsv3_diskless.root_args,&nfs_diskless.root_args); if (nfsv3_diskless.root_args.flags & NFSMNT_NFSV3) { nfsv3_diskless.root_fhsize = NFSX_MYFH; @@ -266,8 +291,6 @@ nfsv3_diskless.root_fhsize = NFSX_V2FH; bcopy(nfs_diskless.root_fh, nfsv3_diskless.root_fh, NFSX_V2FH); } - bcopy(&nfs_diskless.root_saddr,&nfsv3_diskless.root_saddr, - sizeof(struct sockaddr_in)); bcopy(nfs_diskless.root_hostnam, nfsv3_diskless.root_hostnam, MNAMELEN); nfsv3_diskless.root_time = nfs_diskless.root_time; bcopy(nfs_diskless.my_hostnam, nfsv3_diskless.my_hostnam, @@ -403,9 +426,7 @@ struct vnode *vp; struct ifreq ir; int error; - u_long l; - char buf[128]; - char *cp; + char *afstr, *cp; #if defined(BOOTP_NFSROOT) && defined(BOOTP) bootpc_init(); /* use bootp to get nfs_diskless filled in */ @@ -422,38 +443,37 @@ * Do enough of ifconfig(8) so that the critical net interface can * talk to the server. */ - error = socreate(nd->myif.ifra_addr.sa_family, &so, nd->root_args.sotype, 0, + error = socreate(nd->sa_family, &so, nd->root_args.sotype, 0, td->td_ucred, td); if (error) - panic("nfs_mountroot: socreate(%04x): %d", - nd->myif.ifra_addr.sa_family, error); + panic("%s: socreate(%04x): %d", __func__, nd->sa_family, error); -#if 0 /* XXX Bad idea */ - /* - * We might not have been told the right interface, so we pass - * over the first ten interfaces of the same kind, until we get - * one of them configured. - */ - - for (i = strlen(nd->myif.ifra_name) - 1; - nd->myif.ifra_name[i] >= '0' && - nd->myif.ifra_name[i] <= '9'; - nd->myif.ifra_name[i] ++) { + afstr = ""; + switch (nd->sa_family) { +#ifdef INET + case PF_INET: error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td); - if(!error) - break; - } + break; #endif - error = ifioctl(so, SIOCAIFADDR, (caddr_t)&nd->myif, td); +#ifdef INET6 + case PF_INET6: + error = ifioctl(so, SIOCAIFADDR_IN6, (caddr_t)&nd->myif6, td); + afstr = "_IN6"; + break; +#endif + default: + error = EPFNOSUPPORT; + break; + } if (error) - panic("nfs_mountroot: SIOCAIFADDR: %d", error); + panic("%s: SIOCAIFADDR%s: %d", __func__, afstr, error); if ((cp = kern_getenv("boot.netif.mtu")) != NULL) { ir.ifr_mtu = strtol(cp, NULL, 10); bcopy(nd->myif.ifra_name, ir.ifr_name, IFNAMSIZ); freeenv(cp); error = ifioctl(so, SIOCSIFMTU, (caddr_t)&ir, td); if (error) - printf("nfs_mountroot: SIOCSIFMTU: %d", error); + printf("%s: SIOCSIFMTU: %d", __func__, error); } soclose(so); @@ -463,7 +483,8 @@ * is not set by the DHCP server. Check also for a value of 0 * to avoid panicking inappropriately in that situation. */ - if (nd->mygateway.sin_len != 0 && +#ifdef INET + if (nd->sa_family == PF_INET && nd->mygateway.sin_len != 0 && nd->mygateway.sin_addr.s_addr != 0) { struct sockaddr_in mask, sin; @@ -479,22 +500,37 @@ RTF_UP | RTF_GATEWAY, NULL, RT_DEFAULT_FIB); CURVNET_RESTORE(); if (error) - panic("nfs_mountroot: RTM_ADD: %d", error); + panic("%s: RTM_ADD: %d", __func__, error); } +#endif +#ifdef INET6 + if (nd->sa_family == PF_INET6 && nd->mygateway6.sin6_len != 0 && + !IN6_IS_ADDR_UNSPECIFIED(&nd->mygateway6.sin6_addr)) { + struct sockaddr_in6 sin6, mask; + bzero(&sin6, sizeof(sin6)); + bzero(&mask, sizeof(mask)); + sin6.sin6_family = PF_INET6; + sin6.sin6_len = mask.sin6_len = sizeof(mask); + /* XXX MRT use table 0 for this sort of thing */ + CURVNET_SET(TD_TO_VNET(td)); + error = in6_rtrequest(RTM_ADD, (struct sockaddr *)&sin6, + (struct sockaddr *)&nd->mygateway6, + (struct sockaddr *)&mask, + RTF_UP | RTF_GATEWAY, NULL, RT_DEFAULT_FIB); + CURVNET_RESTORE(); + if (error) + panic("%s: RTM_ADD: %d", __func__, error); + + } +#endif + /* * Create the rootfs mount point. */ nd->root_args.fh = nd->root_fh; nd->root_args.fhsize = nd->root_fhsize; - l = ntohl(nd->root_saddr.sin_addr.s_addr); - snprintf(buf, sizeof(buf), "%ld.%ld.%ld.%ld:%s", - (l >> 24) & 0xff, (l >> 16) & 0xff, - (l >> 8) & 0xff, (l >> 0) & 0xff, nd->root_hostnam); - printf("NFS ROOT: %s\n", buf); - nd->root_args.hostname = buf; - if ((error = nfs_mountdiskless(buf, - &nd->root_saddr, &nd->root_args, td, &vp, mp)) != 0) { + if ((error = nfs_mountdiskless(nd, td, &vp, mp)) != 0) { return (error); } @@ -515,28 +551,77 @@ * Internal version of mount system call for diskless setup. */ static int -nfs_mountdiskless(char *path, - struct sockaddr_in *sin, struct nfs_args *args, struct thread *td, - struct vnode **vpp, struct mount *mp) +nfs_mountdiskless(struct nfsv3_diskless *nd, + struct thread *td, struct vnode **vpp, struct mount *mp) { +#ifdef INET6 + char sname6[INET6_ADDRSTRLEN]; + struct sockaddr_in6 *sin6; +#endif +#ifdef INET + char sname[INET_ADDRSTRLEN]; + struct sockaddr_in *sin; +#endif struct sockaddr *nam; int dirlen, error; + char buf[128]; char *dirpath; + switch (nd->sa_family) { +#ifdef INET + case PF_INET: + sin = &nd->root_saddr; + inet_ntop(PF_INET, &sin->sin_addr, sname, sizeof(sname)); + snprintf(buf, sizeof(buf), "%s:%s", sname, nd->root_hostnam); + nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK); + break; +#endif +#ifdef INET6 + case PF_INET6: + sin6 = &nd->root_saddr6; + inet_ntop(PF_INET6, &sin6->sin6_addr, sname6, sizeof(sname6)); + snprintf(buf, sizeof(buf), "[%s]:%s", sname6, nd->root_hostnam); + nam = sodupsockaddr((struct sockaddr *)sin6, M_WAITOK); + break; +#endif + default: + printf(buf, sizeof(buf), + "%s: IP support in your config is:/nonexistent", __func__); + return (EPFNOSUPPORT); + } + + printf("NFS ROOT: %s\n", buf); + nd->root_args.hostname = buf; + /* * Find the directory path in "path", which also has the server's * name/ip address in it. */ - dirpath = strchr(path, ':'); + dirpath = nd->root_hostnam; if (dirpath != NULL) - dirlen = strlen(++dirpath); + dirlen = strlen(dirpath); else dirlen = 0; - nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK); - if ((error = mountnfs(args, mp, nam, path, NULL, 0, dirpath, dirlen, - NULL, 0, vpp, td->td_ucred, td, NFS_DEFAULT_NAMETIMEO, + + if (nd->ifp != NULL && nd->ifp->if_link_state != LINK_STATE_UP) { + if (bootverbose) + printf("%s: waiting for link up..\n", __func__); + for (error = 0; nd->ifp != NULL && error < 10; error++) { + if (nd->ifp->if_link_state == LINK_STATE_UP) { + break; + } + pause("nfslink", hz); + } + if (bootverbose) + printf("%s: link %s\n", __func__, + (nd->ifp->if_link_state == LINK_STATE_UP) ? + "UP" : "DOWN"); + } + + if ((error = mountnfs(&nd->root_args, mp, nam, buf, NULL, 0, dirpath, + dirlen, NULL, 0, vpp, td->td_ucred, td, NFS_DEFAULT_NAMETIMEO, NFS_DEFAULT_NEGNAMETIMEO, 0)) != 0) { - printf("nfs_mountroot: mount %s on /: %d\n", path, error); + printf("%s: mount %s on /: %d\n", __func__, buf, error); return (error); } return (0); @@ -829,7 +914,7 @@ /* * XXX: hardcoded port number. */ - sin->sin_port = htons(2049); + sin->sin_port = htons(NFS_PORT); *hostnamep = strdup(nam, M_NEWNFSMNT); *sinp = sin; Index: sys/nfs/nfs_diskless.c =================================================================== --- sys/nfs/nfs_diskless.c +++ sys/nfs/nfs_diskless.c @@ -38,6 +38,8 @@ __FBSDID("$FreeBSD$"); #include "opt_bootp.h" +#include "opt_inet.h" +#include "opt_inet6.h" #include #include @@ -54,14 +56,27 @@ #include #include +#if defined(INET6) || defined(INET) #include +#endif +#if defined(INET6) +#include +#include +#endif + #include #include #include #define NFS_IFACE_TIMEOUT_SECS 10 /* Timeout for interface to appear. */ +#ifdef INET static int inaddr_to_sockaddr(char *ev, struct sockaddr_in *sa); +#endif +#ifdef INET6 +static int in6addr_to_sockaddr(char *ev, struct sockaddr_in6 *sin6, + struct sockaddr_in6 *in6mask); +#endif static int hwaddr_to_sockaddr(char *ev, struct sockaddr_dl *sa); static int decode_nfshandle(char *ev, u_char *fh, int maxfh); @@ -70,8 +85,8 @@ * server for a diskless/dataless machine. It is initialized below just * to ensure that it is allocated to initialized data (.data not .bss). */ -struct nfs_diskless nfs_diskless = { { { 0 } } }; -struct nfsv3_diskless nfsv3_diskless = { { { 0 } } }; +struct nfs_diskless nfs_diskless = { AF_UNSPEC, NULL, { { 0 } } }; +struct nfsv3_diskless nfsv3_diskless = { AF_UNSPEC, NULL, { { 0 } } }; int nfs_diskless_valid = 0; /* @@ -146,18 +161,52 @@ free(opts, M_TEMP); } +#ifdef INET6 +static void +setup_myif6(struct in6_aliasreq *ifr, + struct sockaddr_in6 *sin6, struct sockaddr_in6 *mask) +{ + + bcopy(sin6, &ifr->ifra_addr, sizeof(*sin6)); + /* ifra_dstaddr */ + bcopy(mask, &ifr->ifra_prefixmask, sizeof(*mask)); + ifr->ifra_flags = 0; + ifr->ifra_lifetime.ia6t_expire = 0; + ifr->ifra_lifetime.ia6t_preferred = 0; + ifr->ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; + ifr->ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; +} +#endif + +#ifdef INET +static void +setup_myif(struct ifaliasreq *ifr, + struct sockaddr_in *sin, struct sockaddr_in *netmask) +{ + + bcopy(sin, &ifr->ifra_addr, sizeof(*sin)); + bcopy(sin, &ifr->ifra_broadaddr, sizeof(*sin)); + ((struct sockaddr_in *)&ifr->ifra_broadaddr)->sin_addr.s_addr = + sin->sin_addr.s_addr | ~ netmask->sin_addr.s_addr; + bcopy(netmask, &ifr->ifra_mask, sizeof(*netmask)); +} +#endif + /* * Populate the essential fields in the nfsv3_diskless structure. * * The loader is expected to export the following environment variables: * * boot.netif.name name of boot interface - * boot.netif.ip IP address on boot interface - * boot.netif.netmask netmask on boot interface - * boot.netif.gateway default gateway (optional) + * boot.netif.ip IPv4 address on boot interface + * boot.netif.netmask IPv4 netmask on boot interface + * boot.netif.gateway IPv4 default gateway (optional) + * boot.netif.ip6 IPv6 address on boot interface + * boot.netif.gateway6 IPv6 default gateway (optional) * boot.netif.hwaddr hardware address of boot interface * boot.netif.mtu interface mtu from bootp/dhcp (optional) - * boot.nfsroot.server IP address of root filesystem server + * boot.nfsroot.server IPv4 address of root filesystem server + * boot.nfsroot.server6 IPv6 address of root filesystem server * boot.nfsroot.path path of the root filesystem on server * boot.nfsroot.nfshandle NFS handle for root filesystem on server * boot.nfsroot.nfshandlelen and length of this handle (for NFSv3 only) @@ -171,7 +220,14 @@ struct ifnet *ifp; struct ifaddr *ifa; struct sockaddr_dl *sdl, ourdl; - struct sockaddr_in myaddr, netmask; +#ifdef INET + struct sockaddr_in sin, netmask; + bool ipv4; +#endif +#ifdef INET6 + struct sockaddr_in6 sin6, in6mask; + bool ipv6; +#endif char *cp; int cnt, fhlen, is_nfsv3; uint32_t len; @@ -180,43 +236,88 @@ if (nfs_diskless_valid != 0) return; +#if !defined(INET6) && !defined(INET) + printf("%s failing: neither INET nor INET6 support.\n", __func__); + return; +#endif + /* get handle size. If this succeeds, it's an NFSv3 setup. */ if ((cp = kern_getenv("boot.nfsroot.nfshandlelen")) != NULL) { cnt = sscanf(cp, "%d", &len); freeenv(cp); if (cnt != 1 || len == 0 || len > NFSX_V3FHMAX) { - printf("nfs_diskless: bad NFS handle len\n"); + printf("%s: bad NFS handle len\n", __func__); return; } nd3->root_fhsize = len; is_nfsv3 = 1; } else is_nfsv3 = 0; - /* set up interface */ - if (inaddr_to_sockaddr("boot.netif.ip", &myaddr)) - return; - if (inaddr_to_sockaddr("boot.netif.netmask", &netmask)) { - printf("nfs_diskless: no netmask\n"); - return; + /* + * Set up interface. + * For now assume that v4 and v6 addresses are stored + * in different env. variables from loader. Prefer v6 if + * available. Trying a fallback to a 2nd choice is hard in this + * NFS code. If you want v4 do not set v6. + */ +#ifdef INET6 + ipv6 = true; + if (ipv6 && in6addr_to_sockaddr("boot.netif.ip6", &sin6, &in6mask)) { + if (bootverbose) + printf("%s: skipping IPv6, no prefix.\n", __func__); + ipv6 = false; } +#endif +#ifdef INET + ipv4 = true; + if (ipv4 && inaddr_to_sockaddr("boot.netif.ip", &sin)) { + if (bootverbose) + printf("%s: skipping IPv4, no address.\n", __func__); + ipv4 = false; + } + if (ipv4 && inaddr_to_sockaddr("boot.netif.netmask", &netmask)) { + if (bootverbose) + printf("%s: skipping IPv4, no netmask.\n", __func__); + ipv4 = false; + } +#endif + if (is_nfsv3 != 0) { - bcopy(&myaddr, &nd3->myif.ifra_addr, sizeof(myaddr)); - bcopy(&myaddr, &nd3->myif.ifra_broadaddr, sizeof(myaddr)); - ((struct sockaddr_in *) - &nd3->myif.ifra_broadaddr)->sin_addr.s_addr = - myaddr.sin_addr.s_addr | ~ netmask.sin_addr.s_addr; - bcopy(&netmask, &nd3->myif.ifra_mask, sizeof(netmask)); +#ifdef INET6 + if (ipv6) { + nd3->sa_family = AF_INET6; + setup_myif6(&nd3->myif6, &sin6, &in6mask); + } +#endif +#if defined(INET6) && defined(INET) + else +#endif +#ifdef INET + if (ipv4) { + nd3->sa_family = AF_INET; + setup_myif(&nd3->myif, &sin, &netmask); + } +#endif } else { - bcopy(&myaddr, &nd->myif.ifra_addr, sizeof(myaddr)); - bcopy(&myaddr, &nd->myif.ifra_broadaddr, sizeof(myaddr)); - ((struct sockaddr_in *) - &nd->myif.ifra_broadaddr)->sin_addr.s_addr = - myaddr.sin_addr.s_addr | ~ netmask.sin_addr.s_addr; - bcopy(&netmask, &nd->myif.ifra_mask, sizeof(netmask)); +#ifdef INET6 + if (ipv6) { + nd->sa_family = AF_INET6; + setup_myif6(&nd->myif6, &sin6, &in6mask); + } +#endif +#if defined(INET6) && defined(INET) + else +#endif +#ifdef INET + if (ipv4) { + nd->sa_family = AF_INET; + setup_myif(&nd->myif, &sin, &netmask); + } +#endif } if (hwaddr_to_sockaddr("boot.netif.hwaddr", &ourdl)) { - printf("nfs_diskless: no hardware address\n"); + printf("%s aborting: no hardware address\n", __func__); return; } ifa = NULL; @@ -246,37 +347,58 @@ pause("nfssdl", hz / 5); goto retry; } - printf("nfs_diskless: no interface\n"); + printf("%s: no interface\n", __func__); return; /* no matching interface */ match_done: kern_setenv("boot.netif.name", ifp->if_xname); - if (is_nfsv3 != 0) { - strlcpy(nd3->myif.ifra_name, ifp->if_xname, - sizeof(nd3->myif.ifra_name)); - - /* set up gateway */ - inaddr_to_sockaddr("boot.netif.gateway", &nd3->mygateway); + if (is_nfsv3 != 0) { + nd3->ifp = ifp; /* XXX-BZ this is a dangerous game; ref it! */ +#ifdef INET6 + if (ipv6) { + strlcpy(nd3->myif6.ifra_name, ifp->if_xname, + sizeof(nd3->myif6.ifra_name)); + if (in6addr_to_sockaddr("boot.nfsroot.server6", + &nd3->root_saddr6, NULL)) { + printf("%s: no IPv6 server\n", __func__); + return; + } + nd3->root_saddr6.sin6_port = htons(NFS_PORT); + in6addr_to_sockaddr("boot.netif.gateway6", + &nd3->mygateway6, NULL); + } +#endif +#if defined(INET6) && defined(INET) + else +#endif +#ifdef INET + if (ipv4) { + strlcpy(nd3->myif.ifra_name, ifp->if_xname, + sizeof(nd3->myif.ifra_name)); + if (inaddr_to_sockaddr("boot.nfsroot.server", + &nd3->root_saddr)) { + printf("%s: no IPv4 server\n", __func__); + return; + } + nd3->root_saddr.sin_port = htons(NFS_PORT); + inaddr_to_sockaddr("boot.netif.gateway", + &nd3->mygateway); + } +#endif /* set up root mount */ nd3->root_args.rsize = 32768; /* XXX tunable? */ nd3->root_args.wsize = 32768; nd3->root_args.sotype = SOCK_STREAM; nd3->root_args.flags = (NFSMNT_NFSV3 | NFSMNT_WSIZE | NFSMNT_RSIZE | NFSMNT_RESVPORT); - if (inaddr_to_sockaddr("boot.nfsroot.server", - &nd3->root_saddr)) { - printf("nfs_diskless: no server\n"); - return; - } - nd3->root_saddr.sin_port = htons(NFS_PORT); fhlen = decode_nfshandle("boot.nfsroot.nfshandle", &nd3->root_fh[0], NFSX_V3FHMAX); if (fhlen == 0) { - printf("nfs_diskless: no NFS handle\n"); + printf("%s: no NFS handle\n", __func__); return; } if (fhlen != nd3->root_fhsize) { - printf("nfs_diskless: bad NFS handle len=%d\n", fhlen); + printf("%s: bad NFS handle len=%d\n", __func__, fhlen); return; } if ((cp = kern_getenv("boot.nfsroot.path")) != NULL) { @@ -290,27 +412,48 @@ nfs_diskless_valid = 3; } else { - strlcpy(nd->myif.ifra_name, ifp->if_xname, - sizeof(nd->myif.ifra_name)); - - /* set up gateway */ - inaddr_to_sockaddr("boot.netif.gateway", &nd->mygateway); - + nd->ifp = ifp; +#ifdef INET6 + if (ipv6) { + strlcpy(nd->myif6.ifra_name, ifp->if_xname, + sizeof(nd->myif6.ifra_name)); + if (in6addr_to_sockaddr("boot.nfsroot.server6", + &nd->root_saddr6, NULL)) { + printf("%s: no IPv6 server\n", __func__); + return; + } + nd->root_saddr6.sin6_port = htons(NFS_PORT); + in6addr_to_sockaddr("boot.netif.gateway6", + &nd->mygateway6, NULL); + } +#endif +#if defined(INET6) && defined(INET) + else +#endif +#ifdef INET + if (ipv4) { + strlcpy(nd->myif.ifra_name, ifp->if_xname, + sizeof(nd->myif.ifra_name)); + if (inaddr_to_sockaddr("boot.nfsroot.server", + &nd->root_saddr)) { + printf("%s: no IPv4 server\n", __func__); + return; + } + nd->root_saddr.sin_port = htons(NFS_PORT); + inaddr_to_sockaddr("boot.netif.gateway", + &nd->mygateway); + } +#endif /* set up root mount */ nd->root_args.rsize = 8192; /* XXX tunable? */ nd->root_args.wsize = 8192; nd->root_args.sotype = SOCK_STREAM; nd->root_args.flags = (NFSMNT_WSIZE | NFSMNT_RSIZE | NFSMNT_RESVPORT); - if (inaddr_to_sockaddr("boot.nfsroot.server", - &nd->root_saddr)) { - printf("nfs_diskless: no server\n"); - return; - } - nd->root_saddr.sin_port = htons(NFS_PORT); + if (decode_nfshandle("boot.nfsroot.nfshandle", &nd->root_fh[0], NFSX_V2FH) == 0) { - printf("nfs_diskless: no NFS handle\n"); + printf("%s: no NFS handle\n", __func__); return; } if ((cp = kern_getenv("boot.nfsroot.path")) != NULL) { @@ -340,27 +483,85 @@ } } +#ifdef INET static int -inaddr_to_sockaddr(char *ev, struct sockaddr_in *sa) +inaddr_to_sockaddr(char *ev, struct sockaddr_in *sin) { - u_int32_t a[4]; char *cp; - int count; + int rc; - bzero(sa, sizeof(*sa)); - sa->sin_len = sizeof(*sa); - sa->sin_family = AF_INET; + bzero(sin, sizeof(*sin)); + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; if ((cp = kern_getenv(ev)) == NULL) return (1); - count = sscanf(cp, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]); + rc = inet_pton(AF_INET, cp, &sin->sin_addr); freeenv(cp); - if (count != 4) + if (rc != 1) return (1); - sa->sin_addr.s_addr = - htonl((a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3]); return (0); } +#endif + +#ifdef INET6 +static int +in6addr_to_sockaddr(char *ev, struct sockaddr_in6 *sin6, + struct sockaddr_in6 *in6mask) +{ + char *cp, *p, *pend; + unsigned long l; + int rc; + + bzero(sin6, sizeof(*sin6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(*sin6); + + if ((cp = kern_getenv(ev)) == NULL) + return (1); + + p = strchr(cp, '/'); + if (p != NULL) + *p++ = '\0'; + rc = inet_pton(AF_INET6, cp, &sin6->sin6_addr); + if (rc != 1) { + freeenv(cp); + return (1); + } + + if (in6mask != NULL) { + /* + * Try to convert the /prefix into something we can + * use as a mask. + */ + bzero(in6mask, sizeof(*in6mask)); + /* We do not set faimly for the mask? */ + in6mask->sin6_len = sizeof(*in6mask); + if (p != NULL && *p != '\0') { + l = strtoul(p, &pend, 0); + if (l == ULONG_MAX || (l == 0 && pend == p)) { + freeenv(cp); + return (1); + } + /* If l > 128, input error, use /64. */ + if (l > 128) + in6mask->sin6_addr = in6mask64; + else if (l == 0 || l == 128) + in6mask->sin6_addr = in6mask128; + else { + for (p = (u_char *)&in6mask->sin6_addr; l > 7; + l -= 8) + *p++ = 0xff; + *p = 0xff << (8 - l); + } + } else + in6mask->sin6_addr = in6mask64; + } + freeenv(cp); + + return (0); +} +#endif static int hwaddr_to_sockaddr(char *ev, struct sockaddr_dl *sa) Index: sys/nfs/nfsdiskless.h =================================================================== --- sys/nfs/nfsdiskless.h +++ sys/nfs/nfsdiskless.h @@ -38,7 +38,19 @@ #ifndef _NFSCLIENT_NFSDISKLESS_H_ #define _NFSCLIENT_NFSDISKLESS_H_ +#ifndef _SOCKADDR_UNION_DEFINED +#define _SOCKADDR_UNION_DEFINED /* + * The union of all possible address formats we handle. + */ +union sockaddr_union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; +}; +#endif /* _SOCKADDR_UNION_DEFINED */ + +/* * Structure that must be initialized for a diskless nfs client. * This structure is used by nfs_mountroot() to set up the root vnode, * and to do a partial ifconfig(8) and route(8) so that the critical net @@ -59,16 +71,27 @@ * still Version 2 anyhow.) */ struct nfsv3_diskless { - struct ifaliasreq myif; /* Default interface */ - struct sockaddr_in mygateway; /* Default gateway */ + sa_family_t sa_family; + struct ifnet *ifp; + union { + struct ifaliasreq _myif; /* Default interface */ + struct in6_aliasreq _myif6; + } _lif; + union sockaddr_union _mygateway; /* Default gateway */ struct nfs_args root_args; /* Mount args for root fs */ int root_fhsize; /* Size of root file handle */ u_char root_fh[NFSX_V3FHMAX]; /* File handle of root dir */ - struct sockaddr_in root_saddr; /* Address of root server */ + union sockaddr_union _root_saddr; /* Address of root server */ char root_hostnam[MNAMELEN]; /* Host name for mount pt */ long root_time; /* Timestamp of root fs */ char my_hostnam[MAXHOSTNAMELEN]; /* Client host name */ }; +#define myif _lif._myif +#define myif6 _lif._myif6 +#define mygateway _mygateway.sin +#define mygateway6 _mygateway.sin6 +#define root_saddr _root_saddr.sin +#define root_saddr6 _root_saddr.sin6 /* * Old arguments to mount NFS @@ -94,11 +117,16 @@ }; struct nfs_diskless { - struct ifaliasreq myif; /* Default interface */ - struct sockaddr_in mygateway; /* Default gateway */ + sa_family_t sa_family; + struct ifnet *ifp; + union { + struct ifaliasreq _myif; /* Default interface */ + struct in6_aliasreq _myif6; + } _lif; + union sockaddr_union _mygateway; /* Default gateway */ struct onfs_args root_args; /* Mount args for root fs */ u_char root_fh[NFSX_V2FH]; /* File handle of root dir */ - struct sockaddr_in root_saddr; /* Address of root server */ + union sockaddr_union _root_saddr; /* Address of root server */ char root_hostnam[MNAMELEN]; /* Host name for mount pt */ long root_time; /* Timestamp of root fs */ char my_hostnam[MAXHOSTNAMELEN]; /* Client host name */