Merge tag 'linux-kselftest-next-5.12-rc1' of git://git.kernel.org/pub/scm/linux/kerne...
[linux-2.6-microblaze.git] / fs / afs / vl_list.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* AFS vlserver list management.
3  *
4  * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
5  * Written by David Howells (dhowells@redhat.com)
6  */
7
8 #include <linux/kernel.h>
9 #include <linux/slab.h>
10 #include "internal.h"
11
12 struct afs_vlserver *afs_alloc_vlserver(const char *name, size_t name_len,
13                                         unsigned short port)
14 {
15         struct afs_vlserver *vlserver;
16
17         vlserver = kzalloc(struct_size(vlserver, name, name_len + 1),
18                            GFP_KERNEL);
19         if (vlserver) {
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->rtt = UINT_MAX;
25                 vlserver->name_len = name_len;
26                 vlserver->port = port;
27                 memcpy(vlserver->name, name, name_len);
28         }
29         return vlserver;
30 }
31
32 static void afs_vlserver_rcu(struct rcu_head *rcu)
33 {
34         struct afs_vlserver *vlserver = container_of(rcu, struct afs_vlserver, rcu);
35
36         afs_put_addrlist(rcu_access_pointer(vlserver->addresses));
37         kfree_rcu(vlserver, rcu);
38 }
39
40 void afs_put_vlserver(struct afs_net *net, struct afs_vlserver *vlserver)
41 {
42         if (vlserver) {
43                 unsigned int u = atomic_dec_return(&vlserver->usage);
44                 //_debug("VL PUT %p{%u}", vlserver, u);
45
46                 if (u == 0)
47                         call_rcu(&vlserver->rcu, afs_vlserver_rcu);
48         }
49 }
50
51 struct afs_vlserver_list *afs_alloc_vlserver_list(unsigned int nr_servers)
52 {
53         struct afs_vlserver_list *vllist;
54
55         vllist = kzalloc(struct_size(vllist, servers, nr_servers), GFP_KERNEL);
56         if (vllist) {
57                 atomic_set(&vllist->usage, 1);
58                 rwlock_init(&vllist->lock);
59         }
60
61         return vllist;
62 }
63
64 void afs_put_vlserverlist(struct afs_net *net, struct afs_vlserver_list *vllist)
65 {
66         if (vllist) {
67                 unsigned int u = atomic_dec_return(&vllist->usage);
68
69                 //_debug("VLLS PUT %p{%u}", vllist, u);
70                 if (u == 0) {
71                         int i;
72
73                         for (i = 0; i < vllist->nr_servers; i++) {
74                                 afs_put_vlserver(net, vllist->servers[i].server);
75                         }
76                         kfree_rcu(vllist, rcu);
77                 }
78         }
79 }
80
81 static u16 afs_extract_le16(const u8 **_b)
82 {
83         u16 val;
84
85         val  = (u16)*(*_b)++ << 0;
86         val |= (u16)*(*_b)++ << 8;
87         return val;
88 }
89
90 /*
91  * Build a VL server address list from a DNS queried server list.
92  */
93 static struct afs_addr_list *afs_extract_vl_addrs(const u8 **_b, const u8 *end,
94                                                   u8 nr_addrs, u16 port)
95 {
96         struct afs_addr_list *alist;
97         const u8 *b = *_b;
98         int ret = -EINVAL;
99
100         alist = afs_alloc_addrlist(nr_addrs, VL_SERVICE, port);
101         if (!alist)
102                 return ERR_PTR(-ENOMEM);
103         if (nr_addrs == 0)
104                 return alist;
105
106         for (; nr_addrs > 0 && end - b >= nr_addrs; nr_addrs--) {
107                 struct dns_server_list_v1_address hdr;
108                 __be32 x[4];
109
110                 hdr.address_type = *b++;
111
112                 switch (hdr.address_type) {
113                 case DNS_ADDRESS_IS_IPV4:
114                         if (end - b < 4) {
115                                 _leave(" = -EINVAL [short inet]");
116                                 goto error;
117                         }
118                         memcpy(x, b, 4);
119                         afs_merge_fs_addr4(alist, x[0], port);
120                         b += 4;
121                         break;
122
123                 case DNS_ADDRESS_IS_IPV6:
124                         if (end - b < 16) {
125                                 _leave(" = -EINVAL [short inet6]");
126                                 goto error;
127                         }
128                         memcpy(x, b, 16);
129                         afs_merge_fs_addr6(alist, x, port);
130                         b += 16;
131                         break;
132
133                 default:
134                         _leave(" = -EADDRNOTAVAIL [unknown af %u]",
135                                hdr.address_type);
136                         ret = -EADDRNOTAVAIL;
137                         goto error;
138                 }
139         }
140
141         /* Start with IPv6 if available. */
142         if (alist->nr_ipv4 < alist->nr_addrs)
143                 alist->preferred = alist->nr_ipv4;
144
145         *_b = b;
146         return alist;
147
148 error:
149         *_b = b;
150         afs_put_addrlist(alist);
151         return ERR_PTR(ret);
152 }
153
154 /*
155  * Build a VL server list from a DNS queried server list.
156  */
157 struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *cell,
158                                                     const void *buffer,
159                                                     size_t buffer_size)
160 {
161         const struct dns_server_list_v1_header *hdr = buffer;
162         struct dns_server_list_v1_server bs;
163         struct afs_vlserver_list *vllist, *previous;
164         struct afs_addr_list *addrs;
165         struct afs_vlserver *server;
166         const u8 *b = buffer, *end = buffer + buffer_size;
167         int ret = -ENOMEM, nr_servers, i, j;
168
169         _enter("");
170
171         /* Check that it's a server list, v1 */
172         if (end - b < sizeof(*hdr) ||
173             hdr->hdr.content != DNS_PAYLOAD_IS_SERVER_LIST ||
174             hdr->hdr.version != 1) {
175                 pr_notice("kAFS: Got DNS record [%u,%u] len %zu\n",
176                           hdr->hdr.content, hdr->hdr.version, end - b);
177                 ret = -EDESTADDRREQ;
178                 goto dump;
179         }
180
181         nr_servers = hdr->nr_servers;
182
183         vllist = afs_alloc_vlserver_list(nr_servers);
184         if (!vllist)
185                 return ERR_PTR(-ENOMEM);
186
187         vllist->source = (hdr->source < NR__dns_record_source) ?
188                 hdr->source : NR__dns_record_source;
189         vllist->status = (hdr->status < NR__dns_lookup_status) ?
190                 hdr->status : NR__dns_lookup_status;
191
192         read_lock(&cell->vl_servers_lock);
193         previous = afs_get_vlserverlist(
194                 rcu_dereference_protected(cell->vl_servers,
195                                           lockdep_is_held(&cell->vl_servers_lock)));
196         read_unlock(&cell->vl_servers_lock);
197
198         b += sizeof(*hdr);
199         while (end - b >= sizeof(bs)) {
200                 bs.name_len     = afs_extract_le16(&b);
201                 bs.priority     = afs_extract_le16(&b);
202                 bs.weight       = afs_extract_le16(&b);
203                 bs.port         = afs_extract_le16(&b);
204                 bs.source       = *b++;
205                 bs.status       = *b++;
206                 bs.protocol     = *b++;
207                 bs.nr_addrs     = *b++;
208
209                 _debug("extract %u %u %u %u %u %u %*.*s",
210                        bs.name_len, bs.priority, bs.weight,
211                        bs.port, bs.protocol, bs.nr_addrs,
212                        bs.name_len, bs.name_len, b);
213
214                 if (end - b < bs.name_len)
215                         break;
216
217                 ret = -EPROTONOSUPPORT;
218                 if (bs.protocol == DNS_SERVER_PROTOCOL_UNSPECIFIED) {
219                         bs.protocol = DNS_SERVER_PROTOCOL_UDP;
220                 } else if (bs.protocol != DNS_SERVER_PROTOCOL_UDP) {
221                         _leave(" = [proto %u]", bs.protocol);
222                         goto error;
223                 }
224
225                 if (bs.port == 0)
226                         bs.port = AFS_VL_PORT;
227                 if (bs.source > NR__dns_record_source)
228                         bs.source = NR__dns_record_source;
229                 if (bs.status > NR__dns_lookup_status)
230                         bs.status = NR__dns_lookup_status;
231
232                 /* See if we can update an old server record */
233                 server = NULL;
234                 for (i = 0; i < previous->nr_servers; i++) {
235                         struct afs_vlserver *p = previous->servers[i].server;
236
237                         if (p->name_len == bs.name_len &&
238                             p->port == bs.port &&
239                             strncasecmp(b, p->name, bs.name_len) == 0) {
240                                 server = afs_get_vlserver(p);
241                                 break;
242                         }
243                 }
244
245                 if (!server) {
246                         ret = -ENOMEM;
247                         server = afs_alloc_vlserver(b, bs.name_len, bs.port);
248                         if (!server)
249                                 goto error;
250                 }
251
252                 b += bs.name_len;
253
254                 /* Extract the addresses - note that we can't skip this as we
255                  * have to advance the payload pointer.
256                  */
257                 addrs = afs_extract_vl_addrs(&b, end, bs.nr_addrs, bs.port);
258                 if (IS_ERR(addrs)) {
259                         ret = PTR_ERR(addrs);
260                         goto error_2;
261                 }
262
263                 if (vllist->nr_servers >= nr_servers) {
264                         _debug("skip %u >= %u", vllist->nr_servers, nr_servers);
265                         afs_put_addrlist(addrs);
266                         afs_put_vlserver(cell->net, server);
267                         continue;
268                 }
269
270                 addrs->source = bs.source;
271                 addrs->status = bs.status;
272
273                 if (addrs->nr_addrs == 0) {
274                         afs_put_addrlist(addrs);
275                         if (!rcu_access_pointer(server->addresses)) {
276                                 afs_put_vlserver(cell->net, server);
277                                 continue;
278                         }
279                 } else {
280                         struct afs_addr_list *old = addrs;
281
282                         write_lock(&server->lock);
283                         old = rcu_replace_pointer(server->addresses, old,
284                                                   lockdep_is_held(&server->lock));
285                         write_unlock(&server->lock);
286                         afs_put_addrlist(old);
287                 }
288
289
290                 /* TODO: Might want to check for duplicates */
291
292                 /* Insertion-sort by priority and weight */
293                 for (j = 0; j < vllist->nr_servers; j++) {
294                         if (bs.priority < vllist->servers[j].priority)
295                                 break; /* Lower preferable */
296                         if (bs.priority == vllist->servers[j].priority &&
297                             bs.weight > vllist->servers[j].weight)
298                                 break; /* Higher preferable */
299                 }
300
301                 if (j < vllist->nr_servers) {
302                         memmove(vllist->servers + j + 1,
303                                 vllist->servers + j,
304                                 (vllist->nr_servers - j) * sizeof(struct afs_vlserver_entry));
305                 }
306
307                 clear_bit(AFS_VLSERVER_FL_PROBED, &server->flags);
308
309                 vllist->servers[j].priority = bs.priority;
310                 vllist->servers[j].weight = bs.weight;
311                 vllist->servers[j].server = server;
312                 vllist->nr_servers++;
313         }
314
315         if (b != end) {
316                 _debug("parse error %zd", b - end);
317                 goto error;
318         }
319
320         afs_put_vlserverlist(cell->net, previous);
321         _leave(" = ok [%u]", vllist->nr_servers);
322         return vllist;
323
324 error_2:
325         afs_put_vlserver(cell->net, server);
326 error:
327         afs_put_vlserverlist(cell->net, vllist);
328         afs_put_vlserverlist(cell->net, previous);
329 dump:
330         if (ret != -ENOMEM) {
331                 printk(KERN_DEBUG "DNS: at %zu\n", (const void *)b - buffer);
332                 print_hex_dump_bytes("DNS: ", DUMP_PREFIX_NONE, buffer, buffer_size);
333         }
334         return ERR_PTR(ret);
335 }