diff options
| author | Alexei Starovoitov <ast@kernel.org> | 2019-06-15 12:12:24 -0700 | 
|---|---|---|
| committer | Daniel Borkmann <daniel@iogearbox.net> | 2019-06-19 02:22:52 +0200 | 
| commit | b061017f8b4d0e05d4c11486581a702fb2a975b2 (patch) | |
| tree | 8c8ba40095dc1d4aeb480c454df2abb683925d80 /tools/testing/selftests/bpf/progs/test_xdp_loop.c | |
| parent | 0d3679e99ae4b7868da22e3b8540fd597df501f5 (diff) | |
| download | blackbird-op-linux-b061017f8b4d0e05d4c11486581a702fb2a975b2.tar.gz blackbird-op-linux-b061017f8b4d0e05d4c11486581a702fb2a975b2.zip  | |
selftests/bpf: add realistic loop tests
Add a bunch of loop tests. Most of them are created by replacing
'#pragma unroll' with '#pragma clang loop unroll(disable)'
Several tests are artificially large:
  /* partial unroll. llvm will unroll loop ~150 times.
   * C loop count -> 600.
   * Asm loop count -> 4.
   * 16k insns in loop body.
   * Total of 5 such loops. Total program size ~82k insns.
   */
  "./pyperf600.o",
  /* no unroll at all.
   * C loop count -> 600.
   * ASM loop count -> 600.
   * ~110 insns in loop body.
   * Total of 5 such loops. Total program size ~1500 insns.
   */
  "./pyperf600_nounroll.o",
  /* partial unroll. 19k insn in a loop.
   * Total program size 20.8k insn.
   * ~350k processed_insns
   */
  "./strobemeta.o",
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Andrii Nakryiko <andriin@fb.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Diffstat (limited to 'tools/testing/selftests/bpf/progs/test_xdp_loop.c')
| -rw-r--r-- | tools/testing/selftests/bpf/progs/test_xdp_loop.c | 231 | 
1 files changed, 231 insertions, 0 deletions
diff --git a/tools/testing/selftests/bpf/progs/test_xdp_loop.c b/tools/testing/selftests/bpf/progs/test_xdp_loop.c new file mode 100644 index 000000000000..7fa4677df22e --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_xdp_loop.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Facebook +#include <stddef.h> +#include <string.h> +#include <linux/bpf.h> +#include <linux/if_ether.h> +#include <linux/if_packet.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <linux/in.h> +#include <linux/udp.h> +#include <linux/tcp.h> +#include <linux/pkt_cls.h> +#include <sys/socket.h> +#include "bpf_helpers.h" +#include "bpf_endian.h" +#include "test_iptunnel_common.h" + +int _version SEC("version") = 1; + +struct bpf_map_def SEC("maps") rxcnt = { +	.type = BPF_MAP_TYPE_PERCPU_ARRAY, +	.key_size = sizeof(__u32), +	.value_size = sizeof(__u64), +	.max_entries = 256, +}; + +struct bpf_map_def SEC("maps") vip2tnl = { +	.type = BPF_MAP_TYPE_HASH, +	.key_size = sizeof(struct vip), +	.value_size = sizeof(struct iptnl_info), +	.max_entries = MAX_IPTNL_ENTRIES, +}; + +static __always_inline void count_tx(__u32 protocol) +{ +	__u64 *rxcnt_count; + +	rxcnt_count = bpf_map_lookup_elem(&rxcnt, &protocol); +	if (rxcnt_count) +		*rxcnt_count += 1; +} + +static __always_inline int get_dport(void *trans_data, void *data_end, +				     __u8 protocol) +{ +	struct tcphdr *th; +	struct udphdr *uh; + +	switch (protocol) { +	case IPPROTO_TCP: +		th = (struct tcphdr *)trans_data; +		if (th + 1 > data_end) +			return -1; +		return th->dest; +	case IPPROTO_UDP: +		uh = (struct udphdr *)trans_data; +		if (uh + 1 > data_end) +			return -1; +		return uh->dest; +	default: +		return 0; +	} +} + +static __always_inline void set_ethhdr(struct ethhdr *new_eth, +				       const struct ethhdr *old_eth, +				       const struct iptnl_info *tnl, +				       __be16 h_proto) +{ +	memcpy(new_eth->h_source, old_eth->h_dest, sizeof(new_eth->h_source)); +	memcpy(new_eth->h_dest, tnl->dmac, sizeof(new_eth->h_dest)); +	new_eth->h_proto = h_proto; +} + +static __always_inline int handle_ipv4(struct xdp_md *xdp) +{ +	void *data_end = (void *)(long)xdp->data_end; +	void *data = (void *)(long)xdp->data; +	struct iptnl_info *tnl; +	struct ethhdr *new_eth; +	struct ethhdr *old_eth; +	struct iphdr *iph = data + sizeof(struct ethhdr); +	__u16 *next_iph; +	__u16 payload_len; +	struct vip vip = {}; +	int dport; +	__u32 csum = 0; +	int i; + +	if (iph + 1 > data_end) +		return XDP_DROP; + +	dport = get_dport(iph + 1, data_end, iph->protocol); +	if (dport == -1) +		return XDP_DROP; + +	vip.protocol = iph->protocol; +	vip.family = AF_INET; +	vip.daddr.v4 = iph->daddr; +	vip.dport = dport; +	payload_len = bpf_ntohs(iph->tot_len); + +	tnl = bpf_map_lookup_elem(&vip2tnl, &vip); +	/* It only does v4-in-v4 */ +	if (!tnl || tnl->family != AF_INET) +		return XDP_PASS; + +	if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(struct iphdr))) +		return XDP_DROP; + +	data = (void *)(long)xdp->data; +	data_end = (void *)(long)xdp->data_end; + +	new_eth = data; +	iph = data + sizeof(*new_eth); +	old_eth = data + sizeof(*iph); + +	if (new_eth + 1 > data_end || +	    old_eth + 1 > data_end || +	    iph + 1 > data_end) +		return XDP_DROP; + +	set_ethhdr(new_eth, old_eth, tnl, bpf_htons(ETH_P_IP)); + +	iph->version = 4; +	iph->ihl = sizeof(*iph) >> 2; +	iph->frag_off =	0; +	iph->protocol = IPPROTO_IPIP; +	iph->check = 0; +	iph->tos = 0; +	iph->tot_len = bpf_htons(payload_len + sizeof(*iph)); +	iph->daddr = tnl->daddr.v4; +	iph->saddr = tnl->saddr.v4; +	iph->ttl = 8; + +	next_iph = (__u16 *)iph; +#pragma clang loop unroll(disable) +	for (i = 0; i < sizeof(*iph) >> 1; i++) +		csum += *next_iph++; + +	iph->check = ~((csum & 0xffff) + (csum >> 16)); + +	count_tx(vip.protocol); + +	return XDP_TX; +} + +static __always_inline int handle_ipv6(struct xdp_md *xdp) +{ +	void *data_end = (void *)(long)xdp->data_end; +	void *data = (void *)(long)xdp->data; +	struct iptnl_info *tnl; +	struct ethhdr *new_eth; +	struct ethhdr *old_eth; +	struct ipv6hdr *ip6h = data + sizeof(struct ethhdr); +	__u16 payload_len; +	struct vip vip = {}; +	int dport; + +	if (ip6h + 1 > data_end) +		return XDP_DROP; + +	dport = get_dport(ip6h + 1, data_end, ip6h->nexthdr); +	if (dport == -1) +		return XDP_DROP; + +	vip.protocol = ip6h->nexthdr; +	vip.family = AF_INET6; +	memcpy(vip.daddr.v6, ip6h->daddr.s6_addr32, sizeof(vip.daddr)); +	vip.dport = dport; +	payload_len = ip6h->payload_len; + +	tnl = bpf_map_lookup_elem(&vip2tnl, &vip); +	/* It only does v6-in-v6 */ +	if (!tnl || tnl->family != AF_INET6) +		return XDP_PASS; + +	if (bpf_xdp_adjust_head(xdp, 0 - (int)sizeof(struct ipv6hdr))) +		return XDP_DROP; + +	data = (void *)(long)xdp->data; +	data_end = (void *)(long)xdp->data_end; + +	new_eth = data; +	ip6h = data + sizeof(*new_eth); +	old_eth = data + sizeof(*ip6h); + +	if (new_eth + 1 > data_end || old_eth + 1 > data_end || +	    ip6h + 1 > data_end) +		return XDP_DROP; + +	set_ethhdr(new_eth, old_eth, tnl, bpf_htons(ETH_P_IPV6)); + +	ip6h->version = 6; +	ip6h->priority = 0; +	memset(ip6h->flow_lbl, 0, sizeof(ip6h->flow_lbl)); +	ip6h->payload_len = bpf_htons(bpf_ntohs(payload_len) + sizeof(*ip6h)); +	ip6h->nexthdr = IPPROTO_IPV6; +	ip6h->hop_limit = 8; +	memcpy(ip6h->saddr.s6_addr32, tnl->saddr.v6, sizeof(tnl->saddr.v6)); +	memcpy(ip6h->daddr.s6_addr32, tnl->daddr.v6, sizeof(tnl->daddr.v6)); + +	count_tx(vip.protocol); + +	return XDP_TX; +} + +SEC("xdp_tx_iptunnel") +int _xdp_tx_iptunnel(struct xdp_md *xdp) +{ +	void *data_end = (void *)(long)xdp->data_end; +	void *data = (void *)(long)xdp->data; +	struct ethhdr *eth = data; +	__u16 h_proto; + +	if (eth + 1 > data_end) +		return XDP_DROP; + +	h_proto = eth->h_proto; + +	if (h_proto == bpf_htons(ETH_P_IP)) +		return handle_ipv4(xdp); +	else if (h_proto == bpf_htons(ETH_P_IPV6)) + +		return handle_ipv6(xdp); +	else +		return XDP_DROP; +} + +char _license[] SEC("license") = "GPL";  | 

