bpf: Allow SO_BINDTODEVICE opt in bpf_setsockopt
authorFerenc Fejes <fejes@inf.elte.hu>
Sat, 30 May 2020 21:09:01 +0000 (23:09 +0200)
committerAlexei Starovoitov <ast@kernel.org>
Mon, 1 Jun 2020 21:57:14 +0000 (14:57 -0700)
Extending the supported sockopts in bpf_setsockopt with
SO_BINDTODEVICE. We call sock_bindtoindex with parameter
lock_sk = false in this context because we already owning
the socket.

Signed-off-by: Ferenc Fejes <fejes@inf.elte.hu>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/4149e304867b8d5a606a305bc59e29b063e51f49.1590871065.git.fejes@inf.elte.hu
net/core/filter.c

index 85ff827..ae82bcb 100644 (file)
@@ -4248,6 +4248,9 @@ static const struct bpf_func_proto bpf_get_socket_uid_proto = {
 static int _bpf_setsockopt(struct sock *sk, int level, int optname,
                           char *optval, int optlen, u32 flags)
 {
+       char devname[IFNAMSIZ];
+       struct net *net;
+       int ifindex;
        int ret = 0;
        int val;
 
@@ -4257,7 +4260,7 @@ static int _bpf_setsockopt(struct sock *sk, int level, int optname,
        sock_owned_by_me(sk);
 
        if (level == SOL_SOCKET) {
-               if (optlen != sizeof(int))
+               if (optlen != sizeof(int) && optname != SO_BINDTODEVICE)
                        return -EINVAL;
                val = *((int *)optval);
 
@@ -4298,6 +4301,29 @@ static int _bpf_setsockopt(struct sock *sk, int level, int optname,
                                sk_dst_reset(sk);
                        }
                        break;
+               case SO_BINDTODEVICE:
+                       ret = -ENOPROTOOPT;
+#ifdef CONFIG_NETDEVICES
+                       optlen = min_t(long, optlen, IFNAMSIZ - 1);
+                       strncpy(devname, optval, optlen);
+                       devname[optlen] = 0;
+
+                       ifindex = 0;
+                       if (devname[0] != '\0') {
+                               struct net_device *dev;
+
+                               ret = -ENODEV;
+
+                               net = sock_net(sk);
+                               dev = dev_get_by_name(net, devname);
+                               if (!dev)
+                                       break;
+                               ifindex = dev->ifindex;
+                               dev_put(dev);
+                       }
+                       ret = sock_bindtoindex(sk, ifindex, false);
+#endif
+                       break;
                default:
                        ret = -EINVAL;
                }