cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

addr_list.c (8748B)


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