Commit 790eb673 authored by Eric Dumazet's avatar Eric Dumazet Committed by Jakub Kicinski

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: default avatarEric Dumazet <edumazet@google.com>
Acked-by: default avatarSoheil Hassas Yeganeh <soheil@google.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent cc17c3c8
...@@ -1092,6 +1092,7 @@ struct in6_addr *fl6_update_dst(struct flowi6 *fl6, ...@@ -1092,6 +1092,7 @@ struct in6_addr *fl6_update_dst(struct flowi6 *fl6,
/* /*
* socket options (ipv6_sockglue.c) * socket options (ipv6_sockglue.c)
*/ */
DECLARE_STATIC_KEY_FALSE(ip6_min_hopcount);
int ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, int ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
unsigned int optlen); unsigned int optlen);
......
...@@ -55,6 +55,8 @@ ...@@ -55,6 +55,8 @@
struct ip6_ra_chain *ip6_ra_chain; struct ip6_ra_chain *ip6_ra_chain;
DEFINE_RWLOCK(ip6_ra_lock); DEFINE_RWLOCK(ip6_ra_lock);
DEFINE_STATIC_KEY_FALSE(ip6_min_hopcount);
int ip6_ra_control(struct sock *sk, int sel) int ip6_ra_control(struct sock *sk, int sel)
{ {
struct ip6_ra_chain *ra, *new_ra, **rap; struct ip6_ra_chain *ra, *new_ra, **rap;
...@@ -950,6 +952,10 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, ...@@ -950,6 +952,10 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
goto e_inval; goto e_inval;
if (val < 0 || val > 255) if (val < 0 || val > 255)
goto e_inval; goto e_inval;
if (val)
static_branch_enable(&ip6_min_hopcount);
/* tcp_v6_err() and tcp_v6_rcv() might read min_hopcount /* tcp_v6_err() and tcp_v6_rcv() might read min_hopcount
* while we are changing it. * while we are changing it.
*/ */
......
...@@ -414,10 +414,12 @@ static int tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, ...@@ -414,10 +414,12 @@ static int tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
if (sk->sk_state == TCP_CLOSE) if (sk->sk_state == TCP_CLOSE)
goto out; goto out;
/* min_hopcount can be changed concurrently from do_ipv6_setsockopt() */ if (static_branch_unlikely(&ip6_min_hopcount)) {
if (ipv6_hdr(skb)->hop_limit < READ_ONCE(tcp_inet6_sk(sk)->min_hopcount)) { /* min_hopcount can be changed concurrently from do_ipv6_setsockopt() */
__NET_INC_STATS(net, LINUX_MIB_TCPMINTTLDROP); if (ipv6_hdr(skb)->hop_limit < READ_ONCE(tcp_inet6_sk(sk)->min_hopcount)) {
goto out; __NET_INC_STATS(net, LINUX_MIB_TCPMINTTLDROP);
goto out;
}
} }
tp = tcp_sk(sk); tp = tcp_sk(sk);
...@@ -1727,10 +1729,13 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) ...@@ -1727,10 +1729,13 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb)
return 0; return 0;
} }
} }
/* min_hopcount can be changed concurrently from do_ipv6_setsockopt() */
if (hdr->hop_limit < READ_ONCE(tcp_inet6_sk(sk)->min_hopcount)) { if (static_branch_unlikely(&ip6_min_hopcount)) {
__NET_INC_STATS(net, LINUX_MIB_TCPMINTTLDROP); /* min_hopcount can be changed concurrently from do_ipv6_setsockopt() */
goto discard_and_relse; if (hdr->hop_limit < READ_ONCE(tcp_inet6_sk(sk)->min_hopcount)) {
__NET_INC_STATS(net, LINUX_MIB_TCPMINTTLDROP);
goto discard_and_relse;
}
} }
if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment