Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: test bpf flow dissection

Adds a test that sends different types of packets over multiple
tunnels and verifies that valid packets are dissected correctly. To do
so, a tc-flower rule is added to drop packets on UDP src port 9, and
packets are sent from ports 8, 9, and 10. Only the packets on port 9
should be dropped. Because tc-flower relies on the flow dissector to
match flows, correct classification demonstrates correct dissection.

Also add support logic to load the BPF program and to inject the test
packets.

Signed-off-by: Petar Penkov <ppenkov@google.com>
Signed-off-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Petar Penkov and committed by
Alexei Starovoitov
50b3ed57 9c98b13c

+1134 -2
+2
tools/testing/selftests/bpf/.gitignore
··· 23 23 test_socket_cookie 24 24 test_cgroup_storage 25 25 test_select_reuseport 26 + test_flow_dissector 27 + flow_dissector_load
+4 -2
tools/testing/selftests/bpf/Makefile
··· 47 47 test_tunnel.sh \ 48 48 test_lwt_seg6local.sh \ 49 49 test_lirc_mode2.sh \ 50 - test_skb_cgroup_id.sh 50 + test_skb_cgroup_id.sh \ 51 + test_flow_dissector.sh 51 52 52 53 # Compile but not part of 'make run_tests' 53 - TEST_GEN_PROGS_EXTENDED = test_libbpf_open test_sock_addr test_skb_cgroup_id_user 54 + TEST_GEN_PROGS_EXTENDED = test_libbpf_open test_sock_addr test_skb_cgroup_id_user \ 55 + flow_dissector_load test_flow_dissector 54 56 55 57 include ../lib.mk 56 58
+1
tools/testing/selftests/bpf/config
··· 18 18 CONFIG_CRYPTO_SHA256=m 19 19 CONFIG_VXLAN=y 20 20 CONFIG_GENEVE=y 21 + CONFIG_NET_CLS_FLOWER=m
+140
tools/testing/selftests/bpf/flow_dissector_load.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <error.h> 3 + #include <errno.h> 4 + #include <getopt.h> 5 + #include <stdio.h> 6 + #include <stdlib.h> 7 + #include <string.h> 8 + #include <sys/stat.h> 9 + #include <fcntl.h> 10 + #include <unistd.h> 11 + #include <bpf/bpf.h> 12 + #include <bpf/libbpf.h> 13 + 14 + const char *cfg_pin_path = "/sys/fs/bpf/flow_dissector"; 15 + const char *cfg_map_name = "jmp_table"; 16 + bool cfg_attach = true; 17 + char *cfg_section_name; 18 + char *cfg_path_name; 19 + 20 + static void load_and_attach_program(void) 21 + { 22 + struct bpf_program *prog, *main_prog; 23 + struct bpf_map *prog_array; 24 + int i, fd, prog_fd, ret; 25 + struct bpf_object *obj; 26 + int prog_array_fd; 27 + 28 + ret = bpf_prog_load(cfg_path_name, BPF_PROG_TYPE_FLOW_DISSECTOR, &obj, 29 + &prog_fd); 30 + if (ret) 31 + error(1, 0, "bpf_prog_load %s", cfg_path_name); 32 + 33 + main_prog = bpf_object__find_program_by_title(obj, cfg_section_name); 34 + if (!main_prog) 35 + error(1, 0, "bpf_object__find_program_by_title %s", 36 + cfg_section_name); 37 + 38 + prog_fd = bpf_program__fd(main_prog); 39 + if (prog_fd < 0) 40 + error(1, 0, "bpf_program__fd"); 41 + 42 + prog_array = bpf_object__find_map_by_name(obj, cfg_map_name); 43 + if (!prog_array) 44 + error(1, 0, "bpf_object__find_map_by_name %s", cfg_map_name); 45 + 46 + prog_array_fd = bpf_map__fd(prog_array); 47 + if (prog_array_fd < 0) 48 + error(1, 0, "bpf_map__fd %s", cfg_map_name); 49 + 50 + i = 0; 51 + bpf_object__for_each_program(prog, obj) { 52 + fd = bpf_program__fd(prog); 53 + if (fd < 0) 54 + error(1, 0, "bpf_program__fd"); 55 + 56 + if (fd != prog_fd) { 57 + printf("%d: %s\n", i, bpf_program__title(prog, false)); 58 + bpf_map_update_elem(prog_array_fd, &i, &fd, BPF_ANY); 59 + ++i; 60 + } 61 + } 62 + 63 + ret = bpf_prog_attach(prog_fd, 0 /* Ignore */, BPF_FLOW_DISSECTOR, 0); 64 + if (ret) 65 + error(1, 0, "bpf_prog_attach %s", cfg_path_name); 66 + 67 + ret = bpf_object__pin(obj, cfg_pin_path); 68 + if (ret) 69 + error(1, 0, "bpf_object__pin %s", cfg_pin_path); 70 + 71 + } 72 + 73 + static void detach_program(void) 74 + { 75 + char command[64]; 76 + int ret; 77 + 78 + ret = bpf_prog_detach(0, BPF_FLOW_DISSECTOR); 79 + if (ret) 80 + error(1, 0, "bpf_prog_detach"); 81 + 82 + /* To unpin, it is necessary and sufficient to just remove this dir */ 83 + sprintf(command, "rm -r %s", cfg_pin_path); 84 + ret = system(command); 85 + if (ret) 86 + error(1, errno, command); 87 + } 88 + 89 + static void parse_opts(int argc, char **argv) 90 + { 91 + bool attach = false; 92 + bool detach = false; 93 + int c; 94 + 95 + while ((c = getopt(argc, argv, "adp:s:")) != -1) { 96 + switch (c) { 97 + case 'a': 98 + if (detach) 99 + error(1, 0, "attach/detach are exclusive"); 100 + attach = true; 101 + break; 102 + case 'd': 103 + if (attach) 104 + error(1, 0, "attach/detach are exclusive"); 105 + detach = true; 106 + break; 107 + case 'p': 108 + if (cfg_path_name) 109 + error(1, 0, "only one prog name can be given"); 110 + 111 + cfg_path_name = optarg; 112 + break; 113 + case 's': 114 + if (cfg_section_name) 115 + error(1, 0, "only one section can be given"); 116 + 117 + cfg_section_name = optarg; 118 + break; 119 + } 120 + } 121 + 122 + if (detach) 123 + cfg_attach = false; 124 + 125 + if (cfg_attach && !cfg_path_name) 126 + error(1, 0, "must provide a path to the BPF program"); 127 + 128 + if (cfg_attach && !cfg_section_name) 129 + error(1, 0, "must provide a section name"); 130 + } 131 + 132 + int main(int argc, char **argv) 133 + { 134 + parse_opts(argc, argv); 135 + if (cfg_attach) 136 + load_and_attach_program(); 137 + else 138 + detach_program(); 139 + return 0; 140 + }
+782
tools/testing/selftests/bpf/test_flow_dissector.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Inject packets with all sorts of encapsulation into the kernel. 4 + * 5 + * IPv4/IPv6 outer layer 3 6 + * GRE/GUE/BARE outer layer 4, where bare is IPIP/SIT/IPv4-in-IPv6/.. 7 + * IPv4/IPv6 inner layer 3 8 + */ 9 + 10 + #define _GNU_SOURCE 11 + 12 + #include <stddef.h> 13 + #include <arpa/inet.h> 14 + #include <asm/byteorder.h> 15 + #include <error.h> 16 + #include <errno.h> 17 + #include <linux/if_packet.h> 18 + #include <linux/if_ether.h> 19 + #include <linux/if_packet.h> 20 + #include <linux/ipv6.h> 21 + #include <netinet/ip.h> 22 + #include <netinet/in.h> 23 + #include <netinet/udp.h> 24 + #include <poll.h> 25 + #include <stdbool.h> 26 + #include <stdlib.h> 27 + #include <stdio.h> 28 + #include <stdlib.h> 29 + #include <string.h> 30 + #include <sys/ioctl.h> 31 + #include <sys/socket.h> 32 + #include <sys/stat.h> 33 + #include <sys/time.h> 34 + #include <sys/types.h> 35 + #include <unistd.h> 36 + 37 + #define CFG_PORT_INNER 8000 38 + 39 + /* Add some protocol definitions that do not exist in userspace */ 40 + 41 + struct grehdr { 42 + uint16_t unused; 43 + uint16_t protocol; 44 + } __attribute__((packed)); 45 + 46 + struct guehdr { 47 + union { 48 + struct { 49 + #if defined(__LITTLE_ENDIAN_BITFIELD) 50 + __u8 hlen:5, 51 + control:1, 52 + version:2; 53 + #elif defined (__BIG_ENDIAN_BITFIELD) 54 + __u8 version:2, 55 + control:1, 56 + hlen:5; 57 + #else 58 + #error "Please fix <asm/byteorder.h>" 59 + #endif 60 + __u8 proto_ctype; 61 + __be16 flags; 62 + }; 63 + __be32 word; 64 + }; 65 + }; 66 + 67 + static uint8_t cfg_dsfield_inner; 68 + static uint8_t cfg_dsfield_outer; 69 + static uint8_t cfg_encap_proto; 70 + static bool cfg_expect_failure = false; 71 + static int cfg_l3_extra = AF_UNSPEC; /* optional SIT prefix */ 72 + static int cfg_l3_inner = AF_UNSPEC; 73 + static int cfg_l3_outer = AF_UNSPEC; 74 + static int cfg_num_pkt = 10; 75 + static int cfg_num_secs = 0; 76 + static char cfg_payload_char = 'a'; 77 + static int cfg_payload_len = 100; 78 + static int cfg_port_gue = 6080; 79 + static bool cfg_only_rx; 80 + static bool cfg_only_tx; 81 + static int cfg_src_port = 9; 82 + 83 + static char buf[ETH_DATA_LEN]; 84 + 85 + #define INIT_ADDR4(name, addr4, port) \ 86 + static struct sockaddr_in name = { \ 87 + .sin_family = AF_INET, \ 88 + .sin_port = __constant_htons(port), \ 89 + .sin_addr.s_addr = __constant_htonl(addr4), \ 90 + }; 91 + 92 + #define INIT_ADDR6(name, addr6, port) \ 93 + static struct sockaddr_in6 name = { \ 94 + .sin6_family = AF_INET6, \ 95 + .sin6_port = __constant_htons(port), \ 96 + .sin6_addr = addr6, \ 97 + }; 98 + 99 + INIT_ADDR4(in_daddr4, INADDR_LOOPBACK, CFG_PORT_INNER) 100 + INIT_ADDR4(in_saddr4, INADDR_LOOPBACK + 2, 0) 101 + INIT_ADDR4(out_daddr4, INADDR_LOOPBACK, 0) 102 + INIT_ADDR4(out_saddr4, INADDR_LOOPBACK + 1, 0) 103 + INIT_ADDR4(extra_daddr4, INADDR_LOOPBACK, 0) 104 + INIT_ADDR4(extra_saddr4, INADDR_LOOPBACK + 1, 0) 105 + 106 + INIT_ADDR6(in_daddr6, IN6ADDR_LOOPBACK_INIT, CFG_PORT_INNER) 107 + INIT_ADDR6(in_saddr6, IN6ADDR_LOOPBACK_INIT, 0) 108 + INIT_ADDR6(out_daddr6, IN6ADDR_LOOPBACK_INIT, 0) 109 + INIT_ADDR6(out_saddr6, IN6ADDR_LOOPBACK_INIT, 0) 110 + INIT_ADDR6(extra_daddr6, IN6ADDR_LOOPBACK_INIT, 0) 111 + INIT_ADDR6(extra_saddr6, IN6ADDR_LOOPBACK_INIT, 0) 112 + 113 + static unsigned long util_gettime(void) 114 + { 115 + struct timeval tv; 116 + 117 + gettimeofday(&tv, NULL); 118 + return (tv.tv_sec * 1000) + (tv.tv_usec / 1000); 119 + } 120 + 121 + static void util_printaddr(const char *msg, struct sockaddr *addr) 122 + { 123 + unsigned long off = 0; 124 + char nbuf[INET6_ADDRSTRLEN]; 125 + 126 + switch (addr->sa_family) { 127 + case PF_INET: 128 + off = __builtin_offsetof(struct sockaddr_in, sin_addr); 129 + break; 130 + case PF_INET6: 131 + off = __builtin_offsetof(struct sockaddr_in6, sin6_addr); 132 + break; 133 + default: 134 + error(1, 0, "printaddr: unsupported family %u\n", 135 + addr->sa_family); 136 + } 137 + 138 + if (!inet_ntop(addr->sa_family, ((void *) addr) + off, nbuf, 139 + sizeof(nbuf))) 140 + error(1, errno, "inet_ntop"); 141 + 142 + fprintf(stderr, "%s: %s\n", msg, nbuf); 143 + } 144 + 145 + static unsigned long add_csum_hword(const uint16_t *start, int num_u16) 146 + { 147 + unsigned long sum = 0; 148 + int i; 149 + 150 + for (i = 0; i < num_u16; i++) 151 + sum += start[i]; 152 + 153 + return sum; 154 + } 155 + 156 + static uint16_t build_ip_csum(const uint16_t *start, int num_u16, 157 + unsigned long sum) 158 + { 159 + sum += add_csum_hword(start, num_u16); 160 + 161 + while (sum >> 16) 162 + sum = (sum & 0xffff) + (sum >> 16); 163 + 164 + return ~sum; 165 + } 166 + 167 + static void build_ipv4_header(void *header, uint8_t proto, 168 + uint32_t src, uint32_t dst, 169 + int payload_len, uint8_t tos) 170 + { 171 + struct iphdr *iph = header; 172 + 173 + iph->ihl = 5; 174 + iph->version = 4; 175 + iph->tos = tos; 176 + iph->ttl = 8; 177 + iph->tot_len = htons(sizeof(*iph) + payload_len); 178 + iph->id = htons(1337); 179 + iph->protocol = proto; 180 + iph->saddr = src; 181 + iph->daddr = dst; 182 + iph->check = build_ip_csum((void *) iph, iph->ihl << 1, 0); 183 + } 184 + 185 + static void ipv6_set_dsfield(struct ipv6hdr *ip6h, uint8_t dsfield) 186 + { 187 + uint16_t val, *ptr = (uint16_t *)ip6h; 188 + 189 + val = ntohs(*ptr); 190 + val &= 0xF00F; 191 + val |= ((uint16_t) dsfield) << 4; 192 + *ptr = htons(val); 193 + } 194 + 195 + static void build_ipv6_header(void *header, uint8_t proto, 196 + struct sockaddr_in6 *src, 197 + struct sockaddr_in6 *dst, 198 + int payload_len, uint8_t dsfield) 199 + { 200 + struct ipv6hdr *ip6h = header; 201 + 202 + ip6h->version = 6; 203 + ip6h->payload_len = htons(payload_len); 204 + ip6h->nexthdr = proto; 205 + ip6h->hop_limit = 8; 206 + ipv6_set_dsfield(ip6h, dsfield); 207 + 208 + memcpy(&ip6h->saddr, &src->sin6_addr, sizeof(ip6h->saddr)); 209 + memcpy(&ip6h->daddr, &dst->sin6_addr, sizeof(ip6h->daddr)); 210 + } 211 + 212 + static uint16_t build_udp_v4_csum(const struct iphdr *iph, 213 + const struct udphdr *udph, 214 + int num_words) 215 + { 216 + unsigned long pseudo_sum; 217 + int num_u16 = sizeof(iph->saddr); /* halfwords: twice byte len */ 218 + 219 + pseudo_sum = add_csum_hword((void *) &iph->saddr, num_u16); 220 + pseudo_sum += htons(IPPROTO_UDP); 221 + pseudo_sum += udph->len; 222 + return build_ip_csum((void *) udph, num_words, pseudo_sum); 223 + } 224 + 225 + static uint16_t build_udp_v6_csum(const struct ipv6hdr *ip6h, 226 + const struct udphdr *udph, 227 + int num_words) 228 + { 229 + unsigned long pseudo_sum; 230 + int num_u16 = sizeof(ip6h->saddr); /* halfwords: twice byte len */ 231 + 232 + pseudo_sum = add_csum_hword((void *) &ip6h->saddr, num_u16); 233 + pseudo_sum += htons(ip6h->nexthdr); 234 + pseudo_sum += ip6h->payload_len; 235 + return build_ip_csum((void *) udph, num_words, pseudo_sum); 236 + } 237 + 238 + static void build_udp_header(void *header, int payload_len, 239 + uint16_t dport, int family) 240 + { 241 + struct udphdr *udph = header; 242 + int len = sizeof(*udph) + payload_len; 243 + 244 + udph->source = htons(cfg_src_port); 245 + udph->dest = htons(dport); 246 + udph->len = htons(len); 247 + udph->check = 0; 248 + if (family == AF_INET) 249 + udph->check = build_udp_v4_csum(header - sizeof(struct iphdr), 250 + udph, len >> 1); 251 + else 252 + udph->check = build_udp_v6_csum(header - sizeof(struct ipv6hdr), 253 + udph, len >> 1); 254 + } 255 + 256 + static void build_gue_header(void *header, uint8_t proto) 257 + { 258 + struct guehdr *gueh = header; 259 + 260 + gueh->proto_ctype = proto; 261 + } 262 + 263 + static void build_gre_header(void *header, uint16_t proto) 264 + { 265 + struct grehdr *greh = header; 266 + 267 + greh->protocol = htons(proto); 268 + } 269 + 270 + static int l3_length(int family) 271 + { 272 + if (family == AF_INET) 273 + return sizeof(struct iphdr); 274 + else 275 + return sizeof(struct ipv6hdr); 276 + } 277 + 278 + static int build_packet(void) 279 + { 280 + int ol3_len = 0, ol4_len = 0, il3_len = 0, il4_len = 0; 281 + int el3_len = 0; 282 + 283 + if (cfg_l3_extra) 284 + el3_len = l3_length(cfg_l3_extra); 285 + 286 + /* calculate header offsets */ 287 + if (cfg_encap_proto) { 288 + ol3_len = l3_length(cfg_l3_outer); 289 + 290 + if (cfg_encap_proto == IPPROTO_GRE) 291 + ol4_len = sizeof(struct grehdr); 292 + else if (cfg_encap_proto == IPPROTO_UDP) 293 + ol4_len = sizeof(struct udphdr) + sizeof(struct guehdr); 294 + } 295 + 296 + il3_len = l3_length(cfg_l3_inner); 297 + il4_len = sizeof(struct udphdr); 298 + 299 + if (el3_len + ol3_len + ol4_len + il3_len + il4_len + cfg_payload_len >= 300 + sizeof(buf)) 301 + error(1, 0, "packet too large\n"); 302 + 303 + /* 304 + * Fill packet from inside out, to calculate correct checksums. 305 + * But create ip before udp headers, as udp uses ip for pseudo-sum. 306 + */ 307 + memset(buf + el3_len + ol3_len + ol4_len + il3_len + il4_len, 308 + cfg_payload_char, cfg_payload_len); 309 + 310 + /* add zero byte for udp csum padding */ 311 + buf[el3_len + ol3_len + ol4_len + il3_len + il4_len + cfg_payload_len] = 0; 312 + 313 + switch (cfg_l3_inner) { 314 + case PF_INET: 315 + build_ipv4_header(buf + el3_len + ol3_len + ol4_len, 316 + IPPROTO_UDP, 317 + in_saddr4.sin_addr.s_addr, 318 + in_daddr4.sin_addr.s_addr, 319 + il4_len + cfg_payload_len, 320 + cfg_dsfield_inner); 321 + break; 322 + case PF_INET6: 323 + build_ipv6_header(buf + el3_len + ol3_len + ol4_len, 324 + IPPROTO_UDP, 325 + &in_saddr6, &in_daddr6, 326 + il4_len + cfg_payload_len, 327 + cfg_dsfield_inner); 328 + break; 329 + } 330 + 331 + build_udp_header(buf + el3_len + ol3_len + ol4_len + il3_len, 332 + cfg_payload_len, CFG_PORT_INNER, cfg_l3_inner); 333 + 334 + if (!cfg_encap_proto) 335 + return il3_len + il4_len + cfg_payload_len; 336 + 337 + switch (cfg_l3_outer) { 338 + case PF_INET: 339 + build_ipv4_header(buf + el3_len, cfg_encap_proto, 340 + out_saddr4.sin_addr.s_addr, 341 + out_daddr4.sin_addr.s_addr, 342 + ol4_len + il3_len + il4_len + cfg_payload_len, 343 + cfg_dsfield_outer); 344 + break; 345 + case PF_INET6: 346 + build_ipv6_header(buf + el3_len, cfg_encap_proto, 347 + &out_saddr6, &out_daddr6, 348 + ol4_len + il3_len + il4_len + cfg_payload_len, 349 + cfg_dsfield_outer); 350 + break; 351 + } 352 + 353 + switch (cfg_encap_proto) { 354 + case IPPROTO_UDP: 355 + build_gue_header(buf + el3_len + ol3_len + ol4_len - 356 + sizeof(struct guehdr), 357 + cfg_l3_inner == PF_INET ? IPPROTO_IPIP 358 + : IPPROTO_IPV6); 359 + build_udp_header(buf + el3_len + ol3_len, 360 + sizeof(struct guehdr) + il3_len + il4_len + 361 + cfg_payload_len, 362 + cfg_port_gue, cfg_l3_outer); 363 + break; 364 + case IPPROTO_GRE: 365 + build_gre_header(buf + el3_len + ol3_len, 366 + cfg_l3_inner == PF_INET ? ETH_P_IP 367 + : ETH_P_IPV6); 368 + break; 369 + } 370 + 371 + switch (cfg_l3_extra) { 372 + case PF_INET: 373 + build_ipv4_header(buf, 374 + cfg_l3_outer == PF_INET ? IPPROTO_IPIP 375 + : IPPROTO_IPV6, 376 + extra_saddr4.sin_addr.s_addr, 377 + extra_daddr4.sin_addr.s_addr, 378 + ol3_len + ol4_len + il3_len + il4_len + 379 + cfg_payload_len, 0); 380 + break; 381 + case PF_INET6: 382 + build_ipv6_header(buf, 383 + cfg_l3_outer == PF_INET ? IPPROTO_IPIP 384 + : IPPROTO_IPV6, 385 + &extra_saddr6, &extra_daddr6, 386 + ol3_len + ol4_len + il3_len + il4_len + 387 + cfg_payload_len, 0); 388 + break; 389 + } 390 + 391 + return el3_len + ol3_len + ol4_len + il3_len + il4_len + 392 + cfg_payload_len; 393 + } 394 + 395 + /* sender transmits encapsulated over RAW or unencap'd over UDP */ 396 + static int setup_tx(void) 397 + { 398 + int family, fd, ret; 399 + 400 + if (cfg_l3_extra) 401 + family = cfg_l3_extra; 402 + else if (cfg_l3_outer) 403 + family = cfg_l3_outer; 404 + else 405 + family = cfg_l3_inner; 406 + 407 + fd = socket(family, SOCK_RAW, IPPROTO_RAW); 408 + if (fd == -1) 409 + error(1, errno, "socket tx"); 410 + 411 + if (cfg_l3_extra) { 412 + if (cfg_l3_extra == PF_INET) 413 + ret = connect(fd, (void *) &extra_daddr4, 414 + sizeof(extra_daddr4)); 415 + else 416 + ret = connect(fd, (void *) &extra_daddr6, 417 + sizeof(extra_daddr6)); 418 + if (ret) 419 + error(1, errno, "connect tx"); 420 + } else if (cfg_l3_outer) { 421 + /* connect to destination if not encapsulated */ 422 + if (cfg_l3_outer == PF_INET) 423 + ret = connect(fd, (void *) &out_daddr4, 424 + sizeof(out_daddr4)); 425 + else 426 + ret = connect(fd, (void *) &out_daddr6, 427 + sizeof(out_daddr6)); 428 + if (ret) 429 + error(1, errno, "connect tx"); 430 + } else { 431 + /* otherwise using loopback */ 432 + if (cfg_l3_inner == PF_INET) 433 + ret = connect(fd, (void *) &in_daddr4, 434 + sizeof(in_daddr4)); 435 + else 436 + ret = connect(fd, (void *) &in_daddr6, 437 + sizeof(in_daddr6)); 438 + if (ret) 439 + error(1, errno, "connect tx"); 440 + } 441 + 442 + return fd; 443 + } 444 + 445 + /* receiver reads unencapsulated UDP */ 446 + static int setup_rx(void) 447 + { 448 + int fd, ret; 449 + 450 + fd = socket(cfg_l3_inner, SOCK_DGRAM, 0); 451 + if (fd == -1) 452 + error(1, errno, "socket rx"); 453 + 454 + if (cfg_l3_inner == PF_INET) 455 + ret = bind(fd, (void *) &in_daddr4, sizeof(in_daddr4)); 456 + else 457 + ret = bind(fd, (void *) &in_daddr6, sizeof(in_daddr6)); 458 + if (ret) 459 + error(1, errno, "bind rx"); 460 + 461 + return fd; 462 + } 463 + 464 + static int do_tx(int fd, const char *pkt, int len) 465 + { 466 + int ret; 467 + 468 + ret = write(fd, pkt, len); 469 + if (ret == -1) 470 + error(1, errno, "send"); 471 + if (ret != len) 472 + error(1, errno, "send: len (%d < %d)\n", ret, len); 473 + 474 + return 1; 475 + } 476 + 477 + static int do_poll(int fd, short events, int timeout) 478 + { 479 + struct pollfd pfd; 480 + int ret; 481 + 482 + pfd.fd = fd; 483 + pfd.events = events; 484 + 485 + ret = poll(&pfd, 1, timeout); 486 + if (ret == -1) 487 + error(1, errno, "poll"); 488 + if (ret && !(pfd.revents & POLLIN)) 489 + error(1, errno, "poll: unexpected event 0x%x\n", pfd.revents); 490 + 491 + return ret; 492 + } 493 + 494 + static int do_rx(int fd) 495 + { 496 + char rbuf; 497 + int ret, num = 0; 498 + 499 + while (1) { 500 + ret = recv(fd, &rbuf, 1, MSG_DONTWAIT); 501 + if (ret == -1 && errno == EAGAIN) 502 + break; 503 + if (ret == -1) 504 + error(1, errno, "recv"); 505 + if (rbuf != cfg_payload_char) 506 + error(1, 0, "recv: payload mismatch"); 507 + num++; 508 + }; 509 + 510 + return num; 511 + } 512 + 513 + static int do_main(void) 514 + { 515 + unsigned long tstop, treport, tcur; 516 + int fdt = -1, fdr = -1, len, tx = 0, rx = 0; 517 + 518 + if (!cfg_only_tx) 519 + fdr = setup_rx(); 520 + if (!cfg_only_rx) 521 + fdt = setup_tx(); 522 + 523 + len = build_packet(); 524 + 525 + tcur = util_gettime(); 526 + treport = tcur + 1000; 527 + tstop = tcur + (cfg_num_secs * 1000); 528 + 529 + while (1) { 530 + if (!cfg_only_rx) 531 + tx += do_tx(fdt, buf, len); 532 + 533 + if (!cfg_only_tx) 534 + rx += do_rx(fdr); 535 + 536 + if (cfg_num_secs) { 537 + tcur = util_gettime(); 538 + if (tcur >= tstop) 539 + break; 540 + if (tcur >= treport) { 541 + fprintf(stderr, "pkts: tx=%u rx=%u\n", tx, rx); 542 + tx = 0; 543 + rx = 0; 544 + treport = tcur + 1000; 545 + } 546 + } else { 547 + if (tx == cfg_num_pkt) 548 + break; 549 + } 550 + } 551 + 552 + /* read straggler packets, if any */ 553 + if (rx < tx) { 554 + tstop = util_gettime() + 100; 555 + while (rx < tx) { 556 + tcur = util_gettime(); 557 + if (tcur >= tstop) 558 + break; 559 + 560 + do_poll(fdr, POLLIN, tstop - tcur); 561 + rx += do_rx(fdr); 562 + } 563 + } 564 + 565 + fprintf(stderr, "pkts: tx=%u rx=%u\n", tx, rx); 566 + 567 + if (fdr != -1 && close(fdr)) 568 + error(1, errno, "close rx"); 569 + if (fdt != -1 && close(fdt)) 570 + error(1, errno, "close tx"); 571 + 572 + /* 573 + * success (== 0) only if received all packets 574 + * unless failure is expected, in which case none must arrive. 575 + */ 576 + if (cfg_expect_failure) 577 + return rx != 0; 578 + else 579 + return rx != tx; 580 + } 581 + 582 + 583 + static void __attribute__((noreturn)) usage(const char *filepath) 584 + { 585 + fprintf(stderr, "Usage: %s [-e gre|gue|bare|none] [-i 4|6] [-l len] " 586 + "[-O 4|6] [-o 4|6] [-n num] [-t secs] [-R] [-T] " 587 + "[-s <osrc> [-d <odst>] [-S <isrc>] [-D <idst>] " 588 + "[-x <otos>] [-X <itos>] [-f <isport>] [-F]\n", 589 + filepath); 590 + exit(1); 591 + } 592 + 593 + static void parse_addr(int family, void *addr, const char *optarg) 594 + { 595 + int ret; 596 + 597 + ret = inet_pton(family, optarg, addr); 598 + if (ret == -1) 599 + error(1, errno, "inet_pton"); 600 + if (ret == 0) 601 + error(1, 0, "inet_pton: bad string"); 602 + } 603 + 604 + static void parse_addr4(struct sockaddr_in *addr, const char *optarg) 605 + { 606 + parse_addr(AF_INET, &addr->sin_addr, optarg); 607 + } 608 + 609 + static void parse_addr6(struct sockaddr_in6 *addr, const char *optarg) 610 + { 611 + parse_addr(AF_INET6, &addr->sin6_addr, optarg); 612 + } 613 + 614 + static int parse_protocol_family(const char *filepath, const char *optarg) 615 + { 616 + if (!strcmp(optarg, "4")) 617 + return PF_INET; 618 + if (!strcmp(optarg, "6")) 619 + return PF_INET6; 620 + 621 + usage(filepath); 622 + } 623 + 624 + static void parse_opts(int argc, char **argv) 625 + { 626 + int c; 627 + 628 + while ((c = getopt(argc, argv, "d:D:e:f:Fhi:l:n:o:O:Rs:S:t:Tx:X:")) != -1) { 629 + switch (c) { 630 + case 'd': 631 + if (cfg_l3_outer == AF_UNSPEC) 632 + error(1, 0, "-d must be preceded by -o"); 633 + if (cfg_l3_outer == AF_INET) 634 + parse_addr4(&out_daddr4, optarg); 635 + else 636 + parse_addr6(&out_daddr6, optarg); 637 + break; 638 + case 'D': 639 + if (cfg_l3_inner == AF_UNSPEC) 640 + error(1, 0, "-D must be preceded by -i"); 641 + if (cfg_l3_inner == AF_INET) 642 + parse_addr4(&in_daddr4, optarg); 643 + else 644 + parse_addr6(&in_daddr6, optarg); 645 + break; 646 + case 'e': 647 + if (!strcmp(optarg, "gre")) 648 + cfg_encap_proto = IPPROTO_GRE; 649 + else if (!strcmp(optarg, "gue")) 650 + cfg_encap_proto = IPPROTO_UDP; 651 + else if (!strcmp(optarg, "bare")) 652 + cfg_encap_proto = IPPROTO_IPIP; 653 + else if (!strcmp(optarg, "none")) 654 + cfg_encap_proto = IPPROTO_IP; /* == 0 */ 655 + else 656 + usage(argv[0]); 657 + break; 658 + case 'f': 659 + cfg_src_port = strtol(optarg, NULL, 0); 660 + break; 661 + case 'F': 662 + cfg_expect_failure = true; 663 + break; 664 + case 'h': 665 + usage(argv[0]); 666 + break; 667 + case 'i': 668 + if (!strcmp(optarg, "4")) 669 + cfg_l3_inner = PF_INET; 670 + else if (!strcmp(optarg, "6")) 671 + cfg_l3_inner = PF_INET6; 672 + else 673 + usage(argv[0]); 674 + break; 675 + case 'l': 676 + cfg_payload_len = strtol(optarg, NULL, 0); 677 + break; 678 + case 'n': 679 + cfg_num_pkt = strtol(optarg, NULL, 0); 680 + break; 681 + case 'o': 682 + cfg_l3_outer = parse_protocol_family(argv[0], optarg); 683 + break; 684 + case 'O': 685 + cfg_l3_extra = parse_protocol_family(argv[0], optarg); 686 + break; 687 + case 'R': 688 + cfg_only_rx = true; 689 + break; 690 + case 's': 691 + if (cfg_l3_outer == AF_INET) 692 + parse_addr4(&out_saddr4, optarg); 693 + else 694 + parse_addr6(&out_saddr6, optarg); 695 + break; 696 + case 'S': 697 + if (cfg_l3_inner == AF_INET) 698 + parse_addr4(&in_saddr4, optarg); 699 + else 700 + parse_addr6(&in_saddr6, optarg); 701 + break; 702 + case 't': 703 + cfg_num_secs = strtol(optarg, NULL, 0); 704 + break; 705 + case 'T': 706 + cfg_only_tx = true; 707 + break; 708 + case 'x': 709 + cfg_dsfield_outer = strtol(optarg, NULL, 0); 710 + break; 711 + case 'X': 712 + cfg_dsfield_inner = strtol(optarg, NULL, 0); 713 + break; 714 + } 715 + } 716 + 717 + if (cfg_only_rx && cfg_only_tx) 718 + error(1, 0, "options: cannot combine rx-only and tx-only"); 719 + 720 + if (cfg_encap_proto && cfg_l3_outer == AF_UNSPEC) 721 + error(1, 0, "options: must specify outer with encap"); 722 + else if ((!cfg_encap_proto) && cfg_l3_outer != AF_UNSPEC) 723 + error(1, 0, "options: cannot combine no-encap and outer"); 724 + else if ((!cfg_encap_proto) && cfg_l3_extra != AF_UNSPEC) 725 + error(1, 0, "options: cannot combine no-encap and extra"); 726 + 727 + if (cfg_l3_inner == AF_UNSPEC) 728 + cfg_l3_inner = AF_INET6; 729 + if (cfg_l3_inner == AF_INET6 && cfg_encap_proto == IPPROTO_IPIP) 730 + cfg_encap_proto = IPPROTO_IPV6; 731 + 732 + /* RFC 6040 4.2: 733 + * on decap, if outer encountered congestion (CE == 0x3), 734 + * but inner cannot encode ECN (NoECT == 0x0), then drop packet. 735 + */ 736 + if (((cfg_dsfield_outer & 0x3) == 0x3) && 737 + ((cfg_dsfield_inner & 0x3) == 0x0)) 738 + cfg_expect_failure = true; 739 + } 740 + 741 + static void print_opts(void) 742 + { 743 + if (cfg_l3_inner == PF_INET6) { 744 + util_printaddr("inner.dest6", (void *) &in_daddr6); 745 + util_printaddr("inner.source6", (void *) &in_saddr6); 746 + } else { 747 + util_printaddr("inner.dest4", (void *) &in_daddr4); 748 + util_printaddr("inner.source4", (void *) &in_saddr4); 749 + } 750 + 751 + if (!cfg_l3_outer) 752 + return; 753 + 754 + fprintf(stderr, "encap proto: %u\n", cfg_encap_proto); 755 + 756 + if (cfg_l3_outer == PF_INET6) { 757 + util_printaddr("outer.dest6", (void *) &out_daddr6); 758 + util_printaddr("outer.source6", (void *) &out_saddr6); 759 + } else { 760 + util_printaddr("outer.dest4", (void *) &out_daddr4); 761 + util_printaddr("outer.source4", (void *) &out_saddr4); 762 + } 763 + 764 + if (!cfg_l3_extra) 765 + return; 766 + 767 + if (cfg_l3_outer == PF_INET6) { 768 + util_printaddr("extra.dest6", (void *) &extra_daddr6); 769 + util_printaddr("extra.source6", (void *) &extra_saddr6); 770 + } else { 771 + util_printaddr("extra.dest4", (void *) &extra_daddr4); 772 + util_printaddr("extra.source4", (void *) &extra_saddr4); 773 + } 774 + 775 + } 776 + 777 + int main(int argc, char **argv) 778 + { 779 + parse_opts(argc, argv); 780 + print_opts(); 781 + return do_main(); 782 + }
+115
tools/testing/selftests/bpf/test_flow_dissector.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + # 4 + # Load BPF flow dissector and verify it correctly dissects traffic 5 + export TESTNAME=test_flow_dissector 6 + unmount=0 7 + 8 + # Kselftest framework requirement - SKIP code is 4. 9 + ksft_skip=4 10 + 11 + msg="skip all tests:" 12 + if [ $UID != 0 ]; then 13 + echo $msg please run this as root >&2 14 + exit $ksft_skip 15 + fi 16 + 17 + # This test needs to be run in a network namespace with in_netns.sh. Check if 18 + # this is the case and run it with in_netns.sh if it is being run in the root 19 + # namespace. 20 + if [[ -z $(ip netns identify $$) ]]; then 21 + ../net/in_netns.sh "$0" "$@" 22 + exit $? 23 + fi 24 + 25 + # Determine selftest success via shell exit code 26 + exit_handler() 27 + { 28 + if (( $? == 0 )); then 29 + echo "selftests: $TESTNAME [PASS]"; 30 + else 31 + echo "selftests: $TESTNAME [FAILED]"; 32 + fi 33 + 34 + set +e 35 + 36 + # Cleanup 37 + tc filter del dev lo ingress pref 1337 2> /dev/null 38 + tc qdisc del dev lo ingress 2> /dev/null 39 + ./flow_dissector_load -d 2> /dev/null 40 + if [ $unmount -ne 0 ]; then 41 + umount bpffs 2> /dev/null 42 + fi 43 + } 44 + 45 + # Exit script immediately (well catched by trap handler) if any 46 + # program/thing exits with a non-zero status. 47 + set -e 48 + 49 + # (Use 'trap -l' to list meaning of numbers) 50 + trap exit_handler 0 2 3 6 9 51 + 52 + # Mount BPF file system 53 + if /bin/mount | grep /sys/fs/bpf > /dev/null; then 54 + echo "bpffs already mounted" 55 + else 56 + echo "bpffs not mounted. Mounting..." 57 + unmount=1 58 + /bin/mount bpffs /sys/fs/bpf -t bpf 59 + fi 60 + 61 + # Attach BPF program 62 + ./flow_dissector_load -p bpf_flow.o -s dissect 63 + 64 + # Setup 65 + tc qdisc add dev lo ingress 66 + 67 + echo "Testing IPv4..." 68 + # Drops all IP/UDP packets coming from port 9 69 + tc filter add dev lo parent ffff: protocol ip pref 1337 flower ip_proto \ 70 + udp src_port 9 action drop 71 + 72 + # Send 10 IPv4/UDP packets from port 8. Filter should not drop any. 73 + ./test_flow_dissector -i 4 -f 8 74 + # Send 10 IPv4/UDP packets from port 9. Filter should drop all. 75 + ./test_flow_dissector -i 4 -f 9 -F 76 + # Send 10 IPv4/UDP packets from port 10. Filter should not drop any. 77 + ./test_flow_dissector -i 4 -f 10 78 + 79 + echo "Testing IPIP..." 80 + # Send 10 IPv4/IPv4/UDP packets from port 8. Filter should not drop any. 81 + ./with_addr.sh ./with_tunnels.sh ./test_flow_dissector -o 4 -e bare -i 4 \ 82 + -D 192.168.0.1 -S 1.1.1.1 -f 8 83 + # Send 10 IPv4/IPv4/UDP packets from port 9. Filter should drop all. 84 + ./with_addr.sh ./with_tunnels.sh ./test_flow_dissector -o 4 -e bare -i 4 \ 85 + -D 192.168.0.1 -S 1.1.1.1 -f 9 -F 86 + # Send 10 IPv4/IPv4/UDP packets from port 10. Filter should not drop any. 87 + ./with_addr.sh ./with_tunnels.sh ./test_flow_dissector -o 4 -e bare -i 4 \ 88 + -D 192.168.0.1 -S 1.1.1.1 -f 10 89 + 90 + echo "Testing IPv4 + GRE..." 91 + # Send 10 IPv4/GRE/IPv4/UDP packets from port 8. Filter should not drop any. 92 + ./with_addr.sh ./with_tunnels.sh ./test_flow_dissector -o 4 -e gre -i 4 \ 93 + -D 192.168.0.1 -S 1.1.1.1 -f 8 94 + # Send 10 IPv4/GRE/IPv4/UDP packets from port 9. Filter should drop all. 95 + ./with_addr.sh ./with_tunnels.sh ./test_flow_dissector -o 4 -e gre -i 4 \ 96 + -D 192.168.0.1 -S 1.1.1.1 -f 9 -F 97 + # Send 10 IPv4/GRE/IPv4/UDP packets from port 10. Filter should not drop any. 98 + ./with_addr.sh ./with_tunnels.sh ./test_flow_dissector -o 4 -e gre -i 4 \ 99 + -D 192.168.0.1 -S 1.1.1.1 -f 10 100 + 101 + tc filter del dev lo ingress pref 1337 102 + 103 + echo "Testing IPv6..." 104 + # Drops all IPv6/UDP packets coming from port 9 105 + tc filter add dev lo parent ffff: protocol ipv6 pref 1337 flower ip_proto \ 106 + udp src_port 9 action drop 107 + 108 + # Send 10 IPv6/UDP packets from port 8. Filter should not drop any. 109 + ./test_flow_dissector -i 6 -f 8 110 + # Send 10 IPv6/UDP packets from port 9. Filter should drop all. 111 + ./test_flow_dissector -i 6 -f 9 -F 112 + # Send 10 IPv6/UDP packets from port 10. Filter should not drop any. 113 + ./test_flow_dissector -i 6 -f 10 114 + 115 + exit 0
+54
tools/testing/selftests/bpf/with_addr.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + # 4 + # add private ipv4 and ipv6 addresses to loopback 5 + 6 + readonly V6_INNER='100::a/128' 7 + readonly V4_INNER='192.168.0.1/32' 8 + 9 + if getopts ":s" opt; then 10 + readonly SIT_DEV_NAME='sixtofourtest0' 11 + readonly V6_SIT='2::/64' 12 + readonly V4_SIT='172.17.0.1/32' 13 + shift 14 + fi 15 + 16 + fail() { 17 + echo "error: $*" 1>&2 18 + exit 1 19 + } 20 + 21 + setup() { 22 + ip -6 addr add "${V6_INNER}" dev lo || fail 'failed to setup v6 address' 23 + ip -4 addr add "${V4_INNER}" dev lo || fail 'failed to setup v4 address' 24 + 25 + if [[ -n "${V6_SIT}" ]]; then 26 + ip link add "${SIT_DEV_NAME}" type sit remote any local any \ 27 + || fail 'failed to add sit' 28 + ip link set dev "${SIT_DEV_NAME}" up \ 29 + || fail 'failed to bring sit device up' 30 + ip -6 addr add "${V6_SIT}" dev "${SIT_DEV_NAME}" \ 31 + || fail 'failed to setup v6 SIT address' 32 + ip -4 addr add "${V4_SIT}" dev "${SIT_DEV_NAME}" \ 33 + || fail 'failed to setup v4 SIT address' 34 + fi 35 + 36 + sleep 2 # avoid race causing bind to fail 37 + } 38 + 39 + cleanup() { 40 + if [[ -n "${V6_SIT}" ]]; then 41 + ip -4 addr del "${V4_SIT}" dev "${SIT_DEV_NAME}" 42 + ip -6 addr del "${V6_SIT}" dev "${SIT_DEV_NAME}" 43 + ip link del "${SIT_DEV_NAME}" 44 + fi 45 + 46 + ip -4 addr del "${V4_INNER}" dev lo 47 + ip -6 addr del "${V6_INNER}" dev lo 48 + } 49 + 50 + trap cleanup EXIT 51 + 52 + setup 53 + "$@" 54 + exit "$?"
+36
tools/testing/selftests/bpf/with_tunnels.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + # 4 + # setup tunnels for flow dissection test 5 + 6 + readonly SUFFIX="test_$(mktemp -u XXXX)" 7 + CONFIG="remote 127.0.0.2 local 127.0.0.1 dev lo" 8 + 9 + setup() { 10 + ip link add "ipip_${SUFFIX}" type ipip ${CONFIG} 11 + ip link add "gre_${SUFFIX}" type gre ${CONFIG} 12 + ip link add "sit_${SUFFIX}" type sit ${CONFIG} 13 + 14 + echo "tunnels before test:" 15 + ip tunnel show 16 + 17 + ip link set "ipip_${SUFFIX}" up 18 + ip link set "gre_${SUFFIX}" up 19 + ip link set "sit_${SUFFIX}" up 20 + } 21 + 22 + 23 + cleanup() { 24 + ip tunnel del "ipip_${SUFFIX}" 25 + ip tunnel del "gre_${SUFFIX}" 26 + ip tunnel del "sit_${SUFFIX}" 27 + 28 + echo "tunnels after test:" 29 + ip tunnel show 30 + } 31 + 32 + trap cleanup EXIT 33 + 34 + setup 35 + "$@" 36 + exit "$?"