Skip to content

Commit

Permalink
Merge branch 'bpf_setsockopt-SO_BINDTODEVICE'
Browse files Browse the repository at this point in the history
Ferenc Fejes says:

====================
This option makes it possible to programatically bind sockets
to netdevices. With the help of this option sockets
of VRF unaware applications could be distributed between
multiple VRFs with an eBPF program. This lets the applications
benefit from multiple possible routes.

v2:
- splitting up the patch to three parts
- lock_sk parameter for optional locking in sock_bindtoindex - Stanislav Fomichev
- testing the SO_BINDTODEVICE option - Andrii Nakryiko
====================

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
  • Loading branch information
Alexei Starovoitov committed Jun 1, 2020
2 parents bb2359f + 9c441fe commit c48a24f
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 8 deletions.
2 changes: 1 addition & 1 deletion include/net/sock.h
Original file line number Diff line number Diff line change
Expand Up @@ -2690,7 +2690,7 @@ static inline bool sk_dev_equal_l3scope(struct sock *sk, int dif)

void sock_def_readable(struct sock *sk);

int sock_bindtoindex(struct sock *sk, int ifindex);
int sock_bindtoindex(struct sock *sk, int ifindex, bool lock_sk);
void sock_enable_timestamps(struct sock *sk);
void sock_no_linger(struct sock *sk);
void sock_set_keepalive(struct sock *sk);
Expand Down
28 changes: 27 additions & 1 deletion net/core/filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);

Expand Down Expand Up @@ -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;
}
Expand Down
10 changes: 6 additions & 4 deletions net/core/sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -594,13 +594,15 @@ static int sock_bindtoindex_locked(struct sock *sk, int ifindex)
return ret;
}

int sock_bindtoindex(struct sock *sk, int ifindex)
int sock_bindtoindex(struct sock *sk, int ifindex, bool lock_sk)
{
int ret;

lock_sock(sk);
if (lock_sk)
lock_sock(sk);
ret = sock_bindtoindex_locked(sk, ifindex);
release_sock(sk);
if (lock_sk)
release_sock(sk);

return ret;
}
Expand Down Expand Up @@ -646,7 +648,7 @@ static int sock_setbindtodevice(struct sock *sk, char __user *optval,
goto out;
}

return sock_bindtoindex(sk, index);
return sock_bindtoindex(sk, index, true);
out:
#endif

Expand Down
2 changes: 1 addition & 1 deletion net/ipv4/udp_tunnel.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ int udp_sock_create4(struct net *net, struct udp_port_cfg *cfg,
goto error;

if (cfg->bind_ifindex) {
err = sock_bindtoindex(sock->sk, cfg->bind_ifindex);
err = sock_bindtoindex(sock->sk, cfg->bind_ifindex, true);
if (err < 0)
goto error;
}
Expand Down
2 changes: 1 addition & 1 deletion net/ipv6/ip6_udp_tunnel.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ int udp_sock_create6(struct net *net, struct udp_port_cfg *cfg,
goto error;
}
if (cfg->bind_ifindex) {
err = sock_bindtoindex(sock->sk, cfg->bind_ifindex);
err = sock_bindtoindex(sock->sk, cfg->bind_ifindex, true);
if (err < 0)
goto error;
}
Expand Down
33 changes: 33 additions & 0 deletions tools/testing/selftests/bpf/progs/connect4_prog.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include <linux/in6.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <linux/if.h>
#include <errno.h>

#include <bpf/bpf_helpers.h>
#include <bpf/bpf_endian.h>
Expand All @@ -21,6 +23,10 @@
#define TCP_CA_NAME_MAX 16
#endif

#ifndef IFNAMSIZ
#define IFNAMSIZ 16
#endif

int _version SEC("version") = 1;

__attribute__ ((noinline))
Expand Down Expand Up @@ -75,6 +81,29 @@ static __inline int set_cc(struct bpf_sock_addr *ctx)
return 0;
}

static __inline int bind_to_device(struct bpf_sock_addr *ctx)
{
char veth1[IFNAMSIZ] = "test_sock_addr1";
char veth2[IFNAMSIZ] = "test_sock_addr2";
char missing[IFNAMSIZ] = "nonexistent_dev";
char del_bind[IFNAMSIZ] = "";

if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
&veth1, sizeof(veth1)))
return 1;
if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
&veth2, sizeof(veth2)))
return 1;
if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
&missing, sizeof(missing)) != -ENODEV)
return 1;
if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
&del_bind, sizeof(del_bind)))
return 1;

return 0;
}

SEC("cgroup/connect4")
int connect_v4_prog(struct bpf_sock_addr *ctx)
{
Expand All @@ -88,6 +117,10 @@ int connect_v4_prog(struct bpf_sock_addr *ctx)
tuple.ipv4.daddr = bpf_htonl(DST_REWRITE_IP4);
tuple.ipv4.dport = bpf_htons(DST_REWRITE_PORT4);

/* Bind to device and unbind it. */
if (bind_to_device(ctx))
return 0;

if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM)
return 0;
else if (ctx->type == SOCK_STREAM)
Expand Down

0 comments on commit c48a24f

Please sign in to comment.