Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2020 Red Hat, Inc.
3 : : */
4 : :
5 : : #include "test.h"
6 : :
7 : : #include <time.h>
8 : :
9 : : #include <rte_common.h>
10 : : #include <rte_cycles.h>
11 : : #include <rte_hexdump.h>
12 : : #include <rte_ip.h>
13 : : #include <rte_ip_frag.h>
14 : : #include <rte_mbuf.h>
15 : : #include <rte_random.h>
16 : :
17 : : #define NUM_MBUFS 128
18 : : #define BURST 32
19 : :
20 : : uint8_t expected_first_frag_ipv4_opts_copied[] = {
21 : : 0x07, 0x0b, 0x04, 0x00,
22 : : 0x00, 0x00, 0x00, 0x00,
23 : : 0x00, 0x00, 0x00, 0x83,
24 : : 0x07, 0x04, 0xc0, 0xa8,
25 : : 0xe3, 0x96, 0x00, 0x00,
26 : : };
27 : :
28 : : uint8_t expected_sub_frag_ipv4_opts_copied[] = {
29 : : 0x83, 0x07, 0x04, 0xc0,
30 : : 0xa8, 0xe3, 0x96, 0x00,
31 : : };
32 : :
33 : : uint8_t expected_first_frag_ipv4_opts_nocopied[] = {
34 : : 0x07, 0x0b, 0x04, 0x00,
35 : : 0x00, 0x00, 0x00, 0x00,
36 : : 0x00, 0x00, 0x00, 0x00,
37 : : };
38 : :
39 : : uint8_t expected_sub_frag_ipv4_opts_nocopied[0];
40 : :
41 : : struct test_opt_data {
42 : : bool is_first_frag; /**< offset is 0 */
43 : : bool opt_copied; /**< ip option copied flag */
44 : : uint16_t len; /**< option data len */
45 : : uint8_t data[RTE_IPV4_HDR_OPT_MAX_LEN]; /**< option data */
46 : : };
47 : :
48 : : static struct rte_mempool *pkt_pool,
49 : : *direct_pool,
50 : : *indirect_pool;
51 : :
52 : : static inline void
53 : 48 : hex_to_str(uint8_t *hex, uint16_t len, char *str)
54 : : {
55 : : int i;
56 : :
57 [ + + ]: 344 : for (i = 0; i < len; i++) {
58 : 296 : sprintf(str, "%02x", hex[i]);
59 : 296 : str += 2;
60 : : }
61 : 48 : *str = 0;
62 : 48 : }
63 : :
64 : : static int
65 : 1 : setup_buf_pool(void)
66 : : {
67 : 1 : pkt_pool = rte_pktmbuf_pool_create("FRAG_MBUF_POOL",
68 : : NUM_MBUFS, BURST, 0,
69 : : RTE_MBUF_DEFAULT_BUF_SIZE,
70 : : SOCKET_ID_ANY);
71 [ - + ]: 1 : if (pkt_pool == NULL) {
72 : : printf("%s: Error creating pkt mempool\n", __func__);
73 : 0 : goto bad_setup;
74 : : }
75 : :
76 : 1 : direct_pool = rte_pktmbuf_pool_create("FRAG_D_MBUF_POOL",
77 : : NUM_MBUFS, BURST, 0,
78 : : RTE_MBUF_DEFAULT_BUF_SIZE,
79 : : SOCKET_ID_ANY);
80 [ - + ]: 1 : if (direct_pool == NULL) {
81 : : printf("%s: Error creating direct mempool\n", __func__);
82 : 0 : goto bad_setup;
83 : : }
84 : :
85 : 1 : indirect_pool = rte_pktmbuf_pool_create("FRAG_I_MBUF_POOL",
86 : : NUM_MBUFS, BURST, 0,
87 : : 0, SOCKET_ID_ANY);
88 [ - + ]: 1 : if (indirect_pool == NULL) {
89 : : printf("%s: Error creating indirect mempool\n", __func__);
90 : 0 : goto bad_setup;
91 : : }
92 : :
93 : : return TEST_SUCCESS;
94 : :
95 : 0 : bad_setup:
96 : 0 : rte_mempool_free(pkt_pool);
97 : 0 : pkt_pool = NULL;
98 : :
99 : 0 : rte_mempool_free(direct_pool);
100 : 0 : direct_pool = NULL;
101 : :
102 : 0 : return TEST_FAILED;
103 : : }
104 : :
105 : 1 : static int testsuite_setup(void)
106 : : {
107 : 1 : return setup_buf_pool();
108 : : }
109 : :
110 : 1 : static void testsuite_teardown(void)
111 : : {
112 : 1 : rte_mempool_free(pkt_pool);
113 : 1 : rte_mempool_free(direct_pool);
114 : 1 : rte_mempool_free(indirect_pool);
115 : :
116 : 1 : pkt_pool = NULL;
117 : 1 : direct_pool = NULL;
118 : 1 : indirect_pool = NULL;
119 : 1 : }
120 : :
121 : 1 : static int ut_setup(void)
122 : : {
123 : 1 : return TEST_SUCCESS;
124 : : }
125 : :
126 : 1 : static void ut_teardown(void)
127 : : {
128 : 1 : }
129 : :
130 : : static inline void
131 : 31 : test_get_ipv4_opt(bool is_first_frag, bool opt_copied,
132 : : struct test_opt_data *expected_opt)
133 : : {
134 [ + + ]: 31 : if (is_first_frag) {
135 [ + + ]: 6 : if (opt_copied) {
136 : 4 : expected_opt->len =
137 : : sizeof(expected_first_frag_ipv4_opts_copied);
138 : 4 : memcpy(expected_opt->data,
139 : : expected_first_frag_ipv4_opts_copied,
140 : : sizeof(expected_first_frag_ipv4_opts_copied));
141 : : } else {
142 : 2 : expected_opt->len =
143 : : sizeof(expected_first_frag_ipv4_opts_nocopied);
144 : 2 : memcpy(expected_opt->data,
145 : : expected_first_frag_ipv4_opts_nocopied,
146 : : sizeof(expected_first_frag_ipv4_opts_nocopied));
147 : : }
148 : : } else {
149 [ + + ]: 25 : if (opt_copied) {
150 : 14 : expected_opt->len =
151 : : sizeof(expected_sub_frag_ipv4_opts_copied);
152 : 14 : memcpy(expected_opt->data,
153 : : expected_sub_frag_ipv4_opts_copied,
154 : : sizeof(expected_sub_frag_ipv4_opts_copied));
155 : : } else {
156 : 11 : expected_opt->len =
157 : : sizeof(expected_sub_frag_ipv4_opts_nocopied);
158 : : memcpy(expected_opt->data,
159 : : expected_sub_frag_ipv4_opts_nocopied,
160 : : sizeof(expected_sub_frag_ipv4_opts_nocopied));
161 : : }
162 : : }
163 : 31 : }
164 : :
165 : : static void
166 : 12 : v4_allocate_packet_of(struct rte_mbuf *b, int fill, size_t s,
167 : : int df, uint8_t mf, uint16_t off, uint8_t ttl, uint8_t proto,
168 : : uint16_t pktid, bool have_opt, bool is_first_frag, bool opt_copied)
169 : : {
170 : : /* Create a packet, 2k bytes long */
171 : 12 : b->data_off = 0;
172 : 12 : char *data = rte_pktmbuf_mtod(b, char *);
173 : : rte_be16_t fragment_offset = 0; /* fragmentation offset */
174 : : uint16_t iph_len;
175 : : struct test_opt_data opt;
176 : :
177 : 12 : opt.len = 0;
178 : :
179 [ + + ]: 12 : if (have_opt)
180 : 7 : test_get_ipv4_opt(is_first_frag, opt_copied, &opt);
181 : :
182 : 12 : iph_len = sizeof(struct rte_ipv4_hdr) + opt.len;
183 [ - + ]: 12 : memset(data, fill, iph_len + s);
184 : :
185 : : struct rte_ipv4_hdr *hdr = (struct rte_ipv4_hdr *)data;
186 : :
187 : : hdr->version_ihl = 0x40; /* ipv4 */
188 : 12 : hdr->version_ihl += (iph_len / 4);
189 : 12 : hdr->type_of_service = 0;
190 : 12 : b->pkt_len = s + iph_len;
191 : 12 : b->data_len = b->pkt_len;
192 [ - + ]: 12 : hdr->total_length = rte_cpu_to_be_16(b->pkt_len);
193 [ - + ]: 12 : hdr->packet_id = rte_cpu_to_be_16(pktid);
194 : :
195 [ + + ]: 12 : if (df)
196 : : fragment_offset |= 0x4000;
197 : :
198 [ + + ]: 12 : if (mf)
199 : 4 : fragment_offset |= 0x2000;
200 : :
201 [ + + ]: 12 : if (off)
202 : 4 : fragment_offset |= off;
203 : :
204 [ - + ]: 12 : hdr->fragment_offset = rte_cpu_to_be_16(fragment_offset);
205 : :
206 [ + + ]: 12 : if (!ttl)
207 : : ttl = 64; /* default to 64 */
208 : :
209 : : if (!proto)
210 : : proto = 1; /* icmp */
211 : :
212 : 12 : hdr->time_to_live = ttl;
213 : 12 : hdr->next_proto_id = proto;
214 : 12 : hdr->hdr_checksum = 0;
215 : 12 : hdr->src_addr = rte_cpu_to_be_32(0x8080808);
216 : 12 : hdr->dst_addr = rte_cpu_to_be_32(0x8080404);
217 : :
218 : 12 : memcpy(hdr + 1, opt.data, opt.len);
219 : 12 : }
220 : :
221 : : static void
222 : 4 : v6_allocate_packet_of(struct rte_mbuf *b, int fill, size_t s, uint8_t ttl,
223 : : uint8_t proto, uint16_t pktid)
224 : : {
225 : : /* Create a packet, 2k bytes long */
226 : 4 : b->data_off = 0;
227 : 4 : char *data = rte_pktmbuf_mtod(b, char *);
228 : :
229 [ - + ]: 4 : memset(data, fill, sizeof(struct rte_ipv6_hdr) + s);
230 : :
231 : : struct rte_ipv6_hdr *hdr = (struct rte_ipv6_hdr *)data;
232 : 4 : b->pkt_len = s + sizeof(struct rte_ipv6_hdr);
233 : 4 : b->data_len = b->pkt_len;
234 : :
235 : : /* basic v6 header */
236 [ - + ]: 4 : hdr->vtc_flow = rte_cpu_to_be_32(0x60 << 24 | pktid);
237 [ - + ]: 4 : hdr->payload_len = rte_cpu_to_be_16(b->pkt_len);
238 : 4 : hdr->proto = proto;
239 : 4 : hdr->hop_limits = ttl;
240 : :
241 : 4 : memset(&hdr->src_addr, 0x08, sizeof(hdr->src_addr));
242 : 4 : memset(&hdr->dst_addr, 0x04, sizeof(hdr->src_addr));
243 : 4 : }
244 : :
245 : : static inline void
246 : : test_free_fragments(struct rte_mbuf *mb[], uint32_t num)
247 : : {
248 : : uint32_t i;
249 [ + + ]: 50 : for (i = 0; i < num; i++)
250 : 37 : rte_pktmbuf_free(mb[i]);
251 : : }
252 : :
253 : : static inline void
254 : 13 : test_get_offset(struct rte_mbuf **mb, int32_t len,
255 : : uint16_t *offset, int ipv)
256 : : {
257 : : int32_t i;
258 : :
259 [ + + ]: 50 : for (i = 0; i < len; i++) {
260 [ + + ]: 37 : if (ipv == 4) {
261 : 31 : struct rte_ipv4_hdr *iph =
262 : 31 : rte_pktmbuf_mtod(mb[i], struct rte_ipv4_hdr *);
263 : 31 : offset[i] = iph->fragment_offset;
264 [ + - ]: 6 : } else if (ipv == 6) {
265 : 6 : struct ipv6_extension_fragment *fh =
266 : 6 : rte_pktmbuf_mtod_offset(
267 : : mb[i],
268 : : struct ipv6_extension_fragment *,
269 : : sizeof(struct rte_ipv6_hdr));
270 : 6 : offset[i] = fh->frag_data;
271 : : }
272 : : }
273 : 13 : }
274 : :
275 : : static inline void
276 : 7 : test_get_frag_opt(struct rte_mbuf **mb, int32_t num,
277 : : struct test_opt_data *opt, int ipv, bool opt_copied)
278 : : {
279 : : int32_t i;
280 : :
281 [ + + ]: 31 : for (i = 0; i < num; i++) {
282 [ + - ]: 24 : if (ipv == 4) {
283 : 24 : struct rte_ipv4_hdr *iph =
284 : 24 : rte_pktmbuf_mtod(mb[i], struct rte_ipv4_hdr *);
285 : 24 : uint16_t header_len = (iph->version_ihl &
286 : : RTE_IPV4_HDR_IHL_MASK) *
287 : : RTE_IPV4_IHL_MULTIPLIER;
288 : 24 : uint16_t opt_len = header_len -
289 : : sizeof(struct rte_ipv4_hdr);
290 : :
291 : 24 : opt->opt_copied = opt_copied;
292 : :
293 [ + + ]: 24 : if ((rte_be_to_cpu_16(iph->fragment_offset) &
294 [ - + ]: 48 : RTE_IPV4_HDR_OFFSET_MASK) == 0)
295 : 3 : opt->is_first_frag = true;
296 : : else
297 : 21 : opt->is_first_frag = false;
298 : :
299 [ + - ]: 24 : if (likely(opt_len <= RTE_IPV4_HDR_OPT_MAX_LEN)) {
300 : 24 : char *iph_opt = rte_pktmbuf_mtod_offset(mb[i],
301 : : char *, sizeof(struct rte_ipv4_hdr));
302 : 24 : opt->len = opt_len;
303 : 24 : memcpy(opt->data, iph_opt, opt_len);
304 : : } else {
305 : 0 : opt->len = RTE_IPV4_HDR_OPT_MAX_LEN;
306 : 0 : memset(opt->data, RTE_IPV4_HDR_OPT_EOL,
307 : : sizeof(opt->data));
308 : : }
309 : 24 : opt++;
310 : : }
311 : : }
312 : 7 : }
313 : :
314 : : static int
315 : 1 : test_ip_frag(void)
316 : : {
317 : : static const uint16_t RND_ID = UINT16_MAX;
318 : : int result = TEST_SUCCESS;
319 : : size_t i, j;
320 : :
321 : : struct test_ip_frags {
322 : : int ipv;
323 : : size_t mtu_size;
324 : : size_t pkt_size;
325 : : int set_df;
326 : : uint8_t set_mf;
327 : : uint16_t set_of;
328 : : uint8_t ttl;
329 : : uint8_t proto;
330 : : uint16_t pkt_id;
331 : : int expected_frags;
332 : : uint16_t expected_fragment_offset[BURST];
333 : : bool have_opt;
334 : : bool is_first_frag;
335 : : bool opt_copied;
336 : 1 : } tests[] = {
337 : : {4, 1280, 1400, 0, 0, 0, 64, IPPROTO_ICMP, RND_ID, 2,
338 : : {0x2000, 0x009D}, false},
339 : : {4, 1280, 1400, 0, 0, 0, 64, IPPROTO_ICMP, 0, 2,
340 : : {0x2000, 0x009D}, false},
341 : : {4, 600, 1400, 0, 0, 0, 64, IPPROTO_ICMP, RND_ID, 3,
342 : : {0x2000, 0x2048, 0x0090}, false},
343 : : {4, 4, 1400, 0, 0, 0, 64, IPPROTO_ICMP, RND_ID, -EINVAL},
344 : : {4, 600, 1400, 1, 0, 0, 64, IPPROTO_ICMP, RND_ID, -ENOTSUP},
345 : : {4, 600, 1400, 0, 0, 0, 0, IPPROTO_ICMP, RND_ID, 3,
346 : : {0x2000, 0x2046, 0x008C}, true, true, true},
347 : : /* The first fragment */
348 : : {4, 68, 104, 0, 1, 0, 0, IPPROTO_ICMP, RND_ID, 5,
349 : : {0x2000, 0x2003, 0x2006, 0x2009, 0x200C}, true, true, true},
350 : : /* The middle fragment */
351 : : {4, 68, 104, 0, 1, 13, 0, IPPROTO_ICMP, RND_ID, 3,
352 : : {0x200D, 0x2012, 0x2017}, true, false, true},
353 : : /* The last fragment */
354 : : {4, 68, 104, 0, 0, 26, 0, IPPROTO_ICMP, RND_ID, 3,
355 : : {0x201A, 0x201F, 0x0024}, true, false, true},
356 : : /* The first fragment */
357 : : {4, 68, 104, 0, 1, 0, 0, IPPROTO_ICMP, RND_ID, 4,
358 : : {0x2000, 0x2004, 0x2008, 0x200C}, true, true, false},
359 : : /* The middle fragment */
360 : : {4, 68, 104, 0, 1, 13, 0, IPPROTO_ICMP, RND_ID, 3,
361 : : {0x200D, 0x2013, 0x2019}, true, false, false},
362 : : /* The last fragment */
363 : : {4, 68, 104, 0, 0, 26, 0, IPPROTO_ICMP, RND_ID, 3,
364 : : {0x201A, 0x2020, 0x0026}, true, false, false},
365 : : {6, 1280, 1400, 0, 0, 0, 64, IPPROTO_ICMP, RND_ID, 2,
366 : : {0x0001, 0x04D0}, false},
367 : : {6, 1300, 1400, 0, 0, 0, 64, IPPROTO_ICMP, RND_ID, 2,
368 : : {0x0001, 0x04E0}, false},
369 : : {6, 4, 1400, 0, 0, 0, 64, IPPROTO_ICMP, RND_ID, -EINVAL},
370 : : {6, 1300, 1400, 0, 0, 0, 0, IPPROTO_ICMP, RND_ID, 2,
371 : : {0x0001, 0x04E0}, false},
372 : : };
373 : :
374 [ + + ]: 17 : for (i = 0; i < RTE_DIM(tests); i++) {
375 : : int32_t len = 0;
376 : : uint16_t fragment_offset[BURST];
377 : : struct test_opt_data opt_res[BURST];
378 : : struct test_opt_data opt_exp;
379 : 16 : uint16_t pktid = tests[i].pkt_id;
380 : : struct rte_mbuf *pkts_out[BURST];
381 : 16 : struct rte_mbuf *b = rte_pktmbuf_alloc(pkt_pool);
382 : :
383 [ - + ]: 16 : RTE_TEST_ASSERT_NOT_EQUAL(b, NULL,
384 : : "Failed to allocate pkt.");
385 : :
386 [ + + ]: 16 : if (tests[i].pkt_id == RND_ID)
387 : 15 : pktid = rte_rand_max(UINT16_MAX);
388 : :
389 [ + + ]: 16 : if (tests[i].ipv == 4) {
390 : 12 : v4_allocate_packet_of(b, 0x41414141,
391 : : tests[i].pkt_size,
392 : : tests[i].set_df,
393 : 12 : tests[i].set_mf,
394 : 12 : tests[i].set_of,
395 : 12 : tests[i].ttl,
396 : 12 : tests[i].proto,
397 : : pktid,
398 : 12 : tests[i].have_opt,
399 : 12 : tests[i].is_first_frag,
400 : 12 : tests[i].opt_copied);
401 [ + - ]: 4 : } else if (tests[i].ipv == 6) {
402 : 4 : v6_allocate_packet_of(b, 0x41414141,
403 : : tests[i].pkt_size,
404 : 4 : tests[i].ttl,
405 : 4 : tests[i].proto,
406 : : pktid);
407 : : }
408 : :
409 [ + + ]: 16 : if (tests[i].ipv == 4)
410 [ + + ]: 12 : if (i % 2)
411 : 6 : len = rte_ipv4_fragment_packet(b, pkts_out, BURST,
412 : 6 : tests[i].mtu_size,
413 : : direct_pool,
414 : : indirect_pool);
415 : : else
416 : 6 : len = rte_ipv4_fragment_copy_nonseg_packet(b,
417 : : pkts_out,
418 : : BURST,
419 : 6 : tests[i].mtu_size,
420 : : direct_pool);
421 [ + - ]: 4 : else if (tests[i].ipv == 6)
422 : 4 : len = rte_ipv6_fragment_packet(b, pkts_out, BURST,
423 : 4 : tests[i].mtu_size,
424 : : direct_pool,
425 : : indirect_pool);
426 : :
427 : 16 : rte_pktmbuf_free(b);
428 : :
429 [ + + ]: 16 : if (len > 0) {
430 : 13 : test_get_offset(pkts_out, len,
431 : : fragment_offset, tests[i].ipv);
432 [ + + ]: 13 : if (tests[i].have_opt)
433 : 7 : test_get_frag_opt(pkts_out, len, opt_res,
434 : 7 : tests[i].ipv, tests[i].opt_copied);
435 : 13 : test_free_fragments(pkts_out, len);
436 : : }
437 : :
438 : 16 : printf("[check frag number]%zd: checking %d with %d\n", i, len,
439 : : tests[i].expected_frags);
440 [ - + ]: 16 : RTE_TEST_ASSERT_EQUAL(len, tests[i].expected_frags,
441 : : "Failed case %zd.\n", i);
442 : :
443 [ + + ]: 16 : if (len > 0) {
444 [ + + ]: 50 : for (j = 0; j < (size_t)len; j++) {
445 : 37 : printf("[check offset]%zd-%zd: checking %d with %d\n",
446 : 37 : i, j, fragment_offset[j],
447 [ - + ]: 37 : rte_cpu_to_be_16(
448 : : tests[i].expected_fragment_offset[j]));
449 [ - + - + ]: 74 : RTE_TEST_ASSERT_EQUAL(fragment_offset[j],
450 : : rte_cpu_to_be_16(
451 : : tests[i].expected_fragment_offset[j]),
452 : : "Failed case %zd.\n", i);
453 : : }
454 : :
455 [ + + + - ]: 13 : if (tests[i].have_opt && (tests[i].ipv == 4)) {
456 [ + + ]: 31 : for (j = 0; j < (size_t)len; j++) {
457 : : char opt_res_str[2 *
458 : : RTE_IPV4_HDR_OPT_MAX_LEN + 1];
459 : : char opt_exp_str[2 *
460 : : RTE_IPV4_HDR_OPT_MAX_LEN + 1];
461 : :
462 : 24 : test_get_ipv4_opt(
463 : 24 : opt_res[j].is_first_frag,
464 : 24 : opt_res[j].opt_copied,
465 : : &opt_exp);
466 : 24 : hex_to_str(opt_res[j].data,
467 : 24 : opt_res[j].len,
468 : : opt_res_str);
469 : 24 : hex_to_str(opt_exp.data,
470 : 24 : opt_exp.len,
471 : : opt_exp_str);
472 : :
473 : 24 : printf(
474 : : "[check ipv4 option]%zd-%zd: checking (len:%u)%s with (len:%u)%s\n",
475 : : i, j,
476 : 24 : opt_res[j].len, opt_res_str,
477 : 24 : opt_exp.len, opt_exp_str);
478 [ - + ]: 24 : RTE_TEST_ASSERT_SUCCESS(
479 : : strcmp(opt_res_str,
480 : : opt_exp_str),
481 : : "Failed case %zd.\n", i);
482 : : }
483 : : }
484 : : }
485 : :
486 : : }
487 : :
488 : : return result;
489 : : }
490 : :
491 : : static struct unit_test_suite ipfrag_testsuite = {
492 : : .suite_name = "IP Frag Unit Test Suite",
493 : : .setup = testsuite_setup,
494 : : .teardown = testsuite_teardown,
495 : : .unit_test_cases = {
496 : : TEST_CASE_ST(ut_setup, ut_teardown,
497 : : test_ip_frag),
498 : :
499 : : TEST_CASES_END() /**< NULL terminate unit test array */
500 : : }
501 : : };
502 : :
503 : : static int
504 : 1 : test_ipfrag(void)
505 : : {
506 : 1 : rte_log_set_global_level(RTE_LOG_DEBUG);
507 : 1 : rte_log_set_level(RTE_LOGTYPE_EAL, RTE_LOG_DEBUG);
508 : :
509 : 1 : return unit_test_suite_runner(&ipfrag_testsuite);
510 : : }
511 : :
512 : :
513 : 251 : REGISTER_FAST_TEST(ipfrag_autotest, false, true, test_ipfrag);
|