1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (c) 2020 Facebook */ |
3 | |
4 | #define _GNU_SOURCE |
5 | #include <netinet/in.h> |
6 | #include <arpa/inet.h> |
7 | #include <unistd.h> |
8 | #include <stdlib.h> |
9 | #include <string.h> |
10 | #include <errno.h> |
11 | #include <sched.h> |
12 | #include <net/if.h> |
13 | #include <linux/compiler.h> |
14 | #include <bpf/libbpf.h> |
15 | |
16 | #include "network_helpers.h" |
17 | #include "test_progs.h" |
18 | #include "test_btf_skc_cls_ingress.skel.h" |
19 | |
20 | static struct test_btf_skc_cls_ingress *skel; |
21 | static struct sockaddr_in6 srv_sa6; |
22 | static __u32 duration; |
23 | |
24 | static int prepare_netns(void) |
25 | { |
26 | LIBBPF_OPTS(bpf_tc_hook, qdisc_lo, .attach_point = BPF_TC_INGRESS); |
27 | LIBBPF_OPTS(bpf_tc_opts, tc_attach, |
28 | .prog_fd = bpf_program__fd(skel->progs.cls_ingress)); |
29 | |
30 | if (CHECK(unshare(CLONE_NEWNET), "create netns" , |
31 | "unshare(CLONE_NEWNET): %s (%d)" , |
32 | strerror(errno), errno)) |
33 | return -1; |
34 | |
35 | if (CHECK(system("ip link set dev lo up" ), |
36 | "ip link set dev lo up" , "failed\n" )) |
37 | return -1; |
38 | |
39 | qdisc_lo.ifindex = if_nametoindex("lo" ); |
40 | if (!ASSERT_OK(bpf_tc_hook_create(&qdisc_lo), "qdisc add dev lo clsact" )) |
41 | return -1; |
42 | |
43 | if (!ASSERT_OK(bpf_tc_attach(&qdisc_lo, &tc_attach), |
44 | "filter add dev lo ingress" )) |
45 | return -1; |
46 | |
47 | /* Ensure 20 bytes options (i.e. in total 40 bytes tcp header) for the |
48 | * bpf_tcp_gen_syncookie() helper. |
49 | */ |
50 | if (write_sysctl("/proc/sys/net/ipv4/tcp_window_scaling" , "1" ) || |
51 | write_sysctl("/proc/sys/net/ipv4/tcp_timestamps" , "1" ) || |
52 | write_sysctl("/proc/sys/net/ipv4/tcp_sack" , "1" )) |
53 | return -1; |
54 | |
55 | return 0; |
56 | } |
57 | |
58 | static void reset_test(void) |
59 | { |
60 | memset(&skel->bss->srv_sa6, 0, sizeof(skel->bss->srv_sa6)); |
61 | skel->bss->listen_tp_sport = 0; |
62 | skel->bss->req_sk_sport = 0; |
63 | skel->bss->recv_cookie = 0; |
64 | skel->bss->gen_cookie = 0; |
65 | skel->bss->linum = 0; |
66 | } |
67 | |
68 | static void print_err_line(void) |
69 | { |
70 | if (skel->bss->linum) |
71 | printf("bpf prog error at line %u\n" , skel->bss->linum); |
72 | } |
73 | |
74 | static void test_conn(void) |
75 | { |
76 | int listen_fd = -1, cli_fd = -1, srv_fd = -1, err; |
77 | socklen_t addrlen = sizeof(srv_sa6); |
78 | int srv_port; |
79 | |
80 | if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies" , "1" )) |
81 | return; |
82 | |
83 | listen_fd = start_server(AF_INET6, SOCK_STREAM, "::1" , 0, 0); |
84 | if (CHECK_FAIL(listen_fd == -1)) |
85 | return; |
86 | |
87 | err = getsockname(listen_fd, (struct sockaddr *)&srv_sa6, &addrlen); |
88 | if (CHECK(err, "getsockname(listen_fd)" , "err:%d errno:%d\n" , err, |
89 | errno)) |
90 | goto done; |
91 | memcpy(&skel->bss->srv_sa6, &srv_sa6, sizeof(srv_sa6)); |
92 | srv_port = ntohs(srv_sa6.sin6_port); |
93 | |
94 | cli_fd = connect_to_fd(listen_fd, 0); |
95 | if (CHECK_FAIL(cli_fd == -1)) |
96 | goto done; |
97 | |
98 | srv_fd = accept(listen_fd, NULL, NULL); |
99 | if (CHECK_FAIL(srv_fd == -1)) |
100 | goto done; |
101 | |
102 | if (CHECK(skel->bss->listen_tp_sport != srv_port || |
103 | skel->bss->req_sk_sport != srv_port, |
104 | "Unexpected sk src port" , |
105 | "listen_tp_sport:%u req_sk_sport:%u expected:%u\n" , |
106 | skel->bss->listen_tp_sport, skel->bss->req_sk_sport, |
107 | srv_port)) |
108 | goto done; |
109 | |
110 | if (CHECK(skel->bss->gen_cookie || skel->bss->recv_cookie, |
111 | "Unexpected syncookie states" , |
112 | "gen_cookie:%u recv_cookie:%u\n" , |
113 | skel->bss->gen_cookie, skel->bss->recv_cookie)) |
114 | goto done; |
115 | |
116 | CHECK(skel->bss->linum, "bpf prog detected error" , "at line %u\n" , |
117 | skel->bss->linum); |
118 | |
119 | done: |
120 | if (listen_fd != -1) |
121 | close(listen_fd); |
122 | if (cli_fd != -1) |
123 | close(cli_fd); |
124 | if (srv_fd != -1) |
125 | close(srv_fd); |
126 | } |
127 | |
128 | static void test_syncookie(void) |
129 | { |
130 | int listen_fd = -1, cli_fd = -1, srv_fd = -1, err; |
131 | socklen_t addrlen = sizeof(srv_sa6); |
132 | int srv_port; |
133 | |
134 | /* Enforce syncookie mode */ |
135 | if (write_sysctl("/proc/sys/net/ipv4/tcp_syncookies" , "2" )) |
136 | return; |
137 | |
138 | listen_fd = start_server(AF_INET6, SOCK_STREAM, "::1" , 0, 0); |
139 | if (CHECK_FAIL(listen_fd == -1)) |
140 | return; |
141 | |
142 | err = getsockname(listen_fd, (struct sockaddr *)&srv_sa6, &addrlen); |
143 | if (CHECK(err, "getsockname(listen_fd)" , "err:%d errno:%d\n" , err, |
144 | errno)) |
145 | goto done; |
146 | memcpy(&skel->bss->srv_sa6, &srv_sa6, sizeof(srv_sa6)); |
147 | srv_port = ntohs(srv_sa6.sin6_port); |
148 | |
149 | cli_fd = connect_to_fd(listen_fd, 0); |
150 | if (CHECK_FAIL(cli_fd == -1)) |
151 | goto done; |
152 | |
153 | srv_fd = accept(listen_fd, NULL, NULL); |
154 | if (CHECK_FAIL(srv_fd == -1)) |
155 | goto done; |
156 | |
157 | if (CHECK(skel->bss->listen_tp_sport != srv_port, |
158 | "Unexpected tp src port" , |
159 | "listen_tp_sport:%u expected:%u\n" , |
160 | skel->bss->listen_tp_sport, srv_port)) |
161 | goto done; |
162 | |
163 | if (CHECK(skel->bss->req_sk_sport, |
164 | "Unexpected req_sk src port" , |
165 | "req_sk_sport:%u expected:0\n" , |
166 | skel->bss->req_sk_sport)) |
167 | goto done; |
168 | |
169 | if (CHECK(!skel->bss->gen_cookie || |
170 | skel->bss->gen_cookie != skel->bss->recv_cookie, |
171 | "Unexpected syncookie states" , |
172 | "gen_cookie:%u recv_cookie:%u\n" , |
173 | skel->bss->gen_cookie, skel->bss->recv_cookie)) |
174 | goto done; |
175 | |
176 | CHECK(skel->bss->linum, "bpf prog detected error" , "at line %u\n" , |
177 | skel->bss->linum); |
178 | |
179 | done: |
180 | if (listen_fd != -1) |
181 | close(listen_fd); |
182 | if (cli_fd != -1) |
183 | close(cli_fd); |
184 | if (srv_fd != -1) |
185 | close(srv_fd); |
186 | } |
187 | |
188 | struct test { |
189 | const char *desc; |
190 | void (*run)(void); |
191 | }; |
192 | |
193 | #define DEF_TEST(name) { #name, test_##name } |
194 | static struct test tests[] = { |
195 | DEF_TEST(conn), |
196 | DEF_TEST(syncookie), |
197 | }; |
198 | |
199 | void test_btf_skc_cls_ingress(void) |
200 | { |
201 | int i; |
202 | |
203 | skel = test_btf_skc_cls_ingress__open_and_load(); |
204 | if (CHECK(!skel, "test_btf_skc_cls_ingress__open_and_load" , "failed\n" )) |
205 | return; |
206 | |
207 | for (i = 0; i < ARRAY_SIZE(tests); i++) { |
208 | if (!test__start_subtest(tests[i].desc)) |
209 | continue; |
210 | |
211 | if (prepare_netns()) |
212 | break; |
213 | |
214 | tests[i].run(); |
215 | |
216 | print_err_line(); |
217 | reset_test(); |
218 | } |
219 | |
220 | test_btf_skc_cls_ingress__destroy(skel); |
221 | } |
222 | |