ipv6: lockless IPV6_RECVERR implemetation
np->recverr is moved to inet->inet_flags to fix data-races. Signed-off-by: Eric Dumazet <edumazet@google.com> Reviewed-by: David Ahern <dsahern@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
1086ca7cce
commit
3fa29971c6
@ -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 {
|
|||||||
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)
|
|||||||
|
|
||||||
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,
|
|||||||
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)
|
|||||||
* 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,
|
|||||||
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);
|
|||||||
|
|
||||||
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,
|
|||||||
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 @@ done:
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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 @@ out:
|
|||||||
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,
|
|||||||
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,
|
|||||||
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 @@ csum_partial:
|
|||||||
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 @@ do_append_data:
|
|||||||
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,
|
|||||||
{
|
{
|
||||||
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,
|
|||||||
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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user