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