Linux-libre 5.3.12-gnu
[librecmc/linux-libre.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 #define CHECK_FLOW_KEYS(desc, got, expected)                            \
9         CHECK_ATTR(memcmp(&got, &expected, sizeof(got)) != 0,           \
10               desc,                                                     \
11               "nhoff=%u/%u "                                            \
12               "thoff=%u/%u "                                            \
13               "addr_proto=0x%x/0x%x "                                   \
14               "is_frag=%u/%u "                                          \
15               "is_first_frag=%u/%u "                                    \
16               "is_encap=%u/%u "                                         \
17               "ip_proto=0x%x/0x%x "                                     \
18               "n_proto=0x%x/0x%x "                                      \
19               "sport=%u/%u "                                            \
20               "dport=%u/%u\n",                                          \
21               got.nhoff, expected.nhoff,                                \
22               got.thoff, expected.thoff,                                \
23               got.addr_proto, expected.addr_proto,                      \
24               got.is_frag, expected.is_frag,                            \
25               got.is_first_frag, expected.is_first_frag,                \
26               got.is_encap, expected.is_encap,                          \
27               got.ip_proto, expected.ip_proto,                          \
28               got.n_proto, expected.n_proto,                            \
29               got.sport, expected.sport,                                \
30               got.dport, expected.dport)
31
32 struct ipv4_pkt {
33         struct ethhdr eth;
34         struct iphdr iph;
35         struct tcphdr tcp;
36 } __packed;
37
38 struct svlan_ipv4_pkt {
39         struct ethhdr eth;
40         __u16 vlan_tci;
41         __u16 vlan_proto;
42         struct iphdr iph;
43         struct tcphdr tcp;
44 } __packed;
45
46 struct ipv6_pkt {
47         struct ethhdr eth;
48         struct ipv6hdr iph;
49         struct tcphdr tcp;
50 } __packed;
51
52 struct dvlan_ipv6_pkt {
53         struct ethhdr eth;
54         __u16 vlan_tci;
55         __u16 vlan_proto;
56         __u16 vlan_tci2;
57         __u16 vlan_proto2;
58         struct ipv6hdr iph;
59         struct tcphdr tcp;
60 } __packed;
61
62 struct test {
63         const char *name;
64         union {
65                 struct ipv4_pkt ipv4;
66                 struct svlan_ipv4_pkt svlan_ipv4;
67                 struct ipv6_pkt ipv6;
68                 struct dvlan_ipv6_pkt dvlan_ipv6;
69         } pkt;
70         struct bpf_flow_keys keys;
71 };
72
73 #define VLAN_HLEN       4
74
75 struct test tests[] = {
76         {
77                 .name = "ipv4",
78                 .pkt.ipv4 = {
79                         .eth.h_proto = __bpf_constant_htons(ETH_P_IP),
80                         .iph.ihl = 5,
81                         .iph.protocol = IPPROTO_TCP,
82                         .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
83                         .tcp.doff = 5,
84                 },
85                 .keys = {
86                         .nhoff = ETH_HLEN,
87                         .thoff = ETH_HLEN + sizeof(struct iphdr),
88                         .addr_proto = ETH_P_IP,
89                         .ip_proto = IPPROTO_TCP,
90                         .n_proto = __bpf_constant_htons(ETH_P_IP),
91                 },
92         },
93         {
94                 .name = "ipv6",
95                 .pkt.ipv6 = {
96                         .eth.h_proto = __bpf_constant_htons(ETH_P_IPV6),
97                         .iph.nexthdr = IPPROTO_TCP,
98                         .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
99                         .tcp.doff = 5,
100                 },
101                 .keys = {
102                         .nhoff = ETH_HLEN,
103                         .thoff = ETH_HLEN + sizeof(struct ipv6hdr),
104                         .addr_proto = ETH_P_IPV6,
105                         .ip_proto = IPPROTO_TCP,
106                         .n_proto = __bpf_constant_htons(ETH_P_IPV6),
107                 },
108         },
109         {
110                 .name = "802.1q-ipv4",
111                 .pkt.svlan_ipv4 = {
112                         .eth.h_proto = __bpf_constant_htons(ETH_P_8021Q),
113                         .vlan_proto = __bpf_constant_htons(ETH_P_IP),
114                         .iph.ihl = 5,
115                         .iph.protocol = IPPROTO_TCP,
116                         .iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
117                         .tcp.doff = 5,
118                 },
119                 .keys = {
120                         .nhoff = ETH_HLEN + VLAN_HLEN,
121                         .thoff = ETH_HLEN + VLAN_HLEN + sizeof(struct iphdr),
122                         .addr_proto = ETH_P_IP,
123                         .ip_proto = IPPROTO_TCP,
124                         .n_proto = __bpf_constant_htons(ETH_P_IP),
125                 },
126         },
127         {
128                 .name = "802.1ad-ipv6",
129                 .pkt.dvlan_ipv6 = {
130                         .eth.h_proto = __bpf_constant_htons(ETH_P_8021AD),
131                         .vlan_proto = __bpf_constant_htons(ETH_P_8021Q),
132                         .vlan_proto2 = __bpf_constant_htons(ETH_P_IPV6),
133                         .iph.nexthdr = IPPROTO_TCP,
134                         .iph.payload_len = __bpf_constant_htons(MAGIC_BYTES),
135                         .tcp.doff = 5,
136                 },
137                 .keys = {
138                         .nhoff = ETH_HLEN + VLAN_HLEN * 2,
139                         .thoff = ETH_HLEN + VLAN_HLEN * 2 +
140                                 sizeof(struct ipv6hdr),
141                         .addr_proto = ETH_P_IPV6,
142                         .ip_proto = IPPROTO_TCP,
143                         .n_proto = __bpf_constant_htons(ETH_P_IPV6),
144                 },
145         },
146 };
147
148 static int create_tap(const char *ifname)
149 {
150         struct ifreq ifr = {
151                 .ifr_flags = IFF_TAP | IFF_NO_PI | IFF_NAPI | IFF_NAPI_FRAGS,
152         };
153         int fd, ret;
154
155         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
156
157         fd = open("/dev/net/tun", O_RDWR);
158         if (fd < 0)
159                 return -1;
160
161         ret = ioctl(fd, TUNSETIFF, &ifr);
162         if (ret)
163                 return -1;
164
165         return fd;
166 }
167
168 static int tx_tap(int fd, void *pkt, size_t len)
169 {
170         struct iovec iov[] = {
171                 {
172                         .iov_len = len,
173                         .iov_base = pkt,
174                 },
175         };
176         return writev(fd, iov, ARRAY_SIZE(iov));
177 }
178
179 static int ifup(const char *ifname)
180 {
181         struct ifreq ifr = {};
182         int sk, ret;
183
184         strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
185
186         sk = socket(PF_INET, SOCK_DGRAM, 0);
187         if (sk < 0)
188                 return -1;
189
190         ret = ioctl(sk, SIOCGIFFLAGS, &ifr);
191         if (ret) {
192                 close(sk);
193                 return -1;
194         }
195
196         ifr.ifr_flags |= IFF_UP;
197         ret = ioctl(sk, SIOCSIFFLAGS, &ifr);
198         if (ret) {
199                 close(sk);
200                 return -1;
201         }
202
203         close(sk);
204         return 0;
205 }
206
207 void test_flow_dissector(void)
208 {
209         int i, err, prog_fd, keys_fd = -1, tap_fd;
210         struct bpf_object *obj;
211         __u32 duration = 0;
212
213         err = bpf_flow_load(&obj, "./bpf_flow.o", "flow_dissector",
214                             "jmp_table", "last_dissection", &prog_fd, &keys_fd);
215         if (err) {
216                 error_cnt++;
217                 return;
218         }
219
220         for (i = 0; i < ARRAY_SIZE(tests); i++) {
221                 struct bpf_flow_keys flow_keys;
222                 struct bpf_prog_test_run_attr tattr = {
223                         .prog_fd = prog_fd,
224                         .data_in = &tests[i].pkt,
225                         .data_size_in = sizeof(tests[i].pkt),
226                         .data_out = &flow_keys,
227                 };
228
229                 err = bpf_prog_test_run_xattr(&tattr);
230                 CHECK_ATTR(tattr.data_size_out != sizeof(flow_keys) ||
231                            err || tattr.retval != 1,
232                            tests[i].name,
233                            "err %d errno %d retval %d duration %d size %u/%lu\n",
234                            err, errno, tattr.retval, tattr.duration,
235                            tattr.data_size_out, sizeof(flow_keys));
236                 CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
237         }
238
239         /* Do the same tests but for skb-less flow dissector.
240          * We use a known path in the net/tun driver that calls
241          * eth_get_headlen and we manually export bpf_flow_keys
242          * via BPF map in this case.
243          */
244
245         err = bpf_prog_attach(prog_fd, 0, BPF_FLOW_DISSECTOR, 0);
246         CHECK(err, "bpf_prog_attach", "err %d errno %d\n", err, errno);
247
248         tap_fd = create_tap("tap0");
249         CHECK(tap_fd < 0, "create_tap", "tap_fd %d errno %d\n", tap_fd, errno);
250         err = ifup("tap0");
251         CHECK(err, "ifup", "err %d errno %d\n", err, errno);
252
253         for (i = 0; i < ARRAY_SIZE(tests); i++) {
254                 struct bpf_flow_keys flow_keys = {};
255                 struct bpf_prog_test_run_attr tattr = {};
256                 __u32 key = 0;
257
258                 err = tx_tap(tap_fd, &tests[i].pkt, sizeof(tests[i].pkt));
259                 CHECK(err < 0, "tx_tap", "err %d errno %d\n", err, errno);
260
261                 err = bpf_map_lookup_elem(keys_fd, &key, &flow_keys);
262                 CHECK_ATTR(err, tests[i].name, "bpf_map_lookup_elem %d\n", err);
263
264                 CHECK_ATTR(err, tests[i].name, "skb-less err %d\n", err);
265                 CHECK_FLOW_KEYS(tests[i].name, flow_keys, tests[i].keys);
266         }
267
268         bpf_prog_detach(prog_fd, BPF_FLOW_DISSECTOR);
269         bpf_object__close(obj);
270 }