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

ipv6: lockless IPV6_RECVERR implemetation

np->recverr is moved to inet->inet_flags to fix data-races.
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 1086ca7c
...@@ -243,8 +243,7 @@ struct ipv6_pinfo { ...@@ -243,8 +243,7 @@ struct ipv6_pinfo {
} rxopt; } rxopt;
/* sockopt flags */ /* sockopt flags */
__u16 recverr:1, __u16 sndflow:1,
sndflow:1,
repflow:1, repflow:1,
pmtudisc:3, pmtudisc:3,
padding:1, /* 1 bit hole */ padding:1, /* 1 bit hole */
......
...@@ -274,6 +274,7 @@ enum { ...@@ -274,6 +274,7 @@ enum {
INET_FLAGS_AUTOFLOWLABEL_SET = 23, INET_FLAGS_AUTOFLOWLABEL_SET = 23,
INET_FLAGS_AUTOFLOWLABEL = 24, INET_FLAGS_AUTOFLOWLABEL = 24,
INET_FLAGS_DONTFRAG = 25, INET_FLAGS_DONTFRAG = 25,
INET_FLAGS_RECVERR6 = 26,
}; };
/* cmsg flags for inet */ /* cmsg flags for inet */
......
...@@ -1303,9 +1303,7 @@ static inline int ip6_sock_set_v6only(struct sock *sk) ...@@ -1303,9 +1303,7 @@ static inline int ip6_sock_set_v6only(struct sock *sk)
static inline void ip6_sock_set_recverr(struct sock *sk) static inline void ip6_sock_set_recverr(struct sock *sk)
{ {
lock_sock(sk); inet6_set_bit(RECVERR6, sk);
inet6_sk(sk)->recverr = true;
release_sock(sk);
} }
static inline int __ip6_sock_set_addr_preferences(struct sock *sk, int val) static inline int __ip6_sock_set_addr_preferences(struct sock *sk, int val)
......
...@@ -185,7 +185,7 @@ static int dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, ...@@ -185,7 +185,7 @@ static int dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
goto out; goto out;
} }
if (!sock_owned_by_user(sk) && np->recverr) { if (!sock_owned_by_user(sk) && inet6_test_bit(RECVERR6, sk)) {
sk->sk_err = err; sk->sk_err = err;
sk_error_report(sk); sk_error_report(sk);
} else { } else {
......
...@@ -581,7 +581,7 @@ void ping_err(struct sk_buff *skb, int offset, u32 info) ...@@ -581,7 +581,7 @@ void ping_err(struct sk_buff *skb, int offset, u32 info)
* 4.1.3.3. * 4.1.3.3.
*/ */
if ((family == AF_INET && !inet_test_bit(RECVERR, sk)) || if ((family == AF_INET && !inet_test_bit(RECVERR, sk)) ||
(family == AF_INET6 && !inet6_sk(sk)->recverr)) { (family == AF_INET6 && !inet6_test_bit(RECVERR6, sk))) {
if (!harderr || sk->sk_state != TCP_ESTABLISHED) if (!harderr || sk->sk_state != TCP_ESTABLISHED)
goto out; goto out;
} else { } else {
......
...@@ -305,11 +305,10 @@ static void ipv6_icmp_error_rfc4884(const struct sk_buff *skb, ...@@ -305,11 +305,10 @@ static void ipv6_icmp_error_rfc4884(const struct sk_buff *skb,
void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err,
__be16 port, u32 info, u8 *payload) __be16 port, u32 info, u8 *payload)
{ {
struct ipv6_pinfo *np = inet6_sk(sk);
struct icmp6hdr *icmph = icmp6_hdr(skb); struct icmp6hdr *icmph = icmp6_hdr(skb);
struct sock_exterr_skb *serr; struct sock_exterr_skb *serr;
if (!np->recverr) if (!inet6_test_bit(RECVERR6, sk))
return; return;
skb = skb_clone(skb, GFP_ATOMIC); skb = skb_clone(skb, GFP_ATOMIC);
...@@ -344,12 +343,11 @@ EXPORT_SYMBOL_GPL(ipv6_icmp_error); ...@@ -344,12 +343,11 @@ EXPORT_SYMBOL_GPL(ipv6_icmp_error);
void ipv6_local_error(struct sock *sk, int err, struct flowi6 *fl6, u32 info) void ipv6_local_error(struct sock *sk, int err, struct flowi6 *fl6, u32 info)
{ {
const struct ipv6_pinfo *np = inet6_sk(sk);
struct sock_exterr_skb *serr; struct sock_exterr_skb *serr;
struct ipv6hdr *iph; struct ipv6hdr *iph;
struct sk_buff *skb; struct sk_buff *skb;
if (!np->recverr) if (!inet6_test_bit(RECVERR6, sk))
return; return;
skb = alloc_skb(sizeof(struct ipv6hdr), GFP_ATOMIC); skb = alloc_skb(sizeof(struct ipv6hdr), GFP_ATOMIC);
......
...@@ -481,6 +481,13 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname, ...@@ -481,6 +481,13 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
case IPV6_DONTFRAG: case IPV6_DONTFRAG:
inet6_assign_bit(DONTFRAG, sk, valbool); inet6_assign_bit(DONTFRAG, sk, valbool);
return 0; return 0;
case IPV6_RECVERR:
if (optlen < sizeof(int))
return -EINVAL;
inet6_assign_bit(RECVERR6, sk, valbool);
if (!val)
skb_errqueue_purge(&sk->sk_error_queue);
return 0;
} }
if (needs_rtnl) if (needs_rtnl)
rtnl_lock(); rtnl_lock();
...@@ -943,14 +950,6 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname, ...@@ -943,14 +950,6 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
np->pmtudisc = val; np->pmtudisc = val;
retv = 0; retv = 0;
break; break;
case IPV6_RECVERR:
if (optlen < sizeof(int))
goto e_inval;
np->recverr = valbool;
if (!val)
skb_errqueue_purge(&sk->sk_error_queue);
retv = 0;
break;
case IPV6_FLOWINFO_SEND: case IPV6_FLOWINFO_SEND:
if (optlen < sizeof(int)) if (optlen < sizeof(int))
goto e_inval; goto e_inval;
...@@ -1380,7 +1379,7 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname, ...@@ -1380,7 +1379,7 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
break; break;
case IPV6_RECVERR: case IPV6_RECVERR:
val = np->recverr; val = inet6_test_bit(RECVERR6, sk);
break; break;
case IPV6_FLOWINFO_SEND: case IPV6_FLOWINFO_SEND:
......
...@@ -291,6 +291,7 @@ static void rawv6_err(struct sock *sk, struct sk_buff *skb, ...@@ -291,6 +291,7 @@ static void rawv6_err(struct sock *sk, struct sk_buff *skb,
struct inet6_skb_parm *opt, struct inet6_skb_parm *opt,
u8 type, u8 code, int offset, __be32 info) u8 type, u8 code, int offset, __be32 info)
{ {
bool recverr = inet6_test_bit(RECVERR6, sk);
struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk);
int err; int err;
int harderr; int harderr;
...@@ -300,7 +301,7 @@ static void rawv6_err(struct sock *sk, struct sk_buff *skb, ...@@ -300,7 +301,7 @@ static void rawv6_err(struct sock *sk, struct sk_buff *skb,
2. Socket is connected (otherwise the error indication 2. Socket is connected (otherwise the error indication
is useless without recverr and error is hard. is useless without recverr and error is hard.
*/ */
if (!np->recverr && sk->sk_state != TCP_ESTABLISHED) if (!recverr && sk->sk_state != TCP_ESTABLISHED)
return; return;
harderr = icmpv6_err_convert(type, code, &err); harderr = icmpv6_err_convert(type, code, &err);
...@@ -312,14 +313,14 @@ static void rawv6_err(struct sock *sk, struct sk_buff *skb, ...@@ -312,14 +313,14 @@ static void rawv6_err(struct sock *sk, struct sk_buff *skb,
ip6_sk_redirect(skb, sk); ip6_sk_redirect(skb, sk);
return; return;
} }
if (np->recverr) { if (recverr) {
u8 *payload = skb->data; u8 *payload = skb->data;
if (!inet_test_bit(HDRINCL, sk)) if (!inet_test_bit(HDRINCL, sk))
payload += offset; payload += offset;
ipv6_icmp_error(sk, skb, err, 0, ntohl(info), payload); ipv6_icmp_error(sk, skb, err, 0, ntohl(info), payload);
} }
if (np->recverr || harderr) { if (recverr || harderr) {
sk->sk_err = err; sk->sk_err = err;
sk_error_report(sk); sk_error_report(sk);
} }
...@@ -587,7 +588,6 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length, ...@@ -587,7 +588,6 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length,
struct flowi6 *fl6, struct dst_entry **dstp, struct flowi6 *fl6, struct dst_entry **dstp,
unsigned int flags, const struct sockcm_cookie *sockc) unsigned int flags, const struct sockcm_cookie *sockc)
{ {
struct ipv6_pinfo *np = inet6_sk(sk);
struct net *net = sock_net(sk); struct net *net = sock_net(sk);
struct ipv6hdr *iph; struct ipv6hdr *iph;
struct sk_buff *skb; struct sk_buff *skb;
...@@ -668,7 +668,7 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length, ...@@ -668,7 +668,7 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length,
error: error:
IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
error_check: error_check:
if (err == -ENOBUFS && !np->recverr) if (err == -ENOBUFS && !inet6_test_bit(RECVERR6, sk))
err = 0; err = 0;
return err; return err;
} }
......
...@@ -508,7 +508,7 @@ static int tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, ...@@ -508,7 +508,7 @@ static int tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
tcp_ld_RTO_revert(sk, seq); tcp_ld_RTO_revert(sk, seq);
} }
if (!sock_owned_by_user(sk) && np->recverr) { if (!sock_owned_by_user(sk) && inet6_test_bit(RECVERR6, sk)) {
WRITE_ONCE(sk->sk_err, err); WRITE_ONCE(sk->sk_err, err);
sk_error_report(sk); sk_error_report(sk);
} else { } else {
......
...@@ -619,7 +619,7 @@ int __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt, ...@@ -619,7 +619,7 @@ int __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
goto out; goto out;
} }
if (!np->recverr) { if (!inet6_test_bit(RECVERR6, sk)) {
if (!harderr || sk->sk_state != TCP_ESTABLISHED) if (!harderr || sk->sk_state != TCP_ESTABLISHED)
goto out; goto out;
} else { } else {
...@@ -1283,7 +1283,7 @@ static int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6, ...@@ -1283,7 +1283,7 @@ static int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6,
send: send:
err = ip6_send_skb(skb); err = ip6_send_skb(skb);
if (err) { if (err) {
if (err == -ENOBUFS && !inet6_sk(sk)->recverr) { if (err == -ENOBUFS && !inet6_test_bit(RECVERR6, sk)) {
UDP6_INC_STATS(sock_net(sk), UDP6_INC_STATS(sock_net(sk),
UDP_MIB_SNDBUFERRORS, is_udplite); UDP_MIB_SNDBUFERRORS, is_udplite);
err = 0; err = 0;
...@@ -1608,7 +1608,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -1608,7 +1608,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
up->pending = 0; up->pending = 0;
if (err > 0) if (err > 0)
err = np->recverr ? net_xmit_errno(err) : 0; err = inet6_test_bit(RECVERR6, sk) ? net_xmit_errno(err) : 0;
release_sock(sk); release_sock(sk);
out: out:
......
...@@ -128,7 +128,6 @@ static void sctp_v6_err_handle(struct sctp_transport *t, struct sk_buff *skb, ...@@ -128,7 +128,6 @@ static void sctp_v6_err_handle(struct sctp_transport *t, struct sk_buff *skb,
{ {
struct sctp_association *asoc = t->asoc; struct sctp_association *asoc = t->asoc;
struct sock *sk = asoc->base.sk; struct sock *sk = asoc->base.sk;
struct ipv6_pinfo *np;
int err = 0; int err = 0;
switch (type) { switch (type) {
...@@ -149,9 +148,8 @@ static void sctp_v6_err_handle(struct sctp_transport *t, struct sk_buff *skb, ...@@ -149,9 +148,8 @@ static void sctp_v6_err_handle(struct sctp_transport *t, struct sk_buff *skb,
break; break;
} }
np = inet6_sk(sk);
icmpv6_err_convert(type, code, &err); icmpv6_err_convert(type, code, &err);
if (!sock_owned_by_user(sk) && np->recverr) { if (!sock_owned_by_user(sk) && inet6_test_bit(RECVERR6, sk)) {
sk->sk_err = err; sk->sk_err = err;
sk_error_report(sk); sk_error_report(sk);
} else { } else {
......
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