From nobody Wed Feb 11 12:07:32 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 09632C7EE22 for ; Tue, 9 May 2023 22:16:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235274AbjEIWQX (ORCPT ); Tue, 9 May 2023 18:16:23 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53600 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235018AbjEIWQU (ORCPT ); Tue, 9 May 2023 18:16:20 -0400 Received: from mail-wm1-x32b.google.com (mail-wm1-x32b.google.com [IPv6:2a00:1450:4864:20::32b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7135D3A8D for ; Tue, 9 May 2023 15:16:18 -0700 (PDT) Received: by mail-wm1-x32b.google.com with SMTP id 5b1f17b1804b1-3f417ea5252so27833245e9.0 for ; Tue, 09 May 2023 15:16:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=arista.com; s=google; t=1683670577; x=1686262577; 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=Y6EGYjnQ/WyZ5lGG/G0UzhGu7aE+rdmJ2jNh/CB1dks=; b=RLcV9kBTmM0ms/dV9Q+Kz1njzs2zTjAcvgDTRRAJoTYQaD+6u1/81mYky4/NWOlPVl U+PFv6QhGWcat+FRsZhjBlRjc1A4s7Bpn2o2ux6lyD1J/tVjWChZgjK/pM0q/LWVzwgq RorYb1Pxwx+i4qMJMmUddHcjbS4jBpag7BdGBLR7gJT5T2HejFSdYSLG34MgqVcTvH9U D6vdYvVtditjO+R4guAOf8WavmkHJhMKASPP3RDVSi4cCzlJky2THm+GDZSQQQd6jCXH +AdKi5oO3+EfDy4Ms7a6Hy2krjb1HGJRwBSMGOPQpF3pw02Er48skcq4HllK4iqfwRK4 zqDQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1683670577; x=1686262577; 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=Y6EGYjnQ/WyZ5lGG/G0UzhGu7aE+rdmJ2jNh/CB1dks=; b=AYXI8MpM7xEefns5+Vd5fxn+e7UMlkAyvPhO4y7wZuqFVgQXJWu3h6zVidcGCg89QG RlAbPBAYUPRw3WN+7j1aYRW49Mh85HU3ZznADf9zUAs8OwXZQmsbf2QSslOuKCHnebO5 x/HHaZUoCKSkDvON2KBpW0DtGct8ELfUwK1cuV4DNXLY9tgwnbBFzsLBzk9ZJC9qYmMS JrHmH3JRyzIg8bgGkah7ZIZBOt1QUmriqBQeIC86eVIlQB1QRsEhbEBttJ8UuqUZeSpX FHU/WfMzmRLdlg2t9eMRPplhb6HTBtmXSbxJyL+ZGIoYurviwsXqT2Ugdz6rBqnHUu6q HtgQ== X-Gm-Message-State: AC+VfDwuJbtvsRLSPeUCLRlDH4SvSb/f+ZdA1JP324LshMox3o9xuyzD zGGJLjyeVEVhRU2yrmgcix2wcZWH6JA1k/71TZY= X-Google-Smtp-Source: ACHHUZ6wgvotrUb/tm0TOg0te2P4XxsvMPXWrxHm+qcAywC9h/wd9u01Ef+pBiYFL4pQQDhwCkmTKQ== X-Received: by 2002:a1c:7c10:0:b0:3f4:253b:92b3 with SMTP id x16-20020a1c7c10000000b003f4253b92b3mr5795825wmc.18.1683670576558; Tue, 09 May 2023 15:16:16 -0700 (PDT) Received: from Mindolluin.ire.aristanetworks.com ([217.173.96.166]) by smtp.gmail.com with ESMTPSA id t25-20020a7bc3d9000000b003f42d3111b8sm2052888wmj.30.2023.05.09.15.16.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 09 May 2023 15:16:16 -0700 (PDT) From: Dmitry Safonov To: linux-kernel@vger.kernel.org, David Ahern , Eric Dumazet , Paolo Abeni , Jakub Kicinski , "David S. Miller" Cc: Dmitry Safonov , Dmitry Safonov <0x7f454c46@gmail.com>, Hideaki YOSHIFUJI , Leonard Crestez , Salam Noureddine , netdev@vger.kernel.org Subject: [PATCH 1/5] net/tcp: Separate TCP-MD5 signing from tcp_v{4,6}_send_reset() Date: Tue, 9 May 2023 23:16:04 +0100 Message-Id: <20230509221608.2569333-2-dima@arista.com> X-Mailer: git-send-email 2.40.0 In-Reply-To: <20230509221608.2569333-1-dima@arista.com> References: <20230509221608.2569333-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" Separate TCP-MD5 part from the generic TCP code, cleaning it up from MD5-related ifdeffery (this is most noticeable on ipv4 part). Mostly, it is refactoring, but with a small bonus: now RST sending functions can nicely get tcp_md5_needed static key check, making them faster on systems without TCP-MD5 keys. Signed-off-by: Dmitry Safonov --- net/ipv4/tcp_ipv4.c | 177 +++++++++++++++++++++++--------------------- net/ipv6/tcp_ipv6.c | 106 ++++++++++++++------------ 2 files changed, 152 insertions(+), 131 deletions(-) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 39bda2b1066e..b1056a4af60f 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -655,6 +655,97 @@ void tcp_v4_send_check(struct sock *sk, struct sk_buff= *skb) } EXPORT_SYMBOL(tcp_v4_send_check); =20 +#define REPLY_OPTIONS_LEN (MAX_TCP_OPTION_SPACE / sizeof(__be32)) + +static bool tcp_v4_md5_sign_reset(struct net *net, const struct sock *sk, + struct sk_buff *skb, struct ip_reply_arg *arg, + struct tcphdr *reply, + __be32 reply_options[REPLY_OPTIONS_LEN]) +{ +#ifdef CONFIG_TCP_MD5SIG + const struct tcphdr *th =3D tcp_hdr(skb); + struct tcp_md5sig_key *key =3D NULL; + const __u8 *hash_location =3D NULL; + unsigned char newhash[16]; + struct sock *sk1 =3D NULL; + int genhash; + + hash_location =3D tcp_parse_md5sig_option(th); + /* Fastpath: no keys in system, don't send RST iff segment is signed */ + if (!static_branch_unlikely(&tcp_md5_needed.key)) + return !!hash_location; + + rcu_read_lock(); + if (sk && sk_fullsock(sk)) { + const union tcp_md5_addr *addr; + int l3index; + + /* sdif set, means packet ingressed via a device + * in an L3 domain and inet_iif is set to it. + */ + l3index =3D tcp_v4_sdif(skb) ? inet_iif(skb) : 0; + addr =3D (union tcp_md5_addr *)&ip_hdr(skb)->saddr; + key =3D tcp_md5_do_lookup(sk, l3index, addr, AF_INET); + } else if (hash_location) { + const union tcp_md5_addr *addr; + int sdif =3D tcp_v4_sdif(skb); + int dif =3D inet_iif(skb); + int l3index; + + /* + * active side is lost. Try to find listening socket through + * source port, and then find md5 key through listening socket. + * we are not loose security here: + * Incoming packet is checked with md5 hash with finding key, + * no RST generated if md5 hash doesn't match. + */ + sk1 =3D __inet_lookup_listener(net, net->ipv4.tcp_death_row.hashinfo, + NULL, 0, ip_hdr(skb)->saddr, + th->source, ip_hdr(skb)->daddr, + ntohs(th->source), dif, sdif); + /* don't send rst if it can't find key */ + if (!sk1) { + rcu_read_unlock(); + return true; + } + + /* sdif set, means packet ingressed via a device + * in an L3 domain and dif is set to it. + */ + l3index =3D sdif ? dif : 0; + addr =3D (union tcp_md5_addr *)&ip_hdr(skb)->saddr; + key =3D tcp_md5_do_lookup(sk1, l3index, addr, AF_INET); + if (!key) { + rcu_read_unlock(); + return true; + } + + genhash =3D tcp_v4_md5_hash_skb(newhash, key, NULL, skb); + if (genhash || memcmp(hash_location, newhash, 16) !=3D 0) { + rcu_read_unlock(); + return true; + } + } + + if (key) { + reply_options[0] =3D htonl((TCPOPT_NOP << 24) | + (TCPOPT_NOP << 16) | + (TCPOPT_MD5SIG << 8) | + TCPOLEN_MD5SIG); + /* Update length and the length the header thinks exists */ + arg->iov[0].iov_len +=3D TCPOLEN_MD5SIG_ALIGNED; + reply->doff =3D arg->iov[0].iov_len / 4; + + tcp_v4_md5_hash_hdr((__u8 *)&reply_options[1], + key, ip_hdr(skb)->saddr, + ip_hdr(skb)->daddr, reply); + } + rcu_read_unlock(); +#endif + + return false; +} + /* * This routine will send an RST to the other tcp. * @@ -668,27 +759,14 @@ EXPORT_SYMBOL(tcp_v4_send_check); * Exception: precedence violation. We do not implement it in any case. */ =20 -#ifdef CONFIG_TCP_MD5SIG -#define OPTION_BYTES TCPOLEN_MD5SIG_ALIGNED -#else -#define OPTION_BYTES sizeof(__be32) -#endif - static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) { const struct tcphdr *th =3D tcp_hdr(skb); struct { struct tcphdr th; - __be32 opt[OPTION_BYTES / sizeof(__be32)]; + __be32 opt[REPLY_OPTIONS_LEN]; } rep; struct ip_reply_arg arg; -#ifdef CONFIG_TCP_MD5SIG - struct tcp_md5sig_key *key =3D NULL; - const __u8 *hash_location =3D NULL; - unsigned char newhash[16]; - int genhash; - struct sock *sk1 =3D NULL; -#endif u64 transmit_time =3D 0; struct sock *ctl_sk; struct net *net; @@ -723,70 +801,8 @@ static void tcp_v4_send_reset(const struct sock *sk, s= truct sk_buff *skb) arg.iov[0].iov_len =3D sizeof(rep.th); =20 net =3D sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev); -#ifdef CONFIG_TCP_MD5SIG - rcu_read_lock(); - hash_location =3D tcp_parse_md5sig_option(th); - if (sk && sk_fullsock(sk)) { - const union tcp_md5_addr *addr; - int l3index; - - /* sdif set, means packet ingressed via a device - * in an L3 domain and inet_iif is set to it. - */ - l3index =3D tcp_v4_sdif(skb) ? inet_iif(skb) : 0; - addr =3D (union tcp_md5_addr *)&ip_hdr(skb)->saddr; - key =3D tcp_md5_do_lookup(sk, l3index, addr, AF_INET); - } else if (hash_location) { - const union tcp_md5_addr *addr; - int sdif =3D tcp_v4_sdif(skb); - int dif =3D inet_iif(skb); - int l3index; - - /* - * active side is lost. Try to find listening socket through - * source port, and then find md5 key through listening socket. - * we are not loose security here: - * Incoming packet is checked with md5 hash with finding key, - * no RST generated if md5 hash doesn't match. - */ - sk1 =3D __inet_lookup_listener(net, net->ipv4.tcp_death_row.hashinfo, - NULL, 0, ip_hdr(skb)->saddr, - th->source, ip_hdr(skb)->daddr, - ntohs(th->source), dif, sdif); - /* don't send rst if it can't find key */ - if (!sk1) - goto out; - - /* sdif set, means packet ingressed via a device - * in an L3 domain and dif is set to it. - */ - l3index =3D sdif ? dif : 0; - addr =3D (union tcp_md5_addr *)&ip_hdr(skb)->saddr; - key =3D tcp_md5_do_lookup(sk1, l3index, addr, AF_INET); - if (!key) - goto out; - - - genhash =3D tcp_v4_md5_hash_skb(newhash, key, NULL, skb); - if (genhash || memcmp(hash_location, newhash, 16) !=3D 0) - goto out; - - } - - if (key) { - rep.opt[0] =3D htonl((TCPOPT_NOP << 24) | - (TCPOPT_NOP << 16) | - (TCPOPT_MD5SIG << 8) | - TCPOLEN_MD5SIG); - /* Update length and the length the header thinks exists */ - arg.iov[0].iov_len +=3D TCPOLEN_MD5SIG_ALIGNED; - rep.th.doff =3D arg.iov[0].iov_len / 4; - - tcp_v4_md5_hash_hdr((__u8 *) &rep.opt[1], - key, ip_hdr(skb)->saddr, - ip_hdr(skb)->daddr, &rep.th); - } -#endif + if (tcp_v4_md5_sign_reset(net, sk, skb, &arg, &rep.th, rep.opt)) + return; /* Can't co-exist with TCPMD5, hence check rep.opt[0] */ if (rep.opt[0] =3D=3D 0) { __be32 mrst =3D mptcp_reset_option(skb); @@ -842,11 +858,6 @@ static void tcp_v4_send_reset(const struct sock *sk, s= truct sk_buff *skb) __TCP_INC_STATS(net, TCP_MIB_OUTSEGS); __TCP_INC_STATS(net, TCP_MIB_OUTRSTS); local_bh_enable(); - -#ifdef CONFIG_TCP_MD5SIG -out: - rcu_read_unlock(); -#endif } =20 /* The code following below sending ACKs in SYN-RECV and TIME-WAIT states diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 7132eb213a7a..42792bc5b9bf 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -977,18 +977,67 @@ static void tcp_v6_send_response(const struct sock *s= k, struct sk_buff *skb, u32 kfree_skb(buff); } =20 +#ifdef CONFIG_TCP_MD5SIG +static int tcp_v6_md5_lookup_reset_key(struct net *net, const struct sock = *sk, + struct sk_buff *skb, struct tcp_md5sig_key **key, + const struct tcphdr *th, struct ipv6hdr *ipv6h) +{ + const __u8 *hash_location =3D NULL; + int genhash, l3index; + + hash_location =3D tcp_parse_md5sig_option(th); + if (!static_branch_unlikely(&tcp_md5_needed.key)) + return !!hash_location; + + if (sk && sk_fullsock(sk)) { + /* sdif set, means packet ingressed via a device + * in an L3 domain and inet_iif is set to it. + */ + l3index =3D tcp_v6_sdif(skb) ? tcp_v6_iif_l3_slave(skb) : 0; + *key =3D tcp_v6_md5_do_lookup(sk, &ipv6h->saddr, l3index); + } else if (hash_location) { + int dif =3D tcp_v6_iif_l3_slave(skb); + int sdif =3D tcp_v6_sdif(skb); + unsigned char newhash[16]; + struct sock *sk1; + + /* + * active side is lost. Try to find listening socket through + * source port, and then find md5 key through listening socket. + * we are not loose security here: + * Incoming packet is checked with md5 hash with finding key, + * no RST generated if md5 hash doesn't match. + */ + sk1 =3D inet6_lookup_listener(net, net->ipv4.tcp_death_row.hashinfo, + NULL, 0, &ipv6h->saddr, th->source, + &ipv6h->daddr, ntohs(th->source), + dif, sdif); + if (!sk1) + return -ENOKEY; + + /* sdif set, means packet ingressed via a device + * in an L3 domain and dif is set to it. + */ + l3index =3D tcp_v6_sdif(skb) ? dif : 0; + + *key =3D tcp_v6_md5_do_lookup(sk1, &ipv6h->saddr, l3index); + if (!*key) + return -ENOKEY; + + genhash =3D tcp_v6_md5_hash_skb(newhash, *key, NULL, skb); + if (genhash || memcmp(hash_location, newhash, 16) !=3D 0) + return -EKEYREJECTED; + } + return 0; +} +#endif + static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb) { const struct tcphdr *th =3D tcp_hdr(skb); struct ipv6hdr *ipv6h =3D ipv6_hdr(skb); - u32 seq =3D 0, ack_seq =3D 0; struct tcp_md5sig_key *key =3D NULL; -#ifdef CONFIG_TCP_MD5SIG - const __u8 *hash_location =3D NULL; - unsigned char newhash[16]; - int genhash; - struct sock *sk1 =3D NULL; -#endif + u32 seq =3D 0, ack_seq =3D 0; __be32 label =3D 0; u32 priority =3D 0; struct net *net; @@ -1007,47 +1056,8 @@ static void tcp_v6_send_reset(const struct sock *sk,= struct sk_buff *skb) net =3D sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev); #ifdef CONFIG_TCP_MD5SIG rcu_read_lock(); - hash_location =3D tcp_parse_md5sig_option(th); - if (sk && sk_fullsock(sk)) { - int l3index; - - /* sdif set, means packet ingressed via a device - * in an L3 domain and inet_iif is set to it. - */ - l3index =3D tcp_v6_sdif(skb) ? tcp_v6_iif_l3_slave(skb) : 0; - key =3D tcp_v6_md5_do_lookup(sk, &ipv6h->saddr, l3index); - } else if (hash_location) { - int dif =3D tcp_v6_iif_l3_slave(skb); - int sdif =3D tcp_v6_sdif(skb); - int l3index; - - /* - * active side is lost. Try to find listening socket through - * source port, and then find md5 key through listening socket. - * we are not loose security here: - * Incoming packet is checked with md5 hash with finding key, - * no RST generated if md5 hash doesn't match. - */ - sk1 =3D inet6_lookup_listener(net, net->ipv4.tcp_death_row.hashinfo, - NULL, 0, &ipv6h->saddr, th->source, - &ipv6h->daddr, ntohs(th->source), - dif, sdif); - if (!sk1) - goto out; - - /* sdif set, means packet ingressed via a device - * in an L3 domain and dif is set to it. - */ - l3index =3D tcp_v6_sdif(skb) ? dif : 0; - - key =3D tcp_v6_md5_do_lookup(sk1, &ipv6h->saddr, l3index); - if (!key) - goto out; - - genhash =3D tcp_v6_md5_hash_skb(newhash, key, NULL, skb); - if (genhash || memcmp(hash_location, newhash, 16) !=3D 0) - goto out; - } + if (tcp_v6_md5_lookup_reset_key(net, sk, skb, &key, th, ipv6h)) + goto out; #endif =20 if (th->ack) --=20 2.40.0