Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2// Copyright (c) 2018 Facebook
3
4#include <string.h>
5
6#include <linux/stddef.h>
7#include <linux/bpf.h>
8#include <linux/in.h>
9#include <linux/in6.h>
10#include <linux/tcp.h>
11#include <linux/if.h>
12#include <errno.h>
13
14#include <bpf/bpf_helpers.h>
15#include <bpf/bpf_endian.h>
16
17#define SRC_REWRITE_IP4 0x7f000004U
18#define DST_REWRITE_IP4 0x7f000001U
19#define DST_REWRITE_PORT4 4444
20
21#ifndef TCP_CA_NAME_MAX
22#define TCP_CA_NAME_MAX 16
23#endif
24
25#ifndef TCP_NOTSENT_LOWAT
26#define TCP_NOTSENT_LOWAT 25
27#endif
28
29#ifndef IFNAMSIZ
30#define IFNAMSIZ 16
31#endif
32
33#ifndef SOL_TCP
34#define SOL_TCP 6
35#endif
36
37const char reno[] = "reno";
38const char cubic[] = "cubic";
39
40__attribute__ ((noinline)) __weak
41int do_bind(struct bpf_sock_addr *ctx)
42{
43 struct sockaddr_in sa = {};
44
45 sa.sin_family = AF_INET;
46 sa.sin_port = bpf_htons(0);
47 sa.sin_addr.s_addr = bpf_htonl(SRC_REWRITE_IP4);
48
49 if (bpf_bind(ctx, (struct sockaddr *)&sa, sizeof(sa)) != 0)
50 return 0;
51
52 return 1;
53}
54
55static __inline int verify_cc(struct bpf_sock_addr *ctx,
56 const char expected[])
57{
58 char buf[TCP_CA_NAME_MAX];
59
60 if (bpf_getsockopt(ctx, SOL_TCP, TCP_CONGESTION, &buf, sizeof(buf)))
61 return 1;
62
63 if (bpf_strncmp(buf, TCP_CA_NAME_MAX, expected))
64 return 1;
65
66 return 0;
67}
68
69static __inline int set_cc(struct bpf_sock_addr *ctx)
70{
71 if (bpf_setsockopt(ctx, SOL_TCP, TCP_CONGESTION, (void *)reno, sizeof(reno)))
72 return 1;
73 if (verify_cc(ctx, reno))
74 return 1;
75
76 if (bpf_setsockopt(ctx, SOL_TCP, TCP_CONGESTION, (void *)cubic, sizeof(cubic)))
77 return 1;
78 if (verify_cc(ctx, cubic))
79 return 1;
80
81 return 0;
82}
83
84static __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
107static __inline int set_keepalive(struct bpf_sock_addr *ctx)
108{
109 int zero = 0, one = 1;
110
111 if (bpf_setsockopt(ctx, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one)))
112 return 1;
113 if (ctx->type == SOCK_STREAM) {
114 if (bpf_setsockopt(ctx, SOL_TCP, TCP_KEEPIDLE, &one, sizeof(one)))
115 return 1;
116 if (bpf_setsockopt(ctx, SOL_TCP, TCP_KEEPINTVL, &one, sizeof(one)))
117 return 1;
118 if (bpf_setsockopt(ctx, SOL_TCP, TCP_KEEPCNT, &one, sizeof(one)))
119 return 1;
120 if (bpf_setsockopt(ctx, SOL_TCP, TCP_SYNCNT, &one, sizeof(one)))
121 return 1;
122 if (bpf_setsockopt(ctx, SOL_TCP, TCP_USER_TIMEOUT, &one, sizeof(one)))
123 return 1;
124 }
125 if (bpf_setsockopt(ctx, SOL_SOCKET, SO_KEEPALIVE, &zero, sizeof(zero)))
126 return 1;
127
128 return 0;
129}
130
131static __inline int set_notsent_lowat(struct bpf_sock_addr *ctx)
132{
133 int lowat = 65535;
134
135 if (ctx->type == SOCK_STREAM) {
136 if (bpf_setsockopt(ctx, SOL_TCP, TCP_NOTSENT_LOWAT, &lowat, sizeof(lowat)))
137 return 1;
138 }
139
140 return 0;
141}
142
143SEC("cgroup/connect4")
144int connect_v4_prog(struct bpf_sock_addr *ctx)
145{
146 struct bpf_sock_tuple tuple = {};
147 struct bpf_sock *sk;
148
149 /* Verify that new destination is available. */
150 memset(&tuple.ipv4.saddr, 0, sizeof(tuple.ipv4.saddr));
151 memset(&tuple.ipv4.sport, 0, sizeof(tuple.ipv4.sport));
152
153 tuple.ipv4.daddr = bpf_htonl(DST_REWRITE_IP4);
154 tuple.ipv4.dport = bpf_htons(DST_REWRITE_PORT4);
155
156 /* Bind to device and unbind it. */
157 if (bind_to_device(ctx))
158 return 0;
159
160 if (set_keepalive(ctx))
161 return 0;
162
163 if (set_notsent_lowat(ctx))
164 return 0;
165
166 if (ctx->type != SOCK_STREAM && ctx->type != SOCK_DGRAM)
167 return 0;
168 else if (ctx->type == SOCK_STREAM)
169 sk = bpf_sk_lookup_tcp(ctx, &tuple, sizeof(tuple.ipv4),
170 BPF_F_CURRENT_NETNS, 0);
171 else
172 sk = bpf_sk_lookup_udp(ctx, &tuple, sizeof(tuple.ipv4),
173 BPF_F_CURRENT_NETNS, 0);
174
175 if (!sk)
176 return 0;
177
178 if (sk->src_ip4 != tuple.ipv4.daddr ||
179 sk->src_port != DST_REWRITE_PORT4) {
180 bpf_sk_release(sk);
181 return 0;
182 }
183
184 bpf_sk_release(sk);
185
186 /* Rewrite congestion control. */
187 if (ctx->type == SOCK_STREAM && set_cc(ctx))
188 return 0;
189
190 /* Rewrite destination. */
191 ctx->user_ip4 = bpf_htonl(DST_REWRITE_IP4);
192 ctx->user_port = bpf_htons(DST_REWRITE_PORT4);
193
194 return do_bind(ctx) ? 1 : 0;
195}
196
197SEC("cgroup/connect4")
198int connect_v4_deny_prog(struct bpf_sock_addr *ctx)
199{
200 return 0;
201}
202
203char _license[] SEC("license") = "GPL";