net: introduce SO_BINDTOIFINDEX sockopt
authorDavid Herrmann <dh.herrmann@gmail.com>
Tue, 15 Jan 2019 13:42:14 +0000 (14:42 +0100)
committerDavid S. Miller <davem@davemloft.net>
Thu, 17 Jan 2019 22:55:51 +0000 (14:55 -0800)
This introduces a new generic SOL_SOCKET-level socket option called
SO_BINDTOIFINDEX. It behaves similar to SO_BINDTODEVICE, but takes a
network interface index as argument, rather than the network interface
name.

User-space often refers to network-interfaces via their index, but has
to temporarily resolve it to a name for a call into SO_BINDTODEVICE.
This might pose problems when the network-device is renamed
asynchronously by other parts of the system. When this happens, the
SO_BINDTODEVICE might either fail, or worse, it might bind to the wrong
device.

In most cases user-space only ever operates on devices which they
either manage themselves, or otherwise have a guarantee that the device
name will not change (e.g., devices that are UP cannot be renamed).
However, particularly in libraries this guarantee is non-obvious and it
would be nice if that race-condition would simply not exist. It would
make it easier for those libraries to operate even in situations where
the device-name might change under the hood.

A real use-case that we recently hit is trying to start the network
stack early in the initrd but make it survive into the real system.
Existing distributions rename network-interfaces during the transition
from initrd into the real system. This, obviously, cannot affect
devices that are up and running (unless you also consider moving them
between network-namespaces). However, the network manager now has to
make sure its management engine for dormant devices will not run in
parallel to these renames. Particularly, when you offload operations
like DHCP into separate processes, these might setup their sockets
early, and thus have to resolve the device-name possibly running into
this race-condition.

By avoiding a call to resolve the device-name, we no longer depend on
the name and can run network setup of dormant devices in parallel to
the transition off the initrd. The SO_BINDTOIFINDEX ioctl plugs this
race.

Reviewed-by: Tom Gundersen <teg@jklm.no>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
Acked-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
arch/alpha/include/uapi/asm/socket.h
arch/ia64/include/uapi/asm/socket.h
arch/mips/include/uapi/asm/socket.h
arch/parisc/include/uapi/asm/socket.h
arch/s390/include/uapi/asm/socket.h
arch/sparc/include/uapi/asm/socket.h
arch/xtensa/include/uapi/asm/socket.h
include/uapi/asm-generic/socket.h
net/core/sock.c

index 065fb372e355cf86905fc1be80b97be5ef3218b3..b1c9b542c021b1376a503980805bebbf23dfad58 100644 (file)
 #define SO_TXTIME              61
 #define SCM_TXTIME             SO_TXTIME
 
+#define SO_BINDTOIFINDEX       62
+
 #endif /* _UAPI_ASM_SOCKET_H */
index c872c4e6bafb64c4334810f73475eb21845481a6..ba0d245f9576c36a9db55fa825c44bf5ad70c286 100644 (file)
 #define SO_TXTIME              61
 #define SCM_TXTIME             SO_TXTIME
 
+#define SO_BINDTOIFINDEX       62
+
 #endif /* _ASM_IA64_SOCKET_H */
index 71370fb3ceef4ee4c235876bd1ac7056cff7f1e8..73e25e35d8037bce5e09d98a0b31dcda65c77d3d 100644 (file)
 #define SO_TXTIME              61
 #define SCM_TXTIME             SO_TXTIME
 
+#define SO_BINDTOIFINDEX       62
+
 #endif /* _UAPI_ASM_SOCKET_H */
index 061b9cf2a77988a6b82eb47f3d1fd1a53e3264a2..52bed5976cbeb0ea82eeb141138d6e45db6e2bcc 100644 (file)
 #define SO_TXTIME              0x4036
 #define SCM_TXTIME             SO_TXTIME
 
+#define SO_BINDTOIFINDEX       0x4037
+
 #endif /* _UAPI_ASM_SOCKET_H */
index 39d901476ee5d351f1e34a66f0828ca9d9fe522b..49c971587087e5bab0a2c3aeac2cb15bd8c93247 100644 (file)
 #define SO_TXTIME              61
 #define SCM_TXTIME             SO_TXTIME
 
+#define SO_BINDTOIFINDEX       62
+
 #endif /* _ASM_SOCKET_H */
index 7ea35e5601b6bed654363456c4127f28beb7cada..bbdb81594dd4d899dc3bcdaf45b4bf09d354cde6 100644 (file)
 #define SO_TXTIME              0x003f
 #define SCM_TXTIME             SO_TXTIME
 
+#define SO_BINDTOIFINDEX       0x0041
+
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_SECURITY_AUTHENTICATION             0x5001
 #define SO_SECURITY_ENCRYPTION_TRANSPORT       0x5002
index 1de07a7f76806984e504bc03638df7e0610a3455..b434217783d04a09d702fa5c84c05f4b28335027 100644 (file)
 #define SO_TXTIME              61
 #define SCM_TXTIME             SO_TXTIME
 
+#define SO_BINDTOIFINDEX       62
+
 #endif /* _XTENSA_SOCKET_H */
index a12692e5f7a8462b22791a09fcb0ef41d0450938..3066ab3853a894c99185b6fd3ae29a9bf1f603cf 100644 (file)
 #define SO_TXTIME              61
 #define SCM_TXTIME             SO_TXTIME
 
+#define SO_BINDTOIFINDEX       62
+
 #endif /* __ASM_GENERIC_SOCKET_H */
index 6aa2e7e0b4fbdbc29d43d6b61a53b8de2a7ba269..b53764ebb9737286ffcf5485bb97d081e7de86b2 100644 (file)
@@ -520,20 +520,43 @@ struct dst_entry *sk_dst_check(struct sock *sk, u32 cookie)
 }
 EXPORT_SYMBOL(sk_dst_check);
 
-static int sock_setbindtodevice(struct sock *sk, char __user *optval,
-                               int optlen)
+static int sock_setbindtodevice_locked(struct sock *sk, int ifindex)
 {
        int ret = -ENOPROTOOPT;
 #ifdef CONFIG_NETDEVICES
        struct net *net = sock_net(sk);
-       char devname[IFNAMSIZ];
-       int index;
 
        /* Sorry... */
        ret = -EPERM;
        if (!ns_capable(net->user_ns, CAP_NET_RAW))
                goto out;
 
+       ret = -EINVAL;
+       if (ifindex < 0)
+               goto out;
+
+       sk->sk_bound_dev_if = ifindex;
+       if (sk->sk_prot->rehash)
+               sk->sk_prot->rehash(sk);
+       sk_dst_reset(sk);
+
+       ret = 0;
+
+out:
+#endif
+
+       return ret;
+}
+
+static int sock_setbindtodevice(struct sock *sk, char __user *optval,
+                               int optlen)
+{
+       int ret = -ENOPROTOOPT;
+#ifdef CONFIG_NETDEVICES
+       struct net *net = sock_net(sk);
+       char devname[IFNAMSIZ];
+       int index;
+
        ret = -EINVAL;
        if (optlen < 0)
                goto out;
@@ -566,14 +589,9 @@ static int sock_setbindtodevice(struct sock *sk, char __user *optval,
        }
 
        lock_sock(sk);
-       sk->sk_bound_dev_if = index;
-       if (sk->sk_prot->rehash)
-               sk->sk_prot->rehash(sk);
-       sk_dst_reset(sk);
+       ret = sock_setbindtodevice_locked(sk, index);
        release_sock(sk);
 
-       ret = 0;
-
 out:
 #endif
 
@@ -1055,6 +1073,10 @@ set_rcvbuf:
                }
                break;
 
+       case SO_BINDTOIFINDEX:
+               ret = sock_setbindtodevice_locked(sk, val);
+               break;
+
        default:
                ret = -ENOPROTOOPT;
                break;
@@ -1399,6 +1421,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
                                  SOF_TXTIME_REPORT_ERRORS : 0;
                break;
 
+       case SO_BINDTOIFINDEX:
+               v.val = sk->sk_bound_dev_if;
+               break;
+
        default:
                /* We implement the SO_SNDLOWAT etc to not be settable
                 * (1003.1g 7).