/* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2020 Intel Corporation */ #include #include #include #ifdef RTE_PORT_PCAP #include #endif #include #include #include #include #include "rte_swx_port_source_sink.h" #define CHECK(condition) \ do { \ if (!(condition)) \ return NULL; \ } while (0) #ifndef TRACE_LEVEL #define TRACE_LEVEL 0 #endif #if TRACE_LEVEL #define TRACE(...) printf(__VA_ARGS__) #else #define TRACE(...) #endif /* * Port SOURCE */ #ifdef RTE_PORT_PCAP struct source { struct { struct rte_mempool *pool; uint64_t n_loops; } params; struct rte_swx_port_in_stats stats; struct rte_mbuf **pkts; uint32_t n_pkts; uint32_t pos; }; static void source_free(void *port) { struct source *p = port; uint32_t i; if (!p) return; for (i = 0; i < p->n_pkts; i++) rte_pktmbuf_free(p->pkts[i]); free(p->pkts); free(p); } static void * source_create(void *args) { char pcap_errbuf[PCAP_ERRBUF_SIZE]; struct rte_swx_port_source_params *params = args; struct source *p = NULL; pcap_t *f = NULL; uint32_t n_pkts_max, i; /* Check input arguments. */ CHECK(params); CHECK(params->pool); CHECK(params->file_name && params->file_name[0]); n_pkts_max = params->n_pkts_max ? params->n_pkts_max : RTE_SWX_PORT_SOURCE_PKTS_MAX; /* Resource allocation. */ f = pcap_open_offline(params->file_name, pcap_errbuf); if (!f) goto error; p = calloc(1, sizeof(struct source)); if (!p) goto error; p->pkts = calloc(n_pkts_max, sizeof(struct rte_mbuf *)); if (!p->pkts) goto error; /* Initialization. */ p->params.pool = params->pool; p->params.n_loops = params->n_loops ? params->n_loops : UINT64_MAX; /* PCAP file. */ for (i = 0; i < n_pkts_max; i++) { struct pcap_pkthdr pcap_pkthdr; const uint8_t *pcap_pktdata; struct rte_mbuf *m; uint8_t *m_data; /* Read new packet from PCAP file. */ pcap_pktdata = pcap_next(f, &pcap_pkthdr); if (!pcap_pktdata) break; /* Allocate new buffer from pool. */ m = rte_pktmbuf_alloc(params->pool); if (!m) goto error; m_data = rte_pktmbuf_mtod(m, uint8_t *); rte_memcpy(m_data, pcap_pktdata, pcap_pkthdr.caplen); m->data_len = pcap_pkthdr.caplen; m->pkt_len = pcap_pkthdr.caplen; p->pkts[p->n_pkts] = m; p->n_pkts++; } if (!p->n_pkts) goto error; pcap_close(f); return p; error: source_free(p); if (f) pcap_close(f); return NULL; } static int source_pkt_rx(void *port, struct rte_swx_pkt *pkt) { struct source *p = port; struct rte_mbuf *m_dst, *m_src; uint8_t *m_dst_data, *m_src_data; if (!p->params.n_loops) return 0; /* m_src identification. */ m_src = p->pkts[p->pos]; m_src_data = rte_pktmbuf_mtod(m_src, uint8_t *); /* m_dst allocation from pool. */ m_dst = rte_pktmbuf_alloc(p->params.pool); if (!m_dst) return 0; /* m_dst initialization. */ m_dst->data_len = m_src->data_len; m_dst->pkt_len = m_src->pkt_len; m_dst->data_off = m_src->data_off; m_dst_data = rte_pktmbuf_mtod(m_dst, uint8_t *); rte_memcpy(m_dst_data, m_src_data, m_src->data_len); /* pkt initialization. */ pkt->handle = m_dst; pkt->pkt = m_dst->buf_addr; pkt->offset = m_dst->data_off; pkt->length = m_dst->pkt_len; TRACE("[Source port] Pkt RX (%u bytes at offset %u)\n", pkt->length, pkt->offset); if (TRACE_LEVEL) rte_hexdump(stdout, NULL, &pkt->pkt[pkt->offset], pkt->length); /* port stats update. */ p->stats.n_pkts++; p->stats.n_bytes += pkt->length; /* m_src next. */ p->pos++; if (p->pos == p->n_pkts) { p->pos = 0; p->params.n_loops--; } return 1; } static void source_stats_read(void *port, struct rte_swx_port_in_stats *stats) { struct source *p = port; if (!p || !stats) return; memcpy(stats, &p->stats, sizeof(p->stats)); } struct rte_swx_port_in_ops rte_swx_port_source_ops = { .create = source_create, .free = source_free, .pkt_rx = source_pkt_rx, .stats_read = source_stats_read, }; #else struct rte_swx_port_in_ops rte_swx_port_source_ops = { .create = NULL, .free = NULL, .pkt_rx = NULL, .stats_read = NULL, }; #endif /* * Port SINK */ struct sink { struct rte_swx_port_out_stats stats; #ifdef RTE_PORT_PCAP pcap_t *f_pcap; pcap_dumper_t *f_dump; #endif }; static void sink_free(void *port) { struct sink *p = port; if (!p) return; #ifdef RTE_PORT_PCAP if (p->f_dump) pcap_dump_close(p->f_dump); if (p->f_pcap) pcap_close(p->f_pcap); #endif free(p); } static void * sink_create(void *args __rte_unused) { struct sink *p; /* Memory allocation. */ p = calloc(1, sizeof(struct sink)); if (!p) goto error; #ifdef RTE_PORT_PCAP if (args) { struct rte_swx_port_sink_params *params = args; if (params->file_name && params->file_name[0]) { p->f_pcap = pcap_open_dead(DLT_EN10MB, 65535); if (!p->f_pcap) goto error; p->f_dump = pcap_dump_open(p->f_pcap, params->file_name); if (!p->f_dump) goto error; } } #endif return p; error: sink_free(p); return NULL; } static void sink_pkt_tx(void *port, struct rte_swx_pkt *pkt) { struct sink *p = port; struct rte_mbuf *m = pkt->handle; TRACE("[Sink port] Pkt TX (%u bytes at offset %u)\n", pkt->length, pkt->offset); if (TRACE_LEVEL) rte_hexdump(stdout, NULL, &pkt->pkt[pkt->offset], pkt->length); m->data_len = (uint16_t)(pkt->length + m->data_len - m->pkt_len); m->pkt_len = pkt->length; m->data_off = (uint16_t)pkt->offset; p->stats.n_pkts_drop++; p->stats.n_bytes_drop += pkt->length; #ifdef RTE_PORT_PCAP if (p->f_dump) { struct pcap_pkthdr pcap_pkthdr; uint8_t *m_data = rte_pktmbuf_mtod(m, uint8_t *); pcap_pkthdr.len = m->pkt_len; pcap_pkthdr.caplen = m->data_len; gettimeofday(&pcap_pkthdr.ts, NULL); pcap_dump((uint8_t *)p->f_dump, &pcap_pkthdr, m_data); pcap_dump_flush(p->f_dump); } #endif rte_pktmbuf_free(m); } static void __sink_pkt_clone_tx(void *port, struct rte_swx_pkt *pkt, uint32_t truncation_length __rte_unused) { struct sink *p = port; struct rte_mbuf *m = pkt->handle; TRACE("[Sink port] Pkt TX (%u bytes at offset %u) (clone)\n", pkt->length, pkt->offset); if (TRACE_LEVEL) rte_hexdump(stdout, NULL, &pkt->pkt[pkt->offset], pkt->length); m->data_len = (uint16_t)(pkt->length + m->data_len - m->pkt_len); m->pkt_len = pkt->length; m->data_off = (uint16_t)pkt->offset; p->stats.n_pkts_drop++; p->stats.n_bytes_drop += pkt->length; p->stats.n_pkts_clone++; #ifdef RTE_PORT_PCAP if (p->f_dump) { struct pcap_pkthdr pcap_pkthdr; uint8_t *m_data = rte_pktmbuf_mtod(m, uint8_t *); pcap_pkthdr.len = m->pkt_len; pcap_pkthdr.caplen = RTE_MIN(m->data_len, truncation_length); gettimeofday(&pcap_pkthdr.ts, NULL); pcap_dump((uint8_t *)p->f_dump, &pcap_pkthdr, m_data); pcap_dump_flush(p->f_dump); } #endif } static void sink_pkt_fast_clone_tx(void *port, struct rte_swx_pkt *pkt) { __sink_pkt_clone_tx(port, pkt, UINT32_MAX); } static void sink_pkt_clone_tx(void *port, struct rte_swx_pkt *pkt, uint32_t truncation_length) { __sink_pkt_clone_tx(port, pkt, truncation_length); } static void sink_stats_read(void *port, struct rte_swx_port_out_stats *stats) { struct sink *p = port; if (!p || !stats) return; memcpy(stats, &p->stats, sizeof(p->stats)); } /* * Summary of port operations */ struct rte_swx_port_out_ops rte_swx_port_sink_ops = { .create = sink_create, .free = sink_free, .pkt_tx = sink_pkt_tx, .pkt_fast_clone_tx = sink_pkt_fast_clone_tx, .pkt_clone_tx = sink_pkt_clone_tx, .flush = NULL, .stats_read = sink_stats_read, };