From 35f38de3c9e29f26df89dae34cbf8da31057d391 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Fri, 27 Jul 2018 23:10:28 +0000 Subject: [PATCH] Add support for IPv6 addresses to the pNFS "-p" option. This patch adds code to handle IPv6 addresses returned by getaddrinfo() for the host entries in the "-p" command line argument. If the IPv6 address is a link local address, only use it if it is the only address for the host. This is done since there is no way to know if the NFSv4.1 pNFS client is in the same scope zone as the MDS. inet_ntop() is used for the IPv6 address translation, since the client will have no use for the scope zone suffix and inet_ntop() does not put this in the address string. Discussed with: bu7cher@yandex.ru --- usr.sbin/nfsd/nfsd.c | 57 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/usr.sbin/nfsd/nfsd.c b/usr.sbin/nfsd/nfsd.c index c6a14ae6b894..2414ba8d7b85 100644 --- a/usr.sbin/nfsd/nfsd.c +++ b/usr.sbin/nfsd/nfsd.c @@ -1179,14 +1179,16 @@ backup_stable(__unused int signo) static void parse_dsserver(const char *optionarg, struct nfsd_nfsd_args *nfsdargp) { - char *ad, *cp, *cp2, *dsaddr, *dshost, *dspath, *dsvol, nfsprt[9]; - char *mdspath, *mdsp; + char *cp, *cp2, *dsaddr, *dshost, *dspath, *dsvol, nfsprt[9]; + char *mdspath, *mdsp, ip6[INET6_ADDRSTRLEN]; + const char *ad; int ecode; u_int adsiz, dsaddrcnt, dshostcnt, dspathcnt, hostsiz, pathsiz; u_int mdspathcnt; size_t dsaddrsiz, dshostsiz, dspathsiz, nfsprtsiz, mdspathsiz; - struct addrinfo hints, *ai_tcp; + struct addrinfo hints, *ai_tcp, *res; struct sockaddr_in sin; + struct sockaddr_in6 sin6; cp = strdup(optionarg); if (cp == NULL) @@ -1275,22 +1277,55 @@ parse_dsserver(const char *optionarg, struct nfsd_nfsd_args *nfsdargp) /* Get the fully qualified domain name and IP address. */ memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_CANONNAME; - hints.ai_family = AF_INET; + hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; + hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; ecode = getaddrinfo(cp, NULL, &hints, &ai_tcp); if (ecode != 0) err(1, "getaddrinfo pnfs: %s %s", cp, gai_strerror(ecode)); - if (ai_tcp->ai_addr->sa_family != AF_INET || - ai_tcp->ai_addrlen < sizeof(sin)) - err(1, "getaddrinfo() returned non-INET address"); - /* Mips cares about sockaddr_in alignment, so copy the addr. */ - memcpy(&sin, ai_tcp->ai_addr, sizeof(sin)); + ad = NULL; + for (res = ai_tcp; res != NULL; res = res->ai_next) { + if (res->ai_addr->sa_family == AF_INET) { + if (res->ai_addrlen < sizeof(sin)) + err(1, "getaddrinfo() returned " + "undersized IPv4 address"); + /* + * Mips cares about sockaddr_in alignment, + * so copy the address. + */ + memcpy(&sin, res->ai_addr, sizeof(sin)); + ad = inet_ntoa(sin.sin_addr); + break; + } else if (res->ai_family == AF_INET6) { + if (res->ai_addrlen < sizeof(sin6)) + err(1, "getaddrinfo() returned " + "undersized IPv6 address"); + /* + * Mips cares about sockaddr_in6 alignment, + * so copy the address. + */ + memcpy(&sin6, res->ai_addr, sizeof(sin6)); + ad = inet_ntop(AF_INET6, &sin6.sin6_addr, ip6, + sizeof(ip6)); + + /* + * XXX + * Since a link local address will only + * work if the client and DS are in the + * same scope zone, only use it if it is + * the only address. + */ + if (ad != NULL && + !IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr)) + break; + } + } + if (ad == NULL) + err(1, "No IP address for %s", cp); /* Append this address to dsaddr. */ - ad = inet_ntoa(sin.sin_addr); adsiz = strlen(ad); if (dsaddrcnt + adsiz + nfsprtsiz + 1 > dsaddrsiz) { dsaddrsiz *= 2;