1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2018 Facebook
3 // Copyright (c) 2019 Cloudflare
4
5 #include <limits.h>
6 #include <string.h>
7 #include <stdlib.h>
8 #include <unistd.h>
9
10 #include <arpa/inet.h>
11 #include <netinet/in.h>
12 #include <sys/types.h>
13 #include <sys/socket.h>
14
15 #include <bpf/bpf.h>
16 #include <bpf/libbpf.h>
17
18 #include "cgroup_helpers.h"
19 #include "network_helpers.h"
20
get_map_fd_by_prog_id(int prog_id,bool * xdp)21 static int get_map_fd_by_prog_id(int prog_id, bool *xdp)
22 {
23 struct bpf_prog_info info = {};
24 __u32 info_len = sizeof(info);
25 __u32 map_ids[1];
26 int prog_fd = -1;
27 int map_fd = -1;
28
29 prog_fd = bpf_prog_get_fd_by_id(prog_id);
30 if (prog_fd < 0) {
31 log_err("Failed to get fd by prog id %d", prog_id);
32 goto err;
33 }
34
35 info.nr_map_ids = 1;
36 info.map_ids = (__u64)(unsigned long)map_ids;
37
38 if (bpf_prog_get_info_by_fd(prog_fd, &info, &info_len)) {
39 log_err("Failed to get info by prog fd %d", prog_fd);
40 goto err;
41 }
42
43 if (!info.nr_map_ids) {
44 log_err("No maps found for prog fd %d", prog_fd);
45 goto err;
46 }
47
48 *xdp = info.type == BPF_PROG_TYPE_XDP;
49
50 map_fd = bpf_map_get_fd_by_id(map_ids[0]);
51 if (map_fd < 0)
52 log_err("Failed to get fd by map id %d", map_ids[0]);
53 err:
54 if (prog_fd >= 0)
55 close(prog_fd);
56 return map_fd;
57 }
58
run_test(int server_fd,int results_fd,bool xdp)59 static int run_test(int server_fd, int results_fd, bool xdp)
60 {
61 int client = -1, srv_client = -1;
62 int ret = 0;
63 __u32 key = 0;
64 __u32 key_gen = 1;
65 __u32 key_mss = 2;
66 __u32 value = 0;
67 __u32 value_gen = 0;
68 __u32 value_mss = 0;
69
70 if (bpf_map_update_elem(results_fd, &key, &value, 0) < 0) {
71 log_err("Can't clear results");
72 goto err;
73 }
74
75 if (bpf_map_update_elem(results_fd, &key_gen, &value_gen, 0) < 0) {
76 log_err("Can't clear results");
77 goto err;
78 }
79
80 if (bpf_map_update_elem(results_fd, &key_mss, &value_mss, 0) < 0) {
81 log_err("Can't clear results");
82 goto err;
83 }
84
85 client = connect_to_fd(server_fd, 0);
86 if (client == -1)
87 goto err;
88
89 srv_client = accept(server_fd, NULL, 0);
90 if (srv_client == -1) {
91 log_err("Can't accept connection");
92 goto err;
93 }
94
95 if (bpf_map_lookup_elem(results_fd, &key, &value) < 0) {
96 log_err("Can't lookup result");
97 goto err;
98 }
99
100 if (value == 0) {
101 log_err("Didn't match syncookie: %u", value);
102 goto err;
103 }
104
105 if (bpf_map_lookup_elem(results_fd, &key_gen, &value_gen) < 0) {
106 log_err("Can't lookup result");
107 goto err;
108 }
109
110 if (xdp && value_gen == 0) {
111 // SYN packets do not get passed through generic XDP, skip the
112 // rest of the test.
113 printf("Skipping XDP cookie check\n");
114 goto out;
115 }
116
117 if (bpf_map_lookup_elem(results_fd, &key_mss, &value_mss) < 0) {
118 log_err("Can't lookup result");
119 goto err;
120 }
121
122 if (value != value_gen) {
123 log_err("BPF generated cookie does not match kernel one");
124 goto err;
125 }
126
127 if (value_mss < 536 || value_mss > USHRT_MAX) {
128 log_err("Unexpected MSS retrieved");
129 goto err;
130 }
131
132 goto out;
133
134 err:
135 ret = 1;
136 out:
137 close(client);
138 close(srv_client);
139 return ret;
140 }
141
v6only_true(int fd,void * opts)142 static int v6only_true(int fd, void *opts)
143 {
144 int mode = true;
145
146 return setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &mode, sizeof(mode));
147 }
148
v6only_false(int fd,void * opts)149 static int v6only_false(int fd, void *opts)
150 {
151 int mode = false;
152
153 return setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &mode, sizeof(mode));
154 }
155
main(int argc,char ** argv)156 int main(int argc, char **argv)
157 {
158 struct network_helper_opts opts = { 0 };
159 int server = -1;
160 int server_v6 = -1;
161 int server_dual = -1;
162 int results = -1;
163 int err = 0;
164 bool xdp;
165
166 if (argc < 2) {
167 fprintf(stderr, "Usage: %s prog_id\n", argv[0]);
168 exit(1);
169 }
170
171 /* Use libbpf 1.0 API mode */
172 libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
173
174 results = get_map_fd_by_prog_id(atoi(argv[1]), &xdp);
175 if (results < 0) {
176 log_err("Can't get map");
177 goto err;
178 }
179
180 server = start_server_str(AF_INET, SOCK_STREAM, "127.0.0.1", 0, NULL);
181 if (server == -1)
182 goto err;
183
184 opts.post_socket_cb = v6only_true;
185 server_v6 = start_server_str(AF_INET6, SOCK_STREAM, "::1", 0, &opts);
186 if (server_v6 == -1)
187 goto err;
188
189 opts.post_socket_cb = v6only_false;
190 server_dual = start_server_str(AF_INET6, SOCK_STREAM, "::0", 0, &opts);
191 if (server_dual == -1)
192 goto err;
193
194 if (run_test(server, results, xdp))
195 goto err;
196
197 if (run_test(server_v6, results, xdp))
198 goto err;
199
200 if (run_test(server_dual, results, xdp))
201 goto err;
202
203 printf("ok\n");
204 goto out;
205 err:
206 err = 1;
207 out:
208 close(server);
209 close(server_v6);
210 close(server_dual);
211 close(results);
212 return err;
213 }
214