ipv6: lockless IPV6_FLOWINFO_SEND implementation
authorEric Dumazet <edumazet@google.com>
Tue, 12 Sep 2023 16:02:12 +0000 (16:02 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 15 Sep 2023 09:33:48 +0000 (10:33 +0100)
np->sndflow reads are racy.

Use one bit ftom atomic inet->inet_flags instead,
IPV6_FLOWINFO_SEND setsockopt() can be lockless.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
13 files changed:
include/linux/ipv6.h
include/net/inet_sock.h
net/dccp/ipv6.c
net/ipv4/ping.c
net/ipv6/af_inet6.c
net/ipv6/datagram.c
net/ipv6/ipv6_sockglue.c
net/ipv6/ping.c
net/ipv6/raw.c
net/ipv6/tcp_ipv6.c
net/ipv6/udp.c
net/l2tp/l2tp_ip6.c
net/sctp/ipv6.c

index 10f521a..0925382 100644 (file)
@@ -243,8 +243,7 @@ struct ipv6_pinfo {
        } rxopt;
 
        /* sockopt flags */
-       __u8                    sndflow:1,
-                               srcprefs:3;     /* 001: prefer temporary address
+       __u8                    srcprefs:3;     /* 001: prefer temporary address
                                                 * 010: prefer public address
                                                 * 100: prefer care-of address
                                                 */
index befee0f..98e1195 100644 (file)
@@ -277,6 +277,7 @@ enum {
        INET_FLAGS_RECVERR6     = 26,
        INET_FLAGS_REPFLOW      = 27,
        INET_FLAGS_RTALERT_ISOLATE = 28,
+       INET_FLAGS_SNDFLOW      = 29,
 };
 
 /* cmsg flags for inet */
index d7e63ee..4803f06 100644 (file)
@@ -844,7 +844,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
 
        memset(&fl6, 0, sizeof(fl6));
 
-       if (np->sndflow) {
+       if (inet6_test_bit(SNDFLOW, sk)) {
                fl6.flowlabel = usin->sin6_flowinfo & IPV6_FLOWINFO_MASK;
                IP6_ECN_flow_init(fl6.flowlabel);
                if (fl6.flowlabel & IPV6_FLOWLABEL_MASK) {
index bc01ad5..4dd809b 100644 (file)
@@ -899,7 +899,6 @@ int ping_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags,
 
 #if IS_ENABLED(CONFIG_IPV6)
        } else if (family == AF_INET6) {
-               struct ipv6_pinfo *np = inet6_sk(sk);
                struct ipv6hdr *ip6 = ipv6_hdr(skb);
                DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name);
 
@@ -908,7 +907,7 @@ int ping_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int flags,
                        sin6->sin6_port = 0;
                        sin6->sin6_addr = ip6->saddr;
                        sin6->sin6_flowinfo = 0;
-                       if (np->sndflow)
+                       if (inet6_test_bit(SNDFLOW, sk))
                                sin6->sin6_flowinfo = ip6_flowinfo(ip6);
                        sin6->sin6_scope_id =
                                ipv6_iface_scope_id(&sin6->sin6_addr,
index 4873736..c6ad0d6 100644 (file)
@@ -537,7 +537,7 @@ int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
                }
                sin->sin6_port = inet->inet_dport;
                sin->sin6_addr = sk->sk_v6_daddr;
-               if (np->sndflow)
+               if (inet6_test_bit(SNDFLOW, sk))
                        sin->sin6_flowinfo = np->flow_label;
                BPF_CGROUP_RUN_SA_PROG(sk, (struct sockaddr *)sin,
                                       CGROUP_INET6_GETPEERNAME);
index 74673a5..cc6a502 100644 (file)
@@ -80,7 +80,8 @@ int ip6_datagram_dst_update(struct sock *sk, bool fix_sk_saddr)
        struct flowi6 fl6;
        int err = 0;
 
-       if (np->sndflow && (np->flow_label & IPV6_FLOWLABEL_MASK)) {
+       if (inet6_test_bit(SNDFLOW, sk) &&
+           (np->flow_label & IPV6_FLOWLABEL_MASK)) {
                flowlabel = fl6_sock_lookup(sk, np->flow_label);
                if (IS_ERR(flowlabel))
                        return -EINVAL;
@@ -163,7 +164,7 @@ int __ip6_datagram_connect(struct sock *sk, struct sockaddr *uaddr,
        if (usin->sin6_family != AF_INET6)
                return -EAFNOSUPPORT;
 
-       if (np->sndflow)
+       if (inet6_test_bit(SNDFLOW, sk))
                fl6_flowlabel = usin->sin6_flowinfo & IPV6_FLOWINFO_MASK;
 
        if (ipv6_addr_any(&usin->sin6_addr)) {
@@ -491,7 +492,7 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
                        const struct ipv6hdr *ip6h = container_of((struct in6_addr *)(nh + serr->addr_offset),
                                                                  struct ipv6hdr, daddr);
                        sin->sin6_addr = ip6h->daddr;
-                       if (np->sndflow)
+                       if (inet6_test_bit(SNDFLOW, sk))
                                sin->sin6_flowinfo = ip6_flowinfo(ip6h);
                        sin->sin6_scope_id =
                                ipv6_iface_scope_id(&sin->sin6_addr,
index 85ea426..e9dc6f8 100644 (file)
@@ -500,6 +500,11 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
                        return -EINVAL;
                WRITE_ONCE(np->pmtudisc, val);
                return 0;
+       case IPV6_FLOWINFO_SEND:
+               if (optlen < sizeof(int))
+                       return -EINVAL;
+               inet6_assign_bit(SNDFLOW, sk, valbool);
+               return 0;
        }
        if (needs_rtnl)
                rtnl_lock();
@@ -948,12 +953,6 @@ done:
                        goto e_inval;
                retv = ip6_ra_control(sk, val);
                break;
-       case IPV6_FLOWINFO_SEND:
-               if (optlen < sizeof(int))
-                       goto e_inval;
-               np->sndflow = valbool;
-               retv = 0;
-               break;
        case IPV6_FLOWLABEL_MGR:
                retv = ipv6_flowlabel_opt(sk, optval, optlen);
                break;
@@ -1381,7 +1380,7 @@ int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
                break;
 
        case IPV6_FLOWINFO_SEND:
-               val = np->sndflow;
+               val = inet6_test_bit(SNDFLOW, sk);
                break;
 
        case IPV6_FLOWLABEL_MGR:
index 4444b61..e8fb0d2 100644 (file)
@@ -89,7 +89,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
                        return -EAFNOSUPPORT;
                }
                daddr = &(u->sin6_addr);
-               if (np->sndflow)
+               if (inet6_test_bit(SNDFLOW, sk))
                        fl6.flowlabel = u->sin6_flowinfo & IPV6_FLOWINFO_MASK;
                if (__ipv6_addr_needs_scope_id(ipv6_addr_type(daddr)))
                        oif = u->sin6_scope_id;
index 47372cc..a2aa54a 100644 (file)
@@ -795,7 +795,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
                        return -EINVAL;
 
                daddr = &sin6->sin6_addr;
-               if (np->sndflow) {
+               if (inet6_test_bit(SNDFLOW, sk)) {
                        fl6.flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK;
                        if (fl6.flowlabel&IPV6_FLOWLABEL_MASK) {
                                flowlabel = fl6_sock_lookup(sk, fl6.flowlabel);
index 201caf8..94afb8d 100644 (file)
@@ -163,7 +163,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
 
        memset(&fl6, 0, sizeof(fl6));
 
-       if (np->sndflow) {
+       if (inet6_test_bit(SNDFLOW, sk)) {
                fl6.flowlabel = usin->sin6_flowinfo&IPV6_FLOWINFO_MASK;
                IP6_ECN_flow_init(fl6.flowlabel);
                if (fl6.flowlabel&IPV6_FLOWLABEL_MASK) {
index c17e19f..5e9312e 100644 (file)
@@ -1429,7 +1429,7 @@ do_udp_sendmsg:
                fl6->fl6_dport = sin6->sin6_port;
                daddr = &sin6->sin6_addr;
 
-               if (np->sndflow) {
+               if (inet6_test_bit(SNDFLOW, sk)) {
                        fl6->flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK;
                        if (fl6->flowlabel & IPV6_FLOWLABEL_MASK) {
                                flowlabel = fl6_sock_lookup(sk, fl6->flowlabel);
index 40af243..44cfb72 100644 (file)
@@ -431,7 +431,7 @@ static int l2tp_ip6_getname(struct socket *sock, struct sockaddr *uaddr,
                        return -ENOTCONN;
                lsa->l2tp_conn_id = lsk->peer_conn_id;
                lsa->l2tp_addr = sk->sk_v6_daddr;
-               if (np->sndflow)
+               if (inet6_test_bit(SNDFLOW, sk))
                        lsa->l2tp_flowinfo = np->flow_label;
        } else {
                if (ipv6_addr_any(&sk->sk_v6_rcv_saddr))
@@ -529,7 +529,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
                        return -EAFNOSUPPORT;
 
                daddr = &lsa->l2tp_addr;
-               if (np->sndflow) {
+               if (inet6_test_bit(SNDFLOW, sk)) {
                        fl6.flowlabel = lsa->l2tp_flowinfo & IPV6_FLOWINFO_MASK;
                        if (fl6.flowlabel & IPV6_FLOWLABEL_MASK) {
                                flowlabel = fl6_sock_lookup(sk, fl6.flowlabel);
index 42b5b85..5c0ed59 100644 (file)
@@ -296,7 +296,8 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
        if (t->flowlabel & SCTP_FLOWLABEL_SET_MASK)
                fl6->flowlabel = htonl(t->flowlabel & SCTP_FLOWLABEL_VAL_MASK);
 
-       if (np->sndflow && (fl6->flowlabel & IPV6_FLOWLABEL_MASK)) {
+       if (inet6_test_bit(SNDFLOW, sk) &&
+           (fl6->flowlabel & IPV6_FLOWLABEL_MASK)) {
                struct ip6_flowlabel *flowlabel;
 
                flowlabel = fl6_sock_lookup(sk, fl6->flowlabel);