Skip to content

Commit

Permalink
ipv6: lockless IPV6_MULTICAST_HOPS implementation
Browse files Browse the repository at this point in the history
This fixes data-races around np->mcast_hops,
and make IPV6_MULTICAST_HOPS lockless.

Note that np->mcast_hops is never negative,
thus can fit an u8 field instead of s16.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Eric Dumazet authored and davem330 committed Sep 15, 2023
1 parent d986f52 commit 2da23eb
Show file tree
Hide file tree
Showing 6 changed files with 21 additions and 25 deletions.
9 changes: 1 addition & 8 deletions include/linux/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,15 +214,8 @@ struct ipv6_pinfo {
__u32 frag_size;

s16 hop_limit;
u8 mcast_hops;

#if defined(__BIG_ENDIAN_BITFIELD)
/* Packed in 16bits. */
__s16 mcast_hops:9;
__u16 __unused_2:7,
#else
__u16 __unused_2:7;
__s16 mcast_hops:9;
#endif
int ucast_oif;
int mcast_oif;

Expand Down
2 changes: 1 addition & 1 deletion include/net/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -914,7 +914,7 @@ static inline int ip6_sk_dst_hoplimit(struct ipv6_pinfo *np, struct flowi6 *fl6,
int hlimit;

if (ipv6_addr_is_multicast(&fl6->daddr))
hlimit = np->mcast_hops;
hlimit = READ_ONCE(np->mcast_hops);
else
hlimit = READ_ONCE(np->hop_limit);
if (hlimit < 0)
Expand Down
2 changes: 1 addition & 1 deletion net/dccp/ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
if (np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo)
np->mcast_oif = inet6_iif(opt_skb);
if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim)
np->mcast_hops = ipv6_hdr(opt_skb)->hop_limit;
WRITE_ONCE(np->mcast_hops, ipv6_hdr(opt_skb)->hop_limit);
if (np->rxopt.bits.rxflow || np->rxopt.bits.rxtclass)
np->rcv_flowinfo = ip6_flowinfo(ipv6_hdr(opt_skb));
if (np->repflow)
Expand Down
28 changes: 15 additions & 13 deletions net/ipv6/ipv6_sockglue.c
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,16 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
return -EINVAL;
inet6_assign_bit(MC6_LOOP, sk, valbool);
return 0;
case IPV6_MULTICAST_HOPS:
if (sk->sk_type == SOCK_STREAM)
return retv;
if (optlen < sizeof(int))
return -EINVAL;
if (val > 255 || val < -1)
return -EINVAL;
WRITE_ONCE(np->mcast_hops,
val == -1 ? IPV6_DEFAULT_MCASTHOPS : val);
return 0;
}
if (needs_rtnl)
rtnl_lock();
Expand Down Expand Up @@ -751,16 +761,6 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
break;
}

case IPV6_MULTICAST_HOPS:
if (sk->sk_type == SOCK_STREAM)
break;
if (optlen < sizeof(int))
goto e_inval;
if (val > 255 || val < -1)
goto e_inval;
np->mcast_hops = (val == -1 ? IPV6_DEFAULT_MCASTHOPS : val);
retv = 0;
break;

case IPV6_UNICAST_IF:
{
Expand Down Expand Up @@ -1180,7 +1180,8 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
put_cmsg(&msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info);
}
if (np->rxopt.bits.rxhlim) {
int hlim = np->mcast_hops;
int hlim = READ_ONCE(np->mcast_hops);

put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
}
if (np->rxopt.bits.rxtclass) {
Expand All @@ -1197,7 +1198,8 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
put_cmsg(&msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info);
}
if (np->rxopt.bits.rxohlim) {
int hlim = np->mcast_hops;
int hlim = READ_ONCE(np->mcast_hops);

put_cmsg(&msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim);
}
if (np->rxopt.bits.rxflow) {
Expand Down Expand Up @@ -1349,7 +1351,7 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
if (optname == IPV6_UNICAST_HOPS)
val = READ_ONCE(np->hop_limit);
else
val = np->mcast_hops;
val = READ_ONCE(np->mcast_hops);

if (val < 0) {
rcu_read_lock();
Expand Down
3 changes: 2 additions & 1 deletion net/ipv6/tcp_ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -1542,7 +1542,8 @@ int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
if (np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo)
np->mcast_oif = tcp_v6_iif(opt_skb);
if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim)
np->mcast_hops = ipv6_hdr(opt_skb)->hop_limit;
WRITE_ONCE(np->mcast_hops,
ipv6_hdr(opt_skb)->hop_limit);
if (np->rxopt.bits.rxflow || np->rxopt.bits.rxtclass)
np->rcv_flowinfo = ip6_flowinfo(ipv6_hdr(opt_skb));
if (np->repflow)
Expand Down
2 changes: 1 addition & 1 deletion net/netfilter/ipvs/ip_vs_sync.c
Original file line number Diff line number Diff line change
Expand Up @@ -1322,7 +1322,7 @@ static void set_mcast_ttl(struct sock *sk, u_char ttl)
struct ipv6_pinfo *np = inet6_sk(sk);

/* IPV6_MULTICAST_HOPS */
np->mcast_hops = ttl;
WRITE_ONCE(np->mcast_hops, ttl);
}
#endif
release_sock(sk);
Expand Down

0 comments on commit 2da23eb

Please sign in to comment.