14954f17dSDmitry Safonov // SPDX-License-Identifier: GPL-2.0-or-later 24954f17dSDmitry Safonov /* 34954f17dSDmitry Safonov * INET An implementation of the TCP Authentication Option (TCP-AO). 44954f17dSDmitry Safonov * See RFC5925. 54954f17dSDmitry Safonov * 64954f17dSDmitry Safonov * Authors: Dmitry Safonov <dima@arista.com> 74954f17dSDmitry Safonov * Francesco Ruggeri <fruggeri@arista.com> 84954f17dSDmitry Safonov * Salam Noureddine <noureddine@arista.com> 94954f17dSDmitry Safonov */ 104954f17dSDmitry Safonov #define pr_fmt(fmt) "TCP: " fmt 114954f17dSDmitry Safonov 124954f17dSDmitry Safonov #include <crypto/hash.h> 134954f17dSDmitry Safonov #include <linux/inetdevice.h> 144954f17dSDmitry Safonov #include <linux/tcp.h> 154954f17dSDmitry Safonov 164954f17dSDmitry Safonov #include <net/tcp.h> 174954f17dSDmitry Safonov #include <net/ipv6.h> 18953af8e3SDmitry Safonov #include <net/icmp.h> 194954f17dSDmitry Safonov 2067fa83f7SDmitry Safonov DEFINE_STATIC_KEY_DEFERRED_FALSE(tcp_ao_needed, HZ); 2167fa83f7SDmitry Safonov 227c2ffaf2SDmitry Safonov int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, 237c2ffaf2SDmitry Safonov unsigned int len, struct tcp_sigpool *hp) 247c2ffaf2SDmitry Safonov { 257c2ffaf2SDmitry Safonov struct scatterlist sg; 267c2ffaf2SDmitry Safonov int ret; 277c2ffaf2SDmitry Safonov 287c2ffaf2SDmitry Safonov if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp->req), 297c2ffaf2SDmitry Safonov mkt->key, mkt->keylen)) 307c2ffaf2SDmitry Safonov goto clear_hash; 317c2ffaf2SDmitry Safonov 327c2ffaf2SDmitry Safonov ret = crypto_ahash_init(hp->req); 337c2ffaf2SDmitry Safonov if (ret) 347c2ffaf2SDmitry Safonov goto clear_hash; 357c2ffaf2SDmitry Safonov 367c2ffaf2SDmitry Safonov sg_init_one(&sg, ctx, len); 377c2ffaf2SDmitry Safonov ahash_request_set_crypt(hp->req, &sg, key, len); 387c2ffaf2SDmitry Safonov crypto_ahash_update(hp->req); 397c2ffaf2SDmitry Safonov 407c2ffaf2SDmitry Safonov ret = crypto_ahash_final(hp->req); 417c2ffaf2SDmitry Safonov if (ret) 427c2ffaf2SDmitry Safonov goto clear_hash; 437c2ffaf2SDmitry Safonov 447c2ffaf2SDmitry Safonov return 0; 457c2ffaf2SDmitry Safonov clear_hash: 467c2ffaf2SDmitry Safonov memset(key, 0, tcp_ao_digest_size(mkt)); 477c2ffaf2SDmitry Safonov return 1; 487c2ffaf2SDmitry Safonov } 497c2ffaf2SDmitry Safonov 50953af8e3SDmitry Safonov bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code) 51953af8e3SDmitry Safonov { 52953af8e3SDmitry Safonov bool ignore_icmp = false; 53953af8e3SDmitry Safonov struct tcp_ao_info *ao; 54953af8e3SDmitry Safonov 5567fa83f7SDmitry Safonov if (!static_branch_unlikely(&tcp_ao_needed.key)) 5667fa83f7SDmitry Safonov return false; 5767fa83f7SDmitry Safonov 58953af8e3SDmitry Safonov /* RFC5925, 7.8: 59953af8e3SDmitry Safonov * >> A TCP-AO implementation MUST default to ignore incoming ICMPv4 60953af8e3SDmitry Safonov * messages of Type 3 (destination unreachable), Codes 2-4 (protocol 61953af8e3SDmitry Safonov * unreachable, port unreachable, and fragmentation needed -- ’hard 62953af8e3SDmitry Safonov * errors’), and ICMPv6 Type 1 (destination unreachable), Code 1 63953af8e3SDmitry Safonov * (administratively prohibited) and Code 4 (port unreachable) intended 64953af8e3SDmitry Safonov * for connections in synchronized states (ESTABLISHED, FIN-WAIT-1, FIN- 65953af8e3SDmitry Safonov * WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT) that match MKTs. 66953af8e3SDmitry Safonov */ 67953af8e3SDmitry Safonov if (family == AF_INET) { 68953af8e3SDmitry Safonov if (type != ICMP_DEST_UNREACH) 69953af8e3SDmitry Safonov return false; 70953af8e3SDmitry Safonov if (code < ICMP_PROT_UNREACH || code > ICMP_FRAG_NEEDED) 71953af8e3SDmitry Safonov return false; 72953af8e3SDmitry Safonov } else { 73953af8e3SDmitry Safonov if (type != ICMPV6_DEST_UNREACH) 74953af8e3SDmitry Safonov return false; 75953af8e3SDmitry Safonov if (code != ICMPV6_ADM_PROHIBITED && code != ICMPV6_PORT_UNREACH) 76953af8e3SDmitry Safonov return false; 77953af8e3SDmitry Safonov } 78953af8e3SDmitry Safonov 79953af8e3SDmitry Safonov rcu_read_lock(); 80953af8e3SDmitry Safonov switch (sk->sk_state) { 81953af8e3SDmitry Safonov case TCP_TIME_WAIT: 82953af8e3SDmitry Safonov ao = rcu_dereference(tcp_twsk(sk)->ao_info); 83953af8e3SDmitry Safonov break; 84953af8e3SDmitry Safonov case TCP_SYN_SENT: 85953af8e3SDmitry Safonov case TCP_SYN_RECV: 86953af8e3SDmitry Safonov case TCP_LISTEN: 87953af8e3SDmitry Safonov case TCP_NEW_SYN_RECV: 88953af8e3SDmitry Safonov /* RFC5925 specifies to ignore ICMPs *only* on connections 89953af8e3SDmitry Safonov * in synchronized states. 90953af8e3SDmitry Safonov */ 91953af8e3SDmitry Safonov rcu_read_unlock(); 92953af8e3SDmitry Safonov return false; 93953af8e3SDmitry Safonov default: 94953af8e3SDmitry Safonov ao = rcu_dereference(tcp_sk(sk)->ao_info); 95953af8e3SDmitry Safonov } 96953af8e3SDmitry Safonov 97953af8e3SDmitry Safonov if (ao && !ao->accept_icmps) { 98953af8e3SDmitry Safonov ignore_icmp = true; 99953af8e3SDmitry Safonov __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAODROPPEDICMPS); 100953af8e3SDmitry Safonov atomic64_inc(&ao->counters.dropped_icmp); 101953af8e3SDmitry Safonov } 102953af8e3SDmitry Safonov rcu_read_unlock(); 103953af8e3SDmitry Safonov 104953af8e3SDmitry Safonov return ignore_icmp; 105953af8e3SDmitry Safonov } 106953af8e3SDmitry Safonov 1074954f17dSDmitry Safonov /* Optimized version of tcp_ao_do_lookup(): only for sockets for which 1084954f17dSDmitry Safonov * it's known that the keys in ao_info are matching peer's 1094954f17dSDmitry Safonov * family/address/VRF/etc. 1104954f17dSDmitry Safonov */ 111ba7783adSDmitry Safonov struct tcp_ao_key *tcp_ao_established_key(struct tcp_ao_info *ao, 1124954f17dSDmitry Safonov int sndid, int rcvid) 1134954f17dSDmitry Safonov { 1144954f17dSDmitry Safonov struct tcp_ao_key *key; 1154954f17dSDmitry Safonov 1164954f17dSDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) { 1174954f17dSDmitry Safonov if ((sndid >= 0 && key->sndid != sndid) || 1184954f17dSDmitry Safonov (rcvid >= 0 && key->rcvid != rcvid)) 1194954f17dSDmitry Safonov continue; 1204954f17dSDmitry Safonov return key; 1214954f17dSDmitry Safonov } 1224954f17dSDmitry Safonov 1234954f17dSDmitry Safonov return NULL; 1244954f17dSDmitry Safonov } 1254954f17dSDmitry Safonov 1264954f17dSDmitry Safonov static int ipv4_prefix_cmp(const struct in_addr *addr1, 1274954f17dSDmitry Safonov const struct in_addr *addr2, 1284954f17dSDmitry Safonov unsigned int prefixlen) 1294954f17dSDmitry Safonov { 1304954f17dSDmitry Safonov __be32 mask = inet_make_mask(prefixlen); 1314954f17dSDmitry Safonov __be32 a1 = addr1->s_addr & mask; 1324954f17dSDmitry Safonov __be32 a2 = addr2->s_addr & mask; 1334954f17dSDmitry Safonov 1344954f17dSDmitry Safonov if (a1 == a2) 1354954f17dSDmitry Safonov return 0; 1364954f17dSDmitry Safonov return memcmp(&a1, &a2, sizeof(a1)); 1374954f17dSDmitry Safonov } 1384954f17dSDmitry Safonov 139248411b8SDmitry Safonov static int __tcp_ao_key_cmp(const struct tcp_ao_key *key, int l3index, 1404954f17dSDmitry Safonov const union tcp_ao_addr *addr, u8 prefixlen, 1414954f17dSDmitry Safonov int family, int sndid, int rcvid) 1424954f17dSDmitry Safonov { 1434954f17dSDmitry Safonov if (sndid >= 0 && key->sndid != sndid) 1444954f17dSDmitry Safonov return (key->sndid > sndid) ? 1 : -1; 1454954f17dSDmitry Safonov if (rcvid >= 0 && key->rcvid != rcvid) 1464954f17dSDmitry Safonov return (key->rcvid > rcvid) ? 1 : -1; 147248411b8SDmitry Safonov if (l3index >= 0 && (key->keyflags & TCP_AO_KEYF_IFINDEX)) { 148248411b8SDmitry Safonov if (key->l3index != l3index) 149248411b8SDmitry Safonov return (key->l3index > l3index) ? 1 : -1; 150248411b8SDmitry Safonov } 1514954f17dSDmitry Safonov 1524954f17dSDmitry Safonov if (family == AF_UNSPEC) 1534954f17dSDmitry Safonov return 0; 1544954f17dSDmitry Safonov if (key->family != family) 1554954f17dSDmitry Safonov return (key->family > family) ? 1 : -1; 1564954f17dSDmitry Safonov 1574954f17dSDmitry Safonov if (family == AF_INET) { 1584954f17dSDmitry Safonov if (ntohl(key->addr.a4.s_addr) == INADDR_ANY) 1594954f17dSDmitry Safonov return 0; 1604954f17dSDmitry Safonov if (ntohl(addr->a4.s_addr) == INADDR_ANY) 1614954f17dSDmitry Safonov return 0; 1624954f17dSDmitry Safonov return ipv4_prefix_cmp(&key->addr.a4, &addr->a4, prefixlen); 1634954f17dSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 1644954f17dSDmitry Safonov } else { 1654954f17dSDmitry Safonov if (ipv6_addr_any(&key->addr.a6) || ipv6_addr_any(&addr->a6)) 1664954f17dSDmitry Safonov return 0; 1674954f17dSDmitry Safonov if (ipv6_prefix_equal(&key->addr.a6, &addr->a6, prefixlen)) 1684954f17dSDmitry Safonov return 0; 1694954f17dSDmitry Safonov return memcmp(&key->addr.a6, &addr->a6, sizeof(addr->a6)); 1704954f17dSDmitry Safonov #endif 1714954f17dSDmitry Safonov } 1724954f17dSDmitry Safonov return -1; 1734954f17dSDmitry Safonov } 1744954f17dSDmitry Safonov 175248411b8SDmitry Safonov static int tcp_ao_key_cmp(const struct tcp_ao_key *key, int l3index, 1764954f17dSDmitry Safonov const union tcp_ao_addr *addr, u8 prefixlen, 1774954f17dSDmitry Safonov int family, int sndid, int rcvid) 1784954f17dSDmitry Safonov { 1794954f17dSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 1804954f17dSDmitry Safonov if (family == AF_INET6 && ipv6_addr_v4mapped(&addr->a6)) { 1814954f17dSDmitry Safonov __be32 addr4 = addr->a6.s6_addr32[3]; 1824954f17dSDmitry Safonov 183248411b8SDmitry Safonov return __tcp_ao_key_cmp(key, l3index, 184248411b8SDmitry Safonov (union tcp_ao_addr *)&addr4, 1854954f17dSDmitry Safonov prefixlen, AF_INET, sndid, rcvid); 1864954f17dSDmitry Safonov } 1874954f17dSDmitry Safonov #endif 188248411b8SDmitry Safonov return __tcp_ao_key_cmp(key, l3index, addr, 189248411b8SDmitry Safonov prefixlen, family, sndid, rcvid); 1904954f17dSDmitry Safonov } 1914954f17dSDmitry Safonov 192248411b8SDmitry Safonov static struct tcp_ao_key *__tcp_ao_do_lookup(const struct sock *sk, int l3index, 1934954f17dSDmitry Safonov const union tcp_ao_addr *addr, int family, u8 prefix, 1944954f17dSDmitry Safonov int sndid, int rcvid) 1954954f17dSDmitry Safonov { 1964954f17dSDmitry Safonov struct tcp_ao_key *key; 1974954f17dSDmitry Safonov struct tcp_ao_info *ao; 1984954f17dSDmitry Safonov 19967fa83f7SDmitry Safonov if (!static_branch_unlikely(&tcp_ao_needed.key)) 20067fa83f7SDmitry Safonov return NULL; 20167fa83f7SDmitry Safonov 2024954f17dSDmitry Safonov ao = rcu_dereference_check(tcp_sk(sk)->ao_info, 2034954f17dSDmitry Safonov lockdep_sock_is_held(sk)); 2044954f17dSDmitry Safonov if (!ao) 2054954f17dSDmitry Safonov return NULL; 2064954f17dSDmitry Safonov 2074954f17dSDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) { 2084954f17dSDmitry Safonov u8 prefixlen = min(prefix, key->prefixlen); 2094954f17dSDmitry Safonov 210248411b8SDmitry Safonov if (!tcp_ao_key_cmp(key, l3index, addr, prefixlen, 211248411b8SDmitry Safonov family, sndid, rcvid)) 2124954f17dSDmitry Safonov return key; 2134954f17dSDmitry Safonov } 2144954f17dSDmitry Safonov return NULL; 2154954f17dSDmitry Safonov } 2164954f17dSDmitry Safonov 217248411b8SDmitry Safonov struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, int l3index, 2180aadc739SDmitry Safonov const union tcp_ao_addr *addr, 2190aadc739SDmitry Safonov int family, int sndid, int rcvid) 2200aadc739SDmitry Safonov { 221248411b8SDmitry Safonov return __tcp_ao_do_lookup(sk, l3index, addr, family, U8_MAX, sndid, rcvid); 2220aadc739SDmitry Safonov } 2230aadc739SDmitry Safonov 2244954f17dSDmitry Safonov static struct tcp_ao_info *tcp_ao_alloc_info(gfp_t flags) 2254954f17dSDmitry Safonov { 2264954f17dSDmitry Safonov struct tcp_ao_info *ao; 2274954f17dSDmitry Safonov 2284954f17dSDmitry Safonov ao = kzalloc(sizeof(*ao), flags); 2294954f17dSDmitry Safonov if (!ao) 2304954f17dSDmitry Safonov return NULL; 2314954f17dSDmitry Safonov INIT_HLIST_HEAD(&ao->head); 232decde258SDmitry Safonov refcount_set(&ao->refcnt, 1); 2334954f17dSDmitry Safonov 2344954f17dSDmitry Safonov return ao; 2354954f17dSDmitry Safonov } 2364954f17dSDmitry Safonov 2374954f17dSDmitry Safonov static void tcp_ao_link_mkt(struct tcp_ao_info *ao, struct tcp_ao_key *mkt) 2384954f17dSDmitry Safonov { 2394954f17dSDmitry Safonov hlist_add_head_rcu(&mkt->node, &ao->head); 2404954f17dSDmitry Safonov } 2414954f17dSDmitry Safonov 24206b22ef2SDmitry Safonov static struct tcp_ao_key *tcp_ao_copy_key(struct sock *sk, 24306b22ef2SDmitry Safonov struct tcp_ao_key *key) 24406b22ef2SDmitry Safonov { 24506b22ef2SDmitry Safonov struct tcp_ao_key *new_key; 24606b22ef2SDmitry Safonov 24706b22ef2SDmitry Safonov new_key = sock_kmalloc(sk, tcp_ao_sizeof_key(key), 24806b22ef2SDmitry Safonov GFP_ATOMIC); 24906b22ef2SDmitry Safonov if (!new_key) 25006b22ef2SDmitry Safonov return NULL; 25106b22ef2SDmitry Safonov 25206b22ef2SDmitry Safonov *new_key = *key; 25306b22ef2SDmitry Safonov INIT_HLIST_NODE(&new_key->node); 25406b22ef2SDmitry Safonov tcp_sigpool_get(new_key->tcp_sigpool_id); 255af09a341SDmitry Safonov atomic64_set(&new_key->pkt_good, 0); 256af09a341SDmitry Safonov atomic64_set(&new_key->pkt_bad, 0); 25706b22ef2SDmitry Safonov 25806b22ef2SDmitry Safonov return new_key; 25906b22ef2SDmitry Safonov } 26006b22ef2SDmitry Safonov 2614954f17dSDmitry Safonov static void tcp_ao_key_free_rcu(struct rcu_head *head) 2624954f17dSDmitry Safonov { 2634954f17dSDmitry Safonov struct tcp_ao_key *key = container_of(head, struct tcp_ao_key, rcu); 2644954f17dSDmitry Safonov 2654954f17dSDmitry Safonov tcp_sigpool_release(key->tcp_sigpool_id); 2664954f17dSDmitry Safonov kfree_sensitive(key); 2674954f17dSDmitry Safonov } 2684954f17dSDmitry Safonov 269decde258SDmitry Safonov void tcp_ao_destroy_sock(struct sock *sk, bool twsk) 2704954f17dSDmitry Safonov { 2714954f17dSDmitry Safonov struct tcp_ao_info *ao; 2724954f17dSDmitry Safonov struct tcp_ao_key *key; 2734954f17dSDmitry Safonov struct hlist_node *n; 2744954f17dSDmitry Safonov 275decde258SDmitry Safonov if (twsk) { 276decde258SDmitry Safonov ao = rcu_dereference_protected(tcp_twsk(sk)->ao_info, 1); 277decde258SDmitry Safonov tcp_twsk(sk)->ao_info = NULL; 278decde258SDmitry Safonov } else { 2794954f17dSDmitry Safonov ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 1); 2804954f17dSDmitry Safonov tcp_sk(sk)->ao_info = NULL; 281decde258SDmitry Safonov } 2824954f17dSDmitry Safonov 283decde258SDmitry Safonov if (!ao || !refcount_dec_and_test(&ao->refcnt)) 2844954f17dSDmitry Safonov return; 2854954f17dSDmitry Safonov 2864954f17dSDmitry Safonov hlist_for_each_entry_safe(key, n, &ao->head, node) { 2874954f17dSDmitry Safonov hlist_del_rcu(&key->node); 288decde258SDmitry Safonov if (!twsk) 2894954f17dSDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 2904954f17dSDmitry Safonov call_rcu(&key->rcu, tcp_ao_key_free_rcu); 2914954f17dSDmitry Safonov } 2924954f17dSDmitry Safonov 2934954f17dSDmitry Safonov kfree_rcu(ao, rcu); 29467fa83f7SDmitry Safonov static_branch_slow_dec_deferred(&tcp_ao_needed); 2954954f17dSDmitry Safonov } 2964954f17dSDmitry Safonov 297decde258SDmitry Safonov void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp) 298decde258SDmitry Safonov { 299decde258SDmitry Safonov struct tcp_ao_info *ao_info = rcu_dereference_protected(tp->ao_info, 1); 300decde258SDmitry Safonov 301decde258SDmitry Safonov if (ao_info) { 302decde258SDmitry Safonov struct tcp_ao_key *key; 303decde258SDmitry Safonov struct hlist_node *n; 304decde258SDmitry Safonov int omem = 0; 305decde258SDmitry Safonov 306decde258SDmitry Safonov hlist_for_each_entry_safe(key, n, &ao_info->head, node) { 307decde258SDmitry Safonov omem += tcp_ao_sizeof_key(key); 308decde258SDmitry Safonov } 309decde258SDmitry Safonov 310decde258SDmitry Safonov refcount_inc(&ao_info->refcnt); 311decde258SDmitry Safonov atomic_sub(omem, &(((struct sock *)tp)->sk_omem_alloc)); 312decde258SDmitry Safonov rcu_assign_pointer(tcptw->ao_info, ao_info); 313decde258SDmitry Safonov } else { 314decde258SDmitry Safonov tcptw->ao_info = NULL; 315decde258SDmitry Safonov } 316decde258SDmitry Safonov } 317decde258SDmitry Safonov 3187c2ffaf2SDmitry Safonov /* 4 tuple and ISNs are expected in NBO */ 3197c2ffaf2SDmitry Safonov static int tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key, 3207c2ffaf2SDmitry Safonov __be32 saddr, __be32 daddr, 3217c2ffaf2SDmitry Safonov __be16 sport, __be16 dport, 3227c2ffaf2SDmitry Safonov __be32 sisn, __be32 disn) 3237c2ffaf2SDmitry Safonov { 3247c2ffaf2SDmitry Safonov /* See RFC5926 3.1.1 */ 3257c2ffaf2SDmitry Safonov struct kdf_input_block { 3267c2ffaf2SDmitry Safonov u8 counter; 3277c2ffaf2SDmitry Safonov u8 label[6]; 3287c2ffaf2SDmitry Safonov struct tcp4_ao_context ctx; 3297c2ffaf2SDmitry Safonov __be16 outlen; 3307c2ffaf2SDmitry Safonov } __packed * tmp; 3317c2ffaf2SDmitry Safonov struct tcp_sigpool hp; 3327c2ffaf2SDmitry Safonov int err; 3337c2ffaf2SDmitry Safonov 3347c2ffaf2SDmitry Safonov err = tcp_sigpool_start(mkt->tcp_sigpool_id, &hp); 3357c2ffaf2SDmitry Safonov if (err) 3367c2ffaf2SDmitry Safonov return err; 3377c2ffaf2SDmitry Safonov 3387c2ffaf2SDmitry Safonov tmp = hp.scratch; 3397c2ffaf2SDmitry Safonov tmp->counter = 1; 3407c2ffaf2SDmitry Safonov memcpy(tmp->label, "TCP-AO", 6); 3417c2ffaf2SDmitry Safonov tmp->ctx.saddr = saddr; 3427c2ffaf2SDmitry Safonov tmp->ctx.daddr = daddr; 3437c2ffaf2SDmitry Safonov tmp->ctx.sport = sport; 3447c2ffaf2SDmitry Safonov tmp->ctx.dport = dport; 3457c2ffaf2SDmitry Safonov tmp->ctx.sisn = sisn; 3467c2ffaf2SDmitry Safonov tmp->ctx.disn = disn; 3477c2ffaf2SDmitry Safonov tmp->outlen = htons(tcp_ao_digest_size(mkt) * 8); /* in bits */ 3487c2ffaf2SDmitry Safonov 3497c2ffaf2SDmitry Safonov err = tcp_ao_calc_traffic_key(mkt, key, tmp, sizeof(*tmp), &hp); 3507c2ffaf2SDmitry Safonov tcp_sigpool_end(&hp); 3517c2ffaf2SDmitry Safonov 3527c2ffaf2SDmitry Safonov return err; 3537c2ffaf2SDmitry Safonov } 3547c2ffaf2SDmitry Safonov 3557c2ffaf2SDmitry Safonov int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, 3567c2ffaf2SDmitry Safonov const struct sock *sk, 3577c2ffaf2SDmitry Safonov __be32 sisn, __be32 disn, bool send) 3587c2ffaf2SDmitry Safonov { 3597c2ffaf2SDmitry Safonov if (send) 3607c2ffaf2SDmitry Safonov return tcp_v4_ao_calc_key(mkt, key, sk->sk_rcv_saddr, 3617c2ffaf2SDmitry Safonov sk->sk_daddr, htons(sk->sk_num), 3627c2ffaf2SDmitry Safonov sk->sk_dport, sisn, disn); 3637c2ffaf2SDmitry Safonov else 3647c2ffaf2SDmitry Safonov return tcp_v4_ao_calc_key(mkt, key, sk->sk_daddr, 3657c2ffaf2SDmitry Safonov sk->sk_rcv_saddr, sk->sk_dport, 3667c2ffaf2SDmitry Safonov htons(sk->sk_num), disn, sisn); 3677c2ffaf2SDmitry Safonov } 3687c2ffaf2SDmitry Safonov 3697c2ffaf2SDmitry Safonov static int tcp_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, 3707c2ffaf2SDmitry Safonov const struct sock *sk, 3717c2ffaf2SDmitry Safonov __be32 sisn, __be32 disn, bool send) 3727c2ffaf2SDmitry Safonov { 3737c2ffaf2SDmitry Safonov if (mkt->family == AF_INET) 3747c2ffaf2SDmitry Safonov return tcp_v4_ao_calc_key_sk(mkt, key, sk, sisn, disn, send); 3757c2ffaf2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 3767c2ffaf2SDmitry Safonov else if (mkt->family == AF_INET6) 3777c2ffaf2SDmitry Safonov return tcp_v6_ao_calc_key_sk(mkt, key, sk, sisn, disn, send); 3787c2ffaf2SDmitry Safonov #endif 3797c2ffaf2SDmitry Safonov else 3807c2ffaf2SDmitry Safonov return -EOPNOTSUPP; 3817c2ffaf2SDmitry Safonov } 3827c2ffaf2SDmitry Safonov 38306b22ef2SDmitry Safonov int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, 38406b22ef2SDmitry Safonov struct request_sock *req) 38506b22ef2SDmitry Safonov { 38606b22ef2SDmitry Safonov struct inet_request_sock *ireq = inet_rsk(req); 38706b22ef2SDmitry Safonov 38806b22ef2SDmitry Safonov return tcp_v4_ao_calc_key(mkt, key, 38906b22ef2SDmitry Safonov ireq->ir_loc_addr, ireq->ir_rmt_addr, 39006b22ef2SDmitry Safonov htons(ireq->ir_num), ireq->ir_rmt_port, 39106b22ef2SDmitry Safonov htonl(tcp_rsk(req)->snt_isn), 39206b22ef2SDmitry Safonov htonl(tcp_rsk(req)->rcv_isn)); 39306b22ef2SDmitry Safonov } 39406b22ef2SDmitry Safonov 39506b22ef2SDmitry Safonov static int tcp_v4_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, 39606b22ef2SDmitry Safonov const struct sk_buff *skb, 39706b22ef2SDmitry Safonov __be32 sisn, __be32 disn) 39806b22ef2SDmitry Safonov { 39906b22ef2SDmitry Safonov const struct iphdr *iph = ip_hdr(skb); 40006b22ef2SDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 40106b22ef2SDmitry Safonov 40206b22ef2SDmitry Safonov return tcp_v4_ao_calc_key(mkt, key, iph->saddr, iph->daddr, 40306b22ef2SDmitry Safonov th->source, th->dest, sisn, disn); 40406b22ef2SDmitry Safonov } 40506b22ef2SDmitry Safonov 40606b22ef2SDmitry Safonov static int tcp_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, 40706b22ef2SDmitry Safonov const struct sk_buff *skb, 40806b22ef2SDmitry Safonov __be32 sisn, __be32 disn, int family) 40906b22ef2SDmitry Safonov { 41006b22ef2SDmitry Safonov if (family == AF_INET) 41106b22ef2SDmitry Safonov return tcp_v4_ao_calc_key_skb(mkt, key, skb, sisn, disn); 41206b22ef2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 41306b22ef2SDmitry Safonov else if (family == AF_INET6) 41406b22ef2SDmitry Safonov return tcp_v6_ao_calc_key_skb(mkt, key, skb, sisn, disn); 41506b22ef2SDmitry Safonov #endif 41606b22ef2SDmitry Safonov return -EAFNOSUPPORT; 41706b22ef2SDmitry Safonov } 41806b22ef2SDmitry Safonov 4191e03d32bSDmitry Safonov static int tcp_v4_ao_hash_pseudoheader(struct tcp_sigpool *hp, 4201e03d32bSDmitry Safonov __be32 daddr, __be32 saddr, 4211e03d32bSDmitry Safonov int nbytes) 4221e03d32bSDmitry Safonov { 4231e03d32bSDmitry Safonov struct tcp4_pseudohdr *bp; 4241e03d32bSDmitry Safonov struct scatterlist sg; 4251e03d32bSDmitry Safonov 4261e03d32bSDmitry Safonov bp = hp->scratch; 4271e03d32bSDmitry Safonov bp->saddr = saddr; 4281e03d32bSDmitry Safonov bp->daddr = daddr; 4291e03d32bSDmitry Safonov bp->pad = 0; 4301e03d32bSDmitry Safonov bp->protocol = IPPROTO_TCP; 4311e03d32bSDmitry Safonov bp->len = cpu_to_be16(nbytes); 4321e03d32bSDmitry Safonov 4331e03d32bSDmitry Safonov sg_init_one(&sg, bp, sizeof(*bp)); 4341e03d32bSDmitry Safonov ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp)); 4351e03d32bSDmitry Safonov return crypto_ahash_update(hp->req); 4361e03d32bSDmitry Safonov } 4371e03d32bSDmitry Safonov 4381e03d32bSDmitry Safonov static int tcp_ao_hash_pseudoheader(unsigned short int family, 4391e03d32bSDmitry Safonov const struct sock *sk, 4401e03d32bSDmitry Safonov const struct sk_buff *skb, 4411e03d32bSDmitry Safonov struct tcp_sigpool *hp, int nbytes) 4421e03d32bSDmitry Safonov { 4431e03d32bSDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 4441e03d32bSDmitry Safonov 4451e03d32bSDmitry Safonov /* TODO: Can we rely on checksum being zero to mean outbound pkt? */ 4461e03d32bSDmitry Safonov if (!th->check) { 4471e03d32bSDmitry Safonov if (family == AF_INET) 4481e03d32bSDmitry Safonov return tcp_v4_ao_hash_pseudoheader(hp, sk->sk_daddr, 4491e03d32bSDmitry Safonov sk->sk_rcv_saddr, skb->len); 4501e03d32bSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 4511e03d32bSDmitry Safonov else if (family == AF_INET6) 4521e03d32bSDmitry Safonov return tcp_v6_ao_hash_pseudoheader(hp, &sk->sk_v6_daddr, 4531e03d32bSDmitry Safonov &sk->sk_v6_rcv_saddr, skb->len); 4541e03d32bSDmitry Safonov #endif 4551e03d32bSDmitry Safonov else 4561e03d32bSDmitry Safonov return -EAFNOSUPPORT; 4571e03d32bSDmitry Safonov } 4581e03d32bSDmitry Safonov 4591e03d32bSDmitry Safonov if (family == AF_INET) { 4601e03d32bSDmitry Safonov const struct iphdr *iph = ip_hdr(skb); 4611e03d32bSDmitry Safonov 4621e03d32bSDmitry Safonov return tcp_v4_ao_hash_pseudoheader(hp, iph->daddr, 4631e03d32bSDmitry Safonov iph->saddr, skb->len); 4641e03d32bSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 4651e03d32bSDmitry Safonov } else if (family == AF_INET6) { 4661e03d32bSDmitry Safonov const struct ipv6hdr *iph = ipv6_hdr(skb); 4671e03d32bSDmitry Safonov 4681e03d32bSDmitry Safonov return tcp_v6_ao_hash_pseudoheader(hp, &iph->daddr, 4691e03d32bSDmitry Safonov &iph->saddr, skb->len); 4701e03d32bSDmitry Safonov #endif 4711e03d32bSDmitry Safonov } 4721e03d32bSDmitry Safonov return -EAFNOSUPPORT; 4731e03d32bSDmitry Safonov } 4741e03d32bSDmitry Safonov 47564382c71SDmitry Safonov u32 tcp_ao_compute_sne(u32 next_sne, u32 next_seq, u32 seq) 47664382c71SDmitry Safonov { 47764382c71SDmitry Safonov u32 sne = next_sne; 47864382c71SDmitry Safonov 47964382c71SDmitry Safonov if (before(seq, next_seq)) { 48064382c71SDmitry Safonov if (seq > next_seq) 48164382c71SDmitry Safonov sne--; 48264382c71SDmitry Safonov } else { 48364382c71SDmitry Safonov if (seq < next_seq) 48464382c71SDmitry Safonov sne++; 48564382c71SDmitry Safonov } 48664382c71SDmitry Safonov 48764382c71SDmitry Safonov return sne; 48864382c71SDmitry Safonov } 48964382c71SDmitry Safonov 4901e03d32bSDmitry Safonov /* tcp_ao_hash_sne(struct tcp_sigpool *hp) 4911e03d32bSDmitry Safonov * @hp - used for hashing 4921e03d32bSDmitry Safonov * @sne - sne value 4931e03d32bSDmitry Safonov */ 4941e03d32bSDmitry Safonov static int tcp_ao_hash_sne(struct tcp_sigpool *hp, u32 sne) 4951e03d32bSDmitry Safonov { 4961e03d32bSDmitry Safonov struct scatterlist sg; 4971e03d32bSDmitry Safonov __be32 *bp; 4981e03d32bSDmitry Safonov 4991e03d32bSDmitry Safonov bp = (__be32 *)hp->scratch; 5001e03d32bSDmitry Safonov *bp = htonl(sne); 5011e03d32bSDmitry Safonov 5021e03d32bSDmitry Safonov sg_init_one(&sg, bp, sizeof(*bp)); 5031e03d32bSDmitry Safonov ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp)); 5041e03d32bSDmitry Safonov return crypto_ahash_update(hp->req); 5051e03d32bSDmitry Safonov } 5061e03d32bSDmitry Safonov 5071e03d32bSDmitry Safonov static int tcp_ao_hash_header(struct tcp_sigpool *hp, 5081e03d32bSDmitry Safonov const struct tcphdr *th, 5091e03d32bSDmitry Safonov bool exclude_options, u8 *hash, 5101e03d32bSDmitry Safonov int hash_offset, int hash_len) 5111e03d32bSDmitry Safonov { 5121e03d32bSDmitry Safonov struct scatterlist sg; 5131e03d32bSDmitry Safonov u8 *hdr = hp->scratch; 514465c1abcSColin Ian King int err, len; 5151e03d32bSDmitry Safonov 5161e03d32bSDmitry Safonov /* We are not allowed to change tcphdr, make a local copy */ 5171e03d32bSDmitry Safonov if (exclude_options) { 5181e03d32bSDmitry Safonov len = sizeof(*th) + sizeof(struct tcp_ao_hdr) + hash_len; 5191e03d32bSDmitry Safonov memcpy(hdr, th, sizeof(*th)); 5201e03d32bSDmitry Safonov memcpy(hdr + sizeof(*th), 5211e03d32bSDmitry Safonov (u8 *)th + hash_offset - sizeof(struct tcp_ao_hdr), 5221e03d32bSDmitry Safonov sizeof(struct tcp_ao_hdr)); 5231e03d32bSDmitry Safonov memset(hdr + sizeof(*th) + sizeof(struct tcp_ao_hdr), 5241e03d32bSDmitry Safonov 0, hash_len); 5251e03d32bSDmitry Safonov ((struct tcphdr *)hdr)->check = 0; 5261e03d32bSDmitry Safonov } else { 5271e03d32bSDmitry Safonov len = th->doff << 2; 5281e03d32bSDmitry Safonov memcpy(hdr, th, len); 5291e03d32bSDmitry Safonov /* zero out tcp-ao hash */ 5301e03d32bSDmitry Safonov ((struct tcphdr *)hdr)->check = 0; 5311e03d32bSDmitry Safonov memset(hdr + hash_offset, 0, hash_len); 5321e03d32bSDmitry Safonov } 5331e03d32bSDmitry Safonov 5341e03d32bSDmitry Safonov sg_init_one(&sg, hdr, len); 5351e03d32bSDmitry Safonov ahash_request_set_crypt(hp->req, &sg, NULL, len); 5361e03d32bSDmitry Safonov err = crypto_ahash_update(hp->req); 5371e03d32bSDmitry Safonov WARN_ON_ONCE(err != 0); 5381e03d32bSDmitry Safonov return err; 5391e03d32bSDmitry Safonov } 5401e03d32bSDmitry Safonov 541ba7783adSDmitry Safonov int tcp_ao_hash_hdr(unsigned short int family, char *ao_hash, 542ba7783adSDmitry Safonov struct tcp_ao_key *key, const u8 *tkey, 543ba7783adSDmitry Safonov const union tcp_ao_addr *daddr, 544ba7783adSDmitry Safonov const union tcp_ao_addr *saddr, 545ba7783adSDmitry Safonov const struct tcphdr *th, u32 sne) 546ba7783adSDmitry Safonov { 547ba7783adSDmitry Safonov int tkey_len = tcp_ao_digest_size(key); 548ba7783adSDmitry Safonov int hash_offset = ao_hash - (char *)th; 549ba7783adSDmitry Safonov struct tcp_sigpool hp; 550ba7783adSDmitry Safonov void *hash_buf = NULL; 551ba7783adSDmitry Safonov 552ba7783adSDmitry Safonov hash_buf = kmalloc(tkey_len, GFP_ATOMIC); 553ba7783adSDmitry Safonov if (!hash_buf) 554ba7783adSDmitry Safonov goto clear_hash_noput; 555ba7783adSDmitry Safonov 556ba7783adSDmitry Safonov if (tcp_sigpool_start(key->tcp_sigpool_id, &hp)) 557ba7783adSDmitry Safonov goto clear_hash_noput; 558ba7783adSDmitry Safonov 559ba7783adSDmitry Safonov if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), tkey, tkey_len)) 560ba7783adSDmitry Safonov goto clear_hash; 561ba7783adSDmitry Safonov 562ba7783adSDmitry Safonov if (crypto_ahash_init(hp.req)) 563ba7783adSDmitry Safonov goto clear_hash; 564ba7783adSDmitry Safonov 565ba7783adSDmitry Safonov if (tcp_ao_hash_sne(&hp, sne)) 566ba7783adSDmitry Safonov goto clear_hash; 567ba7783adSDmitry Safonov if (family == AF_INET) { 568ba7783adSDmitry Safonov if (tcp_v4_ao_hash_pseudoheader(&hp, daddr->a4.s_addr, 569ba7783adSDmitry Safonov saddr->a4.s_addr, th->doff * 4)) 570ba7783adSDmitry Safonov goto clear_hash; 571ba7783adSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 572ba7783adSDmitry Safonov } else if (family == AF_INET6) { 573ba7783adSDmitry Safonov if (tcp_v6_ao_hash_pseudoheader(&hp, &daddr->a6, 574ba7783adSDmitry Safonov &saddr->a6, th->doff * 4)) 575ba7783adSDmitry Safonov goto clear_hash; 576ba7783adSDmitry Safonov #endif 577ba7783adSDmitry Safonov } else { 578ba7783adSDmitry Safonov WARN_ON_ONCE(1); 579ba7783adSDmitry Safonov goto clear_hash; 580ba7783adSDmitry Safonov } 5817753c2f0SDmitry Safonov if (tcp_ao_hash_header(&hp, th, 5827753c2f0SDmitry Safonov !!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT), 583ba7783adSDmitry Safonov ao_hash, hash_offset, tcp_ao_maclen(key))) 584ba7783adSDmitry Safonov goto clear_hash; 585ba7783adSDmitry Safonov ahash_request_set_crypt(hp.req, NULL, hash_buf, 0); 586ba7783adSDmitry Safonov if (crypto_ahash_final(hp.req)) 587ba7783adSDmitry Safonov goto clear_hash; 588ba7783adSDmitry Safonov 589ba7783adSDmitry Safonov memcpy(ao_hash, hash_buf, tcp_ao_maclen(key)); 590ba7783adSDmitry Safonov tcp_sigpool_end(&hp); 591ba7783adSDmitry Safonov kfree(hash_buf); 592ba7783adSDmitry Safonov return 0; 593ba7783adSDmitry Safonov 594ba7783adSDmitry Safonov clear_hash: 595ba7783adSDmitry Safonov tcp_sigpool_end(&hp); 596ba7783adSDmitry Safonov clear_hash_noput: 597ba7783adSDmitry Safonov memset(ao_hash, 0, tcp_ao_maclen(key)); 598ba7783adSDmitry Safonov kfree(hash_buf); 599ba7783adSDmitry Safonov return 1; 600ba7783adSDmitry Safonov } 601ba7783adSDmitry Safonov 6021e03d32bSDmitry Safonov int tcp_ao_hash_skb(unsigned short int family, 6031e03d32bSDmitry Safonov char *ao_hash, struct tcp_ao_key *key, 6041e03d32bSDmitry Safonov const struct sock *sk, const struct sk_buff *skb, 6051e03d32bSDmitry Safonov const u8 *tkey, int hash_offset, u32 sne) 6061e03d32bSDmitry Safonov { 6071e03d32bSDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 6081e03d32bSDmitry Safonov int tkey_len = tcp_ao_digest_size(key); 6091e03d32bSDmitry Safonov struct tcp_sigpool hp; 6101e03d32bSDmitry Safonov void *hash_buf = NULL; 6111e03d32bSDmitry Safonov 6121e03d32bSDmitry Safonov hash_buf = kmalloc(tkey_len, GFP_ATOMIC); 6131e03d32bSDmitry Safonov if (!hash_buf) 6141e03d32bSDmitry Safonov goto clear_hash_noput; 6151e03d32bSDmitry Safonov 6161e03d32bSDmitry Safonov if (tcp_sigpool_start(key->tcp_sigpool_id, &hp)) 6171e03d32bSDmitry Safonov goto clear_hash_noput; 6181e03d32bSDmitry Safonov 6191e03d32bSDmitry Safonov if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), tkey, tkey_len)) 6201e03d32bSDmitry Safonov goto clear_hash; 6211e03d32bSDmitry Safonov 6221e03d32bSDmitry Safonov /* For now use sha1 by default. Depends on alg in tcp_ao_key */ 6231e03d32bSDmitry Safonov if (crypto_ahash_init(hp.req)) 6241e03d32bSDmitry Safonov goto clear_hash; 6251e03d32bSDmitry Safonov 6261e03d32bSDmitry Safonov if (tcp_ao_hash_sne(&hp, sne)) 6271e03d32bSDmitry Safonov goto clear_hash; 6281e03d32bSDmitry Safonov if (tcp_ao_hash_pseudoheader(family, sk, skb, &hp, skb->len)) 6291e03d32bSDmitry Safonov goto clear_hash; 6307753c2f0SDmitry Safonov if (tcp_ao_hash_header(&hp, th, 6317753c2f0SDmitry Safonov !!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT), 6321e03d32bSDmitry Safonov ao_hash, hash_offset, tcp_ao_maclen(key))) 6331e03d32bSDmitry Safonov goto clear_hash; 6341e03d32bSDmitry Safonov if (tcp_sigpool_hash_skb_data(&hp, skb, th->doff << 2)) 6351e03d32bSDmitry Safonov goto clear_hash; 6361e03d32bSDmitry Safonov ahash_request_set_crypt(hp.req, NULL, hash_buf, 0); 6371e03d32bSDmitry Safonov if (crypto_ahash_final(hp.req)) 6381e03d32bSDmitry Safonov goto clear_hash; 6391e03d32bSDmitry Safonov 6401e03d32bSDmitry Safonov memcpy(ao_hash, hash_buf, tcp_ao_maclen(key)); 6411e03d32bSDmitry Safonov tcp_sigpool_end(&hp); 6421e03d32bSDmitry Safonov kfree(hash_buf); 6431e03d32bSDmitry Safonov return 0; 6441e03d32bSDmitry Safonov 6451e03d32bSDmitry Safonov clear_hash: 6461e03d32bSDmitry Safonov tcp_sigpool_end(&hp); 6471e03d32bSDmitry Safonov clear_hash_noput: 6481e03d32bSDmitry Safonov memset(ao_hash, 0, tcp_ao_maclen(key)); 6491e03d32bSDmitry Safonov kfree(hash_buf); 6501e03d32bSDmitry Safonov return 1; 6511e03d32bSDmitry Safonov } 6521e03d32bSDmitry Safonov 6531e03d32bSDmitry Safonov int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key, 6541e03d32bSDmitry Safonov const struct sock *sk, const struct sk_buff *skb, 6551e03d32bSDmitry Safonov const u8 *tkey, int hash_offset, u32 sne) 6561e03d32bSDmitry Safonov { 6571e03d32bSDmitry Safonov return tcp_ao_hash_skb(AF_INET, ao_hash, key, sk, skb, 6581e03d32bSDmitry Safonov tkey, hash_offset, sne); 6591e03d32bSDmitry Safonov } 6601e03d32bSDmitry Safonov 6619427c6aaSDmitry Safonov int tcp_v4_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key, 6629427c6aaSDmitry Safonov struct request_sock *req, const struct sk_buff *skb, 6639427c6aaSDmitry Safonov int hash_offset, u32 sne) 6649427c6aaSDmitry Safonov { 6659427c6aaSDmitry Safonov void *hash_buf = NULL; 6669427c6aaSDmitry Safonov int err; 6679427c6aaSDmitry Safonov 6689427c6aaSDmitry Safonov hash_buf = kmalloc(tcp_ao_digest_size(ao_key), GFP_ATOMIC); 6699427c6aaSDmitry Safonov if (!hash_buf) 6709427c6aaSDmitry Safonov return -ENOMEM; 6719427c6aaSDmitry Safonov 6729427c6aaSDmitry Safonov err = tcp_v4_ao_calc_key_rsk(ao_key, hash_buf, req); 6739427c6aaSDmitry Safonov if (err) 6749427c6aaSDmitry Safonov goto out; 6759427c6aaSDmitry Safonov 6769427c6aaSDmitry Safonov err = tcp_ao_hash_skb(AF_INET, ao_hash, ao_key, req_to_sk(req), skb, 6779427c6aaSDmitry Safonov hash_buf, hash_offset, sne); 6789427c6aaSDmitry Safonov out: 6799427c6aaSDmitry Safonov kfree(hash_buf); 6809427c6aaSDmitry Safonov return err; 6819427c6aaSDmitry Safonov } 6829427c6aaSDmitry Safonov 68306b22ef2SDmitry Safonov struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct sock *sk, 68406b22ef2SDmitry Safonov struct request_sock *req, 68506b22ef2SDmitry Safonov int sndid, int rcvid) 68606b22ef2SDmitry Safonov { 687248411b8SDmitry Safonov struct inet_request_sock *ireq = inet_rsk(req); 688248411b8SDmitry Safonov union tcp_ao_addr *addr = (union tcp_ao_addr *)&ireq->ir_rmt_addr; 689248411b8SDmitry Safonov int l3index; 69006b22ef2SDmitry Safonov 691248411b8SDmitry Safonov l3index = l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif); 692248411b8SDmitry Safonov return tcp_ao_do_lookup(sk, l3index, addr, AF_INET, sndid, rcvid); 69306b22ef2SDmitry Safonov } 69406b22ef2SDmitry Safonov 6950aadc739SDmitry Safonov struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, 6960aadc739SDmitry Safonov int sndid, int rcvid) 6970aadc739SDmitry Safonov { 698248411b8SDmitry Safonov int l3index = l3mdev_master_ifindex_by_index(sock_net(sk), 699248411b8SDmitry Safonov addr_sk->sk_bound_dev_if); 7000aadc739SDmitry Safonov union tcp_ao_addr *addr = (union tcp_ao_addr *)&addr_sk->sk_daddr; 7010aadc739SDmitry Safonov 702248411b8SDmitry Safonov return tcp_ao_do_lookup(sk, l3index, addr, AF_INET, sndid, rcvid); 7030aadc739SDmitry Safonov } 7040aadc739SDmitry Safonov 705ba7783adSDmitry Safonov int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb, 70664382c71SDmitry Safonov const struct tcp_ao_hdr *aoh, int l3index, u32 seq, 707ba7783adSDmitry Safonov struct tcp_ao_key **key, char **traffic_key, 708ba7783adSDmitry Safonov bool *allocated_traffic_key, u8 *keyid, u32 *sne) 709ba7783adSDmitry Safonov { 71006b22ef2SDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 711ba7783adSDmitry Safonov struct tcp_ao_info *ao_info; 712ba7783adSDmitry Safonov 713ba7783adSDmitry Safonov *allocated_traffic_key = false; 714ba7783adSDmitry Safonov /* If there's no socket - than initial sisn/disn are unknown. 715ba7783adSDmitry Safonov * Drop the segment. RFC5925 (7.7) advises to require graceful 716ba7783adSDmitry Safonov * restart [RFC4724]. Alternatively, the RFC5925 advises to 717ba7783adSDmitry Safonov * save/restore traffic keys before/after reboot. 718ba7783adSDmitry Safonov * Linux TCP-AO support provides TCP_AO_ADD_KEY and TCP_AO_REPAIR 719ba7783adSDmitry Safonov * options to restore a socket post-reboot. 720ba7783adSDmitry Safonov */ 721ba7783adSDmitry Safonov if (!sk) 722ba7783adSDmitry Safonov return -ENOTCONN; 723ba7783adSDmitry Safonov 724decde258SDmitry Safonov if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) { 72506b22ef2SDmitry Safonov unsigned int family = READ_ONCE(sk->sk_family); 72606b22ef2SDmitry Safonov union tcp_ao_addr *addr; 72706b22ef2SDmitry Safonov __be32 disn, sisn; 72806b22ef2SDmitry Safonov 72906b22ef2SDmitry Safonov if (sk->sk_state == TCP_NEW_SYN_RECV) { 73006b22ef2SDmitry Safonov struct request_sock *req = inet_reqsk(sk); 73106b22ef2SDmitry Safonov 73206b22ef2SDmitry Safonov sisn = htonl(tcp_rsk(req)->rcv_isn); 73306b22ef2SDmitry Safonov disn = htonl(tcp_rsk(req)->snt_isn); 73464382c71SDmitry Safonov *sne = tcp_ao_compute_sne(0, tcp_rsk(req)->snt_isn, seq); 73506b22ef2SDmitry Safonov } else { 73606b22ef2SDmitry Safonov sisn = th->seq; 73706b22ef2SDmitry Safonov disn = 0; 73806b22ef2SDmitry Safonov } 73906b22ef2SDmitry Safonov if (IS_ENABLED(CONFIG_IPV6) && family == AF_INET6) 74006b22ef2SDmitry Safonov addr = (union tcp_md5_addr *)&ipv6_hdr(skb)->saddr; 74106b22ef2SDmitry Safonov else 74206b22ef2SDmitry Safonov addr = (union tcp_md5_addr *)&ip_hdr(skb)->saddr; 74306b22ef2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 74406b22ef2SDmitry Safonov if (family == AF_INET6 && ipv6_addr_v4mapped(&sk->sk_v6_daddr)) 74506b22ef2SDmitry Safonov family = AF_INET; 74606b22ef2SDmitry Safonov #endif 74706b22ef2SDmitry Safonov 74806b22ef2SDmitry Safonov sk = sk_const_to_full_sk(sk); 74906b22ef2SDmitry Safonov ao_info = rcu_dereference(tcp_sk(sk)->ao_info); 75006b22ef2SDmitry Safonov if (!ao_info) 75106b22ef2SDmitry Safonov return -ENOENT; 752248411b8SDmitry Safonov *key = tcp_ao_do_lookup(sk, l3index, addr, family, 753248411b8SDmitry Safonov -1, aoh->rnext_keyid); 75406b22ef2SDmitry Safonov if (!*key) 75506b22ef2SDmitry Safonov return -ENOENT; 75606b22ef2SDmitry Safonov *traffic_key = kmalloc(tcp_ao_digest_size(*key), GFP_ATOMIC); 75706b22ef2SDmitry Safonov if (!*traffic_key) 75806b22ef2SDmitry Safonov return -ENOMEM; 75906b22ef2SDmitry Safonov *allocated_traffic_key = true; 76006b22ef2SDmitry Safonov if (tcp_ao_calc_key_skb(*key, *traffic_key, skb, 76106b22ef2SDmitry Safonov sisn, disn, family)) 762ba7783adSDmitry Safonov return -1; 76306b22ef2SDmitry Safonov *keyid = (*key)->rcvid; 76406b22ef2SDmitry Safonov } else { 76506b22ef2SDmitry Safonov struct tcp_ao_key *rnext_key; 76664382c71SDmitry Safonov u32 snd_basis; 767ba7783adSDmitry Safonov 76864382c71SDmitry Safonov if (sk->sk_state == TCP_TIME_WAIT) { 769decde258SDmitry Safonov ao_info = rcu_dereference(tcp_twsk(sk)->ao_info); 77064382c71SDmitry Safonov snd_basis = tcp_twsk(sk)->tw_snd_nxt; 77164382c71SDmitry Safonov } else { 772ba7783adSDmitry Safonov ao_info = rcu_dereference(tcp_sk(sk)->ao_info); 77364382c71SDmitry Safonov snd_basis = tcp_sk(sk)->snd_una; 77464382c71SDmitry Safonov } 775ba7783adSDmitry Safonov if (!ao_info) 776ba7783adSDmitry Safonov return -ENOENT; 777ba7783adSDmitry Safonov 778ba7783adSDmitry Safonov *key = tcp_ao_established_key(ao_info, aoh->rnext_keyid, -1); 779ba7783adSDmitry Safonov if (!*key) 780ba7783adSDmitry Safonov return -ENOENT; 781ba7783adSDmitry Safonov *traffic_key = snd_other_key(*key); 782ba7783adSDmitry Safonov rnext_key = READ_ONCE(ao_info->rnext_key); 783ba7783adSDmitry Safonov *keyid = rnext_key->rcvid; 78464382c71SDmitry Safonov *sne = tcp_ao_compute_sne(READ_ONCE(ao_info->snd_sne), 78564382c71SDmitry Safonov snd_basis, seq); 78606b22ef2SDmitry Safonov } 787ba7783adSDmitry Safonov return 0; 788ba7783adSDmitry Safonov } 789ba7783adSDmitry Safonov 7901e03d32bSDmitry Safonov int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb, 7911e03d32bSDmitry Safonov struct tcp_ao_key *key, struct tcphdr *th, 7921e03d32bSDmitry Safonov __u8 *hash_location) 7931e03d32bSDmitry Safonov { 7941e03d32bSDmitry Safonov struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); 7951e03d32bSDmitry Safonov struct tcp_sock *tp = tcp_sk(sk); 7961e03d32bSDmitry Safonov struct tcp_ao_info *ao; 7971e03d32bSDmitry Safonov void *tkey_buf = NULL; 7981e03d32bSDmitry Safonov u8 *traffic_key; 79964382c71SDmitry Safonov u32 sne; 8001e03d32bSDmitry Safonov 8011e03d32bSDmitry Safonov ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 8021e03d32bSDmitry Safonov lockdep_sock_is_held(sk)); 8031e03d32bSDmitry Safonov traffic_key = snd_other_key(key); 8041e03d32bSDmitry Safonov if (unlikely(tcb->tcp_flags & TCPHDR_SYN)) { 8051e03d32bSDmitry Safonov __be32 disn; 8061e03d32bSDmitry Safonov 8071e03d32bSDmitry Safonov if (!(tcb->tcp_flags & TCPHDR_ACK)) { 8081e03d32bSDmitry Safonov disn = 0; 8091e03d32bSDmitry Safonov tkey_buf = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC); 8101e03d32bSDmitry Safonov if (!tkey_buf) 8111e03d32bSDmitry Safonov return -ENOMEM; 8121e03d32bSDmitry Safonov traffic_key = tkey_buf; 8131e03d32bSDmitry Safonov } else { 8141e03d32bSDmitry Safonov disn = ao->risn; 8151e03d32bSDmitry Safonov } 8161e03d32bSDmitry Safonov tp->af_specific->ao_calc_key_sk(key, traffic_key, 8171e03d32bSDmitry Safonov sk, ao->lisn, disn, true); 8181e03d32bSDmitry Safonov } 81964382c71SDmitry Safonov sne = tcp_ao_compute_sne(READ_ONCE(ao->snd_sne), READ_ONCE(tp->snd_una), 82064382c71SDmitry Safonov ntohl(th->seq)); 8211e03d32bSDmitry Safonov tp->af_specific->calc_ao_hash(hash_location, key, sk, skb, traffic_key, 82264382c71SDmitry Safonov hash_location - (u8 *)th, sne); 8231e03d32bSDmitry Safonov kfree(tkey_buf); 8241e03d32bSDmitry Safonov return 0; 8251e03d32bSDmitry Safonov } 8261e03d32bSDmitry Safonov 82706b22ef2SDmitry Safonov static struct tcp_ao_key *tcp_ao_inbound_lookup(unsigned short int family, 82806b22ef2SDmitry Safonov const struct sock *sk, const struct sk_buff *skb, 829248411b8SDmitry Safonov int sndid, int rcvid, int l3index) 83006b22ef2SDmitry Safonov { 83106b22ef2SDmitry Safonov if (family == AF_INET) { 83206b22ef2SDmitry Safonov const struct iphdr *iph = ip_hdr(skb); 83306b22ef2SDmitry Safonov 834248411b8SDmitry Safonov return tcp_ao_do_lookup(sk, l3index, 835248411b8SDmitry Safonov (union tcp_ao_addr *)&iph->saddr, 83606b22ef2SDmitry Safonov AF_INET, sndid, rcvid); 83706b22ef2SDmitry Safonov } else { 83806b22ef2SDmitry Safonov const struct ipv6hdr *iph = ipv6_hdr(skb); 83906b22ef2SDmitry Safonov 840248411b8SDmitry Safonov return tcp_ao_do_lookup(sk, l3index, 841248411b8SDmitry Safonov (union tcp_ao_addr *)&iph->saddr, 84206b22ef2SDmitry Safonov AF_INET6, sndid, rcvid); 84306b22ef2SDmitry Safonov } 84406b22ef2SDmitry Safonov } 84506b22ef2SDmitry Safonov 84606b22ef2SDmitry Safonov void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, 8477b0f570fSKuniyuki Iwashima struct request_sock *req, unsigned short int family) 84806b22ef2SDmitry Safonov { 8497b0f570fSKuniyuki Iwashima struct tcp_request_sock *treq = tcp_rsk(req); 85006b22ef2SDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 85106b22ef2SDmitry Safonov const struct tcp_ao_hdr *aoh; 85206b22ef2SDmitry Safonov struct tcp_ao_key *key; 8537b0f570fSKuniyuki Iwashima int l3index; 8547b0f570fSKuniyuki Iwashima 8557b0f570fSKuniyuki Iwashima /* treq->af_specific is used to perform TCP_AO lookup 8567b0f570fSKuniyuki Iwashima * in tcp_create_openreq_child(). 8577b0f570fSKuniyuki Iwashima */ 8587b0f570fSKuniyuki Iwashima #if IS_ENABLED(CONFIG_IPV6) 8597b0f570fSKuniyuki Iwashima if (family == AF_INET6) 8607b0f570fSKuniyuki Iwashima treq->af_specific = &tcp_request_sock_ipv6_ops; 8617b0f570fSKuniyuki Iwashima else 8627b0f570fSKuniyuki Iwashima #endif 8637b0f570fSKuniyuki Iwashima treq->af_specific = &tcp_request_sock_ipv4_ops; 86406b22ef2SDmitry Safonov 8659396c4eeSDmitry Safonov treq->used_tcp_ao = false; 86606b22ef2SDmitry Safonov 86706b22ef2SDmitry Safonov if (tcp_parse_auth_options(th, NULL, &aoh) || !aoh) 86806b22ef2SDmitry Safonov return; 86906b22ef2SDmitry Safonov 8707b0f570fSKuniyuki Iwashima l3index = l3mdev_master_ifindex_by_index(sock_net(sk), inet_rsk(req)->ir_iif); 871248411b8SDmitry Safonov key = tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid, l3index); 87206b22ef2SDmitry Safonov if (!key) 87306b22ef2SDmitry Safonov /* Key not found, continue without TCP-AO */ 87406b22ef2SDmitry Safonov return; 87506b22ef2SDmitry Safonov 87606b22ef2SDmitry Safonov treq->ao_rcv_next = aoh->keyid; 87706b22ef2SDmitry Safonov treq->ao_keyid = aoh->rnext_keyid; 8789396c4eeSDmitry Safonov treq->used_tcp_ao = true; 87906b22ef2SDmitry Safonov } 88006b22ef2SDmitry Safonov 8810a3a8090SDmitry Safonov static enum skb_drop_reason 8820a3a8090SDmitry Safonov tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb, 8830a3a8090SDmitry Safonov unsigned short int family, struct tcp_ao_info *info, 8840a3a8090SDmitry Safonov const struct tcp_ao_hdr *aoh, struct tcp_ao_key *key, 885248411b8SDmitry Safonov u8 *traffic_key, u8 *phash, u32 sne, int l3index) 8860a3a8090SDmitry Safonov { 8870a3a8090SDmitry Safonov u8 maclen = aoh->length - sizeof(struct tcp_ao_hdr); 8880a3a8090SDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 8890a3a8090SDmitry Safonov void *hash_buf = NULL; 8900a3a8090SDmitry Safonov 891af09a341SDmitry Safonov if (maclen != tcp_ao_maclen(key)) { 892af09a341SDmitry Safonov NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD); 893af09a341SDmitry Safonov atomic64_inc(&info->counters.pkt_bad); 894af09a341SDmitry Safonov atomic64_inc(&key->pkt_bad); 8952717b5adSDmitry Safonov tcp_hash_fail("AO hash wrong length", family, skb, 896248411b8SDmitry Safonov "%u != %d L3index: %d", maclen, 897248411b8SDmitry Safonov tcp_ao_maclen(key), l3index); 8980a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_AOFAILURE; 899af09a341SDmitry Safonov } 9000a3a8090SDmitry Safonov 9010a3a8090SDmitry Safonov hash_buf = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC); 9020a3a8090SDmitry Safonov if (!hash_buf) 9030a3a8090SDmitry Safonov return SKB_DROP_REASON_NOT_SPECIFIED; 9040a3a8090SDmitry Safonov 9050a3a8090SDmitry Safonov /* XXX: make it per-AF callback? */ 9060a3a8090SDmitry Safonov tcp_ao_hash_skb(family, hash_buf, key, sk, skb, traffic_key, 9070a3a8090SDmitry Safonov (phash - (u8 *)th), sne); 9080a3a8090SDmitry Safonov if (memcmp(phash, hash_buf, maclen)) { 909af09a341SDmitry Safonov NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD); 910af09a341SDmitry Safonov atomic64_inc(&info->counters.pkt_bad); 911af09a341SDmitry Safonov atomic64_inc(&key->pkt_bad); 912248411b8SDmitry Safonov tcp_hash_fail("AO hash mismatch", family, skb, 913248411b8SDmitry Safonov "L3index: %d", l3index); 9140a3a8090SDmitry Safonov kfree(hash_buf); 9150a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_AOFAILURE; 9160a3a8090SDmitry Safonov } 917af09a341SDmitry Safonov NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOGOOD); 918af09a341SDmitry Safonov atomic64_inc(&info->counters.pkt_good); 919af09a341SDmitry Safonov atomic64_inc(&key->pkt_good); 9200a3a8090SDmitry Safonov kfree(hash_buf); 9210a3a8090SDmitry Safonov return SKB_NOT_DROPPED_YET; 9220a3a8090SDmitry Safonov } 9230a3a8090SDmitry Safonov 9240a3a8090SDmitry Safonov enum skb_drop_reason 9250a3a8090SDmitry Safonov tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, 9260a3a8090SDmitry Safonov unsigned short int family, const struct request_sock *req, 927248411b8SDmitry Safonov int l3index, const struct tcp_ao_hdr *aoh) 9280a3a8090SDmitry Safonov { 9290a3a8090SDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 9300a3a8090SDmitry Safonov u8 *phash = (u8 *)(aoh + 1); /* hash goes just after the header */ 9310a3a8090SDmitry Safonov struct tcp_ao_info *info; 9320a3a8090SDmitry Safonov enum skb_drop_reason ret; 9330a3a8090SDmitry Safonov struct tcp_ao_key *key; 9340a3a8090SDmitry Safonov __be32 sisn, disn; 9350a3a8090SDmitry Safonov u8 *traffic_key; 93633700a0cSDmitry Safonov int state; 9370a3a8090SDmitry Safonov u32 sne = 0; 9380a3a8090SDmitry Safonov 9390a3a8090SDmitry Safonov info = rcu_dereference(tcp_sk(sk)->ao_info); 940af09a341SDmitry Safonov if (!info) { 941af09a341SDmitry Safonov NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND); 9422717b5adSDmitry Safonov tcp_hash_fail("AO key not found", family, skb, 943248411b8SDmitry Safonov "keyid: %u L3index: %d", aoh->keyid, l3index); 9440a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_AOUNEXPECTED; 945af09a341SDmitry Safonov } 9460a3a8090SDmitry Safonov 9470a3a8090SDmitry Safonov if (unlikely(th->syn)) { 9480a3a8090SDmitry Safonov sisn = th->seq; 9490a3a8090SDmitry Safonov disn = 0; 9500a3a8090SDmitry Safonov } 9510a3a8090SDmitry Safonov 95233700a0cSDmitry Safonov state = READ_ONCE(sk->sk_state); 9530a3a8090SDmitry Safonov /* Fast-path */ 95433700a0cSDmitry Safonov if (likely((1 << state) & TCP_AO_ESTABLISHED)) { 9550a3a8090SDmitry Safonov enum skb_drop_reason err; 9560a3a8090SDmitry Safonov struct tcp_ao_key *current_key; 9570a3a8090SDmitry Safonov 9580a3a8090SDmitry Safonov /* Check if this socket's rnext_key matches the keyid in the 9590a3a8090SDmitry Safonov * packet. If not we lookup the key based on the keyid 9600a3a8090SDmitry Safonov * matching the rcvid in the mkt. 9610a3a8090SDmitry Safonov */ 9620a3a8090SDmitry Safonov key = READ_ONCE(info->rnext_key); 9630a3a8090SDmitry Safonov if (key->rcvid != aoh->keyid) { 9640a3a8090SDmitry Safonov key = tcp_ao_established_key(info, -1, aoh->keyid); 9650a3a8090SDmitry Safonov if (!key) 9660a3a8090SDmitry Safonov goto key_not_found; 9670a3a8090SDmitry Safonov } 9680a3a8090SDmitry Safonov 9690a3a8090SDmitry Safonov /* Delayed retransmitted SYN */ 9700a3a8090SDmitry Safonov if (unlikely(th->syn && !th->ack)) 9710a3a8090SDmitry Safonov goto verify_hash; 9720a3a8090SDmitry Safonov 97364382c71SDmitry Safonov sne = tcp_ao_compute_sne(info->rcv_sne, tcp_sk(sk)->rcv_nxt, 97464382c71SDmitry Safonov ntohl(th->seq)); 9750a3a8090SDmitry Safonov /* Established socket, traffic key are cached */ 9760a3a8090SDmitry Safonov traffic_key = rcv_other_key(key); 9770a3a8090SDmitry Safonov err = tcp_ao_verify_hash(sk, skb, family, info, aoh, key, 978248411b8SDmitry Safonov traffic_key, phash, sne, l3index); 9790a3a8090SDmitry Safonov if (err) 9800a3a8090SDmitry Safonov return err; 9810a3a8090SDmitry Safonov current_key = READ_ONCE(info->current_key); 9820a3a8090SDmitry Safonov /* Key rotation: the peer asks us to use new key (RNext) */ 9830a3a8090SDmitry Safonov if (unlikely(aoh->rnext_keyid != current_key->sndid)) { 9840a3a8090SDmitry Safonov /* If the key is not found we do nothing. */ 9850a3a8090SDmitry Safonov key = tcp_ao_established_key(info, aoh->rnext_keyid, -1); 9860a3a8090SDmitry Safonov if (key) 9870a3a8090SDmitry Safonov /* pairs with tcp_ao_del_cmd */ 9880a3a8090SDmitry Safonov WRITE_ONCE(info->current_key, key); 9890a3a8090SDmitry Safonov } 9900a3a8090SDmitry Safonov return SKB_NOT_DROPPED_YET; 9910a3a8090SDmitry Safonov } 9920a3a8090SDmitry Safonov 99333700a0cSDmitry Safonov if (unlikely(state == TCP_CLOSE)) 99433700a0cSDmitry Safonov return SKB_DROP_REASON_TCP_CLOSE; 99533700a0cSDmitry Safonov 9960a3a8090SDmitry Safonov /* Lookup key based on peer address and keyid. 9970a3a8090SDmitry Safonov * current_key and rnext_key must not be used on tcp listen 9980a3a8090SDmitry Safonov * sockets as otherwise: 9990a3a8090SDmitry Safonov * - request sockets would race on those key pointers 10000a3a8090SDmitry Safonov * - tcp_ao_del_cmd() allows async key removal 10010a3a8090SDmitry Safonov */ 1002248411b8SDmitry Safonov key = tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid, l3index); 10030a3a8090SDmitry Safonov if (!key) 10040a3a8090SDmitry Safonov goto key_not_found; 10050a3a8090SDmitry Safonov 10060a3a8090SDmitry Safonov if (th->syn && !th->ack) 10070a3a8090SDmitry Safonov goto verify_hash; 10080a3a8090SDmitry Safonov 100933700a0cSDmitry Safonov if ((1 << state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) { 10100a3a8090SDmitry Safonov /* Make the initial syn the likely case here */ 10110a3a8090SDmitry Safonov if (unlikely(req)) { 101264382c71SDmitry Safonov sne = tcp_ao_compute_sne(0, tcp_rsk(req)->rcv_isn, 101364382c71SDmitry Safonov ntohl(th->seq)); 10140a3a8090SDmitry Safonov sisn = htonl(tcp_rsk(req)->rcv_isn); 10150a3a8090SDmitry Safonov disn = htonl(tcp_rsk(req)->snt_isn); 10160a3a8090SDmitry Safonov } else if (unlikely(th->ack && !th->syn)) { 10170a3a8090SDmitry Safonov /* Possible syncookie packet */ 10180a3a8090SDmitry Safonov sisn = htonl(ntohl(th->seq) - 1); 10190a3a8090SDmitry Safonov disn = htonl(ntohl(th->ack_seq) - 1); 102064382c71SDmitry Safonov sne = tcp_ao_compute_sne(0, ntohl(sisn), 102164382c71SDmitry Safonov ntohl(th->seq)); 10220a3a8090SDmitry Safonov } else if (unlikely(!th->syn)) { 10230a3a8090SDmitry Safonov /* no way to figure out initial sisn/disn - drop */ 10240a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_FLAGS; 10250a3a8090SDmitry Safonov } 102633700a0cSDmitry Safonov } else if ((1 << state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) { 10270a3a8090SDmitry Safonov disn = info->lisn; 10280a3a8090SDmitry Safonov if (th->syn || th->rst) 10290a3a8090SDmitry Safonov sisn = th->seq; 10300a3a8090SDmitry Safonov else 10310a3a8090SDmitry Safonov sisn = info->risn; 10320a3a8090SDmitry Safonov } else { 103333700a0cSDmitry Safonov WARN_ONCE(1, "TCP-AO: Unexpected sk_state %d", state); 10340a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_AOFAILURE; 10350a3a8090SDmitry Safonov } 10360a3a8090SDmitry Safonov verify_hash: 10370a3a8090SDmitry Safonov traffic_key = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC); 10380a3a8090SDmitry Safonov if (!traffic_key) 10390a3a8090SDmitry Safonov return SKB_DROP_REASON_NOT_SPECIFIED; 10400a3a8090SDmitry Safonov tcp_ao_calc_key_skb(key, traffic_key, skb, sisn, disn, family); 10410a3a8090SDmitry Safonov ret = tcp_ao_verify_hash(sk, skb, family, info, aoh, key, 1042248411b8SDmitry Safonov traffic_key, phash, sne, l3index); 10430a3a8090SDmitry Safonov kfree(traffic_key); 10440a3a8090SDmitry Safonov return ret; 10450a3a8090SDmitry Safonov 10460a3a8090SDmitry Safonov key_not_found: 1047af09a341SDmitry Safonov NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND); 1048af09a341SDmitry Safonov atomic64_inc(&info->counters.key_not_found); 10492717b5adSDmitry Safonov tcp_hash_fail("Requested by the peer AO key id not found", 1050248411b8SDmitry Safonov family, skb, "L3index: %d", l3index); 10510a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_AOKEYNOTFOUND; 10520a3a8090SDmitry Safonov } 10530a3a8090SDmitry Safonov 10547c2ffaf2SDmitry Safonov static int tcp_ao_cache_traffic_keys(const struct sock *sk, 10557c2ffaf2SDmitry Safonov struct tcp_ao_info *ao, 10567c2ffaf2SDmitry Safonov struct tcp_ao_key *ao_key) 10577c2ffaf2SDmitry Safonov { 10587c2ffaf2SDmitry Safonov u8 *traffic_key = snd_other_key(ao_key); 10597c2ffaf2SDmitry Safonov int ret; 10607c2ffaf2SDmitry Safonov 10617c2ffaf2SDmitry Safonov ret = tcp_ao_calc_key_sk(ao_key, traffic_key, sk, 10627c2ffaf2SDmitry Safonov ao->lisn, ao->risn, true); 10637c2ffaf2SDmitry Safonov if (ret) 10647c2ffaf2SDmitry Safonov return ret; 10657c2ffaf2SDmitry Safonov 10667c2ffaf2SDmitry Safonov traffic_key = rcv_other_key(ao_key); 10677c2ffaf2SDmitry Safonov ret = tcp_ao_calc_key_sk(ao_key, traffic_key, sk, 10687c2ffaf2SDmitry Safonov ao->lisn, ao->risn, false); 10697c2ffaf2SDmitry Safonov return ret; 10707c2ffaf2SDmitry Safonov } 10717c2ffaf2SDmitry Safonov 10727c2ffaf2SDmitry Safonov void tcp_ao_connect_init(struct sock *sk) 10737c2ffaf2SDmitry Safonov { 10747c2ffaf2SDmitry Safonov struct tcp_sock *tp = tcp_sk(sk); 10757c2ffaf2SDmitry Safonov struct tcp_ao_info *ao_info; 107680e679b3SHyunwoo Kim struct hlist_node *next; 10777c2ffaf2SDmitry Safonov union tcp_ao_addr *addr; 10787c2ffaf2SDmitry Safonov struct tcp_ao_key *key; 1079248411b8SDmitry Safonov int family, l3index; 10807c2ffaf2SDmitry Safonov 10817c2ffaf2SDmitry Safonov ao_info = rcu_dereference_protected(tp->ao_info, 10827c2ffaf2SDmitry Safonov lockdep_sock_is_held(sk)); 10837c2ffaf2SDmitry Safonov if (!ao_info) 10847c2ffaf2SDmitry Safonov return; 10857c2ffaf2SDmitry Safonov 10867c2ffaf2SDmitry Safonov /* Remove all keys that don't match the peer */ 10877c2ffaf2SDmitry Safonov family = sk->sk_family; 10887c2ffaf2SDmitry Safonov if (family == AF_INET) 10897c2ffaf2SDmitry Safonov addr = (union tcp_ao_addr *)&sk->sk_daddr; 10907c2ffaf2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 10917c2ffaf2SDmitry Safonov else if (family == AF_INET6) 10927c2ffaf2SDmitry Safonov addr = (union tcp_ao_addr *)&sk->sk_v6_daddr; 10937c2ffaf2SDmitry Safonov #endif 10947c2ffaf2SDmitry Safonov else 10957c2ffaf2SDmitry Safonov return; 1096248411b8SDmitry Safonov l3index = l3mdev_master_ifindex_by_index(sock_net(sk), 1097248411b8SDmitry Safonov sk->sk_bound_dev_if); 10987c2ffaf2SDmitry Safonov 109980e679b3SHyunwoo Kim hlist_for_each_entry_safe(key, next, &ao_info->head, node) { 1100248411b8SDmitry Safonov if (!tcp_ao_key_cmp(key, l3index, addr, key->prefixlen, family, -1, -1)) 11017c2ffaf2SDmitry Safonov continue; 11027c2ffaf2SDmitry Safonov 11037c2ffaf2SDmitry Safonov if (key == ao_info->current_key) 11047c2ffaf2SDmitry Safonov ao_info->current_key = NULL; 11057c2ffaf2SDmitry Safonov if (key == ao_info->rnext_key) 11067c2ffaf2SDmitry Safonov ao_info->rnext_key = NULL; 11077c2ffaf2SDmitry Safonov hlist_del_rcu(&key->node); 11087c2ffaf2SDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 11097c2ffaf2SDmitry Safonov call_rcu(&key->rcu, tcp_ao_key_free_rcu); 11107c2ffaf2SDmitry Safonov } 11117c2ffaf2SDmitry Safonov 11127c2ffaf2SDmitry Safonov key = tp->af_specific->ao_lookup(sk, sk, -1, -1); 11137c2ffaf2SDmitry Safonov if (key) { 11147c2ffaf2SDmitry Safonov /* if current_key or rnext_key were not provided, 11157c2ffaf2SDmitry Safonov * use the first key matching the peer 11167c2ffaf2SDmitry Safonov */ 11177c2ffaf2SDmitry Safonov if (!ao_info->current_key) 11187c2ffaf2SDmitry Safonov ao_info->current_key = key; 11197c2ffaf2SDmitry Safonov if (!ao_info->rnext_key) 11207c2ffaf2SDmitry Safonov ao_info->rnext_key = key; 1121da7dfaa6SDmitry Safonov tp->tcp_header_len += tcp_ao_len_aligned(key); 11227c2ffaf2SDmitry Safonov 11237c2ffaf2SDmitry Safonov ao_info->lisn = htonl(tp->write_seq); 112464382c71SDmitry Safonov ao_info->snd_sne = 0; 11257c2ffaf2SDmitry Safonov } else { 11267c2ffaf2SDmitry Safonov /* Can't happen: tcp_connect() verifies that there's 11277c2ffaf2SDmitry Safonov * at least one tcp-ao key that matches the remote peer. 11287c2ffaf2SDmitry Safonov */ 11297c2ffaf2SDmitry Safonov WARN_ON_ONCE(1); 11307c2ffaf2SDmitry Safonov rcu_assign_pointer(tp->ao_info, NULL); 11317c2ffaf2SDmitry Safonov kfree(ao_info); 11327c2ffaf2SDmitry Safonov } 11337c2ffaf2SDmitry Safonov } 11347c2ffaf2SDmitry Safonov 11357c2ffaf2SDmitry Safonov void tcp_ao_established(struct sock *sk) 11367c2ffaf2SDmitry Safonov { 11377c2ffaf2SDmitry Safonov struct tcp_ao_info *ao; 11387c2ffaf2SDmitry Safonov struct tcp_ao_key *key; 11397c2ffaf2SDmitry Safonov 11407c2ffaf2SDmitry Safonov ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 11417c2ffaf2SDmitry Safonov lockdep_sock_is_held(sk)); 11427c2ffaf2SDmitry Safonov if (!ao) 11437c2ffaf2SDmitry Safonov return; 11447c2ffaf2SDmitry Safonov 11457c2ffaf2SDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) 11467c2ffaf2SDmitry Safonov tcp_ao_cache_traffic_keys(sk, ao, key); 11477c2ffaf2SDmitry Safonov } 11487c2ffaf2SDmitry Safonov 11497c2ffaf2SDmitry Safonov void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb) 11507c2ffaf2SDmitry Safonov { 11517c2ffaf2SDmitry Safonov struct tcp_ao_info *ao; 11527c2ffaf2SDmitry Safonov struct tcp_ao_key *key; 11537c2ffaf2SDmitry Safonov 11547c2ffaf2SDmitry Safonov ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 11557c2ffaf2SDmitry Safonov lockdep_sock_is_held(sk)); 11567c2ffaf2SDmitry Safonov if (!ao) 11577c2ffaf2SDmitry Safonov return; 11587c2ffaf2SDmitry Safonov 11597c2ffaf2SDmitry Safonov WRITE_ONCE(ao->risn, tcp_hdr(skb)->seq); 116064382c71SDmitry Safonov ao->rcv_sne = 0; 11617c2ffaf2SDmitry Safonov 11627c2ffaf2SDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) 11637c2ffaf2SDmitry Safonov tcp_ao_cache_traffic_keys(sk, ao, key); 11647c2ffaf2SDmitry Safonov } 11657c2ffaf2SDmitry Safonov 116606b22ef2SDmitry Safonov int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk, 116706b22ef2SDmitry Safonov struct request_sock *req, struct sk_buff *skb, 116806b22ef2SDmitry Safonov int family) 116906b22ef2SDmitry Safonov { 117006b22ef2SDmitry Safonov struct tcp_ao_key *key, *new_key, *first_key; 117106b22ef2SDmitry Safonov struct tcp_ao_info *new_ao, *ao; 117206b22ef2SDmitry Safonov struct hlist_node *key_head; 1173248411b8SDmitry Safonov int l3index, ret = -ENOMEM; 117406b22ef2SDmitry Safonov union tcp_ao_addr *addr; 117506b22ef2SDmitry Safonov bool match = false; 117606b22ef2SDmitry Safonov 117706b22ef2SDmitry Safonov ao = rcu_dereference(tcp_sk(sk)->ao_info); 117806b22ef2SDmitry Safonov if (!ao) 117906b22ef2SDmitry Safonov return 0; 118006b22ef2SDmitry Safonov 118106b22ef2SDmitry Safonov /* New socket without TCP-AO on it */ 118206b22ef2SDmitry Safonov if (!tcp_rsk_used_ao(req)) 118306b22ef2SDmitry Safonov return 0; 118406b22ef2SDmitry Safonov 118506b22ef2SDmitry Safonov new_ao = tcp_ao_alloc_info(GFP_ATOMIC); 118606b22ef2SDmitry Safonov if (!new_ao) 118706b22ef2SDmitry Safonov return -ENOMEM; 118806b22ef2SDmitry Safonov new_ao->lisn = htonl(tcp_rsk(req)->snt_isn); 118906b22ef2SDmitry Safonov new_ao->risn = htonl(tcp_rsk(req)->rcv_isn); 119006b22ef2SDmitry Safonov new_ao->ao_required = ao->ao_required; 1191953af8e3SDmitry Safonov new_ao->accept_icmps = ao->accept_icmps; 119206b22ef2SDmitry Safonov 119306b22ef2SDmitry Safonov if (family == AF_INET) { 119406b22ef2SDmitry Safonov addr = (union tcp_ao_addr *)&newsk->sk_daddr; 119506b22ef2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 119606b22ef2SDmitry Safonov } else if (family == AF_INET6) { 119706b22ef2SDmitry Safonov addr = (union tcp_ao_addr *)&newsk->sk_v6_daddr; 119806b22ef2SDmitry Safonov #endif 119906b22ef2SDmitry Safonov } else { 120006b22ef2SDmitry Safonov ret = -EAFNOSUPPORT; 120106b22ef2SDmitry Safonov goto free_ao; 120206b22ef2SDmitry Safonov } 1203248411b8SDmitry Safonov l3index = l3mdev_master_ifindex_by_index(sock_net(newsk), 1204248411b8SDmitry Safonov newsk->sk_bound_dev_if); 120506b22ef2SDmitry Safonov 120606b22ef2SDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) { 1207248411b8SDmitry Safonov if (tcp_ao_key_cmp(key, l3index, addr, key->prefixlen, family, -1, -1)) 120806b22ef2SDmitry Safonov continue; 120906b22ef2SDmitry Safonov 121006b22ef2SDmitry Safonov new_key = tcp_ao_copy_key(newsk, key); 121106b22ef2SDmitry Safonov if (!new_key) 121206b22ef2SDmitry Safonov goto free_and_exit; 121306b22ef2SDmitry Safonov 121406b22ef2SDmitry Safonov tcp_ao_cache_traffic_keys(newsk, new_ao, new_key); 121506b22ef2SDmitry Safonov tcp_ao_link_mkt(new_ao, new_key); 121606b22ef2SDmitry Safonov match = true; 121706b22ef2SDmitry Safonov } 121806b22ef2SDmitry Safonov 121906b22ef2SDmitry Safonov if (!match) { 122006b22ef2SDmitry Safonov /* RFC5925 (7.4.1) specifies that the TCP-AO status 122106b22ef2SDmitry Safonov * of a connection is determined on the initial SYN. 122206b22ef2SDmitry Safonov * At this point the connection was TCP-AO enabled, so 122306b22ef2SDmitry Safonov * it can't switch to being unsigned if peer's key 122406b22ef2SDmitry Safonov * disappears on the listening socket. 122506b22ef2SDmitry Safonov */ 122606b22ef2SDmitry Safonov ret = -EKEYREJECTED; 122706b22ef2SDmitry Safonov goto free_and_exit; 122806b22ef2SDmitry Safonov } 122906b22ef2SDmitry Safonov 123067fa83f7SDmitry Safonov if (!static_key_fast_inc_not_disabled(&tcp_ao_needed.key.key)) { 123167fa83f7SDmitry Safonov ret = -EUSERS; 123267fa83f7SDmitry Safonov goto free_and_exit; 123367fa83f7SDmitry Safonov } 123467fa83f7SDmitry Safonov 123506b22ef2SDmitry Safonov key_head = rcu_dereference(hlist_first_rcu(&new_ao->head)); 123606b22ef2SDmitry Safonov first_key = hlist_entry_safe(key_head, struct tcp_ao_key, node); 123706b22ef2SDmitry Safonov 123806b22ef2SDmitry Safonov key = tcp_ao_established_key(new_ao, tcp_rsk(req)->ao_keyid, -1); 123906b22ef2SDmitry Safonov if (key) 124006b22ef2SDmitry Safonov new_ao->current_key = key; 124106b22ef2SDmitry Safonov else 124206b22ef2SDmitry Safonov new_ao->current_key = first_key; 124306b22ef2SDmitry Safonov 124406b22ef2SDmitry Safonov /* set rnext_key */ 124506b22ef2SDmitry Safonov key = tcp_ao_established_key(new_ao, -1, tcp_rsk(req)->ao_rcv_next); 124606b22ef2SDmitry Safonov if (key) 124706b22ef2SDmitry Safonov new_ao->rnext_key = key; 124806b22ef2SDmitry Safonov else 124906b22ef2SDmitry Safonov new_ao->rnext_key = first_key; 125006b22ef2SDmitry Safonov 125106b22ef2SDmitry Safonov sk_gso_disable(newsk); 125206b22ef2SDmitry Safonov rcu_assign_pointer(tcp_sk(newsk)->ao_info, new_ao); 125306b22ef2SDmitry Safonov 125406b22ef2SDmitry Safonov return 0; 125506b22ef2SDmitry Safonov 125606b22ef2SDmitry Safonov free_and_exit: 125706b22ef2SDmitry Safonov hlist_for_each_entry_safe(key, key_head, &new_ao->head, node) { 125806b22ef2SDmitry Safonov hlist_del(&key->node); 125906b22ef2SDmitry Safonov tcp_sigpool_release(key->tcp_sigpool_id); 126006b22ef2SDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &newsk->sk_omem_alloc); 126106b22ef2SDmitry Safonov kfree_sensitive(key); 126206b22ef2SDmitry Safonov } 126306b22ef2SDmitry Safonov free_ao: 126406b22ef2SDmitry Safonov kfree(new_ao); 126506b22ef2SDmitry Safonov return ret; 126606b22ef2SDmitry Safonov } 126706b22ef2SDmitry Safonov 12684954f17dSDmitry Safonov static bool tcp_ao_can_set_current_rnext(struct sock *sk) 12694954f17dSDmitry Safonov { 12704954f17dSDmitry Safonov /* There aren't current/rnext keys on TCP_LISTEN sockets */ 12714954f17dSDmitry Safonov if (sk->sk_state == TCP_LISTEN) 12724954f17dSDmitry Safonov return false; 12734954f17dSDmitry Safonov return true; 12744954f17dSDmitry Safonov } 12754954f17dSDmitry Safonov 12764954f17dSDmitry Safonov static int tcp_ao_verify_ipv4(struct sock *sk, struct tcp_ao_add *cmd, 12774954f17dSDmitry Safonov union tcp_ao_addr **addr) 12784954f17dSDmitry Safonov { 12794954f17dSDmitry Safonov struct sockaddr_in *sin = (struct sockaddr_in *)&cmd->addr; 12804954f17dSDmitry Safonov struct inet_sock *inet = inet_sk(sk); 12814954f17dSDmitry Safonov 12824954f17dSDmitry Safonov if (sin->sin_family != AF_INET) 12834954f17dSDmitry Safonov return -EINVAL; 12844954f17dSDmitry Safonov 12854954f17dSDmitry Safonov /* Currently matching is not performed on port (or port ranges) */ 12864954f17dSDmitry Safonov if (sin->sin_port != 0) 12874954f17dSDmitry Safonov return -EINVAL; 12884954f17dSDmitry Safonov 12894954f17dSDmitry Safonov /* Check prefix and trailing 0's in addr */ 12904954f17dSDmitry Safonov if (cmd->prefix != 0) { 12914954f17dSDmitry Safonov __be32 mask; 12924954f17dSDmitry Safonov 12934954f17dSDmitry Safonov if (ntohl(sin->sin_addr.s_addr) == INADDR_ANY) 12944954f17dSDmitry Safonov return -EINVAL; 12954954f17dSDmitry Safonov if (cmd->prefix > 32) 12964954f17dSDmitry Safonov return -EINVAL; 12974954f17dSDmitry Safonov 12984954f17dSDmitry Safonov mask = inet_make_mask(cmd->prefix); 12994954f17dSDmitry Safonov if (sin->sin_addr.s_addr & ~mask) 13004954f17dSDmitry Safonov return -EINVAL; 13014954f17dSDmitry Safonov 13024954f17dSDmitry Safonov /* Check that MKT address is consistent with socket */ 13034954f17dSDmitry Safonov if (ntohl(inet->inet_daddr) != INADDR_ANY && 13044954f17dSDmitry Safonov (inet->inet_daddr & mask) != sin->sin_addr.s_addr) 13054954f17dSDmitry Safonov return -EINVAL; 13064954f17dSDmitry Safonov } else { 13074954f17dSDmitry Safonov if (ntohl(sin->sin_addr.s_addr) != INADDR_ANY) 13084954f17dSDmitry Safonov return -EINVAL; 13094954f17dSDmitry Safonov } 13104954f17dSDmitry Safonov 13114954f17dSDmitry Safonov *addr = (union tcp_ao_addr *)&sin->sin_addr; 13124954f17dSDmitry Safonov return 0; 13134954f17dSDmitry Safonov } 13144954f17dSDmitry Safonov 13154954f17dSDmitry Safonov static int tcp_ao_parse_crypto(struct tcp_ao_add *cmd, struct tcp_ao_key *key) 13164954f17dSDmitry Safonov { 13174954f17dSDmitry Safonov unsigned int syn_tcp_option_space; 13184954f17dSDmitry Safonov bool is_kdf_aes_128_cmac = false; 13194954f17dSDmitry Safonov struct crypto_ahash *tfm; 13204954f17dSDmitry Safonov struct tcp_sigpool hp; 13214954f17dSDmitry Safonov void *tmp_key = NULL; 13224954f17dSDmitry Safonov int err; 13234954f17dSDmitry Safonov 13244954f17dSDmitry Safonov /* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */ 13254954f17dSDmitry Safonov if (!strcmp("cmac(aes128)", cmd->alg_name)) { 13264954f17dSDmitry Safonov strscpy(cmd->alg_name, "cmac(aes)", sizeof(cmd->alg_name)); 13274954f17dSDmitry Safonov is_kdf_aes_128_cmac = (cmd->keylen != 16); 13284954f17dSDmitry Safonov tmp_key = kmalloc(cmd->keylen, GFP_KERNEL); 13294954f17dSDmitry Safonov if (!tmp_key) 13304954f17dSDmitry Safonov return -ENOMEM; 13314954f17dSDmitry Safonov } 13324954f17dSDmitry Safonov 13334954f17dSDmitry Safonov key->maclen = cmd->maclen ?: 12; /* 12 is the default in RFC5925 */ 13344954f17dSDmitry Safonov 13354954f17dSDmitry Safonov /* Check: maclen + tcp-ao header <= (MAX_TCP_OPTION_SPACE - mss 13360a8e987dSKuniyuki Iwashima * - tstamp (including sackperm) 13370a8e987dSKuniyuki Iwashima * - wscale), 13384954f17dSDmitry Safonov * see tcp_syn_options(), tcp_synack_options(), commit 33ad798c924b. 13394954f17dSDmitry Safonov * 13404954f17dSDmitry Safonov * In order to allow D-SACK with TCP-AO, the header size should be: 13414954f17dSDmitry Safonov * (MAX_TCP_OPTION_SPACE - TCPOLEN_TSTAMP_ALIGNED 13424954f17dSDmitry Safonov * - TCPOLEN_SACK_BASE_ALIGNED 13434954f17dSDmitry Safonov * - 2 * TCPOLEN_SACK_PERBLOCK) = 8 (maclen = 4), 13444954f17dSDmitry Safonov * see tcp_established_options(). 13454954f17dSDmitry Safonov * 13464954f17dSDmitry Safonov * RFC5925, 2.2: 13474954f17dSDmitry Safonov * Typical MACs are 96-128 bits (12-16 bytes), but any length 13484954f17dSDmitry Safonov * that fits in the header of the segment being authenticated 13494954f17dSDmitry Safonov * is allowed. 13504954f17dSDmitry Safonov * 13514954f17dSDmitry Safonov * RFC5925, 7.6: 13524954f17dSDmitry Safonov * TCP-AO continues to consume 16 bytes in non-SYN segments, 13534954f17dSDmitry Safonov * leaving a total of 24 bytes for other options, of which 13544954f17dSDmitry Safonov * the timestamp consumes 10. This leaves 14 bytes, of which 10 13554954f17dSDmitry Safonov * are used for a single SACK block. When two SACK blocks are used, 13564954f17dSDmitry Safonov * such as to handle D-SACK, a smaller TCP-AO MAC would be required 13574954f17dSDmitry Safonov * to make room for the additional SACK block (i.e., to leave 18 13584954f17dSDmitry Safonov * bytes for the D-SACK variant of the SACK option) [RFC2883]. 13594954f17dSDmitry Safonov * Note that D-SACK is not supportable in TCP MD5 in the presence 13604954f17dSDmitry Safonov * of timestamps, because TCP MD5’s MAC length is fixed and too 13614954f17dSDmitry Safonov * large to leave sufficient option space. 13624954f17dSDmitry Safonov */ 13634954f17dSDmitry Safonov syn_tcp_option_space = MAX_TCP_OPTION_SPACE; 13640a8e987dSKuniyuki Iwashima syn_tcp_option_space -= TCPOLEN_MSS_ALIGNED; 13654954f17dSDmitry Safonov syn_tcp_option_space -= TCPOLEN_TSTAMP_ALIGNED; 13664954f17dSDmitry Safonov syn_tcp_option_space -= TCPOLEN_WSCALE_ALIGNED; 1367da7dfaa6SDmitry Safonov if (tcp_ao_len_aligned(key) > syn_tcp_option_space) { 13684954f17dSDmitry Safonov err = -EMSGSIZE; 13694954f17dSDmitry Safonov goto err_kfree; 13704954f17dSDmitry Safonov } 13714954f17dSDmitry Safonov 13724954f17dSDmitry Safonov key->keylen = cmd->keylen; 13734954f17dSDmitry Safonov memcpy(key->key, cmd->key, cmd->keylen); 13744954f17dSDmitry Safonov 13754954f17dSDmitry Safonov err = tcp_sigpool_start(key->tcp_sigpool_id, &hp); 13764954f17dSDmitry Safonov if (err) 13774954f17dSDmitry Safonov goto err_kfree; 13784954f17dSDmitry Safonov 13794954f17dSDmitry Safonov tfm = crypto_ahash_reqtfm(hp.req); 13804954f17dSDmitry Safonov if (is_kdf_aes_128_cmac) { 13814954f17dSDmitry Safonov void *scratch = hp.scratch; 13824954f17dSDmitry Safonov struct scatterlist sg; 13834954f17dSDmitry Safonov 13844954f17dSDmitry Safonov memcpy(tmp_key, cmd->key, cmd->keylen); 13854954f17dSDmitry Safonov sg_init_one(&sg, tmp_key, cmd->keylen); 13864954f17dSDmitry Safonov 13874954f17dSDmitry Safonov /* Using zero-key of 16 bytes as described in RFC5926 */ 13884954f17dSDmitry Safonov memset(scratch, 0, 16); 13894954f17dSDmitry Safonov err = crypto_ahash_setkey(tfm, scratch, 16); 13904954f17dSDmitry Safonov if (err) 13914954f17dSDmitry Safonov goto err_pool_end; 13924954f17dSDmitry Safonov 13934954f17dSDmitry Safonov err = crypto_ahash_init(hp.req); 13944954f17dSDmitry Safonov if (err) 13954954f17dSDmitry Safonov goto err_pool_end; 13964954f17dSDmitry Safonov 13974954f17dSDmitry Safonov ahash_request_set_crypt(hp.req, &sg, key->key, cmd->keylen); 13984954f17dSDmitry Safonov err = crypto_ahash_update(hp.req); 13994954f17dSDmitry Safonov if (err) 14004954f17dSDmitry Safonov goto err_pool_end; 14014954f17dSDmitry Safonov 14024954f17dSDmitry Safonov err |= crypto_ahash_final(hp.req); 14034954f17dSDmitry Safonov if (err) 14044954f17dSDmitry Safonov goto err_pool_end; 14054954f17dSDmitry Safonov key->keylen = 16; 14064954f17dSDmitry Safonov } 14074954f17dSDmitry Safonov 14084954f17dSDmitry Safonov err = crypto_ahash_setkey(tfm, key->key, key->keylen); 14094954f17dSDmitry Safonov if (err) 14104954f17dSDmitry Safonov goto err_pool_end; 14114954f17dSDmitry Safonov 14124954f17dSDmitry Safonov tcp_sigpool_end(&hp); 14134954f17dSDmitry Safonov kfree_sensitive(tmp_key); 14144954f17dSDmitry Safonov 14154954f17dSDmitry Safonov if (tcp_ao_maclen(key) > key->digest_size) 14164954f17dSDmitry Safonov return -EINVAL; 14174954f17dSDmitry Safonov 14184954f17dSDmitry Safonov return 0; 14194954f17dSDmitry Safonov 14204954f17dSDmitry Safonov err_pool_end: 14214954f17dSDmitry Safonov tcp_sigpool_end(&hp); 14224954f17dSDmitry Safonov err_kfree: 14234954f17dSDmitry Safonov kfree_sensitive(tmp_key); 14244954f17dSDmitry Safonov return err; 14254954f17dSDmitry Safonov } 14264954f17dSDmitry Safonov 14274954f17dSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 14284954f17dSDmitry Safonov static int tcp_ao_verify_ipv6(struct sock *sk, struct tcp_ao_add *cmd, 14294954f17dSDmitry Safonov union tcp_ao_addr **paddr, 14304954f17dSDmitry Safonov unsigned short int *family) 14314954f17dSDmitry Safonov { 14324954f17dSDmitry Safonov struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd->addr; 14334954f17dSDmitry Safonov struct in6_addr *addr = &sin6->sin6_addr; 14344954f17dSDmitry Safonov u8 prefix = cmd->prefix; 14354954f17dSDmitry Safonov 14364954f17dSDmitry Safonov if (sin6->sin6_family != AF_INET6) 14374954f17dSDmitry Safonov return -EINVAL; 14384954f17dSDmitry Safonov 14394954f17dSDmitry Safonov /* Currently matching is not performed on port (or port ranges) */ 14404954f17dSDmitry Safonov if (sin6->sin6_port != 0) 14414954f17dSDmitry Safonov return -EINVAL; 14424954f17dSDmitry Safonov 14434954f17dSDmitry Safonov /* Check prefix and trailing 0's in addr */ 14444954f17dSDmitry Safonov if (cmd->prefix != 0 && ipv6_addr_v4mapped(addr)) { 14454954f17dSDmitry Safonov __be32 addr4 = addr->s6_addr32[3]; 14464954f17dSDmitry Safonov __be32 mask; 14474954f17dSDmitry Safonov 14484954f17dSDmitry Safonov if (prefix > 32 || ntohl(addr4) == INADDR_ANY) 14494954f17dSDmitry Safonov return -EINVAL; 14504954f17dSDmitry Safonov 14514954f17dSDmitry Safonov mask = inet_make_mask(prefix); 14524954f17dSDmitry Safonov if (addr4 & ~mask) 14534954f17dSDmitry Safonov return -EINVAL; 14544954f17dSDmitry Safonov 14554954f17dSDmitry Safonov /* Check that MKT address is consistent with socket */ 14564954f17dSDmitry Safonov if (!ipv6_addr_any(&sk->sk_v6_daddr)) { 14574954f17dSDmitry Safonov __be32 daddr4 = sk->sk_v6_daddr.s6_addr32[3]; 14584954f17dSDmitry Safonov 14594954f17dSDmitry Safonov if (!ipv6_addr_v4mapped(&sk->sk_v6_daddr)) 14604954f17dSDmitry Safonov return -EINVAL; 14614954f17dSDmitry Safonov if ((daddr4 & mask) != addr4) 14624954f17dSDmitry Safonov return -EINVAL; 14634954f17dSDmitry Safonov } 14644954f17dSDmitry Safonov 14654954f17dSDmitry Safonov *paddr = (union tcp_ao_addr *)&addr->s6_addr32[3]; 14664954f17dSDmitry Safonov *family = AF_INET; 14674954f17dSDmitry Safonov return 0; 14684954f17dSDmitry Safonov } else if (cmd->prefix != 0) { 14694954f17dSDmitry Safonov struct in6_addr pfx; 14704954f17dSDmitry Safonov 14714954f17dSDmitry Safonov if (ipv6_addr_any(addr) || prefix > 128) 14724954f17dSDmitry Safonov return -EINVAL; 14734954f17dSDmitry Safonov 14744954f17dSDmitry Safonov ipv6_addr_prefix(&pfx, addr, prefix); 14754954f17dSDmitry Safonov if (ipv6_addr_cmp(&pfx, addr)) 14764954f17dSDmitry Safonov return -EINVAL; 14774954f17dSDmitry Safonov 14784954f17dSDmitry Safonov /* Check that MKT address is consistent with socket */ 14794954f17dSDmitry Safonov if (!ipv6_addr_any(&sk->sk_v6_daddr) && 14804954f17dSDmitry Safonov !ipv6_prefix_equal(&sk->sk_v6_daddr, addr, prefix)) 14814954f17dSDmitry Safonov 14824954f17dSDmitry Safonov return -EINVAL; 14834954f17dSDmitry Safonov } else { 14844954f17dSDmitry Safonov if (!ipv6_addr_any(addr)) 14854954f17dSDmitry Safonov return -EINVAL; 14864954f17dSDmitry Safonov } 14874954f17dSDmitry Safonov 14884954f17dSDmitry Safonov *paddr = (union tcp_ao_addr *)addr; 14894954f17dSDmitry Safonov return 0; 14904954f17dSDmitry Safonov } 14914954f17dSDmitry Safonov #else 14924954f17dSDmitry Safonov static int tcp_ao_verify_ipv6(struct sock *sk, struct tcp_ao_add *cmd, 14934954f17dSDmitry Safonov union tcp_ao_addr **paddr, 14944954f17dSDmitry Safonov unsigned short int *family) 14954954f17dSDmitry Safonov { 14964954f17dSDmitry Safonov return -EOPNOTSUPP; 14974954f17dSDmitry Safonov } 14984954f17dSDmitry Safonov #endif 14994954f17dSDmitry Safonov 15004954f17dSDmitry Safonov static struct tcp_ao_info *setsockopt_ao_info(struct sock *sk) 15014954f17dSDmitry Safonov { 15024954f17dSDmitry Safonov if (sk_fullsock(sk)) { 15034954f17dSDmitry Safonov return rcu_dereference_protected(tcp_sk(sk)->ao_info, 15044954f17dSDmitry Safonov lockdep_sock_is_held(sk)); 1505decde258SDmitry Safonov } else if (sk->sk_state == TCP_TIME_WAIT) { 1506decde258SDmitry Safonov return rcu_dereference_protected(tcp_twsk(sk)->ao_info, 1507decde258SDmitry Safonov lockdep_sock_is_held(sk)); 15084954f17dSDmitry Safonov } 15094954f17dSDmitry Safonov return ERR_PTR(-ESOCKTNOSUPPORT); 15104954f17dSDmitry Safonov } 15114954f17dSDmitry Safonov 1512faadfabaSDmitry Safonov static struct tcp_ao_info *getsockopt_ao_info(struct sock *sk) 1513faadfabaSDmitry Safonov { 1514faadfabaSDmitry Safonov if (sk_fullsock(sk)) 1515faadfabaSDmitry Safonov return rcu_dereference(tcp_sk(sk)->ao_info); 1516faadfabaSDmitry Safonov else if (sk->sk_state == TCP_TIME_WAIT) 1517faadfabaSDmitry Safonov return rcu_dereference(tcp_twsk(sk)->ao_info); 1518faadfabaSDmitry Safonov 1519faadfabaSDmitry Safonov return ERR_PTR(-ESOCKTNOSUPPORT); 1520faadfabaSDmitry Safonov } 1521faadfabaSDmitry Safonov 1522248411b8SDmitry Safonov #define TCP_AO_KEYF_ALL (TCP_AO_KEYF_IFINDEX | TCP_AO_KEYF_EXCLUDE_OPT) 1523248411b8SDmitry Safonov #define TCP_AO_GET_KEYF_VALID (TCP_AO_KEYF_IFINDEX) 15244954f17dSDmitry Safonov 15254954f17dSDmitry Safonov static struct tcp_ao_key *tcp_ao_key_alloc(struct sock *sk, 15264954f17dSDmitry Safonov struct tcp_ao_add *cmd) 15274954f17dSDmitry Safonov { 15284954f17dSDmitry Safonov const char *algo = cmd->alg_name; 15294954f17dSDmitry Safonov unsigned int digest_size; 15304954f17dSDmitry Safonov struct crypto_ahash *tfm; 15314954f17dSDmitry Safonov struct tcp_ao_key *key; 15324954f17dSDmitry Safonov struct tcp_sigpool hp; 15334954f17dSDmitry Safonov int err, pool_id; 15344954f17dSDmitry Safonov size_t size; 15354954f17dSDmitry Safonov 15364954f17dSDmitry Safonov /* Force null-termination of alg_name */ 15374954f17dSDmitry Safonov cmd->alg_name[ARRAY_SIZE(cmd->alg_name) - 1] = '\0'; 15384954f17dSDmitry Safonov 15394954f17dSDmitry Safonov /* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */ 15404954f17dSDmitry Safonov if (!strcmp("cmac(aes128)", algo)) 15414954f17dSDmitry Safonov algo = "cmac(aes)"; 15424954f17dSDmitry Safonov 15434954f17dSDmitry Safonov /* Full TCP header (th->doff << 2) should fit into scratch area, 15444954f17dSDmitry Safonov * see tcp_ao_hash_header(). 15454954f17dSDmitry Safonov */ 15464954f17dSDmitry Safonov pool_id = tcp_sigpool_alloc_ahash(algo, 60); 15474954f17dSDmitry Safonov if (pool_id < 0) 15484954f17dSDmitry Safonov return ERR_PTR(pool_id); 15494954f17dSDmitry Safonov 15504954f17dSDmitry Safonov err = tcp_sigpool_start(pool_id, &hp); 15514954f17dSDmitry Safonov if (err) 15524954f17dSDmitry Safonov goto err_free_pool; 15534954f17dSDmitry Safonov 15544954f17dSDmitry Safonov tfm = crypto_ahash_reqtfm(hp.req); 15554954f17dSDmitry Safonov digest_size = crypto_ahash_digestsize(tfm); 15564954f17dSDmitry Safonov tcp_sigpool_end(&hp); 15574954f17dSDmitry Safonov 15584954f17dSDmitry Safonov size = sizeof(struct tcp_ao_key) + (digest_size << 1); 15594954f17dSDmitry Safonov key = sock_kmalloc(sk, size, GFP_KERNEL); 15604954f17dSDmitry Safonov if (!key) { 15614954f17dSDmitry Safonov err = -ENOMEM; 15624954f17dSDmitry Safonov goto err_free_pool; 15634954f17dSDmitry Safonov } 15644954f17dSDmitry Safonov 15654954f17dSDmitry Safonov key->tcp_sigpool_id = pool_id; 15664954f17dSDmitry Safonov key->digest_size = digest_size; 15674954f17dSDmitry Safonov return key; 15684954f17dSDmitry Safonov 15694954f17dSDmitry Safonov err_free_pool: 15704954f17dSDmitry Safonov tcp_sigpool_release(pool_id); 15714954f17dSDmitry Safonov return ERR_PTR(err); 15724954f17dSDmitry Safonov } 15734954f17dSDmitry Safonov 15744954f17dSDmitry Safonov static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, 15754954f17dSDmitry Safonov sockptr_t optval, int optlen) 15764954f17dSDmitry Safonov { 15774954f17dSDmitry Safonov struct tcp_ao_info *ao_info; 15784954f17dSDmitry Safonov union tcp_ao_addr *addr; 15794954f17dSDmitry Safonov struct tcp_ao_key *key; 15804954f17dSDmitry Safonov struct tcp_ao_add cmd; 1581248411b8SDmitry Safonov int ret, l3index = 0; 15824954f17dSDmitry Safonov bool first = false; 15834954f17dSDmitry Safonov 15844954f17dSDmitry Safonov if (optlen < sizeof(cmd)) 15854954f17dSDmitry Safonov return -EINVAL; 15864954f17dSDmitry Safonov 15874954f17dSDmitry Safonov ret = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 15884954f17dSDmitry Safonov if (ret) 15894954f17dSDmitry Safonov return ret; 15904954f17dSDmitry Safonov 15914954f17dSDmitry Safonov if (cmd.keylen > TCP_AO_MAXKEYLEN) 15924954f17dSDmitry Safonov return -EINVAL; 15934954f17dSDmitry Safonov 15944954f17dSDmitry Safonov if (cmd.reserved != 0 || cmd.reserved2 != 0) 15954954f17dSDmitry Safonov return -EINVAL; 15964954f17dSDmitry Safonov 15974954f17dSDmitry Safonov if (family == AF_INET) 15984954f17dSDmitry Safonov ret = tcp_ao_verify_ipv4(sk, &cmd, &addr); 15994954f17dSDmitry Safonov else 16004954f17dSDmitry Safonov ret = tcp_ao_verify_ipv6(sk, &cmd, &addr, &family); 16014954f17dSDmitry Safonov if (ret) 16024954f17dSDmitry Safonov return ret; 16034954f17dSDmitry Safonov 16044954f17dSDmitry Safonov if (cmd.keyflags & ~TCP_AO_KEYF_ALL) 16054954f17dSDmitry Safonov return -EINVAL; 16064954f17dSDmitry Safonov 16074954f17dSDmitry Safonov if (cmd.set_current || cmd.set_rnext) { 16084954f17dSDmitry Safonov if (!tcp_ao_can_set_current_rnext(sk)) 16094954f17dSDmitry Safonov return -EINVAL; 16104954f17dSDmitry Safonov } 16114954f17dSDmitry Safonov 1612248411b8SDmitry Safonov if (cmd.ifindex && !(cmd.keyflags & TCP_AO_KEYF_IFINDEX)) 1613248411b8SDmitry Safonov return -EINVAL; 1614248411b8SDmitry Safonov 1615248411b8SDmitry Safonov /* For cmd.tcp_ifindex = 0 the key will apply to the default VRF */ 1616248411b8SDmitry Safonov if (cmd.keyflags & TCP_AO_KEYF_IFINDEX && cmd.ifindex) { 1617248411b8SDmitry Safonov int bound_dev_if = READ_ONCE(sk->sk_bound_dev_if); 1618248411b8SDmitry Safonov struct net_device *dev; 1619248411b8SDmitry Safonov 1620248411b8SDmitry Safonov rcu_read_lock(); 1621248411b8SDmitry Safonov dev = dev_get_by_index_rcu(sock_net(sk), cmd.ifindex); 1622248411b8SDmitry Safonov if (dev && netif_is_l3_master(dev)) 1623248411b8SDmitry Safonov l3index = dev->ifindex; 1624248411b8SDmitry Safonov rcu_read_unlock(); 1625248411b8SDmitry Safonov 1626248411b8SDmitry Safonov if (!dev || !l3index) 1627248411b8SDmitry Safonov return -EINVAL; 1628248411b8SDmitry Safonov 162912083d72SDmitry Safonov if (!bound_dev_if || bound_dev_if != cmd.ifindex) { 163012083d72SDmitry Safonov /* tcp_ao_established_key() doesn't expect having 163112083d72SDmitry Safonov * non peer-matching key on an established TCP-AO 163212083d72SDmitry Safonov * connection. 163312083d72SDmitry Safonov */ 163412083d72SDmitry Safonov if (!((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) 163512083d72SDmitry Safonov return -EINVAL; 163612083d72SDmitry Safonov } 163712083d72SDmitry Safonov 1638248411b8SDmitry Safonov /* It's still possible to bind after adding keys or even 1639248411b8SDmitry Safonov * re-bind to a different dev (with CAP_NET_RAW). 1640248411b8SDmitry Safonov * So, no reason to return error here, rather try to be 1641248411b8SDmitry Safonov * nice and warn the user. 1642248411b8SDmitry Safonov */ 1643248411b8SDmitry Safonov if (bound_dev_if && bound_dev_if != cmd.ifindex) 1644248411b8SDmitry Safonov net_warn_ratelimited("AO key ifindex %d != sk bound ifindex %d\n", 1645248411b8SDmitry Safonov cmd.ifindex, bound_dev_if); 1646248411b8SDmitry Safonov } 1647248411b8SDmitry Safonov 16480aadc739SDmitry Safonov /* Don't allow keys for peers that have a matching TCP-MD5 key */ 1649248411b8SDmitry Safonov if (cmd.keyflags & TCP_AO_KEYF_IFINDEX) { 1650248411b8SDmitry Safonov /* Non-_exact version of tcp_md5_do_lookup() will 1651248411b8SDmitry Safonov * as well match keys that aren't bound to a specific VRF 1652248411b8SDmitry Safonov * (that will make them match AO key with 1653248411b8SDmitry Safonov * sysctl_tcp_l3dev_accept = 1 1654248411b8SDmitry Safonov */ 1655248411b8SDmitry Safonov if (tcp_md5_do_lookup(sk, l3index, addr, family)) 1656248411b8SDmitry Safonov return -EKEYREJECTED; 1657248411b8SDmitry Safonov } else { 16580aadc739SDmitry Safonov if (tcp_md5_do_lookup_any_l3index(sk, addr, family)) 16590aadc739SDmitry Safonov return -EKEYREJECTED; 1660248411b8SDmitry Safonov } 16610aadc739SDmitry Safonov 16624954f17dSDmitry Safonov ao_info = setsockopt_ao_info(sk); 16634954f17dSDmitry Safonov if (IS_ERR(ao_info)) 16644954f17dSDmitry Safonov return PTR_ERR(ao_info); 16654954f17dSDmitry Safonov 16664954f17dSDmitry Safonov if (!ao_info) { 16674954f17dSDmitry Safonov ao_info = tcp_ao_alloc_info(GFP_KERNEL); 16684954f17dSDmitry Safonov if (!ao_info) 16694954f17dSDmitry Safonov return -ENOMEM; 16704954f17dSDmitry Safonov first = true; 16714954f17dSDmitry Safonov } else { 16724954f17dSDmitry Safonov /* Check that neither RecvID nor SendID match any 16734954f17dSDmitry Safonov * existing key for the peer, RFC5925 3.1: 16744954f17dSDmitry Safonov * > The IDs of MKTs MUST NOT overlap where their 16754954f17dSDmitry Safonov * > TCP connection identifiers overlap. 16764954f17dSDmitry Safonov */ 1677248411b8SDmitry Safonov if (__tcp_ao_do_lookup(sk, l3index, addr, family, cmd.prefix, -1, cmd.rcvid)) 16784954f17dSDmitry Safonov return -EEXIST; 1679248411b8SDmitry Safonov if (__tcp_ao_do_lookup(sk, l3index, addr, family, 16804954f17dSDmitry Safonov cmd.prefix, cmd.sndid, -1)) 16814954f17dSDmitry Safonov return -EEXIST; 16824954f17dSDmitry Safonov } 16834954f17dSDmitry Safonov 16844954f17dSDmitry Safonov key = tcp_ao_key_alloc(sk, &cmd); 16854954f17dSDmitry Safonov if (IS_ERR(key)) { 16864954f17dSDmitry Safonov ret = PTR_ERR(key); 16874954f17dSDmitry Safonov goto err_free_ao; 16884954f17dSDmitry Safonov } 16894954f17dSDmitry Safonov 16904954f17dSDmitry Safonov INIT_HLIST_NODE(&key->node); 16914954f17dSDmitry Safonov memcpy(&key->addr, addr, (family == AF_INET) ? sizeof(struct in_addr) : 16924954f17dSDmitry Safonov sizeof(struct in6_addr)); 16934954f17dSDmitry Safonov key->prefixlen = cmd.prefix; 16944954f17dSDmitry Safonov key->family = family; 16954954f17dSDmitry Safonov key->keyflags = cmd.keyflags; 16964954f17dSDmitry Safonov key->sndid = cmd.sndid; 16974954f17dSDmitry Safonov key->rcvid = cmd.rcvid; 1698248411b8SDmitry Safonov key->l3index = l3index; 1699af09a341SDmitry Safonov atomic64_set(&key->pkt_good, 0); 1700af09a341SDmitry Safonov atomic64_set(&key->pkt_bad, 0); 17014954f17dSDmitry Safonov 17024954f17dSDmitry Safonov ret = tcp_ao_parse_crypto(&cmd, key); 17034954f17dSDmitry Safonov if (ret < 0) 17044954f17dSDmitry Safonov goto err_free_sock; 17054954f17dSDmitry Safonov 1706faadfabaSDmitry Safonov if (!((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) { 17077c2ffaf2SDmitry Safonov tcp_ao_cache_traffic_keys(sk, ao_info, key); 1708faadfabaSDmitry Safonov if (first) { 1709faadfabaSDmitry Safonov ao_info->current_key = key; 1710faadfabaSDmitry Safonov ao_info->rnext_key = key; 1711faadfabaSDmitry Safonov } 1712faadfabaSDmitry Safonov } 17137c2ffaf2SDmitry Safonov 17144954f17dSDmitry Safonov tcp_ao_link_mkt(ao_info, key); 17154954f17dSDmitry Safonov if (first) { 171667fa83f7SDmitry Safonov if (!static_branch_inc(&tcp_ao_needed.key)) { 171767fa83f7SDmitry Safonov ret = -EUSERS; 171867fa83f7SDmitry Safonov goto err_free_sock; 171967fa83f7SDmitry Safonov } 17204954f17dSDmitry Safonov sk_gso_disable(sk); 17214954f17dSDmitry Safonov rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info); 17224954f17dSDmitry Safonov } 17234954f17dSDmitry Safonov 17244954f17dSDmitry Safonov if (cmd.set_current) 17254954f17dSDmitry Safonov WRITE_ONCE(ao_info->current_key, key); 17264954f17dSDmitry Safonov if (cmd.set_rnext) 17274954f17dSDmitry Safonov WRITE_ONCE(ao_info->rnext_key, key); 17284954f17dSDmitry Safonov return 0; 17294954f17dSDmitry Safonov 17304954f17dSDmitry Safonov err_free_sock: 17314954f17dSDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 17324954f17dSDmitry Safonov tcp_sigpool_release(key->tcp_sigpool_id); 17334954f17dSDmitry Safonov kfree_sensitive(key); 17344954f17dSDmitry Safonov err_free_ao: 17354954f17dSDmitry Safonov if (first) 17364954f17dSDmitry Safonov kfree(ao_info); 17374954f17dSDmitry Safonov return ret; 17384954f17dSDmitry Safonov } 17394954f17dSDmitry Safonov 17404954f17dSDmitry Safonov static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_info *ao_info, 1741d6732b95SDmitry Safonov bool del_async, struct tcp_ao_key *key, 17424954f17dSDmitry Safonov struct tcp_ao_key *new_current, 17434954f17dSDmitry Safonov struct tcp_ao_key *new_rnext) 17444954f17dSDmitry Safonov { 17454954f17dSDmitry Safonov int err; 17464954f17dSDmitry Safonov 17474954f17dSDmitry Safonov hlist_del_rcu(&key->node); 17484954f17dSDmitry Safonov 1749d6732b95SDmitry Safonov /* Support for async delete on listening sockets: as they don't 1750d6732b95SDmitry Safonov * need current_key/rnext_key maintaining, we don't need to check 1751d6732b95SDmitry Safonov * them and we can just free all resources in RCU fashion. 1752d6732b95SDmitry Safonov */ 1753d6732b95SDmitry Safonov if (del_async) { 1754d6732b95SDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 1755d6732b95SDmitry Safonov call_rcu(&key->rcu, tcp_ao_key_free_rcu); 1756d6732b95SDmitry Safonov return 0; 1757d6732b95SDmitry Safonov } 1758d6732b95SDmitry Safonov 17594954f17dSDmitry Safonov /* At this moment another CPU could have looked this key up 17604954f17dSDmitry Safonov * while it was unlinked from the list. Wait for RCU grace period, 17614954f17dSDmitry Safonov * after which the key is off-list and can't be looked up again; 17624954f17dSDmitry Safonov * the rx path [just before RCU came] might have used it and set it 17634954f17dSDmitry Safonov * as current_key (very unlikely). 1764d6732b95SDmitry Safonov * Free the key with next RCU grace period (in case it was 1765d6732b95SDmitry Safonov * current_key before tcp_ao_current_rnext() might have 1766d6732b95SDmitry Safonov * changed it in forced-delete). 17674954f17dSDmitry Safonov */ 17684954f17dSDmitry Safonov synchronize_rcu(); 17694954f17dSDmitry Safonov if (new_current) 17704954f17dSDmitry Safonov WRITE_ONCE(ao_info->current_key, new_current); 17714954f17dSDmitry Safonov if (new_rnext) 17724954f17dSDmitry Safonov WRITE_ONCE(ao_info->rnext_key, new_rnext); 17734954f17dSDmitry Safonov 17744954f17dSDmitry Safonov if (unlikely(READ_ONCE(ao_info->current_key) == key || 17754954f17dSDmitry Safonov READ_ONCE(ao_info->rnext_key) == key)) { 17764954f17dSDmitry Safonov err = -EBUSY; 17774954f17dSDmitry Safonov goto add_key; 17784954f17dSDmitry Safonov } 17794954f17dSDmitry Safonov 17804954f17dSDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 17814954f17dSDmitry Safonov call_rcu(&key->rcu, tcp_ao_key_free_rcu); 17824954f17dSDmitry Safonov 17834954f17dSDmitry Safonov return 0; 17844954f17dSDmitry Safonov add_key: 17854954f17dSDmitry Safonov hlist_add_head_rcu(&key->node, &ao_info->head); 17864954f17dSDmitry Safonov return err; 17874954f17dSDmitry Safonov } 17884954f17dSDmitry Safonov 1789248411b8SDmitry Safonov #define TCP_AO_DEL_KEYF_ALL (TCP_AO_KEYF_IFINDEX) 17904954f17dSDmitry Safonov static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family, 17914954f17dSDmitry Safonov sockptr_t optval, int optlen) 17924954f17dSDmitry Safonov { 17934954f17dSDmitry Safonov struct tcp_ao_key *key, *new_current = NULL, *new_rnext = NULL; 1794248411b8SDmitry Safonov int err, addr_len, l3index = 0; 17954954f17dSDmitry Safonov struct tcp_ao_info *ao_info; 17964954f17dSDmitry Safonov union tcp_ao_addr *addr; 17974954f17dSDmitry Safonov struct tcp_ao_del cmd; 17984954f17dSDmitry Safonov __u8 prefix; 17994954f17dSDmitry Safonov u16 port; 18004954f17dSDmitry Safonov 18014954f17dSDmitry Safonov if (optlen < sizeof(cmd)) 18024954f17dSDmitry Safonov return -EINVAL; 18034954f17dSDmitry Safonov 18044954f17dSDmitry Safonov err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 18054954f17dSDmitry Safonov if (err) 18064954f17dSDmitry Safonov return err; 18074954f17dSDmitry Safonov 18084954f17dSDmitry Safonov if (cmd.reserved != 0 || cmd.reserved2 != 0) 18094954f17dSDmitry Safonov return -EINVAL; 18104954f17dSDmitry Safonov 18114954f17dSDmitry Safonov if (cmd.set_current || cmd.set_rnext) { 18124954f17dSDmitry Safonov if (!tcp_ao_can_set_current_rnext(sk)) 18134954f17dSDmitry Safonov return -EINVAL; 18144954f17dSDmitry Safonov } 18154954f17dSDmitry Safonov 1816248411b8SDmitry Safonov if (cmd.keyflags & ~TCP_AO_DEL_KEYF_ALL) 1817248411b8SDmitry Safonov return -EINVAL; 1818248411b8SDmitry Safonov 1819248411b8SDmitry Safonov /* No sanity check for TCP_AO_KEYF_IFINDEX as if a VRF 1820248411b8SDmitry Safonov * was destroyed, there still should be a way to delete keys, 1821248411b8SDmitry Safonov * that were bound to that l3intf. So, fail late at lookup stage 1822248411b8SDmitry Safonov * if there is no key for that ifindex. 1823248411b8SDmitry Safonov */ 1824248411b8SDmitry Safonov if (cmd.ifindex && !(cmd.keyflags & TCP_AO_KEYF_IFINDEX)) 1825248411b8SDmitry Safonov return -EINVAL; 1826248411b8SDmitry Safonov 18274954f17dSDmitry Safonov ao_info = setsockopt_ao_info(sk); 18284954f17dSDmitry Safonov if (IS_ERR(ao_info)) 18294954f17dSDmitry Safonov return PTR_ERR(ao_info); 18304954f17dSDmitry Safonov if (!ao_info) 18314954f17dSDmitry Safonov return -ENOENT; 18324954f17dSDmitry Safonov 18334954f17dSDmitry Safonov /* For sockets in TCP_CLOSED it's possible set keys that aren't 18344954f17dSDmitry Safonov * matching the future peer (address/VRF/etc), 18354954f17dSDmitry Safonov * tcp_ao_connect_init() will choose a correct matching MKT 18364954f17dSDmitry Safonov * if there's any. 18374954f17dSDmitry Safonov */ 18384954f17dSDmitry Safonov if (cmd.set_current) { 18394954f17dSDmitry Safonov new_current = tcp_ao_established_key(ao_info, cmd.current_key, -1); 18404954f17dSDmitry Safonov if (!new_current) 18414954f17dSDmitry Safonov return -ENOENT; 18424954f17dSDmitry Safonov } 18434954f17dSDmitry Safonov if (cmd.set_rnext) { 18444954f17dSDmitry Safonov new_rnext = tcp_ao_established_key(ao_info, -1, cmd.rnext); 18454954f17dSDmitry Safonov if (!new_rnext) 18464954f17dSDmitry Safonov return -ENOENT; 18474954f17dSDmitry Safonov } 1848d6732b95SDmitry Safonov if (cmd.del_async && sk->sk_state != TCP_LISTEN) 1849d6732b95SDmitry Safonov return -EINVAL; 18504954f17dSDmitry Safonov 18514954f17dSDmitry Safonov if (family == AF_INET) { 18524954f17dSDmitry Safonov struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.addr; 18534954f17dSDmitry Safonov 18544954f17dSDmitry Safonov addr = (union tcp_ao_addr *)&sin->sin_addr; 18554954f17dSDmitry Safonov addr_len = sizeof(struct in_addr); 18564954f17dSDmitry Safonov port = ntohs(sin->sin_port); 18574954f17dSDmitry Safonov } else { 18584954f17dSDmitry Safonov struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.addr; 18594954f17dSDmitry Safonov struct in6_addr *addr6 = &sin6->sin6_addr; 18604954f17dSDmitry Safonov 18614954f17dSDmitry Safonov if (ipv6_addr_v4mapped(addr6)) { 18624954f17dSDmitry Safonov addr = (union tcp_ao_addr *)&addr6->s6_addr32[3]; 18634954f17dSDmitry Safonov addr_len = sizeof(struct in_addr); 18644954f17dSDmitry Safonov family = AF_INET; 18654954f17dSDmitry Safonov } else { 18664954f17dSDmitry Safonov addr = (union tcp_ao_addr *)addr6; 18674954f17dSDmitry Safonov addr_len = sizeof(struct in6_addr); 18684954f17dSDmitry Safonov } 18694954f17dSDmitry Safonov port = ntohs(sin6->sin6_port); 18704954f17dSDmitry Safonov } 18714954f17dSDmitry Safonov prefix = cmd.prefix; 18724954f17dSDmitry Safonov 18734954f17dSDmitry Safonov /* Currently matching is not performed on port (or port ranges) */ 18744954f17dSDmitry Safonov if (port != 0) 18754954f17dSDmitry Safonov return -EINVAL; 18764954f17dSDmitry Safonov 18774954f17dSDmitry Safonov /* We could choose random present key here for current/rnext 18784954f17dSDmitry Safonov * but that's less predictable. Let's be strict and don't 18794954f17dSDmitry Safonov * allow removing a key that's in use. RFC5925 doesn't 18804954f17dSDmitry Safonov * specify how-to coordinate key removal, but says: 18814954f17dSDmitry Safonov * "It is presumed that an MKT affecting a particular 18824954f17dSDmitry Safonov * connection cannot be destroyed during an active connection" 18834954f17dSDmitry Safonov */ 18844954f17dSDmitry Safonov hlist_for_each_entry_rcu(key, &ao_info->head, node) { 18854954f17dSDmitry Safonov if (cmd.sndid != key->sndid || 18864954f17dSDmitry Safonov cmd.rcvid != key->rcvid) 18874954f17dSDmitry Safonov continue; 18884954f17dSDmitry Safonov 18894954f17dSDmitry Safonov if (family != key->family || 18904954f17dSDmitry Safonov prefix != key->prefixlen || 18914954f17dSDmitry Safonov memcmp(addr, &key->addr, addr_len)) 18924954f17dSDmitry Safonov continue; 18934954f17dSDmitry Safonov 1894248411b8SDmitry Safonov if ((cmd.keyflags & TCP_AO_KEYF_IFINDEX) != 1895248411b8SDmitry Safonov (key->keyflags & TCP_AO_KEYF_IFINDEX)) 1896248411b8SDmitry Safonov continue; 1897248411b8SDmitry Safonov 1898248411b8SDmitry Safonov if (key->l3index != l3index) 1899248411b8SDmitry Safonov continue; 1900248411b8SDmitry Safonov 19014954f17dSDmitry Safonov if (key == new_current || key == new_rnext) 19024954f17dSDmitry Safonov continue; 19034954f17dSDmitry Safonov 1904d6732b95SDmitry Safonov return tcp_ao_delete_key(sk, ao_info, cmd.del_async, key, 19054954f17dSDmitry Safonov new_current, new_rnext); 19064954f17dSDmitry Safonov } 19074954f17dSDmitry Safonov return -ENOENT; 19084954f17dSDmitry Safonov } 19094954f17dSDmitry Safonov 19100aadc739SDmitry Safonov /* cmd.ao_required makes a socket TCP-AO only. 19110aadc739SDmitry Safonov * Don't allow any md5 keys for any l3intf on the socket together with it. 19120aadc739SDmitry Safonov * Restricting it early in setsockopt() removes a check for 19130aadc739SDmitry Safonov * ao_info->ao_required on inbound tcp segment fast-path. 19140aadc739SDmitry Safonov */ 19150aadc739SDmitry Safonov static int tcp_ao_required_verify(struct sock *sk) 19160aadc739SDmitry Safonov { 19170aadc739SDmitry Safonov #ifdef CONFIG_TCP_MD5SIG 19180aadc739SDmitry Safonov const struct tcp_md5sig_info *md5sig; 19190aadc739SDmitry Safonov 19200aadc739SDmitry Safonov if (!static_branch_unlikely(&tcp_md5_needed.key)) 19210aadc739SDmitry Safonov return 0; 19220aadc739SDmitry Safonov 19230aadc739SDmitry Safonov md5sig = rcu_dereference_check(tcp_sk(sk)->md5sig_info, 19240aadc739SDmitry Safonov lockdep_sock_is_held(sk)); 19250aadc739SDmitry Safonov if (!md5sig) 19260aadc739SDmitry Safonov return 0; 19270aadc739SDmitry Safonov 19280aadc739SDmitry Safonov if (rcu_dereference_check(hlist_first_rcu(&md5sig->head), 19290aadc739SDmitry Safonov lockdep_sock_is_held(sk))) 19300aadc739SDmitry Safonov return 1; 19310aadc739SDmitry Safonov #endif 19320aadc739SDmitry Safonov return 0; 19330aadc739SDmitry Safonov } 19340aadc739SDmitry Safonov 19354954f17dSDmitry Safonov static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family, 19364954f17dSDmitry Safonov sockptr_t optval, int optlen) 19374954f17dSDmitry Safonov { 19384954f17dSDmitry Safonov struct tcp_ao_key *new_current = NULL, *new_rnext = NULL; 19394954f17dSDmitry Safonov struct tcp_ao_info *ao_info; 19404954f17dSDmitry Safonov struct tcp_ao_info_opt cmd; 19414954f17dSDmitry Safonov bool first = false; 19424954f17dSDmitry Safonov int err; 19434954f17dSDmitry Safonov 19444954f17dSDmitry Safonov if (optlen < sizeof(cmd)) 19454954f17dSDmitry Safonov return -EINVAL; 19464954f17dSDmitry Safonov 19474954f17dSDmitry Safonov err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 19484954f17dSDmitry Safonov if (err) 19494954f17dSDmitry Safonov return err; 19504954f17dSDmitry Safonov 19514954f17dSDmitry Safonov if (cmd.set_current || cmd.set_rnext) { 19524954f17dSDmitry Safonov if (!tcp_ao_can_set_current_rnext(sk)) 19534954f17dSDmitry Safonov return -EINVAL; 19544954f17dSDmitry Safonov } 19554954f17dSDmitry Safonov 1956af09a341SDmitry Safonov if (cmd.reserved != 0 || cmd.reserved2 != 0) 19574954f17dSDmitry Safonov return -EINVAL; 19584954f17dSDmitry Safonov 19594954f17dSDmitry Safonov ao_info = setsockopt_ao_info(sk); 19604954f17dSDmitry Safonov if (IS_ERR(ao_info)) 19614954f17dSDmitry Safonov return PTR_ERR(ao_info); 19624954f17dSDmitry Safonov if (!ao_info) { 1963faadfabaSDmitry Safonov if (!((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) 1964faadfabaSDmitry Safonov return -EINVAL; 19654954f17dSDmitry Safonov ao_info = tcp_ao_alloc_info(GFP_KERNEL); 19664954f17dSDmitry Safonov if (!ao_info) 19674954f17dSDmitry Safonov return -ENOMEM; 19684954f17dSDmitry Safonov first = true; 19694954f17dSDmitry Safonov } 19704954f17dSDmitry Safonov 1971*f9ae8489SDmitry Safonov if (cmd.ao_required && tcp_ao_required_verify(sk)) { 1972*f9ae8489SDmitry Safonov err = -EKEYREJECTED; 1973*f9ae8489SDmitry Safonov goto out; 1974*f9ae8489SDmitry Safonov } 19750aadc739SDmitry Safonov 19764954f17dSDmitry Safonov /* For sockets in TCP_CLOSED it's possible set keys that aren't 19774954f17dSDmitry Safonov * matching the future peer (address/port/VRF/etc), 19784954f17dSDmitry Safonov * tcp_ao_connect_init() will choose a correct matching MKT 19794954f17dSDmitry Safonov * if there's any. 19804954f17dSDmitry Safonov */ 19814954f17dSDmitry Safonov if (cmd.set_current) { 19824954f17dSDmitry Safonov new_current = tcp_ao_established_key(ao_info, cmd.current_key, -1); 19834954f17dSDmitry Safonov if (!new_current) { 19844954f17dSDmitry Safonov err = -ENOENT; 19854954f17dSDmitry Safonov goto out; 19864954f17dSDmitry Safonov } 19874954f17dSDmitry Safonov } 19884954f17dSDmitry Safonov if (cmd.set_rnext) { 19894954f17dSDmitry Safonov new_rnext = tcp_ao_established_key(ao_info, -1, cmd.rnext); 19904954f17dSDmitry Safonov if (!new_rnext) { 19914954f17dSDmitry Safonov err = -ENOENT; 19924954f17dSDmitry Safonov goto out; 19934954f17dSDmitry Safonov } 19944954f17dSDmitry Safonov } 1995af09a341SDmitry Safonov if (cmd.set_counters) { 1996af09a341SDmitry Safonov atomic64_set(&ao_info->counters.pkt_good, cmd.pkt_good); 1997af09a341SDmitry Safonov atomic64_set(&ao_info->counters.pkt_bad, cmd.pkt_bad); 1998af09a341SDmitry Safonov atomic64_set(&ao_info->counters.key_not_found, cmd.pkt_key_not_found); 1999af09a341SDmitry Safonov atomic64_set(&ao_info->counters.ao_required, cmd.pkt_ao_required); 2000953af8e3SDmitry Safonov atomic64_set(&ao_info->counters.dropped_icmp, cmd.pkt_dropped_icmp); 2001af09a341SDmitry Safonov } 20024954f17dSDmitry Safonov 20034954f17dSDmitry Safonov ao_info->ao_required = cmd.ao_required; 2004953af8e3SDmitry Safonov ao_info->accept_icmps = cmd.accept_icmps; 20054954f17dSDmitry Safonov if (new_current) 20064954f17dSDmitry Safonov WRITE_ONCE(ao_info->current_key, new_current); 20074954f17dSDmitry Safonov if (new_rnext) 20084954f17dSDmitry Safonov WRITE_ONCE(ao_info->rnext_key, new_rnext); 20094954f17dSDmitry Safonov if (first) { 201067fa83f7SDmitry Safonov if (!static_branch_inc(&tcp_ao_needed.key)) { 201167fa83f7SDmitry Safonov err = -EUSERS; 201267fa83f7SDmitry Safonov goto out; 201367fa83f7SDmitry Safonov } 20144954f17dSDmitry Safonov sk_gso_disable(sk); 20154954f17dSDmitry Safonov rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info); 20164954f17dSDmitry Safonov } 20174954f17dSDmitry Safonov return 0; 20184954f17dSDmitry Safonov out: 20194954f17dSDmitry Safonov if (first) 20204954f17dSDmitry Safonov kfree(ao_info); 20214954f17dSDmitry Safonov return err; 20224954f17dSDmitry Safonov } 20234954f17dSDmitry Safonov 20244954f17dSDmitry Safonov int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, 20254954f17dSDmitry Safonov sockptr_t optval, int optlen) 20264954f17dSDmitry Safonov { 20274954f17dSDmitry Safonov if (WARN_ON_ONCE(family != AF_INET && family != AF_INET6)) 20284954f17dSDmitry Safonov return -EAFNOSUPPORT; 20294954f17dSDmitry Safonov 20304954f17dSDmitry Safonov switch (cmd) { 20314954f17dSDmitry Safonov case TCP_AO_ADD_KEY: 20324954f17dSDmitry Safonov return tcp_ao_add_cmd(sk, family, optval, optlen); 20334954f17dSDmitry Safonov case TCP_AO_DEL_KEY: 20344954f17dSDmitry Safonov return tcp_ao_del_cmd(sk, family, optval, optlen); 20354954f17dSDmitry Safonov case TCP_AO_INFO: 20364954f17dSDmitry Safonov return tcp_ao_info_cmd(sk, family, optval, optlen); 20374954f17dSDmitry Safonov default: 20384954f17dSDmitry Safonov WARN_ON_ONCE(1); 20394954f17dSDmitry Safonov return -EINVAL; 20404954f17dSDmitry Safonov } 20414954f17dSDmitry Safonov } 20424954f17dSDmitry Safonov 20434954f17dSDmitry Safonov int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen) 20444954f17dSDmitry Safonov { 20454954f17dSDmitry Safonov return tcp_parse_ao(sk, cmd, AF_INET, optval, optlen); 20464954f17dSDmitry Safonov } 20474954f17dSDmitry Safonov 2048ef84703aSDmitry Safonov /* tcp_ao_copy_mkts_to_user(ao_info, optval, optlen) 2049ef84703aSDmitry Safonov * 2050ef84703aSDmitry Safonov * @ao_info: struct tcp_ao_info on the socket that 2051ef84703aSDmitry Safonov * socket getsockopt(TCP_AO_GET_KEYS) is executed on 2052ef84703aSDmitry Safonov * @optval: pointer to array of tcp_ao_getsockopt structures in user space. 2053ef84703aSDmitry Safonov * Must be != NULL. 2054ef84703aSDmitry Safonov * @optlen: pointer to size of tcp_ao_getsockopt structure. 2055ef84703aSDmitry Safonov * Must be != NULL. 2056ef84703aSDmitry Safonov * 2057ef84703aSDmitry Safonov * Return value: 0 on success, a negative error number otherwise. 2058ef84703aSDmitry Safonov * 2059ef84703aSDmitry Safonov * optval points to an array of tcp_ao_getsockopt structures in user space. 2060ef84703aSDmitry Safonov * optval[0] is used as both input and output to getsockopt. It determines 2061ef84703aSDmitry Safonov * which keys are returned by the kernel. 2062ef84703aSDmitry Safonov * optval[0].nkeys is the size of the array in user space. On return it contains 2063ef84703aSDmitry Safonov * the number of keys matching the search criteria. 2064ef84703aSDmitry Safonov * If tcp_ao_getsockopt::get_all is set, then all keys in the socket are 2065ef84703aSDmitry Safonov * returned, otherwise only keys matching <addr, prefix, sndid, rcvid> 2066ef84703aSDmitry Safonov * in optval[0] are returned. 2067ef84703aSDmitry Safonov * optlen is also used as both input and output. The user provides the size 2068ef84703aSDmitry Safonov * of struct tcp_ao_getsockopt in user space, and the kernel returns the size 2069ef84703aSDmitry Safonov * of the structure in kernel space. 2070ef84703aSDmitry Safonov * The size of struct tcp_ao_getsockopt may differ between user and kernel. 2071ef84703aSDmitry Safonov * There are three cases to consider: 2072ef84703aSDmitry Safonov * * If usize == ksize, then keys are copied verbatim. 2073ef84703aSDmitry Safonov * * If usize < ksize, then the userspace has passed an old struct to a 2074ef84703aSDmitry Safonov * newer kernel. The rest of the trailing bytes in optval[0] 2075ef84703aSDmitry Safonov * (ksize - usize) are interpreted as 0 by the kernel. 2076ef84703aSDmitry Safonov * * If usize > ksize, then the userspace has passed a new struct to an 2077ef84703aSDmitry Safonov * older kernel. The trailing bytes unknown to the kernel (usize - ksize) 2078ef84703aSDmitry Safonov * are checked to ensure they are zeroed, otherwise -E2BIG is returned. 2079ef84703aSDmitry Safonov * On return the kernel fills in min(usize, ksize) in each entry of the array. 2080ef84703aSDmitry Safonov * The layout of the fields in the user and kernel structures is expected to 2081ef84703aSDmitry Safonov * be the same (including in the 32bit vs 64bit case). 2082ef84703aSDmitry Safonov */ 2083ef84703aSDmitry Safonov static int tcp_ao_copy_mkts_to_user(struct tcp_ao_info *ao_info, 2084ef84703aSDmitry Safonov sockptr_t optval, sockptr_t optlen) 2085ef84703aSDmitry Safonov { 2086ef84703aSDmitry Safonov struct tcp_ao_getsockopt opt_in, opt_out; 2087ef84703aSDmitry Safonov struct tcp_ao_key *key, *current_key; 2088ef84703aSDmitry Safonov bool do_address_matching = true; 2089ef84703aSDmitry Safonov union tcp_ao_addr *addr = NULL; 2090248411b8SDmitry Safonov int err, l3index, user_len; 2091ef84703aSDmitry Safonov unsigned int max_keys; /* maximum number of keys to copy to user */ 2092ef84703aSDmitry Safonov size_t out_offset = 0; 2093ef84703aSDmitry Safonov size_t bytes_to_write; /* number of bytes to write to user level */ 2094ef84703aSDmitry Safonov u32 matched_keys; /* keys from ao_info matched so far */ 2095ef84703aSDmitry Safonov int optlen_out; 2096ef84703aSDmitry Safonov __be16 port = 0; 2097ef84703aSDmitry Safonov 2098ef84703aSDmitry Safonov if (copy_from_sockptr(&user_len, optlen, sizeof(int))) 2099ef84703aSDmitry Safonov return -EFAULT; 2100ef84703aSDmitry Safonov 2101ef84703aSDmitry Safonov if (user_len <= 0) 2102ef84703aSDmitry Safonov return -EINVAL; 2103ef84703aSDmitry Safonov 2104ef84703aSDmitry Safonov memset(&opt_in, 0, sizeof(struct tcp_ao_getsockopt)); 2105ef84703aSDmitry Safonov err = copy_struct_from_sockptr(&opt_in, sizeof(opt_in), 2106ef84703aSDmitry Safonov optval, user_len); 2107ef84703aSDmitry Safonov if (err < 0) 2108ef84703aSDmitry Safonov return err; 2109ef84703aSDmitry Safonov 2110ef84703aSDmitry Safonov if (opt_in.pkt_good || opt_in.pkt_bad) 2111ef84703aSDmitry Safonov return -EINVAL; 2112248411b8SDmitry Safonov if (opt_in.keyflags & ~TCP_AO_GET_KEYF_VALID) 2113248411b8SDmitry Safonov return -EINVAL; 2114248411b8SDmitry Safonov if (opt_in.ifindex && !(opt_in.keyflags & TCP_AO_KEYF_IFINDEX)) 2115248411b8SDmitry Safonov return -EINVAL; 2116ef84703aSDmitry Safonov 2117ef84703aSDmitry Safonov if (opt_in.reserved != 0) 2118ef84703aSDmitry Safonov return -EINVAL; 2119ef84703aSDmitry Safonov 2120ef84703aSDmitry Safonov max_keys = opt_in.nkeys; 2121248411b8SDmitry Safonov l3index = (opt_in.keyflags & TCP_AO_KEYF_IFINDEX) ? opt_in.ifindex : -1; 2122ef84703aSDmitry Safonov 2123ef84703aSDmitry Safonov if (opt_in.get_all || opt_in.is_current || opt_in.is_rnext) { 2124ef84703aSDmitry Safonov if (opt_in.get_all && (opt_in.is_current || opt_in.is_rnext)) 2125ef84703aSDmitry Safonov return -EINVAL; 2126ef84703aSDmitry Safonov do_address_matching = false; 2127ef84703aSDmitry Safonov } 2128ef84703aSDmitry Safonov 2129ef84703aSDmitry Safonov switch (opt_in.addr.ss_family) { 2130ef84703aSDmitry Safonov case AF_INET: { 2131ef84703aSDmitry Safonov struct sockaddr_in *sin; 2132ef84703aSDmitry Safonov __be32 mask; 2133ef84703aSDmitry Safonov 2134ef84703aSDmitry Safonov sin = (struct sockaddr_in *)&opt_in.addr; 2135ef84703aSDmitry Safonov port = sin->sin_port; 2136ef84703aSDmitry Safonov addr = (union tcp_ao_addr *)&sin->sin_addr; 2137ef84703aSDmitry Safonov 2138ef84703aSDmitry Safonov if (opt_in.prefix > 32) 2139ef84703aSDmitry Safonov return -EINVAL; 2140ef84703aSDmitry Safonov 2141ef84703aSDmitry Safonov if (ntohl(sin->sin_addr.s_addr) == INADDR_ANY && 2142ef84703aSDmitry Safonov opt_in.prefix != 0) 2143ef84703aSDmitry Safonov return -EINVAL; 2144ef84703aSDmitry Safonov 2145ef84703aSDmitry Safonov mask = inet_make_mask(opt_in.prefix); 2146ef84703aSDmitry Safonov if (sin->sin_addr.s_addr & ~mask) 2147ef84703aSDmitry Safonov return -EINVAL; 2148ef84703aSDmitry Safonov 2149ef84703aSDmitry Safonov break; 2150ef84703aSDmitry Safonov } 2151ef84703aSDmitry Safonov case AF_INET6: { 2152ef84703aSDmitry Safonov struct sockaddr_in6 *sin6; 2153ef84703aSDmitry Safonov struct in6_addr *addr6; 2154ef84703aSDmitry Safonov 2155ef84703aSDmitry Safonov sin6 = (struct sockaddr_in6 *)&opt_in.addr; 2156ef84703aSDmitry Safonov addr = (union tcp_ao_addr *)&sin6->sin6_addr; 2157ef84703aSDmitry Safonov addr6 = &sin6->sin6_addr; 2158ef84703aSDmitry Safonov port = sin6->sin6_port; 2159ef84703aSDmitry Safonov 2160ef84703aSDmitry Safonov /* We don't have to change family and @addr here if 2161ef84703aSDmitry Safonov * ipv6_addr_v4mapped() like in key adding: 2162ef84703aSDmitry Safonov * tcp_ao_key_cmp() does it. Do the sanity checks though. 2163ef84703aSDmitry Safonov */ 2164ef84703aSDmitry Safonov if (opt_in.prefix != 0) { 2165ef84703aSDmitry Safonov if (ipv6_addr_v4mapped(addr6)) { 2166ef84703aSDmitry Safonov __be32 mask, addr4 = addr6->s6_addr32[3]; 2167ef84703aSDmitry Safonov 2168ef84703aSDmitry Safonov if (opt_in.prefix > 32 || 2169ef84703aSDmitry Safonov ntohl(addr4) == INADDR_ANY) 2170ef84703aSDmitry Safonov return -EINVAL; 2171ef84703aSDmitry Safonov mask = inet_make_mask(opt_in.prefix); 2172ef84703aSDmitry Safonov if (addr4 & ~mask) 2173ef84703aSDmitry Safonov return -EINVAL; 2174ef84703aSDmitry Safonov } else { 2175ef84703aSDmitry Safonov struct in6_addr pfx; 2176ef84703aSDmitry Safonov 2177ef84703aSDmitry Safonov if (ipv6_addr_any(addr6) || 2178ef84703aSDmitry Safonov opt_in.prefix > 128) 2179ef84703aSDmitry Safonov return -EINVAL; 2180ef84703aSDmitry Safonov 2181ef84703aSDmitry Safonov ipv6_addr_prefix(&pfx, addr6, opt_in.prefix); 2182ef84703aSDmitry Safonov if (ipv6_addr_cmp(&pfx, addr6)) 2183ef84703aSDmitry Safonov return -EINVAL; 2184ef84703aSDmitry Safonov } 2185ef84703aSDmitry Safonov } else if (!ipv6_addr_any(addr6)) { 2186ef84703aSDmitry Safonov return -EINVAL; 2187ef84703aSDmitry Safonov } 2188ef84703aSDmitry Safonov break; 2189ef84703aSDmitry Safonov } 2190ef84703aSDmitry Safonov case 0: 2191ef84703aSDmitry Safonov if (!do_address_matching) 2192ef84703aSDmitry Safonov break; 2193ef84703aSDmitry Safonov fallthrough; 2194ef84703aSDmitry Safonov default: 2195ef84703aSDmitry Safonov return -EAFNOSUPPORT; 2196ef84703aSDmitry Safonov } 2197ef84703aSDmitry Safonov 2198ef84703aSDmitry Safonov if (!do_address_matching) { 2199ef84703aSDmitry Safonov /* We could just ignore those, but let's do stricter checks */ 2200ef84703aSDmitry Safonov if (addr || port) 2201ef84703aSDmitry Safonov return -EINVAL; 2202ef84703aSDmitry Safonov if (opt_in.prefix || opt_in.sndid || opt_in.rcvid) 2203ef84703aSDmitry Safonov return -EINVAL; 2204ef84703aSDmitry Safonov } 2205ef84703aSDmitry Safonov 2206ef84703aSDmitry Safonov bytes_to_write = min_t(int, user_len, sizeof(struct tcp_ao_getsockopt)); 2207ef84703aSDmitry Safonov matched_keys = 0; 2208ef84703aSDmitry Safonov /* May change in RX, while we're dumping, pre-fetch it */ 2209ef84703aSDmitry Safonov current_key = READ_ONCE(ao_info->current_key); 2210ef84703aSDmitry Safonov 2211ef84703aSDmitry Safonov hlist_for_each_entry_rcu(key, &ao_info->head, node) { 2212ef84703aSDmitry Safonov if (opt_in.get_all) 2213ef84703aSDmitry Safonov goto match; 2214ef84703aSDmitry Safonov 2215ef84703aSDmitry Safonov if (opt_in.is_current || opt_in.is_rnext) { 2216ef84703aSDmitry Safonov if (opt_in.is_current && key == current_key) 2217ef84703aSDmitry Safonov goto match; 2218ef84703aSDmitry Safonov if (opt_in.is_rnext && key == ao_info->rnext_key) 2219ef84703aSDmitry Safonov goto match; 2220ef84703aSDmitry Safonov continue; 2221ef84703aSDmitry Safonov } 2222ef84703aSDmitry Safonov 2223248411b8SDmitry Safonov if (tcp_ao_key_cmp(key, l3index, addr, opt_in.prefix, 2224ef84703aSDmitry Safonov opt_in.addr.ss_family, 2225ef84703aSDmitry Safonov opt_in.sndid, opt_in.rcvid) != 0) 2226ef84703aSDmitry Safonov continue; 2227ef84703aSDmitry Safonov match: 2228ef84703aSDmitry Safonov matched_keys++; 2229ef84703aSDmitry Safonov if (matched_keys > max_keys) 2230ef84703aSDmitry Safonov continue; 2231ef84703aSDmitry Safonov 2232ef84703aSDmitry Safonov memset(&opt_out, 0, sizeof(struct tcp_ao_getsockopt)); 2233ef84703aSDmitry Safonov 2234ef84703aSDmitry Safonov if (key->family == AF_INET) { 2235ef84703aSDmitry Safonov struct sockaddr_in *sin_out = (struct sockaddr_in *)&opt_out.addr; 2236ef84703aSDmitry Safonov 2237ef84703aSDmitry Safonov sin_out->sin_family = key->family; 2238ef84703aSDmitry Safonov sin_out->sin_port = 0; 2239ef84703aSDmitry Safonov memcpy(&sin_out->sin_addr, &key->addr, sizeof(struct in_addr)); 2240ef84703aSDmitry Safonov } else { 2241ef84703aSDmitry Safonov struct sockaddr_in6 *sin6_out = (struct sockaddr_in6 *)&opt_out.addr; 2242ef84703aSDmitry Safonov 2243ef84703aSDmitry Safonov sin6_out->sin6_family = key->family; 2244ef84703aSDmitry Safonov sin6_out->sin6_port = 0; 2245ef84703aSDmitry Safonov memcpy(&sin6_out->sin6_addr, &key->addr, sizeof(struct in6_addr)); 2246ef84703aSDmitry Safonov } 2247ef84703aSDmitry Safonov opt_out.sndid = key->sndid; 2248ef84703aSDmitry Safonov opt_out.rcvid = key->rcvid; 2249ef84703aSDmitry Safonov opt_out.prefix = key->prefixlen; 2250ef84703aSDmitry Safonov opt_out.keyflags = key->keyflags; 2251ef84703aSDmitry Safonov opt_out.is_current = (key == current_key); 2252ef84703aSDmitry Safonov opt_out.is_rnext = (key == ao_info->rnext_key); 2253ef84703aSDmitry Safonov opt_out.nkeys = 0; 2254ef84703aSDmitry Safonov opt_out.maclen = key->maclen; 2255ef84703aSDmitry Safonov opt_out.keylen = key->keylen; 2256248411b8SDmitry Safonov opt_out.ifindex = key->l3index; 2257ef84703aSDmitry Safonov opt_out.pkt_good = atomic64_read(&key->pkt_good); 2258ef84703aSDmitry Safonov opt_out.pkt_bad = atomic64_read(&key->pkt_bad); 2259ef84703aSDmitry Safonov memcpy(&opt_out.key, key->key, key->keylen); 2260ef84703aSDmitry Safonov tcp_sigpool_algo(key->tcp_sigpool_id, opt_out.alg_name, 64); 2261ef84703aSDmitry Safonov 2262ef84703aSDmitry Safonov /* Copy key to user */ 2263ef84703aSDmitry Safonov if (copy_to_sockptr_offset(optval, out_offset, 2264ef84703aSDmitry Safonov &opt_out, bytes_to_write)) 2265ef84703aSDmitry Safonov return -EFAULT; 2266ef84703aSDmitry Safonov out_offset += user_len; 2267ef84703aSDmitry Safonov } 2268ef84703aSDmitry Safonov 2269ef84703aSDmitry Safonov optlen_out = (int)sizeof(struct tcp_ao_getsockopt); 2270ef84703aSDmitry Safonov if (copy_to_sockptr(optlen, &optlen_out, sizeof(int))) 2271ef84703aSDmitry Safonov return -EFAULT; 2272ef84703aSDmitry Safonov 2273ef84703aSDmitry Safonov out_offset = offsetof(struct tcp_ao_getsockopt, nkeys); 2274ef84703aSDmitry Safonov if (copy_to_sockptr_offset(optval, out_offset, 2275ef84703aSDmitry Safonov &matched_keys, sizeof(u32))) 2276ef84703aSDmitry Safonov return -EFAULT; 2277ef84703aSDmitry Safonov 2278ef84703aSDmitry Safonov return 0; 2279ef84703aSDmitry Safonov } 2280ef84703aSDmitry Safonov 2281ef84703aSDmitry Safonov int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen) 2282ef84703aSDmitry Safonov { 2283ef84703aSDmitry Safonov struct tcp_ao_info *ao_info; 2284ef84703aSDmitry Safonov 2285ef84703aSDmitry Safonov ao_info = setsockopt_ao_info(sk); 2286ef84703aSDmitry Safonov if (IS_ERR(ao_info)) 2287ef84703aSDmitry Safonov return PTR_ERR(ao_info); 2288ef84703aSDmitry Safonov if (!ao_info) 2289ef84703aSDmitry Safonov return -ENOENT; 2290ef84703aSDmitry Safonov 2291ef84703aSDmitry Safonov return tcp_ao_copy_mkts_to_user(ao_info, optval, optlen); 2292ef84703aSDmitry Safonov } 2293ef84703aSDmitry Safonov 2294ef84703aSDmitry Safonov int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen) 2295ef84703aSDmitry Safonov { 2296ef84703aSDmitry Safonov struct tcp_ao_info_opt out, in = {}; 2297ef84703aSDmitry Safonov struct tcp_ao_key *current_key; 2298ef84703aSDmitry Safonov struct tcp_ao_info *ao; 2299ef84703aSDmitry Safonov int err, len; 2300ef84703aSDmitry Safonov 2301ef84703aSDmitry Safonov if (copy_from_sockptr(&len, optlen, sizeof(int))) 2302ef84703aSDmitry Safonov return -EFAULT; 2303ef84703aSDmitry Safonov 2304ef84703aSDmitry Safonov if (len <= 0) 2305ef84703aSDmitry Safonov return -EINVAL; 2306ef84703aSDmitry Safonov 2307ef84703aSDmitry Safonov /* Copying this "in" only to check ::reserved, ::reserved2, 2308ef84703aSDmitry Safonov * that may be needed to extend (struct tcp_ao_info_opt) and 2309ef84703aSDmitry Safonov * what getsockopt() provides in future. 2310ef84703aSDmitry Safonov */ 2311ef84703aSDmitry Safonov err = copy_struct_from_sockptr(&in, sizeof(in), optval, len); 2312ef84703aSDmitry Safonov if (err) 2313ef84703aSDmitry Safonov return err; 2314ef84703aSDmitry Safonov 2315ef84703aSDmitry Safonov if (in.reserved != 0 || in.reserved2 != 0) 2316ef84703aSDmitry Safonov return -EINVAL; 2317ef84703aSDmitry Safonov 2318ef84703aSDmitry Safonov ao = setsockopt_ao_info(sk); 2319ef84703aSDmitry Safonov if (IS_ERR(ao)) 2320ef84703aSDmitry Safonov return PTR_ERR(ao); 2321ef84703aSDmitry Safonov if (!ao) 2322ef84703aSDmitry Safonov return -ENOENT; 2323ef84703aSDmitry Safonov 2324ef84703aSDmitry Safonov memset(&out, 0, sizeof(out)); 2325ef84703aSDmitry Safonov out.ao_required = ao->ao_required; 2326ef84703aSDmitry Safonov out.accept_icmps = ao->accept_icmps; 2327ef84703aSDmitry Safonov out.pkt_good = atomic64_read(&ao->counters.pkt_good); 2328ef84703aSDmitry Safonov out.pkt_bad = atomic64_read(&ao->counters.pkt_bad); 2329ef84703aSDmitry Safonov out.pkt_key_not_found = atomic64_read(&ao->counters.key_not_found); 2330ef84703aSDmitry Safonov out.pkt_ao_required = atomic64_read(&ao->counters.ao_required); 2331ef84703aSDmitry Safonov out.pkt_dropped_icmp = atomic64_read(&ao->counters.dropped_icmp); 2332ef84703aSDmitry Safonov 2333ef84703aSDmitry Safonov current_key = READ_ONCE(ao->current_key); 2334ef84703aSDmitry Safonov if (current_key) { 2335ef84703aSDmitry Safonov out.set_current = 1; 2336ef84703aSDmitry Safonov out.current_key = current_key->sndid; 2337ef84703aSDmitry Safonov } 2338ef84703aSDmitry Safonov if (ao->rnext_key) { 2339ef84703aSDmitry Safonov out.set_rnext = 1; 2340ef84703aSDmitry Safonov out.rnext = ao->rnext_key->rcvid; 2341ef84703aSDmitry Safonov } 2342ef84703aSDmitry Safonov 2343ef84703aSDmitry Safonov if (copy_to_sockptr(optval, &out, min_t(int, len, sizeof(out)))) 2344ef84703aSDmitry Safonov return -EFAULT; 2345ef84703aSDmitry Safonov 2346ef84703aSDmitry Safonov return 0; 2347ef84703aSDmitry Safonov } 2348ef84703aSDmitry Safonov 2349faadfabaSDmitry Safonov int tcp_ao_set_repair(struct sock *sk, sockptr_t optval, unsigned int optlen) 2350faadfabaSDmitry Safonov { 2351faadfabaSDmitry Safonov struct tcp_sock *tp = tcp_sk(sk); 2352faadfabaSDmitry Safonov struct tcp_ao_repair cmd; 2353faadfabaSDmitry Safonov struct tcp_ao_key *key; 2354faadfabaSDmitry Safonov struct tcp_ao_info *ao; 2355faadfabaSDmitry Safonov int err; 2356faadfabaSDmitry Safonov 2357faadfabaSDmitry Safonov if (optlen < sizeof(cmd)) 2358faadfabaSDmitry Safonov return -EINVAL; 2359faadfabaSDmitry Safonov 2360faadfabaSDmitry Safonov err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 2361faadfabaSDmitry Safonov if (err) 2362faadfabaSDmitry Safonov return err; 2363faadfabaSDmitry Safonov 2364faadfabaSDmitry Safonov if (!tp->repair) 2365faadfabaSDmitry Safonov return -EPERM; 2366faadfabaSDmitry Safonov 2367faadfabaSDmitry Safonov ao = setsockopt_ao_info(sk); 2368faadfabaSDmitry Safonov if (IS_ERR(ao)) 2369faadfabaSDmitry Safonov return PTR_ERR(ao); 2370faadfabaSDmitry Safonov if (!ao) 2371faadfabaSDmitry Safonov return -ENOENT; 2372faadfabaSDmitry Safonov 2373faadfabaSDmitry Safonov WRITE_ONCE(ao->lisn, cmd.snt_isn); 2374faadfabaSDmitry Safonov WRITE_ONCE(ao->risn, cmd.rcv_isn); 2375faadfabaSDmitry Safonov WRITE_ONCE(ao->snd_sne, cmd.snd_sne); 2376faadfabaSDmitry Safonov WRITE_ONCE(ao->rcv_sne, cmd.rcv_sne); 2377faadfabaSDmitry Safonov 2378faadfabaSDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) 2379faadfabaSDmitry Safonov tcp_ao_cache_traffic_keys(sk, ao, key); 2380faadfabaSDmitry Safonov 2381faadfabaSDmitry Safonov return 0; 2382faadfabaSDmitry Safonov } 2383faadfabaSDmitry Safonov 2384faadfabaSDmitry Safonov int tcp_ao_get_repair(struct sock *sk, sockptr_t optval, sockptr_t optlen) 2385faadfabaSDmitry Safonov { 2386faadfabaSDmitry Safonov struct tcp_sock *tp = tcp_sk(sk); 2387faadfabaSDmitry Safonov struct tcp_ao_repair opt; 2388faadfabaSDmitry Safonov struct tcp_ao_info *ao; 2389faadfabaSDmitry Safonov int len; 2390faadfabaSDmitry Safonov 2391faadfabaSDmitry Safonov if (copy_from_sockptr(&len, optlen, sizeof(int))) 2392faadfabaSDmitry Safonov return -EFAULT; 2393faadfabaSDmitry Safonov 2394faadfabaSDmitry Safonov if (len <= 0) 2395faadfabaSDmitry Safonov return -EINVAL; 2396faadfabaSDmitry Safonov 2397faadfabaSDmitry Safonov if (!tp->repair) 2398faadfabaSDmitry Safonov return -EPERM; 2399faadfabaSDmitry Safonov 2400faadfabaSDmitry Safonov rcu_read_lock(); 2401faadfabaSDmitry Safonov ao = getsockopt_ao_info(sk); 2402faadfabaSDmitry Safonov if (IS_ERR_OR_NULL(ao)) { 2403faadfabaSDmitry Safonov rcu_read_unlock(); 2404faadfabaSDmitry Safonov return ao ? PTR_ERR(ao) : -ENOENT; 2405faadfabaSDmitry Safonov } 2406faadfabaSDmitry Safonov 2407faadfabaSDmitry Safonov opt.snt_isn = ao->lisn; 2408faadfabaSDmitry Safonov opt.rcv_isn = ao->risn; 2409faadfabaSDmitry Safonov opt.snd_sne = READ_ONCE(ao->snd_sne); 2410faadfabaSDmitry Safonov opt.rcv_sne = READ_ONCE(ao->rcv_sne); 2411faadfabaSDmitry Safonov rcu_read_unlock(); 2412faadfabaSDmitry Safonov 2413faadfabaSDmitry Safonov if (copy_to_sockptr(optval, &opt, min_t(int, len, sizeof(opt)))) 2414faadfabaSDmitry Safonov return -EFAULT; 2415faadfabaSDmitry Safonov return 0; 2416faadfabaSDmitry Safonov } 2417