linux/net/sunrpc/addr.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright 2009, Oracle.  All rights reserved.
   4 *
   5 * Convert socket addresses to presentation addresses and universal
   6 * addresses, and vice versa.
   7 *
   8 * Universal addresses are introduced by RFC 1833 and further refined by
   9 * recent RFCs describing NFSv4.  The universal address format is part
  10 * of the external (network) interface provided by rpcbind version 3
  11 * and 4, and by NFSv4.  Such an address is a string containing a
  12 * presentation format IP address followed by a port number in
  13 * "hibyte.lobyte" format.
  14 *
  15 * IPv6 addresses can also include a scope ID, typically denoted by
  16 * a '%' followed by a device name or a non-negative integer.  Refer to
  17 * RFC 4291, Section 2.2 for details on IPv6 presentation formats.
  18 */
  19
  20#include <net/ipv6.h>
  21#include <linux/sunrpc/addr.h>
  22#include <linux/sunrpc/msg_prot.h>
  23#include <linux/slab.h>
  24#include <linux/export.h>
  25
  26#if IS_ENABLED(CONFIG_IPV6)
  27
  28static size_t rpc_ntop6_noscopeid(const struct sockaddr *sap,
  29                                  char *buf, const int buflen)
  30{
  31        const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
  32        const struct in6_addr *addr = &sin6->sin6_addr;
  33
  34        /*
  35         * RFC 4291, Section 2.2.2
  36         *
  37         * Shorthanded ANY address
  38         */
  39        if (ipv6_addr_any(addr))
  40                return snprintf(buf, buflen, "::");
  41
  42        /*
  43         * RFC 4291, Section 2.2.2
  44         *
  45         * Shorthanded loopback address
  46         */
  47        if (ipv6_addr_loopback(addr))
  48                return snprintf(buf, buflen, "::1");
  49
  50        /*
  51         * RFC 4291, Section 2.2.3
  52         *
  53         * Special presentation address format for mapped v4
  54         * addresses.
  55         */
  56        if (ipv6_addr_v4mapped(addr))
  57                return snprintf(buf, buflen, "::ffff:%pI4",
  58                                        &addr->s6_addr32[3]);
  59
  60        /*
  61         * RFC 4291, Section 2.2.1
  62         */
  63        return snprintf(buf, buflen, "%pI6c", addr);
  64}
  65
  66static size_t rpc_ntop6(const struct sockaddr *sap,
  67                        char *buf, const size_t buflen)
  68{
  69        const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
  70        char scopebuf[IPV6_SCOPE_ID_LEN];
  71        size_t len;
  72        int rc;
  73
  74        len = rpc_ntop6_noscopeid(sap, buf, buflen);
  75        if (unlikely(len == 0))
  76                return len;
  77
  78        if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL))
  79                return len;
  80        if (sin6->sin6_scope_id == 0)
  81                return len;
  82
  83        rc = snprintf(scopebuf, sizeof(scopebuf), "%c%u",
  84                        IPV6_SCOPE_DELIMITER, sin6->sin6_scope_id);
  85        if (unlikely((size_t)rc >= sizeof(scopebuf)))
  86                return 0;
  87
  88        len += rc;
  89        if (unlikely(len >= buflen))
  90                return 0;
  91
  92        strcat(buf, scopebuf);
  93        return len;
  94}
  95
  96#else   /* !IS_ENABLED(CONFIG_IPV6) */
  97
  98static size_t rpc_ntop6_noscopeid(const struct sockaddr *sap,
  99                                  char *buf, const int buflen)
 100{
 101        return 0;
 102}
 103
 104static size_t rpc_ntop6(const struct sockaddr *sap,
 105                        char *buf, const size_t buflen)
 106{
 107        return 0;
 108}
 109
 110#endif  /* !IS_ENABLED(CONFIG_IPV6) */
 111
 112static int rpc_ntop4(const struct sockaddr *sap,
 113                     char *buf, const size_t buflen)
 114{
 115        const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
 116
 117        return snprintf(buf, buflen, "%pI4", &sin->sin_addr);
 118}
 119
 120/**
 121 * rpc_ntop - construct a presentation address in @buf
 122 * @sap: socket address
 123 * @buf: construction area
 124 * @buflen: size of @buf, in bytes
 125 *
 126 * Plants a %NUL-terminated string in @buf and returns the length
 127 * of the string, excluding the %NUL.  Otherwise zero is returned.
 128 */
 129size_t rpc_ntop(const struct sockaddr *sap, char *buf, const size_t buflen)
 130{
 131        switch (sap->sa_family) {
 132        case AF_INET:
 133                return rpc_ntop4(sap, buf, buflen);
 134        case AF_INET6:
 135                return rpc_ntop6(sap, buf, buflen);
 136        }
 137
 138        return 0;
 139}
 140EXPORT_SYMBOL_GPL(rpc_ntop);
 141
 142static size_t rpc_pton4(const char *buf, const size_t buflen,
 143                        struct sockaddr *sap, const size_t salen)
 144{
 145        struct sockaddr_in *sin = (struct sockaddr_in *)sap;
 146        u8 *addr = (u8 *)&sin->sin_addr.s_addr;
 147
 148        if (buflen > INET_ADDRSTRLEN || salen < sizeof(struct sockaddr_in))
 149                return 0;
 150
 151        memset(sap, 0, sizeof(struct sockaddr_in));
 152
 153        if (in4_pton(buf, buflen, addr, '\0', NULL) == 0)
 154                return 0;
 155
 156        sin->sin_family = AF_INET;
 157        return sizeof(struct sockaddr_in);
 158}
 159
 160#if IS_ENABLED(CONFIG_IPV6)
 161static int rpc_parse_scope_id(struct net *net, const char *buf,
 162                              const size_t buflen, const char *delim,
 163                              struct sockaddr_in6 *sin6)
 164{
 165        char *p;
 166        size_t len;
 167
 168        if ((buf + buflen) == delim)
 169                return 1;
 170
 171        if (*delim != IPV6_SCOPE_DELIMITER)
 172                return 0;
 173
 174        if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL))
 175                return 0;
 176
 177        len = (buf + buflen) - delim - 1;
 178        p = kmemdup_nul(delim + 1, len, GFP_KERNEL);
 179        if (p) {
 180                u32 scope_id = 0;
 181                struct net_device *dev;
 182
 183                dev = dev_get_by_name(net, p);
 184                if (dev != NULL) {
 185                        scope_id = dev->ifindex;
 186                        dev_put(dev);
 187                } else {
 188                        if (kstrtou32(p, 10, &scope_id) != 0) {
 189                                kfree(p);
 190                                return 0;
 191                        }
 192                }
 193
 194                kfree(p);
 195
 196                sin6->sin6_scope_id = scope_id;
 197                return 1;
 198        }
 199
 200        return 0;
 201}
 202
 203static size_t rpc_pton6(struct net *net, const char *buf, const size_t buflen,
 204                        struct sockaddr *sap, const size_t salen)
 205{
 206        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
 207        u8 *addr = (u8 *)&sin6->sin6_addr.in6_u;
 208        const char *delim;
 209
 210        if (buflen > (INET6_ADDRSTRLEN + IPV6_SCOPE_ID_LEN) ||
 211            salen < sizeof(struct sockaddr_in6))
 212                return 0;
 213
 214        memset(sap, 0, sizeof(struct sockaddr_in6));
 215
 216        if (in6_pton(buf, buflen, addr, IPV6_SCOPE_DELIMITER, &delim) == 0)
 217                return 0;
 218
 219        if (!rpc_parse_scope_id(net, buf, buflen, delim, sin6))
 220                return 0;
 221
 222        sin6->sin6_family = AF_INET6;
 223        return sizeof(struct sockaddr_in6);
 224}
 225#else
 226static size_t rpc_pton6(struct net *net, const char *buf, const size_t buflen,
 227                        struct sockaddr *sap, const size_t salen)
 228{
 229        return 0;
 230}
 231#endif
 232
 233/**
 234 * rpc_pton - Construct a sockaddr in @sap
 235 * @net: applicable network namespace
 236 * @buf: C string containing presentation format IP address
 237 * @buflen: length of presentation address in bytes
 238 * @sap: buffer into which to plant socket address
 239 * @salen: size of buffer in bytes
 240 *
 241 * Returns the size of the socket address if successful; otherwise
 242 * zero is returned.
 243 *
 244 * Plants a socket address in @sap and returns the size of the
 245 * socket address, if successful.  Returns zero if an error
 246 * occurred.
 247 */
 248size_t rpc_pton(struct net *net, const char *buf, const size_t buflen,
 249                struct sockaddr *sap, const size_t salen)
 250{
 251        unsigned int i;
 252
 253        for (i = 0; i < buflen; i++)
 254                if (buf[i] == ':')
 255                        return rpc_pton6(net, buf, buflen, sap, salen);
 256        return rpc_pton4(buf, buflen, sap, salen);
 257}
 258EXPORT_SYMBOL_GPL(rpc_pton);
 259
 260/**
 261 * rpc_sockaddr2uaddr - Construct a universal address string from @sap.
 262 * @sap: socket address
 263 * @gfp_flags: allocation mode
 264 *
 265 * Returns a %NUL-terminated string in dynamically allocated memory;
 266 * otherwise NULL is returned if an error occurred.  Caller must
 267 * free the returned string.
 268 */
 269char *rpc_sockaddr2uaddr(const struct sockaddr *sap, gfp_t gfp_flags)
 270{
 271        char portbuf[RPCBIND_MAXUADDRPLEN];
 272        char addrbuf[RPCBIND_MAXUADDRLEN];
 273        unsigned short port;
 274
 275        switch (sap->sa_family) {
 276        case AF_INET:
 277                if (rpc_ntop4(sap, addrbuf, sizeof(addrbuf)) == 0)
 278                        return NULL;
 279                port = ntohs(((struct sockaddr_in *)sap)->sin_port);
 280                break;
 281        case AF_INET6:
 282                if (rpc_ntop6_noscopeid(sap, addrbuf, sizeof(addrbuf)) == 0)
 283                        return NULL;
 284                port = ntohs(((struct sockaddr_in6 *)sap)->sin6_port);
 285                break;
 286        default:
 287                return NULL;
 288        }
 289
 290        if (snprintf(portbuf, sizeof(portbuf),
 291                     ".%u.%u", port >> 8, port & 0xff) > (int)sizeof(portbuf))
 292                return NULL;
 293
 294        if (strlcat(addrbuf, portbuf, sizeof(addrbuf)) > sizeof(addrbuf))
 295                return NULL;
 296
 297        return kstrdup(addrbuf, gfp_flags);
 298}
 299
 300/**
 301 * rpc_uaddr2sockaddr - convert a universal address to a socket address.
 302 * @net: applicable network namespace
 303 * @uaddr: C string containing universal address to convert
 304 * @uaddr_len: length of universal address string
 305 * @sap: buffer into which to plant socket address
 306 * @salen: size of buffer
 307 *
 308 * @uaddr does not have to be '\0'-terminated, but kstrtou8() and
 309 * rpc_pton() require proper string termination to be successful.
 310 *
 311 * Returns the size of the socket address if successful; otherwise
 312 * zero is returned.
 313 */
 314size_t rpc_uaddr2sockaddr(struct net *net, const char *uaddr,
 315                          const size_t uaddr_len, struct sockaddr *sap,
 316                          const size_t salen)
 317{
 318        char *c, buf[RPCBIND_MAXUADDRLEN + sizeof('\0')];
 319        u8 portlo, porthi;
 320        unsigned short port;
 321
 322        if (uaddr_len > RPCBIND_MAXUADDRLEN)
 323                return 0;
 324
 325        memcpy(buf, uaddr, uaddr_len);
 326
 327        buf[uaddr_len] = '\0';
 328        c = strrchr(buf, '.');
 329        if (unlikely(c == NULL))
 330                return 0;
 331        if (unlikely(kstrtou8(c + 1, 10, &portlo) != 0))
 332                return 0;
 333
 334        *c = '\0';
 335        c = strrchr(buf, '.');
 336        if (unlikely(c == NULL))
 337                return 0;
 338        if (unlikely(kstrtou8(c + 1, 10, &porthi) != 0))
 339                return 0;
 340
 341        port = (unsigned short)((porthi << 8) | portlo);
 342
 343        *c = '\0';
 344        if (rpc_pton(net, buf, strlen(buf), sap, salen) == 0)
 345                return 0;
 346
 347        switch (sap->sa_family) {
 348        case AF_INET:
 349                ((struct sockaddr_in *)sap)->sin_port = htons(port);
 350                return sizeof(struct sockaddr_in);
 351        case AF_INET6:
 352                ((struct sockaddr_in6 *)sap)->sin6_port = htons(port);
 353                return sizeof(struct sockaddr_in6);
 354        }
 355
 356        return 0;
 357}
 358EXPORT_SYMBOL_GPL(rpc_uaddr2sockaddr);
 359