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
];
124 const char *q
, *stop
;
133 q
= memchr(p
, ']', end
- p
);
135 for (q
= p
; q
< end
; q
++)
136 if (*q
== '+' || *q
== delim
)
140 if (in4_pton(p
, q
- p
,
141 (u8
*)&srx
->transport
.sin6
.sin6_addr
.s6_addr32
[3],
143 srx
->transport
.sin6
.sin6_addr
.s6_addr32
[0] = 0;
144 srx
->transport
.sin6
.sin6_addr
.s6_addr32
[1] = 0;
145 srx
->transport
.sin6
.sin6_addr
.s6_addr32
[2] = htonl(0xffff);
146 } else if (in6_pton(p
, q
- p
,
147 srx
->transport
.sin6
.sin6_addr
.s6_addr
,
158 if (q
< end
&& *q
== ']')
163 /* Port number specification "+1234" */
164 unsigned int xport
= 0;
166 if (p
>= end
|| !isdigit(*p
))
174 } while (p
< end
&& isdigit(*p
));
175 srx
->transport
.sin6
.sin6_port
= htons(xport
);
176 } else if (*p
== delim
) {
184 } while (p
< end
&& alist
->nr_addrs
< AFS_MAX_ADDRESSES
);
186 _leave(" = [nr %u]", alist
->nr_addrs
);
191 return ERR_PTR(-EINVAL
);
195 * Compare old and new address lists to see if there's been any change.
196 * - How to do this in better than O(Nlog(N)) time?
197 * - We don't really want to sort the address list, but would rather take the
198 * list as we got it so as not to undo record rotation by the DNS server.
201 static int afs_cmp_addr_list(const struct afs_addr_list
*a1
,
202 const struct afs_addr_list
*a2
)
208 * Perform a DNS query for VL servers and build a up an address list.
210 struct afs_addr_list
*afs_dns_query(struct afs_cell
*cell
, time64_t
*_expiry
)
212 struct afs_addr_list
*alist
;
216 _enter("%s", cell
->name
);
218 ret
= dns_query("afsdb", cell
->name
, cell
->name_len
,
219 "ipv4", &vllist
, _expiry
);
223 alist
= afs_parse_text_addrs(vllist
, strlen(vllist
), ',',
224 VL_SERVICE
, AFS_VL_PORT
);
227 if (alist
!= ERR_PTR(-ENOMEM
))
228 pr_err("Failed to parse DNS data\n");
237 * Merge an IPv4 entry into a fileserver address list.
239 void afs_merge_fs_addr4(struct afs_addr_list
*alist
, __be32 xdr
, u16 port
)
241 struct sockaddr_in6
*a
;
242 __be16 xport
= htons(port
);
245 for (i
= 0; i
< alist
->nr_ipv4
; i
++) {
246 a
= &alist
->addrs
[i
].transport
.sin6
;
247 if (xdr
== a
->sin6_addr
.s6_addr32
[3] &&
248 xport
== a
->sin6_port
)
250 if (xdr
== a
->sin6_addr
.s6_addr32
[3] &&
251 xport
< a
->sin6_port
)
253 if (xdr
< a
->sin6_addr
.s6_addr32
[3])
257 if (i
< alist
->nr_addrs
)
258 memmove(alist
->addrs
+ i
+ 1,
260 sizeof(alist
->addrs
[0]) * (alist
->nr_addrs
- i
));
262 a
= &alist
->addrs
[i
].transport
.sin6
;
263 a
->sin6_port
= xport
;
264 a
->sin6_addr
.s6_addr32
[0] = 0;
265 a
->sin6_addr
.s6_addr32
[1] = 0;
266 a
->sin6_addr
.s6_addr32
[2] = htonl(0xffff);
267 a
->sin6_addr
.s6_addr32
[3] = xdr
;
273 * Merge an IPv6 entry into a fileserver address list.
275 void afs_merge_fs_addr6(struct afs_addr_list
*alist
, __be32
*xdr
, u16 port
)
277 struct sockaddr_in6
*a
;
278 __be16 xport
= htons(port
);
281 for (i
= alist
->nr_ipv4
; i
< alist
->nr_addrs
; i
++) {
282 a
= &alist
->addrs
[i
].transport
.sin6
;
283 diff
= memcmp(xdr
, &a
->sin6_addr
, 16);
285 xport
== a
->sin6_port
)
288 xport
< a
->sin6_port
)
294 if (i
< alist
->nr_addrs
)
295 memmove(alist
->addrs
+ i
+ 1,
297 sizeof(alist
->addrs
[0]) * (alist
->nr_addrs
- i
));
299 a
= &alist
->addrs
[i
].transport
.sin6
;
300 a
->sin6_port
= xport
;
301 a
->sin6_addr
.s6_addr32
[0] = xdr
[0];
302 a
->sin6_addr
.s6_addr32
[1] = xdr
[1];
303 a
->sin6_addr
.s6_addr32
[2] = xdr
[2];
304 a
->sin6_addr
.s6_addr32
[3] = xdr
[3];
309 * Get an address to try.
311 bool afs_iterate_addresses(struct afs_addr_cursor
*ac
)
313 _enter("%hu+%hd", ac
->start
, (short)ac
->index
);
320 if (ac
->index
== ac
->alist
->nr_addrs
)
323 if (ac
->index
== ac
->start
) {
324 ac
->error
= -EDESTADDRREQ
;
330 ac
->responded
= false;
331 ac
->addr
= &ac
->alist
->addrs
[ac
->index
];
336 * Release an address list cursor.
338 int afs_end_cursor(struct afs_addr_cursor
*ac
)
340 struct afs_addr_list
*alist
;
344 if (ac
->responded
&& ac
->index
!= ac
->start
)
345 WRITE_ONCE(alist
->index
, ac
->index
);
346 afs_put_addrlist(alist
);
356 * Set the address cursor for iterating over VL servers.
358 int afs_set_vl_cursor(struct afs_addr_cursor
*ac
, struct afs_cell
*cell
)
360 struct afs_addr_list
*alist
;
363 if (!rcu_access_pointer(cell
->vl_addrs
)) {
364 ret
= wait_on_bit(&cell
->flags
, AFS_CELL_FL_NO_LOOKUP_YET
,
369 if (!rcu_access_pointer(cell
->vl_addrs
) &&
370 ktime_get_real_seconds() < cell
->dns_expiry
)
374 read_lock(&cell
->vl_addrs_lock
);
375 alist
= rcu_dereference_protected(cell
->vl_addrs
,
376 lockdep_is_held(&cell
->vl_addrs_lock
));
377 if (alist
->nr_addrs
> 0)
378 afs_get_addrlist(alist
);
381 read_unlock(&cell
->vl_addrs_lock
);
384 return -EDESTADDRREQ
;
388 ac
->start
= READ_ONCE(alist
->index
);
389 ac
->index
= ac
->start
;