linux/fs/afs/addr_list.c
<<
>>
Prefs
   1/* Server address list management
   2 *
   3 * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
   4 * Written by David Howells (dhowells@redhat.com)
   5 *
   6 * This program is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU General Public Licence
   8 * as published by the Free Software Foundation; either version
   9 * 2 of the Licence, or (at your option) any later version.
  10 */
  11
  12#include <linux/slab.h>
  13#include <linux/ctype.h>
  14#include <linux/dns_resolver.h>
  15#include <linux/inet.h>
  16#include <keys/rxrpc-type.h>
  17#include "internal.h"
  18#include "afs_fs.h"
  19
  20//#define AFS_MAX_ADDRESSES
  21//      ((unsigned int)((PAGE_SIZE - sizeof(struct afs_addr_list)) /
  22//                      sizeof(struct sockaddr_rxrpc)))
  23#define AFS_MAX_ADDRESSES ((unsigned int)(sizeof(unsigned long) * 8))
  24
  25/*
  26 * Release an address list.
  27 */
  28void afs_put_addrlist(struct afs_addr_list *alist)
  29{
  30        if (alist && refcount_dec_and_test(&alist->usage))
  31                call_rcu(&alist->rcu, (rcu_callback_t)kfree);
  32}
  33
  34/*
  35 * Allocate an address list.
  36 */
  37struct afs_addr_list *afs_alloc_addrlist(unsigned int nr,
  38                                         unsigned short service,
  39                                         unsigned short port)
  40{
  41        struct afs_addr_list *alist;
  42        unsigned int i;
  43
  44        _enter("%u,%u,%u", nr, service, port);
  45
  46        alist = kzalloc(sizeof(*alist) + sizeof(alist->addrs[0]) * nr,
  47                        GFP_KERNEL);
  48        if (!alist)
  49                return NULL;
  50
  51        refcount_set(&alist->usage, 1);
  52
  53        for (i = 0; i < nr; i++) {
  54                struct sockaddr_rxrpc *srx = &alist->addrs[i];
  55                srx->srx_family                 = AF_RXRPC;
  56                srx->srx_service                = service;
  57                srx->transport_type             = SOCK_DGRAM;
  58                srx->transport_len              = sizeof(srx->transport.sin6);
  59                srx->transport.sin6.sin6_family = AF_INET6;
  60                srx->transport.sin6.sin6_port   = htons(port);
  61        }
  62
  63        return alist;
  64}
  65
  66/*
  67 * Parse a text string consisting of delimited addresses.
  68 */
  69struct afs_addr_list *afs_parse_text_addrs(const char *text, size_t len,
  70                                           char delim,
  71                                           unsigned short service,
  72                                           unsigned short port)
  73{
  74        struct afs_addr_list *alist;
  75        const char *p, *end = text + len;
  76        unsigned int nr = 0;
  77
  78        _enter("%*.*s,%c", (int)len, (int)len, text, delim);
  79
  80        if (!len)
  81                return ERR_PTR(-EDESTADDRREQ);
  82
  83        if (delim == ':' && (memchr(text, ',', len) || !memchr(text, '.', len)))
  84                delim = ',';
  85
  86        /* Count the addresses */
  87        p = text;
  88        do {
  89                if (!*p)
  90                        return ERR_PTR(-EINVAL);
  91                if (*p == delim)
  92                        continue;
  93                nr++;
  94                if (*p == '[') {
  95                        p++;
  96                        if (p == end)
  97                                return ERR_PTR(-EINVAL);
  98                        p = memchr(p, ']', end - p);
  99                        if (!p)
 100                                return ERR_PTR(-EINVAL);
 101                        p++;
 102                        if (p >= end)
 103                                break;
 104                }
 105
 106                p = memchr(p, delim, end - p);
 107                if (!p)
 108                        break;
 109                p++;
 110        } while (p < end);
 111
 112        _debug("%u/%u addresses", nr, AFS_MAX_ADDRESSES);
 113        if (nr > AFS_MAX_ADDRESSES)
 114                nr = AFS_MAX_ADDRESSES;
 115
 116        alist = afs_alloc_addrlist(nr, service, port);
 117        if (!alist)
 118                return ERR_PTR(-ENOMEM);
 119
 120        /* Extract the addresses */
 121        p = text;
 122        do {
 123                struct sockaddr_rxrpc *srx = &alist->addrs[alist->nr_addrs];
 124                const char *q, *stop;
 125
 126                if (*p == delim) {
 127                        p++;
 128                        continue;
 129                }
 130
 131                if (*p == '[') {
 132                        p++;
 133                        q = memchr(p, ']', end - p);
 134                } else {
 135                        for (q = p; q < end; q++)
 136                                if (*q == '+' || *q == delim)
 137                                        break;
 138                }
 139
 140                if (in4_pton(p, q - p,
 141                             (u8 *)&srx->transport.sin6.sin6_addr.s6_addr32[3],
 142                             -1, &stop)) {
 143                        srx->transport.sin6.sin6_addr.s6_addr32[0] = 0;
 144                        srx->transport.sin6.sin6_addr.s6_addr32[1] = 0;
 145                        srx->transport.sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
 146                } else if (in6_pton(p, q - p,
 147                                    srx->transport.sin6.sin6_addr.s6_addr,
 148                                    -1, &stop)) {
 149                        /* Nothing to do */
 150                } else {
 151                        goto bad_address;
 152                }
 153
 154                if (stop != q)
 155                        goto bad_address;
 156
 157                p = q;
 158                if (q < end && *q == ']')
 159                        p++;
 160
 161                if (p < end) {
 162                        if (*p == '+') {
 163                                /* Port number specification "+1234" */
 164                                unsigned int xport = 0;
 165                                p++;
 166                                if (p >= end || !isdigit(*p))
 167                                        goto bad_address;
 168                                do {
 169                                        xport *= 10;
 170                                        xport += *p - '0';
 171                                        if (xport > 65535)
 172                                                goto bad_address;
 173                                        p++;
 174                                } while (p < end && isdigit(*p));
 175                                srx->transport.sin6.sin6_port = htons(xport);
 176                        } else if (*p == delim) {
 177                                p++;
 178                        } else {
 179                                goto bad_address;
 180                        }
 181                }
 182
 183                alist->nr_addrs++;
 184        } while (p < end && alist->nr_addrs < AFS_MAX_ADDRESSES);
 185
 186        _leave(" = [nr %u]", alist->nr_addrs);
 187        return alist;
 188
 189bad_address:
 190        kfree(alist);
 191        return ERR_PTR(-EINVAL);
 192}
 193
 194/*
 195 * Compare old and new address lists to see if there's been any change.
 196 * - How to do this in better than O(Nlog(N)) time?
 197 *   - We don't really want to sort the address list, but would rather take the
 198 *     list as we got it so as not to undo record rotation by the DNS server.
 199 */
 200#if 0
 201static int afs_cmp_addr_list(const struct afs_addr_list *a1,
 202                             const struct afs_addr_list *a2)
 203{
 204}
 205#endif
 206
 207/*
 208 * Perform a DNS query for VL servers and build a up an address list.
 209 */
 210struct afs_addr_list *afs_dns_query(struct afs_cell *cell, time64_t *_expiry)
 211{
 212        struct afs_addr_list *alist;
 213        char *vllist = NULL;
 214        int ret;
 215
 216        _enter("%s", cell->name);
 217
 218        ret = dns_query("afsdb", cell->name, cell->name_len,
 219                        "ipv4", &vllist, _expiry);
 220        if (ret < 0)
 221                return ERR_PTR(ret);
 222
 223        alist = afs_parse_text_addrs(vllist, strlen(vllist), ',',
 224                                     VL_SERVICE, AFS_VL_PORT);
 225        if (IS_ERR(alist)) {
 226                kfree(vllist);
 227                if (alist != ERR_PTR(-ENOMEM))
 228                        pr_err("Failed to parse DNS data\n");
 229                return alist;
 230        }
 231
 232        kfree(vllist);
 233        return alist;
 234}
 235
 236/*
 237 * Merge an IPv4 entry into a fileserver address list.
 238 */
 239void afs_merge_fs_addr4(struct afs_addr_list *alist, __be32 xdr, u16 port)
 240{
 241        struct sockaddr_in6 *a;
 242        __be16 xport = htons(port);
 243        int i;
 244
 245        for (i = 0; i < alist->nr_ipv4; i++) {
 246                a = &alist->addrs[i].transport.sin6;
 247                if (xdr == a->sin6_addr.s6_addr32[3] &&
 248                    xport == a->sin6_port)
 249                        return;
 250                if (xdr == a->sin6_addr.s6_addr32[3] &&
 251                    (u16 __force)xport < (u16 __force)a->sin6_port)
 252                        break;
 253                if ((u32 __force)xdr < (u32 __force)a->sin6_addr.s6_addr32[3])
 254                        break;
 255        }
 256
 257        if (i < alist->nr_addrs)
 258                memmove(alist->addrs + i + 1,
 259                        alist->addrs + i,
 260                        sizeof(alist->addrs[0]) * (alist->nr_addrs - i));
 261
 262        a = &alist->addrs[i].transport.sin6;
 263        a->sin6_port              = xport;
 264        a->sin6_addr.s6_addr32[0] = 0;
 265        a->sin6_addr.s6_addr32[1] = 0;
 266        a->sin6_addr.s6_addr32[2] = htonl(0xffff);
 267        a->sin6_addr.s6_addr32[3] = xdr;
 268        alist->nr_ipv4++;
 269        alist->nr_addrs++;
 270}
 271
 272/*
 273 * Merge an IPv6 entry into a fileserver address list.
 274 */
 275void afs_merge_fs_addr6(struct afs_addr_list *alist, __be32 *xdr, u16 port)
 276{
 277        struct sockaddr_in6 *a;
 278        __be16 xport = htons(port);
 279        int i, diff;
 280
 281        for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) {
 282                a = &alist->addrs[i].transport.sin6;
 283                diff = memcmp(xdr, &a->sin6_addr, 16);
 284                if (diff == 0 &&
 285                    xport == a->sin6_port)
 286                        return;
 287                if (diff == 0 &&
 288                    (u16 __force)xport < (u16 __force)a->sin6_port)
 289                        break;
 290                if (diff < 0)
 291                        break;
 292        }
 293
 294        if (i < alist->nr_addrs)
 295                memmove(alist->addrs + i + 1,
 296                        alist->addrs + i,
 297                        sizeof(alist->addrs[0]) * (alist->nr_addrs - i));
 298
 299        a = &alist->addrs[i].transport.sin6;
 300        a->sin6_port              = xport;
 301        a->sin6_addr.s6_addr32[0] = xdr[0];
 302        a->sin6_addr.s6_addr32[1] = xdr[1];
 303        a->sin6_addr.s6_addr32[2] = xdr[2];
 304        a->sin6_addr.s6_addr32[3] = xdr[3];
 305        alist->nr_addrs++;
 306}
 307
 308/*
 309 * Get an address to try.
 310 */
 311bool afs_iterate_addresses(struct afs_addr_cursor *ac)
 312{
 313        _enter("%hu+%hd", ac->start, (short)ac->index);
 314
 315        if (!ac->alist)
 316                return false;
 317
 318        if (ac->begun) {
 319                ac->index++;
 320                if (ac->index == ac->alist->nr_addrs)
 321                        ac->index = 0;
 322
 323                if (ac->index == ac->start) {
 324                        ac->error = -EDESTADDRREQ;
 325                        return false;
 326                }
 327        }
 328
 329        ac->begun = true;
 330        ac->responded = false;
 331        ac->addr = &ac->alist->addrs[ac->index];
 332        return true;
 333}
 334
 335/*
 336 * Release an address list cursor.
 337 */
 338int afs_end_cursor(struct afs_addr_cursor *ac)
 339{
 340        struct afs_addr_list *alist;
 341
 342        alist = ac->alist;
 343        if (alist) {
 344                if (ac->responded && ac->index != ac->start)
 345                        WRITE_ONCE(alist->index, ac->index);
 346                afs_put_addrlist(alist);
 347        }
 348
 349        ac->addr = NULL;
 350        ac->alist = NULL;
 351        ac->begun = false;
 352        return ac->error;
 353}
 354
 355/*
 356 * Set the address cursor for iterating over VL servers.
 357 */
 358int afs_set_vl_cursor(struct afs_addr_cursor *ac, struct afs_cell *cell)
 359{
 360        struct afs_addr_list *alist;
 361        int ret;
 362
 363        if (!rcu_access_pointer(cell->vl_addrs)) {
 364                ret = wait_on_bit(&cell->flags, AFS_CELL_FL_NO_LOOKUP_YET,
 365                                  TASK_INTERRUPTIBLE);
 366                if (ret < 0)
 367                        return ret;
 368
 369                if (!rcu_access_pointer(cell->vl_addrs) &&
 370                    ktime_get_real_seconds() < cell->dns_expiry)
 371                        return cell->error;
 372        }
 373
 374        read_lock(&cell->vl_addrs_lock);
 375        alist = rcu_dereference_protected(cell->vl_addrs,
 376                                          lockdep_is_held(&cell->vl_addrs_lock));
 377        if (alist->nr_addrs > 0)
 378                afs_get_addrlist(alist);
 379        else
 380                alist = NULL;
 381        read_unlock(&cell->vl_addrs_lock);
 382
 383        if (!alist)
 384                return -EDESTADDRREQ;
 385
 386        ac->alist = alist;
 387        ac->addr = NULL;
 388        ac->start = READ_ONCE(alist->index);
 389        ac->index = ac->start;
 390        ac->error = 0;
 391        ac->begun = false;
 392        return 0;
 393}
 394