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