1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ |
3 | #include <linux/bpf.h> |
4 | #include <bpf/bpf_endian.h> |
5 | #include <bpf/bpf_helpers.h> |
6 | |
7 | #include <linux/if_ether.h> |
8 | #include <linux/in.h> |
9 | #include <linux/in6.h> |
10 | #include <linux/ipv6.h> |
11 | #include <linux/tcp.h> |
12 | |
13 | #include <sys/types.h> |
14 | #include <sys/socket.h> |
15 | |
16 | #include "cgroup_tcp_skb.h" |
17 | |
18 | char _license[] SEC("license" ) = "GPL" ; |
19 | |
20 | __u16 g_sock_port = 0; |
21 | __u32 g_sock_state = 0; |
22 | int g_unexpected = 0; |
23 | __u32 g_packet_count = 0; |
24 | |
25 | int needed_tcp_pkt(struct __sk_buff *skb, struct tcphdr *tcph) |
26 | { |
27 | struct ipv6hdr ip6h; |
28 | |
29 | if (skb->protocol != bpf_htons(ETH_P_IPV6)) |
30 | return 0; |
31 | if (bpf_skb_load_bytes(skb, 0, &ip6h, sizeof(ip6h))) |
32 | return 0; |
33 | |
34 | if (ip6h.nexthdr != IPPROTO_TCP) |
35 | return 0; |
36 | |
37 | if (bpf_skb_load_bytes(skb, sizeof(ip6h), tcph, sizeof(*tcph))) |
38 | return 0; |
39 | |
40 | if (tcph->source != bpf_htons(g_sock_port) && |
41 | tcph->dest != bpf_htons(g_sock_port)) |
42 | return 0; |
43 | |
44 | return 1; |
45 | } |
46 | |
47 | /* Run accept() on a socket in the cgroup to receive a new connection. */ |
48 | static int egress_accept(struct tcphdr *tcph) |
49 | { |
50 | if (g_sock_state == SYN_RECV_SENDING_SYN_ACK) { |
51 | if (tcph->fin || !tcph->syn || !tcph->ack) |
52 | g_unexpected++; |
53 | else |
54 | g_sock_state = SYN_RECV; |
55 | return 1; |
56 | } |
57 | |
58 | return 0; |
59 | } |
60 | |
61 | static int ingress_accept(struct tcphdr *tcph) |
62 | { |
63 | switch (g_sock_state) { |
64 | case INIT: |
65 | if (!tcph->syn || tcph->fin || tcph->ack) |
66 | g_unexpected++; |
67 | else |
68 | g_sock_state = SYN_RECV_SENDING_SYN_ACK; |
69 | break; |
70 | case SYN_RECV: |
71 | if (tcph->fin || tcph->syn || !tcph->ack) |
72 | g_unexpected++; |
73 | else |
74 | g_sock_state = ESTABLISHED; |
75 | break; |
76 | default: |
77 | return 0; |
78 | } |
79 | |
80 | return 1; |
81 | } |
82 | |
83 | /* Run connect() on a socket in the cgroup to start a new connection. */ |
84 | static int egress_connect(struct tcphdr *tcph) |
85 | { |
86 | if (g_sock_state == INIT) { |
87 | if (!tcph->syn || tcph->fin || tcph->ack) |
88 | g_unexpected++; |
89 | else |
90 | g_sock_state = SYN_SENT; |
91 | return 1; |
92 | } |
93 | |
94 | return 0; |
95 | } |
96 | |
97 | static int ingress_connect(struct tcphdr *tcph) |
98 | { |
99 | if (g_sock_state == SYN_SENT) { |
100 | if (tcph->fin || !tcph->syn || !tcph->ack) |
101 | g_unexpected++; |
102 | else |
103 | g_sock_state = ESTABLISHED; |
104 | return 1; |
105 | } |
106 | |
107 | return 0; |
108 | } |
109 | |
110 | /* The connection is closed by the peer outside the cgroup. */ |
111 | static int egress_close_remote(struct tcphdr *tcph) |
112 | { |
113 | switch (g_sock_state) { |
114 | case ESTABLISHED: |
115 | break; |
116 | case CLOSE_WAIT_SENDING_ACK: |
117 | if (tcph->fin || tcph->syn || !tcph->ack) |
118 | g_unexpected++; |
119 | else |
120 | g_sock_state = CLOSE_WAIT; |
121 | break; |
122 | case CLOSE_WAIT: |
123 | if (!tcph->fin) |
124 | g_unexpected++; |
125 | else |
126 | g_sock_state = LAST_ACK; |
127 | break; |
128 | default: |
129 | return 0; |
130 | } |
131 | |
132 | return 1; |
133 | } |
134 | |
135 | static int ingress_close_remote(struct tcphdr *tcph) |
136 | { |
137 | switch (g_sock_state) { |
138 | case ESTABLISHED: |
139 | if (tcph->fin) |
140 | g_sock_state = CLOSE_WAIT_SENDING_ACK; |
141 | break; |
142 | case LAST_ACK: |
143 | if (tcph->fin || tcph->syn || !tcph->ack) |
144 | g_unexpected++; |
145 | else |
146 | g_sock_state = CLOSED; |
147 | break; |
148 | default: |
149 | return 0; |
150 | } |
151 | |
152 | return 1; |
153 | } |
154 | |
155 | /* The connection is closed by the endpoint inside the cgroup. */ |
156 | static int egress_close_local(struct tcphdr *tcph) |
157 | { |
158 | switch (g_sock_state) { |
159 | case ESTABLISHED: |
160 | if (tcph->fin) |
161 | g_sock_state = FIN_WAIT1; |
162 | break; |
163 | case TIME_WAIT_SENDING_ACK: |
164 | if (tcph->fin || tcph->syn || !tcph->ack) |
165 | g_unexpected++; |
166 | else |
167 | g_sock_state = TIME_WAIT; |
168 | break; |
169 | default: |
170 | return 0; |
171 | } |
172 | |
173 | return 1; |
174 | } |
175 | |
176 | static int ingress_close_local(struct tcphdr *tcph) |
177 | { |
178 | switch (g_sock_state) { |
179 | case ESTABLISHED: |
180 | break; |
181 | case FIN_WAIT1: |
182 | if (tcph->fin || tcph->syn || !tcph->ack) |
183 | g_unexpected++; |
184 | else |
185 | g_sock_state = FIN_WAIT2; |
186 | break; |
187 | case FIN_WAIT2: |
188 | if (!tcph->fin || tcph->syn || !tcph->ack) |
189 | g_unexpected++; |
190 | else |
191 | g_sock_state = TIME_WAIT_SENDING_ACK; |
192 | break; |
193 | default: |
194 | return 0; |
195 | } |
196 | |
197 | return 1; |
198 | } |
199 | |
200 | /* Check the types of outgoing packets of a server socket to make sure they |
201 | * are consistent with the state of the server socket. |
202 | * |
203 | * The connection is closed by the client side. |
204 | */ |
205 | SEC("cgroup_skb/egress" ) |
206 | int server_egress(struct __sk_buff *skb) |
207 | { |
208 | struct tcphdr tcph; |
209 | |
210 | if (!needed_tcp_pkt(skb, tcph: &tcph)) |
211 | return 1; |
212 | |
213 | g_packet_count++; |
214 | |
215 | /* Egress of the server socket. */ |
216 | if (egress_accept(tcph: &tcph) || egress_close_remote(tcph: &tcph)) |
217 | return 1; |
218 | |
219 | g_unexpected++; |
220 | return 1; |
221 | } |
222 | |
223 | /* Check the types of incoming packets of a server socket to make sure they |
224 | * are consistent with the state of the server socket. |
225 | * |
226 | * The connection is closed by the client side. |
227 | */ |
228 | SEC("cgroup_skb/ingress" ) |
229 | int server_ingress(struct __sk_buff *skb) |
230 | { |
231 | struct tcphdr tcph; |
232 | |
233 | if (!needed_tcp_pkt(skb, tcph: &tcph)) |
234 | return 1; |
235 | |
236 | g_packet_count++; |
237 | |
238 | /* Ingress of the server socket. */ |
239 | if (ingress_accept(tcph: &tcph) || ingress_close_remote(tcph: &tcph)) |
240 | return 1; |
241 | |
242 | g_unexpected++; |
243 | return 1; |
244 | } |
245 | |
246 | /* Check the types of outgoing packets of a server socket to make sure they |
247 | * are consistent with the state of the server socket. |
248 | * |
249 | * The connection is closed by the server side. |
250 | */ |
251 | SEC("cgroup_skb/egress" ) |
252 | int server_egress_srv(struct __sk_buff *skb) |
253 | { |
254 | struct tcphdr tcph; |
255 | |
256 | if (!needed_tcp_pkt(skb, tcph: &tcph)) |
257 | return 1; |
258 | |
259 | g_packet_count++; |
260 | |
261 | /* Egress of the server socket. */ |
262 | if (egress_accept(tcph: &tcph) || egress_close_local(tcph: &tcph)) |
263 | return 1; |
264 | |
265 | g_unexpected++; |
266 | return 1; |
267 | } |
268 | |
269 | /* Check the types of incoming packets of a server socket to make sure they |
270 | * are consistent with the state of the server socket. |
271 | * |
272 | * The connection is closed by the server side. |
273 | */ |
274 | SEC("cgroup_skb/ingress" ) |
275 | int server_ingress_srv(struct __sk_buff *skb) |
276 | { |
277 | struct tcphdr tcph; |
278 | |
279 | if (!needed_tcp_pkt(skb, tcph: &tcph)) |
280 | return 1; |
281 | |
282 | g_packet_count++; |
283 | |
284 | /* Ingress of the server socket. */ |
285 | if (ingress_accept(tcph: &tcph) || ingress_close_local(tcph: &tcph)) |
286 | return 1; |
287 | |
288 | g_unexpected++; |
289 | return 1; |
290 | } |
291 | |
292 | /* Check the types of outgoing packets of a client socket to make sure they |
293 | * are consistent with the state of the client socket. |
294 | * |
295 | * The connection is closed by the server side. |
296 | */ |
297 | SEC("cgroup_skb/egress" ) |
298 | int client_egress_srv(struct __sk_buff *skb) |
299 | { |
300 | struct tcphdr tcph; |
301 | |
302 | if (!needed_tcp_pkt(skb, tcph: &tcph)) |
303 | return 1; |
304 | |
305 | g_packet_count++; |
306 | |
307 | /* Egress of the server socket. */ |
308 | if (egress_connect(tcph: &tcph) || egress_close_remote(tcph: &tcph)) |
309 | return 1; |
310 | |
311 | g_unexpected++; |
312 | return 1; |
313 | } |
314 | |
315 | /* Check the types of incoming packets of a client socket to make sure they |
316 | * are consistent with the state of the client socket. |
317 | * |
318 | * The connection is closed by the server side. |
319 | */ |
320 | SEC("cgroup_skb/ingress" ) |
321 | int client_ingress_srv(struct __sk_buff *skb) |
322 | { |
323 | struct tcphdr tcph; |
324 | |
325 | if (!needed_tcp_pkt(skb, tcph: &tcph)) |
326 | return 1; |
327 | |
328 | g_packet_count++; |
329 | |
330 | /* Ingress of the server socket. */ |
331 | if (ingress_connect(tcph: &tcph) || ingress_close_remote(tcph: &tcph)) |
332 | return 1; |
333 | |
334 | g_unexpected++; |
335 | return 1; |
336 | } |
337 | |
338 | /* Check the types of outgoing packets of a client socket to make sure they |
339 | * are consistent with the state of the client socket. |
340 | * |
341 | * The connection is closed by the client side. |
342 | */ |
343 | SEC("cgroup_skb/egress" ) |
344 | int client_egress(struct __sk_buff *skb) |
345 | { |
346 | struct tcphdr tcph; |
347 | |
348 | if (!needed_tcp_pkt(skb, tcph: &tcph)) |
349 | return 1; |
350 | |
351 | g_packet_count++; |
352 | |
353 | /* Egress of the server socket. */ |
354 | if (egress_connect(tcph: &tcph) || egress_close_local(tcph: &tcph)) |
355 | return 1; |
356 | |
357 | g_unexpected++; |
358 | return 1; |
359 | } |
360 | |
361 | /* Check the types of incoming packets of a client socket to make sure they |
362 | * are consistent with the state of the client socket. |
363 | * |
364 | * The connection is closed by the client side. |
365 | */ |
366 | SEC("cgroup_skb/ingress" ) |
367 | int client_ingress(struct __sk_buff *skb) |
368 | { |
369 | struct tcphdr tcph; |
370 | |
371 | if (!needed_tcp_pkt(skb, tcph: &tcph)) |
372 | return 1; |
373 | |
374 | g_packet_count++; |
375 | |
376 | /* Ingress of the server socket. */ |
377 | if (ingress_connect(tcph: &tcph) || ingress_close_local(tcph: &tcph)) |
378 | return 1; |
379 | |
380 | g_unexpected++; |
381 | return 1; |
382 | } |
383 | |