Re: [PATCH 04/14] text-based mount command: get_client_address support for IPv6

Chuck Lever
2008-07-10T19:37:46Z

On Jul 10, 2008, at 3:30 PM, J. Bruce Fields wrote:
> On Wed, Jul 09, 2008 at 08:37:24PM -0400, Chuck Lever wrote:
>> Introduce IPv6-enabled version of get_client_address. The legacy
>> mount
>> command could use this eventually as well.
>>
>> I don't remember how to tell an NFSv4 server to disable the callback
>> channel: whether an ANY address is passed with SETCLIENTID, or a
>> loopback address is passed. The patch allows either to be used
>> with a
>> compile-time switch.
>
> I would have thought INADDR_ANY. But in any case we should just pick
> one....

Well, yes, but we should pick the "correct" one. :-) The patch does
it this way just to make a note of this issue so we can make a
decision before committing this upstream.

Does RFC 3530 have any recommendation about this?

>
>
> --b.
>
>>
>> Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
>> ---
>>
>> utils/mount/network.c | 122 +++++++++++++++++++++++++++++++++++++++
>> ++++++++++
>> utils/mount/network.h | 2 +
>> 2 files changed, 124 insertions(+), 0 deletions(-)
>>
>> diff --git a/utils/mount/network.c b/utils/mount/network.c
>> index 3f2721b..128d7f7 100644
>> --- a/utils/mount/network.c
>> +++ b/utils/mount/network.c
>> @@ -918,3 +918,125 @@ int get_client_address(struct sockaddr_in
>> *saddr, struct sockaddr_in *caddr)
>> }
>> return 1;
>> }
>> +
>> +/*
>> + * Try a getsockname() on a connected datagram socket.
>> + *
>> + * Returns 1 and fills in @buf if successful; otherwise, zero.
>> + *
>> + * A connected datagram socket prevents leaving a socket in
>> TIME_WAIT.
>> + * This conserves the ephemeral port number space, helping reduce
>> failed
>> + * socket binds during mount storms.
>> + */
>> +static int nfs_ca_sockname(const struct sockaddr *sap, const
>> socklen_t salen,
>> + struct sockaddr *buf, socklen_t *buflen)
>> +{
>> + struct sockaddr_in sin = {
>> + .sin_family = AF_INET,
>> + .sin_addr.s_addr = htonl(INADDR_ANY),
>> + };
>> + struct sockaddr_in6 sin6 = {
>> + .sin6_family = AF_INET6,
>> + .sin6_addr = IN6ADDR_ANY_INIT,
>> + };
>> + int sock;
>> +
>> + sock = socket(sap->sa_family, SOCK_DGRAM, IPPROTO_UDP);
>> + if (sock < 0)
>> + return 0;
>> +
>> + switch (sap->sa_family) {
>> + case AF_INET:
>> + if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
>> + close(sock);
>> + return 0;
>> + }
>> + break;
>> + case AF_INET6:
>> + if (bind(sock, (struct sockaddr *)&sin6, sizeof(sin6)) < 0) {
>> + close(sock);
>> + return 0;
>> + }
>> + break;
>> + default:
>> + errno = EAFNOSUPPORT;
>> + return 0;
>> + }
>> +
>> + if (connect(sock, sap, salen) < 0) {
>> + close(sock);
>> + return 0;
>> + }
>> +
>> + return !getsockname(sock, buf, buflen);
>> +}
>> +
>> +/*
>> + * Try to generate an address that prevents the server from
>> calling us.
>> + *
>> + * Returns 1 and fills in @buf if successful; otherwise, zero.
>> + */
>> +static int nfs_ca_gai(const struct sockaddr *sap, const socklen_t
>> salen,
>> + struct sockaddr *buf, socklen_t *buflen)
>> +{
>> + struct addrinfo *gai_results;
>> + struct addrinfo gai_hint = {
>> + .ai_family = sap->sa_family,
>> +#ifdef GENERATE_LOOPBACK_ADDRESS
>> + .ai_flags = 0, /* loopback */
>> +#else
>> + .ai_flags = AI_PASSIVE, /* ANYADDR */
>> +#endif
>> + };
>> +
>> + if (getaddrinfo(NULL, "", &gai_hint, &gai_results))
>> + return 0;
>> +
>> + *buflen = gai_results->ai_addrlen;
>> + memcpy(buf, gai_results->ai_addr, *buflen);
>> +
>> + freeaddrinfo(gai_results);
>> +
>> + return 1;
>> +}
>> +
>> +/**
>> + * nfs_callback_address - acquire our local network address
>> + * @sap: pointer to address of remote
>> + * @sap_len: length of address
>> + * @buf: pointer to buffer to be filled in with local network
>> address
>> + * @buflen: IN: length of buffer to fill in; OUT: length of filled-
>> in address
>> + *
>> + * Discover a network address that an NFSv4 server can use to call
>> us back.
>> + * On multi-homed clients, this address depends on which NIC we
>> use to
>> + * route requests to the server.
>> + *
>> + * Returns 1 and fills in @buf if an unambiguous local address is
>> + * available; returns 1 and fills in an appropriate ANYADDR address
>> + * if a local address isn't available; otherwise, returns zero.
>> + */
>> +int nfs_callback_address(const struct sockaddr *sap, const
>> socklen_t salen,
>> + struct sockaddr *buf, socklen_t *buflen)
>> +{
>> + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)buf;
>> +
>> + if (nfs_ca_sockname(sap, salen, buf, buflen) == 0)
>> + if (nfs_ca_gai(sap, salen, buf, buflen) == 0)
>> + goto out_failed;
>> +
>> + /*
>> + * The server can't use an interface ID that was generated
>> + * here on the client, so always clear sin6_scope_id.
>> + */
>> + if (sin6->sin6_family == AF_INET6)
>> + sin6->sin6_scope_id = 0;
>> +
>> + return 1;
>> +
>> +out_failed:
>> + *buflen = 0;
>> + if (verbose)
>> + nfs_error(_("%s: failed to construct callback address"));
>> + return 0;
>> +
>> +}
>> diff --git a/utils/mount/network.h b/utils/mount/network.h
>> index 8da7e20..2f4ff3a 100644
>> --- a/utils/mount/network.h
>> +++ b/utils/mount/network.h
>> @@ -58,6 +58,8 @@ int nfs_string_to_sockaddr(const char *, const
>> size_t,
>> int nfs_present_sockaddr(const struct sockaddr *,
>> const socklen_t, char *, const size_t);
>> int get_client_address(struct sockaddr_in *, struct sockaddr_in *);
>> +int nfs_callback_address(const struct sockaddr *, const socklen_t,
>> + struct sockaddr *, socklen_t *);
>> int nfs_call_umount(clnt_addr_t *, dirpath *);
>> int clnt_ping(struct sockaddr_in *, const unsigned long,
>> const unsigned long, const unsigned int,
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-
>> nfs" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html

--
Chuck Lever
chuck[dot]lever[at]oracle[dot]com