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)))
25 * Release an address list.
27 void afs_put_addrlist(struct afs_addr_list
*alist
)
29 if (alist
&& refcount_dec_and_test(&alist
->usage
))
30 call_rcu(&alist
->rcu
, (rcu_callback_t
)kfree
);
34 * Allocate an address list.
36 struct afs_addr_list
*afs_alloc_addrlist(unsigned int nr
,
37 unsigned short service
,
40 struct afs_addr_list
*alist
;
43 _enter("%u,%u,%u", nr
, service
, port
);
45 alist
= kzalloc(sizeof(*alist
) + sizeof(alist
->addrs
[0]) * nr
,
50 refcount_set(&alist
->usage
, 1);
52 for (i
= 0; i
< nr
; i
++) {
53 struct sockaddr_rxrpc
*srx
= &alist
->addrs
[i
];
54 srx
->srx_family
= AF_RXRPC
;
55 srx
->srx_service
= service
;
56 srx
->transport_type
= SOCK_DGRAM
;
57 srx
->transport_len
= sizeof(srx
->transport
.sin6
);
58 srx
->transport
.sin6
.sin6_family
= AF_INET6
;
59 srx
->transport
.sin6
.sin6_port
= htons(port
);
66 * Parse a text string consisting of delimited addresses.
68 struct afs_addr_list
*afs_parse_text_addrs(const char *text
, size_t len
,
70 unsigned short service
,
73 struct afs_addr_list
*alist
;
74 const char *p
, *end
= text
+ len
;
77 _enter("%*.*s,%c", (int)len
, (int)len
, text
, delim
);
80 return ERR_PTR(-EDESTADDRREQ
);
82 if (delim
== ':' && (memchr(text
, ',', len
) || !memchr(text
, '.', len
)))
85 /* Count the addresses */
89 return ERR_PTR(-EINVAL
);
96 return ERR_PTR(-EINVAL
);
97 p
= memchr(p
, ']', end
- p
);
99 return ERR_PTR(-EINVAL
);
105 p
= memchr(p
, delim
, end
- p
);
111 _debug("%u/%u addresses", nr
, AFS_MAX_ADDRESSES
);
112 if (nr
> AFS_MAX_ADDRESSES
)
113 nr
= AFS_MAX_ADDRESSES
;
115 alist
= afs_alloc_addrlist(nr
, service
, port
);
117 return ERR_PTR(-ENOMEM
);
119 /* Extract the addresses */
122 struct sockaddr_rxrpc
*srx
= &alist
->addrs
[alist
->nr_addrs
];
135 if (in4_pton(p
, end
- p
,
136 (u8
*)&srx
->transport
.sin6
.sin6_addr
.s6_addr32
[3],
138 srx
->transport
.sin6
.sin6_addr
.s6_addr32
[0] = 0;
139 srx
->transport
.sin6
.sin6_addr
.s6_addr32
[1] = 0;
140 srx
->transport
.sin6
.sin6_addr
.s6_addr32
[2] = htonl(0xffff);
141 } else if (in6_pton(p
, end
- p
,
142 srx
->transport
.sin6
.sin6_addr
.s6_addr
,
150 if (p
== end
|| *p
!= ']')
157 /* Port number specification "+1234" */
158 unsigned int xport
= 0;
160 if (p
>= end
|| !isdigit(*p
))
168 } while (p
< end
&& isdigit(*p
));
169 srx
->transport
.sin6
.sin6_port
= htons(xport
);
170 } else if (*p
== delim
) {
178 } while (p
< end
&& alist
->nr_addrs
< AFS_MAX_ADDRESSES
);
180 _leave(" = [nr %u]", alist
->nr_addrs
);
185 return ERR_PTR(-EINVAL
);
189 * Compare old and new address lists to see if there's been any change.
190 * - How to do this in better than O(Nlog(N)) time?
191 * - We don't really want to sort the address list, but would rather take the
192 * list as we got it so as not to undo record rotation by the DNS server.
195 static int afs_cmp_addr_list(const struct afs_addr_list
*a1
,
196 const struct afs_addr_list
*a2
)
202 * Perform a DNS query for VL servers and build a up an address list.
204 struct afs_addr_list
*afs_dns_query(struct afs_cell
*cell
, time64_t
*_expiry
)
206 struct afs_addr_list
*alist
;
210 _enter("%s", cell
->name
);
212 ret
= dns_query("afsdb", cell
->name
, cell
->name_len
,
213 "ipv4", &vllist
, _expiry
);
217 alist
= afs_parse_text_addrs(vllist
, strlen(vllist
), ',',
218 VL_SERVICE
, AFS_VL_PORT
);
221 if (alist
!= ERR_PTR(-ENOMEM
))
222 pr_err("Failed to parse DNS data\n");
231 * Merge an IPv4 entry into a fileserver address list.
233 void afs_merge_fs_addr4(struct afs_addr_list
*alist
, __be32 xdr
)
235 struct sockaddr_in6
*a
;
238 for (i
= 0; i
< alist
->nr_ipv4
; i
++) {
239 a
= &alist
->addrs
[i
].transport
.sin6
;
240 if (xdr
== a
->sin6_addr
.s6_addr32
[3])
242 if (xdr
< a
->sin6_addr
.s6_addr32
[3])
246 if (i
< alist
->nr_addrs
)
247 memmove(alist
->addrs
+ i
+ 1,
249 sizeof(alist
->addrs
[0]) * (alist
->nr_addrs
- i
));
251 a
= &alist
->addrs
[i
].transport
.sin6
;
252 a
->sin6_port
= htons(AFS_FS_PORT
);
253 a
->sin6_addr
.s6_addr32
[0] = 0;
254 a
->sin6_addr
.s6_addr32
[1] = 0;
255 a
->sin6_addr
.s6_addr32
[2] = htonl(0xffff);
256 a
->sin6_addr
.s6_addr32
[3] = xdr
;
262 * Get an address to try.
264 bool afs_iterate_addresses(struct afs_addr_cursor
*ac
)
266 _enter("%hu+%hd", ac
->start
, (short)ac
->index
);
273 if (ac
->index
== ac
->alist
->nr_addrs
)
276 if (ac
->index
== ac
->start
) {
277 ac
->error
= -EDESTADDRREQ
;
283 ac
->responded
= false;
284 ac
->addr
= &ac
->alist
->addrs
[ac
->index
];
289 * Release an address list cursor.
291 int afs_end_cursor(struct afs_addr_cursor
*ac
)
293 if (ac
->responded
&& ac
->index
!= ac
->start
)
294 WRITE_ONCE(ac
->alist
->index
, ac
->index
);
296 afs_put_addrlist(ac
->alist
);
302 * Set the address cursor for iterating over VL servers.
304 int afs_set_vl_cursor(struct afs_addr_cursor
*ac
, struct afs_cell
*cell
)
306 struct afs_addr_list
*alist
;
309 if (!rcu_access_pointer(cell
->vl_addrs
)) {
310 ret
= wait_on_bit(&cell
->flags
, AFS_CELL_FL_NO_LOOKUP_YET
,
315 if (!rcu_access_pointer(cell
->vl_addrs
) &&
316 ktime_get_real_seconds() < cell
->dns_expiry
)
320 read_lock(&cell
->vl_addrs_lock
);
321 alist
= rcu_dereference_protected(cell
->vl_addrs
,
322 lockdep_is_held(&cell
->vl_addrs_lock
));
323 if (alist
->nr_addrs
> 0)
324 afs_get_addrlist(alist
);
327 read_unlock(&cell
->vl_addrs_lock
);
330 return -EDESTADDRREQ
;
334 ac
->start
= READ_ONCE(alist
->index
);
335 ac
->index
= ac
->start
;