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 14 #ifdef RTE_EXEC_ENV_WINDOWS 15 static int 16 test_ipfrag(void) 17 { 18 printf("ipfrag not supported on Windows, skipping test\n"); 19 return TEST_SKIPPED; 20 } 21 22 #else 23 24 #include <rte_ip_frag.h> 25 #include <rte_mbuf.h> 26 #include <rte_memcpy.h> 27 #include <rte_random.h> 28 29 #define NUM_MBUFS 128 30 #define BURST 32 31 32 static struct rte_mempool *pkt_pool, 33 *direct_pool, 34 *indirect_pool; 35 36 static int 37 setup_buf_pool(void) 38 { 39 pkt_pool = rte_pktmbuf_pool_create("FRAG_MBUF_POOL", 40 NUM_MBUFS, BURST, 0, 41 RTE_MBUF_DEFAULT_BUF_SIZE, 42 SOCKET_ID_ANY); 43 if (pkt_pool == NULL) { 44 printf("%s: Error creating pkt mempool\n", __func__); 45 goto bad_setup; 46 } 47 48 direct_pool = rte_pktmbuf_pool_create("FRAG_D_MBUF_POOL", 49 NUM_MBUFS, BURST, 0, 50 RTE_MBUF_DEFAULT_BUF_SIZE, 51 SOCKET_ID_ANY); 52 if (direct_pool == NULL) { 53 printf("%s: Error creating direct mempool\n", __func__); 54 goto bad_setup; 55 } 56 57 indirect_pool = rte_pktmbuf_pool_create("FRAG_I_MBUF_POOL", 58 NUM_MBUFS, BURST, 0, 59 0, SOCKET_ID_ANY); 60 if (indirect_pool == NULL) { 61 printf("%s: Error creating indirect mempool\n", __func__); 62 goto bad_setup; 63 } 64 65 return TEST_SUCCESS; 66 67 bad_setup: 68 rte_mempool_free(pkt_pool); 69 pkt_pool = NULL; 70 71 rte_mempool_free(direct_pool); 72 direct_pool = NULL; 73 74 return TEST_FAILED; 75 } 76 77 static int testsuite_setup(void) 78 { 79 return setup_buf_pool(); 80 } 81 82 static void testsuite_teardown(void) 83 { 84 rte_mempool_free(pkt_pool); 85 rte_mempool_free(direct_pool); 86 rte_mempool_free(indirect_pool); 87 88 pkt_pool = NULL; 89 direct_pool = NULL; 90 indirect_pool = NULL; 91 } 92 93 static int ut_setup(void) 94 { 95 return TEST_SUCCESS; 96 } 97 98 static void ut_teardown(void) 99 { 100 } 101 102 static void 103 v4_allocate_packet_of(struct rte_mbuf *b, int fill, 104 size_t s, int df, uint8_t mf, uint16_t off, 105 uint8_t ttl, uint8_t proto, uint16_t pktid) 106 { 107 /* Create a packet, 2k bytes long */ 108 b->data_off = 0; 109 char *data = rte_pktmbuf_mtod(b, char *); 110 rte_be16_t fragment_offset = 0; /**< fragmentation offset */ 111 112 memset(data, fill, sizeof(struct rte_ipv4_hdr) + s); 113 114 struct rte_ipv4_hdr *hdr = (struct rte_ipv4_hdr *)data; 115 116 hdr->version_ihl = 0x45; /* standard IP header... */ 117 hdr->type_of_service = 0; 118 b->pkt_len = s + sizeof(struct rte_ipv4_hdr); 119 b->data_len = b->pkt_len; 120 hdr->total_length = rte_cpu_to_be_16(b->pkt_len); 121 hdr->packet_id = rte_cpu_to_be_16(pktid); 122 123 if (df) 124 fragment_offset |= 0x4000; 125 126 if (mf) 127 fragment_offset |= 0x2000; 128 129 if (off) 130 fragment_offset |= off; 131 132 hdr->fragment_offset = rte_cpu_to_be_16(fragment_offset); 133 134 if (!ttl) 135 ttl = 64; /* default to 64 */ 136 137 if (!proto) 138 proto = 1; /* icmp */ 139 140 hdr->time_to_live = ttl; 141 hdr->next_proto_id = proto; 142 hdr->hdr_checksum = 0; 143 hdr->src_addr = rte_cpu_to_be_32(0x8080808); 144 hdr->dst_addr = rte_cpu_to_be_32(0x8080404); 145 } 146 147 static void 148 v6_allocate_packet_of(struct rte_mbuf *b, int fill, size_t s, uint8_t ttl, 149 uint8_t proto, uint16_t pktid) 150 { 151 /* Create a packet, 2k bytes long */ 152 b->data_off = 0; 153 char *data = rte_pktmbuf_mtod(b, char *); 154 155 memset(data, fill, sizeof(struct rte_ipv6_hdr) + s); 156 157 struct rte_ipv6_hdr *hdr = (struct rte_ipv6_hdr *)data; 158 b->pkt_len = s + sizeof(struct rte_ipv6_hdr); 159 b->data_len = b->pkt_len; 160 161 /* basic v6 header */ 162 hdr->vtc_flow = rte_cpu_to_be_32(0x60 << 24 | pktid); 163 hdr->payload_len = rte_cpu_to_be_16(b->pkt_len); 164 hdr->proto = proto; 165 hdr->hop_limits = ttl; 166 167 memset(hdr->src_addr, 0x08, sizeof(hdr->src_addr)); 168 memset(hdr->dst_addr, 0x04, sizeof(hdr->src_addr)); 169 } 170 171 static inline void 172 test_free_fragments(struct rte_mbuf *mb[], uint32_t num) 173 { 174 uint32_t i; 175 for (i = 0; i < num; i++) 176 rte_pktmbuf_free(mb[i]); 177 } 178 179 static inline void 180 test_get_offset(struct rte_mbuf **mb, int32_t len, 181 uint16_t *offset, int ipv) 182 { 183 int32_t i; 184 185 for (i = 0; i < len; i++) { 186 if (ipv == 4) { 187 struct rte_ipv4_hdr *iph = 188 rte_pktmbuf_mtod(mb[i], struct rte_ipv4_hdr *); 189 offset[i] = iph->fragment_offset; 190 } else if (ipv == 6) { 191 struct ipv6_extension_fragment *fh = 192 rte_pktmbuf_mtod_offset( 193 mb[i], 194 struct ipv6_extension_fragment *, 195 sizeof(struct rte_ipv6_hdr)); 196 offset[i] = fh->frag_data; 197 } 198 } 199 } 200 201 static int 202 test_ip_frag(void) 203 { 204 static const uint16_t RND_ID = UINT16_MAX; 205 int result = TEST_SUCCESS; 206 size_t i, j; 207 208 struct test_ip_frags { 209 int ipv; 210 size_t mtu_size; 211 size_t pkt_size; 212 int set_df; 213 uint8_t set_mf; 214 uint16_t set_of; 215 uint8_t ttl; 216 uint8_t proto; 217 uint16_t pkt_id; 218 int expected_frags; 219 uint16_t expected_fragment_offset[BURST]; 220 } tests[] = { 221 {4, 1280, 1400, 0, 0, 0, 64, IPPROTO_ICMP, RND_ID, 2, 222 {0x2000, 0x009D}}, 223 {4, 1280, 1400, 0, 0, 0, 64, IPPROTO_ICMP, 0, 2, 224 {0x2000, 0x009D}}, 225 {4, 600, 1400, 0, 0, 0, 64, IPPROTO_ICMP, RND_ID, 3, 226 {0x2000, 0x2048, 0x0090}}, 227 {4, 4, 1400, 0, 0, 0, 64, IPPROTO_ICMP, RND_ID, -EINVAL}, 228 {4, 600, 1400, 1, 0, 0, 64, IPPROTO_ICMP, RND_ID, -ENOTSUP}, 229 {4, 600, 1400, 0, 0, 0, 0, IPPROTO_ICMP, RND_ID, 3, 230 {0x2000, 0x2048, 0x0090}}, 231 {4, 68, 104, 0, 1, 13, 0, IPPROTO_ICMP, RND_ID, 3, 232 {0x200D, 0x2013, 0x2019}}, 233 234 {6, 1280, 1400, 0, 0, 0, 64, IPPROTO_ICMP, RND_ID, 2, 235 {0x0001, 0x04D0}}, 236 {6, 1300, 1400, 0, 0, 0, 64, IPPROTO_ICMP, RND_ID, 2, 237 {0x0001, 0x04E0}}, 238 {6, 4, 1400, 0, 0, 0, 64, IPPROTO_ICMP, RND_ID, -EINVAL}, 239 {6, 1300, 1400, 0, 0, 0, 0, IPPROTO_ICMP, RND_ID, 2, 240 {0x0001, 0x04E0}}, 241 }; 242 243 for (i = 0; i < RTE_DIM(tests); i++) { 244 int32_t len = 0; 245 uint16_t fragment_offset[BURST]; 246 uint16_t pktid = tests[i].pkt_id; 247 struct rte_mbuf *pkts_out[BURST]; 248 struct rte_mbuf *b = rte_pktmbuf_alloc(pkt_pool); 249 250 RTE_TEST_ASSERT_NOT_EQUAL(b, NULL, 251 "Failed to allocate pkt."); 252 253 if (tests[i].pkt_id == RND_ID) 254 pktid = rte_rand_max(UINT16_MAX); 255 256 if (tests[i].ipv == 4) { 257 v4_allocate_packet_of(b, 0x41414141, 258 tests[i].pkt_size, 259 tests[i].set_df, 260 tests[i].set_mf, 261 tests[i].set_of, 262 tests[i].ttl, 263 tests[i].proto, 264 pktid); 265 } else if (tests[i].ipv == 6) { 266 v6_allocate_packet_of(b, 0x41414141, 267 tests[i].pkt_size, 268 tests[i].ttl, 269 tests[i].proto, 270 pktid); 271 } 272 273 if (tests[i].ipv == 4) 274 len = rte_ipv4_fragment_packet(b, pkts_out, BURST, 275 tests[i].mtu_size, 276 direct_pool, 277 indirect_pool); 278 else if (tests[i].ipv == 6) 279 len = rte_ipv6_fragment_packet(b, pkts_out, BURST, 280 tests[i].mtu_size, 281 direct_pool, 282 indirect_pool); 283 284 rte_pktmbuf_free(b); 285 286 if (len > 0) { 287 test_get_offset(pkts_out, len, 288 fragment_offset, tests[i].ipv); 289 test_free_fragments(pkts_out, len); 290 } 291 292 printf("%zd: checking %d with %d\n", i, len, 293 tests[i].expected_frags); 294 RTE_TEST_ASSERT_EQUAL(len, tests[i].expected_frags, 295 "Failed case %zd.\n", i); 296 297 if (len > 0) { 298 for (j = 0; j < (size_t)len; j++) { 299 printf("%zd-%zd: checking %d with %d\n", 300 i, j, fragment_offset[j], 301 rte_cpu_to_be_16( 302 tests[i].expected_fragment_offset[j])); 303 RTE_TEST_ASSERT_EQUAL(fragment_offset[j], 304 rte_cpu_to_be_16( 305 tests[i].expected_fragment_offset[j]), 306 "Failed case %zd.\n", i); 307 } 308 } 309 310 } 311 312 return result; 313 } 314 315 static struct unit_test_suite ipfrag_testsuite = { 316 .suite_name = "IP Frag Unit Test Suite", 317 .setup = testsuite_setup, 318 .teardown = testsuite_teardown, 319 .unit_test_cases = { 320 TEST_CASE_ST(ut_setup, ut_teardown, 321 test_ip_frag), 322 323 TEST_CASES_END() /**< NULL terminate unit test array */ 324 } 325 }; 326 327 static int 328 test_ipfrag(void) 329 { 330 rte_log_set_global_level(RTE_LOG_DEBUG); 331 rte_log_set_level(RTE_LOGTYPE_EAL, RTE_LOG_DEBUG); 332 333 return unit_test_suite_runner(&ipfrag_testsuite); 334 } 335 336 #endif /* !RTE_EXEC_ENV_WINDOWS */ 337 338 REGISTER_TEST_COMMAND(ipfrag_autotest, test_ipfrag); 339