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                char tdelim = delim;
 125
 126                if (*p == delim) {
 127                        p++;
 128                        continue;
 129                }
 130
 131                if (*p == '[') {
 132                        p++;
 133                        tdelim = ']';
 134                }
 135
 136                if (in4_pton(p, end - p,
 137                             (u8 *)&srx->transport.sin6.sin6_addr.s6_addr32[3],
 138                             tdelim, &p)) {
 139                        srx->transport.sin6.sin6_addr.s6_addr32[0] = 0;
 140                        srx->transport.sin6.sin6_addr.s6_addr32[1] = 0;
 141                        srx->transport.sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
 142                } else if (in6_pton(p, end - p,
 143                                    srx->transport.sin6.sin6_addr.s6_addr,
 144                                    tdelim, &p)) {
 145                        /* Nothing to do */
 146                } else {
 147                        goto bad_address;
 148                }
 149
 150                if (tdelim == ']') {
 151                        if (p == end || *p != ']')
 152                                goto bad_address;
 153                        p++;
 154                }
 155
 156                if (p < end) {
 157                        if (*p == '+') {
 158                                /* Port number specification "+1234" */
 159                                unsigned int xport = 0;
 160                                p++;
 161                                if (p >= end || !isdigit(*p))
 162                                        goto bad_address;
 163                                do {
 164                                        xport *= 10;
 165                                        xport += *p - '0';
 166                                        if (xport > 65535)
 167                                                goto bad_address;
 168                                        p++;
 169                                } while (p < end && isdigit(*p));
 170                                srx->transport.sin6.sin6_port = htons(xport);
 171                        } else if (*p == delim) {
 172                                p++;
 173                        } else {
 174                                goto bad_address;
 175                        }
 176                }
 177
 178                alist->nr_addrs++;
 179        } while (p < end && alist->nr_addrs < AFS_MAX_ADDRESSES);
 180
 181        _leave(" = [nr %u]", alist->nr_addrs);
 182        return alist;
 183
 184bad_address:
 185        kfree(alist);
 186        return ERR_PTR(-EINVAL);
 187}
 188
 189/*
 190 * Compare old and new address lists to see if there's been any change.
 191 * - How to do this in better than O(Nlog(N)) time?
 192 *   - We don't really want to sort the address list, but would rather take the
 193 *     list as we got it so as not to undo record rotation by the DNS server.
 194 */
 195#if 0
 196static int afs_cmp_addr_list(const struct afs_addr_list *a1,
 197                             const struct afs_addr_list *a2)
 198{
 199}
 200#endif
 201
 202/*
 203 * Perform a DNS query for VL servers and build a up an address list.
 204 */
 205struct afs_addr_list *afs_dns_query(struct afs_cell *cell, time64_t *_expiry)
 206{
 207        struct afs_addr_list *alist;
 208        char *vllist = NULL;
 209        int ret;
 210
 211        _enter("%s", cell->name);
 212
 213        ret = dns_query("afsdb", cell->name, cell->name_len,
 214                        "ipv4", &vllist, _expiry);
 215        if (ret < 0)
 216                return ERR_PTR(ret);
 217
 218        alist = afs_parse_text_addrs(vllist, strlen(vllist), ',',
 219                                     VL_SERVICE, AFS_VL_PORT);
 220        if (IS_ERR(alist)) {
 221                kfree(vllist);
 222                if (alist != ERR_PTR(-ENOMEM))
 223                        pr_err("Failed to parse DNS data\n");
 224                return alist;
 225        }
 226
 227        kfree(vllist);
 228        return alist;
 229}
 230
 231/*
 232 * Merge an IPv4 entry into a fileserver address list.
 233 */
 234void afs_merge_fs_addr4(struct afs_addr_list *alist, __be32 xdr, u16 port)
 235{
 236        struct sockaddr_in6 *a;
 237        __be16 xport = htons(port);
 238        int i;
 239
 240        for (i = 0; i < alist->nr_ipv4; i++) {
 241                a = &alist->addrs[i].transport.sin6;
 242                if (xdr == a->sin6_addr.s6_addr32[3] &&
 243                    xport == a->sin6_port)
 244                        return;
 245                if (xdr == a->sin6_addr.s6_addr32[3] &&
 246                    xport < a->sin6_port)
 247                        break;
 248                if (xdr < a->sin6_addr.s6_addr32[3])
 249                        break;
 250        }
 251
 252        if (i < alist->nr_addrs)
 253                memmove(alist->addrs + i + 1,
 254                        alist->addrs + i,
 255                        sizeof(alist->addrs[0]) * (alist->nr_addrs - i));
 256
 257        a = &alist->addrs[i].transport.sin6;
 258        a->sin6_port              = xport;
 259        a->sin6_addr.s6_addr32[0] = 0;
 260        a->sin6_addr.s6_addr32[1] = 0;
 261        a->sin6_addr.s6_addr32[2] = htonl(0xffff);
 262        a->sin6_addr.s6_addr32[3] = xdr;
 263        alist->nr_ipv4++;
 264        alist->nr_addrs++;
 265}
 266
 267/*
 268 * Merge an IPv6 entry into a fileserver address list.
 269 */
 270void afs_merge_fs_addr6(struct afs_addr_list *alist, __be32 *xdr, u16 port)
 271{
 272        struct sockaddr_in6 *a;
 273        __be16 xport = htons(port);
 274        int i, diff;
 275
 276        for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) {
 277                a = &alist->addrs[i].transport.sin6;
 278                diff = memcmp(xdr, &a->sin6_addr, 16);
 279                if (diff == 0 &&
 280                    xport == a->sin6_port)
 281                        return;
 282                if (diff == 0 &&
 283                    xport < a->sin6_port)
 284                        break;
 285                if (diff < 0)
 286                        break;
 287        }
 288
 289        if (i < alist->nr_addrs)
 290                memmove(alist->addrs + i + 1,
 291                        alist->addrs + i,
 292                        sizeof(alist->addrs[0]) * (alist->nr_addrs - i));
 293
 294        a = &alist->addrs[i].transport.sin6;
 295        a->sin6_port              = xport;
 296        a->sin6_addr.s6_addr32[0] = xdr[0];
 297        a->sin6_addr.s6_addr32[1] = xdr[1];
 298        a->sin6_addr.s6_addr32[2] = xdr[2];
 299        a->sin6_addr.s6_addr32[3] = xdr[3];
 300        alist->nr_addrs++;
 301}
 302
 303/*
 304 * Get an address to try.
 305 */
 306bool afs_iterate_addresses(struct afs_addr_cursor *ac)
 307{
 308        _enter("%hu+%hd", ac->start, (short)ac->index);
 309
 310        if (!ac->alist)
 311                return false;
 312
 313        if (ac->begun) {
 314                ac->index++;
 315                if (ac->index == ac->alist->nr_addrs)
 316                        ac->index = 0;
 317
 318                if (ac->index == ac->start) {
 319                        ac->error = -EDESTADDRREQ;
 320                        return false;
 321                }
 322        }
 323
 324        ac->begun = true;
 325        ac->responded = false;
 326        ac->addr = &ac->alist->addrs[ac->index];
 327        return true;
 328}
 329
 330/*
 331 * Release an address list cursor.
 332 */
 333int afs_end_cursor(struct afs_addr_cursor *ac)
 334{
 335        if (ac->responded && ac->index != ac->start)
 336                WRITE_ONCE(ac->alist->index, ac->index);
 337
 338        afs_put_addrlist(ac->alist);
 339        ac->alist = NULL;
 340        return ac->error;
 341}
 342
 343/*
 344 * Set the address cursor for iterating over VL servers.
 345 */
 346int afs_set_vl_cursor(struct afs_addr_cursor *ac, struct afs_cell *cell)
 347{
 348        struct afs_addr_list *alist;
 349        int ret;
 350
 351        if (!rcu_access_pointer(cell->vl_addrs)) {
 352                ret = wait_on_bit(&cell->flags, AFS_CELL_FL_NO_LOOKUP_YET,
 353                                  TASK_INTERRUPTIBLE);
 354                if (ret < 0)
 355                        return ret;
 356
 357                if (!rcu_access_pointer(cell->vl_addrs) &&
 358                    ktime_get_real_seconds() < cell->dns_expiry)
 359                        return cell->error;
 360        }
 361
 362        read_lock(&cell->vl_addrs_lock);
 363        alist = rcu_dereference_protected(cell->vl_addrs,
 364                                          lockdep_is_held(&cell->vl_addrs_lock));
 365        if (alist->nr_addrs > 0)
 366                afs_get_addrlist(alist);
 367        else
 368                alist = NULL;
 369        read_unlock(&cell->vl_addrs_lock);
 370
 371        if (!alist)
 372                return -EDESTADDRREQ;
 373
 374        ac->alist = alist;
 375        ac->addr = NULL;
 376        ac->start = READ_ONCE(alist->index);
 377        ac->index = ac->start;
 378        ac->error = 0;
 379        ac->begun = false;
 380        return 0;
 381}
 382