Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipv6: guard IPV6_MINHOPCOUNT with a static key

RFC 5082 IPV6_MINHOPCOUNT is rarely used on hosts.

Add a static key to remove from TCP fast path useless code,
and potential cache line miss to fetch tcp_inet6_sk(sk)->min_hopcount

Note that once ip6_min_hopcount static key has been enabled,
it stays enabled until next boot.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Acked-by: Soheil Hassas Yeganeh <soheil@google.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Eric Dumazet and committed by
Jakub Kicinski
790eb673 cc17c3c8

+20 -8
+1
include/net/ipv6.h
··· 1092 1092 /* 1093 1093 * socket options (ipv6_sockglue.c) 1094 1094 */ 1095 + DECLARE_STATIC_KEY_FALSE(ip6_min_hopcount); 1095 1096 1096 1097 int ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, 1097 1098 unsigned int optlen);
+6
net/ipv6/ipv6_sockglue.c
··· 55 55 struct ip6_ra_chain *ip6_ra_chain; 56 56 DEFINE_RWLOCK(ip6_ra_lock); 57 57 58 + DEFINE_STATIC_KEY_FALSE(ip6_min_hopcount); 59 + 58 60 int ip6_ra_control(struct sock *sk, int sel) 59 61 { 60 62 struct ip6_ra_chain *ra, *new_ra, **rap; ··· 952 950 goto e_inval; 953 951 if (val < 0 || val > 255) 954 952 goto e_inval; 953 + 954 + if (val) 955 + static_branch_enable(&ip6_min_hopcount); 956 + 955 957 /* tcp_v6_err() and tcp_v6_rcv() might read min_hopcount 956 958 * while we are changing it. 957 959 */
+13 -8
net/ipv6/tcp_ipv6.c
··· 414 414 if (sk->sk_state == TCP_CLOSE) 415 415 goto out; 416 416 417 - /* min_hopcount can be changed concurrently from do_ipv6_setsockopt() */ 418 - if (ipv6_hdr(skb)->hop_limit < READ_ONCE(tcp_inet6_sk(sk)->min_hopcount)) { 419 - __NET_INC_STATS(net, LINUX_MIB_TCPMINTTLDROP); 420 - goto out; 417 + if (static_branch_unlikely(&ip6_min_hopcount)) { 418 + /* min_hopcount can be changed concurrently from do_ipv6_setsockopt() */ 419 + if (ipv6_hdr(skb)->hop_limit < READ_ONCE(tcp_inet6_sk(sk)->min_hopcount)) { 420 + __NET_INC_STATS(net, LINUX_MIB_TCPMINTTLDROP); 421 + goto out; 422 + } 421 423 } 422 424 423 425 tp = tcp_sk(sk); ··· 1729 1727 return 0; 1730 1728 } 1731 1729 } 1732 - /* min_hopcount can be changed concurrently from do_ipv6_setsockopt() */ 1733 - if (hdr->hop_limit < READ_ONCE(tcp_inet6_sk(sk)->min_hopcount)) { 1734 - __NET_INC_STATS(net, LINUX_MIB_TCPMINTTLDROP); 1735 - goto discard_and_relse; 1730 + 1731 + if (static_branch_unlikely(&ip6_min_hopcount)) { 1732 + /* min_hopcount can be changed concurrently from do_ipv6_setsockopt() */ 1733 + if (hdr->hop_limit < READ_ONCE(tcp_inet6_sk(sk)->min_hopcount)) { 1734 + __NET_INC_STATS(net, LINUX_MIB_TCPMINTTLDROP); 1735 + goto discard_and_relse; 1736 + } 1736 1737 } 1737 1738 1738 1739 if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))