Blame view

fs/afs/addr_list.c 8.54 KB
b4d0d230c   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
8b2a464ce   David Howells   afs: Add an addre...
2
3
4
5
  /* Server address list management
   *
   * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
8b2a464ce   David Howells   afs: Add an addre...
6
7
8
9
10
11
12
13
14
   */
  
  #include <linux/slab.h>
  #include <linux/ctype.h>
  #include <linux/dns_resolver.h>
  #include <linux/inet.h>
  #include <keys/rxrpc-type.h>
  #include "internal.h"
  #include "afs_fs.h"
8b2a464ce   David Howells   afs: Add an addre...
15
16
17
18
19
20
  /*
   * Release an address list.
   */
  void afs_put_addrlist(struct afs_addr_list *alist)
  {
  	if (alist && refcount_dec_and_test(&alist->usage))
ddd2b85ff   Jann Horn   afs: Use kfree_rc...
21
  		kfree_rcu(alist, rcu);
8b2a464ce   David Howells   afs: Add an addre...
22
23
24
25
26
27
28
29
30
31
32
33
34
  }
  
  /*
   * Allocate an address list.
   */
  struct afs_addr_list *afs_alloc_addrlist(unsigned int nr,
  					 unsigned short service,
  					 unsigned short port)
  {
  	struct afs_addr_list *alist;
  	unsigned int i;
  
  	_enter("%u,%u,%u", nr, service, port);
68eb64c3d   David Howells   afs: Do better ma...
35
36
  	if (nr > AFS_MAX_ADDRESSES)
  		nr = AFS_MAX_ADDRESSES;
acafe7e30   Kees Cook   treewide: Use str...
37
  	alist = kzalloc(struct_size(alist, addrs, nr), GFP_KERNEL);
8b2a464ce   David Howells   afs: Add an addre...
38
39
40
41
  	if (!alist)
  		return NULL;
  
  	refcount_set(&alist->usage, 1);
68eb64c3d   David Howells   afs: Do better ma...
42
  	alist->max_addrs = nr;
8b2a464ce   David Howells   afs: Add an addre...
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
  
  	for (i = 0; i < nr; i++) {
  		struct sockaddr_rxrpc *srx = &alist->addrs[i];
  		srx->srx_family			= AF_RXRPC;
  		srx->srx_service		= service;
  		srx->transport_type		= SOCK_DGRAM;
  		srx->transport_len		= sizeof(srx->transport.sin6);
  		srx->transport.sin6.sin6_family	= AF_INET6;
  		srx->transport.sin6.sin6_port	= htons(port);
  	}
  
  	return alist;
  }
  
  /*
   * Parse a text string consisting of delimited addresses.
   */
0a5143f2f   David Howells   afs: Implement VL...
60
61
62
63
64
  struct afs_vlserver_list *afs_parse_text_addrs(struct afs_net *net,
  					       const char *text, size_t len,
  					       char delim,
  					       unsigned short service,
  					       unsigned short port)
8b2a464ce   David Howells   afs: Add an addre...
65
  {
0a5143f2f   David Howells   afs: Implement VL...
66
  	struct afs_vlserver_list *vllist;
8b2a464ce   David Howells   afs: Add an addre...
67
68
  	struct afs_addr_list *alist;
  	const char *p, *end = text + len;
0a5143f2f   David Howells   afs: Implement VL...
69
  	const char *problem;
8b2a464ce   David Howells   afs: Add an addre...
70
  	unsigned int nr = 0;
0a5143f2f   David Howells   afs: Implement VL...
71
  	int ret = -ENOMEM;
8b2a464ce   David Howells   afs: Add an addre...
72
73
  
  	_enter("%*.*s,%c", (int)len, (int)len, text, delim);
0a5143f2f   David Howells   afs: Implement VL...
74
75
  	if (!len) {
  		_leave(" = -EDESTADDRREQ [empty]");
8b2a464ce   David Howells   afs: Add an addre...
76
  		return ERR_PTR(-EDESTADDRREQ);
0a5143f2f   David Howells   afs: Implement VL...
77
  	}
8b2a464ce   David Howells   afs: Add an addre...
78
79
80
81
82
83
84
  
  	if (delim == ':' && (memchr(text, ',', len) || !memchr(text, '.', len)))
  		delim = ',';
  
  	/* Count the addresses */
  	p = text;
  	do {
0a5143f2f   David Howells   afs: Implement VL...
85
86
87
88
  		if (!*p) {
  			problem = "nul";
  			goto inval;
  		}
8b2a464ce   David Howells   afs: Add an addre...
89
90
91
92
93
  		if (*p == delim)
  			continue;
  		nr++;
  		if (*p == '[') {
  			p++;
0a5143f2f   David Howells   afs: Implement VL...
94
95
96
97
  			if (p == end) {
  				problem = "brace1";
  				goto inval;
  			}
8b2a464ce   David Howells   afs: Add an addre...
98
  			p = memchr(p, ']', end - p);
0a5143f2f   David Howells   afs: Implement VL...
99
100
101
102
  			if (!p) {
  				problem = "brace2";
  				goto inval;
  			}
8b2a464ce   David Howells   afs: Add an addre...
103
104
105
106
107
108
109
110
111
112
113
114
  			p++;
  			if (p >= end)
  				break;
  		}
  
  		p = memchr(p, delim, end - p);
  		if (!p)
  			break;
  		p++;
  	} while (p < end);
  
  	_debug("%u/%u addresses", nr, AFS_MAX_ADDRESSES);
8b2a464ce   David Howells   afs: Add an addre...
115

0a5143f2f   David Howells   afs: Implement VL...
116
117
  	vllist = afs_alloc_vlserver_list(1);
  	if (!vllist)
8b2a464ce   David Howells   afs: Add an addre...
118
  		return ERR_PTR(-ENOMEM);
0a5143f2f   David Howells   afs: Implement VL...
119
120
121
122
123
124
125
126
  	vllist->nr_servers = 1;
  	vllist->servers[0].server = afs_alloc_vlserver("<dummy>", 7, AFS_VL_PORT);
  	if (!vllist->servers[0].server)
  		goto error_vl;
  
  	alist = afs_alloc_addrlist(nr, service, AFS_VL_PORT);
  	if (!alist)
  		goto error;
8b2a464ce   David Howells   afs: Add an addre...
127
128
129
  	/* Extract the addresses */
  	p = text;
  	do {
01fd79e6d   David Howells   afs: Fix address ...
130
  		const char *q, *stop;
4c19bbdc7   David Howells   afs: Always build...
131
132
133
  		unsigned int xport = port;
  		__be32 x[4];
  		int family;
8b2a464ce   David Howells   afs: Add an addre...
134
135
136
137
138
139
140
141
  
  		if (*p == delim) {
  			p++;
  			continue;
  		}
  
  		if (*p == '[') {
  			p++;
01fd79e6d   David Howells   afs: Fix address ...
142
143
144
145
146
  			q = memchr(p, ']', end - p);
  		} else {
  			for (q = p; q < end; q++)
  				if (*q == '+' || *q == delim)
  					break;
8b2a464ce   David Howells   afs: Add an addre...
147
  		}
0a5143f2f   David Howells   afs: Implement VL...
148
  		if (in4_pton(p, q - p, (u8 *)&x[0], -1, &stop)) {
4c19bbdc7   David Howells   afs: Always build...
149
  			family = AF_INET;
0a5143f2f   David Howells   afs: Implement VL...
150
  		} else if (in6_pton(p, q - p, (u8 *)x, -1, &stop)) {
4c19bbdc7   David Howells   afs: Always build...
151
  			family = AF_INET6;
0a5143f2f   David Howells   afs: Implement VL...
152
153
  		} else {
  			problem = "family";
8b2a464ce   David Howells   afs: Add an addre...
154
  			goto bad_address;
0a5143f2f   David Howells   afs: Implement VL...
155
  		}
8b2a464ce   David Howells   afs: Add an addre...
156

0a5143f2f   David Howells   afs: Implement VL...
157
158
159
  		p = q;
  		if (stop != p) {
  			problem = "nostop";
01fd79e6d   David Howells   afs: Fix address ...
160
  			goto bad_address;
0a5143f2f   David Howells   afs: Implement VL...
161
  		}
01fd79e6d   David Howells   afs: Fix address ...
162

01fd79e6d   David Howells   afs: Fix address ...
163
  		if (q < end && *q == ']')
8b2a464ce   David Howells   afs: Add an addre...
164
  			p++;
8b2a464ce   David Howells   afs: Add an addre...
165
166
167
168
  
  		if (p < end) {
  			if (*p == '+') {
  				/* Port number specification "+1234" */
4c19bbdc7   David Howells   afs: Always build...
169
  				xport = 0;
8b2a464ce   David Howells   afs: Add an addre...
170
  				p++;
0a5143f2f   David Howells   afs: Implement VL...
171
172
  				if (p >= end || !isdigit(*p)) {
  					problem = "port";
8b2a464ce   David Howells   afs: Add an addre...
173
  					goto bad_address;
0a5143f2f   David Howells   afs: Implement VL...
174
  				}
8b2a464ce   David Howells   afs: Add an addre...
175
176
177
  				do {
  					xport *= 10;
  					xport += *p - '0';
0a5143f2f   David Howells   afs: Implement VL...
178
179
  					if (xport > 65535) {
  						problem = "pval";
8b2a464ce   David Howells   afs: Add an addre...
180
  						goto bad_address;
0a5143f2f   David Howells   afs: Implement VL...
181
  					}
8b2a464ce   David Howells   afs: Add an addre...
182
183
  					p++;
  				} while (p < end && isdigit(*p));
8b2a464ce   David Howells   afs: Add an addre...
184
185
186
  			} else if (*p == delim) {
  				p++;
  			} else {
0a5143f2f   David Howells   afs: Implement VL...
187
  				problem = "weird";
8b2a464ce   David Howells   afs: Add an addre...
188
189
190
  				goto bad_address;
  			}
  		}
4c19bbdc7   David Howells   afs: Always build...
191
192
193
194
195
196
  		if (family == AF_INET)
  			afs_merge_fs_addr4(alist, x[0], xport);
  		else
  			afs_merge_fs_addr6(alist, x, xport);
  
  	} while (p < end);
8b2a464ce   David Howells   afs: Add an addre...
197

0a5143f2f   David Howells   afs: Implement VL...
198
  	rcu_assign_pointer(vllist->servers[0].server->addresses, alist);
8b2a464ce   David Howells   afs: Add an addre...
199
  	_leave(" = [nr %u]", alist->nr_addrs);
0a5143f2f   David Howells   afs: Implement VL...
200
  	return vllist;
8b2a464ce   David Howells   afs: Add an addre...
201

0a5143f2f   David Howells   afs: Implement VL...
202
203
204
  inval:
  	_leave(" = -EINVAL [%s %zu %*.*s]",
  	       problem, p - text, (int)len, (int)len, text);
8b2a464ce   David Howells   afs: Add an addre...
205
  	return ERR_PTR(-EINVAL);
0a5143f2f   David Howells   afs: Implement VL...
206
207
208
209
210
211
212
213
214
  bad_address:
  	_leave(" = -EINVAL [%s %zu %*.*s]",
  	       problem, p - text, (int)len, (int)len, text);
  	ret = -EINVAL;
  error:
  	afs_put_addrlist(alist);
  error_vl:
  	afs_put_vlserverlist(net, vllist);
  	return ERR_PTR(ret);
8b2a464ce   David Howells   afs: Add an addre...
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
  }
  
  /*
   * Compare old and new address lists to see if there's been any change.
   * - How to do this in better than O(Nlog(N)) time?
   *   - We don't really want to sort the address list, but would rather take the
   *     list as we got it so as not to undo record rotation by the DNS server.
   */
  #if 0
  static int afs_cmp_addr_list(const struct afs_addr_list *a1,
  			     const struct afs_addr_list *a2)
  {
  }
  #endif
  
  /*
   * Perform a DNS query for VL servers and build a up an address list.
   */
0a5143f2f   David Howells   afs: Implement VL...
233
  struct afs_vlserver_list *afs_dns_query(struct afs_cell *cell, time64_t *_expiry)
8b2a464ce   David Howells   afs: Add an addre...
234
  {
0a5143f2f   David Howells   afs: Implement VL...
235
236
  	struct afs_vlserver_list *vllist;
  	char *result = NULL;
8b2a464ce   David Howells   afs: Add an addre...
237
238
239
  	int ret;
  
  	_enter("%s", cell->name);
a58946c15   David Howells   keys: Pass the ne...
240
241
  	ret = dns_query(cell->net->net, "afsdb", cell->name, cell->name_len,
  			"srv=1", &result, _expiry, true);
0a5143f2f   David Howells   afs: Implement VL...
242
243
  	if (ret < 0) {
  		_leave(" = %d [dns]", ret);
8b2a464ce   David Howells   afs: Add an addre...
244
  		return ERR_PTR(ret);
8b2a464ce   David Howells   afs: Add an addre...
245
  	}
0a5143f2f   David Howells   afs: Implement VL...
246
247
248
249
250
251
252
253
254
255
256
257
258
259
  	if (*_expiry == 0)
  		*_expiry = ktime_get_real_seconds() + 60;
  
  	if (ret > 1 && result[0] == 0)
  		vllist = afs_extract_vlserver_list(cell, result, ret);
  	else
  		vllist = afs_parse_text_addrs(cell->net, result, ret, ',',
  					      VL_SERVICE, AFS_VL_PORT);
  	kfree(result);
  	if (IS_ERR(vllist) && vllist != ERR_PTR(-ENOMEM))
  		pr_err("Failed to parse DNS data %ld
  ", PTR_ERR(vllist));
  
  	return vllist;
8b2a464ce   David Howells   afs: Add an addre...
260
261
262
  }
  
  /*
d2ddc776a   David Howells   afs: Overhaul vol...
263
264
   * Merge an IPv4 entry into a fileserver address list.
   */
bf99a53ce   David Howells   afs: Make use of ...
265
  void afs_merge_fs_addr4(struct afs_addr_list *alist, __be32 xdr, u16 port)
d2ddc776a   David Howells   afs: Overhaul vol...
266
  {
46894a135   David Howells   rxrpc: Use IPv4 a...
267
  	struct sockaddr_rxrpc *srx;
66be646bd   David Howells   afs: Sort address...
268
  	u32 addr = ntohl(xdr);
d2ddc776a   David Howells   afs: Overhaul vol...
269
  	int i;
68eb64c3d   David Howells   afs: Do better ma...
270
271
  	if (alist->nr_addrs >= alist->max_addrs)
  		return;
d2ddc776a   David Howells   afs: Overhaul vol...
272
  	for (i = 0; i < alist->nr_ipv4; i++) {
46894a135   David Howells   rxrpc: Use IPv4 a...
273
274
275
  		struct sockaddr_in *a = &alist->addrs[i].transport.sin;
  		u32 a_addr = ntohl(a->sin_addr.s_addr);
  		u16 a_port = ntohs(a->sin_port);
66be646bd   David Howells   afs: Sort address...
276
277
  
  		if (addr == a_addr && port == a_port)
d2ddc776a   David Howells   afs: Overhaul vol...
278
  			return;
66be646bd   David Howells   afs: Sort address...
279
  		if (addr == a_addr && port < a_port)
bf99a53ce   David Howells   afs: Make use of ...
280
  			break;
66be646bd   David Howells   afs: Sort address...
281
  		if (addr < a_addr)
d2ddc776a   David Howells   afs: Overhaul vol...
282
283
284
285
286
287
288
  			break;
  	}
  
  	if (i < alist->nr_addrs)
  		memmove(alist->addrs + i + 1,
  			alist->addrs + i,
  			sizeof(alist->addrs[0]) * (alist->nr_addrs - i));
46894a135   David Howells   rxrpc: Use IPv4 a...
289
  	srx = &alist->addrs[i];
3bf0fb6f3   David Howells   afs: Probe multip...
290
291
  	srx->srx_family = AF_RXRPC;
  	srx->transport_type = SOCK_DGRAM;
46894a135   David Howells   rxrpc: Use IPv4 a...
292
293
294
295
  	srx->transport_len = sizeof(srx->transport.sin);
  	srx->transport.sin.sin_family = AF_INET;
  	srx->transport.sin.sin_port = htons(port);
  	srx->transport.sin.sin_addr.s_addr = xdr;
d2ddc776a   David Howells   afs: Overhaul vol...
296
297
298
299
300
  	alist->nr_ipv4++;
  	alist->nr_addrs++;
  }
  
  /*
bf99a53ce   David Howells   afs: Make use of ...
301
302
303
304
   * Merge an IPv6 entry into a fileserver address list.
   */
  void afs_merge_fs_addr6(struct afs_addr_list *alist, __be32 *xdr, u16 port)
  {
46894a135   David Howells   rxrpc: Use IPv4 a...
305
  	struct sockaddr_rxrpc *srx;
bf99a53ce   David Howells   afs: Make use of ...
306
  	int i, diff;
68eb64c3d   David Howells   afs: Do better ma...
307
308
  	if (alist->nr_addrs >= alist->max_addrs)
  		return;
bf99a53ce   David Howells   afs: Make use of ...
309
  	for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) {
66be646bd   David Howells   afs: Sort address...
310
311
  		struct sockaddr_in6 *a = &alist->addrs[i].transport.sin6;
  		u16 a_port = ntohs(a->sin6_port);
bf99a53ce   David Howells   afs: Make use of ...
312
  		diff = memcmp(xdr, &a->sin6_addr, 16);
66be646bd   David Howells   afs: Sort address...
313
  		if (diff == 0 && port == a_port)
bf99a53ce   David Howells   afs: Make use of ...
314
  			return;
66be646bd   David Howells   afs: Sort address...
315
  		if (diff == 0 && port < a_port)
bf99a53ce   David Howells   afs: Make use of ...
316
317
318
319
320
321
322
323
324
  			break;
  		if (diff < 0)
  			break;
  	}
  
  	if (i < alist->nr_addrs)
  		memmove(alist->addrs + i + 1,
  			alist->addrs + i,
  			sizeof(alist->addrs[0]) * (alist->nr_addrs - i));
46894a135   David Howells   rxrpc: Use IPv4 a...
325
  	srx = &alist->addrs[i];
3bf0fb6f3   David Howells   afs: Probe multip...
326
327
  	srx->srx_family = AF_RXRPC;
  	srx->transport_type = SOCK_DGRAM;
46894a135   David Howells   rxrpc: Use IPv4 a...
328
329
330
331
  	srx->transport_len = sizeof(srx->transport.sin6);
  	srx->transport.sin6.sin6_family = AF_INET6;
  	srx->transport.sin6.sin6_port = htons(port);
  	memcpy(&srx->transport.sin6.sin6_addr, xdr, 16);
bf99a53ce   David Howells   afs: Make use of ...
332
333
334
335
  	alist->nr_addrs++;
  }
  
  /*
8b2a464ce   David Howells   afs: Add an addre...
336
337
338
339
   * Get an address to try.
   */
  bool afs_iterate_addresses(struct afs_addr_cursor *ac)
  {
3bf0fb6f3   David Howells   afs: Probe multip...
340
341
  	unsigned long set, failed;
  	int index;
8b2a464ce   David Howells   afs: Add an addre...
342
343
344
  
  	if (!ac->alist)
  		return false;
3bf0fb6f3   David Howells   afs: Probe multip...
345
346
347
  	set = ac->alist->responded;
  	failed = ac->alist->failed;
  	_enter("%lx-%lx-%lx,%d", set, failed, ac->tried, ac->index);
744bcd713   David Howells   afs: Allow dumpin...
348
  	ac->nr_iterations++;
3bf0fb6f3   David Howells   afs: Probe multip...
349
  	set &= ~(failed | ac->tried);
8b2a464ce   David Howells   afs: Add an addre...
350

3bf0fb6f3   David Howells   afs: Probe multip...
351
352
353
354
355
356
357
358
  	if (!set)
  		return false;
  
  	index = READ_ONCE(ac->alist->preferred);
  	if (test_bit(index, &set))
  		goto selected;
  
  	index = __ffs(set);
8b2a464ce   David Howells   afs: Add an addre...
359

3bf0fb6f3   David Howells   afs: Probe multip...
360
361
362
  selected:
  	ac->index = index;
  	set_bit(index, &ac->tried);
8b2a464ce   David Howells   afs: Add an addre...
363
  	ac->responded = false;
8b2a464ce   David Howells   afs: Add an addre...
364
365
366
367
368
369
370
371
  	return true;
  }
  
  /*
   * Release an address list cursor.
   */
  int afs_end_cursor(struct afs_addr_cursor *ac)
  {
fe4d774c8   David Howells   afs: Fix missing ...
372
373
374
375
  	struct afs_addr_list *alist;
  
  	alist = ac->alist;
  	if (alist) {
3bf0fb6f3   David Howells   afs: Probe multip...
376
377
378
379
  		if (ac->responded &&
  		    ac->index != alist->preferred &&
  		    test_bit(ac->alist->preferred, &ac->tried))
  			WRITE_ONCE(alist->preferred, ac->index);
fe4d774c8   David Howells   afs: Fix missing ...
380
  		afs_put_addrlist(alist);
3bf0fb6f3   David Howells   afs: Probe multip...
381
  		ac->alist = NULL;
fe4d774c8   David Howells   afs: Fix missing ...
382
  	}
8b2a464ce   David Howells   afs: Add an addre...
383

8b2a464ce   David Howells   afs: Add an addre...
384
385
  	return ac->error;
  }