Skip to content

Commit c48a24f

Browse files
author
Alexei Starovoitov
committed
Merge branch 'bpf_setsockopt-SO_BINDTODEVICE'
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 <[email protected]>
2 parents bb2359f + 9c441fe commit c48a24f

File tree

6 files changed

+69
-8
lines changed

6 files changed

+69
-8
lines changed

include/net/sock.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2690,7 +2690,7 @@ static inline bool sk_dev_equal_l3scope(struct sock *sk, int dif)
26902690

26912691
void sock_def_readable(struct sock *sk);
26922692

2693-
int sock_bindtoindex(struct sock *sk, int ifindex);
2693+
int sock_bindtoindex(struct sock *sk, int ifindex, bool lock_sk);
26942694
void sock_enable_timestamps(struct sock *sk);
26952695
void sock_no_linger(struct sock *sk);
26962696
void sock_set_keepalive(struct sock *sk);

net/core/filter.c

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4248,6 +4248,9 @@ static const struct bpf_func_proto bpf_get_socket_uid_proto = {
42484248
static int _bpf_setsockopt(struct sock *sk, int level, int optname,
42494249
char *optval, int optlen, u32 flags)
42504250
{
4251+
char devname[IFNAMSIZ];
4252+
struct net *net;
4253+
int ifindex;
42514254
int ret = 0;
42524255
int val;
42534256

@@ -4257,7 +4260,7 @@ static int _bpf_setsockopt(struct sock *sk, int level, int optname,
42574260
sock_owned_by_me(sk);
42584261

42594262
if (level == SOL_SOCKET) {
4260-
if (optlen != sizeof(int))
4263+
if (optlen != sizeof(int) && optname != SO_BINDTODEVICE)
42614264
return -EINVAL;
42624265
val = *((int *)optval);
42634266

@@ -4298,6 +4301,29 @@ static int _bpf_setsockopt(struct sock *sk, int level, int optname,
42984301
sk_dst_reset(sk);
42994302
}
43004303
break;
4304+
case SO_BINDTODEVICE:
4305+
ret = -ENOPROTOOPT;
4306+
#ifdef CONFIG_NETDEVICES
4307+
optlen = min_t(long, optlen, IFNAMSIZ - 1);
4308+
strncpy(devname, optval, optlen);
4309+
devname[optlen] = 0;
4310+
4311+
ifindex = 0;
4312+
if (devname[0] != '\0') {
4313+
struct net_device *dev;
4314+
4315+
ret = -ENODEV;
4316+
4317+
net = sock_net(sk);
4318+
dev = dev_get_by_name(net, devname);
4319+
if (!dev)
4320+
break;
4321+
ifindex = dev->ifindex;
4322+
dev_put(dev);
4323+
}
4324+
ret = sock_bindtoindex(sk, ifindex, false);
4325+
#endif
4326+
break;
43014327
default:
43024328
ret = -EINVAL;
43034329
}

net/core/sock.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -594,13 +594,15 @@ static int sock_bindtoindex_locked(struct sock *sk, int ifindex)
594594
return ret;
595595
}
596596

597-
int sock_bindtoindex(struct sock *sk, int ifindex)
597+
int sock_bindtoindex(struct sock *sk, int ifindex, bool lock_sk)
598598
{
599599
int ret;
600600

601-
lock_sock(sk);
601+
if (lock_sk)
602+
lock_sock(sk);
602603
ret = sock_bindtoindex_locked(sk, ifindex);
603-
release_sock(sk);
604+
if (lock_sk)
605+
release_sock(sk);
604606

605607
return ret;
606608
}
@@ -646,7 +648,7 @@ static int sock_setbindtodevice(struct sock *sk, char __user *optval,
646648
goto out;
647649
}
648650

649-
return sock_bindtoindex(sk, index);
651+
return sock_bindtoindex(sk, index, true);
650652
out:
651653
#endif
652654

net/ipv4/udp_tunnel.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ int udp_sock_create4(struct net *net, struct udp_port_cfg *cfg,
2222
goto error;
2323

2424
if (cfg->bind_ifindex) {
25-
err = sock_bindtoindex(sock->sk, cfg->bind_ifindex);
25+
err = sock_bindtoindex(sock->sk, cfg->bind_ifindex, true);
2626
if (err < 0)
2727
goto error;
2828
}

net/ipv6/ip6_udp_tunnel.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ int udp_sock_create6(struct net *net, struct udp_port_cfg *cfg,
3030
goto error;
3131
}
3232
if (cfg->bind_ifindex) {
33-
err = sock_bindtoindex(sock->sk, cfg->bind_ifindex);
33+
err = sock_bindtoindex(sock->sk, cfg->bind_ifindex, true);
3434
if (err < 0)
3535
goto error;
3636
}

tools/testing/selftests/bpf/progs/connect4_prog.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include <linux/in6.h>
1010
#include <sys/socket.h>
1111
#include <netinet/tcp.h>
12+
#include <linux/if.h>
13+
#include <errno.h>
1214

1315
#include <bpf/bpf_helpers.h>
1416
#include <bpf/bpf_endian.h>
@@ -21,6 +23,10 @@
2123
#define TCP_CA_NAME_MAX 16
2224
#endif
2325

26+
#ifndef IFNAMSIZ
27+
#define IFNAMSIZ 16
28+
#endif
29+
2430
int _version SEC("version") = 1;
2531

2632
__attribute__ ((noinline))
@@ -75,6 +81,29 @@ static __inline int set_cc(struct bpf_sock_addr *ctx)
7581
return 0;
7682
}
7783

84+
static __inline int bind_to_device(struct bpf_sock_addr *ctx)
85+
{
86+
char veth1[IFNAMSIZ] = "test_sock_addr1";
87+
char veth2[IFNAMSIZ] = "test_sock_addr2";
88+
char missing[IFNAMSIZ] = "nonexistent_dev";
89+
char del_bind[IFNAMSIZ] = "";
90+
91+
if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
92+
&veth1, sizeof(veth1)))
93+
return 1;
94+
if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
95+
&veth2, sizeof(veth2)))
96+
return 1;
97+
if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
98+
&missing, sizeof(missing)) != -ENODEV)
99+
return 1;
100+
if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
101+
&del_bind, sizeof(del_bind)))
102+
return 1;
103+
104+
return 0;
105+
}
106+
78107
SEC("cgroup/connect4")
79108
int connect_v4_prog(struct bpf_sock_addr *ctx)
80109
{
@@ -88,6 +117,10 @@ int connect_v4_prog(struct bpf_sock_addr *ctx)
88117
tuple.ipv4.daddr = bpf_htonl(DST_REWRITE_IP4);
89118
tuple.ipv4.dport = bpf_htons(DST_REWRITE_PORT4);
90119

120+
/* Bind to device and unbind it. */
121+
if (bind_to_device(ctx))
122+
return 0;
123+
91124
if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM)
92125
return 0;
93126
else if (ctx->type == SOCK_STREAM)

0 commit comments

Comments
 (0)