1 /* Server address list management 2 * 3 * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved. 4 * Written by David Howells (dhowells@redhat.com) 5 * 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. 10 */ 11 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> 17 #include "internal.h" 18 #include "afs_fs.h" 19 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)) 24 25 /* 26 * Release an address list. 27 */ 28 void afs_put_addrlist(struct afs_addr_list *alist) 29 { 30 if (alist && refcount_dec_and_test(&alist->usage)) 31 call_rcu(&alist->rcu, (rcu_callback_t)kfree); 32 } 33 34 /* 35 * Allocate an address list. 36 */ 37 struct afs_addr_list *afs_alloc_addrlist(unsigned int nr, 38 unsigned short service, 39 unsigned short port) 40 { 41 struct afs_addr_list *alist; 42 unsigned int i; 43 44 _enter("%u,%u,%u", nr, service, port); 45 46 alist = kzalloc(sizeof(*alist) + sizeof(alist->addrs[0]) * nr, 47 GFP_KERNEL); 48 if (!alist) 49 return NULL; 50 51 refcount_set(&alist->usage, 1); 52 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); 61 } 62 63 return alist; 64 } 65 66 /* 67 * Parse a text string consisting of delimited addresses. 68 */ 69 struct afs_addr_list *afs_parse_text_addrs(const char *text, size_t len, 70 char delim, 71 unsigned short service, 72 unsigned short port) 73 { 74 struct afs_addr_list *alist; 75 const char *p, *end = text + len; 76 unsigned int nr = 0; 77 78 _enter("%*.*s,%c", (int)len, (int)len, text, delim); 79 80 if (!len) 81 return ERR_PTR(-EDESTADDRREQ); 82 83 if (delim == ':' && (memchr(text, ',', len) || !memchr(text, '.', len))) 84 delim = ','; 85 86 /* Count the addresses */ 87 p = text; 88 do { 89 if (!*p) 90 return ERR_PTR(-EINVAL); 91 if (*p == delim) 92 continue; 93 nr++; 94 if (*p == '[') { 95 p++; 96 if (p == end) 97 return ERR_PTR(-EINVAL); 98 p = memchr(p, ']', end - p); 99 if (!p) 100 return ERR_PTR(-EINVAL); 101 p++; 102 if (p >= end) 103 break; 104 } 105 106 p = memchr(p, delim, end - p); 107 if (!p) 108 break; 109 p++; 110 } while (p < end); 111 112 _debug("%u/%u addresses", nr, AFS_MAX_ADDRESSES); 113 if (nr > AFS_MAX_ADDRESSES) 114 nr = AFS_MAX_ADDRESSES; 115 116 alist = afs_alloc_addrlist(nr, service, port); 117 if (!alist) 118 return ERR_PTR(-ENOMEM); 119 120 /* Extract the addresses */ 121 p = text; 122 do { 123 struct sockaddr_rxrpc *srx = &alist->addrs[alist->nr_addrs]; 124 char tdelim = delim; 125 126 if (*p == delim) { 127 p++; 128 continue; 129 } 130 131 if (*p == '[') { 132 p++; 133 tdelim = ']'; 134 } 135 136 if (in4_pton(p, end - p, 137 (u8 *)&srx->transport.sin6.sin6_addr.s6_addr32[3], 138 tdelim, &p)) { 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, 144 tdelim, &p)) { 145 /* Nothing to do */ 146 } else { 147 goto bad_address; 148 } 149 150 if (tdelim == ']') { 151 if (p == end || *p != ']') 152 goto bad_address; 153 p++; 154 } 155 156 if (p < end) { 157 if (*p == '+') { 158 /* Port number specification "+1234" */ 159 unsigned int xport = 0; 160 p++; 161 if (p >= end || !isdigit(*p)) 162 goto bad_address; 163 do { 164 xport *= 10; 165 xport += *p - '0'; 166 if (xport > 65535) 167 goto bad_address; 168 p++; 169 } while (p < end && isdigit(*p)); 170 srx->transport.sin6.sin6_port = htons(xport); 171 } else if (*p == delim) { 172 p++; 173 } else { 174 goto bad_address; 175 } 176 } 177 178 alist->nr_addrs++; 179 } while (p < end && alist->nr_addrs < AFS_MAX_ADDRESSES); 180 181 _leave(" = [nr %u]", alist->nr_addrs); 182 return alist; 183 184 bad_address: 185 kfree(alist); 186 return ERR_PTR(-EINVAL); 187 } 188 189 /* 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. 194 */ 195 #if 0 196 static int afs_cmp_addr_list(const struct afs_addr_list *a1, 197 const struct afs_addr_list *a2) 198 { 199 } 200 #endif 201 202 /* 203 * Perform a DNS query for VL servers and build a up an address list. 204 */ 205 struct afs_addr_list *afs_dns_query(struct afs_cell *cell, time64_t *_expiry) 206 { 207 struct afs_addr_list *alist; 208 char *vllist = NULL; 209 int ret; 210 211 _enter("%s", cell->name); 212 213 ret = dns_query("afsdb", cell->name, cell->name_len, 214 "ipv4", &vllist, _expiry); 215 if (ret < 0) 216 return ERR_PTR(ret); 217 218 alist = afs_parse_text_addrs(vllist, strlen(vllist), ',', 219 VL_SERVICE, AFS_VL_PORT); 220 if (IS_ERR(alist)) { 221 kfree(vllist); 222 if (alist != ERR_PTR(-ENOMEM)) 223 pr_err("Failed to parse DNS data\n"); 224 return alist; 225 } 226 227 kfree(vllist); 228 return alist; 229 } 230 231 /* 232 * Merge an IPv4 entry into a fileserver address list. 233 */ 234 void afs_merge_fs_addr4(struct afs_addr_list *alist, __be32 xdr, u16 port) 235 { 236 struct sockaddr_in6 *a; 237 __be16 xport = htons(port); 238 int i; 239 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) 244 return; 245 if (xdr == a->sin6_addr.s6_addr32[3] && 246 xport < a->sin6_port) 247 break; 248 if (xdr < a->sin6_addr.s6_addr32[3]) 249 break; 250 } 251 252 if (i < alist->nr_addrs) 253 memmove(alist->addrs + i + 1, 254 alist->addrs + i, 255 sizeof(alist->addrs[0]) * (alist->nr_addrs - i)); 256 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; 263 alist->nr_ipv4++; 264 alist->nr_addrs++; 265 } 266 267 /* 268 * Merge an IPv6 entry into a fileserver address list. 269 */ 270 void afs_merge_fs_addr6(struct afs_addr_list *alist, __be32 *xdr, u16 port) 271 { 272 struct sockaddr_in6 *a; 273 __be16 xport = htons(port); 274 int i, diff; 275 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); 279 if (diff == 0 && 280 xport == a->sin6_port) 281 return; 282 if (diff == 0 && 283 xport < a->sin6_port) 284 break; 285 if (diff < 0) 286 break; 287 } 288 289 if (i < alist->nr_addrs) 290 memmove(alist->addrs + i + 1, 291 alist->addrs + i, 292 sizeof(alist->addrs[0]) * (alist->nr_addrs - i)); 293 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]; 300 alist->nr_addrs++; 301 } 302 303 /* 304 * Get an address to try. 305 */ 306 bool afs_iterate_addresses(struct afs_addr_cursor *ac) 307 { 308 _enter("%hu+%hd", ac->start, (short)ac->index); 309 310 if (!ac->alist) 311 return false; 312 313 if (ac->begun) { 314 ac->index++; 315 if (ac->index == ac->alist->nr_addrs) 316 ac->index = 0; 317 318 if (ac->index == ac->start) { 319 ac->error = -EDESTADDRREQ; 320 return false; 321 } 322 } 323 324 ac->begun = true; 325 ac->responded = false; 326 ac->addr = &ac->alist->addrs[ac->index]; 327 return true; 328 } 329 330 /* 331 * Release an address list cursor. 332 */ 333 int afs_end_cursor(struct afs_addr_cursor *ac) 334 { 335 if (ac->responded && ac->index != ac->start) 336 WRITE_ONCE(ac->alist->index, ac->index); 337 338 afs_put_addrlist(ac->alist); 339 ac->alist = NULL; 340 return ac->error; 341 } 342 343 /* 344 * Set the address cursor for iterating over VL servers. 345 */ 346 int afs_set_vl_cursor(struct afs_addr_cursor *ac, struct afs_cell *cell) 347 { 348 struct afs_addr_list *alist; 349 int ret; 350 351 if (!rcu_access_pointer(cell->vl_addrs)) { 352 ret = wait_on_bit(&cell->flags, AFS_CELL_FL_NO_LOOKUP_YET, 353 TASK_INTERRUPTIBLE); 354 if (ret < 0) 355 return ret; 356 357 if (!rcu_access_pointer(cell->vl_addrs) && 358 ktime_get_real_seconds() < cell->dns_expiry) 359 return cell->error; 360 } 361 362 read_lock(&cell->vl_addrs_lock); 363 alist = rcu_dereference_protected(cell->vl_addrs, 364 lockdep_is_held(&cell->vl_addrs_lock)); 365 if (alist->nr_addrs > 0) 366 afs_get_addrlist(alist); 367 else 368 alist = NULL; 369 read_unlock(&cell->vl_addrs_lock); 370 371 if (!alist) 372 return -EDESTADDRREQ; 373 374 ac->alist = alist; 375 ac->addr = NULL; 376 ac->start = READ_ONCE(alist->index); 377 ac->index = ac->start; 378 ac->error = 0; 379 ac->begun = false; 380 return 0; 381 } 382