inet: implement lockless getsockopt(IP_UNICAST_IF)
authorEric Dumazet <edumazet@google.com>
Fri, 22 Sep 2023 03:42:19 +0000 (03:42 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sun, 1 Oct 2023 18:39:19 +0000 (19:39 +0100)
Add missing READ_ONCE() annotations when reading inet->uc_index

Implementing getsockopt(IP_UNICAST_IF) locklessly seems possible,
the setsockopt() part might not be possible at the moment.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/datagram.c
net/ipv4/ip_sockglue.c
net/ipv4/ping.c
net/ipv4/raw.c
net/ipv4/udp.c

index cb5dbee9e018fbba1bc1e5705e8bec6c4203af56..1480e9ebdfef445960e1f70f34f33a0e0c52b65b 100644 (file)
@@ -43,7 +43,7 @@ int __ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len
                if (!saddr)
                        saddr = inet->mc_addr;
        } else if (!oif) {
-               oif = inet->uc_index;
+               oif = READ_ONCE(inet->uc_index);
        }
        fl4 = &inet->cork.fl.u.ip4;
        rt = ip_route_connect(fl4, usin->sin_addr.s_addr, saddr, oif,
index 04579e390ddd4dadb8a107ef0b5da15e7a60f1ff..58995526c6e965d613b8cdea61b84916d608a6fb 100644 (file)
@@ -1113,7 +1113,7 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname,
 
                ifindex = (__force int)ntohl((__force __be32)val);
                if (ifindex == 0) {
-                       inet->uc_index = 0;
+                       WRITE_ONCE(inet->uc_index, 0);
                        err = 0;
                        break;
                }
@@ -1130,7 +1130,7 @@ int do_ip_setsockopt(struct sock *sk, int level, int optname,
                if (sk->sk_bound_dev_if && midx != sk->sk_bound_dev_if)
                        break;
 
-               inet->uc_index = ifindex;
+               WRITE_ONCE(inet->uc_index, ifindex);
                err = 0;
                break;
        }
@@ -1633,6 +1633,9 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname,
                        return -ENOTCONN;
                goto copyval;
        }
+       case IP_UNICAST_IF:
+               val = (__force int)htonl((__u32) READ_ONCE(inet->uc_index));
+               goto copyval;
        }
 
        if (needs_rtnl)
@@ -1640,9 +1643,6 @@ int do_ip_getsockopt(struct sock *sk, int level, int optname,
        sockopt_lock_sock(sk);
 
        switch (optname) {
-       case IP_UNICAST_IF:
-               val = (__force int)htonl((__u32) inet->uc_index);
-               break;
        case IP_MULTICAST_IF:
        {
                struct in_addr addr;
index 50d12b0c8d46fdcd9b448c3ebc90395ebf426075..66ad1f95af49f222afe0ee75b9163dd0af0a2c49 100644 (file)
@@ -777,7 +777,7 @@ static int ping_v4_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
                if (!saddr)
                        saddr = inet->mc_addr;
        } else if (!ipc.oif)
-               ipc.oif = inet->uc_index;
+               ipc.oif = READ_ONCE(inet->uc_index);
 
        flowi4_init_output(&fl4, ipc.oif, ipc.sockc.mark, tos, scope,
                           sk->sk_protocol, inet_sk_flowi_flags(sk), faddr,
index ade1aecd7c71184d753a28a67bc9b30087247db4..e2357d23202e5a39832bb1550c365de9a836c363 100644 (file)
@@ -482,7 +482,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
        int free = 0;
        __be32 daddr;
        __be32 saddr;
-       int err;
+       int uc_index, err;
        struct ip_options_data opt_copy;
        struct raw_frag_vec rfv;
        int hdrincl;
@@ -576,24 +576,25 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
        tos = get_rttos(&ipc, inet);
        scope = ip_sendmsg_scope(inet, &ipc, msg);
 
+       uc_index = READ_ONCE(inet->uc_index);
        if (ipv4_is_multicast(daddr)) {
                if (!ipc.oif || netif_index_is_l3_master(sock_net(sk), ipc.oif))
                        ipc.oif = inet->mc_index;
                if (!saddr)
                        saddr = inet->mc_addr;
        } else if (!ipc.oif) {
-               ipc.oif = inet->uc_index;
-       } else if (ipv4_is_lbcast(daddr) && inet->uc_index) {
+               ipc.oif = uc_index;
+       } else if (ipv4_is_lbcast(daddr) && uc_index) {
                /* oif is set, packet is to local broadcast
                 * and uc_index is set. oif is most likely set
                 * by sk_bound_dev_if. If uc_index != oif check if the
                 * oif is an L3 master and uc_index is an L3 slave.
                 * If so, we want to allow the send using the uc_index.
                 */
-               if (ipc.oif != inet->uc_index &&
+               if (ipc.oif != uc_index &&
                    ipc.oif == l3mdev_master_ifindex_by_index(sock_net(sk),
-                                                             inet->uc_index)) {
-                       ipc.oif = inet->uc_index;
+                                                             uc_index)) {
+                       ipc.oif = uc_index;
                }
        }
 
index 731a723dc80816f0b5b0803d7397f7e9e8cd8b09..1e0c3aba1e5a88c7ba50a28511412a1710f1bab5 100644 (file)
@@ -1055,6 +1055,7 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
        int (*getfrag)(void *, char *, int, int, int, struct sk_buff *);
        struct sk_buff *skb;
        struct ip_options_data opt_copy;
+       int uc_index;
 
        if (len > 0xFFFF)
                return -EMSGSIZE;
@@ -1173,6 +1174,7 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
        if (scope == RT_SCOPE_LINK)
                connected = 0;
 
+       uc_index = READ_ONCE(inet->uc_index);
        if (ipv4_is_multicast(daddr)) {
                if (!ipc.oif || netif_index_is_l3_master(sock_net(sk), ipc.oif))
                        ipc.oif = inet->mc_index;
@@ -1180,18 +1182,18 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
                        saddr = inet->mc_addr;
                connected = 0;
        } else if (!ipc.oif) {
-               ipc.oif = inet->uc_index;
-       } else if (ipv4_is_lbcast(daddr) && inet->uc_index) {
+               ipc.oif = uc_index;
+       } else if (ipv4_is_lbcast(daddr) && uc_index) {
                /* oif is set, packet is to local broadcast and
                 * uc_index is set. oif is most likely set
                 * by sk_bound_dev_if. If uc_index != oif check if the
                 * oif is an L3 master and uc_index is an L3 slave.
                 * If so, we want to allow the send using the uc_index.
                 */
-               if (ipc.oif != inet->uc_index &&
+               if (ipc.oif != uc_index &&
                    ipc.oif == l3mdev_master_ifindex_by_index(sock_net(sk),
-                                                             inet->uc_index)) {
-                       ipc.oif = inet->uc_index;
+                                                             uc_index)) {
+                       ipc.oif = uc_index;
                }
        }