diff options
author | Alexei Starovoitov <ast@fb.com> | 2017-12-14 20:55:11 -0500 |
---|---|---|
committer | Daniel Borkmann <daniel@iogearbox.net> | 2017-12-17 14:34:36 -0500 |
commit | 3bc35c63cb70466c78d3972ceaf8205aa463a192 (patch) | |
tree | 0ff4d6d6f950bdd224a919a02c6a2cbd7ad282dd /tools | |
parent | 48cca7e44f9f8268fdcd4351e2f19ff2275119d1 (diff) |
selftests/bpf: add bpf_call test
strip always_inline from test_l4lb.c and compile it with -fno-inline
to let verifier go through 11 function with various function arguments
and return values
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/testing/selftests/bpf/Makefile | 11 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/test_l4lb_noinline.c | 473 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/test_progs.c | 14 |
3 files changed, 492 insertions, 6 deletions
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 255fb1f50f6b..6970d073df5b 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile | |||
@@ -17,7 +17,8 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test | |||
17 | 17 | ||
18 | TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \ | 18 | TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \ |
19 | test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \ | 19 | test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \ |
20 | sockmap_verdict_prog.o dev_cgroup.o sample_ret0.o test_tracepoint.o | 20 | sockmap_verdict_prog.o dev_cgroup.o sample_ret0.o test_tracepoint.o \ |
21 | test_l4lb_noinline.o | ||
21 | 22 | ||
22 | TEST_PROGS := test_kmod.sh test_xdp_redirect.sh test_xdp_meta.sh \ | 23 | TEST_PROGS := test_kmod.sh test_xdp_redirect.sh test_xdp_meta.sh \ |
23 | test_offload.py | 24 | test_offload.py |
@@ -49,8 +50,12 @@ else | |||
49 | CPU ?= generic | 50 | CPU ?= generic |
50 | endif | 51 | endif |
51 | 52 | ||
53 | CLANG_FLAGS = -I. -I./include/uapi -I../../../include/uapi \ | ||
54 | -Wno-compare-distinct-pointer-types | ||
55 | |||
56 | $(OUTPUT)/test_l4lb_noinline.o: CLANG_FLAGS += -fno-inline | ||
57 | |||
52 | %.o: %.c | 58 | %.o: %.c |
53 | $(CLANG) -I. -I./include/uapi -I../../../include/uapi \ | 59 | $(CLANG) $(CLANG_FLAGS) \ |
54 | -Wno-compare-distinct-pointer-types \ | ||
55 | -O2 -target bpf -emit-llvm -c $< -o - | \ | 60 | -O2 -target bpf -emit-llvm -c $< -o - | \ |
56 | $(LLC) -march=bpf -mcpu=$(CPU) -filetype=obj -o $@ | 61 | $(LLC) -march=bpf -mcpu=$(CPU) -filetype=obj -o $@ |
diff --git a/tools/testing/selftests/bpf/test_l4lb_noinline.c b/tools/testing/selftests/bpf/test_l4lb_noinline.c new file mode 100644 index 000000000000..ba44a14e6dc4 --- /dev/null +++ b/tools/testing/selftests/bpf/test_l4lb_noinline.c | |||
@@ -0,0 +1,473 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (c) 2017 Facebook | ||
3 | #include <stddef.h> | ||
4 | #include <stdbool.h> | ||
5 | #include <string.h> | ||
6 | #include <linux/pkt_cls.h> | ||
7 | #include <linux/bpf.h> | ||
8 | #include <linux/in.h> | ||
9 | #include <linux/if_ether.h> | ||
10 | #include <linux/ip.h> | ||
11 | #include <linux/ipv6.h> | ||
12 | #include <linux/icmp.h> | ||
13 | #include <linux/icmpv6.h> | ||
14 | #include <linux/tcp.h> | ||
15 | #include <linux/udp.h> | ||
16 | #include "bpf_helpers.h" | ||
17 | #include "test_iptunnel_common.h" | ||
18 | #include "bpf_endian.h" | ||
19 | |||
20 | int _version SEC("version") = 1; | ||
21 | |||
22 | static __u32 rol32(__u32 word, unsigned int shift) | ||
23 | { | ||
24 | return (word << shift) | (word >> ((-shift) & 31)); | ||
25 | } | ||
26 | |||
27 | /* copy paste of jhash from kernel sources to make sure llvm | ||
28 | * can compile it into valid sequence of bpf instructions | ||
29 | */ | ||
30 | #define __jhash_mix(a, b, c) \ | ||
31 | { \ | ||
32 | a -= c; a ^= rol32(c, 4); c += b; \ | ||
33 | b -= a; b ^= rol32(a, 6); a += c; \ | ||
34 | c -= b; c ^= rol32(b, 8); b += a; \ | ||
35 | a -= c; a ^= rol32(c, 16); c += b; \ | ||
36 | b -= a; b ^= rol32(a, 19); a += c; \ | ||
37 | c -= b; c ^= rol32(b, 4); b += a; \ | ||
38 | } | ||
39 | |||
40 | #define __jhash_final(a, b, c) \ | ||
41 | { \ | ||
42 | c ^= b; c -= rol32(b, 14); \ | ||
43 | a ^= c; a -= rol32(c, 11); \ | ||
44 | b ^= a; b -= rol32(a, 25); \ | ||
45 | c ^= b; c -= rol32(b, 16); \ | ||
46 | a ^= c; a -= rol32(c, 4); \ | ||
47 | b ^= a; b -= rol32(a, 14); \ | ||
48 | c ^= b; c -= rol32(b, 24); \ | ||
49 | } | ||
50 | |||
51 | #define JHASH_INITVAL 0xdeadbeef | ||
52 | |||
53 | typedef unsigned int u32; | ||
54 | |||
55 | static u32 jhash(const void *key, u32 length, u32 initval) | ||
56 | { | ||
57 | u32 a, b, c; | ||
58 | const unsigned char *k = key; | ||
59 | |||
60 | a = b = c = JHASH_INITVAL + length + initval; | ||
61 | |||
62 | while (length > 12) { | ||
63 | a += *(u32 *)(k); | ||
64 | b += *(u32 *)(k + 4); | ||
65 | c += *(u32 *)(k + 8); | ||
66 | __jhash_mix(a, b, c); | ||
67 | length -= 12; | ||
68 | k += 12; | ||
69 | } | ||
70 | switch (length) { | ||
71 | case 12: c += (u32)k[11]<<24; | ||
72 | case 11: c += (u32)k[10]<<16; | ||
73 | case 10: c += (u32)k[9]<<8; | ||
74 | case 9: c += k[8]; | ||
75 | case 8: b += (u32)k[7]<<24; | ||
76 | case 7: b += (u32)k[6]<<16; | ||
77 | case 6: b += (u32)k[5]<<8; | ||
78 | case 5: b += k[4]; | ||
79 | case 4: a += (u32)k[3]<<24; | ||
80 | case 3: a += (u32)k[2]<<16; | ||
81 | case 2: a += (u32)k[1]<<8; | ||
82 | case 1: a += k[0]; | ||
83 | __jhash_final(a, b, c); | ||
84 | case 0: /* Nothing left to add */ | ||
85 | break; | ||
86 | } | ||
87 | |||
88 | return c; | ||
89 | } | ||
90 | |||
91 | static u32 __jhash_nwords(u32 a, u32 b, u32 c, u32 initval) | ||
92 | { | ||
93 | a += initval; | ||
94 | b += initval; | ||
95 | c += initval; | ||
96 | __jhash_final(a, b, c); | ||
97 | return c; | ||
98 | } | ||
99 | |||
100 | static u32 jhash_2words(u32 a, u32 b, u32 initval) | ||
101 | { | ||
102 | return __jhash_nwords(a, b, 0, initval + JHASH_INITVAL + (2 << 2)); | ||
103 | } | ||
104 | |||
105 | #define PCKT_FRAGMENTED 65343 | ||
106 | #define IPV4_HDR_LEN_NO_OPT 20 | ||
107 | #define IPV4_PLUS_ICMP_HDR 28 | ||
108 | #define IPV6_PLUS_ICMP_HDR 48 | ||
109 | #define RING_SIZE 2 | ||
110 | #define MAX_VIPS 12 | ||
111 | #define MAX_REALS 5 | ||
112 | #define CTL_MAP_SIZE 16 | ||
113 | #define CH_RINGS_SIZE (MAX_VIPS * RING_SIZE) | ||
114 | #define F_IPV6 (1 << 0) | ||
115 | #define F_HASH_NO_SRC_PORT (1 << 0) | ||
116 | #define F_ICMP (1 << 0) | ||
117 | #define F_SYN_SET (1 << 1) | ||
118 | |||
119 | struct packet_description { | ||
120 | union { | ||
121 | __be32 src; | ||
122 | __be32 srcv6[4]; | ||
123 | }; | ||
124 | union { | ||
125 | __be32 dst; | ||
126 | __be32 dstv6[4]; | ||
127 | }; | ||
128 | union { | ||
129 | __u32 ports; | ||
130 | __u16 port16[2]; | ||
131 | }; | ||
132 | __u8 proto; | ||
133 | __u8 flags; | ||
134 | }; | ||
135 | |||
136 | struct ctl_value { | ||
137 | union { | ||
138 | __u64 value; | ||
139 | __u32 ifindex; | ||
140 | __u8 mac[6]; | ||
141 | }; | ||
142 | }; | ||
143 | |||
144 | struct vip_meta { | ||
145 | __u32 flags; | ||
146 | __u32 vip_num; | ||
147 | }; | ||
148 | |||
149 | struct real_definition { | ||
150 | union { | ||
151 | __be32 dst; | ||
152 | __be32 dstv6[4]; | ||
153 | }; | ||
154 | __u8 flags; | ||
155 | }; | ||
156 | |||
157 | struct vip_stats { | ||
158 | __u64 bytes; | ||
159 | __u64 pkts; | ||
160 | }; | ||
161 | |||
162 | struct eth_hdr { | ||
163 | unsigned char eth_dest[ETH_ALEN]; | ||
164 | unsigned char eth_source[ETH_ALEN]; | ||
165 | unsigned short eth_proto; | ||
166 | }; | ||
167 | |||
168 | struct bpf_map_def SEC("maps") vip_map = { | ||
169 | .type = BPF_MAP_TYPE_HASH, | ||
170 | .key_size = sizeof(struct vip), | ||
171 | .value_size = sizeof(struct vip_meta), | ||
172 | .max_entries = MAX_VIPS, | ||
173 | }; | ||
174 | |||
175 | struct bpf_map_def SEC("maps") ch_rings = { | ||
176 | .type = BPF_MAP_TYPE_ARRAY, | ||
177 | .key_size = sizeof(__u32), | ||
178 | .value_size = sizeof(__u32), | ||
179 | .max_entries = CH_RINGS_SIZE, | ||
180 | }; | ||
181 | |||
182 | struct bpf_map_def SEC("maps") reals = { | ||
183 | .type = BPF_MAP_TYPE_ARRAY, | ||
184 | .key_size = sizeof(__u32), | ||
185 | .value_size = sizeof(struct real_definition), | ||
186 | .max_entries = MAX_REALS, | ||
187 | }; | ||
188 | |||
189 | struct bpf_map_def SEC("maps") stats = { | ||
190 | .type = BPF_MAP_TYPE_PERCPU_ARRAY, | ||
191 | .key_size = sizeof(__u32), | ||
192 | .value_size = sizeof(struct vip_stats), | ||
193 | .max_entries = MAX_VIPS, | ||
194 | }; | ||
195 | |||
196 | struct bpf_map_def SEC("maps") ctl_array = { | ||
197 | .type = BPF_MAP_TYPE_ARRAY, | ||
198 | .key_size = sizeof(__u32), | ||
199 | .value_size = sizeof(struct ctl_value), | ||
200 | .max_entries = CTL_MAP_SIZE, | ||
201 | }; | ||
202 | |||
203 | static __u32 get_packet_hash(struct packet_description *pckt, | ||
204 | bool ipv6) | ||
205 | { | ||
206 | if (ipv6) | ||
207 | return jhash_2words(jhash(pckt->srcv6, 16, MAX_VIPS), | ||
208 | pckt->ports, CH_RINGS_SIZE); | ||
209 | else | ||
210 | return jhash_2words(pckt->src, pckt->ports, CH_RINGS_SIZE); | ||
211 | } | ||
212 | |||
213 | static bool get_packet_dst(struct real_definition **real, | ||
214 | struct packet_description *pckt, | ||
215 | struct vip_meta *vip_info, | ||
216 | bool is_ipv6) | ||
217 | { | ||
218 | __u32 hash = get_packet_hash(pckt, is_ipv6); | ||
219 | __u32 key = RING_SIZE * vip_info->vip_num + hash % RING_SIZE; | ||
220 | __u32 *real_pos; | ||
221 | |||
222 | if (hash != 0x358459b7 /* jhash of ipv4 packet */ && | ||
223 | hash != 0x2f4bc6bb /* jhash of ipv6 packet */) | ||
224 | return 0; | ||
225 | |||
226 | real_pos = bpf_map_lookup_elem(&ch_rings, &key); | ||
227 | if (!real_pos) | ||
228 | return false; | ||
229 | key = *real_pos; | ||
230 | *real = bpf_map_lookup_elem(&reals, &key); | ||
231 | if (!(*real)) | ||
232 | return false; | ||
233 | return true; | ||
234 | } | ||
235 | |||
236 | static int parse_icmpv6(void *data, void *data_end, __u64 off, | ||
237 | struct packet_description *pckt) | ||
238 | { | ||
239 | struct icmp6hdr *icmp_hdr; | ||
240 | struct ipv6hdr *ip6h; | ||
241 | |||
242 | icmp_hdr = data + off; | ||
243 | if (icmp_hdr + 1 > data_end) | ||
244 | return TC_ACT_SHOT; | ||
245 | if (icmp_hdr->icmp6_type != ICMPV6_PKT_TOOBIG) | ||
246 | return TC_ACT_OK; | ||
247 | off += sizeof(struct icmp6hdr); | ||
248 | ip6h = data + off; | ||
249 | if (ip6h + 1 > data_end) | ||
250 | return TC_ACT_SHOT; | ||
251 | pckt->proto = ip6h->nexthdr; | ||
252 | pckt->flags |= F_ICMP; | ||
253 | memcpy(pckt->srcv6, ip6h->daddr.s6_addr32, 16); | ||
254 | memcpy(pckt->dstv6, ip6h->saddr.s6_addr32, 16); | ||
255 | return TC_ACT_UNSPEC; | ||
256 | } | ||
257 | |||
258 | static int parse_icmp(void *data, void *data_end, __u64 off, | ||
259 | struct packet_description *pckt) | ||
260 | { | ||
261 | struct icmphdr *icmp_hdr; | ||
262 | struct iphdr *iph; | ||
263 | |||
264 | icmp_hdr = data + off; | ||
265 | if (icmp_hdr + 1 > data_end) | ||
266 | return TC_ACT_SHOT; | ||
267 | if (icmp_hdr->type != ICMP_DEST_UNREACH || | ||
268 | icmp_hdr->code != ICMP_FRAG_NEEDED) | ||
269 | return TC_ACT_OK; | ||
270 | off += sizeof(struct icmphdr); | ||
271 | iph = data + off; | ||
272 | if (iph + 1 > data_end) | ||
273 | return TC_ACT_SHOT; | ||
274 | if (iph->ihl != 5) | ||
275 | return TC_ACT_SHOT; | ||
276 | pckt->proto = iph->protocol; | ||
277 | pckt->flags |= F_ICMP; | ||
278 | pckt->src = iph->daddr; | ||
279 | pckt->dst = iph->saddr; | ||
280 | return TC_ACT_UNSPEC; | ||
281 | } | ||
282 | |||
283 | static bool parse_udp(void *data, __u64 off, void *data_end, | ||
284 | struct packet_description *pckt) | ||
285 | { | ||
286 | struct udphdr *udp; | ||
287 | udp = data + off; | ||
288 | |||
289 | if (udp + 1 > data_end) | ||
290 | return false; | ||
291 | |||
292 | if (!(pckt->flags & F_ICMP)) { | ||
293 | pckt->port16[0] = udp->source; | ||
294 | pckt->port16[1] = udp->dest; | ||
295 | } else { | ||
296 | pckt->port16[0] = udp->dest; | ||
297 | pckt->port16[1] = udp->source; | ||
298 | } | ||
299 | return true; | ||
300 | } | ||
301 | |||
302 | static bool parse_tcp(void *data, __u64 off, void *data_end, | ||
303 | struct packet_description *pckt) | ||
304 | { | ||
305 | struct tcphdr *tcp; | ||
306 | |||
307 | tcp = data + off; | ||
308 | if (tcp + 1 > data_end) | ||
309 | return false; | ||
310 | |||
311 | if (tcp->syn) | ||
312 | pckt->flags |= F_SYN_SET; | ||
313 | |||
314 | if (!(pckt->flags & F_ICMP)) { | ||
315 | pckt->port16[0] = tcp->source; | ||
316 | pckt->port16[1] = tcp->dest; | ||
317 | } else { | ||
318 | pckt->port16[0] = tcp->dest; | ||
319 | pckt->port16[1] = tcp->source; | ||
320 | } | ||
321 | return true; | ||
322 | } | ||
323 | |||
324 | static int process_packet(void *data, __u64 off, void *data_end, | ||
325 | bool is_ipv6, struct __sk_buff *skb) | ||
326 | { | ||
327 | void *pkt_start = (void *)(long)skb->data; | ||
328 | struct packet_description pckt = {}; | ||
329 | struct eth_hdr *eth = pkt_start; | ||
330 | struct bpf_tunnel_key tkey = {}; | ||
331 | struct vip_stats *data_stats; | ||
332 | struct real_definition *dst; | ||
333 | struct vip_meta *vip_info; | ||
334 | struct ctl_value *cval; | ||
335 | __u32 v4_intf_pos = 1; | ||
336 | __u32 v6_intf_pos = 2; | ||
337 | struct ipv6hdr *ip6h; | ||
338 | struct vip vip = {}; | ||
339 | struct iphdr *iph; | ||
340 | int tun_flag = 0; | ||
341 | __u16 pkt_bytes; | ||
342 | __u64 iph_len; | ||
343 | __u32 ifindex; | ||
344 | __u8 protocol; | ||
345 | __u32 vip_num; | ||
346 | int action; | ||
347 | |||
348 | tkey.tunnel_ttl = 64; | ||
349 | if (is_ipv6) { | ||
350 | ip6h = data + off; | ||
351 | if (ip6h + 1 > data_end) | ||
352 | return TC_ACT_SHOT; | ||
353 | |||
354 | iph_len = sizeof(struct ipv6hdr); | ||
355 | protocol = ip6h->nexthdr; | ||
356 | pckt.proto = protocol; | ||
357 | pkt_bytes = bpf_ntohs(ip6h->payload_len); | ||
358 | off += iph_len; | ||
359 | if (protocol == IPPROTO_FRAGMENT) { | ||
360 | return TC_ACT_SHOT; | ||
361 | } else if (protocol == IPPROTO_ICMPV6) { | ||
362 | action = parse_icmpv6(data, data_end, off, &pckt); | ||
363 | if (action >= 0) | ||
364 | return action; | ||
365 | off += IPV6_PLUS_ICMP_HDR; | ||
366 | } else { | ||
367 | memcpy(pckt.srcv6, ip6h->saddr.s6_addr32, 16); | ||
368 | memcpy(pckt.dstv6, ip6h->daddr.s6_addr32, 16); | ||
369 | } | ||
370 | } else { | ||
371 | iph = data + off; | ||
372 | if (iph + 1 > data_end) | ||
373 | return TC_ACT_SHOT; | ||
374 | if (iph->ihl != 5) | ||
375 | return TC_ACT_SHOT; | ||
376 | |||
377 | protocol = iph->protocol; | ||
378 | pckt.proto = protocol; | ||
379 | pkt_bytes = bpf_ntohs(iph->tot_len); | ||
380 | off += IPV4_HDR_LEN_NO_OPT; | ||
381 | |||
382 | if (iph->frag_off & PCKT_FRAGMENTED) | ||
383 | return TC_ACT_SHOT; | ||
384 | if (protocol == IPPROTO_ICMP) { | ||
385 | action = parse_icmp(data, data_end, off, &pckt); | ||
386 | if (action >= 0) | ||
387 | return action; | ||
388 | off += IPV4_PLUS_ICMP_HDR; | ||
389 | } else { | ||
390 | pckt.src = iph->saddr; | ||
391 | pckt.dst = iph->daddr; | ||
392 | } | ||
393 | } | ||
394 | protocol = pckt.proto; | ||
395 | |||
396 | if (protocol == IPPROTO_TCP) { | ||
397 | if (!parse_tcp(data, off, data_end, &pckt)) | ||
398 | return TC_ACT_SHOT; | ||
399 | } else if (protocol == IPPROTO_UDP) { | ||
400 | if (!parse_udp(data, off, data_end, &pckt)) | ||
401 | return TC_ACT_SHOT; | ||
402 | } else { | ||
403 | return TC_ACT_SHOT; | ||
404 | } | ||
405 | |||
406 | if (is_ipv6) | ||
407 | memcpy(vip.daddr.v6, pckt.dstv6, 16); | ||
408 | else | ||
409 | vip.daddr.v4 = pckt.dst; | ||
410 | |||
411 | vip.dport = pckt.port16[1]; | ||
412 | vip.protocol = pckt.proto; | ||
413 | vip_info = bpf_map_lookup_elem(&vip_map, &vip); | ||
414 | if (!vip_info) { | ||
415 | vip.dport = 0; | ||
416 | vip_info = bpf_map_lookup_elem(&vip_map, &vip); | ||
417 | if (!vip_info) | ||
418 | return TC_ACT_SHOT; | ||
419 | pckt.port16[1] = 0; | ||
420 | } | ||
421 | |||
422 | if (vip_info->flags & F_HASH_NO_SRC_PORT) | ||
423 | pckt.port16[0] = 0; | ||
424 | |||
425 | if (!get_packet_dst(&dst, &pckt, vip_info, is_ipv6)) | ||
426 | return TC_ACT_SHOT; | ||
427 | |||
428 | if (dst->flags & F_IPV6) { | ||
429 | cval = bpf_map_lookup_elem(&ctl_array, &v6_intf_pos); | ||
430 | if (!cval) | ||
431 | return TC_ACT_SHOT; | ||
432 | ifindex = cval->ifindex; | ||
433 | memcpy(tkey.remote_ipv6, dst->dstv6, 16); | ||
434 | tun_flag = BPF_F_TUNINFO_IPV6; | ||
435 | } else { | ||
436 | cval = bpf_map_lookup_elem(&ctl_array, &v4_intf_pos); | ||
437 | if (!cval) | ||
438 | return TC_ACT_SHOT; | ||
439 | ifindex = cval->ifindex; | ||
440 | tkey.remote_ipv4 = dst->dst; | ||
441 | } | ||
442 | vip_num = vip_info->vip_num; | ||
443 | data_stats = bpf_map_lookup_elem(&stats, &vip_num); | ||
444 | if (!data_stats) | ||
445 | return TC_ACT_SHOT; | ||
446 | data_stats->pkts++; | ||
447 | data_stats->bytes += pkt_bytes; | ||
448 | bpf_skb_set_tunnel_key(skb, &tkey, sizeof(tkey), tun_flag); | ||
449 | *(u32 *)eth->eth_dest = tkey.remote_ipv4; | ||
450 | return bpf_redirect(ifindex, 0); | ||
451 | } | ||
452 | |||
453 | SEC("l4lb-demo") | ||
454 | int balancer_ingress(struct __sk_buff *ctx) | ||
455 | { | ||
456 | void *data_end = (void *)(long)ctx->data_end; | ||
457 | void *data = (void *)(long)ctx->data; | ||
458 | struct eth_hdr *eth = data; | ||
459 | __u32 eth_proto; | ||
460 | __u32 nh_off; | ||
461 | |||
462 | nh_off = sizeof(struct eth_hdr); | ||
463 | if (data + nh_off > data_end) | ||
464 | return TC_ACT_SHOT; | ||
465 | eth_proto = eth->eth_proto; | ||
466 | if (eth_proto == bpf_htons(ETH_P_IP)) | ||
467 | return process_packet(data, nh_off, data_end, false, ctx); | ||
468 | else if (eth_proto == bpf_htons(ETH_P_IPV6)) | ||
469 | return process_packet(data, nh_off, data_end, true, ctx); | ||
470 | else | ||
471 | return TC_ACT_SHOT; | ||
472 | } | ||
473 | char _license[] SEC("license") = "GPL"; | ||
diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c index 1d7d2149163a..abff83bf8d40 100644 --- a/tools/testing/selftests/bpf/test_progs.c +++ b/tools/testing/selftests/bpf/test_progs.c | |||
@@ -169,10 +169,9 @@ out: | |||
169 | #define NUM_ITER 100000 | 169 | #define NUM_ITER 100000 |
170 | #define VIP_NUM 5 | 170 | #define VIP_NUM 5 |
171 | 171 | ||
172 | static void test_l4lb(void) | 172 | static void test_l4lb(const char *file) |
173 | { | 173 | { |
174 | unsigned int nr_cpus = bpf_num_possible_cpus(); | 174 | unsigned int nr_cpus = bpf_num_possible_cpus(); |
175 | const char *file = "./test_l4lb.o"; | ||
176 | struct vip key = {.protocol = 6}; | 175 | struct vip key = {.protocol = 6}; |
177 | struct vip_meta { | 176 | struct vip_meta { |
178 | __u32 flags; | 177 | __u32 flags; |
@@ -249,6 +248,15 @@ out: | |||
249 | bpf_object__close(obj); | 248 | bpf_object__close(obj); |
250 | } | 249 | } |
251 | 250 | ||
251 | static void test_l4lb_all(void) | ||
252 | { | ||
253 | const char *file1 = "./test_l4lb.o"; | ||
254 | const char *file2 = "./test_l4lb_noinline.o"; | ||
255 | |||
256 | test_l4lb(file1); | ||
257 | test_l4lb(file2); | ||
258 | } | ||
259 | |||
252 | static void test_tcp_estats(void) | 260 | static void test_tcp_estats(void) |
253 | { | 261 | { |
254 | const char *file = "./test_tcp_estats.o"; | 262 | const char *file = "./test_tcp_estats.o"; |
@@ -757,7 +765,7 @@ int main(void) | |||
757 | 765 | ||
758 | test_pkt_access(); | 766 | test_pkt_access(); |
759 | test_xdp(); | 767 | test_xdp(); |
760 | test_l4lb(); | 768 | test_l4lb_all(); |
761 | test_tcp_estats(); | 769 | test_tcp_estats(); |
762 | test_bpf_obj_id(); | 770 | test_bpf_obj_id(); |
763 | test_pkt_md_access(); | 771 | test_pkt_md_access(); |