1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* Server address list management
4 * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
5 * Written by David Howells (dhowells@redhat.com)
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>
16 static void afs_free_addrlist(struct rcu_head
*rcu
)
18 struct afs_addr_list
*alist
= container_of(rcu
, struct afs_addr_list
, rcu
);
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
);
28 * Release an address list.
30 void afs_put_addrlist(struct afs_addr_list
*alist
, enum afs_alist_trace reason
)
32 unsigned int debug_id
;
38 debug_id
= alist
->debug_id
;
39 dead
= __refcount_dec_and_test(&alist
->usage
, &r
);
40 trace_afs_alist(debug_id
, r
- 1, reason
);
42 call_rcu(&alist
->rcu
, afs_free_addrlist
);
45 struct afs_addr_list
*afs_get_addrlist(struct afs_addr_list
*alist
, enum afs_alist_trace reason
)
50 __refcount_inc(&alist
->usage
, &r
);
51 trace_afs_alist(alist
->debug_id
, r
+ 1, reason
);
57 * Allocate an address list.
59 struct afs_addr_list
*afs_alloc_addrlist(unsigned int nr
)
61 struct afs_addr_list
*alist
;
62 static atomic_t debug_id
;
66 if (nr
> AFS_MAX_ADDRESSES
)
67 nr
= AFS_MAX_ADDRESSES
;
69 alist
= kzalloc(struct_size(alist
, addrs
, nr
), GFP_KERNEL
);
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
);
81 * Parse a text string consisting of delimited addresses.
83 struct afs_vlserver_list
*afs_parse_text_addrs(struct afs_net
*net
,
84 const char *text
, size_t len
,
86 unsigned short service
,
89 struct afs_vlserver_list
*vllist
;
90 struct afs_addr_list
*alist
;
91 const char *p
, *end
= text
+ len
;
96 _enter("%*.*s,%c", (int)len
, (int)len
, text
, delim
);
99 _leave(" = -EDESTADDRREQ [empty]");
100 return ERR_PTR(-EDESTADDRREQ
);
103 if (delim
== ':' && (memchr(text
, ',', len
) || !memchr(text
, '.', len
)))
106 /* Count the addresses */
122 p
= memchr(p
, ']', end
- p
);
132 p
= memchr(p
, delim
, end
- p
);
138 _debug("%u/%u addresses", nr
, AFS_MAX_ADDRESSES
);
140 vllist
= afs_alloc_vlserver_list(1);
142 return ERR_PTR(-ENOMEM
);
144 vllist
->nr_servers
= 1;
145 vllist
->servers
[0].server
= afs_alloc_vlserver("<dummy>", 7, AFS_VL_PORT
);
146 if (!vllist
->servers
[0].server
)
149 alist
= afs_alloc_addrlist(nr
);
153 /* Extract the addresses */
156 const char *q
, *stop
;
157 unsigned int xport
= port
;
168 q
= memchr(p
, ']', end
- p
);
170 for (q
= p
; q
< end
; q
++)
171 if (*q
== '+' || *q
== delim
)
175 if (in4_pton(p
, q
- p
, (u8
*)&x
[0], -1, &stop
)) {
177 } else if (in6_pton(p
, q
- p
, (u8
*)x
, -1, &stop
)) {
190 if (q
< end
&& *q
== ']')
195 /* Port number specification "+1234" */
198 if (p
>= end
|| !isdigit(*p
)) {
210 } while (p
< end
&& isdigit(*p
));
211 } else if (*p
== delim
) {
219 if (family
== AF_INET
)
220 ret
= afs_merge_fs_addr4(net
, alist
, x
[0], xport
);
222 ret
= afs_merge_fs_addr6(net
, alist
, x
, xport
);
228 rcu_assign_pointer(vllist
->servers
[0].server
->addresses
, alist
);
229 _leave(" = [nr %u]", alist
->nr_addrs
);
233 _leave(" = -EINVAL [%s %zu %*.*s]",
234 problem
, p
- text
, (int)len
, (int)len
, text
);
235 return ERR_PTR(-EINVAL
);
237 _leave(" = -EINVAL [%s %zu %*.*s]",
238 problem
, p
- text
, (int)len
, (int)len
, text
);
241 afs_put_addrlist(alist
, afs_alist_trace_put_parse_error
);
243 afs_put_vlserverlist(net
, vllist
);
248 * Perform a DNS query for VL servers and build a up an address list.
250 struct afs_vlserver_list
*afs_dns_query(struct afs_cell
*cell
, time64_t
*_expiry
)
252 struct afs_vlserver_list
*vllist
;
256 _enter("%s", cell
->name
);
258 ret
= dns_query(cell
->net
->net
, "afsdb", cell
->name
, cell
->name_len
,
259 "srv=1", &result
, _expiry
, true);
261 _leave(" = %d [dns]", ret
);
266 *_expiry
= ktime_get_real_seconds() + 60;
268 if (ret
> 1 && result
[0] == 0)
269 vllist
= afs_extract_vlserver_list(cell
, result
, ret
);
271 vllist
= afs_parse_text_addrs(cell
->net
, result
, ret
, ',',
272 VL_SERVICE
, AFS_VL_PORT
);
274 if (IS_ERR(vllist
) && vllist
!= ERR_PTR(-ENOMEM
))
275 pr_err("Failed to parse DNS data %ld\n", PTR_ERR(vllist
));
281 * Merge an IPv4 entry into a fileserver address list.
283 int afs_merge_fs_addr4(struct afs_net
*net
, struct afs_addr_list
*alist
,
284 __be32 xdr
, u16 port
)
286 struct sockaddr_rxrpc srx
;
287 struct rxrpc_peer
*peer
;
290 if (alist
->nr_addrs
>= alist
->max_addrs
)
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
;
300 peer
= rxrpc_kernel_lookup_peer(net
->socket
, &srx
, GFP_KERNEL
);
304 for (i
= 0; i
< alist
->nr_ipv4
; i
++) {
305 if (peer
== alist
->addrs
[i
].peer
) {
306 rxrpc_kernel_put_peer(peer
);
309 if (peer
<= alist
->addrs
[i
].peer
)
313 if (i
< alist
->nr_addrs
)
314 memmove(alist
->addrs
+ i
+ 1,
316 sizeof(alist
->addrs
[0]) * (alist
->nr_addrs
- i
));
318 alist
->addrs
[i
].peer
= peer
;
325 * Merge an IPv6 entry into a fileserver address list.
327 int afs_merge_fs_addr6(struct afs_net
*net
, struct afs_addr_list
*alist
,
328 __be32
*xdr
, u16 port
)
330 struct sockaddr_rxrpc srx
;
331 struct rxrpc_peer
*peer
;
334 if (alist
->nr_addrs
>= alist
->max_addrs
)
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);
344 peer
= rxrpc_kernel_lookup_peer(net
->socket
, &srx
, GFP_KERNEL
);
348 for (i
= alist
->nr_ipv4
; i
< alist
->nr_addrs
; i
++) {
349 if (peer
== alist
->addrs
[i
].peer
) {
350 rxrpc_kernel_put_peer(peer
);
353 if (peer
<= alist
->addrs
[i
].peer
)
357 if (i
< alist
->nr_addrs
)
358 memmove(alist
->addrs
+ i
+ 1,
360 sizeof(alist
->addrs
[0]) * (alist
->nr_addrs
- i
));
361 alist
->addrs
[i
].peer
= peer
;