From nobody Wed Feb 11 19:42:38 2026 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 50A84CA0ECD for ; Mon, 11 Sep 2023 22:47:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1379165AbjIKWls (ORCPT ); Mon, 11 Sep 2023 18:41:48 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:38362 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S242565AbjIKV0o (ORCPT ); Mon, 11 Sep 2023 17:26:44 -0400 Received: from mail-lf1-x12d.google.com (mail-lf1-x12d.google.com [IPv6:2a00:1450:4864:20::12d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 41A461E833 for ; Mon, 11 Sep 2023 14:16:55 -0700 (PDT) Received: by mail-lf1-x12d.google.com with SMTP id 2adb3069b0e04-502a25ab777so5160747e87.2 for ; Mon, 11 Sep 2023 14:16:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1694466935; x=1695071735; darn=vger.kernel.org; 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=F6K85PdP9lB0JzkPPamPZs3fpIiu2fUYMlR17W4DJ+I=; b=N0tc1cdATfewZ+kmUy0eRO3rqbnRBeYXN9+3eORPretYReG4IC/r+TN424En1L7my1 SG/KOCHdhAv6oCVkw/85QJGOBzuy43V3Ii4vyZoCwYA6UC0jQpUVUhhzK4ABcFB6BsWA 7zOuh1h3eeHSE2PJ/TitBGT0+yY0L/qi6nE0MHQjuiamxUbcFpXu55m+vt4WdnTE+kqq zLRn0ZJiFq7VYb0ZnaWY/pqTqDCa3+gENULEfWam7uGvjN71zHlGY0Cou9gg7JbNTDPu 7UvblLJ7eljn8GpReZWg9uaS1IpvXtXE/yzRKOtTwNCH8na1a/D7sC3PTFthr9P5OdcM yayA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1694466935; x=1695071735; 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=F6K85PdP9lB0JzkPPamPZs3fpIiu2fUYMlR17W4DJ+I=; b=RoQlYXHCymBPPjEM4wKJvbkvOiDcOZO5d6l5v4BQfGe1odsVrjKt5rqigGPx7zfWjf UKQe8FbpSFjKmujFgR12AOIpEjUYp3NcJDL2r3/iVfEF1Ngw69HjZoReVAwLdFAhs+6n TP3e4WA9wG4undskO3pRRzQEfIZglwGlCFyt7jrXTepV88w0Ey74inPy3X6DQJu9D6Mo ZIootvSlFWThWhEYIPe+AJdfhmMfBSpmfD2orlI+/B4LcbqmIoDusuVVbJvzezhoQREb MMLdzfp9Bl7LTTFe+NFY+TZRSkwog+Sa4AhAe49JB/S1lS7DXGbVd30VRBEOfEcEfF9X WKow== X-Gm-Message-State: AOJu0YwAt8vFnhKZF73fId7p4XECjfj16PQa8YaP7BRa2ofWqJtHAzd8 k7EF1qyzvMQEo1RJ/gs0NPS16wKj0/VqYzGzFFE= X-Google-Smtp-Source: AGHT+IGlxxEYeK5UiDlwaZ+gAtdfTAhzlBdS0J5ZoiI1dDaMuoE9U1qEhxr3ohCvSHeYUBrMuVDo8Q== X-Received: by 2002:a1c:7c0e:0:b0:401:6800:703c with SMTP id x14-20020a1c7c0e000000b004016800703cmr10408299wmc.21.1694466253413; Mon, 11 Sep 2023 14:04:13 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id z20-20020a1c4c14000000b00402e942561fsm14261699wmf.38.2023.09.11.14.04.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 11 Sep 2023 14:04:12 -0700 (PDT) From: Dmitry Safonov To: David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: linux-kernel@vger.kernel.org, Dmitry Safonov , Andy Lutomirski , Ard Biesheuvel , Bob Gilligan , Dan Carpenter , David Laight , Dmitry Safonov <0x7f454c46@gmail.com>, Donald Cassidy , Eric Biggers , "Eric W. Biederman" , Francesco Ruggeri , "Gaillardetz, Dominik" , Herbert Xu , Hideaki YOSHIFUJI , Ivan Delalande , Leonard Crestez , "Nassiri, Mohammad" , Salam Noureddine , Simon Horman , "Tetreault, Francois" , netdev@vger.kernel.org Subject: [PATCH v11 net-next 10/23] net/tcp: Wire TCP-AO to request sockets Date: Mon, 11 Sep 2023 22:03:30 +0100 Message-ID: <20230911210346.301750-11-dima@arista.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230911210346.301750-1-dima@arista.com> References: <20230911210346.301750-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" Now when the new request socket is created from the listening socket, it's recorded what MKT was used by the peer. tcp_rsk_used_ao() is a new helper for checking if TCP-AO option was used to create the request socket. tcp_ao_copy_all_matching() will copy all keys that match the peer on the request socket, as well as preparing them for the usage (creating traffic keys). Co-developed-by: Francesco Ruggeri Signed-off-by: Francesco Ruggeri Co-developed-by: Salam Noureddine Signed-off-by: Salam Noureddine Signed-off-by: Dmitry Safonov Acked-by: David Ahern --- include/linux/tcp.h | 18 +++ include/net/tcp.h | 6 + include/net/tcp_ao.h | 24 ++++ net/ipv4/syncookies.c | 2 + net/ipv4/tcp_ao.c | 238 ++++++++++++++++++++++++++++++++++++++- net/ipv4/tcp_input.c | 15 +++ net/ipv4/tcp_ipv4.c | 63 ++++++++++- net/ipv4/tcp_minisocks.c | 10 ++ net/ipv4/tcp_output.c | 39 ++++--- net/ipv6/syncookies.c | 2 + net/ipv6/tcp_ao.c | 38 ++++++- net/ipv6/tcp_ipv6.c | 76 +++++++++++-- 12 files changed, 497 insertions(+), 34 deletions(-) diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 4050077cb39e..fb3b43c52dd5 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -165,6 +165,11 @@ struct tcp_request_sock { * after data-in-SYN. */ u8 syn_tos; +#ifdef CONFIG_TCP_AO + u8 ao_keyid; + u8 ao_rcv_next; + u8 maclen; +#endif }; =20 static inline struct tcp_request_sock *tcp_rsk(const struct request_sock *= req) @@ -172,6 +177,19 @@ static inline struct tcp_request_sock *tcp_rsk(const s= truct request_sock *req) return (struct tcp_request_sock *)req; } =20 +static inline bool tcp_rsk_used_ao(const struct request_sock *req) +{ + /* The real length of MAC is saved in the request socket, + * signing anything with zero-length makes no sense, so here is + * a little hack.. + */ +#ifndef CONFIG_TCP_AO + return false; +#else + return tcp_rsk(req)->maclen !=3D 0; +#endif +} + #define TCP_RMEM_TO_WIN_SCALE 8 =20 struct tcp_sock { diff --git a/include/net/tcp.h b/include/net/tcp.h index b372764a33ea..5daa2e98e6a3 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2174,6 +2174,12 @@ struct tcp_request_sock_ops { const struct sock *sk, const struct sk_buff *skb); #endif +#ifdef CONFIG_TCP_AO + struct tcp_ao_key *(*ao_lookup)(const struct sock *sk, + struct request_sock *req, + int sndid, int rcvid); + int (*ao_calc_key)(struct tcp_ao_key *mkt, u8 *key, struct request_sock *= sk); +#endif #ifdef CONFIG_SYN_COOKIES __u32 (*cookie_init_seq)(const struct sk_buff *skb, __u16 *mss); diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 1d967e7b2a0e..d26d98f1b048 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -120,6 +120,9 @@ int tcp_parse_ao(struct sock *sk, int cmd, unsigned sho= rt int family, sockptr_t optval, int optlen); struct tcp_ao_key *tcp_ao_established_key(struct tcp_ao_info *ao, int sndid, int rcvid); +int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk, + struct request_sock *req, struct sk_buff *skb, + int family); int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, unsigned int len, struct tcp_sigpool *hp); void tcp_ao_destroy_sock(struct sock *sk, bool twsk); @@ -144,6 +147,11 @@ struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock = *sk, struct sock *addr_sk, int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, const struct sock *sk, __be32 sisn, __be32 disn, bool send); +int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, + struct request_sock *req); +struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct sock *sk, + struct request_sock *req, + int sndid, int rcvid); int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key, const struct sock *sk, const struct sk_buff *skb, const u8 *tkey, int hash_offset, u32 sne); @@ -151,11 +159,21 @@ int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_k= ey *key, int tcp_v6_ao_hash_pseudoheader(struct tcp_sigpool *hp, const struct in6_addr *daddr, const struct in6_addr *saddr, int nbytes); +int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, + const struct sk_buff *skb, __be32 sisn, __be32 disn); int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, const struct sock *sk, __be32 sisn, __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); +struct tcp_ao_key *tcp_v6_ao_lookup_rsk(const struct sock *sk, + struct request_sock *req, + int sndid, int rcvid); int tcp_v6_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key, const struct sock *sk, const struct sk_buff *skb, const u8 *tkey, int hash_offset, u32 sne); @@ -167,6 +185,12 @@ void tcp_ao_syncookie(struct sock *sk, const struct sk= _buff *skb, unsigned short int family); #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) +{ +} + static inline struct tcp_ao_key *tcp_ao_do_lookup(const struct sock *sk, const union tcp_ao_addr *addr, int family, int sndid, int rcvid) { diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index dc478a0574cb..23fca22bc992 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -394,6 +394,8 @@ 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 diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index 02bfb92cb36c..b114f3d901a0 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -169,6 +169,23 @@ static void tcp_ao_link_mkt(struct tcp_ao_info *ao, st= ruct tcp_ao_key *mkt) hlist_add_head_rcu(&mkt->node, &ao->head); } =20 +static struct tcp_ao_key *tcp_ao_copy_key(struct sock *sk, + struct tcp_ao_key *key) +{ + struct tcp_ao_key *new_key; + + new_key =3D sock_kmalloc(sk, tcp_ao_sizeof_key(key), + GFP_ATOMIC); + if (!new_key) + return NULL; + + *new_key =3D *key; + INIT_HLIST_NODE(&new_key->node); + tcp_sigpool_get(new_key->tcp_sigpool_id); + + return new_key; +} + static void tcp_ao_key_free_rcu(struct rcu_head *head) { struct tcp_ao_key *key =3D container_of(head, struct tcp_ao_key, rcu); @@ -290,6 +307,42 @@ static int tcp_ao_calc_key_sk(struct tcp_ao_key *mkt, = u8 *key, return -EOPNOTSUPP; } =20 +int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, + struct request_sock *req) +{ + struct inet_request_sock *ireq =3D inet_rsk(req); + + return tcp_v4_ao_calc_key(mkt, key, + ireq->ir_loc_addr, ireq->ir_rmt_addr, + htons(ireq->ir_num), ireq->ir_rmt_port, + htonl(tcp_rsk(req)->snt_isn), + htonl(tcp_rsk(req)->rcv_isn)); +} + +static int tcp_v4_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, + const struct sk_buff *skb, + __be32 sisn, __be32 disn) +{ + const struct iphdr *iph =3D ip_hdr(skb); + const struct tcphdr *th =3D tcp_hdr(skb); + + return tcp_v4_ao_calc_key(mkt, key, iph->saddr, iph->daddr, + th->source, th->dest, sisn, disn); +} + +static int tcp_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, + const struct sk_buff *skb, + __be32 sisn, __be32 disn, int family) +{ + if (family =3D=3D AF_INET) + return tcp_v4_ao_calc_key_skb(mkt, key, skb, sisn, disn); +#if IS_ENABLED(CONFIG_IPV6) + else if (family =3D=3D AF_INET6) + return tcp_v6_ao_calc_key_skb(mkt, key, skb, sisn, disn); +#endif + return -EAFNOSUPPORT; +} + static int tcp_v4_ao_hash_pseudoheader(struct tcp_sigpool *hp, __be32 daddr, __be32 saddr, int nbytes) @@ -515,6 +568,16 @@ int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_ke= y *key, tkey, hash_offset, sne); } =20 +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; + + return tcp_ao_do_lookup(sk, addr, AF_INET, sndid, rcvid); +} + struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *ad= dr_sk, int sndid, int rcvid) { @@ -528,6 +591,7 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct = sk_buff *skb, struct tcp_ao_key **key, char **traffic_key, bool *allocated_traffic_key, u8 *keyid, u32 *sne) { + const struct tcphdr *th =3D tcp_hdr(skb); struct tcp_ao_info *ao_info; =20 *allocated_traffic_key =3D false; @@ -542,7 +606,45 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct= sk_buff *skb, return -ENOTCONN; =20 if ((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_NEW_SYN_RECV)) { - return -1; + union tcp_ao_addr *addr; + unsigned int family; + __be32 disn, sisn; + + if (sk->sk_state =3D=3D TCP_NEW_SYN_RECV) { + struct request_sock *req =3D inet_reqsk(sk); + + sisn =3D htonl(tcp_rsk(req)->rcv_isn); + disn =3D htonl(tcp_rsk(req)->snt_isn); + *sne =3D 0; + } else { + sisn =3D th->seq; + disn =3D 0; + } + if (IS_ENABLED(CONFIG_IPV6) && sk->sk_family =3D=3D AF_INET6) + addr =3D (union tcp_md5_addr *)&ipv6_hdr(skb)->saddr; + else + addr =3D (union tcp_md5_addr *)&ip_hdr(skb)->saddr; + family =3D sk->sk_family; +#if IS_ENABLED(CONFIG_IPV6) + if (family =3D=3D AF_INET6 && ipv6_addr_v4mapped(&sk->sk_v6_daddr)) + family =3D AF_INET; +#endif + + sk =3D sk_const_to_full_sk(sk); + ao_info =3D rcu_dereference(tcp_sk(sk)->ao_info); + if (!ao_info) + return -ENOENT; + *key =3D tcp_ao_do_lookup(sk, addr, family, -1, aoh->rnext_keyid); + if (!*key) + return -ENOENT; + *traffic_key =3D kmalloc(tcp_ao_digest_size(*key), GFP_ATOMIC); + if (!*traffic_key) + return -ENOMEM; + *allocated_traffic_key =3D true; + if (tcp_ao_calc_key_skb(*key, *traffic_key, skb, + sisn, disn, family)) + return -1; + *keyid =3D (*key)->rcvid; } else { struct tcp_ao_key *rnext_key; =20 @@ -564,6 +666,46 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct= sk_buff *skb, return 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) +{ + if (family =3D=3D AF_INET) { + const struct iphdr *iph =3D ip_hdr(skb); + + return tcp_ao_do_lookup(sk, (union tcp_ao_addr *)&iph->saddr, + AF_INET, sndid, rcvid); + } else { + const struct ipv6hdr *iph =3D ipv6_hdr(skb); + + return tcp_ao_do_lookup(sk, (union tcp_ao_addr *)&iph->saddr, + AF_INET6, sndid, rcvid); + } +} + +void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb, + struct tcp_request_sock *treq, + unsigned short int family) +{ + const struct tcphdr *th =3D tcp_hdr(skb); + const struct tcp_ao_hdr *aoh; + struct tcp_ao_key *key; + + treq->maclen =3D 0; + + if (tcp_parse_auth_options(th, NULL, &aoh) || !aoh) + return; + + key =3D tcp_ao_inbound_lookup(family, sk, skb, -1, aoh->keyid); + if (!key) + /* Key not found, continue without TCP-AO */ + return; + + treq->ao_rcv_next =3D aoh->keyid; + treq->ao_keyid =3D aoh->rnext_keyid; + treq->maclen =3D tcp_ao_maclen(key); +} + static int tcp_ao_cache_traffic_keys(const struct sock *sk, struct tcp_ao_info *ao, struct tcp_ao_key *ao_key) @@ -655,6 +797,100 @@ void tcp_ao_finish_connect(struct sock *sk, struct sk= _buff *skb) tcp_ao_cache_traffic_keys(sk, ao, key); } =20 +int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk, + struct request_sock *req, struct sk_buff *skb, + int family) +{ + struct tcp_ao_key *key, *new_key, *first_key; + struct tcp_ao_info *new_ao, *ao; + struct hlist_node *key_head; + union tcp_ao_addr *addr; + bool match =3D false; + int ret =3D -ENOMEM; + + ao =3D rcu_dereference(tcp_sk(sk)->ao_info); + if (!ao) + return 0; + + /* New socket without TCP-AO on it */ + if (!tcp_rsk_used_ao(req)) + return 0; + + new_ao =3D tcp_ao_alloc_info(GFP_ATOMIC); + if (!new_ao) + return -ENOMEM; + new_ao->lisn =3D htonl(tcp_rsk(req)->snt_isn); + new_ao->risn =3D htonl(tcp_rsk(req)->rcv_isn); + new_ao->ao_required =3D ao->ao_required; + + if (family =3D=3D AF_INET) { + addr =3D (union tcp_ao_addr *)&newsk->sk_daddr; +#if IS_ENABLED(CONFIG_IPV6) + } else if (family =3D=3D AF_INET6) { + addr =3D (union tcp_ao_addr *)&newsk->sk_v6_daddr; +#endif + } else { + ret =3D -EAFNOSUPPORT; + goto free_ao; + } + + hlist_for_each_entry_rcu(key, &ao->head, node) { + if (tcp_ao_key_cmp(key, addr, key->prefixlen, family, -1, -1)) + continue; + + new_key =3D tcp_ao_copy_key(newsk, key); + if (!new_key) + goto free_and_exit; + + tcp_ao_cache_traffic_keys(newsk, new_ao, new_key); + tcp_ao_link_mkt(new_ao, new_key); + match =3D true; + } + + if (!match) { + /* RFC5925 (7.4.1) specifies that the TCP-AO status + * of a connection is determined on the initial SYN. + * At this point the connection was TCP-AO enabled, so + * it can't switch to being unsigned if peer's key + * disappears on the listening socket. + */ + ret =3D -EKEYREJECTED; + goto free_and_exit; + } + + key_head =3D rcu_dereference(hlist_first_rcu(&new_ao->head)); + first_key =3D hlist_entry_safe(key_head, struct tcp_ao_key, node); + + key =3D tcp_ao_established_key(new_ao, tcp_rsk(req)->ao_keyid, -1); + if (key) + new_ao->current_key =3D key; + else + new_ao->current_key =3D first_key; + + /* set rnext_key */ + key =3D tcp_ao_established_key(new_ao, -1, tcp_rsk(req)->ao_rcv_next); + if (key) + new_ao->rnext_key =3D key; + else + new_ao->rnext_key =3D first_key; + + sk_gso_disable(newsk); + rcu_assign_pointer(tcp_sk(newsk)->ao_info, new_ao); + + return 0; + +free_and_exit: + hlist_for_each_entry_safe(key, key_head, &new_ao->head, node) { + hlist_del(&key->node); + tcp_sigpool_release(key->tcp_sigpool_id); + atomic_sub(tcp_ao_sizeof_key(key), &newsk->sk_omem_alloc); + kfree(key); + } +free_ao: + kfree(new_ao); + return ret; +} + static bool tcp_ao_can_set_current_rnext(struct sock *sk) { /* There aren't current/rnext keys on TCP_LISTEN sockets */ diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index df806262a385..59154235056e 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6978,6 +6978,10 @@ int tcp_conn_request(struct request_sock_ops *rsk_op= s, struct flowi fl; u8 syncookies; =20 +#ifdef CONFIG_TCP_AO + const struct tcp_ao_hdr *aoh; +#endif + syncookies =3D READ_ONCE(net->ipv4.sysctl_tcp_syncookies); =20 /* TW buckets are converted to open requests without @@ -7063,6 +7067,17 @@ int tcp_conn_request(struct request_sock_ops *rsk_op= s, inet_rsk(req)->ecn_ok =3D 0; } =20 +#ifdef CONFIG_TCP_AO + if (tcp_parse_auth_options(tcp_hdr(skb), NULL, &aoh)) + goto drop_and_release; /* Invalid TCP options */ + if (aoh) { + tcp_rsk(req)->maclen =3D aoh->length - sizeof(struct tcp_ao_hdr); + tcp_rsk(req)->ao_rcv_next =3D aoh->keyid; + tcp_rsk(req)->ao_keyid =3D aoh->rnext_keyid; + } else { + tcp_rsk(req)->maclen =3D 0; + } +#endif tcp_rsk(req)->snt_isn =3D isn; tcp_rsk(req)->txhash =3D net_tx_rndhash(); tcp_rsk(req)->syn_tos =3D TCP_SKB_CB(skb)->ip_dsfield; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index c63f43a6e813..9a4ffcc965f3 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1065,33 +1065,76 @@ static void tcp_v4_timewait_ack(struct sock *sk, st= ruct sk_buff *skb) static void tcp_v4_reqsk_send_ack(const struct sock *sk, struct sk_buff *s= kb, struct request_sock *req) { + struct tcp_md5sig_key *md5_key =3D NULL; + struct tcp_ao_key *ao_key =3D NULL; const union tcp_md5_addr *addr; - int l3index; + u8 *traffic_key =3D NULL; + u8 keyid =3D 0; +#ifdef CONFIG_TCP_AO + const struct tcp_ao_hdr *aoh; +#endif =20 /* sk->sk_state =3D=3D TCP_LISTEN -> for regular TCP_SYN_RECV * sk->sk_state =3D=3D TCP_SYN_RECV -> for Fast Open. */ 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; =20 + if (tcp_rsk_used_ao(req)) { +#ifdef CONFIG_TCP_AO + /* Invalid TCP option size or twice included auth */ + if (tcp_parse_auth_options(tcp_hdr(skb), NULL, &aoh)) + return; + + if (!aoh) + return; + + ao_key =3D tcp_ao_do_lookup(sk, addr, AF_INET, aoh->rnext_keyid, -1); + if (unlikely(!ao_key)) { + /* Send ACK with any matching MKT for the peer */ + ao_key =3D tcp_ao_do_lookup(sk, addr, AF_INET, -1, -1); + /* Matching key disappeared (user removed the key?) + * let the handshake timeout. + */ + if (!ao_key) { + net_info_ratelimited("TCP-AO key for (%pI4, %d)->(%pI4, %d) suddenly d= isappeared, won't ACK new connection\n", + addr, + ntohs(tcp_hdr(skb)->source), + &ip_hdr(skb)->daddr, + ntohs(tcp_hdr(skb)->dest)); + return; + } + } + traffic_key =3D kmalloc(tcp_ao_digest_size(ao_key), GFP_ATOMIC); + if (!traffic_key) + return; + + keyid =3D aoh->keyid; + 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 * The window field (SEG.WND) of every outgoing segment, with the * exception of segments, MUST be right-shifted by * Rcv.Wind.Shift bits: */ - addr =3D (union tcp_md5_addr *)&ip_hdr(skb)->saddr; - l3index =3D tcp_v4_sdif(skb) ? inet_iif(skb) : 0; tcp_v4_send_ack(sk, skb, seq, tcp_rsk(req)->rcv_nxt, req->rsk_rcv_wnd >> inet_rsk(req)->rcv_wscale, tcp_time_stamp_raw() + tcp_rsk(req)->ts_off, READ_ONCE(req->ts_recent), 0, - tcp_md5_do_lookup(sk, l3index, addr, AF_INET), - NULL, NULL, 0, 0, + md5_key, ao_key, traffic_key, keyid, 0, inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0, ip_hdr(skb)->tos, READ_ONCE(tcp_rsk(req)->txhash)); + kfree(traffic_key); } =20 /* @@ -1629,6 +1672,10 @@ const struct tcp_request_sock_ops tcp_request_sock_i= pv4_ops =3D { .req_md5_lookup =3D tcp_v4_md5_lookup, .calc_md5_hash =3D tcp_v4_md5_hash_skb, #endif +#ifdef CONFIG_TCP_AO + .ao_lookup =3D tcp_v4_ao_lookup_rsk, + .ao_calc_key =3D tcp_v4_ao_calc_key_rsk, +#endif #ifdef CONFIG_SYN_COOKIES .cookie_init_seq =3D cookie_v4_init_sequence, #endif @@ -1730,12 +1777,16 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock= *sk, struct sk_buff *skb, /* Copy over the MD5 key from the original socket */ addr =3D (union tcp_md5_addr *)&newinet->inet_daddr; key =3D tcp_md5_do_lookup(sk, l3index, addr, AF_INET); - if (key) { + if (key && !tcp_rsk_used_ao(req)) { if (tcp_md5_key_copy(newsk, addr, AF_INET, 32, l3index, key)) goto put_and_exit; sk_gso_disable(newsk); } #endif +#ifdef CONFIG_TCP_AO + if (tcp_ao_copy_all_matching(sk, newsk, req, skb, AF_INET)) + goto put_and_exit; /* OOM, release back memory */ +#endif =20 if (__inet_inherit_port(sk, newsk) < 0) goto put_and_exit; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 59805fd2a9f9..e5c6fe0e2a6e 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -505,6 +505,9 @@ struct sock *tcp_create_openreq_child(const struct sock= *sk, const struct tcp_sock *oldtp; struct tcp_sock *newtp; u32 seq; +#ifdef CONFIG_TCP_AO + struct tcp_ao_key *ao_key; +#endif =20 if (!newsk) return NULL; @@ -581,6 +584,13 @@ struct sock *tcp_create_openreq_child(const struct soc= k *sk, #ifdef CONFIG_TCP_MD5SIG newtp->md5sig_info =3D NULL; /*XXX*/ #endif +#ifdef CONFIG_TCP_AO + newtp->ao_info =3D NULL; + ao_key =3D treq->af_specific->ao_lookup(sk, req, + tcp_rsk(req)->ao_keyid, -1); + if (ao_key) + newtp->tcp_header_len +=3D tcp_ao_len(ao_key); + #endif if (skb->len >=3D TCP_MSS_DEFAULT + newtp->tcp_header_len) newicsk->icsk_ack.last_seg_size =3D skb->len - newtp->tcp_header_len; newtp->rx_opt.mss_clamp =3D req->mss; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index f0760a7712f8..44c97e6ddd50 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -616,6 +616,7 @@ static void bpf_skops_write_hdr_opt(struct sock *sk, st= ruct sk_buff *skb, * (but it may well be that other scenarios fail similarly). */ static void tcp_options_write(struct tcphdr *th, struct tcp_sock *tp, + const struct tcp_request_sock *tcprsk, struct tcp_out_options *opts, struct tcp_ao_key *ao_key) { @@ -631,22 +632,34 @@ static void tcp_options_write(struct tcphdr *th, stru= ct tcp_sock *tp, } #ifdef CONFIG_TCP_AO if (unlikely(OPTION_AO & options)) { - struct tcp_ao_key *rnext_key; - struct tcp_ao_info *ao_info; u8 maclen; =20 if (WARN_ON_ONCE(!ao_key)) goto out_ao; - ao_info =3D rcu_dereference_check(tp->ao_info, - lockdep_sock_is_held(&tp->inet_conn.icsk_inet.sk)); - rnext_key =3D READ_ONCE(ao_info->rnext_key); - if (WARN_ON_ONCE(!rnext_key)) - goto out_ao; maclen =3D tcp_ao_maclen(ao_key); - *ptr++ =3D htonl((TCPOPT_AO << 24) | - (tcp_ao_len(ao_key) << 16) | - (ao_key->sndid << 8) | - (rnext_key->rcvid)); + + if (tcprsk) { + u8 aolen =3D maclen + sizeof(struct tcp_ao_hdr); + + *ptr++ =3D htonl((TCPOPT_AO << 24) | (aolen << 16) | + (tcprsk->ao_keyid << 8) | + (tcprsk->ao_rcv_next)); + } else if (WARN_ON_ONCE(!tp)) { + goto out_ao; + } else { + struct tcp_ao_key *rnext_key; + struct tcp_ao_info *ao_info; + + ao_info =3D rcu_dereference_check(tp->ao_info, + lockdep_sock_is_held(&tp->inet_conn.icsk_inet.sk)); + rnext_key =3D READ_ONCE(ao_info->rnext_key); + if (WARN_ON_ONCE(!rnext_key)) + goto out_ao; + *ptr++ =3D htonl((TCPOPT_AO << 24) | + (tcp_ao_len(ao_key) << 16) | + (ao_key->sndid << 8) | + (rnext_key->rcvid)); + } opts->hash_location =3D (__u8 *)ptr; ptr +=3D maclen / sizeof(*ptr); if (unlikely(maclen % sizeof(*ptr))) { @@ -1420,7 +1433,7 @@ static int __tcp_transmit_skb(struct sock *sk, struct= sk_buff *skb, th->window =3D htons(min(tp->rcv_wnd, 65535U)); } =20 - tcp_options_write(th, tp, &opts, ao_key); + tcp_options_write(th, tp, NULL, &opts, ao_key); =20 #ifdef CONFIG_TCP_MD5SIG /* Calculate the MD5 hash, as we have all we need now */ @@ -3793,7 +3806,7 @@ struct sk_buff *tcp_make_synack(const struct sock *sk= , struct dst_entry *dst, =20 /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. */ th->window =3D htons(min(req->rsk_rcv_wnd, 65535U)); - tcp_options_write(th, NULL, &opts, NULL); + tcp_options_write(th, NULL, NULL, &opts, NULL); th->doff =3D (tcp_header_size >> 2); TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTSEGS); =20 diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 5014aa663452..ad7a8caa7b2a 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -214,6 +214,8 @@ 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); + if (IS_ENABLED(CONFIG_SMC)) ireq->smc_ok =3D 0; =20 diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c index d08735b6f3c5..c9a6fa84f6ce 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -49,6 +49,17 @@ static int tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u8= *key, return err; } =20 +int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, + const struct sk_buff *skb, + __be32 sisn, __be32 disn) +{ + const struct ipv6hdr *iph =3D ipv6_hdr(skb); + const struct tcphdr *th =3D tcp_hdr(skb); + + return tcp_v6_ao_calc_key(mkt, key, &iph->saddr, &iph->daddr, + th->source, th->dest, sisn, disn); +} + int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, const struct sock *sk, __be32 sisn, __be32 disn, bool send) @@ -63,9 +74,21 @@ int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *ke= y, htons(sk->sk_num), disn, sisn); } =20 -static struct tcp_ao_key *tcp_v6_ao_do_lookup(const struct sock *sk, - const struct in6_addr *addr, - int sndid, int rcvid) +int tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key, + struct request_sock *req) +{ + struct inet_request_sock *ireq =3D inet_rsk(req); + + return tcp_v6_ao_calc_key(mkt, key, + &ireq->ir_v6_loc_addr, &ireq->ir_v6_rmt_addr, + htons(ireq->ir_num), ireq->ir_rmt_port, + htonl(tcp_rsk(req)->snt_isn), + htonl(tcp_rsk(req)->rcv_isn)); +} + +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); @@ -80,6 +103,15 @@ struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *= sk, return tcp_v6_ao_do_lookup(sk, addr, sndid, rcvid); } =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; + + return tcp_v6_ao_do_lookup(sk, addr, sndid, rcvid); +} + int tcp_v6_ao_hash_pseudoheader(struct tcp_sigpool *hp, const struct in6_addr *daddr, const struct in6_addr *saddr, int nbytes) diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 947fef0f52fe..c060cd964f91 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -842,6 +842,10 @@ const struct tcp_request_sock_ops tcp_request_sock_ipv= 6_ops =3D { .req_md5_lookup =3D tcp_v6_md5_lookup, .calc_md5_hash =3D tcp_v6_md5_hash_skb, #endif +#ifdef CONFIG_TCP_AO + .ao_lookup =3D tcp_v6_ao_lookup_rsk, + .ao_calc_key =3D tcp_v6_ao_calc_key_rsk, +#endif #ifdef CONFIG_SYN_COOKIES .cookie_init_seq =3D cookie_v6_init_sequence, #endif @@ -1194,9 +1198,51 @@ static void tcp_v6_timewait_ack(struct sock *sk, str= uct sk_buff *skb) static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *s= kb, struct request_sock *req) { + struct tcp_md5sig_key *md5_key =3D NULL; + struct tcp_ao_key *ao_key =3D NULL; + const struct in6_addr *addr; + u8 *traffic_key =3D NULL; + u8 keyid =3D 0; int l3index; =20 l3index =3D tcp_v6_sdif(skb) ? tcp_v6_iif_l3_slave(skb) : 0; + addr =3D &ipv6_hdr(skb)->saddr; + + if (tcp_rsk_used_ao(req)) { +#ifdef CONFIG_TCP_AO + const struct tcp_ao_hdr *aoh; + + /* Invalid TCP option size or twice included auth */ + if (tcp_parse_auth_options(tcp_hdr(skb), NULL, &aoh)) + return; + if (!aoh) + return; + ao_key =3D tcp_v6_ao_do_lookup(sk, addr, aoh->rnext_keyid, -1); + 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); + /* Matching key disappeared (user removed the key?) + * let the handshake timeout. + */ + if (!ao_key) { + net_info_ratelimited("TCP-AO key for (%pI6, %d)->(%pI6, %d) suddenly d= isappeared, won't ACK new connection\n", + addr, + ntohs(tcp_hdr(skb)->source), + &ipv6_hdr(skb)->daddr, + ntohs(tcp_hdr(skb)->dest)); + return; + } + } + traffic_key =3D kmalloc(tcp_ao_digest_size(ao_key), GFP_ATOMIC); + if (!traffic_key) + return; + + keyid =3D aoh->keyid; + tcp_v6_ao_calc_key_rsk(ao_key, traffic_key, req); +#endif + } else { + md5_key =3D tcp_v6_md5_do_lookup(sk, addr, l3index); + } =20 /* sk->sk_state =3D=3D TCP_LISTEN -> for regular TCP_SYN_RECV * sk->sk_state =3D=3D TCP_SYN_RECV -> for Fast Open. @@ -1212,11 +1258,12 @@ static void tcp_v6_reqsk_send_ack(const struct sock= *sk, struct sk_buff *skb, req->rsk_rcv_wnd >> inet_rsk(req)->rcv_wscale, tcp_time_stamp_raw() + tcp_rsk(req)->ts_off, READ_ONCE(req->ts_recent), sk->sk_bound_dev_if, - tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->saddr, l3index), + md5_key, ipv6_get_dsfield(ipv6_hdr(skb)), 0, READ_ONCE(sk->sk_priority), READ_ONCE(tcp_rsk(req)->txhash), - NULL, NULL, 0, 0); + ao_key, traffic_key, keyid, 0); + kfree(traffic_key); } =20 =20 @@ -1446,19 +1493,26 @@ static struct sock *tcp_v6_syn_recv_sock(const stru= ct sock *sk, struct sk_buff * #ifdef CONFIG_TCP_MD5SIG l3index =3D l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif); =20 - /* Copy over the MD5 key from the original socket */ - key =3D tcp_v6_md5_do_lookup(sk, &newsk->sk_v6_daddr, l3index); - if (key) { - const union tcp_md5_addr *addr; + if (!tcp_rsk_used_ao(req)) { + /* Copy over the MD5 key from the original socket */ + key =3D tcp_v6_md5_do_lookup(sk, &newsk->sk_v6_daddr, l3index); + if (key) { + const union tcp_md5_addr *addr; =20 - addr =3D (union tcp_md5_addr *)&newsk->sk_v6_daddr; - if (tcp_md5_key_copy(newsk, addr, AF_INET6, 128, l3index, key)) { - inet_csk_prepare_forced_close(newsk); - tcp_done(newsk); - goto out; + addr =3D (union tcp_md5_addr *)&newsk->sk_v6_daddr; + if (tcp_md5_key_copy(newsk, addr, AF_INET6, 128, l3index, key)) { + inet_csk_prepare_forced_close(newsk); + tcp_done(newsk); + goto out; + } } } #endif +#ifdef CONFIG_TCP_AO + /* Copy over tcp_ao_info if any */ + if (tcp_ao_copy_all_matching(sk, newsk, req, skb, AF_INET6)) + goto out; /* OOM */ +#endif =20 if (__inet_inherit_port(sk, newsk) < 0) { inet_csk_prepare_forced_close(newsk); --=20 2.41.0