10a5143f2SDavid Howells /* AFS vlserver list management. 20a5143f2SDavid Howells * 30a5143f2SDavid Howells * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved. 40a5143f2SDavid Howells * Written by David Howells (dhowells@redhat.com) 50a5143f2SDavid Howells * 60a5143f2SDavid Howells * This program is free software; you can redistribute it and/or 70a5143f2SDavid Howells * modify it under the terms of the GNU General Public License 80a5143f2SDavid Howells * as published by the Free Software Foundation; either version 90a5143f2SDavid Howells * 2 of the License, or (at your option) any later version. 100a5143f2SDavid Howells */ 110a5143f2SDavid Howells 120a5143f2SDavid Howells #include <linux/kernel.h> 130a5143f2SDavid Howells #include <linux/slab.h> 140a5143f2SDavid Howells #include "internal.h" 150a5143f2SDavid Howells 160a5143f2SDavid Howells struct afs_vlserver *afs_alloc_vlserver(const char *name, size_t name_len, 170a5143f2SDavid Howells unsigned short port) 180a5143f2SDavid Howells { 190a5143f2SDavid Howells struct afs_vlserver *vlserver; 200a5143f2SDavid Howells 210a5143f2SDavid Howells vlserver = kzalloc(struct_size(vlserver, name, name_len + 1), 220a5143f2SDavid Howells GFP_KERNEL); 230a5143f2SDavid Howells if (vlserver) { 240a5143f2SDavid Howells atomic_set(&vlserver->usage, 1); 250a5143f2SDavid Howells rwlock_init(&vlserver->lock); 260a5143f2SDavid Howells vlserver->name_len = name_len; 270a5143f2SDavid Howells vlserver->port = port; 280a5143f2SDavid Howells memcpy(vlserver->name, name, name_len); 290a5143f2SDavid Howells } 300a5143f2SDavid Howells return vlserver; 310a5143f2SDavid Howells } 320a5143f2SDavid Howells 330a5143f2SDavid Howells static void afs_vlserver_rcu(struct rcu_head *rcu) 340a5143f2SDavid Howells { 350a5143f2SDavid Howells struct afs_vlserver *vlserver = container_of(rcu, struct afs_vlserver, rcu); 360a5143f2SDavid Howells 370a5143f2SDavid Howells afs_put_addrlist(rcu_access_pointer(vlserver->addresses)); 380a5143f2SDavid Howells kfree_rcu(vlserver, rcu); 390a5143f2SDavid Howells } 400a5143f2SDavid Howells 410a5143f2SDavid Howells void afs_put_vlserver(struct afs_net *net, struct afs_vlserver *vlserver) 420a5143f2SDavid Howells { 430a5143f2SDavid Howells if (vlserver) { 440a5143f2SDavid Howells unsigned int u = atomic_dec_return(&vlserver->usage); 450a5143f2SDavid Howells //_debug("VL PUT %p{%u}", vlserver, u); 460a5143f2SDavid Howells 470a5143f2SDavid Howells if (u == 0) 480a5143f2SDavid Howells call_rcu(&vlserver->rcu, afs_vlserver_rcu); 490a5143f2SDavid Howells } 500a5143f2SDavid Howells } 510a5143f2SDavid Howells 520a5143f2SDavid Howells struct afs_vlserver_list *afs_alloc_vlserver_list(unsigned int nr_servers) 530a5143f2SDavid Howells { 540a5143f2SDavid Howells struct afs_vlserver_list *vllist; 550a5143f2SDavid Howells 560a5143f2SDavid Howells vllist = kzalloc(struct_size(vllist, servers, nr_servers), GFP_KERNEL); 570a5143f2SDavid Howells if (vllist) { 580a5143f2SDavid Howells atomic_set(&vllist->usage, 1); 590a5143f2SDavid Howells rwlock_init(&vllist->lock); 600a5143f2SDavid Howells } 610a5143f2SDavid Howells 620a5143f2SDavid Howells return vllist; 630a5143f2SDavid Howells } 640a5143f2SDavid Howells 650a5143f2SDavid Howells void afs_put_vlserverlist(struct afs_net *net, struct afs_vlserver_list *vllist) 660a5143f2SDavid Howells { 670a5143f2SDavid Howells if (vllist) { 680a5143f2SDavid Howells unsigned int u = atomic_dec_return(&vllist->usage); 690a5143f2SDavid Howells 700a5143f2SDavid Howells //_debug("VLLS PUT %p{%u}", vllist, u); 710a5143f2SDavid Howells if (u == 0) { 720a5143f2SDavid Howells int i; 730a5143f2SDavid Howells 740a5143f2SDavid Howells for (i = 0; i < vllist->nr_servers; i++) { 750a5143f2SDavid Howells afs_put_vlserver(net, vllist->servers[i].server); 760a5143f2SDavid Howells } 770a5143f2SDavid Howells kfree_rcu(vllist, rcu); 780a5143f2SDavid Howells } 790a5143f2SDavid Howells } 800a5143f2SDavid Howells } 810a5143f2SDavid Howells 820a5143f2SDavid Howells static u16 afs_extract_le16(const u8 **_b) 830a5143f2SDavid Howells { 840a5143f2SDavid Howells u16 val; 850a5143f2SDavid Howells 860a5143f2SDavid Howells val = (u16)*(*_b)++ << 0; 870a5143f2SDavid Howells val |= (u16)*(*_b)++ << 8; 880a5143f2SDavid Howells return val; 890a5143f2SDavid Howells } 900a5143f2SDavid Howells 910a5143f2SDavid Howells /* 920a5143f2SDavid Howells * Build a VL server address list from a DNS queried server list. 930a5143f2SDavid Howells */ 940a5143f2SDavid Howells static struct afs_addr_list *afs_extract_vl_addrs(const u8 **_b, const u8 *end, 950a5143f2SDavid Howells u8 nr_addrs, u16 port) 960a5143f2SDavid Howells { 970a5143f2SDavid Howells struct afs_addr_list *alist; 980a5143f2SDavid Howells const u8 *b = *_b; 990a5143f2SDavid Howells int ret = -EINVAL; 1000a5143f2SDavid Howells 1010a5143f2SDavid Howells alist = afs_alloc_addrlist(nr_addrs, VL_SERVICE, port); 1020a5143f2SDavid Howells if (!alist) 1030a5143f2SDavid Howells return ERR_PTR(-ENOMEM); 1040a5143f2SDavid Howells if (nr_addrs == 0) 1050a5143f2SDavid Howells return alist; 1060a5143f2SDavid Howells 1070a5143f2SDavid Howells for (; nr_addrs > 0 && end - b >= nr_addrs; nr_addrs--) { 1080a5143f2SDavid Howells struct dns_server_list_v1_address hdr; 1090a5143f2SDavid Howells __be32 x[4]; 1100a5143f2SDavid Howells 1110a5143f2SDavid Howells hdr.address_type = *b++; 1120a5143f2SDavid Howells 1130a5143f2SDavid Howells switch (hdr.address_type) { 1140a5143f2SDavid Howells case DNS_ADDRESS_IS_IPV4: 1150a5143f2SDavid Howells if (end - b < 4) { 1160a5143f2SDavid Howells _leave(" = -EINVAL [short inet]"); 1170a5143f2SDavid Howells goto error; 1180a5143f2SDavid Howells } 1190a5143f2SDavid Howells memcpy(x, b, 4); 1200a5143f2SDavid Howells afs_merge_fs_addr4(alist, x[0], port); 1210a5143f2SDavid Howells b += 4; 1220a5143f2SDavid Howells break; 1230a5143f2SDavid Howells 1240a5143f2SDavid Howells case DNS_ADDRESS_IS_IPV6: 1250a5143f2SDavid Howells if (end - b < 16) { 1260a5143f2SDavid Howells _leave(" = -EINVAL [short inet6]"); 1270a5143f2SDavid Howells goto error; 1280a5143f2SDavid Howells } 1290a5143f2SDavid Howells memcpy(x, b, 16); 1300a5143f2SDavid Howells afs_merge_fs_addr6(alist, x, port); 1310a5143f2SDavid Howells b += 16; 1320a5143f2SDavid Howells break; 1330a5143f2SDavid Howells 1340a5143f2SDavid Howells default: 1350a5143f2SDavid Howells _leave(" = -EADDRNOTAVAIL [unknown af %u]", 1360a5143f2SDavid Howells hdr.address_type); 1370a5143f2SDavid Howells ret = -EADDRNOTAVAIL; 1380a5143f2SDavid Howells goto error; 1390a5143f2SDavid Howells } 1400a5143f2SDavid Howells } 1410a5143f2SDavid Howells 1420a5143f2SDavid Howells /* Start with IPv6 if available. */ 1430a5143f2SDavid Howells if (alist->nr_ipv4 < alist->nr_addrs) 1440a5143f2SDavid Howells alist->index = alist->nr_ipv4; 1450a5143f2SDavid Howells 1460a5143f2SDavid Howells *_b = b; 1470a5143f2SDavid Howells return alist; 1480a5143f2SDavid Howells 1490a5143f2SDavid Howells error: 1500a5143f2SDavid Howells *_b = b; 1510a5143f2SDavid Howells afs_put_addrlist(alist); 1520a5143f2SDavid Howells return ERR_PTR(ret); 1530a5143f2SDavid Howells } 1540a5143f2SDavid Howells 1550a5143f2SDavid Howells /* 1560a5143f2SDavid Howells * Build a VL server list from a DNS queried server list. 1570a5143f2SDavid Howells */ 1580a5143f2SDavid Howells struct afs_vlserver_list *afs_extract_vlserver_list(struct afs_cell *cell, 1590a5143f2SDavid Howells const void *buffer, 1600a5143f2SDavid Howells size_t buffer_size) 1610a5143f2SDavid Howells { 1620a5143f2SDavid Howells const struct dns_server_list_v1_header *hdr = buffer; 1630a5143f2SDavid Howells struct dns_server_list_v1_server bs; 1640a5143f2SDavid Howells struct afs_vlserver_list *vllist, *previous; 1650a5143f2SDavid Howells struct afs_addr_list *addrs; 1660a5143f2SDavid Howells struct afs_vlserver *server; 1670a5143f2SDavid Howells const u8 *b = buffer, *end = buffer + buffer_size; 1680a5143f2SDavid Howells int ret = -ENOMEM, nr_servers, i, j; 1690a5143f2SDavid Howells 1700a5143f2SDavid Howells _enter(""); 1710a5143f2SDavid Howells 1720a5143f2SDavid Howells /* Check that it's a server list, v1 */ 1730a5143f2SDavid Howells if (end - b < sizeof(*hdr) || 1740a5143f2SDavid Howells hdr->hdr.content != DNS_PAYLOAD_IS_SERVER_LIST || 1750a5143f2SDavid Howells hdr->hdr.version != 1) { 1760a5143f2SDavid Howells pr_notice("kAFS: Got DNS record [%u,%u] len %zu\n", 1770a5143f2SDavid Howells hdr->hdr.content, hdr->hdr.version, end - b); 1780a5143f2SDavid Howells ret = -EDESTADDRREQ; 1790a5143f2SDavid Howells goto dump; 1800a5143f2SDavid Howells } 1810a5143f2SDavid Howells 1820a5143f2SDavid Howells nr_servers = hdr->nr_servers; 1830a5143f2SDavid Howells 1840a5143f2SDavid Howells vllist = afs_alloc_vlserver_list(nr_servers); 1850a5143f2SDavid Howells if (!vllist) 1860a5143f2SDavid Howells return ERR_PTR(-ENOMEM); 1870a5143f2SDavid Howells 1880a5143f2SDavid Howells vllist->source = (hdr->source < NR__dns_record_source) ? 1890a5143f2SDavid Howells hdr->source : NR__dns_record_source; 1900a5143f2SDavid Howells vllist->status = (hdr->status < NR__dns_lookup_status) ? 1910a5143f2SDavid Howells hdr->status : NR__dns_lookup_status; 1920a5143f2SDavid Howells 1930a5143f2SDavid Howells read_lock(&cell->vl_servers_lock); 1940a5143f2SDavid Howells previous = afs_get_vlserverlist( 1950a5143f2SDavid Howells rcu_dereference_protected(cell->vl_servers, 1960a5143f2SDavid Howells lockdep_is_held(&cell->vl_servers_lock))); 1970a5143f2SDavid Howells read_unlock(&cell->vl_servers_lock); 1980a5143f2SDavid Howells 1990a5143f2SDavid Howells b += sizeof(*hdr); 2000a5143f2SDavid Howells while (end - b >= sizeof(bs)) { 2010a5143f2SDavid Howells bs.name_len = afs_extract_le16(&b); 2020a5143f2SDavid Howells bs.priority = afs_extract_le16(&b); 2030a5143f2SDavid Howells bs.weight = afs_extract_le16(&b); 2040a5143f2SDavid Howells bs.port = afs_extract_le16(&b); 2050a5143f2SDavid Howells bs.source = *b++; 2060a5143f2SDavid Howells bs.status = *b++; 2070a5143f2SDavid Howells bs.protocol = *b++; 2080a5143f2SDavid Howells bs.nr_addrs = *b++; 2090a5143f2SDavid Howells 2100a5143f2SDavid Howells _debug("extract %u %u %u %u %u %u %*.*s", 2110a5143f2SDavid Howells bs.name_len, bs.priority, bs.weight, 2120a5143f2SDavid Howells bs.port, bs.protocol, bs.nr_addrs, 2130a5143f2SDavid Howells bs.name_len, bs.name_len, b); 2140a5143f2SDavid Howells 2150a5143f2SDavid Howells if (end - b < bs.name_len) 2160a5143f2SDavid Howells break; 2170a5143f2SDavid Howells 2180a5143f2SDavid Howells ret = -EPROTONOSUPPORT; 2190a5143f2SDavid Howells if (bs.protocol == DNS_SERVER_PROTOCOL_UNSPECIFIED) { 2200a5143f2SDavid Howells bs.protocol = DNS_SERVER_PROTOCOL_UDP; 2210a5143f2SDavid Howells } else if (bs.protocol != DNS_SERVER_PROTOCOL_UDP) { 2220a5143f2SDavid Howells _leave(" = [proto %u]", bs.protocol); 2230a5143f2SDavid Howells goto error; 2240a5143f2SDavid Howells } 2250a5143f2SDavid Howells 2260a5143f2SDavid Howells if (bs.port == 0) 2270a5143f2SDavid Howells bs.port = AFS_VL_PORT; 2280a5143f2SDavid Howells if (bs.source > NR__dns_record_source) 2290a5143f2SDavid Howells bs.source = NR__dns_record_source; 2300a5143f2SDavid Howells if (bs.status > NR__dns_lookup_status) 2310a5143f2SDavid Howells bs.status = NR__dns_lookup_status; 2320a5143f2SDavid Howells 2330a5143f2SDavid Howells server = NULL; 2340a5143f2SDavid Howells if (previous) { 2350a5143f2SDavid Howells /* See if we can update an old server record */ 2360a5143f2SDavid Howells for (i = 0; i < previous->nr_servers; i++) { 2370a5143f2SDavid Howells struct afs_vlserver *p = previous->servers[i].server; 2380a5143f2SDavid Howells 2390a5143f2SDavid Howells if (p->name_len == bs.name_len && 2400a5143f2SDavid Howells p->port == bs.port && 2410a5143f2SDavid Howells strncasecmp(b, p->name, bs.name_len) == 0) { 2420a5143f2SDavid Howells server = afs_get_vlserver(p); 2430a5143f2SDavid Howells break; 2440a5143f2SDavid Howells } 2450a5143f2SDavid Howells } 2460a5143f2SDavid Howells } 2470a5143f2SDavid Howells 2480a5143f2SDavid Howells if (!server) { 2490a5143f2SDavid Howells ret = -ENOMEM; 2500a5143f2SDavid Howells server = afs_alloc_vlserver(b, bs.name_len, bs.port); 2510a5143f2SDavid Howells if (!server) 2520a5143f2SDavid Howells goto error; 2530a5143f2SDavid Howells } 2540a5143f2SDavid Howells 2550a5143f2SDavid Howells b += bs.name_len; 2560a5143f2SDavid Howells 2570a5143f2SDavid Howells /* Extract the addresses - note that we can't skip this as we 2580a5143f2SDavid Howells * have to advance the payload pointer. 2590a5143f2SDavid Howells */ 2600a5143f2SDavid Howells addrs = afs_extract_vl_addrs(&b, end, bs.nr_addrs, bs.port); 2610a5143f2SDavid Howells if (IS_ERR(addrs)) { 2620a5143f2SDavid Howells ret = PTR_ERR(addrs); 2630a5143f2SDavid Howells goto error_2; 2640a5143f2SDavid Howells } 2650a5143f2SDavid Howells 2660a5143f2SDavid Howells if (vllist->nr_servers >= nr_servers) { 2670a5143f2SDavid Howells _debug("skip %u >= %u", vllist->nr_servers, nr_servers); 2680a5143f2SDavid Howells afs_put_addrlist(addrs); 2690a5143f2SDavid Howells afs_put_vlserver(cell->net, server); 2700a5143f2SDavid Howells continue; 2710a5143f2SDavid Howells } 2720a5143f2SDavid Howells 2730a5143f2SDavid Howells addrs->source = bs.source; 2740a5143f2SDavid Howells addrs->status = bs.status; 2750a5143f2SDavid Howells 2760a5143f2SDavid Howells if (addrs->nr_addrs == 0) { 2770a5143f2SDavid Howells afs_put_addrlist(addrs); 2780a5143f2SDavid Howells if (!rcu_access_pointer(server->addresses)) { 2790a5143f2SDavid Howells afs_put_vlserver(cell->net, server); 2800a5143f2SDavid Howells continue; 2810a5143f2SDavid Howells } 2820a5143f2SDavid Howells } else { 2830a5143f2SDavid Howells struct afs_addr_list *old = addrs; 2840a5143f2SDavid Howells 2850a5143f2SDavid Howells write_lock(&server->lock); 2860a5143f2SDavid Howells rcu_swap_protected(server->addresses, old, 2870a5143f2SDavid Howells lockdep_is_held(&server->lock)); 2880a5143f2SDavid Howells write_unlock(&server->lock); 2890a5143f2SDavid Howells afs_put_addrlist(old); 2900a5143f2SDavid Howells } 2910a5143f2SDavid Howells 2920a5143f2SDavid Howells 2930a5143f2SDavid Howells /* TODO: Might want to check for duplicates */ 2940a5143f2SDavid Howells 2950a5143f2SDavid Howells /* Insertion-sort by priority and weight */ 2960a5143f2SDavid Howells for (j = 0; j < vllist->nr_servers; j++) { 2970a5143f2SDavid Howells if (bs.priority < vllist->servers[j].priority) 2980a5143f2SDavid Howells break; /* Lower preferable */ 2990a5143f2SDavid Howells if (bs.priority == vllist->servers[j].priority && 3000a5143f2SDavid Howells bs.weight > vllist->servers[j].weight) 3010a5143f2SDavid Howells break; /* Higher preferable */ 3020a5143f2SDavid Howells } 3030a5143f2SDavid Howells 3040a5143f2SDavid Howells if (j < vllist->nr_servers) { 3050a5143f2SDavid Howells memmove(vllist->servers + j + 1, 3060a5143f2SDavid Howells vllist->servers + j, 3070a5143f2SDavid Howells (vllist->nr_servers - j) * sizeof(struct afs_vlserver_entry)); 3080a5143f2SDavid Howells } 3090a5143f2SDavid Howells 3100a5143f2SDavid Howells vllist->servers[j].priority = bs.priority; 3110a5143f2SDavid Howells vllist->servers[j].weight = bs.weight; 3120a5143f2SDavid Howells vllist->servers[j].server = server; 3130a5143f2SDavid Howells vllist->nr_servers++; 3140a5143f2SDavid Howells } 3150a5143f2SDavid Howells 3160a5143f2SDavid Howells if (b != end) { 3170a5143f2SDavid Howells _debug("parse error %zd", b - end); 3180a5143f2SDavid Howells goto error; 3190a5143f2SDavid Howells } 3200a5143f2SDavid Howells 3210a5143f2SDavid Howells afs_put_vlserverlist(cell->net, previous); 3220a5143f2SDavid Howells _leave(" = ok [%u]", vllist->nr_servers); 3230a5143f2SDavid Howells return vllist; 3240a5143f2SDavid Howells 3250a5143f2SDavid Howells error_2: 3260a5143f2SDavid Howells afs_put_vlserver(cell->net, server); 3270a5143f2SDavid Howells error: 3280a5143f2SDavid Howells afs_put_vlserverlist(cell->net, vllist); 3290a5143f2SDavid Howells afs_put_vlserverlist(cell->net, previous); 3300a5143f2SDavid Howells dump: 3310a5143f2SDavid Howells if (ret != -ENOMEM) { 3320a5143f2SDavid Howells printk(KERN_DEBUG "DNS: at %zu\n", (const void *)b - buffer); 3330a5143f2SDavid Howells print_hex_dump_bytes("DNS: ", DUMP_PREFIX_NONE, buffer, buffer_size); 3340a5143f2SDavid Howells } 3350a5143f2SDavid Howells return ERR_PTR(ret); 3360a5143f2SDavid Howells } 337