1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (c) 2020 Cloudflare 3 #include <error.h> 4 5 #include "test_progs.h" 6 #include "test_skmsg_load_helpers.skel.h" 7 #include "test_sockmap_update.skel.h" 8 #include "test_sockmap_invalid_update.skel.h" 9 #include "bpf_iter_sockmap.skel.h" 10 11 #include "progs/bpf_iter_sockmap.h" 12 13 #define TCP_REPAIR 19 /* TCP sock is under repair right now */ 14 15 #define TCP_REPAIR_ON 1 16 #define TCP_REPAIR_OFF_NO_WP -1 /* Turn off without window probes */ 17 18 static int connected_socket_v4(void) 19 { 20 struct sockaddr_in addr = { 21 .sin_family = AF_INET, 22 .sin_port = htons(80), 23 .sin_addr = { inet_addr("127.0.0.1") }, 24 }; 25 socklen_t len = sizeof(addr); 26 int s, repair, err; 27 28 s = socket(AF_INET, SOCK_STREAM, 0); 29 if (CHECK_FAIL(s == -1)) 30 goto error; 31 32 repair = TCP_REPAIR_ON; 33 err = setsockopt(s, SOL_TCP, TCP_REPAIR, &repair, sizeof(repair)); 34 if (CHECK_FAIL(err)) 35 goto error; 36 37 err = connect(s, (struct sockaddr *)&addr, len); 38 if (CHECK_FAIL(err)) 39 goto error; 40 41 repair = TCP_REPAIR_OFF_NO_WP; 42 err = setsockopt(s, SOL_TCP, TCP_REPAIR, &repair, sizeof(repair)); 43 if (CHECK_FAIL(err)) 44 goto error; 45 46 return s; 47 error: 48 perror(__func__); 49 close(s); 50 return -1; 51 } 52 53 /* Create a map, populate it with one socket, and free the map. */ 54 static void test_sockmap_create_update_free(enum bpf_map_type map_type) 55 { 56 const int zero = 0; 57 int s, map, err; 58 59 s = connected_socket_v4(); 60 if (CHECK_FAIL(s == -1)) 61 return; 62 63 map = bpf_create_map(map_type, sizeof(int), sizeof(int), 1, 0); 64 if (CHECK_FAIL(map == -1)) { 65 perror("bpf_create_map"); 66 goto out; 67 } 68 69 err = bpf_map_update_elem(map, &zero, &s, BPF_NOEXIST); 70 if (CHECK_FAIL(err)) { 71 perror("bpf_map_update"); 72 goto out; 73 } 74 75 out: 76 close(map); 77 close(s); 78 } 79 80 static void test_skmsg_helpers(enum bpf_map_type map_type) 81 { 82 struct test_skmsg_load_helpers *skel; 83 int err, map, verdict; 84 85 skel = test_skmsg_load_helpers__open_and_load(); 86 if (CHECK_FAIL(!skel)) { 87 perror("test_skmsg_load_helpers__open_and_load"); 88 return; 89 } 90 91 verdict = bpf_program__fd(skel->progs.prog_msg_verdict); 92 map = bpf_map__fd(skel->maps.sock_map); 93 94 err = bpf_prog_attach(verdict, map, BPF_SK_MSG_VERDICT, 0); 95 if (CHECK_FAIL(err)) { 96 perror("bpf_prog_attach"); 97 goto out; 98 } 99 100 err = bpf_prog_detach2(verdict, map, BPF_SK_MSG_VERDICT); 101 if (CHECK_FAIL(err)) { 102 perror("bpf_prog_detach2"); 103 goto out; 104 } 105 out: 106 test_skmsg_load_helpers__destroy(skel); 107 } 108 109 static void test_sockmap_update(enum bpf_map_type map_type) 110 { 111 struct bpf_prog_test_run_attr tattr; 112 int err, prog, src, dst, duration = 0; 113 struct test_sockmap_update *skel; 114 __u64 src_cookie, dst_cookie; 115 const __u32 zero = 0; 116 char dummy[14] = {0}; 117 __s64 sk; 118 119 sk = connected_socket_v4(); 120 if (CHECK(sk == -1, "connected_socket_v4", "cannot connect\n")) 121 return; 122 123 skel = test_sockmap_update__open_and_load(); 124 if (CHECK(!skel, "open_and_load", "cannot load skeleton\n")) 125 goto close_sk; 126 127 prog = bpf_program__fd(skel->progs.copy_sock_map); 128 src = bpf_map__fd(skel->maps.src); 129 if (map_type == BPF_MAP_TYPE_SOCKMAP) 130 dst = bpf_map__fd(skel->maps.dst_sock_map); 131 else 132 dst = bpf_map__fd(skel->maps.dst_sock_hash); 133 134 err = bpf_map_update_elem(src, &zero, &sk, BPF_NOEXIST); 135 if (CHECK(err, "update_elem(src)", "errno=%u\n", errno)) 136 goto out; 137 138 err = bpf_map_lookup_elem(src, &zero, &src_cookie); 139 if (CHECK(err, "lookup_elem(src, cookie)", "errno=%u\n", errno)) 140 goto out; 141 142 tattr = (struct bpf_prog_test_run_attr){ 143 .prog_fd = prog, 144 .repeat = 1, 145 .data_in = dummy, 146 .data_size_in = sizeof(dummy), 147 }; 148 149 err = bpf_prog_test_run_xattr(&tattr); 150 if (CHECK_ATTR(err || !tattr.retval, "bpf_prog_test_run", 151 "errno=%u retval=%u\n", errno, tattr.retval)) 152 goto out; 153 154 err = bpf_map_lookup_elem(dst, &zero, &dst_cookie); 155 if (CHECK(err, "lookup_elem(dst, cookie)", "errno=%u\n", errno)) 156 goto out; 157 158 CHECK(dst_cookie != src_cookie, "cookie mismatch", "%llu != %llu\n", 159 dst_cookie, src_cookie); 160 161 out: 162 test_sockmap_update__destroy(skel); 163 close_sk: 164 close(sk); 165 } 166 167 static void test_sockmap_invalid_update(void) 168 { 169 struct test_sockmap_invalid_update *skel; 170 int duration = 0; 171 172 skel = test_sockmap_invalid_update__open_and_load(); 173 if (CHECK(skel, "open_and_load", "verifier accepted map_update\n")) 174 test_sockmap_invalid_update__destroy(skel); 175 } 176 177 static void test_sockmap_iter(enum bpf_map_type map_type) 178 { 179 DECLARE_LIBBPF_OPTS(bpf_iter_attach_opts, opts); 180 int err, len, src_fd, iter_fd, duration = 0; 181 union bpf_iter_link_info linfo = {0}; 182 __s64 sock_fd[SOCKMAP_MAX_ENTRIES]; 183 __u32 i, num_sockets, max_elems; 184 struct bpf_iter_sockmap *skel; 185 struct bpf_link *link; 186 struct bpf_map *src; 187 char buf[64]; 188 189 skel = bpf_iter_sockmap__open_and_load(); 190 if (CHECK(!skel, "bpf_iter_sockmap__open_and_load", "skeleton open_and_load failed\n")) 191 return; 192 193 for (i = 0; i < ARRAY_SIZE(sock_fd); i++) 194 sock_fd[i] = -1; 195 196 /* Make sure we have at least one "empty" entry to test iteration of 197 * an empty slot. 198 */ 199 num_sockets = ARRAY_SIZE(sock_fd) - 1; 200 201 if (map_type == BPF_MAP_TYPE_SOCKMAP) { 202 src = skel->maps.sockmap; 203 max_elems = bpf_map__max_entries(src); 204 } else { 205 src = skel->maps.sockhash; 206 max_elems = num_sockets; 207 } 208 209 src_fd = bpf_map__fd(src); 210 211 for (i = 0; i < num_sockets; i++) { 212 sock_fd[i] = connected_socket_v4(); 213 if (CHECK(sock_fd[i] == -1, "connected_socket_v4", "cannot connect\n")) 214 goto out; 215 216 err = bpf_map_update_elem(src_fd, &i, &sock_fd[i], BPF_NOEXIST); 217 if (CHECK(err, "map_update", "failed: %s\n", strerror(errno))) 218 goto out; 219 } 220 221 linfo.map.map_fd = src_fd; 222 opts.link_info = &linfo; 223 opts.link_info_len = sizeof(linfo); 224 link = bpf_program__attach_iter(skel->progs.count_elems, &opts); 225 if (CHECK(IS_ERR(link), "attach_iter", "attach_iter failed\n")) 226 goto out; 227 228 iter_fd = bpf_iter_create(bpf_link__fd(link)); 229 if (CHECK(iter_fd < 0, "create_iter", "create_iter failed\n")) 230 goto free_link; 231 232 /* do some tests */ 233 while ((len = read(iter_fd, buf, sizeof(buf))) > 0) 234 ; 235 if (CHECK(len < 0, "read", "failed: %s\n", strerror(errno))) 236 goto close_iter; 237 238 /* test results */ 239 if (CHECK(skel->bss->elems != max_elems, "elems", "got %u expected %u\n", 240 skel->bss->elems, max_elems)) 241 goto close_iter; 242 243 if (CHECK(skel->bss->socks != num_sockets, "socks", "got %u expected %u\n", 244 skel->bss->socks, num_sockets)) 245 goto close_iter; 246 247 close_iter: 248 close(iter_fd); 249 free_link: 250 bpf_link__destroy(link); 251 out: 252 for (i = 0; i < num_sockets; i++) { 253 if (sock_fd[i] >= 0) 254 close(sock_fd[i]); 255 } 256 bpf_iter_sockmap__destroy(skel); 257 } 258 259 void test_sockmap_basic(void) 260 { 261 if (test__start_subtest("sockmap create_update_free")) 262 test_sockmap_create_update_free(BPF_MAP_TYPE_SOCKMAP); 263 if (test__start_subtest("sockhash create_update_free")) 264 test_sockmap_create_update_free(BPF_MAP_TYPE_SOCKHASH); 265 if (test__start_subtest("sockmap sk_msg load helpers")) 266 test_skmsg_helpers(BPF_MAP_TYPE_SOCKMAP); 267 if (test__start_subtest("sockhash sk_msg load helpers")) 268 test_skmsg_helpers(BPF_MAP_TYPE_SOCKHASH); 269 if (test__start_subtest("sockmap update")) 270 test_sockmap_update(BPF_MAP_TYPE_SOCKMAP); 271 if (test__start_subtest("sockhash update")) 272 test_sockmap_update(BPF_MAP_TYPE_SOCKHASH); 273 if (test__start_subtest("sockmap update in unsafe context")) 274 test_sockmap_invalid_update(); 275 if (test__start_subtest("sockmap iter")) 276 test_sockmap_iter(BPF_MAP_TYPE_SOCKMAP); 277 if (test__start_subtest("sockhash iter")) 278 test_sockmap_iter(BPF_MAP_TYPE_SOCKHASH); 279 } 280