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> 1996be3dcdSDmitry Safonov #include <trace/events/tcp.h> 204954f17dSDmitry Safonov 2167fa83f7SDmitry Safonov DEFINE_STATIC_KEY_DEFERRED_FALSE(tcp_ao_needed, HZ); 2267fa83f7SDmitry Safonov 237c2ffaf2SDmitry Safonov int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, 247c2ffaf2SDmitry Safonov unsigned int len, struct tcp_sigpool *hp) 257c2ffaf2SDmitry Safonov { 267c2ffaf2SDmitry Safonov struct scatterlist sg; 277c2ffaf2SDmitry Safonov int ret; 287c2ffaf2SDmitry Safonov 297c2ffaf2SDmitry Safonov if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp->req), 307c2ffaf2SDmitry Safonov mkt->key, mkt->keylen)) 317c2ffaf2SDmitry Safonov goto clear_hash; 327c2ffaf2SDmitry Safonov 337c2ffaf2SDmitry Safonov ret = crypto_ahash_init(hp->req); 347c2ffaf2SDmitry Safonov if (ret) 357c2ffaf2SDmitry Safonov goto clear_hash; 367c2ffaf2SDmitry Safonov 377c2ffaf2SDmitry Safonov sg_init_one(&sg, ctx, len); 387c2ffaf2SDmitry Safonov ahash_request_set_crypt(hp->req, &sg, key, len); 397c2ffaf2SDmitry Safonov crypto_ahash_update(hp->req); 407c2ffaf2SDmitry Safonov 417c2ffaf2SDmitry Safonov ret = crypto_ahash_final(hp->req); 427c2ffaf2SDmitry Safonov if (ret) 437c2ffaf2SDmitry Safonov goto clear_hash; 447c2ffaf2SDmitry Safonov 457c2ffaf2SDmitry Safonov return 0; 467c2ffaf2SDmitry Safonov clear_hash: 477c2ffaf2SDmitry Safonov memset(key, 0, tcp_ao_digest_size(mkt)); 487c2ffaf2SDmitry Safonov return 1; 497c2ffaf2SDmitry Safonov } 507c2ffaf2SDmitry Safonov 51953af8e3SDmitry Safonov bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code) 52953af8e3SDmitry Safonov { 53953af8e3SDmitry Safonov bool ignore_icmp = false; 54953af8e3SDmitry Safonov struct tcp_ao_info *ao; 55953af8e3SDmitry Safonov 5667fa83f7SDmitry Safonov if (!static_branch_unlikely(&tcp_ao_needed.key)) 5767fa83f7SDmitry Safonov return false; 5867fa83f7SDmitry Safonov 59953af8e3SDmitry Safonov /* RFC5925, 7.8: 60953af8e3SDmitry Safonov * >> A TCP-AO implementation MUST default to ignore incoming ICMPv4 61953af8e3SDmitry Safonov * messages of Type 3 (destination unreachable), Codes 2-4 (protocol 62953af8e3SDmitry Safonov * unreachable, port unreachable, and fragmentation needed -- ’hard 63953af8e3SDmitry Safonov * errors’), and ICMPv6 Type 1 (destination unreachable), Code 1 64953af8e3SDmitry Safonov * (administratively prohibited) and Code 4 (port unreachable) intended 65953af8e3SDmitry Safonov * for connections in synchronized states (ESTABLISHED, FIN-WAIT-1, FIN- 66953af8e3SDmitry Safonov * WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT) that match MKTs. 67953af8e3SDmitry Safonov */ 68953af8e3SDmitry Safonov if (family == AF_INET) { 69953af8e3SDmitry Safonov if (type != ICMP_DEST_UNREACH) 70953af8e3SDmitry Safonov return false; 71953af8e3SDmitry Safonov if (code < ICMP_PROT_UNREACH || code > ICMP_FRAG_NEEDED) 72953af8e3SDmitry Safonov return false; 73953af8e3SDmitry Safonov } else { 74953af8e3SDmitry Safonov if (type != ICMPV6_DEST_UNREACH) 75953af8e3SDmitry Safonov return false; 76953af8e3SDmitry Safonov if (code != ICMPV6_ADM_PROHIBITED && code != ICMPV6_PORT_UNREACH) 77953af8e3SDmitry Safonov return false; 78953af8e3SDmitry Safonov } 79953af8e3SDmitry Safonov 80953af8e3SDmitry Safonov rcu_read_lock(); 81953af8e3SDmitry Safonov switch (sk->sk_state) { 82953af8e3SDmitry Safonov case TCP_TIME_WAIT: 83953af8e3SDmitry Safonov ao = rcu_dereference(tcp_twsk(sk)->ao_info); 84953af8e3SDmitry Safonov break; 85953af8e3SDmitry Safonov case TCP_SYN_SENT: 86953af8e3SDmitry Safonov case TCP_SYN_RECV: 87953af8e3SDmitry Safonov case TCP_LISTEN: 88953af8e3SDmitry Safonov case TCP_NEW_SYN_RECV: 89953af8e3SDmitry Safonov /* RFC5925 specifies to ignore ICMPs *only* on connections 90953af8e3SDmitry Safonov * in synchronized states. 91953af8e3SDmitry Safonov */ 92953af8e3SDmitry Safonov rcu_read_unlock(); 93953af8e3SDmitry Safonov return false; 94953af8e3SDmitry Safonov default: 95953af8e3SDmitry Safonov ao = rcu_dereference(tcp_sk(sk)->ao_info); 96953af8e3SDmitry Safonov } 97953af8e3SDmitry Safonov 98953af8e3SDmitry Safonov if (ao && !ao->accept_icmps) { 99953af8e3SDmitry Safonov ignore_icmp = true; 100953af8e3SDmitry Safonov __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAODROPPEDICMPS); 101953af8e3SDmitry Safonov atomic64_inc(&ao->counters.dropped_icmp); 102953af8e3SDmitry Safonov } 103953af8e3SDmitry Safonov rcu_read_unlock(); 104953af8e3SDmitry Safonov 105953af8e3SDmitry Safonov return ignore_icmp; 106953af8e3SDmitry Safonov } 107953af8e3SDmitry Safonov 1084954f17dSDmitry Safonov /* Optimized version of tcp_ao_do_lookup(): only for sockets for which 1094954f17dSDmitry Safonov * it's known that the keys in ao_info are matching peer's 1104954f17dSDmitry Safonov * family/address/VRF/etc. 1114954f17dSDmitry Safonov */ 112ba7783adSDmitry Safonov struct tcp_ao_key *tcp_ao_established_key(struct tcp_ao_info *ao, 1134954f17dSDmitry Safonov int sndid, int rcvid) 1144954f17dSDmitry Safonov { 1154954f17dSDmitry Safonov struct tcp_ao_key *key; 1164954f17dSDmitry Safonov 1174954f17dSDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) { 1184954f17dSDmitry Safonov if ((sndid >= 0 && key->sndid != sndid) || 1194954f17dSDmitry Safonov (rcvid >= 0 && key->rcvid != rcvid)) 1204954f17dSDmitry Safonov continue; 1214954f17dSDmitry Safonov return key; 1224954f17dSDmitry Safonov } 1234954f17dSDmitry Safonov 1244954f17dSDmitry Safonov return NULL; 1254954f17dSDmitry Safonov } 1264954f17dSDmitry Safonov 1274954f17dSDmitry Safonov static int ipv4_prefix_cmp(const struct in_addr *addr1, 1284954f17dSDmitry Safonov const struct in_addr *addr2, 1294954f17dSDmitry Safonov unsigned int prefixlen) 1304954f17dSDmitry Safonov { 1314954f17dSDmitry Safonov __be32 mask = inet_make_mask(prefixlen); 1324954f17dSDmitry Safonov __be32 a1 = addr1->s_addr & mask; 1334954f17dSDmitry Safonov __be32 a2 = addr2->s_addr & mask; 1344954f17dSDmitry Safonov 1354954f17dSDmitry Safonov if (a1 == a2) 1364954f17dSDmitry Safonov return 0; 1374954f17dSDmitry Safonov return memcmp(&a1, &a2, sizeof(a1)); 1384954f17dSDmitry Safonov } 1394954f17dSDmitry Safonov 140248411b8SDmitry Safonov static int __tcp_ao_key_cmp(const struct tcp_ao_key *key, int l3index, 1414954f17dSDmitry Safonov const union tcp_ao_addr *addr, u8 prefixlen, 1424954f17dSDmitry Safonov int family, int sndid, int rcvid) 1434954f17dSDmitry Safonov { 1444954f17dSDmitry Safonov if (sndid >= 0 && key->sndid != sndid) 1454954f17dSDmitry Safonov return (key->sndid > sndid) ? 1 : -1; 1464954f17dSDmitry Safonov if (rcvid >= 0 && key->rcvid != rcvid) 1474954f17dSDmitry Safonov return (key->rcvid > rcvid) ? 1 : -1; 148248411b8SDmitry Safonov if (l3index >= 0 && (key->keyflags & TCP_AO_KEYF_IFINDEX)) { 149248411b8SDmitry Safonov if (key->l3index != l3index) 150248411b8SDmitry Safonov return (key->l3index > l3index) ? 1 : -1; 151248411b8SDmitry Safonov } 1524954f17dSDmitry Safonov 1534954f17dSDmitry Safonov if (family == AF_UNSPEC) 1544954f17dSDmitry Safonov return 0; 1554954f17dSDmitry Safonov if (key->family != family) 1564954f17dSDmitry Safonov return (key->family > family) ? 1 : -1; 1574954f17dSDmitry Safonov 1584954f17dSDmitry Safonov if (family == AF_INET) { 1594954f17dSDmitry Safonov if (ntohl(key->addr.a4.s_addr) == INADDR_ANY) 1604954f17dSDmitry Safonov return 0; 1614954f17dSDmitry Safonov if (ntohl(addr->a4.s_addr) == INADDR_ANY) 1624954f17dSDmitry Safonov return 0; 1634954f17dSDmitry Safonov return ipv4_prefix_cmp(&key->addr.a4, &addr->a4, prefixlen); 1644954f17dSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 1654954f17dSDmitry Safonov } else { 1664954f17dSDmitry Safonov if (ipv6_addr_any(&key->addr.a6) || ipv6_addr_any(&addr->a6)) 1674954f17dSDmitry Safonov return 0; 1684954f17dSDmitry Safonov if (ipv6_prefix_equal(&key->addr.a6, &addr->a6, prefixlen)) 1694954f17dSDmitry Safonov return 0; 1704954f17dSDmitry Safonov return memcmp(&key->addr.a6, &addr->a6, sizeof(addr->a6)); 1714954f17dSDmitry Safonov #endif 1724954f17dSDmitry Safonov } 1734954f17dSDmitry Safonov return -1; 1744954f17dSDmitry Safonov } 1754954f17dSDmitry Safonov 176248411b8SDmitry Safonov static int tcp_ao_key_cmp(const struct tcp_ao_key *key, int l3index, 1774954f17dSDmitry Safonov const union tcp_ao_addr *addr, u8 prefixlen, 1784954f17dSDmitry Safonov int family, int sndid, int rcvid) 1794954f17dSDmitry Safonov { 1804954f17dSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 1814954f17dSDmitry Safonov if (family == AF_INET6 && ipv6_addr_v4mapped(&addr->a6)) { 1824954f17dSDmitry Safonov __be32 addr4 = addr->a6.s6_addr32[3]; 1834954f17dSDmitry Safonov 184248411b8SDmitry Safonov return __tcp_ao_key_cmp(key, l3index, 185248411b8SDmitry Safonov (union tcp_ao_addr *)&addr4, 1864954f17dSDmitry Safonov prefixlen, AF_INET, sndid, rcvid); 1874954f17dSDmitry Safonov } 1884954f17dSDmitry Safonov #endif 189248411b8SDmitry Safonov return __tcp_ao_key_cmp(key, l3index, addr, 190248411b8SDmitry Safonov prefixlen, family, sndid, rcvid); 1914954f17dSDmitry Safonov } 1924954f17dSDmitry Safonov 193248411b8SDmitry Safonov static struct tcp_ao_key *__tcp_ao_do_lookup(const struct sock *sk, int l3index, 1944954f17dSDmitry Safonov const union tcp_ao_addr *addr, int family, u8 prefix, 1954954f17dSDmitry Safonov int sndid, int rcvid) 1964954f17dSDmitry Safonov { 1974954f17dSDmitry Safonov struct tcp_ao_key *key; 1984954f17dSDmitry Safonov struct tcp_ao_info *ao; 1994954f17dSDmitry Safonov 20067fa83f7SDmitry Safonov if (!static_branch_unlikely(&tcp_ao_needed.key)) 20167fa83f7SDmitry Safonov return NULL; 20267fa83f7SDmitry Safonov 2034954f17dSDmitry Safonov ao = rcu_dereference_check(tcp_sk(sk)->ao_info, 2044954f17dSDmitry Safonov lockdep_sock_is_held(sk)); 2054954f17dSDmitry Safonov if (!ao) 2064954f17dSDmitry Safonov return NULL; 2074954f17dSDmitry Safonov 2084954f17dSDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) { 2094954f17dSDmitry Safonov u8 prefixlen = min(prefix, key->prefixlen); 2104954f17dSDmitry Safonov 211248411b8SDmitry Safonov if (!tcp_ao_key_cmp(key, l3index, addr, prefixlen, 212248411b8SDmitry Safonov family, sndid, rcvid)) 2134954f17dSDmitry Safonov return key; 2144954f17dSDmitry Safonov } 2154954f17dSDmitry Safonov return NULL; 2164954f17dSDmitry Safonov } 2174954f17dSDmitry Safonov 218248411b8SDmitry Safonov struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, int l3index, 2190aadc739SDmitry Safonov const union tcp_ao_addr *addr, 2200aadc739SDmitry Safonov int family, int sndid, int rcvid) 2210aadc739SDmitry Safonov { 222248411b8SDmitry Safonov return __tcp_ao_do_lookup(sk, l3index, addr, family, U8_MAX, sndid, rcvid); 2230aadc739SDmitry Safonov } 2240aadc739SDmitry Safonov 2254954f17dSDmitry Safonov static struct tcp_ao_info *tcp_ao_alloc_info(gfp_t flags) 2264954f17dSDmitry Safonov { 2274954f17dSDmitry Safonov struct tcp_ao_info *ao; 2284954f17dSDmitry Safonov 2294954f17dSDmitry Safonov ao = kzalloc(sizeof(*ao), flags); 2304954f17dSDmitry Safonov if (!ao) 2314954f17dSDmitry Safonov return NULL; 2324954f17dSDmitry Safonov INIT_HLIST_HEAD(&ao->head); 233decde258SDmitry Safonov refcount_set(&ao->refcnt, 1); 2344954f17dSDmitry Safonov 2354954f17dSDmitry Safonov return ao; 2364954f17dSDmitry Safonov } 2374954f17dSDmitry Safonov 2384954f17dSDmitry Safonov static void tcp_ao_link_mkt(struct tcp_ao_info *ao, struct tcp_ao_key *mkt) 2394954f17dSDmitry Safonov { 2404954f17dSDmitry Safonov hlist_add_head_rcu(&mkt->node, &ao->head); 2414954f17dSDmitry Safonov } 2424954f17dSDmitry Safonov 24306b22ef2SDmitry Safonov static struct tcp_ao_key *tcp_ao_copy_key(struct sock *sk, 24406b22ef2SDmitry Safonov struct tcp_ao_key *key) 24506b22ef2SDmitry Safonov { 24606b22ef2SDmitry Safonov struct tcp_ao_key *new_key; 24706b22ef2SDmitry Safonov 24806b22ef2SDmitry Safonov new_key = sock_kmalloc(sk, tcp_ao_sizeof_key(key), 24906b22ef2SDmitry Safonov GFP_ATOMIC); 25006b22ef2SDmitry Safonov if (!new_key) 25106b22ef2SDmitry Safonov return NULL; 25206b22ef2SDmitry Safonov 25306b22ef2SDmitry Safonov *new_key = *key; 25406b22ef2SDmitry Safonov INIT_HLIST_NODE(&new_key->node); 25506b22ef2SDmitry Safonov tcp_sigpool_get(new_key->tcp_sigpool_id); 256af09a341SDmitry Safonov atomic64_set(&new_key->pkt_good, 0); 257af09a341SDmitry Safonov atomic64_set(&new_key->pkt_bad, 0); 25806b22ef2SDmitry Safonov 25906b22ef2SDmitry Safonov return new_key; 26006b22ef2SDmitry Safonov } 26106b22ef2SDmitry Safonov 2624954f17dSDmitry Safonov static void tcp_ao_key_free_rcu(struct rcu_head *head) 2634954f17dSDmitry Safonov { 2644954f17dSDmitry Safonov struct tcp_ao_key *key = container_of(head, struct tcp_ao_key, rcu); 2654954f17dSDmitry Safonov 2664954f17dSDmitry Safonov tcp_sigpool_release(key->tcp_sigpool_id); 2674954f17dSDmitry Safonov kfree_sensitive(key); 2684954f17dSDmitry Safonov } 2694954f17dSDmitry Safonov 270*14ab4792SDmitry Safonov static void tcp_ao_info_free_rcu(struct rcu_head *head) 2714954f17dSDmitry Safonov { 272*14ab4792SDmitry Safonov struct tcp_ao_info *ao = container_of(head, struct tcp_ao_info, rcu); 2734954f17dSDmitry Safonov struct tcp_ao_key *key; 2744954f17dSDmitry Safonov struct hlist_node *n; 2754954f17dSDmitry Safonov 276*14ab4792SDmitry Safonov hlist_for_each_entry_safe(key, n, &ao->head, node) { 277*14ab4792SDmitry Safonov hlist_del(&key->node); 278*14ab4792SDmitry Safonov tcp_sigpool_release(key->tcp_sigpool_id); 279*14ab4792SDmitry Safonov kfree_sensitive(key); 280*14ab4792SDmitry Safonov } 281*14ab4792SDmitry Safonov kfree(ao); 282*14ab4792SDmitry Safonov static_branch_slow_dec_deferred(&tcp_ao_needed); 283*14ab4792SDmitry Safonov } 284*14ab4792SDmitry Safonov 285*14ab4792SDmitry Safonov static void tcp_ao_sk_omem_free(struct sock *sk, struct tcp_ao_info *ao) 286*14ab4792SDmitry Safonov { 287*14ab4792SDmitry Safonov size_t total_ao_sk_mem = 0; 288*14ab4792SDmitry Safonov struct tcp_ao_key *key; 289*14ab4792SDmitry Safonov 290*14ab4792SDmitry Safonov hlist_for_each_entry(key, &ao->head, node) 291*14ab4792SDmitry Safonov total_ao_sk_mem += tcp_ao_sizeof_key(key); 292*14ab4792SDmitry Safonov atomic_sub(total_ao_sk_mem, &sk->sk_omem_alloc); 293*14ab4792SDmitry Safonov } 294*14ab4792SDmitry Safonov 295*14ab4792SDmitry Safonov void tcp_ao_destroy_sock(struct sock *sk, bool twsk) 296*14ab4792SDmitry Safonov { 297*14ab4792SDmitry Safonov struct tcp_ao_info *ao; 298*14ab4792SDmitry Safonov 299decde258SDmitry Safonov if (twsk) { 300decde258SDmitry Safonov ao = rcu_dereference_protected(tcp_twsk(sk)->ao_info, 1); 301*14ab4792SDmitry Safonov rcu_assign_pointer(tcp_twsk(sk)->ao_info, NULL); 302decde258SDmitry Safonov } else { 3034954f17dSDmitry Safonov ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 1); 304*14ab4792SDmitry Safonov rcu_assign_pointer(tcp_sk(sk)->ao_info, NULL); 305decde258SDmitry Safonov } 3064954f17dSDmitry Safonov 307decde258SDmitry Safonov if (!ao || !refcount_dec_and_test(&ao->refcnt)) 3084954f17dSDmitry Safonov return; 3094954f17dSDmitry Safonov 310decde258SDmitry Safonov if (!twsk) 311*14ab4792SDmitry Safonov tcp_ao_sk_omem_free(sk, ao); 312*14ab4792SDmitry Safonov call_rcu(&ao->rcu, tcp_ao_info_free_rcu); 3134954f17dSDmitry Safonov } 3144954f17dSDmitry Safonov 315decde258SDmitry Safonov void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp) 316decde258SDmitry Safonov { 317decde258SDmitry Safonov struct tcp_ao_info *ao_info = rcu_dereference_protected(tp->ao_info, 1); 318decde258SDmitry Safonov 319decde258SDmitry Safonov if (ao_info) { 320decde258SDmitry Safonov struct tcp_ao_key *key; 321decde258SDmitry Safonov struct hlist_node *n; 322decde258SDmitry Safonov int omem = 0; 323decde258SDmitry Safonov 324decde258SDmitry Safonov hlist_for_each_entry_safe(key, n, &ao_info->head, node) { 325decde258SDmitry Safonov omem += tcp_ao_sizeof_key(key); 326decde258SDmitry Safonov } 327decde258SDmitry Safonov 328decde258SDmitry Safonov refcount_inc(&ao_info->refcnt); 329decde258SDmitry Safonov atomic_sub(omem, &(((struct sock *)tp)->sk_omem_alloc)); 330decde258SDmitry Safonov rcu_assign_pointer(tcptw->ao_info, ao_info); 331decde258SDmitry Safonov } else { 332decde258SDmitry Safonov tcptw->ao_info = NULL; 333decde258SDmitry Safonov } 334decde258SDmitry Safonov } 335decde258SDmitry Safonov 3367c2ffaf2SDmitry Safonov /* 4 tuple and ISNs are expected in NBO */ 3377c2ffaf2SDmitry Safonov static int tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key, 3387c2ffaf2SDmitry Safonov __be32 saddr, __be32 daddr, 3397c2ffaf2SDmitry Safonov __be16 sport, __be16 dport, 3407c2ffaf2SDmitry Safonov __be32 sisn, __be32 disn) 3417c2ffaf2SDmitry Safonov { 3427c2ffaf2SDmitry Safonov /* See RFC5926 3.1.1 */ 3437c2ffaf2SDmitry Safonov struct kdf_input_block { 3447c2ffaf2SDmitry Safonov u8 counter; 3457c2ffaf2SDmitry Safonov u8 label[6]; 3467c2ffaf2SDmitry Safonov struct tcp4_ao_context ctx; 3477c2ffaf2SDmitry Safonov __be16 outlen; 3487c2ffaf2SDmitry Safonov } __packed * tmp; 3497c2ffaf2SDmitry Safonov struct tcp_sigpool hp; 3507c2ffaf2SDmitry Safonov int err; 3517c2ffaf2SDmitry Safonov 3527c2ffaf2SDmitry Safonov err = tcp_sigpool_start(mkt->tcp_sigpool_id, &hp); 3537c2ffaf2SDmitry Safonov if (err) 3547c2ffaf2SDmitry Safonov return err; 3557c2ffaf2SDmitry Safonov 3567c2ffaf2SDmitry Safonov tmp = hp.scratch; 3577c2ffaf2SDmitry Safonov tmp->counter = 1; 3587c2ffaf2SDmitry Safonov memcpy(tmp->label, "TCP-AO", 6); 3597c2ffaf2SDmitry Safonov tmp->ctx.saddr = saddr; 3607c2ffaf2SDmitry Safonov tmp->ctx.daddr = daddr; 3617c2ffaf2SDmitry Safonov tmp->ctx.sport = sport; 3627c2ffaf2SDmitry Safonov tmp->ctx.dport = dport; 3637c2ffaf2SDmitry Safonov tmp->ctx.sisn = sisn; 3647c2ffaf2SDmitry Safonov tmp->ctx.disn = disn; 3657c2ffaf2SDmitry Safonov tmp->outlen = htons(tcp_ao_digest_size(mkt) * 8); /* in bits */ 3667c2ffaf2SDmitry Safonov 3677c2ffaf2SDmitry Safonov err = tcp_ao_calc_traffic_key(mkt, key, tmp, sizeof(*tmp), &hp); 3687c2ffaf2SDmitry Safonov tcp_sigpool_end(&hp); 3697c2ffaf2SDmitry Safonov 3707c2ffaf2SDmitry Safonov return err; 3717c2ffaf2SDmitry Safonov } 3727c2ffaf2SDmitry Safonov 3737c2ffaf2SDmitry Safonov int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, 3747c2ffaf2SDmitry Safonov const struct sock *sk, 3757c2ffaf2SDmitry Safonov __be32 sisn, __be32 disn, bool send) 3767c2ffaf2SDmitry Safonov { 3777c2ffaf2SDmitry Safonov if (send) 3787c2ffaf2SDmitry Safonov return tcp_v4_ao_calc_key(mkt, key, sk->sk_rcv_saddr, 3797c2ffaf2SDmitry Safonov sk->sk_daddr, htons(sk->sk_num), 3807c2ffaf2SDmitry Safonov sk->sk_dport, sisn, disn); 3817c2ffaf2SDmitry Safonov else 3827c2ffaf2SDmitry Safonov return tcp_v4_ao_calc_key(mkt, key, sk->sk_daddr, 3837c2ffaf2SDmitry Safonov sk->sk_rcv_saddr, sk->sk_dport, 3847c2ffaf2SDmitry Safonov htons(sk->sk_num), disn, sisn); 3857c2ffaf2SDmitry Safonov } 3867c2ffaf2SDmitry Safonov 3877c2ffaf2SDmitry Safonov static int tcp_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, 3887c2ffaf2SDmitry Safonov const struct sock *sk, 3897c2ffaf2SDmitry Safonov __be32 sisn, __be32 disn, bool send) 3907c2ffaf2SDmitry Safonov { 3917c2ffaf2SDmitry Safonov if (mkt->family == AF_INET) 3927c2ffaf2SDmitry Safonov return tcp_v4_ao_calc_key_sk(mkt, key, sk, sisn, disn, send); 3937c2ffaf2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 3947c2ffaf2SDmitry Safonov else if (mkt->family == AF_INET6) 3957c2ffaf2SDmitry Safonov return tcp_v6_ao_calc_key_sk(mkt, key, sk, sisn, disn, send); 3967c2ffaf2SDmitry Safonov #endif 3977c2ffaf2SDmitry Safonov else 3987c2ffaf2SDmitry Safonov return -EOPNOTSUPP; 3997c2ffaf2SDmitry Safonov } 4007c2ffaf2SDmitry Safonov 40106b22ef2SDmitry Safonov int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, 40206b22ef2SDmitry Safonov struct request_sock *req) 40306b22ef2SDmitry Safonov { 40406b22ef2SDmitry Safonov struct inet_request_sock *ireq = inet_rsk(req); 40506b22ef2SDmitry Safonov 40606b22ef2SDmitry Safonov return tcp_v4_ao_calc_key(mkt, key, 40706b22ef2SDmitry Safonov ireq->ir_loc_addr, ireq->ir_rmt_addr, 40806b22ef2SDmitry Safonov htons(ireq->ir_num), ireq->ir_rmt_port, 40906b22ef2SDmitry Safonov htonl(tcp_rsk(req)->snt_isn), 41006b22ef2SDmitry Safonov htonl(tcp_rsk(req)->rcv_isn)); 41106b22ef2SDmitry Safonov } 41206b22ef2SDmitry Safonov 41306b22ef2SDmitry Safonov static int tcp_v4_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, 41406b22ef2SDmitry Safonov const struct sk_buff *skb, 41506b22ef2SDmitry Safonov __be32 sisn, __be32 disn) 41606b22ef2SDmitry Safonov { 41706b22ef2SDmitry Safonov const struct iphdr *iph = ip_hdr(skb); 41806b22ef2SDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 41906b22ef2SDmitry Safonov 42006b22ef2SDmitry Safonov return tcp_v4_ao_calc_key(mkt, key, iph->saddr, iph->daddr, 42106b22ef2SDmitry Safonov th->source, th->dest, sisn, disn); 42206b22ef2SDmitry Safonov } 42306b22ef2SDmitry Safonov 42406b22ef2SDmitry Safonov static int tcp_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, 42506b22ef2SDmitry Safonov const struct sk_buff *skb, 42606b22ef2SDmitry Safonov __be32 sisn, __be32 disn, int family) 42706b22ef2SDmitry Safonov { 42806b22ef2SDmitry Safonov if (family == AF_INET) 42906b22ef2SDmitry Safonov return tcp_v4_ao_calc_key_skb(mkt, key, skb, sisn, disn); 43006b22ef2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 43106b22ef2SDmitry Safonov else if (family == AF_INET6) 43206b22ef2SDmitry Safonov return tcp_v6_ao_calc_key_skb(mkt, key, skb, sisn, disn); 43306b22ef2SDmitry Safonov #endif 43406b22ef2SDmitry Safonov return -EAFNOSUPPORT; 43506b22ef2SDmitry Safonov } 43606b22ef2SDmitry Safonov 4371e03d32bSDmitry Safonov static int tcp_v4_ao_hash_pseudoheader(struct tcp_sigpool *hp, 4381e03d32bSDmitry Safonov __be32 daddr, __be32 saddr, 4391e03d32bSDmitry Safonov int nbytes) 4401e03d32bSDmitry Safonov { 4411e03d32bSDmitry Safonov struct tcp4_pseudohdr *bp; 4421e03d32bSDmitry Safonov struct scatterlist sg; 4431e03d32bSDmitry Safonov 4441e03d32bSDmitry Safonov bp = hp->scratch; 4451e03d32bSDmitry Safonov bp->saddr = saddr; 4461e03d32bSDmitry Safonov bp->daddr = daddr; 4471e03d32bSDmitry Safonov bp->pad = 0; 4481e03d32bSDmitry Safonov bp->protocol = IPPROTO_TCP; 4491e03d32bSDmitry Safonov bp->len = cpu_to_be16(nbytes); 4501e03d32bSDmitry Safonov 4511e03d32bSDmitry Safonov sg_init_one(&sg, bp, sizeof(*bp)); 4521e03d32bSDmitry Safonov ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp)); 4531e03d32bSDmitry Safonov return crypto_ahash_update(hp->req); 4541e03d32bSDmitry Safonov } 4551e03d32bSDmitry Safonov 4561e03d32bSDmitry Safonov static int tcp_ao_hash_pseudoheader(unsigned short int family, 4571e03d32bSDmitry Safonov const struct sock *sk, 4581e03d32bSDmitry Safonov const struct sk_buff *skb, 4591e03d32bSDmitry Safonov struct tcp_sigpool *hp, int nbytes) 4601e03d32bSDmitry Safonov { 4611e03d32bSDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 4621e03d32bSDmitry Safonov 4631e03d32bSDmitry Safonov /* TODO: Can we rely on checksum being zero to mean outbound pkt? */ 4641e03d32bSDmitry Safonov if (!th->check) { 4651e03d32bSDmitry Safonov if (family == AF_INET) 4661e03d32bSDmitry Safonov return tcp_v4_ao_hash_pseudoheader(hp, sk->sk_daddr, 4671e03d32bSDmitry Safonov sk->sk_rcv_saddr, skb->len); 4681e03d32bSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 4691e03d32bSDmitry Safonov else if (family == AF_INET6) 4701e03d32bSDmitry Safonov return tcp_v6_ao_hash_pseudoheader(hp, &sk->sk_v6_daddr, 4711e03d32bSDmitry Safonov &sk->sk_v6_rcv_saddr, skb->len); 4721e03d32bSDmitry Safonov #endif 4731e03d32bSDmitry Safonov else 4741e03d32bSDmitry Safonov return -EAFNOSUPPORT; 4751e03d32bSDmitry Safonov } 4761e03d32bSDmitry Safonov 4771e03d32bSDmitry Safonov if (family == AF_INET) { 4781e03d32bSDmitry Safonov const struct iphdr *iph = ip_hdr(skb); 4791e03d32bSDmitry Safonov 4801e03d32bSDmitry Safonov return tcp_v4_ao_hash_pseudoheader(hp, iph->daddr, 4811e03d32bSDmitry Safonov iph->saddr, skb->len); 4821e03d32bSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 4831e03d32bSDmitry Safonov } else if (family == AF_INET6) { 4841e03d32bSDmitry Safonov const struct ipv6hdr *iph = ipv6_hdr(skb); 4851e03d32bSDmitry Safonov 4861e03d32bSDmitry Safonov return tcp_v6_ao_hash_pseudoheader(hp, &iph->daddr, 4871e03d32bSDmitry Safonov &iph->saddr, skb->len); 4881e03d32bSDmitry Safonov #endif 4891e03d32bSDmitry Safonov } 4901e03d32bSDmitry Safonov return -EAFNOSUPPORT; 4911e03d32bSDmitry Safonov } 4921e03d32bSDmitry Safonov 49364382c71SDmitry Safonov u32 tcp_ao_compute_sne(u32 next_sne, u32 next_seq, u32 seq) 49464382c71SDmitry Safonov { 49564382c71SDmitry Safonov u32 sne = next_sne; 49664382c71SDmitry Safonov 49764382c71SDmitry Safonov if (before(seq, next_seq)) { 49864382c71SDmitry Safonov if (seq > next_seq) 49964382c71SDmitry Safonov sne--; 50064382c71SDmitry Safonov } else { 50164382c71SDmitry Safonov if (seq < next_seq) 50264382c71SDmitry Safonov sne++; 50364382c71SDmitry Safonov } 50464382c71SDmitry Safonov 50564382c71SDmitry Safonov return sne; 50664382c71SDmitry Safonov } 50764382c71SDmitry Safonov 5081e03d32bSDmitry Safonov /* tcp_ao_hash_sne(struct tcp_sigpool *hp) 5091e03d32bSDmitry Safonov * @hp - used for hashing 5101e03d32bSDmitry Safonov * @sne - sne value 5111e03d32bSDmitry Safonov */ 5121e03d32bSDmitry Safonov static int tcp_ao_hash_sne(struct tcp_sigpool *hp, u32 sne) 5131e03d32bSDmitry Safonov { 5141e03d32bSDmitry Safonov struct scatterlist sg; 5151e03d32bSDmitry Safonov __be32 *bp; 5161e03d32bSDmitry Safonov 5171e03d32bSDmitry Safonov bp = (__be32 *)hp->scratch; 5181e03d32bSDmitry Safonov *bp = htonl(sne); 5191e03d32bSDmitry Safonov 5201e03d32bSDmitry Safonov sg_init_one(&sg, bp, sizeof(*bp)); 5211e03d32bSDmitry Safonov ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp)); 5221e03d32bSDmitry Safonov return crypto_ahash_update(hp->req); 5231e03d32bSDmitry Safonov } 5241e03d32bSDmitry Safonov 5251e03d32bSDmitry Safonov static int tcp_ao_hash_header(struct tcp_sigpool *hp, 5261e03d32bSDmitry Safonov const struct tcphdr *th, 5271e03d32bSDmitry Safonov bool exclude_options, u8 *hash, 5281e03d32bSDmitry Safonov int hash_offset, int hash_len) 5291e03d32bSDmitry Safonov { 5301e03d32bSDmitry Safonov struct scatterlist sg; 5311e03d32bSDmitry Safonov u8 *hdr = hp->scratch; 532465c1abcSColin Ian King int err, len; 5331e03d32bSDmitry Safonov 5341e03d32bSDmitry Safonov /* We are not allowed to change tcphdr, make a local copy */ 5351e03d32bSDmitry Safonov if (exclude_options) { 5361e03d32bSDmitry Safonov len = sizeof(*th) + sizeof(struct tcp_ao_hdr) + hash_len; 5371e03d32bSDmitry Safonov memcpy(hdr, th, sizeof(*th)); 5381e03d32bSDmitry Safonov memcpy(hdr + sizeof(*th), 5391e03d32bSDmitry Safonov (u8 *)th + hash_offset - sizeof(struct tcp_ao_hdr), 5401e03d32bSDmitry Safonov sizeof(struct tcp_ao_hdr)); 5411e03d32bSDmitry Safonov memset(hdr + sizeof(*th) + sizeof(struct tcp_ao_hdr), 5421e03d32bSDmitry Safonov 0, hash_len); 5431e03d32bSDmitry Safonov ((struct tcphdr *)hdr)->check = 0; 5441e03d32bSDmitry Safonov } else { 5451e03d32bSDmitry Safonov len = th->doff << 2; 5461e03d32bSDmitry Safonov memcpy(hdr, th, len); 5471e03d32bSDmitry Safonov /* zero out tcp-ao hash */ 5481e03d32bSDmitry Safonov ((struct tcphdr *)hdr)->check = 0; 5491e03d32bSDmitry Safonov memset(hdr + hash_offset, 0, hash_len); 5501e03d32bSDmitry Safonov } 5511e03d32bSDmitry Safonov 5521e03d32bSDmitry Safonov sg_init_one(&sg, hdr, len); 5531e03d32bSDmitry Safonov ahash_request_set_crypt(hp->req, &sg, NULL, len); 5541e03d32bSDmitry Safonov err = crypto_ahash_update(hp->req); 5551e03d32bSDmitry Safonov WARN_ON_ONCE(err != 0); 5561e03d32bSDmitry Safonov return err; 5571e03d32bSDmitry Safonov } 5581e03d32bSDmitry Safonov 559ba7783adSDmitry Safonov int tcp_ao_hash_hdr(unsigned short int family, char *ao_hash, 560ba7783adSDmitry Safonov struct tcp_ao_key *key, const u8 *tkey, 561ba7783adSDmitry Safonov const union tcp_ao_addr *daddr, 562ba7783adSDmitry Safonov const union tcp_ao_addr *saddr, 563ba7783adSDmitry Safonov const struct tcphdr *th, u32 sne) 564ba7783adSDmitry Safonov { 565ba7783adSDmitry Safonov int tkey_len = tcp_ao_digest_size(key); 566ba7783adSDmitry Safonov int hash_offset = ao_hash - (char *)th; 567ba7783adSDmitry Safonov struct tcp_sigpool hp; 568ba7783adSDmitry Safonov void *hash_buf = NULL; 569ba7783adSDmitry Safonov 570ba7783adSDmitry Safonov hash_buf = kmalloc(tkey_len, GFP_ATOMIC); 571ba7783adSDmitry Safonov if (!hash_buf) 572ba7783adSDmitry Safonov goto clear_hash_noput; 573ba7783adSDmitry Safonov 574ba7783adSDmitry Safonov if (tcp_sigpool_start(key->tcp_sigpool_id, &hp)) 575ba7783adSDmitry Safonov goto clear_hash_noput; 576ba7783adSDmitry Safonov 577ba7783adSDmitry Safonov if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), tkey, tkey_len)) 578ba7783adSDmitry Safonov goto clear_hash; 579ba7783adSDmitry Safonov 580ba7783adSDmitry Safonov if (crypto_ahash_init(hp.req)) 581ba7783adSDmitry Safonov goto clear_hash; 582ba7783adSDmitry Safonov 583ba7783adSDmitry Safonov if (tcp_ao_hash_sne(&hp, sne)) 584ba7783adSDmitry Safonov goto clear_hash; 585ba7783adSDmitry Safonov if (family == AF_INET) { 586ba7783adSDmitry Safonov if (tcp_v4_ao_hash_pseudoheader(&hp, daddr->a4.s_addr, 587ba7783adSDmitry Safonov saddr->a4.s_addr, th->doff * 4)) 588ba7783adSDmitry Safonov goto clear_hash; 589ba7783adSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 590ba7783adSDmitry Safonov } else if (family == AF_INET6) { 591ba7783adSDmitry Safonov if (tcp_v6_ao_hash_pseudoheader(&hp, &daddr->a6, 592ba7783adSDmitry Safonov &saddr->a6, th->doff * 4)) 593ba7783adSDmitry Safonov goto clear_hash; 594ba7783adSDmitry Safonov #endif 595ba7783adSDmitry Safonov } else { 596ba7783adSDmitry Safonov WARN_ON_ONCE(1); 597ba7783adSDmitry Safonov goto clear_hash; 598ba7783adSDmitry Safonov } 5997753c2f0SDmitry Safonov if (tcp_ao_hash_header(&hp, th, 6007753c2f0SDmitry Safonov !!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT), 601ba7783adSDmitry Safonov ao_hash, hash_offset, tcp_ao_maclen(key))) 602ba7783adSDmitry Safonov goto clear_hash; 603ba7783adSDmitry Safonov ahash_request_set_crypt(hp.req, NULL, hash_buf, 0); 604ba7783adSDmitry Safonov if (crypto_ahash_final(hp.req)) 605ba7783adSDmitry Safonov goto clear_hash; 606ba7783adSDmitry Safonov 607ba7783adSDmitry Safonov memcpy(ao_hash, hash_buf, tcp_ao_maclen(key)); 608ba7783adSDmitry Safonov tcp_sigpool_end(&hp); 609ba7783adSDmitry Safonov kfree(hash_buf); 610ba7783adSDmitry Safonov return 0; 611ba7783adSDmitry Safonov 612ba7783adSDmitry Safonov clear_hash: 613ba7783adSDmitry Safonov tcp_sigpool_end(&hp); 614ba7783adSDmitry Safonov clear_hash_noput: 615ba7783adSDmitry Safonov memset(ao_hash, 0, tcp_ao_maclen(key)); 616ba7783adSDmitry Safonov kfree(hash_buf); 617ba7783adSDmitry Safonov return 1; 618ba7783adSDmitry Safonov } 619ba7783adSDmitry Safonov 6201e03d32bSDmitry Safonov int tcp_ao_hash_skb(unsigned short int family, 6211e03d32bSDmitry Safonov char *ao_hash, struct tcp_ao_key *key, 6221e03d32bSDmitry Safonov const struct sock *sk, const struct sk_buff *skb, 6231e03d32bSDmitry Safonov const u8 *tkey, int hash_offset, u32 sne) 6241e03d32bSDmitry Safonov { 6251e03d32bSDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 6261e03d32bSDmitry Safonov int tkey_len = tcp_ao_digest_size(key); 6271e03d32bSDmitry Safonov struct tcp_sigpool hp; 6281e03d32bSDmitry Safonov void *hash_buf = NULL; 6291e03d32bSDmitry Safonov 6301e03d32bSDmitry Safonov hash_buf = kmalloc(tkey_len, GFP_ATOMIC); 6311e03d32bSDmitry Safonov if (!hash_buf) 6321e03d32bSDmitry Safonov goto clear_hash_noput; 6331e03d32bSDmitry Safonov 6341e03d32bSDmitry Safonov if (tcp_sigpool_start(key->tcp_sigpool_id, &hp)) 6351e03d32bSDmitry Safonov goto clear_hash_noput; 6361e03d32bSDmitry Safonov 6371e03d32bSDmitry Safonov if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), tkey, tkey_len)) 6381e03d32bSDmitry Safonov goto clear_hash; 6391e03d32bSDmitry Safonov 6401e03d32bSDmitry Safonov /* For now use sha1 by default. Depends on alg in tcp_ao_key */ 6411e03d32bSDmitry Safonov if (crypto_ahash_init(hp.req)) 6421e03d32bSDmitry Safonov goto clear_hash; 6431e03d32bSDmitry Safonov 6441e03d32bSDmitry Safonov if (tcp_ao_hash_sne(&hp, sne)) 6451e03d32bSDmitry Safonov goto clear_hash; 6461e03d32bSDmitry Safonov if (tcp_ao_hash_pseudoheader(family, sk, skb, &hp, skb->len)) 6471e03d32bSDmitry Safonov goto clear_hash; 6487753c2f0SDmitry Safonov if (tcp_ao_hash_header(&hp, th, 6497753c2f0SDmitry Safonov !!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT), 6501e03d32bSDmitry Safonov ao_hash, hash_offset, tcp_ao_maclen(key))) 6511e03d32bSDmitry Safonov goto clear_hash; 6521e03d32bSDmitry Safonov if (tcp_sigpool_hash_skb_data(&hp, skb, th->doff << 2)) 6531e03d32bSDmitry Safonov goto clear_hash; 6541e03d32bSDmitry Safonov ahash_request_set_crypt(hp.req, NULL, hash_buf, 0); 6551e03d32bSDmitry Safonov if (crypto_ahash_final(hp.req)) 6561e03d32bSDmitry Safonov goto clear_hash; 6571e03d32bSDmitry Safonov 6581e03d32bSDmitry Safonov memcpy(ao_hash, hash_buf, tcp_ao_maclen(key)); 6591e03d32bSDmitry Safonov tcp_sigpool_end(&hp); 6601e03d32bSDmitry Safonov kfree(hash_buf); 6611e03d32bSDmitry Safonov return 0; 6621e03d32bSDmitry Safonov 6631e03d32bSDmitry Safonov clear_hash: 6641e03d32bSDmitry Safonov tcp_sigpool_end(&hp); 6651e03d32bSDmitry Safonov clear_hash_noput: 6661e03d32bSDmitry Safonov memset(ao_hash, 0, tcp_ao_maclen(key)); 6671e03d32bSDmitry Safonov kfree(hash_buf); 6681e03d32bSDmitry Safonov return 1; 6691e03d32bSDmitry Safonov } 6701e03d32bSDmitry Safonov 6711e03d32bSDmitry Safonov int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key, 6721e03d32bSDmitry Safonov const struct sock *sk, const struct sk_buff *skb, 6731e03d32bSDmitry Safonov const u8 *tkey, int hash_offset, u32 sne) 6741e03d32bSDmitry Safonov { 6751e03d32bSDmitry Safonov return tcp_ao_hash_skb(AF_INET, ao_hash, key, sk, skb, 6761e03d32bSDmitry Safonov tkey, hash_offset, sne); 6771e03d32bSDmitry Safonov } 6781e03d32bSDmitry Safonov 6799427c6aaSDmitry Safonov int tcp_v4_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key, 6809427c6aaSDmitry Safonov struct request_sock *req, const struct sk_buff *skb, 6819427c6aaSDmitry Safonov int hash_offset, u32 sne) 6829427c6aaSDmitry Safonov { 6839427c6aaSDmitry Safonov void *hash_buf = NULL; 6849427c6aaSDmitry Safonov int err; 6859427c6aaSDmitry Safonov 6869427c6aaSDmitry Safonov hash_buf = kmalloc(tcp_ao_digest_size(ao_key), GFP_ATOMIC); 6879427c6aaSDmitry Safonov if (!hash_buf) 6889427c6aaSDmitry Safonov return -ENOMEM; 6899427c6aaSDmitry Safonov 6909427c6aaSDmitry Safonov err = tcp_v4_ao_calc_key_rsk(ao_key, hash_buf, req); 6919427c6aaSDmitry Safonov if (err) 6929427c6aaSDmitry Safonov goto out; 6939427c6aaSDmitry Safonov 6949427c6aaSDmitry Safonov err = tcp_ao_hash_skb(AF_INET, ao_hash, ao_key, req_to_sk(req), skb, 6959427c6aaSDmitry Safonov hash_buf, hash_offset, sne); 6969427c6aaSDmitry Safonov out: 6979427c6aaSDmitry Safonov kfree(hash_buf); 6989427c6aaSDmitry Safonov return err; 6999427c6aaSDmitry Safonov } 7009427c6aaSDmitry Safonov 70106b22ef2SDmitry Safonov struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct sock *sk, 70206b22ef2SDmitry Safonov struct request_sock *req, 70306b22ef2SDmitry Safonov int sndid, int rcvid) 70406b22ef2SDmitry Safonov { 705248411b8SDmitry Safonov struct inet_request_sock *ireq = inet_rsk(req); 706248411b8SDmitry Safonov union tcp_ao_addr *addr = (union tcp_ao_addr *)&ireq->ir_rmt_addr; 707248411b8SDmitry Safonov int l3index; 70806b22ef2SDmitry Safonov 709248411b8SDmitry Safonov l3index = l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif); 710248411b8SDmitry Safonov return tcp_ao_do_lookup(sk, l3index, addr, AF_INET, sndid, rcvid); 71106b22ef2SDmitry Safonov } 71206b22ef2SDmitry Safonov 7130aadc739SDmitry Safonov struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk, 7140aadc739SDmitry Safonov int sndid, int rcvid) 7150aadc739SDmitry Safonov { 716248411b8SDmitry Safonov int l3index = l3mdev_master_ifindex_by_index(sock_net(sk), 717248411b8SDmitry Safonov addr_sk->sk_bound_dev_if); 7180aadc739SDmitry Safonov union tcp_ao_addr *addr = (union tcp_ao_addr *)&addr_sk->sk_daddr; 7190aadc739SDmitry Safonov 720248411b8SDmitry Safonov return tcp_ao_do_lookup(sk, l3index, addr, AF_INET, sndid, rcvid); 7210aadc739SDmitry Safonov } 7220aadc739SDmitry Safonov 723ba7783adSDmitry Safonov int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb, 72464382c71SDmitry Safonov const struct tcp_ao_hdr *aoh, int l3index, u32 seq, 725ba7783adSDmitry Safonov struct tcp_ao_key **key, char **traffic_key, 726ba7783adSDmitry Safonov bool *allocated_traffic_key, u8 *keyid, u32 *sne) 727ba7783adSDmitry Safonov { 72806b22ef2SDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 729ba7783adSDmitry Safonov struct tcp_ao_info *ao_info; 730ba7783adSDmitry Safonov 731ba7783adSDmitry Safonov *allocated_traffic_key = false; 732ba7783adSDmitry Safonov /* If there's no socket - than initial sisn/disn are unknown. 733ba7783adSDmitry Safonov * Drop the segment. RFC5925 (7.7) advises to require graceful 734ba7783adSDmitry Safonov * restart [RFC4724]. Alternatively, the RFC5925 advises to 735ba7783adSDmitry Safonov * save/restore traffic keys before/after reboot. 736ba7783adSDmitry Safonov * Linux TCP-AO support provides TCP_AO_ADD_KEY and TCP_AO_REPAIR 737ba7783adSDmitry Safonov * options to restore a socket post-reboot. 738ba7783adSDmitry Safonov */ 739ba7783adSDmitry Safonov if (!sk) 740ba7783adSDmitry Safonov return -ENOTCONN; 741ba7783adSDmitry Safonov 742decde258SDmitry Safonov if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) { 74306b22ef2SDmitry Safonov unsigned int family = READ_ONCE(sk->sk_family); 74406b22ef2SDmitry Safonov union tcp_ao_addr *addr; 74506b22ef2SDmitry Safonov __be32 disn, sisn; 74606b22ef2SDmitry Safonov 74706b22ef2SDmitry Safonov if (sk->sk_state == TCP_NEW_SYN_RECV) { 74806b22ef2SDmitry Safonov struct request_sock *req = inet_reqsk(sk); 74906b22ef2SDmitry Safonov 75006b22ef2SDmitry Safonov sisn = htonl(tcp_rsk(req)->rcv_isn); 75106b22ef2SDmitry Safonov disn = htonl(tcp_rsk(req)->snt_isn); 75264382c71SDmitry Safonov *sne = tcp_ao_compute_sne(0, tcp_rsk(req)->snt_isn, seq); 75306b22ef2SDmitry Safonov } else { 75406b22ef2SDmitry Safonov sisn = th->seq; 75506b22ef2SDmitry Safonov disn = 0; 75606b22ef2SDmitry Safonov } 75706b22ef2SDmitry Safonov if (IS_ENABLED(CONFIG_IPV6) && family == AF_INET6) 75806b22ef2SDmitry Safonov addr = (union tcp_md5_addr *)&ipv6_hdr(skb)->saddr; 75906b22ef2SDmitry Safonov else 76006b22ef2SDmitry Safonov addr = (union tcp_md5_addr *)&ip_hdr(skb)->saddr; 76106b22ef2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 76206b22ef2SDmitry Safonov if (family == AF_INET6 && ipv6_addr_v4mapped(&sk->sk_v6_daddr)) 76306b22ef2SDmitry Safonov family = AF_INET; 76406b22ef2SDmitry Safonov #endif 76506b22ef2SDmitry Safonov 76606b22ef2SDmitry Safonov sk = sk_const_to_full_sk(sk); 76706b22ef2SDmitry Safonov ao_info = rcu_dereference(tcp_sk(sk)->ao_info); 76806b22ef2SDmitry Safonov if (!ao_info) 76906b22ef2SDmitry Safonov return -ENOENT; 770248411b8SDmitry Safonov *key = tcp_ao_do_lookup(sk, l3index, addr, family, 771248411b8SDmitry Safonov -1, aoh->rnext_keyid); 77206b22ef2SDmitry Safonov if (!*key) 77306b22ef2SDmitry Safonov return -ENOENT; 77406b22ef2SDmitry Safonov *traffic_key = kmalloc(tcp_ao_digest_size(*key), GFP_ATOMIC); 77506b22ef2SDmitry Safonov if (!*traffic_key) 77606b22ef2SDmitry Safonov return -ENOMEM; 77706b22ef2SDmitry Safonov *allocated_traffic_key = true; 77806b22ef2SDmitry Safonov if (tcp_ao_calc_key_skb(*key, *traffic_key, skb, 77906b22ef2SDmitry Safonov sisn, disn, family)) 780ba7783adSDmitry Safonov return -1; 78106b22ef2SDmitry Safonov *keyid = (*key)->rcvid; 78206b22ef2SDmitry Safonov } else { 78306b22ef2SDmitry Safonov struct tcp_ao_key *rnext_key; 78464382c71SDmitry Safonov u32 snd_basis; 785ba7783adSDmitry Safonov 78664382c71SDmitry Safonov if (sk->sk_state == TCP_TIME_WAIT) { 787decde258SDmitry Safonov ao_info = rcu_dereference(tcp_twsk(sk)->ao_info); 78864382c71SDmitry Safonov snd_basis = tcp_twsk(sk)->tw_snd_nxt; 78964382c71SDmitry Safonov } else { 790ba7783adSDmitry Safonov ao_info = rcu_dereference(tcp_sk(sk)->ao_info); 79164382c71SDmitry Safonov snd_basis = tcp_sk(sk)->snd_una; 79264382c71SDmitry Safonov } 793ba7783adSDmitry Safonov if (!ao_info) 794ba7783adSDmitry Safonov return -ENOENT; 795ba7783adSDmitry Safonov 796ba7783adSDmitry Safonov *key = tcp_ao_established_key(ao_info, aoh->rnext_keyid, -1); 797ba7783adSDmitry Safonov if (!*key) 798ba7783adSDmitry Safonov return -ENOENT; 799ba7783adSDmitry Safonov *traffic_key = snd_other_key(*key); 800ba7783adSDmitry Safonov rnext_key = READ_ONCE(ao_info->rnext_key); 801ba7783adSDmitry Safonov *keyid = rnext_key->rcvid; 80264382c71SDmitry Safonov *sne = tcp_ao_compute_sne(READ_ONCE(ao_info->snd_sne), 80364382c71SDmitry Safonov snd_basis, seq); 80406b22ef2SDmitry Safonov } 805ba7783adSDmitry Safonov return 0; 806ba7783adSDmitry Safonov } 807ba7783adSDmitry Safonov 8081e03d32bSDmitry Safonov int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb, 8091e03d32bSDmitry Safonov struct tcp_ao_key *key, struct tcphdr *th, 8101e03d32bSDmitry Safonov __u8 *hash_location) 8111e03d32bSDmitry Safonov { 8121e03d32bSDmitry Safonov struct tcp_skb_cb *tcb = TCP_SKB_CB(skb); 8131e03d32bSDmitry Safonov struct tcp_sock *tp = tcp_sk(sk); 8141e03d32bSDmitry Safonov struct tcp_ao_info *ao; 8151e03d32bSDmitry Safonov void *tkey_buf = NULL; 8161e03d32bSDmitry Safonov u8 *traffic_key; 81764382c71SDmitry Safonov u32 sne; 8181e03d32bSDmitry Safonov 8191e03d32bSDmitry Safonov ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 8201e03d32bSDmitry Safonov lockdep_sock_is_held(sk)); 8211e03d32bSDmitry Safonov traffic_key = snd_other_key(key); 8221e03d32bSDmitry Safonov if (unlikely(tcb->tcp_flags & TCPHDR_SYN)) { 8231e03d32bSDmitry Safonov __be32 disn; 8241e03d32bSDmitry Safonov 8251e03d32bSDmitry Safonov if (!(tcb->tcp_flags & TCPHDR_ACK)) { 8261e03d32bSDmitry Safonov disn = 0; 8271e03d32bSDmitry Safonov tkey_buf = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC); 8281e03d32bSDmitry Safonov if (!tkey_buf) 8291e03d32bSDmitry Safonov return -ENOMEM; 8301e03d32bSDmitry Safonov traffic_key = tkey_buf; 8311e03d32bSDmitry Safonov } else { 8321e03d32bSDmitry Safonov disn = ao->risn; 8331e03d32bSDmitry Safonov } 8341e03d32bSDmitry Safonov tp->af_specific->ao_calc_key_sk(key, traffic_key, 8351e03d32bSDmitry Safonov sk, ao->lisn, disn, true); 8361e03d32bSDmitry Safonov } 83764382c71SDmitry Safonov sne = tcp_ao_compute_sne(READ_ONCE(ao->snd_sne), READ_ONCE(tp->snd_una), 83864382c71SDmitry Safonov ntohl(th->seq)); 8391e03d32bSDmitry Safonov tp->af_specific->calc_ao_hash(hash_location, key, sk, skb, traffic_key, 84064382c71SDmitry Safonov hash_location - (u8 *)th, sne); 8411e03d32bSDmitry Safonov kfree(tkey_buf); 8421e03d32bSDmitry Safonov return 0; 8431e03d32bSDmitry Safonov } 8441e03d32bSDmitry Safonov 84506b22ef2SDmitry Safonov static struct tcp_ao_key *tcp_ao_inbound_lookup(unsigned short int family, 84606b22ef2SDmitry Safonov const struct sock *sk, const struct sk_buff *skb, 847248411b8SDmitry Safonov int sndid, int rcvid, int l3index) 84806b22ef2SDmitry Safonov { 84906b22ef2SDmitry Safonov if (family == AF_INET) { 85006b22ef2SDmitry Safonov const struct iphdr *iph = ip_hdr(skb); 85106b22ef2SDmitry Safonov 852248411b8SDmitry Safonov return tcp_ao_do_lookup(sk, l3index, 853248411b8SDmitry Safonov (union tcp_ao_addr *)&iph->saddr, 85406b22ef2SDmitry Safonov AF_INET, sndid, rcvid); 85506b22ef2SDmitry Safonov } else { 85606b22ef2SDmitry Safonov const struct ipv6hdr *iph = ipv6_hdr(skb); 85706b22ef2SDmitry Safonov 858248411b8SDmitry Safonov return tcp_ao_do_lookup(sk, l3index, 859248411b8SDmitry Safonov (union tcp_ao_addr *)&iph->saddr, 86006b22ef2SDmitry Safonov AF_INET6, sndid, rcvid); 86106b22ef2SDmitry Safonov } 86206b22ef2SDmitry Safonov } 86306b22ef2SDmitry Safonov 86406b22ef2SDmitry Safonov void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, 8657b0f570fSKuniyuki Iwashima struct request_sock *req, unsigned short int family) 86606b22ef2SDmitry Safonov { 8677b0f570fSKuniyuki Iwashima struct tcp_request_sock *treq = tcp_rsk(req); 86806b22ef2SDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 86906b22ef2SDmitry Safonov const struct tcp_ao_hdr *aoh; 87006b22ef2SDmitry Safonov struct tcp_ao_key *key; 8717b0f570fSKuniyuki Iwashima int l3index; 8727b0f570fSKuniyuki Iwashima 8737b0f570fSKuniyuki Iwashima /* treq->af_specific is used to perform TCP_AO lookup 8747b0f570fSKuniyuki Iwashima * in tcp_create_openreq_child(). 8757b0f570fSKuniyuki Iwashima */ 8767b0f570fSKuniyuki Iwashima #if IS_ENABLED(CONFIG_IPV6) 8777b0f570fSKuniyuki Iwashima if (family == AF_INET6) 8787b0f570fSKuniyuki Iwashima treq->af_specific = &tcp_request_sock_ipv6_ops; 8797b0f570fSKuniyuki Iwashima else 8807b0f570fSKuniyuki Iwashima #endif 8817b0f570fSKuniyuki Iwashima treq->af_specific = &tcp_request_sock_ipv4_ops; 88206b22ef2SDmitry Safonov 8839396c4eeSDmitry Safonov treq->used_tcp_ao = false; 88406b22ef2SDmitry Safonov 88506b22ef2SDmitry Safonov if (tcp_parse_auth_options(th, NULL, &aoh) || !aoh) 88606b22ef2SDmitry Safonov return; 88706b22ef2SDmitry Safonov 8887b0f570fSKuniyuki Iwashima l3index = l3mdev_master_ifindex_by_index(sock_net(sk), inet_rsk(req)->ir_iif); 889248411b8SDmitry Safonov key = tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid, l3index); 89006b22ef2SDmitry Safonov if (!key) 89106b22ef2SDmitry Safonov /* Key not found, continue without TCP-AO */ 89206b22ef2SDmitry Safonov return; 89306b22ef2SDmitry Safonov 89406b22ef2SDmitry Safonov treq->ao_rcv_next = aoh->keyid; 89506b22ef2SDmitry Safonov treq->ao_keyid = aoh->rnext_keyid; 8969396c4eeSDmitry Safonov treq->used_tcp_ao = true; 89706b22ef2SDmitry Safonov } 89806b22ef2SDmitry Safonov 8990a3a8090SDmitry Safonov static enum skb_drop_reason 9000a3a8090SDmitry Safonov tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb, 9010a3a8090SDmitry Safonov unsigned short int family, struct tcp_ao_info *info, 9020a3a8090SDmitry Safonov const struct tcp_ao_hdr *aoh, struct tcp_ao_key *key, 903248411b8SDmitry Safonov u8 *traffic_key, u8 *phash, u32 sne, int l3index) 9040a3a8090SDmitry Safonov { 9050a3a8090SDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 90672863087SDmitry Safonov u8 maclen = tcp_ao_hdr_maclen(aoh); 9070a3a8090SDmitry Safonov void *hash_buf = NULL; 9080a3a8090SDmitry Safonov 909af09a341SDmitry Safonov if (maclen != tcp_ao_maclen(key)) { 910af09a341SDmitry Safonov NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD); 911af09a341SDmitry Safonov atomic64_inc(&info->counters.pkt_bad); 912af09a341SDmitry Safonov atomic64_inc(&key->pkt_bad); 91396be3dcdSDmitry Safonov trace_tcp_ao_wrong_maclen(sk, skb, aoh->keyid, 91496be3dcdSDmitry Safonov aoh->rnext_keyid, maclen); 9150a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_AOFAILURE; 916af09a341SDmitry Safonov } 9170a3a8090SDmitry Safonov 9180a3a8090SDmitry Safonov hash_buf = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC); 9190a3a8090SDmitry Safonov if (!hash_buf) 9200a3a8090SDmitry Safonov return SKB_DROP_REASON_NOT_SPECIFIED; 9210a3a8090SDmitry Safonov 9220a3a8090SDmitry Safonov /* XXX: make it per-AF callback? */ 9230a3a8090SDmitry Safonov tcp_ao_hash_skb(family, hash_buf, key, sk, skb, traffic_key, 9240a3a8090SDmitry Safonov (phash - (u8 *)th), sne); 9250a3a8090SDmitry Safonov if (memcmp(phash, hash_buf, maclen)) { 926af09a341SDmitry Safonov NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD); 927af09a341SDmitry Safonov atomic64_inc(&info->counters.pkt_bad); 928af09a341SDmitry Safonov atomic64_inc(&key->pkt_bad); 92996be3dcdSDmitry Safonov trace_tcp_ao_mismatch(sk, skb, aoh->keyid, 93096be3dcdSDmitry Safonov aoh->rnext_keyid, maclen); 9310a3a8090SDmitry Safonov kfree(hash_buf); 9320a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_AOFAILURE; 9330a3a8090SDmitry Safonov } 934af09a341SDmitry Safonov NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOGOOD); 935af09a341SDmitry Safonov atomic64_inc(&info->counters.pkt_good); 936af09a341SDmitry Safonov atomic64_inc(&key->pkt_good); 9370a3a8090SDmitry Safonov kfree(hash_buf); 9380a3a8090SDmitry Safonov return SKB_NOT_DROPPED_YET; 9390a3a8090SDmitry Safonov } 9400a3a8090SDmitry Safonov 9410a3a8090SDmitry Safonov enum skb_drop_reason 9420a3a8090SDmitry Safonov tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, 9430a3a8090SDmitry Safonov unsigned short int family, const struct request_sock *req, 944248411b8SDmitry Safonov int l3index, const struct tcp_ao_hdr *aoh) 9450a3a8090SDmitry Safonov { 9460a3a8090SDmitry Safonov const struct tcphdr *th = tcp_hdr(skb); 94796be3dcdSDmitry Safonov u8 maclen = tcp_ao_hdr_maclen(aoh); 9480a3a8090SDmitry Safonov u8 *phash = (u8 *)(aoh + 1); /* hash goes just after the header */ 9490a3a8090SDmitry Safonov struct tcp_ao_info *info; 9500a3a8090SDmitry Safonov enum skb_drop_reason ret; 9510a3a8090SDmitry Safonov struct tcp_ao_key *key; 9520a3a8090SDmitry Safonov __be32 sisn, disn; 9530a3a8090SDmitry Safonov u8 *traffic_key; 95433700a0cSDmitry Safonov int state; 9550a3a8090SDmitry Safonov u32 sne = 0; 9560a3a8090SDmitry Safonov 9570a3a8090SDmitry Safonov info = rcu_dereference(tcp_sk(sk)->ao_info); 958af09a341SDmitry Safonov if (!info) { 959af09a341SDmitry Safonov NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND); 96096be3dcdSDmitry Safonov trace_tcp_ao_key_not_found(sk, skb, aoh->keyid, 96196be3dcdSDmitry Safonov aoh->rnext_keyid, maclen); 9620a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_AOUNEXPECTED; 963af09a341SDmitry Safonov } 9640a3a8090SDmitry Safonov 9650a3a8090SDmitry Safonov if (unlikely(th->syn)) { 9660a3a8090SDmitry Safonov sisn = th->seq; 9670a3a8090SDmitry Safonov disn = 0; 9680a3a8090SDmitry Safonov } 9690a3a8090SDmitry Safonov 97033700a0cSDmitry Safonov state = READ_ONCE(sk->sk_state); 9710a3a8090SDmitry Safonov /* Fast-path */ 97233700a0cSDmitry Safonov if (likely((1 << state) & TCP_AO_ESTABLISHED)) { 9730a3a8090SDmitry Safonov enum skb_drop_reason err; 9740a3a8090SDmitry Safonov struct tcp_ao_key *current_key; 9750a3a8090SDmitry Safonov 9760a3a8090SDmitry Safonov /* Check if this socket's rnext_key matches the keyid in the 9770a3a8090SDmitry Safonov * packet. If not we lookup the key based on the keyid 9780a3a8090SDmitry Safonov * matching the rcvid in the mkt. 9790a3a8090SDmitry Safonov */ 9800a3a8090SDmitry Safonov key = READ_ONCE(info->rnext_key); 9810a3a8090SDmitry Safonov if (key->rcvid != aoh->keyid) { 9820a3a8090SDmitry Safonov key = tcp_ao_established_key(info, -1, aoh->keyid); 9830a3a8090SDmitry Safonov if (!key) 9840a3a8090SDmitry Safonov goto key_not_found; 9850a3a8090SDmitry Safonov } 9860a3a8090SDmitry Safonov 9870a3a8090SDmitry Safonov /* Delayed retransmitted SYN */ 9880a3a8090SDmitry Safonov if (unlikely(th->syn && !th->ack)) 9890a3a8090SDmitry Safonov goto verify_hash; 9900a3a8090SDmitry Safonov 99164382c71SDmitry Safonov sne = tcp_ao_compute_sne(info->rcv_sne, tcp_sk(sk)->rcv_nxt, 99264382c71SDmitry Safonov ntohl(th->seq)); 9930a3a8090SDmitry Safonov /* Established socket, traffic key are cached */ 9940a3a8090SDmitry Safonov traffic_key = rcv_other_key(key); 9950a3a8090SDmitry Safonov err = tcp_ao_verify_hash(sk, skb, family, info, aoh, key, 996248411b8SDmitry Safonov traffic_key, phash, sne, l3index); 9970a3a8090SDmitry Safonov if (err) 9980a3a8090SDmitry Safonov return err; 9990a3a8090SDmitry Safonov current_key = READ_ONCE(info->current_key); 10000a3a8090SDmitry Safonov /* Key rotation: the peer asks us to use new key (RNext) */ 10010a3a8090SDmitry Safonov if (unlikely(aoh->rnext_keyid != current_key->sndid)) { 100296be3dcdSDmitry Safonov trace_tcp_ao_rnext_request(sk, skb, current_key->sndid, 100396be3dcdSDmitry Safonov aoh->rnext_keyid, 100496be3dcdSDmitry Safonov tcp_ao_hdr_maclen(aoh)); 10050a3a8090SDmitry Safonov /* If the key is not found we do nothing. */ 10060a3a8090SDmitry Safonov key = tcp_ao_established_key(info, aoh->rnext_keyid, -1); 10070a3a8090SDmitry Safonov if (key) 10080a3a8090SDmitry Safonov /* pairs with tcp_ao_del_cmd */ 10090a3a8090SDmitry Safonov WRITE_ONCE(info->current_key, key); 10100a3a8090SDmitry Safonov } 10110a3a8090SDmitry Safonov return SKB_NOT_DROPPED_YET; 10120a3a8090SDmitry Safonov } 10130a3a8090SDmitry Safonov 101433700a0cSDmitry Safonov if (unlikely(state == TCP_CLOSE)) 101533700a0cSDmitry Safonov return SKB_DROP_REASON_TCP_CLOSE; 101633700a0cSDmitry Safonov 10170a3a8090SDmitry Safonov /* Lookup key based on peer address and keyid. 10180a3a8090SDmitry Safonov * current_key and rnext_key must not be used on tcp listen 10190a3a8090SDmitry Safonov * sockets as otherwise: 10200a3a8090SDmitry Safonov * - request sockets would race on those key pointers 10210a3a8090SDmitry Safonov * - tcp_ao_del_cmd() allows async key removal 10220a3a8090SDmitry Safonov */ 1023248411b8SDmitry Safonov key = tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid, l3index); 10240a3a8090SDmitry Safonov if (!key) 10250a3a8090SDmitry Safonov goto key_not_found; 10260a3a8090SDmitry Safonov 10270a3a8090SDmitry Safonov if (th->syn && !th->ack) 10280a3a8090SDmitry Safonov goto verify_hash; 10290a3a8090SDmitry Safonov 103033700a0cSDmitry Safonov if ((1 << state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) { 10310a3a8090SDmitry Safonov /* Make the initial syn the likely case here */ 10320a3a8090SDmitry Safonov if (unlikely(req)) { 103364382c71SDmitry Safonov sne = tcp_ao_compute_sne(0, tcp_rsk(req)->rcv_isn, 103464382c71SDmitry Safonov ntohl(th->seq)); 10350a3a8090SDmitry Safonov sisn = htonl(tcp_rsk(req)->rcv_isn); 10360a3a8090SDmitry Safonov disn = htonl(tcp_rsk(req)->snt_isn); 10370a3a8090SDmitry Safonov } else if (unlikely(th->ack && !th->syn)) { 10380a3a8090SDmitry Safonov /* Possible syncookie packet */ 10390a3a8090SDmitry Safonov sisn = htonl(ntohl(th->seq) - 1); 10400a3a8090SDmitry Safonov disn = htonl(ntohl(th->ack_seq) - 1); 104164382c71SDmitry Safonov sne = tcp_ao_compute_sne(0, ntohl(sisn), 104264382c71SDmitry Safonov ntohl(th->seq)); 10430a3a8090SDmitry Safonov } else if (unlikely(!th->syn)) { 10440a3a8090SDmitry Safonov /* no way to figure out initial sisn/disn - drop */ 10450a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_FLAGS; 10460a3a8090SDmitry Safonov } 104733700a0cSDmitry Safonov } else if ((1 << state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) { 10480a3a8090SDmitry Safonov disn = info->lisn; 10490a3a8090SDmitry Safonov if (th->syn || th->rst) 10500a3a8090SDmitry Safonov sisn = th->seq; 10510a3a8090SDmitry Safonov else 10520a3a8090SDmitry Safonov sisn = info->risn; 10530a3a8090SDmitry Safonov } else { 105433700a0cSDmitry Safonov WARN_ONCE(1, "TCP-AO: Unexpected sk_state %d", state); 10550a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_AOFAILURE; 10560a3a8090SDmitry Safonov } 10570a3a8090SDmitry Safonov verify_hash: 10580a3a8090SDmitry Safonov traffic_key = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC); 10590a3a8090SDmitry Safonov if (!traffic_key) 10600a3a8090SDmitry Safonov return SKB_DROP_REASON_NOT_SPECIFIED; 10610a3a8090SDmitry Safonov tcp_ao_calc_key_skb(key, traffic_key, skb, sisn, disn, family); 10620a3a8090SDmitry Safonov ret = tcp_ao_verify_hash(sk, skb, family, info, aoh, key, 1063248411b8SDmitry Safonov traffic_key, phash, sne, l3index); 10640a3a8090SDmitry Safonov kfree(traffic_key); 10650a3a8090SDmitry Safonov return ret; 10660a3a8090SDmitry Safonov 10670a3a8090SDmitry Safonov key_not_found: 1068af09a341SDmitry Safonov NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND); 1069af09a341SDmitry Safonov atomic64_inc(&info->counters.key_not_found); 107096be3dcdSDmitry Safonov trace_tcp_ao_key_not_found(sk, skb, aoh->keyid, 107196be3dcdSDmitry Safonov aoh->rnext_keyid, maclen); 10720a3a8090SDmitry Safonov return SKB_DROP_REASON_TCP_AOKEYNOTFOUND; 10730a3a8090SDmitry Safonov } 10740a3a8090SDmitry Safonov 10757c2ffaf2SDmitry Safonov static int tcp_ao_cache_traffic_keys(const struct sock *sk, 10767c2ffaf2SDmitry Safonov struct tcp_ao_info *ao, 10777c2ffaf2SDmitry Safonov struct tcp_ao_key *ao_key) 10787c2ffaf2SDmitry Safonov { 10797c2ffaf2SDmitry Safonov u8 *traffic_key = snd_other_key(ao_key); 10807c2ffaf2SDmitry Safonov int ret; 10817c2ffaf2SDmitry Safonov 10827c2ffaf2SDmitry Safonov ret = tcp_ao_calc_key_sk(ao_key, traffic_key, sk, 10837c2ffaf2SDmitry Safonov ao->lisn, ao->risn, true); 10847c2ffaf2SDmitry Safonov if (ret) 10857c2ffaf2SDmitry Safonov return ret; 10867c2ffaf2SDmitry Safonov 10877c2ffaf2SDmitry Safonov traffic_key = rcv_other_key(ao_key); 10887c2ffaf2SDmitry Safonov ret = tcp_ao_calc_key_sk(ao_key, traffic_key, sk, 10897c2ffaf2SDmitry Safonov ao->lisn, ao->risn, false); 10907c2ffaf2SDmitry Safonov return ret; 10917c2ffaf2SDmitry Safonov } 10927c2ffaf2SDmitry Safonov 10937c2ffaf2SDmitry Safonov void tcp_ao_connect_init(struct sock *sk) 10947c2ffaf2SDmitry Safonov { 10957c2ffaf2SDmitry Safonov struct tcp_sock *tp = tcp_sk(sk); 10967c2ffaf2SDmitry Safonov struct tcp_ao_info *ao_info; 109780e679b3SHyunwoo Kim struct hlist_node *next; 10987c2ffaf2SDmitry Safonov union tcp_ao_addr *addr; 10997c2ffaf2SDmitry Safonov struct tcp_ao_key *key; 1100248411b8SDmitry Safonov int family, l3index; 11017c2ffaf2SDmitry Safonov 11027c2ffaf2SDmitry Safonov ao_info = rcu_dereference_protected(tp->ao_info, 11037c2ffaf2SDmitry Safonov lockdep_sock_is_held(sk)); 11047c2ffaf2SDmitry Safonov if (!ao_info) 11057c2ffaf2SDmitry Safonov return; 11067c2ffaf2SDmitry Safonov 11077c2ffaf2SDmitry Safonov /* Remove all keys that don't match the peer */ 11087c2ffaf2SDmitry Safonov family = sk->sk_family; 11097c2ffaf2SDmitry Safonov if (family == AF_INET) 11107c2ffaf2SDmitry Safonov addr = (union tcp_ao_addr *)&sk->sk_daddr; 11117c2ffaf2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 11127c2ffaf2SDmitry Safonov else if (family == AF_INET6) 11137c2ffaf2SDmitry Safonov addr = (union tcp_ao_addr *)&sk->sk_v6_daddr; 11147c2ffaf2SDmitry Safonov #endif 11157c2ffaf2SDmitry Safonov else 11167c2ffaf2SDmitry Safonov return; 1117248411b8SDmitry Safonov l3index = l3mdev_master_ifindex_by_index(sock_net(sk), 1118248411b8SDmitry Safonov sk->sk_bound_dev_if); 11197c2ffaf2SDmitry Safonov 112080e679b3SHyunwoo Kim hlist_for_each_entry_safe(key, next, &ao_info->head, node) { 1121248411b8SDmitry Safonov if (!tcp_ao_key_cmp(key, l3index, addr, key->prefixlen, family, -1, -1)) 11227c2ffaf2SDmitry Safonov continue; 11237c2ffaf2SDmitry Safonov 11247c2ffaf2SDmitry Safonov if (key == ao_info->current_key) 11257c2ffaf2SDmitry Safonov ao_info->current_key = NULL; 11267c2ffaf2SDmitry Safonov if (key == ao_info->rnext_key) 11277c2ffaf2SDmitry Safonov ao_info->rnext_key = NULL; 11287c2ffaf2SDmitry Safonov hlist_del_rcu(&key->node); 11297c2ffaf2SDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 11307c2ffaf2SDmitry Safonov call_rcu(&key->rcu, tcp_ao_key_free_rcu); 11317c2ffaf2SDmitry Safonov } 11327c2ffaf2SDmitry Safonov 11337c2ffaf2SDmitry Safonov key = tp->af_specific->ao_lookup(sk, sk, -1, -1); 11347c2ffaf2SDmitry Safonov if (key) { 11357c2ffaf2SDmitry Safonov /* if current_key or rnext_key were not provided, 11367c2ffaf2SDmitry Safonov * use the first key matching the peer 11377c2ffaf2SDmitry Safonov */ 11387c2ffaf2SDmitry Safonov if (!ao_info->current_key) 11397c2ffaf2SDmitry Safonov ao_info->current_key = key; 11407c2ffaf2SDmitry Safonov if (!ao_info->rnext_key) 11417c2ffaf2SDmitry Safonov ao_info->rnext_key = key; 1142da7dfaa6SDmitry Safonov tp->tcp_header_len += tcp_ao_len_aligned(key); 11437c2ffaf2SDmitry Safonov 11447c2ffaf2SDmitry Safonov ao_info->lisn = htonl(tp->write_seq); 114564382c71SDmitry Safonov ao_info->snd_sne = 0; 11467c2ffaf2SDmitry Safonov } else { 11477c2ffaf2SDmitry Safonov /* Can't happen: tcp_connect() verifies that there's 11487c2ffaf2SDmitry Safonov * at least one tcp-ao key that matches the remote peer. 11497c2ffaf2SDmitry Safonov */ 11507c2ffaf2SDmitry Safonov WARN_ON_ONCE(1); 11517c2ffaf2SDmitry Safonov rcu_assign_pointer(tp->ao_info, NULL); 11527c2ffaf2SDmitry Safonov kfree(ao_info); 11537c2ffaf2SDmitry Safonov } 11547c2ffaf2SDmitry Safonov } 11557c2ffaf2SDmitry Safonov 11567c2ffaf2SDmitry Safonov void tcp_ao_established(struct sock *sk) 11577c2ffaf2SDmitry Safonov { 11587c2ffaf2SDmitry Safonov struct tcp_ao_info *ao; 11597c2ffaf2SDmitry Safonov struct tcp_ao_key *key; 11607c2ffaf2SDmitry Safonov 11617c2ffaf2SDmitry Safonov ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 11627c2ffaf2SDmitry Safonov lockdep_sock_is_held(sk)); 11637c2ffaf2SDmitry Safonov if (!ao) 11647c2ffaf2SDmitry Safonov return; 11657c2ffaf2SDmitry Safonov 11667c2ffaf2SDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) 11677c2ffaf2SDmitry Safonov tcp_ao_cache_traffic_keys(sk, ao, key); 11687c2ffaf2SDmitry Safonov } 11697c2ffaf2SDmitry Safonov 11707c2ffaf2SDmitry Safonov void tcp_ao_finish_connect(struct sock *sk, struct sk_buff *skb) 11717c2ffaf2SDmitry Safonov { 11727c2ffaf2SDmitry Safonov struct tcp_ao_info *ao; 11737c2ffaf2SDmitry Safonov struct tcp_ao_key *key; 11747c2ffaf2SDmitry Safonov 11757c2ffaf2SDmitry Safonov ao = rcu_dereference_protected(tcp_sk(sk)->ao_info, 11767c2ffaf2SDmitry Safonov lockdep_sock_is_held(sk)); 11777c2ffaf2SDmitry Safonov if (!ao) 11787c2ffaf2SDmitry Safonov return; 11797c2ffaf2SDmitry Safonov 11807c2ffaf2SDmitry Safonov WRITE_ONCE(ao->risn, tcp_hdr(skb)->seq); 118164382c71SDmitry Safonov ao->rcv_sne = 0; 11827c2ffaf2SDmitry Safonov 11837c2ffaf2SDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) 11847c2ffaf2SDmitry Safonov tcp_ao_cache_traffic_keys(sk, ao, key); 11857c2ffaf2SDmitry Safonov } 11867c2ffaf2SDmitry Safonov 118706b22ef2SDmitry Safonov int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk, 118806b22ef2SDmitry Safonov struct request_sock *req, struct sk_buff *skb, 118906b22ef2SDmitry Safonov int family) 119006b22ef2SDmitry Safonov { 119106b22ef2SDmitry Safonov struct tcp_ao_key *key, *new_key, *first_key; 119206b22ef2SDmitry Safonov struct tcp_ao_info *new_ao, *ao; 119306b22ef2SDmitry Safonov struct hlist_node *key_head; 1194248411b8SDmitry Safonov int l3index, ret = -ENOMEM; 119506b22ef2SDmitry Safonov union tcp_ao_addr *addr; 119606b22ef2SDmitry Safonov bool match = false; 119706b22ef2SDmitry Safonov 119806b22ef2SDmitry Safonov ao = rcu_dereference(tcp_sk(sk)->ao_info); 119906b22ef2SDmitry Safonov if (!ao) 120006b22ef2SDmitry Safonov return 0; 120106b22ef2SDmitry Safonov 120206b22ef2SDmitry Safonov /* New socket without TCP-AO on it */ 120306b22ef2SDmitry Safonov if (!tcp_rsk_used_ao(req)) 120406b22ef2SDmitry Safonov return 0; 120506b22ef2SDmitry Safonov 120606b22ef2SDmitry Safonov new_ao = tcp_ao_alloc_info(GFP_ATOMIC); 120706b22ef2SDmitry Safonov if (!new_ao) 120806b22ef2SDmitry Safonov return -ENOMEM; 120906b22ef2SDmitry Safonov new_ao->lisn = htonl(tcp_rsk(req)->snt_isn); 121006b22ef2SDmitry Safonov new_ao->risn = htonl(tcp_rsk(req)->rcv_isn); 121106b22ef2SDmitry Safonov new_ao->ao_required = ao->ao_required; 1212953af8e3SDmitry Safonov new_ao->accept_icmps = ao->accept_icmps; 121306b22ef2SDmitry Safonov 121406b22ef2SDmitry Safonov if (family == AF_INET) { 121506b22ef2SDmitry Safonov addr = (union tcp_ao_addr *)&newsk->sk_daddr; 121606b22ef2SDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 121706b22ef2SDmitry Safonov } else if (family == AF_INET6) { 121806b22ef2SDmitry Safonov addr = (union tcp_ao_addr *)&newsk->sk_v6_daddr; 121906b22ef2SDmitry Safonov #endif 122006b22ef2SDmitry Safonov } else { 122106b22ef2SDmitry Safonov ret = -EAFNOSUPPORT; 122206b22ef2SDmitry Safonov goto free_ao; 122306b22ef2SDmitry Safonov } 1224248411b8SDmitry Safonov l3index = l3mdev_master_ifindex_by_index(sock_net(newsk), 1225248411b8SDmitry Safonov newsk->sk_bound_dev_if); 122606b22ef2SDmitry Safonov 122706b22ef2SDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) { 1228248411b8SDmitry Safonov if (tcp_ao_key_cmp(key, l3index, addr, key->prefixlen, family, -1, -1)) 122906b22ef2SDmitry Safonov continue; 123006b22ef2SDmitry Safonov 123106b22ef2SDmitry Safonov new_key = tcp_ao_copy_key(newsk, key); 123206b22ef2SDmitry Safonov if (!new_key) 123306b22ef2SDmitry Safonov goto free_and_exit; 123406b22ef2SDmitry Safonov 123506b22ef2SDmitry Safonov tcp_ao_cache_traffic_keys(newsk, new_ao, new_key); 123606b22ef2SDmitry Safonov tcp_ao_link_mkt(new_ao, new_key); 123706b22ef2SDmitry Safonov match = true; 123806b22ef2SDmitry Safonov } 123906b22ef2SDmitry Safonov 124006b22ef2SDmitry Safonov if (!match) { 124106b22ef2SDmitry Safonov /* RFC5925 (7.4.1) specifies that the TCP-AO status 124206b22ef2SDmitry Safonov * of a connection is determined on the initial SYN. 124306b22ef2SDmitry Safonov * At this point the connection was TCP-AO enabled, so 124406b22ef2SDmitry Safonov * it can't switch to being unsigned if peer's key 124506b22ef2SDmitry Safonov * disappears on the listening socket. 124606b22ef2SDmitry Safonov */ 124706b22ef2SDmitry Safonov ret = -EKEYREJECTED; 124806b22ef2SDmitry Safonov goto free_and_exit; 124906b22ef2SDmitry Safonov } 125006b22ef2SDmitry Safonov 125167fa83f7SDmitry Safonov if (!static_key_fast_inc_not_disabled(&tcp_ao_needed.key.key)) { 125267fa83f7SDmitry Safonov ret = -EUSERS; 125367fa83f7SDmitry Safonov goto free_and_exit; 125467fa83f7SDmitry Safonov } 125567fa83f7SDmitry Safonov 125606b22ef2SDmitry Safonov key_head = rcu_dereference(hlist_first_rcu(&new_ao->head)); 125706b22ef2SDmitry Safonov first_key = hlist_entry_safe(key_head, struct tcp_ao_key, node); 125806b22ef2SDmitry Safonov 125906b22ef2SDmitry Safonov key = tcp_ao_established_key(new_ao, tcp_rsk(req)->ao_keyid, -1); 126006b22ef2SDmitry Safonov if (key) 126106b22ef2SDmitry Safonov new_ao->current_key = key; 126206b22ef2SDmitry Safonov else 126306b22ef2SDmitry Safonov new_ao->current_key = first_key; 126406b22ef2SDmitry Safonov 126506b22ef2SDmitry Safonov /* set rnext_key */ 126606b22ef2SDmitry Safonov key = tcp_ao_established_key(new_ao, -1, tcp_rsk(req)->ao_rcv_next); 126706b22ef2SDmitry Safonov if (key) 126806b22ef2SDmitry Safonov new_ao->rnext_key = key; 126906b22ef2SDmitry Safonov else 127006b22ef2SDmitry Safonov new_ao->rnext_key = first_key; 127106b22ef2SDmitry Safonov 127206b22ef2SDmitry Safonov sk_gso_disable(newsk); 127306b22ef2SDmitry Safonov rcu_assign_pointer(tcp_sk(newsk)->ao_info, new_ao); 127406b22ef2SDmitry Safonov 127506b22ef2SDmitry Safonov return 0; 127606b22ef2SDmitry Safonov 127706b22ef2SDmitry Safonov free_and_exit: 127806b22ef2SDmitry Safonov hlist_for_each_entry_safe(key, key_head, &new_ao->head, node) { 127906b22ef2SDmitry Safonov hlist_del(&key->node); 128006b22ef2SDmitry Safonov tcp_sigpool_release(key->tcp_sigpool_id); 128106b22ef2SDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &newsk->sk_omem_alloc); 128206b22ef2SDmitry Safonov kfree_sensitive(key); 128306b22ef2SDmitry Safonov } 128406b22ef2SDmitry Safonov free_ao: 128506b22ef2SDmitry Safonov kfree(new_ao); 128606b22ef2SDmitry Safonov return ret; 128706b22ef2SDmitry Safonov } 128806b22ef2SDmitry Safonov 12894954f17dSDmitry Safonov static bool tcp_ao_can_set_current_rnext(struct sock *sk) 12904954f17dSDmitry Safonov { 12914954f17dSDmitry Safonov /* There aren't current/rnext keys on TCP_LISTEN sockets */ 12924954f17dSDmitry Safonov if (sk->sk_state == TCP_LISTEN) 12934954f17dSDmitry Safonov return false; 12944954f17dSDmitry Safonov return true; 12954954f17dSDmitry Safonov } 12964954f17dSDmitry Safonov 12974954f17dSDmitry Safonov static int tcp_ao_verify_ipv4(struct sock *sk, struct tcp_ao_add *cmd, 12984954f17dSDmitry Safonov union tcp_ao_addr **addr) 12994954f17dSDmitry Safonov { 13004954f17dSDmitry Safonov struct sockaddr_in *sin = (struct sockaddr_in *)&cmd->addr; 13014954f17dSDmitry Safonov struct inet_sock *inet = inet_sk(sk); 13024954f17dSDmitry Safonov 13034954f17dSDmitry Safonov if (sin->sin_family != AF_INET) 13044954f17dSDmitry Safonov return -EINVAL; 13054954f17dSDmitry Safonov 13064954f17dSDmitry Safonov /* Currently matching is not performed on port (or port ranges) */ 13074954f17dSDmitry Safonov if (sin->sin_port != 0) 13084954f17dSDmitry Safonov return -EINVAL; 13094954f17dSDmitry Safonov 13104954f17dSDmitry Safonov /* Check prefix and trailing 0's in addr */ 13114954f17dSDmitry Safonov if (cmd->prefix != 0) { 13124954f17dSDmitry Safonov __be32 mask; 13134954f17dSDmitry Safonov 13144954f17dSDmitry Safonov if (ntohl(sin->sin_addr.s_addr) == INADDR_ANY) 13154954f17dSDmitry Safonov return -EINVAL; 13164954f17dSDmitry Safonov if (cmd->prefix > 32) 13174954f17dSDmitry Safonov return -EINVAL; 13184954f17dSDmitry Safonov 13194954f17dSDmitry Safonov mask = inet_make_mask(cmd->prefix); 13204954f17dSDmitry Safonov if (sin->sin_addr.s_addr & ~mask) 13214954f17dSDmitry Safonov return -EINVAL; 13224954f17dSDmitry Safonov 13234954f17dSDmitry Safonov /* Check that MKT address is consistent with socket */ 13244954f17dSDmitry Safonov if (ntohl(inet->inet_daddr) != INADDR_ANY && 13254954f17dSDmitry Safonov (inet->inet_daddr & mask) != sin->sin_addr.s_addr) 13264954f17dSDmitry Safonov return -EINVAL; 13274954f17dSDmitry Safonov } else { 13284954f17dSDmitry Safonov if (ntohl(sin->sin_addr.s_addr) != INADDR_ANY) 13294954f17dSDmitry Safonov return -EINVAL; 13304954f17dSDmitry Safonov } 13314954f17dSDmitry Safonov 13324954f17dSDmitry Safonov *addr = (union tcp_ao_addr *)&sin->sin_addr; 13334954f17dSDmitry Safonov return 0; 13344954f17dSDmitry Safonov } 13354954f17dSDmitry Safonov 13364954f17dSDmitry Safonov static int tcp_ao_parse_crypto(struct tcp_ao_add *cmd, struct tcp_ao_key *key) 13374954f17dSDmitry Safonov { 13384954f17dSDmitry Safonov unsigned int syn_tcp_option_space; 13394954f17dSDmitry Safonov bool is_kdf_aes_128_cmac = false; 13404954f17dSDmitry Safonov struct crypto_ahash *tfm; 13414954f17dSDmitry Safonov struct tcp_sigpool hp; 13424954f17dSDmitry Safonov void *tmp_key = NULL; 13434954f17dSDmitry Safonov int err; 13444954f17dSDmitry Safonov 13454954f17dSDmitry Safonov /* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */ 13464954f17dSDmitry Safonov if (!strcmp("cmac(aes128)", cmd->alg_name)) { 13474954f17dSDmitry Safonov strscpy(cmd->alg_name, "cmac(aes)", sizeof(cmd->alg_name)); 13484954f17dSDmitry Safonov is_kdf_aes_128_cmac = (cmd->keylen != 16); 13494954f17dSDmitry Safonov tmp_key = kmalloc(cmd->keylen, GFP_KERNEL); 13504954f17dSDmitry Safonov if (!tmp_key) 13514954f17dSDmitry Safonov return -ENOMEM; 13524954f17dSDmitry Safonov } 13534954f17dSDmitry Safonov 13544954f17dSDmitry Safonov key->maclen = cmd->maclen ?: 12; /* 12 is the default in RFC5925 */ 13554954f17dSDmitry Safonov 13564954f17dSDmitry Safonov /* Check: maclen + tcp-ao header <= (MAX_TCP_OPTION_SPACE - mss 13570a8e987dSKuniyuki Iwashima * - tstamp (including sackperm) 13580a8e987dSKuniyuki Iwashima * - wscale), 13594954f17dSDmitry Safonov * see tcp_syn_options(), tcp_synack_options(), commit 33ad798c924b. 13604954f17dSDmitry Safonov * 13614954f17dSDmitry Safonov * In order to allow D-SACK with TCP-AO, the header size should be: 13624954f17dSDmitry Safonov * (MAX_TCP_OPTION_SPACE - TCPOLEN_TSTAMP_ALIGNED 13634954f17dSDmitry Safonov * - TCPOLEN_SACK_BASE_ALIGNED 13644954f17dSDmitry Safonov * - 2 * TCPOLEN_SACK_PERBLOCK) = 8 (maclen = 4), 13654954f17dSDmitry Safonov * see tcp_established_options(). 13664954f17dSDmitry Safonov * 13674954f17dSDmitry Safonov * RFC5925, 2.2: 13684954f17dSDmitry Safonov * Typical MACs are 96-128 bits (12-16 bytes), but any length 13694954f17dSDmitry Safonov * that fits in the header of the segment being authenticated 13704954f17dSDmitry Safonov * is allowed. 13714954f17dSDmitry Safonov * 13724954f17dSDmitry Safonov * RFC5925, 7.6: 13734954f17dSDmitry Safonov * TCP-AO continues to consume 16 bytes in non-SYN segments, 13744954f17dSDmitry Safonov * leaving a total of 24 bytes for other options, of which 13754954f17dSDmitry Safonov * the timestamp consumes 10. This leaves 14 bytes, of which 10 13764954f17dSDmitry Safonov * are used for a single SACK block. When two SACK blocks are used, 13774954f17dSDmitry Safonov * such as to handle D-SACK, a smaller TCP-AO MAC would be required 13784954f17dSDmitry Safonov * to make room for the additional SACK block (i.e., to leave 18 13794954f17dSDmitry Safonov * bytes for the D-SACK variant of the SACK option) [RFC2883]. 13804954f17dSDmitry Safonov * Note that D-SACK is not supportable in TCP MD5 in the presence 13814954f17dSDmitry Safonov * of timestamps, because TCP MD5’s MAC length is fixed and too 13824954f17dSDmitry Safonov * large to leave sufficient option space. 13834954f17dSDmitry Safonov */ 13844954f17dSDmitry Safonov syn_tcp_option_space = MAX_TCP_OPTION_SPACE; 13850a8e987dSKuniyuki Iwashima syn_tcp_option_space -= TCPOLEN_MSS_ALIGNED; 13864954f17dSDmitry Safonov syn_tcp_option_space -= TCPOLEN_TSTAMP_ALIGNED; 13874954f17dSDmitry Safonov syn_tcp_option_space -= TCPOLEN_WSCALE_ALIGNED; 1388da7dfaa6SDmitry Safonov if (tcp_ao_len_aligned(key) > syn_tcp_option_space) { 13894954f17dSDmitry Safonov err = -EMSGSIZE; 13904954f17dSDmitry Safonov goto err_kfree; 13914954f17dSDmitry Safonov } 13924954f17dSDmitry Safonov 13934954f17dSDmitry Safonov key->keylen = cmd->keylen; 13944954f17dSDmitry Safonov memcpy(key->key, cmd->key, cmd->keylen); 13954954f17dSDmitry Safonov 13964954f17dSDmitry Safonov err = tcp_sigpool_start(key->tcp_sigpool_id, &hp); 13974954f17dSDmitry Safonov if (err) 13984954f17dSDmitry Safonov goto err_kfree; 13994954f17dSDmitry Safonov 14004954f17dSDmitry Safonov tfm = crypto_ahash_reqtfm(hp.req); 14014954f17dSDmitry Safonov if (is_kdf_aes_128_cmac) { 14024954f17dSDmitry Safonov void *scratch = hp.scratch; 14034954f17dSDmitry Safonov struct scatterlist sg; 14044954f17dSDmitry Safonov 14054954f17dSDmitry Safonov memcpy(tmp_key, cmd->key, cmd->keylen); 14064954f17dSDmitry Safonov sg_init_one(&sg, tmp_key, cmd->keylen); 14074954f17dSDmitry Safonov 14084954f17dSDmitry Safonov /* Using zero-key of 16 bytes as described in RFC5926 */ 14094954f17dSDmitry Safonov memset(scratch, 0, 16); 14104954f17dSDmitry Safonov err = crypto_ahash_setkey(tfm, scratch, 16); 14114954f17dSDmitry Safonov if (err) 14124954f17dSDmitry Safonov goto err_pool_end; 14134954f17dSDmitry Safonov 14144954f17dSDmitry Safonov err = crypto_ahash_init(hp.req); 14154954f17dSDmitry Safonov if (err) 14164954f17dSDmitry Safonov goto err_pool_end; 14174954f17dSDmitry Safonov 14184954f17dSDmitry Safonov ahash_request_set_crypt(hp.req, &sg, key->key, cmd->keylen); 14194954f17dSDmitry Safonov err = crypto_ahash_update(hp.req); 14204954f17dSDmitry Safonov if (err) 14214954f17dSDmitry Safonov goto err_pool_end; 14224954f17dSDmitry Safonov 14234954f17dSDmitry Safonov err |= crypto_ahash_final(hp.req); 14244954f17dSDmitry Safonov if (err) 14254954f17dSDmitry Safonov goto err_pool_end; 14264954f17dSDmitry Safonov key->keylen = 16; 14274954f17dSDmitry Safonov } 14284954f17dSDmitry Safonov 14294954f17dSDmitry Safonov err = crypto_ahash_setkey(tfm, key->key, key->keylen); 14304954f17dSDmitry Safonov if (err) 14314954f17dSDmitry Safonov goto err_pool_end; 14324954f17dSDmitry Safonov 14334954f17dSDmitry Safonov tcp_sigpool_end(&hp); 14344954f17dSDmitry Safonov kfree_sensitive(tmp_key); 14354954f17dSDmitry Safonov 14364954f17dSDmitry Safonov if (tcp_ao_maclen(key) > key->digest_size) 14374954f17dSDmitry Safonov return -EINVAL; 14384954f17dSDmitry Safonov 14394954f17dSDmitry Safonov return 0; 14404954f17dSDmitry Safonov 14414954f17dSDmitry Safonov err_pool_end: 14424954f17dSDmitry Safonov tcp_sigpool_end(&hp); 14434954f17dSDmitry Safonov err_kfree: 14444954f17dSDmitry Safonov kfree_sensitive(tmp_key); 14454954f17dSDmitry Safonov return err; 14464954f17dSDmitry Safonov } 14474954f17dSDmitry Safonov 14484954f17dSDmitry Safonov #if IS_ENABLED(CONFIG_IPV6) 14494954f17dSDmitry Safonov static int tcp_ao_verify_ipv6(struct sock *sk, struct tcp_ao_add *cmd, 14504954f17dSDmitry Safonov union tcp_ao_addr **paddr, 14514954f17dSDmitry Safonov unsigned short int *family) 14524954f17dSDmitry Safonov { 14534954f17dSDmitry Safonov struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd->addr; 14544954f17dSDmitry Safonov struct in6_addr *addr = &sin6->sin6_addr; 14554954f17dSDmitry Safonov u8 prefix = cmd->prefix; 14564954f17dSDmitry Safonov 14574954f17dSDmitry Safonov if (sin6->sin6_family != AF_INET6) 14584954f17dSDmitry Safonov return -EINVAL; 14594954f17dSDmitry Safonov 14604954f17dSDmitry Safonov /* Currently matching is not performed on port (or port ranges) */ 14614954f17dSDmitry Safonov if (sin6->sin6_port != 0) 14624954f17dSDmitry Safonov return -EINVAL; 14634954f17dSDmitry Safonov 14644954f17dSDmitry Safonov /* Check prefix and trailing 0's in addr */ 14654954f17dSDmitry Safonov if (cmd->prefix != 0 && ipv6_addr_v4mapped(addr)) { 14664954f17dSDmitry Safonov __be32 addr4 = addr->s6_addr32[3]; 14674954f17dSDmitry Safonov __be32 mask; 14684954f17dSDmitry Safonov 14694954f17dSDmitry Safonov if (prefix > 32 || ntohl(addr4) == INADDR_ANY) 14704954f17dSDmitry Safonov return -EINVAL; 14714954f17dSDmitry Safonov 14724954f17dSDmitry Safonov mask = inet_make_mask(prefix); 14734954f17dSDmitry Safonov if (addr4 & ~mask) 14744954f17dSDmitry Safonov return -EINVAL; 14754954f17dSDmitry Safonov 14764954f17dSDmitry Safonov /* Check that MKT address is consistent with socket */ 14774954f17dSDmitry Safonov if (!ipv6_addr_any(&sk->sk_v6_daddr)) { 14784954f17dSDmitry Safonov __be32 daddr4 = sk->sk_v6_daddr.s6_addr32[3]; 14794954f17dSDmitry Safonov 14804954f17dSDmitry Safonov if (!ipv6_addr_v4mapped(&sk->sk_v6_daddr)) 14814954f17dSDmitry Safonov return -EINVAL; 14824954f17dSDmitry Safonov if ((daddr4 & mask) != addr4) 14834954f17dSDmitry Safonov return -EINVAL; 14844954f17dSDmitry Safonov } 14854954f17dSDmitry Safonov 14864954f17dSDmitry Safonov *paddr = (union tcp_ao_addr *)&addr->s6_addr32[3]; 14874954f17dSDmitry Safonov *family = AF_INET; 14884954f17dSDmitry Safonov return 0; 14894954f17dSDmitry Safonov } else if (cmd->prefix != 0) { 14904954f17dSDmitry Safonov struct in6_addr pfx; 14914954f17dSDmitry Safonov 14924954f17dSDmitry Safonov if (ipv6_addr_any(addr) || prefix > 128) 14934954f17dSDmitry Safonov return -EINVAL; 14944954f17dSDmitry Safonov 14954954f17dSDmitry Safonov ipv6_addr_prefix(&pfx, addr, prefix); 14964954f17dSDmitry Safonov if (ipv6_addr_cmp(&pfx, addr)) 14974954f17dSDmitry Safonov return -EINVAL; 14984954f17dSDmitry Safonov 14994954f17dSDmitry Safonov /* Check that MKT address is consistent with socket */ 15004954f17dSDmitry Safonov if (!ipv6_addr_any(&sk->sk_v6_daddr) && 15014954f17dSDmitry Safonov !ipv6_prefix_equal(&sk->sk_v6_daddr, addr, prefix)) 15024954f17dSDmitry Safonov 15034954f17dSDmitry Safonov return -EINVAL; 15044954f17dSDmitry Safonov } else { 15054954f17dSDmitry Safonov if (!ipv6_addr_any(addr)) 15064954f17dSDmitry Safonov return -EINVAL; 15074954f17dSDmitry Safonov } 15084954f17dSDmitry Safonov 15094954f17dSDmitry Safonov *paddr = (union tcp_ao_addr *)addr; 15104954f17dSDmitry Safonov return 0; 15114954f17dSDmitry Safonov } 15124954f17dSDmitry Safonov #else 15134954f17dSDmitry Safonov static int tcp_ao_verify_ipv6(struct sock *sk, struct tcp_ao_add *cmd, 15144954f17dSDmitry Safonov union tcp_ao_addr **paddr, 15154954f17dSDmitry Safonov unsigned short int *family) 15164954f17dSDmitry Safonov { 15174954f17dSDmitry Safonov return -EOPNOTSUPP; 15184954f17dSDmitry Safonov } 15194954f17dSDmitry Safonov #endif 15204954f17dSDmitry Safonov 15214954f17dSDmitry Safonov static struct tcp_ao_info *setsockopt_ao_info(struct sock *sk) 15224954f17dSDmitry Safonov { 15234954f17dSDmitry Safonov if (sk_fullsock(sk)) { 15244954f17dSDmitry Safonov return rcu_dereference_protected(tcp_sk(sk)->ao_info, 15254954f17dSDmitry Safonov lockdep_sock_is_held(sk)); 1526decde258SDmitry Safonov } else if (sk->sk_state == TCP_TIME_WAIT) { 1527decde258SDmitry Safonov return rcu_dereference_protected(tcp_twsk(sk)->ao_info, 1528decde258SDmitry Safonov lockdep_sock_is_held(sk)); 15294954f17dSDmitry Safonov } 15304954f17dSDmitry Safonov return ERR_PTR(-ESOCKTNOSUPPORT); 15314954f17dSDmitry Safonov } 15324954f17dSDmitry Safonov 1533faadfabaSDmitry Safonov static struct tcp_ao_info *getsockopt_ao_info(struct sock *sk) 1534faadfabaSDmitry Safonov { 1535faadfabaSDmitry Safonov if (sk_fullsock(sk)) 1536faadfabaSDmitry Safonov return rcu_dereference(tcp_sk(sk)->ao_info); 1537faadfabaSDmitry Safonov else if (sk->sk_state == TCP_TIME_WAIT) 1538faadfabaSDmitry Safonov return rcu_dereference(tcp_twsk(sk)->ao_info); 1539faadfabaSDmitry Safonov 1540faadfabaSDmitry Safonov return ERR_PTR(-ESOCKTNOSUPPORT); 1541faadfabaSDmitry Safonov } 1542faadfabaSDmitry Safonov 1543248411b8SDmitry Safonov #define TCP_AO_KEYF_ALL (TCP_AO_KEYF_IFINDEX | TCP_AO_KEYF_EXCLUDE_OPT) 1544248411b8SDmitry Safonov #define TCP_AO_GET_KEYF_VALID (TCP_AO_KEYF_IFINDEX) 15454954f17dSDmitry Safonov 15464954f17dSDmitry Safonov static struct tcp_ao_key *tcp_ao_key_alloc(struct sock *sk, 15474954f17dSDmitry Safonov struct tcp_ao_add *cmd) 15484954f17dSDmitry Safonov { 15494954f17dSDmitry Safonov const char *algo = cmd->alg_name; 15504954f17dSDmitry Safonov unsigned int digest_size; 15514954f17dSDmitry Safonov struct crypto_ahash *tfm; 15524954f17dSDmitry Safonov struct tcp_ao_key *key; 15534954f17dSDmitry Safonov struct tcp_sigpool hp; 15544954f17dSDmitry Safonov int err, pool_id; 15554954f17dSDmitry Safonov size_t size; 15564954f17dSDmitry Safonov 15574954f17dSDmitry Safonov /* Force null-termination of alg_name */ 15584954f17dSDmitry Safonov cmd->alg_name[ARRAY_SIZE(cmd->alg_name) - 1] = '\0'; 15594954f17dSDmitry Safonov 15604954f17dSDmitry Safonov /* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */ 15614954f17dSDmitry Safonov if (!strcmp("cmac(aes128)", algo)) 15624954f17dSDmitry Safonov algo = "cmac(aes)"; 15634954f17dSDmitry Safonov 15644954f17dSDmitry Safonov /* Full TCP header (th->doff << 2) should fit into scratch area, 15654954f17dSDmitry Safonov * see tcp_ao_hash_header(). 15664954f17dSDmitry Safonov */ 15674954f17dSDmitry Safonov pool_id = tcp_sigpool_alloc_ahash(algo, 60); 15684954f17dSDmitry Safonov if (pool_id < 0) 15694954f17dSDmitry Safonov return ERR_PTR(pool_id); 15704954f17dSDmitry Safonov 15714954f17dSDmitry Safonov err = tcp_sigpool_start(pool_id, &hp); 15724954f17dSDmitry Safonov if (err) 15734954f17dSDmitry Safonov goto err_free_pool; 15744954f17dSDmitry Safonov 15754954f17dSDmitry Safonov tfm = crypto_ahash_reqtfm(hp.req); 15764954f17dSDmitry Safonov digest_size = crypto_ahash_digestsize(tfm); 15774954f17dSDmitry Safonov tcp_sigpool_end(&hp); 15784954f17dSDmitry Safonov 15794954f17dSDmitry Safonov size = sizeof(struct tcp_ao_key) + (digest_size << 1); 15804954f17dSDmitry Safonov key = sock_kmalloc(sk, size, GFP_KERNEL); 15814954f17dSDmitry Safonov if (!key) { 15824954f17dSDmitry Safonov err = -ENOMEM; 15834954f17dSDmitry Safonov goto err_free_pool; 15844954f17dSDmitry Safonov } 15854954f17dSDmitry Safonov 15864954f17dSDmitry Safonov key->tcp_sigpool_id = pool_id; 15874954f17dSDmitry Safonov key->digest_size = digest_size; 15884954f17dSDmitry Safonov return key; 15894954f17dSDmitry Safonov 15904954f17dSDmitry Safonov err_free_pool: 15914954f17dSDmitry Safonov tcp_sigpool_release(pool_id); 15924954f17dSDmitry Safonov return ERR_PTR(err); 15934954f17dSDmitry Safonov } 15944954f17dSDmitry Safonov 15954954f17dSDmitry Safonov static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, 15964954f17dSDmitry Safonov sockptr_t optval, int optlen) 15974954f17dSDmitry Safonov { 15984954f17dSDmitry Safonov struct tcp_ao_info *ao_info; 15994954f17dSDmitry Safonov union tcp_ao_addr *addr; 16004954f17dSDmitry Safonov struct tcp_ao_key *key; 16014954f17dSDmitry Safonov struct tcp_ao_add cmd; 1602248411b8SDmitry Safonov int ret, l3index = 0; 16034954f17dSDmitry Safonov bool first = false; 16044954f17dSDmitry Safonov 16054954f17dSDmitry Safonov if (optlen < sizeof(cmd)) 16064954f17dSDmitry Safonov return -EINVAL; 16074954f17dSDmitry Safonov 16084954f17dSDmitry Safonov ret = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 16094954f17dSDmitry Safonov if (ret) 16104954f17dSDmitry Safonov return ret; 16114954f17dSDmitry Safonov 16124954f17dSDmitry Safonov if (cmd.keylen > TCP_AO_MAXKEYLEN) 16134954f17dSDmitry Safonov return -EINVAL; 16144954f17dSDmitry Safonov 16154954f17dSDmitry Safonov if (cmd.reserved != 0 || cmd.reserved2 != 0) 16164954f17dSDmitry Safonov return -EINVAL; 16174954f17dSDmitry Safonov 16184954f17dSDmitry Safonov if (family == AF_INET) 16194954f17dSDmitry Safonov ret = tcp_ao_verify_ipv4(sk, &cmd, &addr); 16204954f17dSDmitry Safonov else 16214954f17dSDmitry Safonov ret = tcp_ao_verify_ipv6(sk, &cmd, &addr, &family); 16224954f17dSDmitry Safonov if (ret) 16234954f17dSDmitry Safonov return ret; 16244954f17dSDmitry Safonov 16254954f17dSDmitry Safonov if (cmd.keyflags & ~TCP_AO_KEYF_ALL) 16264954f17dSDmitry Safonov return -EINVAL; 16274954f17dSDmitry Safonov 16284954f17dSDmitry Safonov if (cmd.set_current || cmd.set_rnext) { 16294954f17dSDmitry Safonov if (!tcp_ao_can_set_current_rnext(sk)) 16304954f17dSDmitry Safonov return -EINVAL; 16314954f17dSDmitry Safonov } 16324954f17dSDmitry Safonov 1633248411b8SDmitry Safonov if (cmd.ifindex && !(cmd.keyflags & TCP_AO_KEYF_IFINDEX)) 1634248411b8SDmitry Safonov return -EINVAL; 1635248411b8SDmitry Safonov 1636248411b8SDmitry Safonov /* For cmd.tcp_ifindex = 0 the key will apply to the default VRF */ 1637248411b8SDmitry Safonov if (cmd.keyflags & TCP_AO_KEYF_IFINDEX && cmd.ifindex) { 1638248411b8SDmitry Safonov int bound_dev_if = READ_ONCE(sk->sk_bound_dev_if); 1639248411b8SDmitry Safonov struct net_device *dev; 1640248411b8SDmitry Safonov 1641248411b8SDmitry Safonov rcu_read_lock(); 1642248411b8SDmitry Safonov dev = dev_get_by_index_rcu(sock_net(sk), cmd.ifindex); 1643248411b8SDmitry Safonov if (dev && netif_is_l3_master(dev)) 1644248411b8SDmitry Safonov l3index = dev->ifindex; 1645248411b8SDmitry Safonov rcu_read_unlock(); 1646248411b8SDmitry Safonov 1647248411b8SDmitry Safonov if (!dev || !l3index) 1648248411b8SDmitry Safonov return -EINVAL; 1649248411b8SDmitry Safonov 165012083d72SDmitry Safonov if (!bound_dev_if || bound_dev_if != cmd.ifindex) { 165112083d72SDmitry Safonov /* tcp_ao_established_key() doesn't expect having 165212083d72SDmitry Safonov * non peer-matching key on an established TCP-AO 165312083d72SDmitry Safonov * connection. 165412083d72SDmitry Safonov */ 165512083d72SDmitry Safonov if (!((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) 165612083d72SDmitry Safonov return -EINVAL; 165712083d72SDmitry Safonov } 165812083d72SDmitry Safonov 1659248411b8SDmitry Safonov /* It's still possible to bind after adding keys or even 1660248411b8SDmitry Safonov * re-bind to a different dev (with CAP_NET_RAW). 1661248411b8SDmitry Safonov * So, no reason to return error here, rather try to be 1662248411b8SDmitry Safonov * nice and warn the user. 1663248411b8SDmitry Safonov */ 1664248411b8SDmitry Safonov if (bound_dev_if && bound_dev_if != cmd.ifindex) 1665248411b8SDmitry Safonov net_warn_ratelimited("AO key ifindex %d != sk bound ifindex %d\n", 1666248411b8SDmitry Safonov cmd.ifindex, bound_dev_if); 1667248411b8SDmitry Safonov } 1668248411b8SDmitry Safonov 16690aadc739SDmitry Safonov /* Don't allow keys for peers that have a matching TCP-MD5 key */ 1670248411b8SDmitry Safonov if (cmd.keyflags & TCP_AO_KEYF_IFINDEX) { 1671248411b8SDmitry Safonov /* Non-_exact version of tcp_md5_do_lookup() will 1672248411b8SDmitry Safonov * as well match keys that aren't bound to a specific VRF 1673248411b8SDmitry Safonov * (that will make them match AO key with 1674248411b8SDmitry Safonov * sysctl_tcp_l3dev_accept = 1 1675248411b8SDmitry Safonov */ 1676248411b8SDmitry Safonov if (tcp_md5_do_lookup(sk, l3index, addr, family)) 1677248411b8SDmitry Safonov return -EKEYREJECTED; 1678248411b8SDmitry Safonov } else { 16790aadc739SDmitry Safonov if (tcp_md5_do_lookup_any_l3index(sk, addr, family)) 16800aadc739SDmitry Safonov return -EKEYREJECTED; 1681248411b8SDmitry Safonov } 16820aadc739SDmitry Safonov 16834954f17dSDmitry Safonov ao_info = setsockopt_ao_info(sk); 16844954f17dSDmitry Safonov if (IS_ERR(ao_info)) 16854954f17dSDmitry Safonov return PTR_ERR(ao_info); 16864954f17dSDmitry Safonov 16874954f17dSDmitry Safonov if (!ao_info) { 16884954f17dSDmitry Safonov ao_info = tcp_ao_alloc_info(GFP_KERNEL); 16894954f17dSDmitry Safonov if (!ao_info) 16904954f17dSDmitry Safonov return -ENOMEM; 16914954f17dSDmitry Safonov first = true; 16924954f17dSDmitry Safonov } else { 16934954f17dSDmitry Safonov /* Check that neither RecvID nor SendID match any 16944954f17dSDmitry Safonov * existing key for the peer, RFC5925 3.1: 16954954f17dSDmitry Safonov * > The IDs of MKTs MUST NOT overlap where their 16964954f17dSDmitry Safonov * > TCP connection identifiers overlap. 16974954f17dSDmitry Safonov */ 1698248411b8SDmitry Safonov if (__tcp_ao_do_lookup(sk, l3index, addr, family, cmd.prefix, -1, cmd.rcvid)) 16994954f17dSDmitry Safonov return -EEXIST; 1700248411b8SDmitry Safonov if (__tcp_ao_do_lookup(sk, l3index, addr, family, 17014954f17dSDmitry Safonov cmd.prefix, cmd.sndid, -1)) 17024954f17dSDmitry Safonov return -EEXIST; 17034954f17dSDmitry Safonov } 17044954f17dSDmitry Safonov 17054954f17dSDmitry Safonov key = tcp_ao_key_alloc(sk, &cmd); 17064954f17dSDmitry Safonov if (IS_ERR(key)) { 17074954f17dSDmitry Safonov ret = PTR_ERR(key); 17084954f17dSDmitry Safonov goto err_free_ao; 17094954f17dSDmitry Safonov } 17104954f17dSDmitry Safonov 17114954f17dSDmitry Safonov INIT_HLIST_NODE(&key->node); 17124954f17dSDmitry Safonov memcpy(&key->addr, addr, (family == AF_INET) ? sizeof(struct in_addr) : 17134954f17dSDmitry Safonov sizeof(struct in6_addr)); 17144954f17dSDmitry Safonov key->prefixlen = cmd.prefix; 17154954f17dSDmitry Safonov key->family = family; 17164954f17dSDmitry Safonov key->keyflags = cmd.keyflags; 17174954f17dSDmitry Safonov key->sndid = cmd.sndid; 17184954f17dSDmitry Safonov key->rcvid = cmd.rcvid; 1719248411b8SDmitry Safonov key->l3index = l3index; 1720af09a341SDmitry Safonov atomic64_set(&key->pkt_good, 0); 1721af09a341SDmitry Safonov atomic64_set(&key->pkt_bad, 0); 17224954f17dSDmitry Safonov 17234954f17dSDmitry Safonov ret = tcp_ao_parse_crypto(&cmd, key); 17244954f17dSDmitry Safonov if (ret < 0) 17254954f17dSDmitry Safonov goto err_free_sock; 17264954f17dSDmitry Safonov 1727faadfabaSDmitry Safonov if (!((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) { 17287c2ffaf2SDmitry Safonov tcp_ao_cache_traffic_keys(sk, ao_info, key); 1729faadfabaSDmitry Safonov if (first) { 1730faadfabaSDmitry Safonov ao_info->current_key = key; 1731faadfabaSDmitry Safonov ao_info->rnext_key = key; 1732faadfabaSDmitry Safonov } 1733faadfabaSDmitry Safonov } 17347c2ffaf2SDmitry Safonov 17354954f17dSDmitry Safonov tcp_ao_link_mkt(ao_info, key); 17364954f17dSDmitry Safonov if (first) { 173767fa83f7SDmitry Safonov if (!static_branch_inc(&tcp_ao_needed.key)) { 173867fa83f7SDmitry Safonov ret = -EUSERS; 173967fa83f7SDmitry Safonov goto err_free_sock; 174067fa83f7SDmitry Safonov } 17414954f17dSDmitry Safonov sk_gso_disable(sk); 17424954f17dSDmitry Safonov rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info); 17434954f17dSDmitry Safonov } 17444954f17dSDmitry Safonov 17454954f17dSDmitry Safonov if (cmd.set_current) 17464954f17dSDmitry Safonov WRITE_ONCE(ao_info->current_key, key); 17474954f17dSDmitry Safonov if (cmd.set_rnext) 17484954f17dSDmitry Safonov WRITE_ONCE(ao_info->rnext_key, key); 17494954f17dSDmitry Safonov return 0; 17504954f17dSDmitry Safonov 17514954f17dSDmitry Safonov err_free_sock: 17524954f17dSDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 17534954f17dSDmitry Safonov tcp_sigpool_release(key->tcp_sigpool_id); 17544954f17dSDmitry Safonov kfree_sensitive(key); 17554954f17dSDmitry Safonov err_free_ao: 17564954f17dSDmitry Safonov if (first) 17574954f17dSDmitry Safonov kfree(ao_info); 17584954f17dSDmitry Safonov return ret; 17594954f17dSDmitry Safonov } 17604954f17dSDmitry Safonov 17614954f17dSDmitry Safonov static int tcp_ao_delete_key(struct sock *sk, struct tcp_ao_info *ao_info, 1762d6732b95SDmitry Safonov bool del_async, struct tcp_ao_key *key, 17634954f17dSDmitry Safonov struct tcp_ao_key *new_current, 17644954f17dSDmitry Safonov struct tcp_ao_key *new_rnext) 17654954f17dSDmitry Safonov { 17664954f17dSDmitry Safonov int err; 17674954f17dSDmitry Safonov 17684954f17dSDmitry Safonov hlist_del_rcu(&key->node); 17694954f17dSDmitry Safonov 1770d6732b95SDmitry Safonov /* Support for async delete on listening sockets: as they don't 1771d6732b95SDmitry Safonov * need current_key/rnext_key maintaining, we don't need to check 1772d6732b95SDmitry Safonov * them and we can just free all resources in RCU fashion. 1773d6732b95SDmitry Safonov */ 1774d6732b95SDmitry Safonov if (del_async) { 1775d6732b95SDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 1776d6732b95SDmitry Safonov call_rcu(&key->rcu, tcp_ao_key_free_rcu); 1777d6732b95SDmitry Safonov return 0; 1778d6732b95SDmitry Safonov } 1779d6732b95SDmitry Safonov 17804954f17dSDmitry Safonov /* At this moment another CPU could have looked this key up 17814954f17dSDmitry Safonov * while it was unlinked from the list. Wait for RCU grace period, 17824954f17dSDmitry Safonov * after which the key is off-list and can't be looked up again; 17834954f17dSDmitry Safonov * the rx path [just before RCU came] might have used it and set it 17844954f17dSDmitry Safonov * as current_key (very unlikely). 1785d6732b95SDmitry Safonov * Free the key with next RCU grace period (in case it was 1786d6732b95SDmitry Safonov * current_key before tcp_ao_current_rnext() might have 1787d6732b95SDmitry Safonov * changed it in forced-delete). 17884954f17dSDmitry Safonov */ 17894954f17dSDmitry Safonov synchronize_rcu(); 17904954f17dSDmitry Safonov if (new_current) 17914954f17dSDmitry Safonov WRITE_ONCE(ao_info->current_key, new_current); 17924954f17dSDmitry Safonov if (new_rnext) 17934954f17dSDmitry Safonov WRITE_ONCE(ao_info->rnext_key, new_rnext); 17944954f17dSDmitry Safonov 17954954f17dSDmitry Safonov if (unlikely(READ_ONCE(ao_info->current_key) == key || 17964954f17dSDmitry Safonov READ_ONCE(ao_info->rnext_key) == key)) { 17974954f17dSDmitry Safonov err = -EBUSY; 17984954f17dSDmitry Safonov goto add_key; 17994954f17dSDmitry Safonov } 18004954f17dSDmitry Safonov 18014954f17dSDmitry Safonov atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc); 18024954f17dSDmitry Safonov call_rcu(&key->rcu, tcp_ao_key_free_rcu); 18034954f17dSDmitry Safonov 18044954f17dSDmitry Safonov return 0; 18054954f17dSDmitry Safonov add_key: 18064954f17dSDmitry Safonov hlist_add_head_rcu(&key->node, &ao_info->head); 18074954f17dSDmitry Safonov return err; 18084954f17dSDmitry Safonov } 18094954f17dSDmitry Safonov 1810248411b8SDmitry Safonov #define TCP_AO_DEL_KEYF_ALL (TCP_AO_KEYF_IFINDEX) 18114954f17dSDmitry Safonov static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family, 18124954f17dSDmitry Safonov sockptr_t optval, int optlen) 18134954f17dSDmitry Safonov { 18144954f17dSDmitry Safonov struct tcp_ao_key *key, *new_current = NULL, *new_rnext = NULL; 1815248411b8SDmitry Safonov int err, addr_len, l3index = 0; 18164954f17dSDmitry Safonov struct tcp_ao_info *ao_info; 18174954f17dSDmitry Safonov union tcp_ao_addr *addr; 18184954f17dSDmitry Safonov struct tcp_ao_del cmd; 18194954f17dSDmitry Safonov __u8 prefix; 18204954f17dSDmitry Safonov u16 port; 18214954f17dSDmitry Safonov 18224954f17dSDmitry Safonov if (optlen < sizeof(cmd)) 18234954f17dSDmitry Safonov return -EINVAL; 18244954f17dSDmitry Safonov 18254954f17dSDmitry Safonov err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 18264954f17dSDmitry Safonov if (err) 18274954f17dSDmitry Safonov return err; 18284954f17dSDmitry Safonov 18294954f17dSDmitry Safonov if (cmd.reserved != 0 || cmd.reserved2 != 0) 18304954f17dSDmitry Safonov return -EINVAL; 18314954f17dSDmitry Safonov 18324954f17dSDmitry Safonov if (cmd.set_current || cmd.set_rnext) { 18334954f17dSDmitry Safonov if (!tcp_ao_can_set_current_rnext(sk)) 18344954f17dSDmitry Safonov return -EINVAL; 18354954f17dSDmitry Safonov } 18364954f17dSDmitry Safonov 1837248411b8SDmitry Safonov if (cmd.keyflags & ~TCP_AO_DEL_KEYF_ALL) 1838248411b8SDmitry Safonov return -EINVAL; 1839248411b8SDmitry Safonov 1840248411b8SDmitry Safonov /* No sanity check for TCP_AO_KEYF_IFINDEX as if a VRF 1841248411b8SDmitry Safonov * was destroyed, there still should be a way to delete keys, 1842248411b8SDmitry Safonov * that were bound to that l3intf. So, fail late at lookup stage 1843248411b8SDmitry Safonov * if there is no key for that ifindex. 1844248411b8SDmitry Safonov */ 1845248411b8SDmitry Safonov if (cmd.ifindex && !(cmd.keyflags & TCP_AO_KEYF_IFINDEX)) 1846248411b8SDmitry Safonov return -EINVAL; 1847248411b8SDmitry Safonov 18484954f17dSDmitry Safonov ao_info = setsockopt_ao_info(sk); 18494954f17dSDmitry Safonov if (IS_ERR(ao_info)) 18504954f17dSDmitry Safonov return PTR_ERR(ao_info); 18514954f17dSDmitry Safonov if (!ao_info) 18524954f17dSDmitry Safonov return -ENOENT; 18534954f17dSDmitry Safonov 18544954f17dSDmitry Safonov /* For sockets in TCP_CLOSED it's possible set keys that aren't 18554954f17dSDmitry Safonov * matching the future peer (address/VRF/etc), 18564954f17dSDmitry Safonov * tcp_ao_connect_init() will choose a correct matching MKT 18574954f17dSDmitry Safonov * if there's any. 18584954f17dSDmitry Safonov */ 18594954f17dSDmitry Safonov if (cmd.set_current) { 18604954f17dSDmitry Safonov new_current = tcp_ao_established_key(ao_info, cmd.current_key, -1); 18614954f17dSDmitry Safonov if (!new_current) 18624954f17dSDmitry Safonov return -ENOENT; 18634954f17dSDmitry Safonov } 18644954f17dSDmitry Safonov if (cmd.set_rnext) { 18654954f17dSDmitry Safonov new_rnext = tcp_ao_established_key(ao_info, -1, cmd.rnext); 18664954f17dSDmitry Safonov if (!new_rnext) 18674954f17dSDmitry Safonov return -ENOENT; 18684954f17dSDmitry Safonov } 1869d6732b95SDmitry Safonov if (cmd.del_async && sk->sk_state != TCP_LISTEN) 1870d6732b95SDmitry Safonov return -EINVAL; 18714954f17dSDmitry Safonov 18724954f17dSDmitry Safonov if (family == AF_INET) { 18734954f17dSDmitry Safonov struct sockaddr_in *sin = (struct sockaddr_in *)&cmd.addr; 18744954f17dSDmitry Safonov 18754954f17dSDmitry Safonov addr = (union tcp_ao_addr *)&sin->sin_addr; 18764954f17dSDmitry Safonov addr_len = sizeof(struct in_addr); 18774954f17dSDmitry Safonov port = ntohs(sin->sin_port); 18784954f17dSDmitry Safonov } else { 18794954f17dSDmitry Safonov struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.addr; 18804954f17dSDmitry Safonov struct in6_addr *addr6 = &sin6->sin6_addr; 18814954f17dSDmitry Safonov 18824954f17dSDmitry Safonov if (ipv6_addr_v4mapped(addr6)) { 18834954f17dSDmitry Safonov addr = (union tcp_ao_addr *)&addr6->s6_addr32[3]; 18844954f17dSDmitry Safonov addr_len = sizeof(struct in_addr); 18854954f17dSDmitry Safonov family = AF_INET; 18864954f17dSDmitry Safonov } else { 18874954f17dSDmitry Safonov addr = (union tcp_ao_addr *)addr6; 18884954f17dSDmitry Safonov addr_len = sizeof(struct in6_addr); 18894954f17dSDmitry Safonov } 18904954f17dSDmitry Safonov port = ntohs(sin6->sin6_port); 18914954f17dSDmitry Safonov } 18924954f17dSDmitry Safonov prefix = cmd.prefix; 18934954f17dSDmitry Safonov 18944954f17dSDmitry Safonov /* Currently matching is not performed on port (or port ranges) */ 18954954f17dSDmitry Safonov if (port != 0) 18964954f17dSDmitry Safonov return -EINVAL; 18974954f17dSDmitry Safonov 18984954f17dSDmitry Safonov /* We could choose random present key here for current/rnext 18994954f17dSDmitry Safonov * but that's less predictable. Let's be strict and don't 19004954f17dSDmitry Safonov * allow removing a key that's in use. RFC5925 doesn't 19014954f17dSDmitry Safonov * specify how-to coordinate key removal, but says: 19024954f17dSDmitry Safonov * "It is presumed that an MKT affecting a particular 19034954f17dSDmitry Safonov * connection cannot be destroyed during an active connection" 19044954f17dSDmitry Safonov */ 19054954f17dSDmitry Safonov hlist_for_each_entry_rcu(key, &ao_info->head, node) { 19064954f17dSDmitry Safonov if (cmd.sndid != key->sndid || 19074954f17dSDmitry Safonov cmd.rcvid != key->rcvid) 19084954f17dSDmitry Safonov continue; 19094954f17dSDmitry Safonov 19104954f17dSDmitry Safonov if (family != key->family || 19114954f17dSDmitry Safonov prefix != key->prefixlen || 19124954f17dSDmitry Safonov memcmp(addr, &key->addr, addr_len)) 19134954f17dSDmitry Safonov continue; 19144954f17dSDmitry Safonov 1915248411b8SDmitry Safonov if ((cmd.keyflags & TCP_AO_KEYF_IFINDEX) != 1916248411b8SDmitry Safonov (key->keyflags & TCP_AO_KEYF_IFINDEX)) 1917248411b8SDmitry Safonov continue; 1918248411b8SDmitry Safonov 1919248411b8SDmitry Safonov if (key->l3index != l3index) 1920248411b8SDmitry Safonov continue; 1921248411b8SDmitry Safonov 19224954f17dSDmitry Safonov if (key == new_current || key == new_rnext) 19234954f17dSDmitry Safonov continue; 19244954f17dSDmitry Safonov 1925d6732b95SDmitry Safonov return tcp_ao_delete_key(sk, ao_info, cmd.del_async, key, 19264954f17dSDmitry Safonov new_current, new_rnext); 19274954f17dSDmitry Safonov } 19284954f17dSDmitry Safonov return -ENOENT; 19294954f17dSDmitry Safonov } 19304954f17dSDmitry Safonov 19310aadc739SDmitry Safonov /* cmd.ao_required makes a socket TCP-AO only. 19320aadc739SDmitry Safonov * Don't allow any md5 keys for any l3intf on the socket together with it. 19330aadc739SDmitry Safonov * Restricting it early in setsockopt() removes a check for 19340aadc739SDmitry Safonov * ao_info->ao_required on inbound tcp segment fast-path. 19350aadc739SDmitry Safonov */ 19360aadc739SDmitry Safonov static int tcp_ao_required_verify(struct sock *sk) 19370aadc739SDmitry Safonov { 19380aadc739SDmitry Safonov #ifdef CONFIG_TCP_MD5SIG 19390aadc739SDmitry Safonov const struct tcp_md5sig_info *md5sig; 19400aadc739SDmitry Safonov 19410aadc739SDmitry Safonov if (!static_branch_unlikely(&tcp_md5_needed.key)) 19420aadc739SDmitry Safonov return 0; 19430aadc739SDmitry Safonov 19440aadc739SDmitry Safonov md5sig = rcu_dereference_check(tcp_sk(sk)->md5sig_info, 19450aadc739SDmitry Safonov lockdep_sock_is_held(sk)); 19460aadc739SDmitry Safonov if (!md5sig) 19470aadc739SDmitry Safonov return 0; 19480aadc739SDmitry Safonov 19490aadc739SDmitry Safonov if (rcu_dereference_check(hlist_first_rcu(&md5sig->head), 19500aadc739SDmitry Safonov lockdep_sock_is_held(sk))) 19510aadc739SDmitry Safonov return 1; 19520aadc739SDmitry Safonov #endif 19530aadc739SDmitry Safonov return 0; 19540aadc739SDmitry Safonov } 19550aadc739SDmitry Safonov 19564954f17dSDmitry Safonov static int tcp_ao_info_cmd(struct sock *sk, unsigned short int family, 19574954f17dSDmitry Safonov sockptr_t optval, int optlen) 19584954f17dSDmitry Safonov { 19594954f17dSDmitry Safonov struct tcp_ao_key *new_current = NULL, *new_rnext = NULL; 19604954f17dSDmitry Safonov struct tcp_ao_info *ao_info; 19614954f17dSDmitry Safonov struct tcp_ao_info_opt cmd; 19624954f17dSDmitry Safonov bool first = false; 19634954f17dSDmitry Safonov int err; 19644954f17dSDmitry Safonov 19654954f17dSDmitry Safonov if (optlen < sizeof(cmd)) 19664954f17dSDmitry Safonov return -EINVAL; 19674954f17dSDmitry Safonov 19684954f17dSDmitry Safonov err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 19694954f17dSDmitry Safonov if (err) 19704954f17dSDmitry Safonov return err; 19714954f17dSDmitry Safonov 19724954f17dSDmitry Safonov if (cmd.set_current || cmd.set_rnext) { 19734954f17dSDmitry Safonov if (!tcp_ao_can_set_current_rnext(sk)) 19744954f17dSDmitry Safonov return -EINVAL; 19754954f17dSDmitry Safonov } 19764954f17dSDmitry Safonov 1977af09a341SDmitry Safonov if (cmd.reserved != 0 || cmd.reserved2 != 0) 19784954f17dSDmitry Safonov return -EINVAL; 19794954f17dSDmitry Safonov 19804954f17dSDmitry Safonov ao_info = setsockopt_ao_info(sk); 19814954f17dSDmitry Safonov if (IS_ERR(ao_info)) 19824954f17dSDmitry Safonov return PTR_ERR(ao_info); 19834954f17dSDmitry Safonov if (!ao_info) { 1984faadfabaSDmitry Safonov if (!((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE))) 1985faadfabaSDmitry Safonov return -EINVAL; 19864954f17dSDmitry Safonov ao_info = tcp_ao_alloc_info(GFP_KERNEL); 19874954f17dSDmitry Safonov if (!ao_info) 19884954f17dSDmitry Safonov return -ENOMEM; 19894954f17dSDmitry Safonov first = true; 19904954f17dSDmitry Safonov } 19914954f17dSDmitry Safonov 1992f9ae8489SDmitry Safonov if (cmd.ao_required && tcp_ao_required_verify(sk)) { 1993f9ae8489SDmitry Safonov err = -EKEYREJECTED; 1994f9ae8489SDmitry Safonov goto out; 1995f9ae8489SDmitry Safonov } 19960aadc739SDmitry Safonov 19974954f17dSDmitry Safonov /* For sockets in TCP_CLOSED it's possible set keys that aren't 19984954f17dSDmitry Safonov * matching the future peer (address/port/VRF/etc), 19994954f17dSDmitry Safonov * tcp_ao_connect_init() will choose a correct matching MKT 20004954f17dSDmitry Safonov * if there's any. 20014954f17dSDmitry Safonov */ 20024954f17dSDmitry Safonov if (cmd.set_current) { 20034954f17dSDmitry Safonov new_current = tcp_ao_established_key(ao_info, cmd.current_key, -1); 20044954f17dSDmitry Safonov if (!new_current) { 20054954f17dSDmitry Safonov err = -ENOENT; 20064954f17dSDmitry Safonov goto out; 20074954f17dSDmitry Safonov } 20084954f17dSDmitry Safonov } 20094954f17dSDmitry Safonov if (cmd.set_rnext) { 20104954f17dSDmitry Safonov new_rnext = tcp_ao_established_key(ao_info, -1, cmd.rnext); 20114954f17dSDmitry Safonov if (!new_rnext) { 20124954f17dSDmitry Safonov err = -ENOENT; 20134954f17dSDmitry Safonov goto out; 20144954f17dSDmitry Safonov } 20154954f17dSDmitry Safonov } 2016af09a341SDmitry Safonov if (cmd.set_counters) { 2017af09a341SDmitry Safonov atomic64_set(&ao_info->counters.pkt_good, cmd.pkt_good); 2018af09a341SDmitry Safonov atomic64_set(&ao_info->counters.pkt_bad, cmd.pkt_bad); 2019af09a341SDmitry Safonov atomic64_set(&ao_info->counters.key_not_found, cmd.pkt_key_not_found); 2020af09a341SDmitry Safonov atomic64_set(&ao_info->counters.ao_required, cmd.pkt_ao_required); 2021953af8e3SDmitry Safonov atomic64_set(&ao_info->counters.dropped_icmp, cmd.pkt_dropped_icmp); 2022af09a341SDmitry Safonov } 20234954f17dSDmitry Safonov 20244954f17dSDmitry Safonov ao_info->ao_required = cmd.ao_required; 2025953af8e3SDmitry Safonov ao_info->accept_icmps = cmd.accept_icmps; 20264954f17dSDmitry Safonov if (new_current) 20274954f17dSDmitry Safonov WRITE_ONCE(ao_info->current_key, new_current); 20284954f17dSDmitry Safonov if (new_rnext) 20294954f17dSDmitry Safonov WRITE_ONCE(ao_info->rnext_key, new_rnext); 20304954f17dSDmitry Safonov if (first) { 203167fa83f7SDmitry Safonov if (!static_branch_inc(&tcp_ao_needed.key)) { 203267fa83f7SDmitry Safonov err = -EUSERS; 203367fa83f7SDmitry Safonov goto out; 203467fa83f7SDmitry Safonov } 20354954f17dSDmitry Safonov sk_gso_disable(sk); 20364954f17dSDmitry Safonov rcu_assign_pointer(tcp_sk(sk)->ao_info, ao_info); 20374954f17dSDmitry Safonov } 20384954f17dSDmitry Safonov return 0; 20394954f17dSDmitry Safonov out: 20404954f17dSDmitry Safonov if (first) 20414954f17dSDmitry Safonov kfree(ao_info); 20424954f17dSDmitry Safonov return err; 20434954f17dSDmitry Safonov } 20444954f17dSDmitry Safonov 20454954f17dSDmitry Safonov int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, 20464954f17dSDmitry Safonov sockptr_t optval, int optlen) 20474954f17dSDmitry Safonov { 20484954f17dSDmitry Safonov if (WARN_ON_ONCE(family != AF_INET && family != AF_INET6)) 20494954f17dSDmitry Safonov return -EAFNOSUPPORT; 20504954f17dSDmitry Safonov 20514954f17dSDmitry Safonov switch (cmd) { 20524954f17dSDmitry Safonov case TCP_AO_ADD_KEY: 20534954f17dSDmitry Safonov return tcp_ao_add_cmd(sk, family, optval, optlen); 20544954f17dSDmitry Safonov case TCP_AO_DEL_KEY: 20554954f17dSDmitry Safonov return tcp_ao_del_cmd(sk, family, optval, optlen); 20564954f17dSDmitry Safonov case TCP_AO_INFO: 20574954f17dSDmitry Safonov return tcp_ao_info_cmd(sk, family, optval, optlen); 20584954f17dSDmitry Safonov default: 20594954f17dSDmitry Safonov WARN_ON_ONCE(1); 20604954f17dSDmitry Safonov return -EINVAL; 20614954f17dSDmitry Safonov } 20624954f17dSDmitry Safonov } 20634954f17dSDmitry Safonov 20644954f17dSDmitry Safonov int tcp_v4_parse_ao(struct sock *sk, int cmd, sockptr_t optval, int optlen) 20654954f17dSDmitry Safonov { 20664954f17dSDmitry Safonov return tcp_parse_ao(sk, cmd, AF_INET, optval, optlen); 20674954f17dSDmitry Safonov } 20684954f17dSDmitry Safonov 2069ef84703aSDmitry Safonov /* tcp_ao_copy_mkts_to_user(ao_info, optval, optlen) 2070ef84703aSDmitry Safonov * 2071ef84703aSDmitry Safonov * @ao_info: struct tcp_ao_info on the socket that 2072ef84703aSDmitry Safonov * socket getsockopt(TCP_AO_GET_KEYS) is executed on 2073ef84703aSDmitry Safonov * @optval: pointer to array of tcp_ao_getsockopt structures in user space. 2074ef84703aSDmitry Safonov * Must be != NULL. 2075ef84703aSDmitry Safonov * @optlen: pointer to size of tcp_ao_getsockopt structure. 2076ef84703aSDmitry Safonov * Must be != NULL. 2077ef84703aSDmitry Safonov * 2078ef84703aSDmitry Safonov * Return value: 0 on success, a negative error number otherwise. 2079ef84703aSDmitry Safonov * 2080ef84703aSDmitry Safonov * optval points to an array of tcp_ao_getsockopt structures in user space. 2081ef84703aSDmitry Safonov * optval[0] is used as both input and output to getsockopt. It determines 2082ef84703aSDmitry Safonov * which keys are returned by the kernel. 2083ef84703aSDmitry Safonov * optval[0].nkeys is the size of the array in user space. On return it contains 2084ef84703aSDmitry Safonov * the number of keys matching the search criteria. 2085ef84703aSDmitry Safonov * If tcp_ao_getsockopt::get_all is set, then all keys in the socket are 2086ef84703aSDmitry Safonov * returned, otherwise only keys matching <addr, prefix, sndid, rcvid> 2087ef84703aSDmitry Safonov * in optval[0] are returned. 2088ef84703aSDmitry Safonov * optlen is also used as both input and output. The user provides the size 2089ef84703aSDmitry Safonov * of struct tcp_ao_getsockopt in user space, and the kernel returns the size 2090ef84703aSDmitry Safonov * of the structure in kernel space. 2091ef84703aSDmitry Safonov * The size of struct tcp_ao_getsockopt may differ between user and kernel. 2092ef84703aSDmitry Safonov * There are three cases to consider: 2093ef84703aSDmitry Safonov * * If usize == ksize, then keys are copied verbatim. 2094ef84703aSDmitry Safonov * * If usize < ksize, then the userspace has passed an old struct to a 2095ef84703aSDmitry Safonov * newer kernel. The rest of the trailing bytes in optval[0] 2096ef84703aSDmitry Safonov * (ksize - usize) are interpreted as 0 by the kernel. 2097ef84703aSDmitry Safonov * * If usize > ksize, then the userspace has passed a new struct to an 2098ef84703aSDmitry Safonov * older kernel. The trailing bytes unknown to the kernel (usize - ksize) 2099ef84703aSDmitry Safonov * are checked to ensure they are zeroed, otherwise -E2BIG is returned. 2100ef84703aSDmitry Safonov * On return the kernel fills in min(usize, ksize) in each entry of the array. 2101ef84703aSDmitry Safonov * The layout of the fields in the user and kernel structures is expected to 2102ef84703aSDmitry Safonov * be the same (including in the 32bit vs 64bit case). 2103ef84703aSDmitry Safonov */ 2104ef84703aSDmitry Safonov static int tcp_ao_copy_mkts_to_user(struct tcp_ao_info *ao_info, 2105ef84703aSDmitry Safonov sockptr_t optval, sockptr_t optlen) 2106ef84703aSDmitry Safonov { 2107ef84703aSDmitry Safonov struct tcp_ao_getsockopt opt_in, opt_out; 2108ef84703aSDmitry Safonov struct tcp_ao_key *key, *current_key; 2109ef84703aSDmitry Safonov bool do_address_matching = true; 2110ef84703aSDmitry Safonov union tcp_ao_addr *addr = NULL; 2111248411b8SDmitry Safonov int err, l3index, user_len; 2112ef84703aSDmitry Safonov unsigned int max_keys; /* maximum number of keys to copy to user */ 2113ef84703aSDmitry Safonov size_t out_offset = 0; 2114ef84703aSDmitry Safonov size_t bytes_to_write; /* number of bytes to write to user level */ 2115ef84703aSDmitry Safonov u32 matched_keys; /* keys from ao_info matched so far */ 2116ef84703aSDmitry Safonov int optlen_out; 2117ef84703aSDmitry Safonov __be16 port = 0; 2118ef84703aSDmitry Safonov 2119ef84703aSDmitry Safonov if (copy_from_sockptr(&user_len, optlen, sizeof(int))) 2120ef84703aSDmitry Safonov return -EFAULT; 2121ef84703aSDmitry Safonov 2122ef84703aSDmitry Safonov if (user_len <= 0) 2123ef84703aSDmitry Safonov return -EINVAL; 2124ef84703aSDmitry Safonov 2125ef84703aSDmitry Safonov memset(&opt_in, 0, sizeof(struct tcp_ao_getsockopt)); 2126ef84703aSDmitry Safonov err = copy_struct_from_sockptr(&opt_in, sizeof(opt_in), 2127ef84703aSDmitry Safonov optval, user_len); 2128ef84703aSDmitry Safonov if (err < 0) 2129ef84703aSDmitry Safonov return err; 2130ef84703aSDmitry Safonov 2131ef84703aSDmitry Safonov if (opt_in.pkt_good || opt_in.pkt_bad) 2132ef84703aSDmitry Safonov return -EINVAL; 2133248411b8SDmitry Safonov if (opt_in.keyflags & ~TCP_AO_GET_KEYF_VALID) 2134248411b8SDmitry Safonov return -EINVAL; 2135248411b8SDmitry Safonov if (opt_in.ifindex && !(opt_in.keyflags & TCP_AO_KEYF_IFINDEX)) 2136248411b8SDmitry Safonov return -EINVAL; 2137ef84703aSDmitry Safonov 2138ef84703aSDmitry Safonov if (opt_in.reserved != 0) 2139ef84703aSDmitry Safonov return -EINVAL; 2140ef84703aSDmitry Safonov 2141ef84703aSDmitry Safonov max_keys = opt_in.nkeys; 2142248411b8SDmitry Safonov l3index = (opt_in.keyflags & TCP_AO_KEYF_IFINDEX) ? opt_in.ifindex : -1; 2143ef84703aSDmitry Safonov 2144ef84703aSDmitry Safonov if (opt_in.get_all || opt_in.is_current || opt_in.is_rnext) { 2145ef84703aSDmitry Safonov if (opt_in.get_all && (opt_in.is_current || opt_in.is_rnext)) 2146ef84703aSDmitry Safonov return -EINVAL; 2147ef84703aSDmitry Safonov do_address_matching = false; 2148ef84703aSDmitry Safonov } 2149ef84703aSDmitry Safonov 2150ef84703aSDmitry Safonov switch (opt_in.addr.ss_family) { 2151ef84703aSDmitry Safonov case AF_INET: { 2152ef84703aSDmitry Safonov struct sockaddr_in *sin; 2153ef84703aSDmitry Safonov __be32 mask; 2154ef84703aSDmitry Safonov 2155ef84703aSDmitry Safonov sin = (struct sockaddr_in *)&opt_in.addr; 2156ef84703aSDmitry Safonov port = sin->sin_port; 2157ef84703aSDmitry Safonov addr = (union tcp_ao_addr *)&sin->sin_addr; 2158ef84703aSDmitry Safonov 2159ef84703aSDmitry Safonov if (opt_in.prefix > 32) 2160ef84703aSDmitry Safonov return -EINVAL; 2161ef84703aSDmitry Safonov 2162ef84703aSDmitry Safonov if (ntohl(sin->sin_addr.s_addr) == INADDR_ANY && 2163ef84703aSDmitry Safonov opt_in.prefix != 0) 2164ef84703aSDmitry Safonov return -EINVAL; 2165ef84703aSDmitry Safonov 2166ef84703aSDmitry Safonov mask = inet_make_mask(opt_in.prefix); 2167ef84703aSDmitry Safonov if (sin->sin_addr.s_addr & ~mask) 2168ef84703aSDmitry Safonov return -EINVAL; 2169ef84703aSDmitry Safonov 2170ef84703aSDmitry Safonov break; 2171ef84703aSDmitry Safonov } 2172ef84703aSDmitry Safonov case AF_INET6: { 2173ef84703aSDmitry Safonov struct sockaddr_in6 *sin6; 2174ef84703aSDmitry Safonov struct in6_addr *addr6; 2175ef84703aSDmitry Safonov 2176ef84703aSDmitry Safonov sin6 = (struct sockaddr_in6 *)&opt_in.addr; 2177ef84703aSDmitry Safonov addr = (union tcp_ao_addr *)&sin6->sin6_addr; 2178ef84703aSDmitry Safonov addr6 = &sin6->sin6_addr; 2179ef84703aSDmitry Safonov port = sin6->sin6_port; 2180ef84703aSDmitry Safonov 2181ef84703aSDmitry Safonov /* We don't have to change family and @addr here if 2182ef84703aSDmitry Safonov * ipv6_addr_v4mapped() like in key adding: 2183ef84703aSDmitry Safonov * tcp_ao_key_cmp() does it. Do the sanity checks though. 2184ef84703aSDmitry Safonov */ 2185ef84703aSDmitry Safonov if (opt_in.prefix != 0) { 2186ef84703aSDmitry Safonov if (ipv6_addr_v4mapped(addr6)) { 2187ef84703aSDmitry Safonov __be32 mask, addr4 = addr6->s6_addr32[3]; 2188ef84703aSDmitry Safonov 2189ef84703aSDmitry Safonov if (opt_in.prefix > 32 || 2190ef84703aSDmitry Safonov ntohl(addr4) == INADDR_ANY) 2191ef84703aSDmitry Safonov return -EINVAL; 2192ef84703aSDmitry Safonov mask = inet_make_mask(opt_in.prefix); 2193ef84703aSDmitry Safonov if (addr4 & ~mask) 2194ef84703aSDmitry Safonov return -EINVAL; 2195ef84703aSDmitry Safonov } else { 2196ef84703aSDmitry Safonov struct in6_addr pfx; 2197ef84703aSDmitry Safonov 2198ef84703aSDmitry Safonov if (ipv6_addr_any(addr6) || 2199ef84703aSDmitry Safonov opt_in.prefix > 128) 2200ef84703aSDmitry Safonov return -EINVAL; 2201ef84703aSDmitry Safonov 2202ef84703aSDmitry Safonov ipv6_addr_prefix(&pfx, addr6, opt_in.prefix); 2203ef84703aSDmitry Safonov if (ipv6_addr_cmp(&pfx, addr6)) 2204ef84703aSDmitry Safonov return -EINVAL; 2205ef84703aSDmitry Safonov } 2206ef84703aSDmitry Safonov } else if (!ipv6_addr_any(addr6)) { 2207ef84703aSDmitry Safonov return -EINVAL; 2208ef84703aSDmitry Safonov } 2209ef84703aSDmitry Safonov break; 2210ef84703aSDmitry Safonov } 2211ef84703aSDmitry Safonov case 0: 2212ef84703aSDmitry Safonov if (!do_address_matching) 2213ef84703aSDmitry Safonov break; 2214ef84703aSDmitry Safonov fallthrough; 2215ef84703aSDmitry Safonov default: 2216ef84703aSDmitry Safonov return -EAFNOSUPPORT; 2217ef84703aSDmitry Safonov } 2218ef84703aSDmitry Safonov 2219ef84703aSDmitry Safonov if (!do_address_matching) { 2220ef84703aSDmitry Safonov /* We could just ignore those, but let's do stricter checks */ 2221ef84703aSDmitry Safonov if (addr || port) 2222ef84703aSDmitry Safonov return -EINVAL; 2223ef84703aSDmitry Safonov if (opt_in.prefix || opt_in.sndid || opt_in.rcvid) 2224ef84703aSDmitry Safonov return -EINVAL; 2225ef84703aSDmitry Safonov } 2226ef84703aSDmitry Safonov 2227ef84703aSDmitry Safonov bytes_to_write = min_t(int, user_len, sizeof(struct tcp_ao_getsockopt)); 2228ef84703aSDmitry Safonov matched_keys = 0; 2229ef84703aSDmitry Safonov /* May change in RX, while we're dumping, pre-fetch it */ 2230ef84703aSDmitry Safonov current_key = READ_ONCE(ao_info->current_key); 2231ef84703aSDmitry Safonov 2232ef84703aSDmitry Safonov hlist_for_each_entry_rcu(key, &ao_info->head, node) { 2233ef84703aSDmitry Safonov if (opt_in.get_all) 2234ef84703aSDmitry Safonov goto match; 2235ef84703aSDmitry Safonov 2236ef84703aSDmitry Safonov if (opt_in.is_current || opt_in.is_rnext) { 2237ef84703aSDmitry Safonov if (opt_in.is_current && key == current_key) 2238ef84703aSDmitry Safonov goto match; 2239ef84703aSDmitry Safonov if (opt_in.is_rnext && key == ao_info->rnext_key) 2240ef84703aSDmitry Safonov goto match; 2241ef84703aSDmitry Safonov continue; 2242ef84703aSDmitry Safonov } 2243ef84703aSDmitry Safonov 2244248411b8SDmitry Safonov if (tcp_ao_key_cmp(key, l3index, addr, opt_in.prefix, 2245ef84703aSDmitry Safonov opt_in.addr.ss_family, 2246ef84703aSDmitry Safonov opt_in.sndid, opt_in.rcvid) != 0) 2247ef84703aSDmitry Safonov continue; 2248ef84703aSDmitry Safonov match: 2249ef84703aSDmitry Safonov matched_keys++; 2250ef84703aSDmitry Safonov if (matched_keys > max_keys) 2251ef84703aSDmitry Safonov continue; 2252ef84703aSDmitry Safonov 2253ef84703aSDmitry Safonov memset(&opt_out, 0, sizeof(struct tcp_ao_getsockopt)); 2254ef84703aSDmitry Safonov 2255ef84703aSDmitry Safonov if (key->family == AF_INET) { 2256ef84703aSDmitry Safonov struct sockaddr_in *sin_out = (struct sockaddr_in *)&opt_out.addr; 2257ef84703aSDmitry Safonov 2258ef84703aSDmitry Safonov sin_out->sin_family = key->family; 2259ef84703aSDmitry Safonov sin_out->sin_port = 0; 2260ef84703aSDmitry Safonov memcpy(&sin_out->sin_addr, &key->addr, sizeof(struct in_addr)); 2261ef84703aSDmitry Safonov } else { 2262ef84703aSDmitry Safonov struct sockaddr_in6 *sin6_out = (struct sockaddr_in6 *)&opt_out.addr; 2263ef84703aSDmitry Safonov 2264ef84703aSDmitry Safonov sin6_out->sin6_family = key->family; 2265ef84703aSDmitry Safonov sin6_out->sin6_port = 0; 2266ef84703aSDmitry Safonov memcpy(&sin6_out->sin6_addr, &key->addr, sizeof(struct in6_addr)); 2267ef84703aSDmitry Safonov } 2268ef84703aSDmitry Safonov opt_out.sndid = key->sndid; 2269ef84703aSDmitry Safonov opt_out.rcvid = key->rcvid; 2270ef84703aSDmitry Safonov opt_out.prefix = key->prefixlen; 2271ef84703aSDmitry Safonov opt_out.keyflags = key->keyflags; 2272ef84703aSDmitry Safonov opt_out.is_current = (key == current_key); 2273ef84703aSDmitry Safonov opt_out.is_rnext = (key == ao_info->rnext_key); 2274ef84703aSDmitry Safonov opt_out.nkeys = 0; 2275ef84703aSDmitry Safonov opt_out.maclen = key->maclen; 2276ef84703aSDmitry Safonov opt_out.keylen = key->keylen; 2277248411b8SDmitry Safonov opt_out.ifindex = key->l3index; 2278ef84703aSDmitry Safonov opt_out.pkt_good = atomic64_read(&key->pkt_good); 2279ef84703aSDmitry Safonov opt_out.pkt_bad = atomic64_read(&key->pkt_bad); 2280ef84703aSDmitry Safonov memcpy(&opt_out.key, key->key, key->keylen); 2281ef84703aSDmitry Safonov tcp_sigpool_algo(key->tcp_sigpool_id, opt_out.alg_name, 64); 2282ef84703aSDmitry Safonov 2283ef84703aSDmitry Safonov /* Copy key to user */ 2284ef84703aSDmitry Safonov if (copy_to_sockptr_offset(optval, out_offset, 2285ef84703aSDmitry Safonov &opt_out, bytes_to_write)) 2286ef84703aSDmitry Safonov return -EFAULT; 2287ef84703aSDmitry Safonov out_offset += user_len; 2288ef84703aSDmitry Safonov } 2289ef84703aSDmitry Safonov 2290ef84703aSDmitry Safonov optlen_out = (int)sizeof(struct tcp_ao_getsockopt); 2291ef84703aSDmitry Safonov if (copy_to_sockptr(optlen, &optlen_out, sizeof(int))) 2292ef84703aSDmitry Safonov return -EFAULT; 2293ef84703aSDmitry Safonov 2294ef84703aSDmitry Safonov out_offset = offsetof(struct tcp_ao_getsockopt, nkeys); 2295ef84703aSDmitry Safonov if (copy_to_sockptr_offset(optval, out_offset, 2296ef84703aSDmitry Safonov &matched_keys, sizeof(u32))) 2297ef84703aSDmitry Safonov return -EFAULT; 2298ef84703aSDmitry Safonov 2299ef84703aSDmitry Safonov return 0; 2300ef84703aSDmitry Safonov } 2301ef84703aSDmitry Safonov 2302ef84703aSDmitry Safonov int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen) 2303ef84703aSDmitry Safonov { 2304ef84703aSDmitry Safonov struct tcp_ao_info *ao_info; 2305ef84703aSDmitry Safonov 2306ef84703aSDmitry Safonov ao_info = setsockopt_ao_info(sk); 2307ef84703aSDmitry Safonov if (IS_ERR(ao_info)) 2308ef84703aSDmitry Safonov return PTR_ERR(ao_info); 2309ef84703aSDmitry Safonov if (!ao_info) 2310ef84703aSDmitry Safonov return -ENOENT; 2311ef84703aSDmitry Safonov 2312ef84703aSDmitry Safonov return tcp_ao_copy_mkts_to_user(ao_info, optval, optlen); 2313ef84703aSDmitry Safonov } 2314ef84703aSDmitry Safonov 2315ef84703aSDmitry Safonov int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optlen) 2316ef84703aSDmitry Safonov { 2317ef84703aSDmitry Safonov struct tcp_ao_info_opt out, in = {}; 2318ef84703aSDmitry Safonov struct tcp_ao_key *current_key; 2319ef84703aSDmitry Safonov struct tcp_ao_info *ao; 2320ef84703aSDmitry Safonov int err, len; 2321ef84703aSDmitry Safonov 2322ef84703aSDmitry Safonov if (copy_from_sockptr(&len, optlen, sizeof(int))) 2323ef84703aSDmitry Safonov return -EFAULT; 2324ef84703aSDmitry Safonov 2325ef84703aSDmitry Safonov if (len <= 0) 2326ef84703aSDmitry Safonov return -EINVAL; 2327ef84703aSDmitry Safonov 2328ef84703aSDmitry Safonov /* Copying this "in" only to check ::reserved, ::reserved2, 2329ef84703aSDmitry Safonov * that may be needed to extend (struct tcp_ao_info_opt) and 2330ef84703aSDmitry Safonov * what getsockopt() provides in future. 2331ef84703aSDmitry Safonov */ 2332ef84703aSDmitry Safonov err = copy_struct_from_sockptr(&in, sizeof(in), optval, len); 2333ef84703aSDmitry Safonov if (err) 2334ef84703aSDmitry Safonov return err; 2335ef84703aSDmitry Safonov 2336ef84703aSDmitry Safonov if (in.reserved != 0 || in.reserved2 != 0) 2337ef84703aSDmitry Safonov return -EINVAL; 2338ef84703aSDmitry Safonov 2339ef84703aSDmitry Safonov ao = setsockopt_ao_info(sk); 2340ef84703aSDmitry Safonov if (IS_ERR(ao)) 2341ef84703aSDmitry Safonov return PTR_ERR(ao); 2342ef84703aSDmitry Safonov if (!ao) 2343ef84703aSDmitry Safonov return -ENOENT; 2344ef84703aSDmitry Safonov 2345ef84703aSDmitry Safonov memset(&out, 0, sizeof(out)); 2346ef84703aSDmitry Safonov out.ao_required = ao->ao_required; 2347ef84703aSDmitry Safonov out.accept_icmps = ao->accept_icmps; 2348ef84703aSDmitry Safonov out.pkt_good = atomic64_read(&ao->counters.pkt_good); 2349ef84703aSDmitry Safonov out.pkt_bad = atomic64_read(&ao->counters.pkt_bad); 2350ef84703aSDmitry Safonov out.pkt_key_not_found = atomic64_read(&ao->counters.key_not_found); 2351ef84703aSDmitry Safonov out.pkt_ao_required = atomic64_read(&ao->counters.ao_required); 2352ef84703aSDmitry Safonov out.pkt_dropped_icmp = atomic64_read(&ao->counters.dropped_icmp); 2353ef84703aSDmitry Safonov 2354ef84703aSDmitry Safonov current_key = READ_ONCE(ao->current_key); 2355ef84703aSDmitry Safonov if (current_key) { 2356ef84703aSDmitry Safonov out.set_current = 1; 2357ef84703aSDmitry Safonov out.current_key = current_key->sndid; 2358ef84703aSDmitry Safonov } 2359ef84703aSDmitry Safonov if (ao->rnext_key) { 2360ef84703aSDmitry Safonov out.set_rnext = 1; 2361ef84703aSDmitry Safonov out.rnext = ao->rnext_key->rcvid; 2362ef84703aSDmitry Safonov } 2363ef84703aSDmitry Safonov 2364ef84703aSDmitry Safonov if (copy_to_sockptr(optval, &out, min_t(int, len, sizeof(out)))) 2365ef84703aSDmitry Safonov return -EFAULT; 2366ef84703aSDmitry Safonov 2367ef84703aSDmitry Safonov return 0; 2368ef84703aSDmitry Safonov } 2369ef84703aSDmitry Safonov 2370faadfabaSDmitry Safonov int tcp_ao_set_repair(struct sock *sk, sockptr_t optval, unsigned int optlen) 2371faadfabaSDmitry Safonov { 2372faadfabaSDmitry Safonov struct tcp_sock *tp = tcp_sk(sk); 2373faadfabaSDmitry Safonov struct tcp_ao_repair cmd; 2374faadfabaSDmitry Safonov struct tcp_ao_key *key; 2375faadfabaSDmitry Safonov struct tcp_ao_info *ao; 2376faadfabaSDmitry Safonov int err; 2377faadfabaSDmitry Safonov 2378faadfabaSDmitry Safonov if (optlen < sizeof(cmd)) 2379faadfabaSDmitry Safonov return -EINVAL; 2380faadfabaSDmitry Safonov 2381faadfabaSDmitry Safonov err = copy_struct_from_sockptr(&cmd, sizeof(cmd), optval, optlen); 2382faadfabaSDmitry Safonov if (err) 2383faadfabaSDmitry Safonov return err; 2384faadfabaSDmitry Safonov 2385faadfabaSDmitry Safonov if (!tp->repair) 2386faadfabaSDmitry Safonov return -EPERM; 2387faadfabaSDmitry Safonov 2388faadfabaSDmitry Safonov ao = setsockopt_ao_info(sk); 2389faadfabaSDmitry Safonov if (IS_ERR(ao)) 2390faadfabaSDmitry Safonov return PTR_ERR(ao); 2391faadfabaSDmitry Safonov if (!ao) 2392faadfabaSDmitry Safonov return -ENOENT; 2393faadfabaSDmitry Safonov 2394faadfabaSDmitry Safonov WRITE_ONCE(ao->lisn, cmd.snt_isn); 2395faadfabaSDmitry Safonov WRITE_ONCE(ao->risn, cmd.rcv_isn); 2396faadfabaSDmitry Safonov WRITE_ONCE(ao->snd_sne, cmd.snd_sne); 2397faadfabaSDmitry Safonov WRITE_ONCE(ao->rcv_sne, cmd.rcv_sne); 2398faadfabaSDmitry Safonov 2399faadfabaSDmitry Safonov hlist_for_each_entry_rcu(key, &ao->head, node) 2400faadfabaSDmitry Safonov tcp_ao_cache_traffic_keys(sk, ao, key); 2401faadfabaSDmitry Safonov 2402faadfabaSDmitry Safonov return 0; 2403faadfabaSDmitry Safonov } 2404faadfabaSDmitry Safonov 2405faadfabaSDmitry Safonov int tcp_ao_get_repair(struct sock *sk, sockptr_t optval, sockptr_t optlen) 2406faadfabaSDmitry Safonov { 2407faadfabaSDmitry Safonov struct tcp_sock *tp = tcp_sk(sk); 2408faadfabaSDmitry Safonov struct tcp_ao_repair opt; 2409faadfabaSDmitry Safonov struct tcp_ao_info *ao; 2410faadfabaSDmitry Safonov int len; 2411faadfabaSDmitry Safonov 2412faadfabaSDmitry Safonov if (copy_from_sockptr(&len, optlen, sizeof(int))) 2413faadfabaSDmitry Safonov return -EFAULT; 2414faadfabaSDmitry Safonov 2415faadfabaSDmitry Safonov if (len <= 0) 2416faadfabaSDmitry Safonov return -EINVAL; 2417faadfabaSDmitry Safonov 2418faadfabaSDmitry Safonov if (!tp->repair) 2419faadfabaSDmitry Safonov return -EPERM; 2420faadfabaSDmitry Safonov 2421faadfabaSDmitry Safonov rcu_read_lock(); 2422faadfabaSDmitry Safonov ao = getsockopt_ao_info(sk); 2423faadfabaSDmitry Safonov if (IS_ERR_OR_NULL(ao)) { 2424faadfabaSDmitry Safonov rcu_read_unlock(); 2425faadfabaSDmitry Safonov return ao ? PTR_ERR(ao) : -ENOENT; 2426faadfabaSDmitry Safonov } 2427faadfabaSDmitry Safonov 2428faadfabaSDmitry Safonov opt.snt_isn = ao->lisn; 2429faadfabaSDmitry Safonov opt.rcv_isn = ao->risn; 2430faadfabaSDmitry Safonov opt.snd_sne = READ_ONCE(ao->snd_sne); 2431faadfabaSDmitry Safonov opt.rcv_sne = READ_ONCE(ao->rcv_sne); 2432faadfabaSDmitry Safonov rcu_read_unlock(); 2433faadfabaSDmitry Safonov 2434faadfabaSDmitry Safonov if (copy_to_sockptr(optval, &opt, min_t(int, len, sizeof(opt)))) 2435faadfabaSDmitry Safonov return -EFAULT; 2436faadfabaSDmitry Safonov return 0; 2437faadfabaSDmitry Safonov } 2438