1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* AFS vlserver list management.
4 * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
5 * Written by David Howells (dhowells@redhat.com)
8 #include <linux/kernel.h>
9 #include <linux/slab.h>
12 struct afs_vlserver
*afs_alloc_vlserver(const char *name
, size_t name_len
,
15 struct afs_vlserver
*vlserver
;
17 vlserver
= kzalloc(struct_size(vlserver
, name
, name_len
+ 1),
20 atomic_set(&vlserver
->usage
, 1);
21 rwlock_init(&vlserver
->lock
);
22 init_waitqueue_head(&vlserver
->probe_wq
);
23 spin_lock_init(&vlserver
->probe_lock
);
24 vlserver
->name_len
= name_len
;
25 vlserver
->port
= port
;
26 memcpy(vlserver
->name
, name
, name_len
);
31 static void afs_vlserver_rcu(struct rcu_head
*rcu
)
33 struct afs_vlserver
*vlserver
= container_of(rcu
, struct afs_vlserver
, rcu
);
35 afs_put_addrlist(rcu_access_pointer(vlserver
->addresses
));
36 kfree_rcu(vlserver
, rcu
);
39 void afs_put_vlserver(struct afs_net
*net
, struct afs_vlserver
*vlserver
)
42 unsigned int u
= atomic_dec_return(&vlserver
->usage
);
43 //_debug("VL PUT %p{%u}", vlserver, u);
46 call_rcu(&vlserver
->rcu
, afs_vlserver_rcu
);
50 struct afs_vlserver_list
*afs_alloc_vlserver_list(unsigned int nr_servers
)
52 struct afs_vlserver_list
*vllist
;
54 vllist
= kzalloc(struct_size(vllist
, servers
, nr_servers
), GFP_KERNEL
);
56 atomic_set(&vllist
->usage
, 1);
57 rwlock_init(&vllist
->lock
);
63 void afs_put_vlserverlist(struct afs_net
*net
, struct afs_vlserver_list
*vllist
)
66 unsigned int u
= atomic_dec_return(&vllist
->usage
);
68 //_debug("VLLS PUT %p{%u}", vllist, u);
72 for (i
= 0; i
< vllist
->nr_servers
; i
++) {
73 afs_put_vlserver(net
, vllist
->servers
[i
].server
);
75 kfree_rcu(vllist
, rcu
);
80 static u16
afs_extract_le16(const u8
**_b
)
84 val
= (u16
)*(*_b
)++ << 0;
85 val
|= (u16
)*(*_b
)++ << 8;
90 * Build a VL server address list from a DNS queried server list.
92 static struct afs_addr_list
*afs_extract_vl_addrs(const u8
**_b
, const u8
*end
,
93 u8 nr_addrs
, u16 port
)
95 struct afs_addr_list
*alist
;
99 alist
= afs_alloc_addrlist(nr_addrs
, VL_SERVICE
, port
);
101 return ERR_PTR(-ENOMEM
);
105 for (; nr_addrs
> 0 && end
- b
>= nr_addrs
; nr_addrs
--) {
106 struct dns_server_list_v1_address hdr
;
109 hdr
.address_type
= *b
++;
111 switch (hdr
.address_type
) {
112 case DNS_ADDRESS_IS_IPV4
:
114 _leave(" = -EINVAL [short inet]");
118 afs_merge_fs_addr4(alist
, x
[0], port
);
122 case DNS_ADDRESS_IS_IPV6
:
124 _leave(" = -EINVAL [short inet6]");
128 afs_merge_fs_addr6(alist
, x
, port
);
133 _leave(" = -EADDRNOTAVAIL [unknown af %u]",
135 ret
= -EADDRNOTAVAIL
;
140 /* Start with IPv6 if available. */
141 if (alist
->nr_ipv4
< alist
->nr_addrs
)
142 alist
->preferred
= alist
->nr_ipv4
;
149 afs_put_addrlist(alist
);
154 * Build a VL server list from a DNS queried server list.
156 struct afs_vlserver_list
*afs_extract_vlserver_list(struct afs_cell
*cell
,
160 const struct dns_server_list_v1_header
*hdr
= buffer
;
161 struct dns_server_list_v1_server bs
;
162 struct afs_vlserver_list
*vllist
, *previous
;
163 struct afs_addr_list
*addrs
;
164 struct afs_vlserver
*server
;
165 const u8
*b
= buffer
, *end
= buffer
+ buffer_size
;
166 int ret
= -ENOMEM
, nr_servers
, i
, j
;
170 /* Check that it's a server list, v1 */
171 if (end
- b
< sizeof(*hdr
) ||
172 hdr
->hdr
.content
!= DNS_PAYLOAD_IS_SERVER_LIST
||
173 hdr
->hdr
.version
!= 1) {
174 pr_notice("kAFS: Got DNS record [%u,%u] len %zu\n",
175 hdr
->hdr
.content
, hdr
->hdr
.version
, end
- b
);
180 nr_servers
= hdr
->nr_servers
;
182 vllist
= afs_alloc_vlserver_list(nr_servers
);
184 return ERR_PTR(-ENOMEM
);
186 vllist
->source
= (hdr
->source
< NR__dns_record_source
) ?
187 hdr
->source
: NR__dns_record_source
;
188 vllist
->status
= (hdr
->status
< NR__dns_lookup_status
) ?
189 hdr
->status
: NR__dns_lookup_status
;
191 read_lock(&cell
->vl_servers_lock
);
192 previous
= afs_get_vlserverlist(
193 rcu_dereference_protected(cell
->vl_servers
,
194 lockdep_is_held(&cell
->vl_servers_lock
)));
195 read_unlock(&cell
->vl_servers_lock
);
198 while (end
- b
>= sizeof(bs
)) {
199 bs
.name_len
= afs_extract_le16(&b
);
200 bs
.priority
= afs_extract_le16(&b
);
201 bs
.weight
= afs_extract_le16(&b
);
202 bs
.port
= afs_extract_le16(&b
);
208 _debug("extract %u %u %u %u %u %u %*.*s",
209 bs
.name_len
, bs
.priority
, bs
.weight
,
210 bs
.port
, bs
.protocol
, bs
.nr_addrs
,
211 bs
.name_len
, bs
.name_len
, b
);
213 if (end
- b
< bs
.name_len
)
216 ret
= -EPROTONOSUPPORT
;
217 if (bs
.protocol
== DNS_SERVER_PROTOCOL_UNSPECIFIED
) {
218 bs
.protocol
= DNS_SERVER_PROTOCOL_UDP
;
219 } else if (bs
.protocol
!= DNS_SERVER_PROTOCOL_UDP
) {
220 _leave(" = [proto %u]", bs
.protocol
);
225 bs
.port
= AFS_VL_PORT
;
226 if (bs
.source
> NR__dns_record_source
)
227 bs
.source
= NR__dns_record_source
;
228 if (bs
.status
> NR__dns_lookup_status
)
229 bs
.status
= NR__dns_lookup_status
;
231 /* See if we can update an old server record */
233 for (i
= 0; i
< previous
->nr_servers
; i
++) {
234 struct afs_vlserver
*p
= previous
->servers
[i
].server
;
236 if (p
->name_len
== bs
.name_len
&&
237 p
->port
== bs
.port
&&
238 strncasecmp(b
, p
->name
, bs
.name_len
) == 0) {
239 server
= afs_get_vlserver(p
);
246 server
= afs_alloc_vlserver(b
, bs
.name_len
, bs
.port
);
253 /* Extract the addresses - note that we can't skip this as we
254 * have to advance the payload pointer.
256 addrs
= afs_extract_vl_addrs(&b
, end
, bs
.nr_addrs
, bs
.port
);
258 ret
= PTR_ERR(addrs
);
262 if (vllist
->nr_servers
>= nr_servers
) {
263 _debug("skip %u >= %u", vllist
->nr_servers
, nr_servers
);
264 afs_put_addrlist(addrs
);
265 afs_put_vlserver(cell
->net
, server
);
269 addrs
->source
= bs
.source
;
270 addrs
->status
= bs
.status
;
272 if (addrs
->nr_addrs
== 0) {
273 afs_put_addrlist(addrs
);
274 if (!rcu_access_pointer(server
->addresses
)) {
275 afs_put_vlserver(cell
->net
, server
);
279 struct afs_addr_list
*old
= addrs
;
281 write_lock(&server
->lock
);
282 old
= rcu_replace_pointer(server
->addresses
, old
,
283 lockdep_is_held(&server
->lock
));
284 write_unlock(&server
->lock
);
285 afs_put_addrlist(old
);
289 /* TODO: Might want to check for duplicates */
291 /* Insertion-sort by priority and weight */
292 for (j
= 0; j
< vllist
->nr_servers
; j
++) {
293 if (bs
.priority
< vllist
->servers
[j
].priority
)
294 break; /* Lower preferable */
295 if (bs
.priority
== vllist
->servers
[j
].priority
&&
296 bs
.weight
> vllist
->servers
[j
].weight
)
297 break; /* Higher preferable */
300 if (j
< vllist
->nr_servers
) {
301 memmove(vllist
->servers
+ j
+ 1,
303 (vllist
->nr_servers
- j
) * sizeof(struct afs_vlserver_entry
));
306 clear_bit(AFS_VLSERVER_FL_PROBED
, &server
->flags
);
308 vllist
->servers
[j
].priority
= bs
.priority
;
309 vllist
->servers
[j
].weight
= bs
.weight
;
310 vllist
->servers
[j
].server
= server
;
311 vllist
->nr_servers
++;
315 _debug("parse error %zd", b
- end
);
319 afs_put_vlserverlist(cell
->net
, previous
);
320 _leave(" = ok [%u]", vllist
->nr_servers
);
324 afs_put_vlserver(cell
->net
, server
);
326 afs_put_vlserverlist(cell
->net
, vllist
);
327 afs_put_vlserverlist(cell
->net
, previous
);
329 if (ret
!= -ENOMEM
) {
330 printk(KERN_DEBUG
"DNS: at %zu\n", (const void *)b
- buffer
);
331 print_hex_dump_bytes("DNS: ", DUMP_PREFIX_NONE
, buffer
, buffer_size
);