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

J. Bruce Fields
2008-07-10T19:30:20Z

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....

--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