1 /* Server address list management
3 * Copyright (C) 2017 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 Licence
8 * as published by the Free Software Foundation; either version
9 * 2 of the Licence, or (at your option) any later version.
12 #include <linux/slab.h>
13 #include <linux/ctype.h>
14 #include <linux/dns_resolver.h>
15 #include <linux/inet.h>
16 #include <keys/rxrpc-type.h>
20 //#define AFS_MAX_ADDRESSES
21 // ((unsigned int)((PAGE_SIZE - sizeof(struct afs_addr_list)) /
22 // sizeof(struct sockaddr_rxrpc)))
23 #define AFS_MAX_ADDRESSES ((unsigned int)(sizeof(unsigned long) * 8))
26 * Release an address list.
28 void afs_put_addrlist(struct afs_addr_list
*alist
)
30 if (alist
&& refcount_dec_and_test(&alist
->usage
))
31 call_rcu(&alist
->rcu
, (rcu_callback_t
)kfree
);
35 * Allocate an address list.
37 struct afs_addr_list
*afs_alloc_addrlist(unsigned int nr
,
38 unsigned short service
,
41 struct afs_addr_list
*alist
;
44 _enter("%u,%u,%u", nr
, service
, port
);
46 alist
= kzalloc(sizeof(*alist
) + sizeof(alist
->addrs
[0]) * nr
,
51 refcount_set(&alist
->usage
, 1);
53 for (i
= 0; i
< nr
; i
++) {
54 struct sockaddr_rxrpc
*srx
= &alist
->addrs
[i
];
55 srx
->srx_family
= AF_RXRPC
;
56 srx
->srx_service
= service
;
57 srx
->transport_type
= SOCK_DGRAM
;
58 srx
->transport_len
= sizeof(srx
->transport
.sin6
);
59 srx
->transport
.sin6
.sin6_family
= AF_INET6
;
60 srx
->transport
.sin6
.sin6_port
= htons(port
);
67 * Parse a text string consisting of delimited addresses.
69 struct afs_addr_list
*afs_parse_text_addrs(const char *text
, size_t len
,
71 unsigned short service
,
74 struct afs_addr_list
*alist
;
75 const char *p
, *end
= text
+ len
;
78 _enter("%*.*s,%c", (int)len
, (int)len
, text
, delim
);
81 return ERR_PTR(-EDESTADDRREQ
);
83 if (delim
== ':' && (memchr(text
, ',', len
) || !memchr(text
, '.', len
)))
86 /* Count the addresses */
90 return ERR_PTR(-EINVAL
);
97 return ERR_PTR(-EINVAL
);
98 p
= memchr(p
, ']', end
- p
);
100 return ERR_PTR(-EINVAL
);
106 p
= memchr(p
, delim
, end
- p
);
112 _debug("%u/%u addresses", nr
, AFS_MAX_ADDRESSES
);
113 if (nr
> AFS_MAX_ADDRESSES
)
114 nr
= AFS_MAX_ADDRESSES
;
116 alist
= afs_alloc_addrlist(nr
, service
, port
);
118 return ERR_PTR(-ENOMEM
);
120 /* Extract the addresses */
123 struct sockaddr_rxrpc
*srx
= &alist
->addrs
[alist
->nr_addrs
];
136 if (in4_pton(p
, end
- p
,
137 (u8
*)&srx
->transport
.sin6
.sin6_addr
.s6_addr32
[3],
139 srx
->transport
.sin6
.sin6_addr
.s6_addr32
[0] = 0;
140 srx
->transport
.sin6
.sin6_addr
.s6_addr32
[1] = 0;
141 srx
->transport
.sin6
.sin6_addr
.s6_addr32
[2] = htonl(0xffff);
142 } else if (in6_pton(p
, end
- p
,
143 srx
->transport
.sin6
.sin6_addr
.s6_addr
,
151 if (p
== end
|| *p
!= ']')
158 /* Port number specification "+1234" */
159 unsigned int xport
= 0;
161 if (p
>= end
|| !isdigit(*p
))
169 } while (p
< end
&& isdigit(*p
));
170 srx
->transport
.sin6
.sin6_port
= htons(xport
);
171 } else if (*p
== delim
) {
179 } while (p
< end
&& alist
->nr_addrs
< AFS_MAX_ADDRESSES
);
181 _leave(" = [nr %u]", alist
->nr_addrs
);
186 return ERR_PTR(-EINVAL
);
190 * Compare old and new address lists to see if there's been any change.
191 * - How to do this in better than O(Nlog(N)) time?
192 * - We don't really want to sort the address list, but would rather take the
193 * list as we got it so as not to undo record rotation by the DNS server.
196 static int afs_cmp_addr_list(const struct afs_addr_list
*a1
,
197 const struct afs_addr_list
*a2
)
203 * Perform a DNS query for VL servers and build a up an address list.
205 struct afs_addr_list
*afs_dns_query(struct afs_cell
*cell
, time64_t
*_expiry
)
207 struct afs_addr_list
*alist
;
211 _enter("%s", cell
->name
);
213 ret
= dns_query("afsdb", cell
->name
, cell
->name_len
,
214 "ipv4", &vllist
, _expiry
);
218 alist
= afs_parse_text_addrs(vllist
, strlen(vllist
), ',',
219 VL_SERVICE
, AFS_VL_PORT
);
222 if (alist
!= ERR_PTR(-ENOMEM
))
223 pr_err("Failed to parse DNS data\n");
232 * Merge an IPv4 entry into a fileserver address list.
234 void afs_merge_fs_addr4(struct afs_addr_list
*alist
, __be32 xdr
, u16 port
)
236 struct sockaddr_in6
*a
;
237 __be16 xport
= htons(port
);
240 for (i
= 0; i
< alist
->nr_ipv4
; i
++) {
241 a
= &alist
->addrs
[i
].transport
.sin6
;
242 if (xdr
== a
->sin6_addr
.s6_addr32
[3] &&
243 xport
== a
->sin6_port
)
245 if (xdr
== a
->sin6_addr
.s6_addr32
[3] &&
246 xport
< a
->sin6_port
)
248 if (xdr
< a
->sin6_addr
.s6_addr32
[3])
252 if (i
< alist
->nr_addrs
)
253 memmove(alist
->addrs
+ i
+ 1,
255 sizeof(alist
->addrs
[0]) * (alist
->nr_addrs
- i
));
257 a
= &alist
->addrs
[i
].transport
.sin6
;
258 a
->sin6_port
= xport
;
259 a
->sin6_addr
.s6_addr32
[0] = 0;
260 a
->sin6_addr
.s6_addr32
[1] = 0;
261 a
->sin6_addr
.s6_addr32
[2] = htonl(0xffff);
262 a
->sin6_addr
.s6_addr32
[3] = xdr
;
268 * Merge an IPv6 entry into a fileserver address list.
270 void afs_merge_fs_addr6(struct afs_addr_list
*alist
, __be32
*xdr
, u16 port
)
272 struct sockaddr_in6
*a
;
273 __be16 xport
= htons(port
);
276 for (i
= alist
->nr_ipv4
; i
< alist
->nr_addrs
; i
++) {
277 a
= &alist
->addrs
[i
].transport
.sin6
;
278 diff
= memcmp(xdr
, &a
->sin6_addr
, 16);
280 xport
== a
->sin6_port
)
283 xport
< a
->sin6_port
)
289 if (i
< alist
->nr_addrs
)
290 memmove(alist
->addrs
+ i
+ 1,
292 sizeof(alist
->addrs
[0]) * (alist
->nr_addrs
- i
));
294 a
= &alist
->addrs
[i
].transport
.sin6
;
295 a
->sin6_port
= xport
;
296 a
->sin6_addr
.s6_addr32
[0] = xdr
[0];
297 a
->sin6_addr
.s6_addr32
[1] = xdr
[1];
298 a
->sin6_addr
.s6_addr32
[2] = xdr
[2];
299 a
->sin6_addr
.s6_addr32
[3] = xdr
[3];
304 * Get an address to try.
306 bool afs_iterate_addresses(struct afs_addr_cursor
*ac
)
308 _enter("%hu+%hd", ac
->start
, (short)ac
->index
);
315 if (ac
->index
== ac
->alist
->nr_addrs
)
318 if (ac
->index
== ac
->start
) {
319 ac
->error
= -EDESTADDRREQ
;
325 ac
->responded
= false;
326 ac
->addr
= &ac
->alist
->addrs
[ac
->index
];
331 * Release an address list cursor.
333 int afs_end_cursor(struct afs_addr_cursor
*ac
)
335 struct afs_addr_list
*alist
;
339 if (ac
->responded
&& ac
->index
!= ac
->start
)
340 WRITE_ONCE(alist
->index
, ac
->index
);
341 afs_put_addrlist(alist
);
351 * Set the address cursor for iterating over VL servers.
353 int afs_set_vl_cursor(struct afs_addr_cursor
*ac
, struct afs_cell
*cell
)
355 struct afs_addr_list
*alist
;
358 if (!rcu_access_pointer(cell
->vl_addrs
)) {
359 ret
= wait_on_bit(&cell
->flags
, AFS_CELL_FL_NO_LOOKUP_YET
,
364 if (!rcu_access_pointer(cell
->vl_addrs
) &&
365 ktime_get_real_seconds() < cell
->dns_expiry
)
369 read_lock(&cell
->vl_addrs_lock
);
370 alist
= rcu_dereference_protected(cell
->vl_addrs
,
371 lockdep_is_held(&cell
->vl_addrs_lock
));
372 if (alist
->nr_addrs
> 0)
373 afs_get_addrlist(alist
);
376 read_unlock(&cell
->vl_addrs_lock
);
379 return -EDESTADDRREQ
;
383 ac
->start
= READ_ONCE(alist
->index
);
384 ac
->index
= ac
->start
;