Blame view
fs/afs/addr_list.c
8.54 KB
b4d0d230c
|
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
8b2a464ce
|
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
|
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
|
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
|
21 |
kfree_rcu(alist, rcu); |
8b2a464ce
|
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
|
35 36 |
if (nr > AFS_MAX_ADDRESSES) nr = AFS_MAX_ADDRESSES; |
acafe7e30
|
37 |
alist = kzalloc(struct_size(alist, addrs, nr), GFP_KERNEL); |
8b2a464ce
|
38 39 40 41 |
if (!alist) return NULL; refcount_set(&alist->usage, 1); |
68eb64c3d
|
42 |
alist->max_addrs = nr; |
8b2a464ce
|
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
|
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
|
65 |
{ |
0a5143f2f
|
66 |
struct afs_vlserver_list *vllist; |
8b2a464ce
|
67 68 |
struct afs_addr_list *alist; const char *p, *end = text + len; |
0a5143f2f
|
69 |
const char *problem; |
8b2a464ce
|
70 |
unsigned int nr = 0; |
0a5143f2f
|
71 |
int ret = -ENOMEM; |
8b2a464ce
|
72 73 |
_enter("%*.*s,%c", (int)len, (int)len, text, delim); |
0a5143f2f
|
74 75 |
if (!len) { _leave(" = -EDESTADDRREQ [empty]"); |
8b2a464ce
|
76 |
return ERR_PTR(-EDESTADDRREQ); |
0a5143f2f
|
77 |
} |
8b2a464ce
|
78 79 80 81 82 83 84 |
if (delim == ':' && (memchr(text, ',', len) || !memchr(text, '.', len))) delim = ','; /* Count the addresses */ p = text; do { |
0a5143f2f
|
85 86 87 88 |
if (!*p) { problem = "nul"; goto inval; } |
8b2a464ce
|
89 90 91 92 93 |
if (*p == delim) continue; nr++; if (*p == '[') { p++; |
0a5143f2f
|
94 95 96 97 |
if (p == end) { problem = "brace1"; goto inval; } |
8b2a464ce
|
98 |
p = memchr(p, ']', end - p); |
0a5143f2f
|
99 100 101 102 |
if (!p) { problem = "brace2"; goto inval; } |
8b2a464ce
|
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
|
115 |
|
0a5143f2f
|
116 117 |
vllist = afs_alloc_vlserver_list(1); if (!vllist) |
8b2a464ce
|
118 |
return ERR_PTR(-ENOMEM); |
0a5143f2f
|
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
|
127 128 129 |
/* Extract the addresses */ p = text; do { |
01fd79e6d
|
130 |
const char *q, *stop; |
4c19bbdc7
|
131 132 133 |
unsigned int xport = port; __be32 x[4]; int family; |
8b2a464ce
|
134 135 136 137 138 139 140 141 |
if (*p == delim) { p++; continue; } if (*p == '[') { p++; |
01fd79e6d
|
142 143 144 145 146 |
q = memchr(p, ']', end - p); } else { for (q = p; q < end; q++) if (*q == '+' || *q == delim) break; |
8b2a464ce
|
147 |
} |
0a5143f2f
|
148 |
if (in4_pton(p, q - p, (u8 *)&x[0], -1, &stop)) { |
4c19bbdc7
|
149 |
family = AF_INET; |
0a5143f2f
|
150 |
} else if (in6_pton(p, q - p, (u8 *)x, -1, &stop)) { |
4c19bbdc7
|
151 |
family = AF_INET6; |
0a5143f2f
|
152 153 |
} else { problem = "family"; |
8b2a464ce
|
154 |
goto bad_address; |
0a5143f2f
|
155 |
} |
8b2a464ce
|
156 |
|
0a5143f2f
|
157 158 159 |
p = q; if (stop != p) { problem = "nostop"; |
01fd79e6d
|
160 |
goto bad_address; |
0a5143f2f
|
161 |
} |
01fd79e6d
|
162 |
|
01fd79e6d
|
163 |
if (q < end && *q == ']') |
8b2a464ce
|
164 |
p++; |
8b2a464ce
|
165 166 167 168 |
if (p < end) { if (*p == '+') { /* Port number specification "+1234" */ |
4c19bbdc7
|
169 |
xport = 0; |
8b2a464ce
|
170 |
p++; |
0a5143f2f
|
171 172 |
if (p >= end || !isdigit(*p)) { problem = "port"; |
8b2a464ce
|
173 |
goto bad_address; |
0a5143f2f
|
174 |
} |
8b2a464ce
|
175 176 177 |
do { xport *= 10; xport += *p - '0'; |
0a5143f2f
|
178 179 |
if (xport > 65535) { problem = "pval"; |
8b2a464ce
|
180 |
goto bad_address; |
0a5143f2f
|
181 |
} |
8b2a464ce
|
182 183 |
p++; } while (p < end && isdigit(*p)); |
8b2a464ce
|
184 185 186 |
} else if (*p == delim) { p++; } else { |
0a5143f2f
|
187 |
problem = "weird"; |
8b2a464ce
|
188 189 190 |
goto bad_address; } } |
4c19bbdc7
|
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
|
197 |
|
0a5143f2f
|
198 |
rcu_assign_pointer(vllist->servers[0].server->addresses, alist); |
8b2a464ce
|
199 |
_leave(" = [nr %u]", alist->nr_addrs); |
0a5143f2f
|
200 |
return vllist; |
8b2a464ce
|
201 |
|
0a5143f2f
|
202 203 204 |
inval: _leave(" = -EINVAL [%s %zu %*.*s]", problem, p - text, (int)len, (int)len, text); |
8b2a464ce
|
205 |
return ERR_PTR(-EINVAL); |
0a5143f2f
|
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
|
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
|
233 |
struct afs_vlserver_list *afs_dns_query(struct afs_cell *cell, time64_t *_expiry) |
8b2a464ce
|
234 |
{ |
0a5143f2f
|
235 236 |
struct afs_vlserver_list *vllist; char *result = NULL; |
8b2a464ce
|
237 238 239 |
int ret; _enter("%s", cell->name); |
a58946c15
|
240 241 |
ret = dns_query(cell->net->net, "afsdb", cell->name, cell->name_len, "srv=1", &result, _expiry, true); |
0a5143f2f
|
242 243 |
if (ret < 0) { _leave(" = %d [dns]", ret); |
8b2a464ce
|
244 |
return ERR_PTR(ret); |
8b2a464ce
|
245 |
} |
0a5143f2f
|
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
|
260 261 262 |
} /* |
d2ddc776a
|
263 264 |
* Merge an IPv4 entry into a fileserver address list. */ |
bf99a53ce
|
265 |
void afs_merge_fs_addr4(struct afs_addr_list *alist, __be32 xdr, u16 port) |
d2ddc776a
|
266 |
{ |
46894a135
|
267 |
struct sockaddr_rxrpc *srx; |
66be646bd
|
268 |
u32 addr = ntohl(xdr); |
d2ddc776a
|
269 |
int i; |
68eb64c3d
|
270 271 |
if (alist->nr_addrs >= alist->max_addrs) return; |
d2ddc776a
|
272 |
for (i = 0; i < alist->nr_ipv4; i++) { |
46894a135
|
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
|
276 277 |
if (addr == a_addr && port == a_port) |
d2ddc776a
|
278 |
return; |
66be646bd
|
279 |
if (addr == a_addr && port < a_port) |
bf99a53ce
|
280 |
break; |
66be646bd
|
281 |
if (addr < a_addr) |
d2ddc776a
|
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
|
289 |
srx = &alist->addrs[i]; |
3bf0fb6f3
|
290 291 |
srx->srx_family = AF_RXRPC; srx->transport_type = SOCK_DGRAM; |
46894a135
|
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
|
296 297 298 299 300 |
alist->nr_ipv4++; alist->nr_addrs++; } /* |
bf99a53ce
|
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
|
305 |
struct sockaddr_rxrpc *srx; |
bf99a53ce
|
306 |
int i, diff; |
68eb64c3d
|
307 308 |
if (alist->nr_addrs >= alist->max_addrs) return; |
bf99a53ce
|
309 |
for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) { |
66be646bd
|
310 311 |
struct sockaddr_in6 *a = &alist->addrs[i].transport.sin6; u16 a_port = ntohs(a->sin6_port); |
bf99a53ce
|
312 |
diff = memcmp(xdr, &a->sin6_addr, 16); |
66be646bd
|
313 |
if (diff == 0 && port == a_port) |
bf99a53ce
|
314 |
return; |
66be646bd
|
315 |
if (diff == 0 && port < a_port) |
bf99a53ce
|
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
|
325 |
srx = &alist->addrs[i]; |
3bf0fb6f3
|
326 327 |
srx->srx_family = AF_RXRPC; srx->transport_type = SOCK_DGRAM; |
46894a135
|
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
|
332 333 334 335 |
alist->nr_addrs++; } /* |
8b2a464ce
|
336 337 338 339 |
* Get an address to try. */ bool afs_iterate_addresses(struct afs_addr_cursor *ac) { |
3bf0fb6f3
|
340 341 |
unsigned long set, failed; int index; |
8b2a464ce
|
342 343 344 |
if (!ac->alist) return false; |
3bf0fb6f3
|
345 346 347 |
set = ac->alist->responded; failed = ac->alist->failed; _enter("%lx-%lx-%lx,%d", set, failed, ac->tried, ac->index); |
744bcd713
|
348 |
ac->nr_iterations++; |
3bf0fb6f3
|
349 |
set &= ~(failed | ac->tried); |
8b2a464ce
|
350 |
|
3bf0fb6f3
|
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
|
359 |
|
3bf0fb6f3
|
360 361 362 |
selected: ac->index = index; set_bit(index, &ac->tried); |
8b2a464ce
|
363 |
ac->responded = false; |
8b2a464ce
|
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
|
372 373 374 375 |
struct afs_addr_list *alist; alist = ac->alist; if (alist) { |
3bf0fb6f3
|
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
|
380 |
afs_put_addrlist(alist); |
3bf0fb6f3
|
381 |
ac->alist = NULL; |
fe4d774c8
|
382 |
} |
8b2a464ce
|
383 |
|
8b2a464ce
|
384 385 |
return ac->error; } |