xref: /linux/fs/afs/addr_list.c (revision ae22a94997b8a03dcb3c922857c203246711f9d4)
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 static void afs_free_addrlist(struct rcu_head *rcu)
17 {
18 	struct afs_addr_list *alist = container_of(rcu, struct afs_addr_list, rcu);
19 	unsigned int i;
20 
21 	for (i = 0; i < alist->nr_addrs; i++)
22 		rxrpc_kernel_put_peer(alist->addrs[i].peer);
23 	trace_afs_alist(alist->debug_id, refcount_read(&alist->usage), afs_alist_trace_free);
24 	kfree(alist);
25 }
26 
27 /*
28  * Release an address list.
29  */
30 void afs_put_addrlist(struct afs_addr_list *alist, enum afs_alist_trace reason)
31 {
32 	unsigned int debug_id;
33 	bool dead;
34 	int r;
35 
36 	if (!alist)
37 		return;
38 	debug_id = alist->debug_id;
39 	dead = __refcount_dec_and_test(&alist->usage, &r);
40 	trace_afs_alist(debug_id, r - 1, reason);
41 	if (dead)
42 		call_rcu(&alist->rcu, afs_free_addrlist);
43 }
44 
45 struct afs_addr_list *afs_get_addrlist(struct afs_addr_list *alist, enum afs_alist_trace reason)
46 {
47 	int r;
48 
49 	if (alist) {
50 		__refcount_inc(&alist->usage, &r);
51 		trace_afs_alist(alist->debug_id, r + 1, reason);
52 	}
53 	return alist;
54 }
55 
56 /*
57  * Allocate an address list.
58  */
59 struct afs_addr_list *afs_alloc_addrlist(unsigned int nr)
60 {
61 	struct afs_addr_list *alist;
62 	static atomic_t debug_id;
63 
64 	_enter("%u", nr);
65 
66 	if (nr > AFS_MAX_ADDRESSES)
67 		nr = AFS_MAX_ADDRESSES;
68 
69 	alist = kzalloc(struct_size(alist, addrs, nr), GFP_KERNEL);
70 	if (!alist)
71 		return NULL;
72 
73 	refcount_set(&alist->usage, 1);
74 	alist->max_addrs = nr;
75 	alist->debug_id = atomic_inc_return(&debug_id);
76 	trace_afs_alist(alist->debug_id, 1, afs_alist_trace_alloc);
77 	return alist;
78 }
79 
80 /*
81  * Parse a text string consisting of delimited addresses.
82  */
83 struct afs_vlserver_list *afs_parse_text_addrs(struct afs_net *net,
84 					       const char *text, size_t len,
85 					       char delim,
86 					       unsigned short service,
87 					       unsigned short port)
88 {
89 	struct afs_vlserver_list *vllist;
90 	struct afs_addr_list *alist;
91 	const char *p, *end = text + len;
92 	const char *problem;
93 	unsigned int nr = 0;
94 	int ret = -ENOMEM;
95 
96 	_enter("%*.*s,%c", (int)len, (int)len, text, delim);
97 
98 	if (!len) {
99 		_leave(" = -EDESTADDRREQ [empty]");
100 		return ERR_PTR(-EDESTADDRREQ);
101 	}
102 
103 	if (delim == ':' && (memchr(text, ',', len) || !memchr(text, '.', len)))
104 		delim = ',';
105 
106 	/* Count the addresses */
107 	p = text;
108 	do {
109 		if (!*p) {
110 			problem = "nul";
111 			goto inval;
112 		}
113 		if (*p == delim)
114 			continue;
115 		nr++;
116 		if (*p == '[') {
117 			p++;
118 			if (p == end) {
119 				problem = "brace1";
120 				goto inval;
121 			}
122 			p = memchr(p, ']', end - p);
123 			if (!p) {
124 				problem = "brace2";
125 				goto inval;
126 			}
127 			p++;
128 			if (p >= end)
129 				break;
130 		}
131 
132 		p = memchr(p, delim, end - p);
133 		if (!p)
134 			break;
135 		p++;
136 	} while (p < end);
137 
138 	_debug("%u/%u addresses", nr, AFS_MAX_ADDRESSES);
139 
140 	vllist = afs_alloc_vlserver_list(1);
141 	if (!vllist)
142 		return ERR_PTR(-ENOMEM);
143 
144 	vllist->nr_servers = 1;
145 	vllist->servers[0].server = afs_alloc_vlserver("<dummy>", 7, AFS_VL_PORT);
146 	if (!vllist->servers[0].server)
147 		goto error_vl;
148 
149 	alist = afs_alloc_addrlist(nr);
150 	if (!alist)
151 		goto error;
152 
153 	/* Extract the addresses */
154 	p = text;
155 	do {
156 		const char *q, *stop;
157 		unsigned int xport = port;
158 		__be32 x[4];
159 		int family;
160 
161 		if (*p == delim) {
162 			p++;
163 			continue;
164 		}
165 
166 		if (*p == '[') {
167 			p++;
168 			q = memchr(p, ']', end - p);
169 		} else {
170 			for (q = p; q < end; q++)
171 				if (*q == '+' || *q == delim)
172 					break;
173 		}
174 
175 		if (in4_pton(p, q - p, (u8 *)&x[0], -1, &stop)) {
176 			family = AF_INET;
177 		} else if (in6_pton(p, q - p, (u8 *)x, -1, &stop)) {
178 			family = AF_INET6;
179 		} else {
180 			problem = "family";
181 			goto bad_address;
182 		}
183 
184 		p = q;
185 		if (stop != p) {
186 			problem = "nostop";
187 			goto bad_address;
188 		}
189 
190 		if (q < end && *q == ']')
191 			p++;
192 
193 		if (p < end) {
194 			if (*p == '+') {
195 				/* Port number specification "+1234" */
196 				xport = 0;
197 				p++;
198 				if (p >= end || !isdigit(*p)) {
199 					problem = "port";
200 					goto bad_address;
201 				}
202 				do {
203 					xport *= 10;
204 					xport += *p - '0';
205 					if (xport > 65535) {
206 						problem = "pval";
207 						goto bad_address;
208 					}
209 					p++;
210 				} while (p < end && isdigit(*p));
211 			} else if (*p == delim) {
212 				p++;
213 			} else {
214 				problem = "weird";
215 				goto bad_address;
216 			}
217 		}
218 
219 		if (family == AF_INET)
220 			ret = afs_merge_fs_addr4(net, alist, x[0], xport);
221 		else
222 			ret = afs_merge_fs_addr6(net, alist, x, xport);
223 		if (ret < 0)
224 			goto error;
225 
226 	} while (p < end);
227 
228 	rcu_assign_pointer(vllist->servers[0].server->addresses, alist);
229 	_leave(" = [nr %u]", alist->nr_addrs);
230 	return vllist;
231 
232 inval:
233 	_leave(" = -EINVAL [%s %zu %*.*s]",
234 	       problem, p - text, (int)len, (int)len, text);
235 	return ERR_PTR(-EINVAL);
236 bad_address:
237 	_leave(" = -EINVAL [%s %zu %*.*s]",
238 	       problem, p - text, (int)len, (int)len, text);
239 	ret = -EINVAL;
240 error:
241 	afs_put_addrlist(alist, afs_alist_trace_put_parse_error);
242 error_vl:
243 	afs_put_vlserverlist(net, vllist);
244 	return ERR_PTR(ret);
245 }
246 
247 /*
248  * Perform a DNS query for VL servers and build a up an address list.
249  */
250 struct afs_vlserver_list *afs_dns_query(struct afs_cell *cell, time64_t *_expiry)
251 {
252 	struct afs_vlserver_list *vllist;
253 	char *result = NULL;
254 	int ret;
255 
256 	_enter("%s", cell->name);
257 
258 	ret = dns_query(cell->net->net, "afsdb", cell->name, cell->name_len,
259 			"srv=1", &result, _expiry, true);
260 	if (ret < 0) {
261 		_leave(" = %d [dns]", ret);
262 		return ERR_PTR(ret);
263 	}
264 
265 	if (*_expiry == 0)
266 		*_expiry = ktime_get_real_seconds() + 60;
267 
268 	if (ret > 1 && result[0] == 0)
269 		vllist = afs_extract_vlserver_list(cell, result, ret);
270 	else
271 		vllist = afs_parse_text_addrs(cell->net, result, ret, ',',
272 					      VL_SERVICE, AFS_VL_PORT);
273 	kfree(result);
274 	if (IS_ERR(vllist) && vllist != ERR_PTR(-ENOMEM))
275 		pr_err("Failed to parse DNS data %ld\n", PTR_ERR(vllist));
276 
277 	return vllist;
278 }
279 
280 /*
281  * Merge an IPv4 entry into a fileserver address list.
282  */
283 int afs_merge_fs_addr4(struct afs_net *net, struct afs_addr_list *alist,
284 		       __be32 xdr, u16 port)
285 {
286 	struct sockaddr_rxrpc srx;
287 	struct rxrpc_peer *peer;
288 	int i;
289 
290 	if (alist->nr_addrs >= alist->max_addrs)
291 		return 0;
292 
293 	srx.srx_family = AF_RXRPC;
294 	srx.transport_type = SOCK_DGRAM;
295 	srx.transport_len = sizeof(srx.transport.sin);
296 	srx.transport.sin.sin_family = AF_INET;
297 	srx.transport.sin.sin_port = htons(port);
298 	srx.transport.sin.sin_addr.s_addr = xdr;
299 
300 	peer = rxrpc_kernel_lookup_peer(net->socket, &srx, GFP_KERNEL);
301 	if (!peer)
302 		return -ENOMEM;
303 
304 	for (i = 0; i < alist->nr_ipv4; i++) {
305 		if (peer == alist->addrs[i].peer) {
306 			rxrpc_kernel_put_peer(peer);
307 			return 0;
308 		}
309 		if (peer <= alist->addrs[i].peer)
310 			break;
311 	}
312 
313 	if (i < alist->nr_addrs)
314 		memmove(alist->addrs + i + 1,
315 			alist->addrs + i,
316 			sizeof(alist->addrs[0]) * (alist->nr_addrs - i));
317 
318 	alist->addrs[i].peer = peer;
319 	alist->nr_ipv4++;
320 	alist->nr_addrs++;
321 	return 0;
322 }
323 
324 /*
325  * Merge an IPv6 entry into a fileserver address list.
326  */
327 int afs_merge_fs_addr6(struct afs_net *net, struct afs_addr_list *alist,
328 		       __be32 *xdr, u16 port)
329 {
330 	struct sockaddr_rxrpc srx;
331 	struct rxrpc_peer *peer;
332 	int i;
333 
334 	if (alist->nr_addrs >= alist->max_addrs)
335 		return 0;
336 
337 	srx.srx_family = AF_RXRPC;
338 	srx.transport_type = SOCK_DGRAM;
339 	srx.transport_len = sizeof(srx.transport.sin6);
340 	srx.transport.sin6.sin6_family = AF_INET6;
341 	srx.transport.sin6.sin6_port = htons(port);
342 	memcpy(&srx.transport.sin6.sin6_addr, xdr, 16);
343 
344 	peer = rxrpc_kernel_lookup_peer(net->socket, &srx, GFP_KERNEL);
345 	if (!peer)
346 		return -ENOMEM;
347 
348 	for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) {
349 		if (peer == alist->addrs[i].peer) {
350 			rxrpc_kernel_put_peer(peer);
351 			return 0;
352 		}
353 		if (peer <= alist->addrs[i].peer)
354 			break;
355 	}
356 
357 	if (i < alist->nr_addrs)
358 		memmove(alist->addrs + i + 1,
359 			alist->addrs + i,
360 			sizeof(alist->addrs[0]) * (alist->nr_addrs - i));
361 	alist->addrs[i].peer = peer;
362 	alist->nr_addrs++;
363 	return 0;
364 }
365