selftests/bpf: fix race in flow dissector tests
[linux-2.6-block.git] / tools / testing / selftests / bpf / prog_tests / flow_dissector.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3 #include <error.h>
4 #include <linux/if.h>
5 #include <linux/if_tun.h>
6 #include <sys/uio.h>
7
8 #ifndef IP_MF
9 #define IP_MF 0x2000
10 #endif
11
12 #define CHECK_FLOW_KEYS(desc, got, expected)                            \
13         CHECK_ATTR(memcmp(&got, &expected, sizeof(got)) != 0,           \
14               desc,                                                     \
15               "nhoff=%u/%u "                                            \
16               "thoff=%u/%u "                                            \
17               "addr_proto=0x%x/0x%x "                                   \
18               "is_frag=%u/%u "                                          \
19               "is_first_frag=%u/%u "                                    \
20               "is_encap=%u/%u "                                         \
21               "ip_proto=0x%x/0x%x "                                     \
22               "n_proto=0x%x/0x%x "                                      \
23               "flow_label=0x%x/0x%x "                                   \
24               "sport=%u/%u "                                            \
25               "dport=%u/%u\n",                                          \
26               got.nhoff, expected.nhoff,                                \
27               got.thoff, expected.thoff,                                \
28               got.addr_proto, expected.addr_proto,                      \
29               got.is_frag, expected.is_frag,                            \
30               got.is_first_frag, expected.is_first_frag,                \
31               got.is_encap, expected.is_encap,                          \
32               got.ip_proto, expected.ip_proto,                          \
33               got.n_proto, expected.n_proto,                            \
34               got.flow_label, expected.flow_label,                      \
35               got.sport, expected.sport,                                \
36               got.dport, expected.dport)
37
38 struct ipv4_pkt {
39         struct ethhdr eth;
40         struct iphdr iph;
41         struct tcphdr tcp;
42 } __packed;
43
44 struct ipip_pkt {
45         struct ethhdr eth;
46         struct iphdr iph;
47         struct iphdr iph_inner;
48         struct tcphdr tcp;
49 } __packed;
50
51 struct svlan_ipv4_pkt {
52         struct ethhdr eth;
53         __u16 vlan_tci;
54         __u16 vlan_proto;
55         struct iphdr iph;
56         struct tcphdr tcp;
57 } __packed;
58
59 struct ipv6_pkt {
60         struct ethhdr eth;
61         struct ipv6hdr iph;
62         struct tcphdr tcp;
63 } __packed;
64
65 struct ipv6_frag_pkt {
66         struct ethhdr eth;
67         struct ipv6hdr iph;
68         struct frag_hdr {
69                 __u8 nexthdr;
70                 __u8 reserved;
71                 __be16 frag_off;
72                 __be32 identification;
73         } ipf;
74         struct tcphdr tcp;
75 } __packed;
76
77 struct dvlan_ipv6_pkt {
78         struct ethhdr eth;
79         __u16 vlan_tci;
80         __u16 vlan_proto;
81         __u16 vlan_tci2;
82         __u16 vlan_proto2;
83         struct ipv6hdr iph;
84         struct tcphdr tcp;
85 } __packed;
86
87 struct test {
88         const char *name;
89         union {
90                 struct ipv4_pkt ipv4;
91                 struct svlan_ipv4_pkt svlan_ipv4;
92                 struct ipip_pkt ipip;
93                 struct ipv6_pkt ipv6;
94                 struct ipv6_frag_pkt ipv6_frag;
95                 struct dvlan_ipv6_pkt dvlan_ipv6;
96         } pkt;
97         struct bpf_flow_keys keys;
98         __u32 flags;
99 };
100
101 #define VLAN_HLEN       4
102
103 struct test tests[] = {
104         {
105                 .name = "ipv4",
106                 .pkt.ipv4 = {
107                         .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
108                         .iph.ihl = 5,
109                         .iph.protocol = IPPROTO_TCP,
110                         .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
111                         .tcp.doff = 5,
112                         .tcp.source = 80,
113                         .tcp.dest = 8080,
114                 },
115                 .keys = {
116                         .nhoff = ETH_HLEN,
117                         .thoff = ETH_HLEN + sizeof(struct iphdr),
118                         .addr_proto = ETH_P_IP,
119                         .ip_proto = IPPROTO_TCP,
120                         .n_proto = __bpf_constant_htons(ETH_P_IP),
121                         .sport = 80,
122                         .dport = 8080,
123                 },
124         },
125         {
126                 .name = "ipv6",
127                 .pkt.ipv6 = {
128                         .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
129                         .iph.nexthdr = IPPROTO_TCP,
130                         .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
131                         .tcp.doff = 5,
132                         .tcp.source = 80,
133                         .tcp.dest = 8080,
134                 },
135                 .keys = {
136                         .nhoff = ETH_HLEN,
137                         .thoff = ETH_HLEN + sizeof(struct ipv6hdr),
138                         .addr_proto = ETH_P_IPV6,
139                         .ip_proto = IPPROTO_TCP,
140                         .n_proto = __bpf_constant_htons(ETH_P_IPV6),
141                         .sport = 80,
142                         .dport = 8080,
143                 },
144         },
145         {
146                 .name = "802.1q-ipv4",
147                 .pkt.svlan_ipv4 = {
148                         .eth.h_proto = __bpf_constant_htons(ETH_P_8021Q),
149                         .vlan_proto = __bpf_constant_htons(ETH_P_IP),
150                         .iph.ihl = 5,
151                         .iph.protocol = IPPROTO_TCP,
152                         .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
153                         .tcp.doff = 5,
154                         .tcp.source = 80,
155                         .tcp.dest = 8080,
156                 },
157                 .keys = {
158                         .nhoff = ETH_HLEN + VLAN_HLEN,
159                         .thoff = ETH_HLEN + VLAN_HLEN + sizeof(struct iphdr),
160                         .addr_proto = ETH_P_IP,
161                         .ip_proto = IPPROTO_TCP,
162                         .n_proto = __bpf_constant_htons(ETH_P_IP),
163                         .sport = 80,
164                         .dport = 8080,
165                 },
166         },
167         {
168                 .name = "802.1ad-ipv6",
169                 .pkt.dvlan_ipv6 = {
170                         .eth.h_proto = __bpf_constant_htons(ETH_P_8021AD),
171                         .vlan_proto = __bpf_constant_htons(ETH_P_8021Q),
172                         .vlan_proto2 = __bpf_constant_htons(ETH_P_IPV6),
173                         .iph.nexthdr = IPPROTO_TCP,
174                         .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
175                         .tcp.doff = 5,
176                         .tcp.source = 80,
177                         .tcp.dest = 8080,
178                 },
179                 .keys = {
180                         .nhoff = ETH_HLEN + VLAN_HLEN * 2,
181                         .thoff = ETH_HLEN + VLAN_HLEN * 2 +
182                                 sizeof(struct ipv6hdr),
183                         .addr_proto = ETH_P_IPV6,
184                         .ip_proto = IPPROTO_TCP,
185                         .n_proto = __bpf_constant_htons(ETH_P_IPV6),
186                         .sport = 80,
187                         .dport = 8080,
188                 },
189         },
190         {
191                 .name = "ipv4-frag",
192                 .pkt.ipv4 = {
193                         .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
194                         .iph.ihl = 5,
195                         .iph.protocol = IPPROTO_TCP,
196                         .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
197                         .iph.frag_off = __bpf_constant_htons(IP_MF),
198                         .tcp.doff = 5,
199                         .tcp.source = 80,
200                         .tcp.dest = 8080,
201                 },
202                 .keys = {
203                         .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
204                         .nhoff = ETH_HLEN,
205                         .thoff = ETH_HLEN + sizeof(struct iphdr),
206                         .addr_proto = ETH_P_IP,
207                         .ip_proto = IPPROTO_TCP,
208                         .n_proto = __bpf_constant_htons(ETH_P_IP),
209                         .is_frag = true,
210                         .is_first_frag = true,
211                         .sport = 80,
212                         .dport = 8080,
213                 },
214                 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
215         },
216         {
217                 .name = "ipv4-no-frag",
218                 .pkt.ipv4 = {
219                         .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
220                         .iph.ihl = 5,
221                         .iph.protocol = IPPROTO_TCP,
222                         .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
223                         .iph.frag_off = __bpf_constant_htons(IP_MF),
224                         .tcp.doff = 5,
225                         .tcp.source = 80,
226                         .tcp.dest = 8080,
227                 },
228                 .keys = {
229                         .nhoff = ETH_HLEN,
230                         .thoff = ETH_HLEN + sizeof(struct iphdr),
231                         .addr_proto = ETH_P_IP,
232                         .ip_proto = IPPROTO_TCP,
233                         .n_proto = __bpf_constant_htons(ETH_P_IP),
234                         .is_frag = true,
235                         .is_first_frag = true,
236                 },
237         },
238         {
239                 .name = "ipv6-frag",
240                 .pkt.ipv6_frag = {
241                         .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
242                         .iph.nexthdr = IPPROTO_FRAGMENT,
243                         .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
244                         .ipf.nexthdr = IPPROTO_TCP,
245                         .tcp.doff = 5,
246                         .tcp.source = 80,
247                         .tcp.dest = 8080,
248                 },
249                 .keys = {
250                         .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
251                         .nhoff = ETH_HLEN,
252                         .thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
253                                 sizeof(struct frag_hdr),
254                         .addr_proto = ETH_P_IPV6,
255                         .ip_proto = IPPROTO_TCP,
256                         .n_proto = __bpf_constant_htons(ETH_P_IPV6),
257                         .is_frag = true,
258                         .is_first_frag = true,
259                         .sport = 80,
260                         .dport = 8080,
261                 },
262                 .flags = BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG,
263         },
264         {
265                 .name = "ipv6-no-frag",
266                 .pkt.ipv6_frag = {
267                         .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
268                         .iph.nexthdr = IPPROTO_FRAGMENT,
269                         .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
270                         .ipf.nexthdr = IPPROTO_TCP,
271                         .tcp.doff = 5,
272                         .tcp.source = 80,
273                         .tcp.dest = 8080,
274                 },
275                 .keys = {
276                         .nhoff = ETH_HLEN,
277                         .thoff = ETH_HLEN + sizeof(struct ipv6hdr) +
278                                 sizeof(struct frag_hdr),
279                         .addr_proto = ETH_P_IPV6,
280                         .ip_proto = IPPROTO_TCP,
281                         .n_proto = __bpf_constant_htons(ETH_P_IPV6),
282                         .is_frag = true,
283                         .is_first_frag = true,
284                 },
285         },
286         {
287                 .name = "ipv6-flow-label",
288                 .pkt.ipv6 = {
289                         .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
290                         .iph.nexthdr = IPPROTO_TCP,
291                         .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
292                         .iph.flow_lbl = { 0xb, 0xee, 0xef },
293                         .tcp.doff = 5,
294                         .tcp.source = 80,
295                         .tcp.dest = 8080,
296                 },
297                 .keys = {
298                         .nhoff = ETH_HLEN,
299                         .thoff = ETH_HLEN + sizeof(struct ipv6hdr),
300                         .addr_proto = ETH_P_IPV6,
301                         .ip_proto = IPPROTO_TCP,
302                         .n_proto = __bpf_constant_htons(ETH_P_IPV6),
303                         .sport = 80,
304                         .dport = 8080,
305                         .flow_label = __bpf_constant_htonl(0xbeeef),
306                 },
307         },
308         {
309                 .name = "ipv6-no-flow-label",
310                 .pkt.ipv6 = {
311                         .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
312                         .iph.nexthdr = IPPROTO_TCP,
313                         .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
314                         .iph.flow_lbl = { 0xb, 0xee, 0xef },
315                         .tcp.doff = 5,
316                         .tcp.source = 80,
317                         .tcp.dest = 8080,
318                 },
319                 .keys = {
320                         .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
321                         .nhoff = ETH_HLEN,
322                         .thoff = ETH_HLEN + sizeof(struct ipv6hdr),
323                         .addr_proto = ETH_P_IPV6,
324                         .ip_proto = IPPROTO_TCP,
325                         .n_proto = __bpf_constant_htons(ETH_P_IPV6),
326                         .flow_label = __bpf_constant_htonl(0xbeeef),
327                 },
328                 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL,
329         },
330         {
331                 .name = "ipip-encap",
332                 .pkt.ipip = {
333                         .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
334                         .iph.ihl = 5,
335                         .iph.protocol = IPPROTO_IPIP,
336                         .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
337                         .iph_inner.ihl = 5,
338                         .iph_inner.protocol = IPPROTO_TCP,
339                         .iph_inner.tot_len =
340                                 __bpf_constant_htons(MAGIC_BYTES) -
341                                 sizeof(struct iphdr),
342                         .tcp.doff = 5,
343                         .tcp.source = 80,
344                         .tcp.dest = 8080,
345                 },
346                 .keys = {
347                         .nhoff = 0,
348                         .nhoff = ETH_HLEN,
349                         .thoff = ETH_HLEN + sizeof(struct iphdr) +
350                                 sizeof(struct iphdr),
351                         .addr_proto = ETH_P_IP,
352                         .ip_proto = IPPROTO_TCP,
353                         .n_proto = __bpf_constant_htons(ETH_P_IP),
354                         .is_encap = true,
355                         .sport = 80,
356                         .dport = 8080,
357                 },
358         },
359         {
360                 .name = "ipip-no-encap",
361                 .pkt.ipip = {
362                         .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
363                         .iph.ihl = 5,
364                         .iph.protocol = IPPROTO_IPIP,
365                         .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
366                         .iph_inner.ihl = 5,
367                         .iph_inner.protocol = IPPROTO_TCP,
368                         .iph_inner.tot_len =
369                                 __bpf_constant_htons(MAGIC_BYTES) -
370                                 sizeof(struct iphdr),
371                         .tcp.doff = 5,
372                         .tcp.source = 80,
373                         .tcp.dest = 8080,
374                 },
375                 .keys = {
376                         .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
377                         .nhoff = ETH_HLEN,
378                         .thoff = ETH_HLEN + sizeof(struct iphdr),
379                         .addr_proto = ETH_P_IP,
380                         .ip_proto = IPPROTO_IPIP,
381                         .n_proto = __bpf_constant_htons(ETH_P_IP),
382                         .is_encap = true,
383                 },
384                 .flags = BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP,
385         },
386 };
387
388 static int create_tap(const char *ifname)
389 {
390         struct ifreq ifr = {
391                 .ifr_flags = IFF_TAP | IFF_NO_PI | IFF_NAPI | IFF_NAPI_FRAGS,
392         };
393         int fd, ret;
394
395         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
396
397         fd = open("/dev/net/tun", O_RDWR);
398         if (fd < 0)
399                 return -1;
400
401         ret = ioctl(fd, TUNSETIFF, &ifr);
402         if (ret)
403                 return -1;
404
405         return fd;
406 }
407
408 static int tx_tap(int fd, void *pkt, size_t len)
409 {
410         struct iovec iov[] = {
411                 {
412                         .iov_len = len,
413                         .iov_base = pkt,
414                 },
415         };
416         return writev(fd, iov, ARRAY_SIZE(iov));
417 }
418
419 static int ifup(const char *ifname)
420 {
421         struct ifreq ifr = {};
422         int sk, ret;
423
424         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
425
426         sk = socket(PF_INET, SOCK_DGRAM, 0);
427         if (sk < 0)
428                 return -1;
429
430         ret = ioctl(sk, SIOCGIFFLAGS, &ifr);
431         if (ret) {
432                 close(sk);
433                 return -1;
434         }
435
436         ifr.ifr_flags |= IFF_UP;
437         ret = ioctl(sk, SIOCSIFFLAGS, &ifr);
438         if (ret) {
439                 close(sk);
440                 return -1;
441         }
442
443         close(sk);
444         return 0;
445 }
446
447 void test_flow_dissector(void)
448 {
449         int i, err, prog_fd, keys_fd = -1, tap_fd;
450         struct bpf_object *obj;
451         __u32 duration = 0;
452
453         err = bpf_flow_load(&obj, "./bpf_flow.o", "flow_dissector",
454                             "jmp_table", "last_dissection", &prog_fd, &keys_fd);
455         if (err) {
456                 error_cnt++;
457                 return;
458         }
459
460         for (i = 0; i < ARRAY_SIZE(tests); i++) {
461                 struct bpf_flow_keys flow_keys;
462                 struct bpf_prog_test_run_attr tattr = {
463                         .prog_fd = prog_fd,
464                         .data_in = &tests[i].pkt,
465                         .data_size_in = sizeof(tests[i].pkt),
466                         .data_out = &flow_keys,
467                 };
468                 static struct bpf_flow_keys ctx = {};
469
470                 if (tests[i].flags) {
471                         tattr.ctx_in = &ctx;
472                         tattr.ctx_size_in = sizeof(ctx);
473                         ctx.flags = tests[i].flags;
474                 }
475
476                 err = bpf_prog_test_run_xattr(&tattr);
477                 CHECK_ATTR(tattr.data_size_out != sizeof(flow_keys) ||
478                            err || tattr.retval != 1,
479                            tests[i].name,
480                            "err %d errno %d retval %d duration %d size %u/%lu\n",
481                            err, errno, tattr.retval, tattr.duration,
482                            tattr.data_size_out, sizeof(flow_keys));
483                 CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
484         }
485
486         /* Do the same tests but for skb-less flow dissector.
487          * We use a known path in the net/tun driver that calls
488          * eth_get_headlen and we manually export bpf_flow_keys
489          * via BPF map in this case.
490          */
491
492         err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
493         CHECK(err, "bpf_prog_attach", "err %d errno %d\n", err, errno);
494
495         tap_fd = create_tap("tap0");
496         CHECK(tap_fd < 0, "create_tap", "tap_fd %d errno %d\n", tap_fd, errno);
497         err = ifup("tap0");
498         CHECK(err, "ifup", "err %d errno %d\n", err, errno);
499
500         for (i = 0; i < ARRAY_SIZE(tests); i++) {
501                 /* Keep in sync with 'flags' from eth_get_headlen. */
502                 __u32 eth_get_headlen_flags =
503                         BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG;
504                 struct bpf_prog_test_run_attr tattr = {};
505                 struct bpf_flow_keys flow_keys = {};
506                 __u32 key = (__u32)(tests[i].keys.sport) << 16 |
507                             tests[i].keys.dport;
508
509                 /* For skb-less case we can't pass input flags; run
510                  * only the tests that have a matching set of flags.
511                  */
512
513                 if (tests[i].flags != eth_get_headlen_flags)
514                         continue;
515
516                 err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt));
517                 CHECK(err < 0, "tx_tap", "err %d errno %d\n", err, errno);
518
519                 err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys);
520                 CHECK_ATTR(err, tests[i].name, "bpf_map_lookup_elem %d\n", err);
521
522                 CHECK_ATTR(err, tests[i].name, "skb-less err %d\n", err);
523                 CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
524
525                 err = bpf_map_delete_elem(keys_fd, &key);
526                 CHECK_ATTR(err, tests[i].name, "bpf_map_delete_elem %d\n", err);
527         }
528
529         bpf_prog_detach(prog_fd, BPF_FLOW_DISSECTOR);
530         bpf_object__close(obj);
531 }