1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2016 Intel Corporation 3 */ 4 #include <stdint.h> 5 #include <string.h> 6 7 #include <rte_mbuf.h> 8 #include <rte_malloc.h> 9 #include <rte_memcpy.h> 10 11 #ifdef RTE_PORT_PCAP 12 #include <rte_ether.h> 13 #include <pcap.h> 14 #endif 15 16 #include "rte_port_source_sink.h" 17 18 /* 19 * Port SOURCE 20 */ 21 #ifdef RTE_PORT_STATS_COLLECT 22 23 #define RTE_PORT_SOURCE_STATS_PKTS_IN_ADD(port, val) \ 24 port->stats.n_pkts_in += val 25 #define RTE_PORT_SOURCE_STATS_PKTS_DROP_ADD(port, val) \ 26 port->stats.n_pkts_drop += val 27 28 #else 29 30 #define RTE_PORT_SOURCE_STATS_PKTS_IN_ADD(port, val) 31 #define RTE_PORT_SOURCE_STATS_PKTS_DROP_ADD(port, val) 32 33 #endif 34 35 struct rte_port_source { 36 struct rte_port_in_stats stats; 37 38 struct rte_mempool *mempool; 39 40 /* PCAP buffers and indices */ 41 uint8_t **pkts; 42 uint8_t *pkt_buff; 43 uint32_t *pkt_len; 44 uint32_t n_pkts; 45 uint32_t pkt_index; 46 }; 47 48 #ifdef RTE_PORT_PCAP 49 50 static int 51 pcap_source_load(struct rte_port_source *port, 52 const char *file_name, 53 uint32_t n_bytes_per_pkt, 54 int socket_id) 55 { 56 uint32_t n_pkts = 0; 57 uint32_t i; 58 uint32_t *pkt_len_aligns = NULL; 59 size_t total_buff_len = 0; 60 pcap_t *pcap_handle; 61 char pcap_errbuf[PCAP_ERRBUF_SIZE]; 62 uint32_t max_len; 63 struct pcap_pkthdr pcap_hdr; 64 const uint8_t *pkt; 65 uint8_t *buff = NULL; 66 uint32_t pktmbuf_maxlen = (uint32_t) 67 (rte_pktmbuf_data_room_size(port->mempool) - 68 RTE_PKTMBUF_HEADROOM); 69 70 if (n_bytes_per_pkt == 0) 71 max_len = pktmbuf_maxlen; 72 else 73 max_len = RTE_MIN(n_bytes_per_pkt, pktmbuf_maxlen); 74 75 /* first time open, get packet number */ 76 pcap_handle = pcap_open_offline(file_name, pcap_errbuf); 77 if (pcap_handle == NULL) { 78 RTE_LOG(ERR, PORT, "Failed to open pcap file " 79 "'%s' for reading\n", file_name); 80 goto error_exit; 81 } 82 83 while ((pkt = pcap_next(pcap_handle, &pcap_hdr)) != NULL) 84 n_pkts++; 85 86 pcap_close(pcap_handle); 87 88 port->pkt_len = rte_zmalloc_socket("PCAP", 89 (sizeof(*port->pkt_len) * n_pkts), 0, socket_id); 90 if (port->pkt_len == NULL) { 91 RTE_LOG(ERR, PORT, "No enough memory\n"); 92 goto error_exit; 93 } 94 95 pkt_len_aligns = rte_malloc("PCAP", 96 (sizeof(*pkt_len_aligns) * n_pkts), 0); 97 if (pkt_len_aligns == NULL) { 98 RTE_LOG(ERR, PORT, "No enough memory\n"); 99 goto error_exit; 100 } 101 102 port->pkts = rte_zmalloc_socket("PCAP", 103 (sizeof(*port->pkts) * n_pkts), 0, socket_id); 104 if (port->pkts == NULL) { 105 RTE_LOG(ERR, PORT, "No enough memory\n"); 106 goto error_exit; 107 } 108 109 /* open 2nd time, get pkt_len */ 110 pcap_handle = pcap_open_offline(file_name, pcap_errbuf); 111 if (pcap_handle == NULL) { 112 RTE_LOG(ERR, PORT, "Failed to open pcap file " 113 "'%s' for reading\n", file_name); 114 goto error_exit; 115 } 116 117 for (i = 0; i < n_pkts; i++) { 118 pcap_next(pcap_handle, &pcap_hdr); 119 port->pkt_len[i] = RTE_MIN(max_len, pcap_hdr.len); 120 pkt_len_aligns[i] = RTE_CACHE_LINE_ROUNDUP( 121 port->pkt_len[i]); 122 total_buff_len += pkt_len_aligns[i]; 123 } 124 125 pcap_close(pcap_handle); 126 127 /* allocate a big trunk of data for pcap file load */ 128 buff = rte_zmalloc_socket("PCAP", 129 total_buff_len, 0, socket_id); 130 if (buff == NULL) { 131 RTE_LOG(ERR, PORT, "No enough memory\n"); 132 goto error_exit; 133 } 134 135 port->pkt_buff = buff; 136 137 /* open file one last time to copy the pkt content */ 138 pcap_handle = pcap_open_offline(file_name, pcap_errbuf); 139 if (pcap_handle == NULL) { 140 RTE_LOG(ERR, PORT, "Failed to open pcap file " 141 "'%s' for reading\n", file_name); 142 goto error_exit; 143 } 144 145 for (i = 0; i < n_pkts; i++) { 146 pkt = pcap_next(pcap_handle, &pcap_hdr); 147 rte_memcpy(buff, pkt, port->pkt_len[i]); 148 port->pkts[i] = buff; 149 buff += pkt_len_aligns[i]; 150 } 151 152 pcap_close(pcap_handle); 153 154 port->n_pkts = n_pkts; 155 156 rte_free(pkt_len_aligns); 157 158 RTE_LOG(INFO, PORT, "Successfully load pcap file " 159 "'%s' with %u pkts\n", 160 file_name, port->n_pkts); 161 162 return 0; 163 164 error_exit: 165 rte_free(pkt_len_aligns); 166 rte_free(port->pkt_len); 167 rte_free(port->pkts); 168 rte_free(port->pkt_buff); 169 170 return -1; 171 } 172 173 #define PCAP_SOURCE_LOAD(port, file_name, n_bytes, socket_id) \ 174 pcap_source_load(port, file_name, n_bytes, socket_id) 175 176 #else /* RTE_PORT_PCAP */ 177 178 #define PCAP_SOURCE_LOAD(port, file_name, n_bytes, socket_id) \ 179 ({ \ 180 int _ret = 0; \ 181 \ 182 if (file_name) { \ 183 RTE_LOG(ERR, PORT, "Source port field " \ 184 "\"file_name\" is not NULL.\n"); \ 185 _ret = -1; \ 186 } \ 187 \ 188 _ret; \ 189 }) 190 191 #endif /* RTE_PORT_PCAP */ 192 193 static void * 194 rte_port_source_create(void *params, int socket_id) 195 { 196 struct rte_port_source_params *p = 197 params; 198 struct rte_port_source *port; 199 200 /* Check input arguments*/ 201 if ((p == NULL) || (p->mempool == NULL)) { 202 RTE_LOG(ERR, PORT, "%s: Invalid params\n", __func__); 203 return NULL; 204 } 205 206 /* Memory allocation */ 207 port = rte_zmalloc_socket("PORT", sizeof(*port), 208 RTE_CACHE_LINE_SIZE, socket_id); 209 if (port == NULL) { 210 RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n", __func__); 211 return NULL; 212 } 213 214 /* Initialization */ 215 port->mempool = (struct rte_mempool *) p->mempool; 216 217 if (p->file_name) { 218 int status = PCAP_SOURCE_LOAD(port, p->file_name, 219 p->n_bytes_per_pkt, socket_id); 220 221 if (status < 0) { 222 rte_free(port); 223 port = NULL; 224 } 225 } 226 227 return port; 228 } 229 230 static int 231 rte_port_source_free(void *port) 232 { 233 struct rte_port_source *p = 234 port; 235 236 /* Check input parameters */ 237 if (p == NULL) 238 return 0; 239 240 rte_free(p->pkt_len); 241 rte_free(p->pkts); 242 rte_free(p->pkt_buff); 243 244 rte_free(p); 245 246 return 0; 247 } 248 249 static int 250 rte_port_source_rx(void *port, struct rte_mbuf **pkts, uint32_t n_pkts) 251 { 252 struct rte_port_source *p = port; 253 uint32_t i; 254 255 if (rte_pktmbuf_alloc_bulk(p->mempool, pkts, n_pkts) != 0) 256 return 0; 257 258 if (p->pkt_buff != NULL) { 259 for (i = 0; i < n_pkts; i++) { 260 uint8_t *pkt_data = rte_pktmbuf_mtod(pkts[i], 261 uint8_t *); 262 263 rte_memcpy(pkt_data, p->pkts[p->pkt_index], 264 p->pkt_len[p->pkt_index]); 265 pkts[i]->data_len = p->pkt_len[p->pkt_index]; 266 pkts[i]->pkt_len = pkts[i]->data_len; 267 268 p->pkt_index++; 269 if (p->pkt_index >= p->n_pkts) 270 p->pkt_index = 0; 271 } 272 } 273 274 RTE_PORT_SOURCE_STATS_PKTS_IN_ADD(p, n_pkts); 275 276 return n_pkts; 277 } 278 279 static int 280 rte_port_source_stats_read(void *port, 281 struct rte_port_in_stats *stats, int clear) 282 { 283 struct rte_port_source *p = 284 port; 285 286 if (stats != NULL) 287 memcpy(stats, &p->stats, sizeof(p->stats)); 288 289 if (clear) 290 memset(&p->stats, 0, sizeof(p->stats)); 291 292 return 0; 293 } 294 295 /* 296 * Port SINK 297 */ 298 #ifdef RTE_PORT_STATS_COLLECT 299 300 #define RTE_PORT_SINK_STATS_PKTS_IN_ADD(port, val) \ 301 (port->stats.n_pkts_in += val) 302 #define RTE_PORT_SINK_STATS_PKTS_DROP_ADD(port, val) \ 303 (port->stats.n_pkts_drop += val) 304 305 #else 306 307 #define RTE_PORT_SINK_STATS_PKTS_IN_ADD(port, val) 308 #define RTE_PORT_SINK_STATS_PKTS_DROP_ADD(port, val) 309 310 #endif 311 312 struct rte_port_sink { 313 struct rte_port_out_stats stats; 314 315 /* PCAP dumper handle and pkts number */ 316 void *dumper; 317 uint32_t max_pkts; 318 uint32_t pkt_index; 319 uint32_t dump_finish; 320 }; 321 322 #ifdef RTE_PORT_PCAP 323 324 static int 325 pcap_sink_open(struct rte_port_sink *port, 326 const char *file_name, 327 uint32_t max_n_pkts) 328 { 329 pcap_t *tx_pcap; 330 pcap_dumper_t *pcap_dumper; 331 332 /** Open a dead pcap handler for opening dumper file */ 333 tx_pcap = pcap_open_dead(DLT_EN10MB, 65535); 334 if (tx_pcap == NULL) { 335 RTE_LOG(ERR, PORT, "Cannot open pcap dead handler\n"); 336 return -1; 337 } 338 339 /* The dumper is created using the previous pcap_t reference */ 340 pcap_dumper = pcap_dump_open(tx_pcap, file_name); 341 if (pcap_dumper == NULL) { 342 RTE_LOG(ERR, PORT, "Failed to open pcap file " 343 "\"%s\" for writing\n", file_name); 344 return -1; 345 } 346 347 port->dumper = pcap_dumper; 348 port->max_pkts = max_n_pkts; 349 port->pkt_index = 0; 350 port->dump_finish = 0; 351 352 RTE_LOG(INFO, PORT, "Ready to dump packets to file \"%s\"\n", 353 file_name); 354 355 return 0; 356 } 357 358 static void 359 pcap_sink_write_pkt(struct rte_port_sink *port, struct rte_mbuf *mbuf) 360 { 361 uint8_t *pcap_dumper = (port->dumper); 362 struct pcap_pkthdr pcap_hdr; 363 uint8_t jumbo_pkt_buf[RTE_ETHER_MAX_JUMBO_FRAME_LEN]; 364 uint8_t *pkt; 365 366 /* Maximum num packets already reached */ 367 if (port->dump_finish) 368 return; 369 370 pkt = rte_pktmbuf_mtod(mbuf, uint8_t *); 371 372 pcap_hdr.len = mbuf->pkt_len; 373 pcap_hdr.caplen = pcap_hdr.len; 374 gettimeofday(&(pcap_hdr.ts), NULL); 375 376 if (mbuf->nb_segs > 1) { 377 struct rte_mbuf *jumbo_mbuf; 378 uint32_t pkt_index = 0; 379 380 /* if packet size longer than RTE_ETHER_MAX_JUMBO_FRAME_LEN, 381 * ignore it. 382 */ 383 if (mbuf->pkt_len > RTE_ETHER_MAX_JUMBO_FRAME_LEN) 384 return; 385 386 for (jumbo_mbuf = mbuf; jumbo_mbuf != NULL; 387 jumbo_mbuf = jumbo_mbuf->next) { 388 rte_memcpy(&jumbo_pkt_buf[pkt_index], 389 rte_pktmbuf_mtod(jumbo_mbuf, uint8_t *), 390 jumbo_mbuf->data_len); 391 pkt_index += jumbo_mbuf->data_len; 392 } 393 394 jumbo_pkt_buf[pkt_index] = '\0'; 395 396 pkt = jumbo_pkt_buf; 397 } 398 399 pcap_dump(pcap_dumper, &pcap_hdr, pkt); 400 401 port->pkt_index++; 402 403 if ((port->max_pkts != 0) && (port->pkt_index >= port->max_pkts)) { 404 port->dump_finish = 1; 405 RTE_LOG(INFO, PORT, "Dumped %u packets to file\n", 406 port->pkt_index); 407 } 408 409 } 410 411 #define PCAP_SINK_OPEN(port, file_name, max_n_pkts) \ 412 pcap_sink_open(port, file_name, max_n_pkts) 413 414 #define PCAP_SINK_WRITE_PKT(port, mbuf) \ 415 pcap_sink_write_pkt(port, mbuf) 416 417 #define PCAP_SINK_FLUSH_PKT(dumper) \ 418 do { \ 419 if (dumper) \ 420 pcap_dump_flush((pcap_dumper_t *)dumper); \ 421 } while (0) 422 423 #define PCAP_SINK_CLOSE(dumper) \ 424 do { \ 425 if (dumper) \ 426 pcap_dump_close((pcap_dumper_t *)dumper); \ 427 } while (0) 428 429 #else 430 431 #define PCAP_SINK_OPEN(port, file_name, max_n_pkts) \ 432 ({ \ 433 int _ret = 0; \ 434 \ 435 if (file_name) { \ 436 RTE_LOG(ERR, PORT, "Sink port field " \ 437 "\"file_name\" is not NULL.\n"); \ 438 _ret = -1; \ 439 } \ 440 \ 441 _ret; \ 442 }) 443 444 #define PCAP_SINK_WRITE_PKT(port, mbuf) {} 445 446 #define PCAP_SINK_FLUSH_PKT(dumper) 447 448 #define PCAP_SINK_CLOSE(dumper) 449 450 #endif 451 452 static void * 453 rte_port_sink_create(void *params, int socket_id) 454 { 455 struct rte_port_sink *port; 456 struct rte_port_sink_params *p = params; 457 458 /* Memory allocation */ 459 port = rte_zmalloc_socket("PORT", sizeof(*port), 460 RTE_CACHE_LINE_SIZE, socket_id); 461 if (port == NULL) { 462 RTE_LOG(ERR, PORT, "%s: Failed to allocate port\n", __func__); 463 return NULL; 464 } 465 466 if (!p) 467 return port; 468 469 if (p->file_name) { 470 int status = PCAP_SINK_OPEN(port, p->file_name, 471 p->max_n_pkts); 472 473 if (status < 0) { 474 rte_free(port); 475 port = NULL; 476 } 477 } 478 479 return port; 480 } 481 482 static int 483 rte_port_sink_tx(void *port, struct rte_mbuf *pkt) 484 { 485 struct rte_port_sink *p = port; 486 487 RTE_PORT_SINK_STATS_PKTS_IN_ADD(p, 1); 488 if (p->dumper != NULL) 489 PCAP_SINK_WRITE_PKT(p, pkt); 490 rte_pktmbuf_free(pkt); 491 RTE_PORT_SINK_STATS_PKTS_DROP_ADD(p, 1); 492 493 return 0; 494 } 495 496 static int 497 rte_port_sink_tx_bulk(void *port, struct rte_mbuf **pkts, 498 uint64_t pkts_mask) 499 { 500 struct rte_port_sink *p = port; 501 502 if ((pkts_mask & (pkts_mask + 1)) == 0) { 503 uint64_t n_pkts = __builtin_popcountll(pkts_mask); 504 uint32_t i; 505 506 RTE_PORT_SINK_STATS_PKTS_IN_ADD(p, n_pkts); 507 RTE_PORT_SINK_STATS_PKTS_DROP_ADD(p, n_pkts); 508 509 if (p->dumper) { 510 for (i = 0; i < n_pkts; i++) 511 PCAP_SINK_WRITE_PKT(p, pkts[i]); 512 } 513 514 for (i = 0; i < n_pkts; i++) { 515 struct rte_mbuf *pkt = pkts[i]; 516 517 rte_pktmbuf_free(pkt); 518 } 519 520 } else { 521 if (p->dumper) { 522 uint64_t dump_pkts_mask = pkts_mask; 523 uint32_t pkt_index; 524 525 for ( ; dump_pkts_mask; ) { 526 pkt_index = __builtin_ctzll( 527 dump_pkts_mask); 528 PCAP_SINK_WRITE_PKT(p, pkts[pkt_index]); 529 dump_pkts_mask &= ~(1LLU << pkt_index); 530 } 531 } 532 533 for ( ; pkts_mask; ) { 534 uint32_t pkt_index = __builtin_ctzll(pkts_mask); 535 uint64_t pkt_mask = 1LLU << pkt_index; 536 struct rte_mbuf *pkt = pkts[pkt_index]; 537 538 RTE_PORT_SINK_STATS_PKTS_IN_ADD(p, 1); 539 RTE_PORT_SINK_STATS_PKTS_DROP_ADD(p, 1); 540 rte_pktmbuf_free(pkt); 541 pkts_mask &= ~pkt_mask; 542 } 543 } 544 545 return 0; 546 } 547 548 static int 549 rte_port_sink_flush(void *port) 550 { 551 struct rte_port_sink *p = 552 port; 553 554 if (p == NULL) 555 return 0; 556 557 PCAP_SINK_FLUSH_PKT(p->dumper); 558 559 return 0; 560 } 561 562 static int 563 rte_port_sink_free(void *port) 564 { 565 struct rte_port_sink *p = 566 port; 567 568 if (p == NULL) 569 return 0; 570 571 PCAP_SINK_CLOSE(p->dumper); 572 573 rte_free(p); 574 575 return 0; 576 } 577 578 static int 579 rte_port_sink_stats_read(void *port, struct rte_port_out_stats *stats, 580 int clear) 581 { 582 struct rte_port_sink *p = 583 port; 584 585 if (stats != NULL) 586 memcpy(stats, &p->stats, sizeof(p->stats)); 587 588 if (clear) 589 memset(&p->stats, 0, sizeof(p->stats)); 590 591 return 0; 592 } 593 594 /* 595 * Summary of port operations 596 */ 597 struct rte_port_in_ops rte_port_source_ops = { 598 .f_create = rte_port_source_create, 599 .f_free = rte_port_source_free, 600 .f_rx = rte_port_source_rx, 601 .f_stats = rte_port_source_stats_read, 602 }; 603 604 struct rte_port_out_ops rte_port_sink_ops = { 605 .f_create = rte_port_sink_create, 606 .f_free = rte_port_sink_free, 607 .f_tx = rte_port_sink_tx, 608 .f_tx_bulk = rte_port_sink_tx_bulk, 609 .f_flush = rte_port_sink_flush, 610 .f_stats = rte_port_sink_stats_read, 611 }; 612