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