1 /* AFS vlserver list management.
3 * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
12 #include <linux/kernel.h>
13 #include <linux/slab.h>
16 struct afs_vlserver
*afs_alloc_vlserver(const char *name
, size_t name_len
,
19 struct afs_vlserver
*vlserver
;
21 vlserver
= kzalloc(struct_size(vlserver
, name
, name_len
+ 1),
24 atomic_set(&vlserver
->usage
, 1);
25 rwlock_init(&vlserver
->lock
);
26 init_waitqueue_head(&vlserver
->probe_wq
);
27 spin_lock_init(&vlserver
->probe_lock
);
28 vlserver
->name_len
= name_len
;
29 vlserver
->port
= port
;
30 memcpy(vlserver
->name
, name
, name_len
);
35 static void afs_vlserver_rcu(struct rcu_head
*rcu
)
37 struct afs_vlserver
*vlserver
= container_of(rcu
, struct afs_vlserver
, rcu
);
39 afs_put_addrlist(rcu_access_pointer(vlserver
->addresses
));
40 kfree_rcu(vlserver
, rcu
);
43 void afs_put_vlserver(struct afs_net
*net
, struct afs_vlserver
*vlserver
)
46 unsigned int u
= atomic_dec_return(&vlserver
->usage
);
47 //_debug("VL PUT %p{%u}", vlserver, u);
50 call_rcu(&vlserver
->rcu
, afs_vlserver_rcu
);
54 struct afs_vlserver_list
*afs_alloc_vlserver_list(unsigned int nr_servers
)
56 struct afs_vlserver_list
*vllist
;
58 vllist
= kzalloc(struct_size(vllist
, servers
, nr_servers
), GFP_KERNEL
);
60 atomic_set(&vllist
->usage
, 1);
61 rwlock_init(&vllist
->lock
);
67 void afs_put_vlserverlist(struct afs_net
*net
, struct afs_vlserver_list
*vllist
)
70 unsigned int u
= atomic_dec_return(&vllist
->usage
);
72 //_debug("VLLS PUT %p{%u}", vllist, u);
76 for (i
= 0; i
< vllist
->nr_servers
; i
++) {
77 afs_put_vlserver(net
, vllist
->servers
[i
].server
);
79 kfree_rcu(vllist
, rcu
);
84 static u16
afs_extract_le16(const u8
**_b
)
88 val
= (u16
)*(*_b
)++ << 0;
89 val
|= (u16
)*(*_b
)++ << 8;
94 * Build a VL server address list from a DNS queried server list.
96 static struct afs_addr_list
*afs_extract_vl_addrs(const u8
**_b
, const u8
*end
,
97 u8 nr_addrs
, u16 port
)
99 struct afs_addr_list
*alist
;
103 alist
= afs_alloc_addrlist(nr_addrs
, VL_SERVICE
, port
);
105 return ERR_PTR(-ENOMEM
);
109 for (; nr_addrs
> 0 && end
- b
>= nr_addrs
; nr_addrs
--) {
110 struct dns_server_list_v1_address hdr
;
113 hdr
.address_type
= *b
++;
115 switch (hdr
.address_type
) {
116 case DNS_ADDRESS_IS_IPV4
:
118 _leave(" = -EINVAL [short inet]");
122 afs_merge_fs_addr4(alist
, x
[0], port
);
126 case DNS_ADDRESS_IS_IPV6
:
128 _leave(" = -EINVAL [short inet6]");
132 afs_merge_fs_addr6(alist
, x
, port
);
137 _leave(" = -EADDRNOTAVAIL [unknown af %u]",
139 ret
= -EADDRNOTAVAIL
;
144 /* Start with IPv6 if available. */
145 if (alist
->nr_ipv4
< alist
->nr_addrs
)
146 alist
->preferred
= alist
->nr_ipv4
;
153 afs_put_addrlist(alist
);
158 * Build a VL server list from a DNS queried server list.
160 struct afs_vlserver_list
*afs_extract_vlserver_list(struct afs_cell
*cell
,
164 const struct dns_server_list_v1_header
*hdr
= buffer
;
165 struct dns_server_list_v1_server bs
;
166 struct afs_vlserver_list
*vllist
, *previous
;
167 struct afs_addr_list
*addrs
;
168 struct afs_vlserver
*server
;
169 const u8
*b
= buffer
, *end
= buffer
+ buffer_size
;
170 int ret
= -ENOMEM
, nr_servers
, i
, j
;
174 /* Check that it's a server list, v1 */
175 if (end
- b
< sizeof(*hdr
) ||
176 hdr
->hdr
.content
!= DNS_PAYLOAD_IS_SERVER_LIST
||
177 hdr
->hdr
.version
!= 1) {
178 pr_notice("kAFS: Got DNS record [%u,%u] len %zu\n",
179 hdr
->hdr
.content
, hdr
->hdr
.version
, end
- b
);
184 nr_servers
= hdr
->nr_servers
;
186 vllist
= afs_alloc_vlserver_list(nr_servers
);
188 return ERR_PTR(-ENOMEM
);
190 vllist
->source
= (hdr
->source
< NR__dns_record_source
) ?
191 hdr
->source
: NR__dns_record_source
;
192 vllist
->status
= (hdr
->status
< NR__dns_lookup_status
) ?
193 hdr
->status
: NR__dns_lookup_status
;
195 read_lock(&cell
->vl_servers_lock
);
196 previous
= afs_get_vlserverlist(
197 rcu_dereference_protected(cell
->vl_servers
,
198 lockdep_is_held(&cell
->vl_servers_lock
)));
199 read_unlock(&cell
->vl_servers_lock
);
202 while (end
- b
>= sizeof(bs
)) {
203 bs
.name_len
= afs_extract_le16(&b
);
204 bs
.priority
= afs_extract_le16(&b
);
205 bs
.weight
= afs_extract_le16(&b
);
206 bs
.port
= afs_extract_le16(&b
);
212 _debug("extract %u %u %u %u %u %u %*.*s",
213 bs
.name_len
, bs
.priority
, bs
.weight
,
214 bs
.port
, bs
.protocol
, bs
.nr_addrs
,
215 bs
.name_len
, bs
.name_len
, b
);
217 if (end
- b
< bs
.name_len
)
220 ret
= -EPROTONOSUPPORT
;
221 if (bs
.protocol
== DNS_SERVER_PROTOCOL_UNSPECIFIED
) {
222 bs
.protocol
= DNS_SERVER_PROTOCOL_UDP
;
223 } else if (bs
.protocol
!= DNS_SERVER_PROTOCOL_UDP
) {
224 _leave(" = [proto %u]", bs
.protocol
);
229 bs
.port
= AFS_VL_PORT
;
230 if (bs
.source
> NR__dns_record_source
)
231 bs
.source
= NR__dns_record_source
;
232 if (bs
.status
> NR__dns_lookup_status
)
233 bs
.status
= NR__dns_lookup_status
;
237 /* See if we can update an old server record */
238 for (i
= 0; i
< previous
->nr_servers
; i
++) {
239 struct afs_vlserver
*p
= previous
->servers
[i
].server
;
241 if (p
->name_len
== bs
.name_len
&&
242 p
->port
== bs
.port
&&
243 strncasecmp(b
, p
->name
, bs
.name_len
) == 0) {
244 server
= afs_get_vlserver(p
);
252 server
= afs_alloc_vlserver(b
, bs
.name_len
, bs
.port
);
259 /* Extract the addresses - note that we can't skip this as we
260 * have to advance the payload pointer.
262 addrs
= afs_extract_vl_addrs(&b
, end
, bs
.nr_addrs
, bs
.port
);
264 ret
= PTR_ERR(addrs
);
268 if (vllist
->nr_servers
>= nr_servers
) {
269 _debug("skip %u >= %u", vllist
->nr_servers
, nr_servers
);
270 afs_put_addrlist(addrs
);
271 afs_put_vlserver(cell
->net
, server
);
275 addrs
->source
= bs
.source
;
276 addrs
->status
= bs
.status
;
278 if (addrs
->nr_addrs
== 0) {
279 afs_put_addrlist(addrs
);
280 if (!rcu_access_pointer(server
->addresses
)) {
281 afs_put_vlserver(cell
->net
, server
);
285 struct afs_addr_list
*old
= addrs
;
287 write_lock(&server
->lock
);
288 rcu_swap_protected(server
->addresses
, old
,
289 lockdep_is_held(&server
->lock
));
290 write_unlock(&server
->lock
);
291 afs_put_addrlist(old
);
295 /* TODO: Might want to check for duplicates */
297 /* Insertion-sort by priority and weight */
298 for (j
= 0; j
< vllist
->nr_servers
; j
++) {
299 if (bs
.priority
< vllist
->servers
[j
].priority
)
300 break; /* Lower preferable */
301 if (bs
.priority
== vllist
->servers
[j
].priority
&&
302 bs
.weight
> vllist
->servers
[j
].weight
)
303 break; /* Higher preferable */
306 if (j
< vllist
->nr_servers
) {
307 memmove(vllist
->servers
+ j
+ 1,
309 (vllist
->nr_servers
- j
) * sizeof(struct afs_vlserver_entry
));
312 clear_bit(AFS_VLSERVER_FL_PROBED
, &server
->flags
);
314 vllist
->servers
[j
].priority
= bs
.priority
;
315 vllist
->servers
[j
].weight
= bs
.weight
;
316 vllist
->servers
[j
].server
= server
;
317 vllist
->nr_servers
++;
321 _debug("parse error %zd", b
- end
);
325 afs_put_vlserverlist(cell
->net
, previous
);
326 _leave(" = ok [%u]", vllist
->nr_servers
);
330 afs_put_vlserver(cell
->net
, server
);
332 afs_put_vlserverlist(cell
->net
, vllist
);
333 afs_put_vlserverlist(cell
->net
, previous
);
335 if (ret
!= -ENOMEM
) {
336 printk(KERN_DEBUG
"DNS: at %zu\n", (const void *)b
- buffer
);
337 print_hex_dump_bytes("DNS: ", DUMP_PREFIX_NONE
, buffer
, buffer_size
);