On 11/13, Alexis Lothoré (eBPF Foundation) wrote:
test_flow_dissector.sh loads flow_dissector program and subprograms, creates and configured relevant tunnels and interfaces, and ensure that the bpf dissection is actually performed correctly. Similar tests exist in test_progs (thanks to flow_dissector.c) and run the same programs, but those are only executed with BPF_PROG_RUN: those tests are then missing some coverage (eg: coverage for flow keys manipulated when the configured flower uses a port range, which has a dedicated test in test_flow_dissector.sh)
Convert test_flow_dissector.sh into test_progs so that the corresponding tests are also run in CI.
Signed-off-by: Alexis Lothoré (eBPF Foundation) alexis.lothore@bootlin.com
The content of this new test is heavily based on the initial test_flow_dissector.c. I have kept most of the packet build function (even if not all packet types are used for the test) to allow extending the test later if needed.
The new test has been executed in a local x86 qemu environment as well as in CI:
# ./test_progs -a flow_dissector_classification #102/1 flow_dissector_classification/ipv4:OK #102/2 flow_dissector_classification/ipv4_continue_dissect:OK #102/3 flow_dissector_classification/ipip:OK #102/4 flow_dissector_classification/gre:OK #102/5 flow_dissector_classification/port_range:OK #102/6 flow_dissector_classification/ipv6:OK
#102 flow_dissector_classification:OK
.../bpf/prog_tests/flow_dissector_classification.c | 851 +++++++++++++++++++++ 1 file changed, 851 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/flow_dissector_classification.c b/tools/testing/selftests/bpf/prog_tests/flow_dissector_classification.c new file mode 100644 index 0000000000000000000000000000000000000000..de90c3e7b6a4b1890c380e384a255b030014a21d --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/flow_dissector_classification.c @@ -0,0 +1,851 @@ +// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <bpf/bpf.h> +#include <linux/bpf.h> +#include <bpf/libbpf.h> +#include <arpa/inet.h> +#include <asm/byteorder.h> +#include <netinet/udp.h> +#include <poll.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <unistd.h> +#include "test_progs.h" +#include "bpf_util.h" +#include "bpf_flow.skel.h"
+#define CFG_PORT_INNER 8000 +#define CFG_PORT_GUE 6080 +#define SUBTEST_NAME_MAX_LEN 32 +#define TEST_NAME_MAX_LEN (32 + SUBTEST_NAME_MAX_LEN) +#define MAX_SOURCE_PORTS 3 +#define TEST_PACKETS_COUNT 10 +#define TEST_PACKET_LEN 100 +#define TEST_PACKET_PATTERN 'a' +#define TEST_IPV4 "192.168.0.1/32" +#define TEST_IPV6 "100::a/128" +#define TEST_TUNNEL_REMOTE "127.0.0.2" +#define TEST_TUNNEL_LOCAL "127.0.0.1"
+#define INIT_ADDR4(addr4, port) \
- { \
.sin_family = AF_INET, \
.sin_port = __constant_htons(port), \
.sin_addr.s_addr = __constant_htonl(addr4), \
- }
+#define INIT_ADDR6(addr6, port) \
- { \
.sin6_family = AF_INET6, \
.sin6_port = __constant_htons(port), \
.sin6_addr = addr6, \
- }
+#define TEST_IN4_SRC_ADDR_DEFAULT INIT_ADDR4(INADDR_LOOPBACK + 2, 0) +#define TEST_IN4_DST_ADDR_DEFAULT INIT_ADDR4(INADDR_LOOPBACK, CFG_PORT_INNER) +#define TEST_OUT4_SRC_ADDR_DEFAULT INIT_ADDR4(INADDR_LOOPBACK + 1, 0) +#define TEST_OUT4_DST_ADDR_DEFAULT INIT_ADDR4(INADDR_LOOPBACK, 0)
+#define TEST_IN6_SRC_ADDR_DEFAULT INIT_ADDR6(IN6ADDR_LOOPBACK_INIT, 0) +#define TEST_IN6_DST_ADDR_DEFAULT \
- INIT_ADDR6(IN6ADDR_LOOPBACK_INIT, CFG_PORT_INNER)
+#define TEST_OUT6_SRC_ADDR_DEFAULT INIT_ADDR6(IN6ADDR_LOOPBACK_INIT, 0) +#define TEST_OUT6_DST_ADDR_DEFAULT INIT_ADDR6(IN6ADDR_LOOPBACK_INIT, 0)
+#define TEST_IN4_SRC_ADDR_DISSECT_CONTINUE INIT_ADDR4(INADDR_LOOPBACK + 126, 0) +#define TEST_IN4_SRC_ADDR_IPIP INIT_ADDR4((in_addr_t)0x01010101, 0) +#define TEST_IN4_DST_ADDR_IPIP INIT_ADDR4((in_addr_t)0xC0A80001, CFG_PORT_INNER)
+struct grehdr {
- uint16_t unused;
- uint16_t protocol;
+} __packed;
+struct guehdr {
- union {
struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 hlen : 5, control : 1, version : 2;
+#elif defined(__BIG_ENDIAN_BITFIELD)
__u8 version : 2, control : 1, hlen : 5;
+#else +#error "Please fix <asm/byteorder.h>" +#endif
__u8 proto_ctype;
__be16 flags;
};
__be32 word;
- };
+};
+static char buf[ETH_DATA_LEN];
+struct test_configuration {
- char name[SUBTEST_NAME_MAX_LEN];
- int (*test_setup)(void);
- void (*test_teardown)(void);
- int source_ports[MAX_SOURCE_PORTS];
- int cfg_l3_inner;
- struct sockaddr_in in_saddr4;
- struct sockaddr_in in_daddr4;
- struct sockaddr_in6 in_saddr6;
- struct sockaddr_in6 in_daddr6;
- int cfg_l3_outer;
- struct sockaddr_in out_saddr4;
- struct sockaddr_in out_daddr4;
- struct sockaddr_in6 out_saddr6;
- struct sockaddr_in6 out_daddr6;
- int cfg_encap_proto;
- uint8_t cfg_dsfield_inner;
- uint8_t cfg_dsfield_outer;
- int cfg_l3_extra;
- struct sockaddr_in extra_saddr4;
- struct sockaddr_in extra_daddr4;
- struct sockaddr_in6 extra_saddr6;
- struct sockaddr_in6 extra_daddr6;
+};
+static unsigned long util_gettime(void) +{
- struct timeval tv;
- gettimeofday(&tv, NULL);
- return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+}
[..]
+static unsigned long add_csum_hword(const uint16_t *start, int num_u16) +{
- unsigned long sum = 0;
- int i;
- for (i = 0; i < num_u16; i++)
sum += start[i];
- return sum;
+}
+static uint16_t build_ip_csum(const uint16_t *start, int num_u16,
unsigned long sum)
+{
- sum += add_csum_hword(start, num_u16);
- while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
- return ~sum;
+}
+static void build_ipv4_header(void *header, uint8_t proto, uint32_t src,
uint32_t dst, int payload_len, uint8_t tos)
+{
- struct iphdr *iph = header;
- iph->ihl = 5;
- iph->version = 4;
- iph->tos = tos;
- iph->ttl = 8;
- iph->tot_len = htons(sizeof(*iph) + payload_len);
- iph->id = htons(1337);
- iph->protocol = proto;
- iph->saddr = src;
- iph->daddr = dst;
- iph->check = build_ip_csum((void *)iph, iph->ihl << 1, 0);
+}
+static void ipv6_set_dsfield(struct ipv6hdr *ip6h, uint8_t dsfield) +{
- uint16_t val, *ptr = (uint16_t *)ip6h;
- val = ntohs(*ptr);
- val &= 0xF00F;
- val |= ((uint16_t)dsfield) << 4;
- *ptr = htons(val);
+}
+static void build_ipv6_header(void *header, uint8_t proto,
struct sockaddr_in6 *src,
struct sockaddr_in6 *dst, int payload_len,
uint8_t dsfield)
+{
- struct ipv6hdr *ip6h = header;
- ip6h->version = 6;
- ip6h->payload_len = htons(payload_len);
- ip6h->nexthdr = proto;
- ip6h->hop_limit = 8;
- ipv6_set_dsfield(ip6h, dsfield);
- memcpy(&ip6h->saddr, &src->sin6_addr, sizeof(ip6h->saddr));
- memcpy(&ip6h->daddr, &dst->sin6_addr, sizeof(ip6h->daddr));
+}
+static uint16_t build_udp_v4_csum(const struct iphdr *iph,
const struct udphdr *udph, int num_words)
+{
- unsigned long pseudo_sum;
- int num_u16 = sizeof(iph->saddr); /* halfwords: twice byte len */
- pseudo_sum = add_csum_hword((void *)&iph->saddr, num_u16);
- pseudo_sum += htons(IPPROTO_UDP);
- pseudo_sum += udph->len;
- return build_ip_csum((void *)udph, num_words, pseudo_sum);
+}
+static uint16_t build_udp_v6_csum(const struct ipv6hdr *ip6h,
const struct udphdr *udph, int num_words)
+{
- unsigned long pseudo_sum;
- int num_u16 = sizeof(ip6h->saddr); /* halfwords: twice byte len */
- pseudo_sum = add_csum_hword((void *)&ip6h->saddr, num_u16);
- pseudo_sum += htons(ip6h->nexthdr);
- pseudo_sum += ip6h->payload_len;
- return build_ip_csum((void *)udph, num_words, pseudo_sum);
+}
I remember adding a bunch of similar code to tools/testing/selftests/bpf/prog_tests/xdp_metadata.c and tools/testing/selftests/bpf/network_helpers.h. The csum handling in particular (csum_tcpudp_magic/etc for pseudo headers). Can you see if something can be reused? Maybe something we can move into network_helpers.h? For example build_ip_csum/ip_csum.