From nobody Fri Sep 12 00:45:51 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 12D81C636D4 for ; Wed, 15 Feb 2023 18:37:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229959AbjBOShF (ORCPT ); Wed, 15 Feb 2023 13:37:05 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51564 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229714AbjBOSgZ (ORCPT ); Wed, 15 Feb 2023 13:36:25 -0500 Received: from mail-wm1-x332.google.com (mail-wm1-x332.google.com [IPv6:2a00:1450:4864:20::332]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5FE5C3A0B4 for ; Wed, 15 Feb 2023 10:34:31 -0800 (PST) Received: by mail-wm1-x332.google.com with SMTP id p5so1603942wmg.4 for ; Wed, 15 Feb 2023 10:34:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=56glGZIk8DVndoPnOZ1LZxON4qiQryUzSa5rkzkLnEg=; b=huUU9RDP0P7/VshnMi4S5ttTvf/I/uZ4K7cuNgNSvhPw2NrPpJZre4lUkHyJ9yKCcP uEy4NGXG65aUXB3RkWbliA/hp1IelN3EMwJPpVluEr770+UOMZNX9eStUu+xj+R2ZUZv vEmSOQANluxMs+nIDzQF62zRp1/iRYk8gVTf5gNA+a5TCcV/xBQzIqoOhTTLjKZ1Kno4 Sr6nYKbJORzqkgAMwzk+G2ULXpuP467gCUUoZaHawBxrkmhnZ6/8ZGnop6yRNFqTbhsJ hNl0mrgiL6MBdcWJuT3NvZEeZYWIiU3Y4l1KN4xEDM4tj49XPVf/0GjZmLdDdysDAURn +muw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=56glGZIk8DVndoPnOZ1LZxON4qiQryUzSa5rkzkLnEg=; b=xVhwAOEmLKgwhQMzT+EAVOcLyCzPCYZhHFf9I+0V7B5FCDPGSHPb3azY5wBILPqUBb 28yQ4gJnymVHa+ijhd11ZaNIuLOCDEKZmO2gmoRVsRHd3kfbmNvo8a5fe+vGj7xJLexg CJmdPsh8NrEK0nP11i0/5ExygobDUtpV81PlYPyQErLhqUzrz1e9TgwyltAsr3aNJlma GXovu06+I+6MmqyKtjepTcoCrYN1vadOZ+LKcgXXiO6laybIuc9mAv/6AJP4hkOXy/Gx DtpC9C6UUdCO4AfTUVuSRQhOvaASrXsj/7mpNS8oz8TUFNgjr7D3U4OVMkYJCTvoMS6B k8Zw== X-Gm-Message-State: AO0yUKW5zPBThUFFL1gvDACodz1pu87unyD/0BvFJ7jgc2Eh3oZ98Ixf rtnCr79YWHceXYeYd6tL56VVNgU9kTvjvhOe X-Google-Smtp-Source: AK7set/VplH0NpwRvnAxScYqRmef+4VYSIg31Kn173o5XBfy2gj0x5nEYNsrIwqSZmi6Zc/VKlYuyg== X-Received: by 2002:a05:600c:244e:b0:3df:57aa:db52 with SMTP id 14-20020a05600c244e00b003df57aadb52mr2919390wmr.4.1676486061170; Wed, 15 Feb 2023 10:34:21 -0800 (PST) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id s9-20020a05600c45c900b003e00c9888besm3196306wmo.30.2023.02.15.10.34.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 15 Feb 2023 10:34:20 -0800 (PST) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org Subject: [PATCH v4 21/21] net/tcp-ao: Wire up l3index to TCP-AO Date: Wed, 15 Feb 2023 18:33:35 +0000 Message-Id: <20230215183335.800122-22-dima@arista.com> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230215183335.800122-1-dima@arista.com> References: <20230215183335.800122-1-dima@arista.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Similarly how TCP_MD5SIG_FLAG_IFINDEX works for TCP-MD5, TCP_AO_KEYF_IFINDEX is an AO-key flag that binds that MKT to a specified by tcpa_ifinndex L3 interface. Similarly, without this flag the key will work in the default VRF l3index =3D 0 for connections. To prevent AO-keys from overlapping, it's restricted to add key B for a socket that has key A, which have the same sndid/rcvid and one of the following is true: - !(A.keyflags & TCP_AO_KEYF_IFINDEX) or !(B.keyflags & TCP_AO_KEYF_IFINDEX) so that any key is non-bound to a VRF - A.l3index =3D=3D B.l3index both want to work for the same VRF Additionally, it's restricted to match TCP-MD5 keys for the same peer the following way: |--------------|--------------------|----------------|---------------| | | MD5 key without | MD5 key | MD5 key | | | l3index | l3index=3D0 | l3index=3DN | |--------------|--------------------|----------------|---------------| | TCP-AO key | | | | | without | reject | reject | reject | | l3index | | | | |--------------|--------------------|----------------|---------------| | TCP-AO key | | | | | l3index=3D0 | reject | reject | allow | |--------------|--------------------|----------------|---------------| | TCP-AO key | | | | | l3index=3DN | reject | allow | reject | |--------------|--------------------|----------------|---------------| This is done with the help of tcp_md5_do_lookup_any_l3index() to reject adding AO key without TCP_AO_KEYF_IFINDEX if there's TCP-MD5 in any VRF. This is important for case where sysctl_tcp_l3mdev_accept =3D 1 Similarly, for TCP-AO lookups tcp_ao_do_lookup() may be used with l3index < 0, so that __tcp_ao_key_cmp() will match TCP-AO key in any VRF. Signed-off-by: Dmitry Safonov --- include/net/tcp.h | 10 +-- include/net/tcp_ao.h | 17 +++-- net/ipv4/syncookies.c | 6 +- net/ipv4/tcp_ao.c | 162 +++++++++++++++++++++++++++++++----------- net/ipv4/tcp_ipv4.c | 18 +++-- net/ipv6/syncookies.c | 5 +- net/ipv6/tcp_ao.c | 20 +++--- net/ipv6/tcp_ipv6.c | 24 +++++-- 8 files changed, 181 insertions(+), 81 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 7b1d8b05e3bc..1576c5a1f154 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2607,7 +2607,8 @@ tcp_inbound_hash(struct sock *sk, const struct reques= t_sock *req, * always at least one current_key. */ #ifdef CONFIG_TCP_AO - if (unlikely(tcp_ao_do_lookup(sk, saddr, family, -1, -1, 0))) { + if (unlikely(tcp_ao_do_lookup(sk, l3index, saddr, + family, -1, -1, 0))) { struct tcp_ao_info *ao_info; =20 ao_info =3D rcu_dereference_check(tcp_sk(sk)->ao_info, @@ -2615,20 +2616,21 @@ tcp_inbound_hash(struct sock *sk, const struct requ= est_sock *req, NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOREQUIRED); atomic64_inc(&ao_info->counters.ao_required); tcp_hash_fail("AO hash is required, but not found", - family, skb, ""); + family, skb, "L3 index %d", l3index); return SKB_DROP_REASON_TCP_AONOTFOUND; } #endif if (unlikely(tcp_md5_do_lookup(sk, l3index, saddr, family))) { NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5NOTFOUND); - tcp_hash_fail("MD5 Hash not found", family, skb, ""); + tcp_hash_fail("MD5 Hash not found", + family, skb, "L3 index %d", l3index); return SKB_DROP_REASON_TCP_MD5NOTFOUND; } return SKB_NOT_DROPPED_YET; } =20 if (aoh) - return tcp_inbound_ao_hash(sk, skb, family, req, aoh); + return tcp_inbound_ao_hash(sk, skb, family, req, l3index, aoh); =20 return tcp_inbound_md5_hash(sk, skb, saddr, daddr, family, l3index, md5_location); diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 253cf2719aed..b783af572c02 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -33,6 +33,7 @@ struct tcp_ao_key { union tcp_ao_addr addr; u8 key[TCP_AO_MAXKEYLEN] __tcp_ao_key_align; unsigned int tcp_sigpool_id; + int l3index; u16 port; u8 prefixlen; u8 family; @@ -177,9 +178,9 @@ bool tcp_ao_ignore_icmp(struct sock *sk, int type, int = code); int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen); enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, unsigned short int family, - const struct request_sock *req, + const struct request_sock *req, int l3index, const struct tcp_ao_hdr *aoh); -struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, +struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, int l3index, const union tcp_ao_addr *addr, int family, int sndid, int rcvid, u16 port); int tcp_ao_hash_hdr(unsigned short family, char *ao_hash, @@ -224,9 +225,6 @@ int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *k= ey, __be32 disn, bool send); int tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, struct request_sock *req); -struct tcp_ao_key *tcp_v6_ao_do_lookup(const struct sock *sk, - const struct in6_addr *addr, - int sndid, int rcvid); struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk, struct sock *addr_sk, int sndid, int rcvid); @@ -245,12 +243,12 @@ void tcp_ao_finish_connect(struct sock *sk, struct sk= _buff *skb); void tcp_ao_connect_init(struct sock *sk); void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, struct tcp_request_sock *treq, - unsigned short int family); + unsigned short int family, int l3index); #else /* CONFIG_TCP_AO */ =20 static inline void tcp_ao_syncookie(struct sock *sk, const struct sk_buff = *skb, struct tcp_request_sock *treq, - unsigned short int family) + unsigned short int family, int l3index) { } =20 @@ -261,13 +259,14 @@ static inline bool tcp_ao_ignore_icmp(struct sock *sk= , int type, int code) =20 static inline enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, unsigned short int family, - const struct request_sock *req, const struct tcp_ao_hdr *aoh) + const struct request_sock *req, int l3index, + const struct tcp_ao_hdr *aoh) { return SKB_NOT_DROPPED_YET; } =20 static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, - const union tcp_ao_addr *addr, + int l3index, const union tcp_ao_addr *addr, int family, int sndid, int rcvid, u16 port) { return NULL; diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 9e3d7083af7d..a994a3405a67 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -337,6 +337,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk= _buff *skb) struct rtable *rt; __u8 rcv_wscale; struct flowi4 fl4; + int l3index; u32 tsoff =3D 0; =20 if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies) || @@ -394,13 +395,14 @@ struct sock *cookie_v4_check(struct sock *sk, struct = sk_buff *skb) treq->snt_synack =3D 0; treq->tfo_listener =3D false; =20 - tcp_ao_syncookie(sk, skb, treq, AF_INET); - if (IS_ENABLED(CONFIG_SMC)) ireq->smc_ok =3D 0; =20 ireq->ir_iif =3D inet_request_bound_dev_if(sk, skb); =20 + l3index =3D l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif); + tcp_ao_syncookie(sk, skb, treq, AF_INET, l3index); + /* We throwed the options of the initial SYN away, so we hope * the ACK carries the same options again (see RFC1122 4.2.3.8) */ diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index adb25e42f64a..b58aad7a47a7 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -146,7 +146,7 @@ static inline int ipv4_prefix_cmp(const struct in_addr = *addr1, return ((addr1->s_addr & mask) > (addr2->s_addr & mask)) ? 1 : -1; } =20 -static int __tcp_ao_key_cmp(const struct tcp_ao_key *key, +static int __tcp_ao_key_cmp(const struct tcp_ao_key *key, int l3index, const union tcp_ao_addr *addr, u8 prefixlen, int family, int sndid, int rcvid, u16 port) { @@ -156,6 +156,10 @@ static int __tcp_ao_key_cmp(const struct tcp_ao_key *k= ey, return (key->rcvid > rcvid) ? 1 : -1; if (port !=3D 0 && key->port !=3D 0 && port !=3D key->port) return (key->port > port) ? 1 : -1; + if (l3index >=3D 0 && (key->keyflags & TCP_AO_KEYF_IFINDEX)) { + if (key->l3index !=3D l3index) + return (key->l3index > l3index) ? 1 : -1; + } =20 if (family =3D=3D AF_UNSPEC) return 0; @@ -180,7 +184,7 @@ static int __tcp_ao_key_cmp(const struct tcp_ao_key *ke= y, return -1; } =20 -static int tcp_ao_key_cmp(const struct tcp_ao_key *key, +static int tcp_ao_key_cmp(const struct tcp_ao_key *key, int l3index, const union tcp_ao_addr *addr, u8 prefixlen, int family, int sndid, int rcvid, u16 port) { @@ -188,14 +192,16 @@ static int tcp_ao_key_cmp(const struct tcp_ao_key *ke= y, if (family =3D=3D AF_INET6 && ipv6_addr_v4mapped(&addr->a6)) { __be32 addr4 =3D addr->a6.s6_addr32[3]; =20 - return __tcp_ao_key_cmp(key, (union tcp_ao_addr *)&addr4, + return __tcp_ao_key_cmp(key, l3index, + (union tcp_ao_addr *)&addr4, prefixlen, AF_INET, sndid, rcvid, port); } #endif - return __tcp_ao_key_cmp(key, addr, prefixlen, family, sndid, rcvid, port); + return __tcp_ao_key_cmp(key, l3index, addr, + prefixlen, family, sndid, rcvid, port); } =20 -struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, +struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, int l3index, const union tcp_ao_addr *addr, int family, int sndid, int rcvid, u16 port) { @@ -211,7 +217,7 @@ struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *= sk, return NULL; =20 hlist_for_each_entry_rcu(key, &ao->head, node) { - if (!tcp_ao_key_cmp(key, addr, key->prefixlen, + if (!tcp_ao_key_cmp(key, l3index, addr, key->prefixlen, family, sndid, rcvid, port)) return key; } @@ -690,41 +696,46 @@ struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct = sock *sk, struct request_sock *req, int sndid, int rcvid) { - union tcp_ao_addr *addr =3D - (union tcp_ao_addr *)&inet_rsk(req)->ir_rmt_addr; + struct inet_request_sock *ireq =3D inet_rsk(req); + union tcp_ao_addr *addr =3D (union tcp_ao_addr *)&ireq->ir_rmt_addr; + int l3index =3D l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif= ); =20 - return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid, 0); + return tcp_ao_do_lookup(sk, l3index, addr, AF_INET, sndid, rcvid, 0); } =20 struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *ad= dr_sk, int sndid, int rcvid) { union tcp_ao_addr *addr =3D (union tcp_ao_addr *)&addr_sk->sk_daddr; + int l3index =3D l3mdev_master_ifindex_by_index(sock_net(sk), + addr_sk->sk_bound_dev_if); =20 - return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid, 0); + return tcp_ao_do_lookup(sk, l3index, addr, AF_INET, sndid, rcvid, 0); } =20 static struct tcp_ao_key *tcp_ao_inbound_lookup(unsigned short int family, const struct sock *sk, const struct sk_buff *skb, - int sndid, int rcvid) + int sndid, int rcvid, int l3index) { if (family =3D=3D AF_INET) { const struct iphdr *iph =3D ip_hdr(skb); =20 - return tcp_ao_do_lookup(sk, (union tcp_ao_addr *)&iph->saddr, - AF_INET, sndid, rcvid, 0); + return tcp_ao_do_lookup(sk, l3index, + (union tcp_ao_addr *)&iph->saddr, + AF_INET, sndid, rcvid, 0); } else { const struct ipv6hdr *iph =3D ipv6_hdr(skb); =20 - return tcp_ao_do_lookup(sk, (union tcp_ao_addr *)&iph->saddr, - AF_INET6, sndid, rcvid, 0); + return tcp_ao_do_lookup(sk, l3index, + (union tcp_ao_addr *)&iph->saddr, + AF_INET6, sndid, rcvid, 0); } } =20 /* Returns maclen of requested key if found */ void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, struct tcp_request_sock *treq, - unsigned short int family) + unsigned short int family, int l3index) { const struct tcphdr *th =3D tcp_hdr(skb); const struct tcp_ao_hdr *aoh; @@ -739,7 +750,7 @@ void tcp_ao_syncookie(struct sock *sk, const struct sk_= buff *skb, if (!aoh) return; =20 - key =3D tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid); + key =3D tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid, l3index); if (!key) /* Key not found, continue without TCP-AO */ return; @@ -753,7 +764,7 @@ static enum skb_drop_reason tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb, unsigned short int family, struct tcp_ao_info *info, const struct tcp_ao_hdr *aoh, struct tcp_ao_key *key, - u8 *traffic_key, u8 *phash, u32 sne) + u8 *traffic_key, u8 *phash, u32 sne, int l3index) { unsigned char newhash[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align; u8 maclen =3D aoh->length - sizeof(struct tcp_ao_hdr); @@ -764,7 +775,8 @@ tcp_ao_verify_hash(const struct sock *sk, const struct = sk_buff *skb, atomic64_inc(&info->counters.pkt_bad); atomic64_inc(&key->pkt_bad); tcp_hash_fail("AO hash wrong length", family, skb, - " %u !=3D %d", maclen, tcp_ao_maclen(key)); + " %u !=3D %d L3index: %d", maclen, + tcp_ao_maclen(key), l3index); return SKB_DROP_REASON_TCP_AOFAILURE; } =20 @@ -775,7 +787,8 @@ tcp_ao_verify_hash(const struct sock *sk, const struct = sk_buff *skb, NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD); atomic64_inc(&info->counters.pkt_bad); atomic64_inc(&key->pkt_bad); - tcp_hash_fail("AO hash mismatch", family, skb, ""); + tcp_hash_fail("AO hash mismatch", family, skb, + " L3index: %d", l3index); return SKB_DROP_REASON_TCP_AOFAILURE; } NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOGOOD); @@ -787,7 +800,7 @@ tcp_ao_verify_hash(const struct sock *sk, const struct = sk_buff *skb, enum skb_drop_reason tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb, unsigned short int family, const struct request_sock *req, - const struct tcp_ao_hdr *aoh) + int l3index, const struct tcp_ao_hdr *aoh) { u8 key_buf[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align; const struct tcphdr *th =3D tcp_hdr(skb); @@ -802,7 +815,7 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_bu= ff *skb, if (!info) { NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND); tcp_hash_fail("AO key not found", family, skb, - " keyid: %u", aoh->keyid); + " keyid: %u L3index: %d", aoh->keyid, l3index); return SKB_DROP_REASON_TCP_AOUNEXPECTED; } =20 @@ -836,7 +849,7 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_bu= ff *skb, /* Established socket, traffic key are cached */ traffic_key =3D rcv_other_key(key); err =3D tcp_ao_verify_hash(sk, skb, family, info, aoh, key, - traffic_key, phash, sne); + traffic_key, phash, sne, l3index); if (err) return err; /* Key rotation: the peer asks us to use new key (RNext) */ @@ -856,7 +869,7 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_bu= ff *skb, * - request sockets would race on those key pointers * - tcp_ao_del_cmd() allows async key removal */ - key =3D tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid); + key =3D tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid, l3index); if (!key) goto key_not_found; =20 @@ -894,13 +907,13 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_= buff *skb, traffic_key =3D key_buf; tcp_ao_calc_key_skb(key, traffic_key, skb, sisn, disn, family); return tcp_ao_verify_hash(sk, skb, family, info, aoh, key, - traffic_key, phash, sne); + traffic_key, phash, sne, l3index); =20 key_not_found: NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND); atomic64_inc(&info->counters.key_not_found); tcp_hash_fail("Requested by the peer AO key id not found", - family, skb, ""); + family, skb, " L3index: %d", l3index); return SKB_DROP_REASON_TCP_AOKEYNOTFOUND; } =20 @@ -927,7 +940,7 @@ void tcp_ao_connect_init(struct sock *sk) struct tcp_ao_key *key; struct tcp_sock *tp =3D tcp_sk(sk); union tcp_ao_addr *addr; - int family; + int family, l3index; =20 ao_info =3D rcu_dereference_protected(tp->ao_info, lockdep_sock_is_held(sk)); @@ -944,9 +957,11 @@ void tcp_ao_connect_init(struct sock *sk) #endif else return; + l3index =3D l3mdev_master_ifindex_by_index(sock_net(sk), + sk->sk_bound_dev_if); =20 hlist_for_each_entry_rcu(key, &ao_info->head, node) { - if (tcp_ao_key_cmp(key, addr, key->prefixlen, family, + if (tcp_ao_key_cmp(key, l3index, addr, key->prefixlen, family, -1, -1, sk->sk_dport) =3D=3D 0) continue; =20 @@ -1010,7 +1025,7 @@ int tcp_ao_copy_all_matching(const struct sock *sk, s= truct sock *newsk, struct hlist_node *key_head; union tcp_ao_addr *addr; bool match =3D false; - int ret =3D -ENOMEM; + int l3index, ret =3D -ENOMEM; =20 ao =3D rcu_dereference(tcp_sk(sk)->ao_info); if (!ao) @@ -1036,9 +1051,11 @@ int tcp_ao_copy_all_matching(const struct sock *sk, = struct sock *newsk, ret =3D -EAFNOSUPPORT; goto free_ao; } + l3index =3D l3mdev_master_ifindex_by_index(sock_net(newsk), + newsk->sk_bound_dev_if); =20 hlist_for_each_entry_rcu(key, &ao->head, node) { - if (tcp_ao_key_cmp(key, addr, key->prefixlen, family, + if (tcp_ao_key_cmp(key, l3index, addr, key->prefixlen, family, -1, -1, 0)) continue; =20 @@ -1352,6 +1369,11 @@ static bool tcp_ao_mkt_overlap_v4(struct tcp_ao *cmd, if (key->sndid !=3D sndid && key->rcvid !=3D rcvid) continue; =20 + if (key->keyflags & TCP_AO_KEYF_IFINDEX && + cmd->tcpa_keyflags & TCP_AO_KEYF_IFINDEX && + key->l3index !=3D cmd->tcpa_ifindex) + continue; + key_addr =3D key->addr.a4.s_addr; mask =3D inet_make_mask(min(prefix, key->prefixlen)); =20 @@ -1462,6 +1484,11 @@ static bool tcp_ao_mkt_overlap_v6(struct tcp_ao *cmd, if (key->sndid !=3D sndid && key->rcvid !=3D rcvid) continue; =20 + if (key->keyflags & TCP_AO_KEYF_IFINDEX && + cmd->tcpa_keyflags & TCP_AO_KEYF_IFINDEX && + key->l3index !=3D cmd->tcpa_ifindex) + continue; + key_addr =3D &key->addr.a6; =20 if (v4_mapped) { @@ -1515,23 +1542,24 @@ static inline bool tcp_ao_mkt_overlap_v6(struct tcp= _ao *cmd, } #endif =20 -#define TCP_AO_KEYF_ALL (TCP_AO_KEYF_EXCLUDE_OPT) +#define TCP_AO_KEYF_ALL (TCP_AO_KEYF_IFINDEX | TCP_AO_KEYF_EXCLUDE_OPT) #define TCP_AO_CMDF_ADDMOD_VALID \ (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT | TCP_AO_CMDF_ACCEPT_ICMP) #define TCP_AO_CMDF_DEL_VALID \ (TCP_AO_CMDF_CURR | TCP_AO_CMDF_NEXT | TCP_AO_CMDF_DEL_ASYNC) #define TCP_AO_GETF_VALID \ (TCP_AO_GET_ALL | TCP_AO_GET_CURR | TCP_AO_GET_NEXT) +#define TCP_AO_GET_KEYF_VALID (TCP_AO_KEYF_IFINDEX) =20 static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, sockptr_t optval, int optlen) { struct tcp_ao_info *ao_info; + int ret, size, l3index =3D 0; union tcp_md5_addr *addr; struct tcp_ao_key *key; bool first =3D false; struct tcp_ao cmd; - int ret, size; u16 port; =20 if (optlen < sizeof(cmd)) @@ -1560,9 +1588,33 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned = short int family, if (cmd.tcpa_keyflags & ~TCP_AO_KEYF_ALL) return -EINVAL; =20 + /* For cmd.tcp_ifindex =3D 0 the key will apply to the default VRF */ + if (cmd.tcpa_keyflags & TCP_AO_KEYF_IFINDEX && cmd.tcpa_ifindex) { + struct net_device *dev; + + rcu_read_lock(); + dev =3D dev_get_by_index_rcu(sock_net(sk), cmd.tcpa_ifindex); + if (dev && netif_is_l3_master(dev)) + l3index =3D dev->ifindex; + rcu_read_unlock(); + + if (!dev || !l3index) + return -EINVAL; + } + /* Don't allow keys for peers that have a matching TCP-MD5 key */ - if (tcp_md5_do_lookup_any_l3index(sk, addr, family)) - return -EKEYREJECTED; + if (cmd.tcpa_keyflags & TCP_AO_KEYF_IFINDEX) { + /* Non-_exact version of tcp_md5_do_lookup() will + * as well match keys that aren't bound to a specific VRF + * (that will make them match AO key with + * sysctl_tcp_l3dev_accept =3D 1 + */ + if (tcp_md5_do_lookup(sk, l3index, addr, family)) + return -EKEYREJECTED; + } else { + if (tcp_md5_do_lookup_any_l3index(sk, addr, family)) + return -EKEYREJECTED; + } =20 ao_info =3D rcu_dereference_protected(tcp_sk(sk)->ao_info, lockdep_sock_is_held(sk)); @@ -1602,6 +1654,7 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned s= hort int family, key->keyflags =3D cmd.tcpa_keyflags; key->sndid =3D cmd.tcpa_sndid; key->rcvid =3D cmd.tcpa_rcvid; + key->l3index =3D l3index; atomic64_set(&key->pkt_good, 0); atomic64_set(&key->pkt_bad, 0); =20 @@ -1692,17 +1745,17 @@ static int tcp_ao_delete_key(struct sock *sk, struc= t tcp_ao_key *key, return err; } =20 +#define TCP_AO_DEL_KEYF_ALL (TCP_AO_KEYF_IFINDEX) static int tcp_ao_del_cmd(struct sock *sk, unsigned short int family, sockptr_t optval, int optlen) { + int err, addr_len, l3index =3D 0; struct tcp_ao_info *ao_info; struct tcp_ao_key *key; struct tcp_ao_del cmd; - int err; union tcp_md5_addr *addr; __u8 prefix; __be16 port; - int addr_len; =20 if (optlen < sizeof(cmd)) return -EINVAL; @@ -1714,9 +1767,26 @@ static int tcp_ao_del_cmd(struct sock *sk, unsigned = short int family, if (cmd.tcpa_flags & ~TCP_AO_CMDF_DEL_VALID) return -EINVAL; =20 + if (cmd.tcpa_keyflags & ~TCP_AO_DEL_KEYF_ALL) + return -EINVAL; + if (cmd.reserved !=3D 0) return -EINVAL; =20 + /* For cmd.tcp_ifindex =3D 0 the key will apply to the default VRF */ + if (cmd.tcpa_keyflags & TCP_AO_KEYF_IFINDEX && cmd.tcpa_ifindex) { + struct net_device *dev; + + rcu_read_lock(); + dev =3D dev_get_by_index_rcu(sock_net(sk), cmd.tcpa_ifindex); + if (dev && netif_is_l3_master(dev)) + l3index =3D dev->ifindex; + rcu_read_unlock(); + + if (!dev || !l3index) + return -EINVAL; + } + ao_info =3D rcu_dereference_protected(tcp_sk(sk)->ao_info, lockdep_sock_is_held(sk)); if (!ao_info) @@ -1762,6 +1832,13 @@ static int tcp_ao_del_cmd(struct sock *sk, unsigned = short int family, memcmp(addr, &key->addr, addr_len)) continue; =20 + if ((cmd.tcpa_keyflags & TCP_AO_KEYF_IFINDEX) !=3D + (key->keyflags & TCP_AO_KEYF_IFINDEX)) + continue; + + if (key->l3index !=3D l3index) + continue; + return tcp_ao_delete_key(sk, key, ao_info, &cmd); } return -ENOENT; @@ -1878,7 +1955,7 @@ static int tcp_ao_copy_mkts_to_user(struct tcp_ao_inf= o *ao_info, u8 prefix_in; u16 port =3D 0; bool copy_all, copy_current, copy_next; - int err; + int err, l3index; =20 if (copy_from_sockptr(&user_len, optlen, sizeof(int))) return -EFAULT; @@ -1892,19 +1969,21 @@ static int tcp_ao_copy_mkts_to_user(struct tcp_ao_i= nfo *ao_info, if (err < 0) return err; =20 - if (opt_in.reserved !=3D 0) - return -EINVAL; - ss_family =3D opt_in.addr.ss_family; =20 BUILD_BUG_ON(TCP_AO_GET_ALL & (TCP_AO_GET_CURR | TCP_AO_GET_NEXT)); if (opt_in.flags & ~TCP_AO_GETF_VALID) return -EINVAL; + if (opt_in.keyflags & ~TCP_AO_GET_KEYF_VALID) + return -EINVAL; + if (opt_in.ifindex < 0) + return -EINVAL; =20 max_keys =3D opt_in.nkeys; copy_all =3D !!(opt_in.flags & TCP_AO_GET_ALL); copy_current =3D !!(opt_in.flags & TCP_AO_GET_CURR); copy_next =3D !!(opt_in.flags & TCP_AO_GET_NEXT); + l3index =3D (opt_in.keyflags & TCP_AO_KEYF_IFINDEX) ? opt_in.ifindex : -1; =20 if (!(copy_all || copy_current || copy_next)) { prefix_in =3D opt_in.prefix; @@ -1969,7 +2048,7 @@ static int tcp_ao_copy_mkts_to_user(struct tcp_ao_inf= o *ao_info, continue; } =20 - if (tcp_ao_key_cmp(key, addr, opt_in.prefix, + if (tcp_ao_key_cmp(key, l3index, addr, opt_in.prefix, opt_in.addr.ss_family, opt_in.sndid, opt_in.rcvid, port) !=3D 0) continue; @@ -2005,6 +2084,7 @@ static int tcp_ao_copy_mkts_to_user(struct tcp_ao_inf= o *ao_info, opt_out.nkeys =3D 0; opt_out.maclen =3D key->maclen; opt_out.keylen =3D key->keylen; + opt_out.ifindex =3D key->l3index; memcpy(&opt_out.key, key->key, key->keylen); tcp_sigpool_algo(key->tcp_sigpool_id, opt_out.alg_name, 64); =20 diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 64a81f1dd4d4..c1dcfc70930a 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1093,6 +1093,7 @@ static void tcp_v4_reqsk_send_ack(const struct sock *= sk, struct sk_buff *skb, struct tcp_ao_key *ao_key =3D NULL; const union tcp_md5_addr *addr; u8 keyid =3D 0; + int l3index; #ifdef CONFIG_TCP_AO u8 traffic_key[TCP_AO_MAX_HASH_SIZE] __tcp_ao_key_align; const struct tcp_ao_hdr *aoh; @@ -1106,6 +1107,7 @@ static void tcp_v4_reqsk_send_ack(const struct sock *= sk, struct sk_buff *skb, u32 seq =3D (sk->sk_state =3D=3D TCP_LISTEN) ? tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt; addr =3D (union tcp_md5_addr *)&ip_hdr(skb)->saddr; + l3index =3D tcp_v4_sdif(skb) ? inet_iif(skb) : 0; =20 if (tcp_rsk_used_ao(req)) { #ifdef CONFIG_TCP_AO @@ -1116,11 +1118,11 @@ static void tcp_v4_reqsk_send_ack(const struct sock= *sk, struct sk_buff *skb, if (!aoh) return; =20 - ao_key =3D tcp_ao_do_lookup(sk, addr, AF_INET, + ao_key =3D tcp_ao_do_lookup(sk, l3index, addr, AF_INET, aoh->rnext_keyid, -1, 0); if (unlikely(!ao_key)) { /* Send ACK with any matching MKT for the peer */ - ao_key =3D tcp_ao_do_lookup(sk, addr, + ao_key =3D tcp_ao_do_lookup(sk, l3index, addr, AF_INET, -1, -1, 0); /* Matching key disappeared (user removed the key?) * let the handshake timeout. @@ -1139,9 +1141,6 @@ static void tcp_v4_reqsk_send_ack(const struct sock *= sk, struct sk_buff *skb, tcp_v4_ao_calc_key_rsk(ao_key, traffic_key, req); #endif } else { - int l3index; - - l3index =3D tcp_v4_sdif(skb) ? inet_iif(skb) : 0; md5_key =3D tcp_md5_do_lookup(sk, l3index, addr, AF_INET); } /* RFC 7323 2.3 @@ -1497,6 +1496,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int= optname, struct tcp_md5sig cmd; struct sockaddr_in *sin =3D (struct sockaddr_in *)&cmd.tcpm_addr; const union tcp_md5_addr *addr; + bool l3flag =3D false; u8 prefixlen =3D 32; int l3index =3D 0; u8 flags; @@ -1523,6 +1523,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, int= optname, cmd.tcpm_flags & TCP_MD5SIG_FLAG_IFINDEX) { struct net_device *dev; =20 + l3flag =3D true; rcu_read_lock(); dev =3D dev_get_by_index_rcu(sock_net(sk), cmd.tcpm_ifindex); if (dev && netif_is_l3_master(dev)) @@ -1545,8 +1546,11 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, in= t optname, if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) return -EINVAL; =20 - /* Don't allow keys for peers that have a matching TCP-AO key */ - if (tcp_ao_do_lookup(sk, addr, AF_INET, -1, -1, 0)) + /* Don't allow keys for peers that have a matching TCP-AO key. + * See the comment in tcp_ao_add_cmd() + */ + if (tcp_ao_do_lookup(sk, l3flag ? l3index : -1, addr, + AF_INET, -1, -1, 0)) return -EKEYREJECTED; =20 return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags, diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index ad7a8caa7b2a..500f6ed3b8cf 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -140,6 +140,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk= _buff *skb) struct dst_entry *dst; __u8 rcv_wscale; u32 tsoff =3D 0; + int l3index; =20 if (!READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_syncookies) || !th->ack || th->rst) @@ -214,7 +215,9 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk= _buff *skb) treq->snt_isn =3D cookie; treq->ts_off =3D 0; treq->txhash =3D net_tx_rndhash(); - tcp_ao_syncookie(sk, skb, treq, AF_INET6); + + l3index =3D l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif); + tcp_ao_syncookie(sk, skb, treq, AF_INET6, l3index); =20 if (IS_ENABLED(CONFIG_SMC)) ireq->smc_ok =3D 0; diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c index 77dfa706349c..ac24721b6b76 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -77,30 +77,28 @@ int tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *= key, htonl(tcp_rsk(req)->rcv_isn)); } =20 -struct tcp_ao_key *tcp_v6_ao_do_lookup(const struct sock *sk, - const struct in6_addr *addr, - int sndid, int rcvid) -{ - return tcp_ao_do_lookup(sk, (union tcp_ao_addr *)addr, AF_INET6, - sndid, rcvid, 0); -} - struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk, struct sock *addr_sk, int sndid, int rcvid) { struct in6_addr *addr =3D &addr_sk->sk_v6_daddr; + int l3index =3D l3mdev_master_ifindex_by_index(sock_net(sk), + addr_sk->sk_bound_dev_if); =20 - return tcp_v6_ao_do_lookup(sk, addr, sndid, rcvid); + return tcp_ao_do_lookup(sk, l3index, (union tcp_ao_addr *)addr, + AF_INET6, sndid, rcvid, 0); } =20 struct tcp_ao_key *tcp_v6_ao_lookup_rsk(const struct sock *sk, struct request_sock *req, int sndid, int rcvid) { - struct in6_addr *addr =3D &inet_rsk(req)->ir_v6_rmt_addr; + struct inet_request_sock *ireq =3D inet_rsk(req); + struct in6_addr *addr =3D &ireq->ir_v6_rmt_addr; + int l3index =3D l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif= ); =20 - return tcp_v6_ao_do_lookup(sk, addr, sndid, rcvid); + return tcp_ao_do_lookup(sk, l3index, (union tcp_ao_addr *)addr, + AF_INET6, sndid, rcvid, 0); } =20 int tcp_v6_ao_hash_pseudoheader(struct tcp_sigpool *hp, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 5c5c8509a6e2..b71bb2cde63b 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -606,6 +606,7 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int o= ptname, struct tcp_md5sig cmd; struct sockaddr_in6 *sin6 =3D (struct sockaddr_in6 *)&cmd.tcpm_addr; union tcp_ao_addr *addr; + bool l3flag =3D false; int l3index =3D 0; u8 prefixlen; u8 flags; @@ -635,6 +636,7 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int o= ptname, cmd.tcpm_flags & TCP_MD5SIG_FLAG_IFINDEX) { struct net_device *dev; =20 + l3flag =3D true; rcu_read_lock(); dev =3D dev_get_by_index_rcu(sock_net(sk), cmd.tcpm_ifindex); if (dev && netif_is_l3_master(dev)) @@ -663,8 +665,11 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int = optname, if (ipv6_addr_v4mapped(&sin6->sin6_addr)) { addr =3D (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3]; =20 - /* Don't allow keys for peers that have a matching TCP-AO key */ - if (tcp_ao_do_lookup(sk, addr, AF_INET, -1, -1, 0)) + /* Don't allow keys for peers that have a matching TCP-AO key. + * See the comment in tcp_ao_add_cmd() + */ + if (tcp_ao_do_lookup(sk, l3flag ? l3index : -1, addr, + AF_INET, -1, -1, 0)) return -EKEYREJECTED; return tcp_md5_do_add(sk, addr, AF_INET, prefixlen, l3index, flags, @@ -673,8 +678,11 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int = optname, =20 addr =3D (union tcp_md5_addr *)&sin6->sin6_addr; =20 - /* Don't allow keys for peers that have a matching TCP-AO key */ - if (tcp_ao_do_lookup(sk, addr, AF_INET6, -1, -1, 0)) + /* Don't allow keys for peers that have a matching TCP-AO key. + * See the comment in tcp_ao_add_cmd() + */ + if (tcp_ao_do_lookup(sk, l3flag ? l3index : -1, addr, + AF_INET6, -1, -1, 0)) return -EKEYREJECTED; =20 return tcp_md5_do_add(sk, addr, AF_INET6, prefixlen, l3index, flags, @@ -1260,10 +1268,14 @@ static void tcp_v6_reqsk_send_ack(const struct sock= *sk, struct sk_buff *skb, return; if (!aoh) return; - ao_key =3D tcp_v6_ao_do_lookup(sk, addr, aoh->rnext_keyid, -1); + ao_key =3D tcp_ao_do_lookup(sk, l3index, + (union tcp_ao_addr *)addr, AF_INET6, + aoh->rnext_keyid, -1, 0); if (unlikely(!ao_key)) { /* Send ACK with any matching MKT for the peer */ - ao_key =3D tcp_v6_ao_do_lookup(sk, addr, -1, -1); + ao_key =3D tcp_ao_do_lookup(sk, l3index, + (union tcp_ao_addr *)addr, + AF_INET6, -1, -1, 0); /* Matching key disappeared (user removed the key?) * let the handshake timeout. */ --=20 2.39.1