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