Commit b0adfba7 authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

ipv6: lockless IPV6_UNICAST_HOPS implementation

Some np->hop_limit accesses are racy, when socket lock is not held.

Add missing annotations and switch to full lockless implementation.
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Reviewed-by: default avatarDavid Ahern <dsahern@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f2fa1c81
...@@ -213,17 +213,7 @@ struct ipv6_pinfo { ...@@ -213,17 +213,7 @@ struct ipv6_pinfo {
__be32 flow_label; __be32 flow_label;
__u32 frag_size; __u32 frag_size;
/* s16 hop_limit;
* Packed in 16bits.
* Omit one shift by putting the signed field at MSB.
*/
#if defined(__BIG_ENDIAN_BITFIELD)
__s16 hop_limit:9;
__u16 __unused_1:7;
#else
__u16 __unused_1:7;
__s16 hop_limit:9;
#endif
#if defined(__BIG_ENDIAN_BITFIELD) #if defined(__BIG_ENDIAN_BITFIELD)
/* Packed in 16bits. */ /* Packed in 16bits. */
......
...@@ -916,7 +916,7 @@ static inline int ip6_sk_dst_hoplimit(struct ipv6_pinfo *np, struct flowi6 *fl6, ...@@ -916,7 +916,7 @@ static inline int ip6_sk_dst_hoplimit(struct ipv6_pinfo *np, struct flowi6 *fl6,
if (ipv6_addr_is_multicast(&fl6->daddr)) if (ipv6_addr_is_multicast(&fl6->daddr))
hlimit = np->mcast_hops; hlimit = np->mcast_hops;
else else
hlimit = np->hop_limit; hlimit = READ_ONCE(np->hop_limit);
if (hlimit < 0) if (hlimit < 0)
hlimit = ip6_dst_hoplimit(dst); hlimit = ip6_dst_hoplimit(dst);
return hlimit; return hlimit;
......
...@@ -309,7 +309,7 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, ...@@ -309,7 +309,7 @@ int ip6_xmit(const struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
* Fill in the IPv6 header * Fill in the IPv6 header
*/ */
if (np) if (np)
hlimit = np->hop_limit; hlimit = READ_ONCE(np->hop_limit);
if (hlimit < 0) if (hlimit < 0)
hlimit = ip6_dst_hoplimit(dst); hlimit = ip6_dst_hoplimit(dst);
......
...@@ -415,6 +415,16 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname, ...@@ -415,6 +415,16 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
if (ip6_mroute_opt(optname)) if (ip6_mroute_opt(optname))
return ip6_mroute_setsockopt(sk, optname, optval, optlen); return ip6_mroute_setsockopt(sk, optname, optval, optlen);
/* Handle options that can be set without locking the socket. */
switch (optname) {
case IPV6_UNICAST_HOPS:
if (optlen < sizeof(int))
return -EINVAL;
if (val > 255 || val < -1)
return -EINVAL;
WRITE_ONCE(np->hop_limit, val);
return 0;
}
if (needs_rtnl) if (needs_rtnl)
rtnl_lock(); rtnl_lock();
sockopt_lock_sock(sk); sockopt_lock_sock(sk);
...@@ -733,14 +743,6 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname, ...@@ -733,14 +743,6 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
} }
break; break;
} }
case IPV6_UNICAST_HOPS:
if (optlen < sizeof(int))
goto e_inval;
if (val > 255 || val < -1)
goto e_inval;
np->hop_limit = val;
retv = 0;
break;
case IPV6_MULTICAST_HOPS: case IPV6_MULTICAST_HOPS:
if (sk->sk_type == SOCK_STREAM) if (sk->sk_type == SOCK_STREAM)
...@@ -1347,7 +1349,7 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname, ...@@ -1347,7 +1349,7 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
struct dst_entry *dst; struct dst_entry *dst;
if (optname == IPV6_UNICAST_HOPS) if (optname == IPV6_UNICAST_HOPS)
val = np->hop_limit; val = READ_ONCE(np->hop_limit);
else else
val = np->mcast_hops; val = np->mcast_hops;
......
...@@ -1716,7 +1716,7 @@ static void ip6_mc_hdr(const struct sock *sk, struct sk_buff *skb, ...@@ -1716,7 +1716,7 @@ static void ip6_mc_hdr(const struct sock *sk, struct sk_buff *skb,
hdr->payload_len = htons(len); hdr->payload_len = htons(len);
hdr->nexthdr = proto; hdr->nexthdr = proto;
hdr->hop_limit = inet6_sk(sk)->hop_limit; hdr->hop_limit = READ_ONCE(inet6_sk(sk)->hop_limit);
hdr->saddr = *saddr; hdr->saddr = *saddr;
hdr->daddr = *daddr; hdr->daddr = *daddr;
......
...@@ -500,7 +500,7 @@ void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr, ...@@ -500,7 +500,7 @@ void ndisc_send_skb(struct sk_buff *skb, const struct in6_addr *daddr,
csum_partial(icmp6h, csum_partial(icmp6h,
skb->len, 0)); skb->len, 0));
ip6_nd_hdr(skb, saddr, daddr, inet6_sk(sk)->hop_limit, skb->len); ip6_nd_hdr(skb, saddr, daddr, READ_ONCE(inet6_sk(sk)->hop_limit), skb->len);
rcu_read_lock(); rcu_read_lock();
idev = __in6_dev_get(dst->dev); idev = __in6_dev_get(dst->dev);
......
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