/* * BSD LICENSE * * Copyright(c) 2016 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "pcapng_proto.h" #include "rte_mbuf_core.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CMD_LINE_OPT_PDUMP "pdump" #define PDUMP_DEV_NAME_ARG "dev_name" #define PDUMP_QUEUE_ARG "queue" #define PDUMP_RX_ARG "rx" #define PDUMP_TX_ARG "tx" #define PDUMP_RING_SIZE_ARG "ring-size" #define PDUMP_MSIZE_ARG "mbuf-size" #define PDUMP_NUM_MBUFS_ARG "total-num-mbufs" #define CMD_LINE_OPT_DUMPFILE_PATH "dumpfile-path" #define CMD_LINE_OPT_DUMPFILE_COUNT "dumpfile-count" #define CMD_LINE_OPT_BPF_RULE "bpf-rule" #define CMD_LINE_OPT_OFFSET "offset" #define CMD_LINE_OPT_SNAPSHOT_LENGTH "snapshot-length" #define CMD_LINE_OPT_HELP "help" #define TX_STREAM_SIZE 64 /* Maximum long option length for option parsing. */ #define APP_ARG_TCPDUMP_MAX_TUPLES 54 #define MBUF_POOL_CACHE_SIZE 250 #define TX_DESC_PER_QUEUE 512 #define RX_DESC_PER_QUEUE 128 #define MBUFS_PER_POOL MR_PDUMP_MBUFS_PER_POOL #define MAX_LONG_OPT_SZ 64 #define SIZE 256 #define BURST_SIZE 32 #define NUM_VDEVS 2 /* true if x is a power of 2 */ #define POWEROF2(x) ((((x) - 1) & (x)) == 0) #define METADATA_MAX_SIZE 256 enum pcap_stream { IFACE = 1, PCAP = 2 }; enum pdump_by { PORT_ID = 1, DEVICE_ID = 2 }; const char * valid_pdump_arguments[] = {PDUMP_DEV_NAME_ARG, PDUMP_QUEUE_ARG, PDUMP_RX_ARG, PDUMP_TX_ARG, PDUMP_RING_SIZE_ARG, PDUMP_MSIZE_ARG, PDUMP_NUM_MBUFS_ARG, NULL}; struct pdump_stats { uint64_t dequeue_pkts; uint64_t rx_pkts; uint64_t tx_pkts; uint64_t freed_pkts; uint64_t bpf_filter_pkts; }; struct pdump_tuples { /* cli params */ uint16_t port_id; enum mr_dev_driver drv_type; char dev_symbol[MR_SYMBOL_MAX]; char * device_id; struct rte_ether_addr eth_addr; uint16_t queue; char rx_dev[TX_STREAM_SIZE]; char tx_dev[TX_STREAM_SIZE]; uint32_t ring_size; uint16_t mbuf_data_size; uint32_t total_num_mbufs; /* params for library API call */ uint32_t dir; struct rte_mempool * mp; struct rte_ring * rx_ring; struct rte_ring * tx_ring; /* params for packet dumping */ enum pdump_by dump_by_type; uint16_t rx_vdev_id; uint16_t tx_vdev_id; enum pcap_stream rx_vdev_stream_type; enum pcap_stream tx_vdev_stream_type; bool single_pdump_dev; /* stats */ struct pdump_stats stats; } __rte_cache_aligned; static struct pdump_tuples pdump_t[APP_ARG_TCPDUMP_MAX_TUPLES]; struct parse_val { uint64_t min; uint64_t max; uint64_t val; }; struct ppi_packetheader { uint8_t pph_version; // Version. Currently 0 uint8_t pph_flags; // Flags. uint16_t pph_len; // Length of entire message, including this header and TLV payload. uint32_t pph_dlt; // libpcap Data Link Type of the captured packet data. }; /* For converting TSC cycles to PCAPNG ns format */ struct pcapng_time { uint64_t ns; uint64_t cycles; } pcapng_time; int num_tuples; volatile uint8_t quit_signal; static char dumpfile_path[PATH_MAX] = "dumpfile.pcapng"; static bool dumpfile_count_set = false; static unsigned long long dumpfile_count = 0; static struct bpf_program bpf_prog; static char str_bpf_expr[4096] = {}; static bool is_str_bpf_expr_set = false; static uint32_t snaplen = UINT32_MAX; static uint32_t offset = 0; static struct rte_bpf_prm * prm = NULL; uint16_t interface_index = 0; static uint16_t port_index[MR_DEVICE_MAX] = {}; /* Pcapng */ static int dumpfile_fd = 0; rte_pcapng_t * pcapng = NULL; /**< display usage */ static void pdump_usage(const char * prgname) { fprintf(stderr, "\nUsage: %s [EAL options] -- --pdump " "'dev_name=)," "(queue=)," "(rx|tx)'\n" "[--dumpfile-path=] default:dumpfile.pcap\n" "[--dumpfile-count=]\n" "[--bpf-rule=] default:\n" "[--offset=>] default:0\n" "[--snapshot-length=] default:\n" "[--help]\n", prgname); } static int parse_queue(const char * key __rte_unused, const char * value, void * extra_args) { unsigned long n; struct pdump_tuples * pt = extra_args; if (!strcmp(value, "*")) pt->queue = RTE_PDUMP_ALL_QUEUES; else { n = strtoul(value, NULL, 10); pt->queue = (uint16_t)n; } return 0; } static int parse_uint_value(const char * key, const char * value, void * extra_args) { struct parse_val * v; unsigned long t; char * end; int ret = 0; errno = 0; v = extra_args; t = strtoul(value, &end, 10); if (errno != 0 || end[0] != 0 || t < v->min || t > v->max) { fprintf(stderr, "invalid value:\"%s\" for key:\"%s\", " "value must be >= %" PRIu64 " and <= %" PRIu64 "\n", value, key, v->min, v->max); ret = -EINVAL; } if (!strcmp(key, PDUMP_RING_SIZE_ARG) && !POWEROF2(t)) { fprintf(stderr, "invalid value:\"%s\" for key:\"%s\", " "value must be power of 2\n", value, key); ret = -EINVAL; } if (ret != 0) return ret; v->val = t; return 0; } static int parse_device_name(const char * key __rte_unused, const char * value, void * extra_args) { struct pdump_tuples * pt = &pdump_t[num_tuples]; strlcpy(pt->dev_symbol, value, sizeof(pt->dev_symbol)); return 0; } static int parse_pdump(const char * optarg) { struct rte_kvargs * kvlist; int ret = 0, cnt1, cnt2; struct pdump_tuples * pt; struct parse_val v = {0}; pt = &pdump_t[num_tuples]; /* initial check for invalid arguments */ kvlist = rte_kvargs_parse(optarg, valid_pdump_arguments); if (kvlist == NULL) { fprintf(stderr, "--pdump=\"%s\": invalid argument passed\n", optarg); return -1; } /* port_id/device_id parsing and validation */ cnt1 = rte_kvargs_count(kvlist, PDUMP_DEV_NAME_ARG); if (cnt1 == 1) { ret = rte_kvargs_process(kvlist, PDUMP_DEV_NAME_ARG, &parse_device_name, NULL); if (ret < 0) goto free_kvlist; } else { fprintf(stderr, "--pdump=\"%s\": must have dev argument\n", optarg); ret = -1; goto free_kvlist; } /* queue parsing and validation */ cnt1 = rte_kvargs_count(kvlist, PDUMP_QUEUE_ARG); if (cnt1 != 1) { fprintf(stderr, "--pdump=\"%s\": must have queue argument\n", optarg); ret = -1; goto free_kvlist; } ret = rte_kvargs_process(kvlist, PDUMP_QUEUE_ARG, &parse_queue, pt); if (ret < 0) goto free_kvlist; cnt1 = rte_kvargs_count(kvlist, PDUMP_RX_ARG); cnt2 = rte_kvargs_count(kvlist, PDUMP_TX_ARG); if ((cnt1 == 0 && cnt2 == 0) || (cnt1 == 1 && cnt2 == 1)) { pt->dir = RTE_PDUMP_FLAG_RXTX; } else if (cnt1 == 1) { pt->dir = RTE_PDUMP_FLAG_RX; } else if (cnt2 == 1) { pt->dir = RTE_PDUMP_FLAG_TX; } else { fprintf(stderr, "--pdump=\"%s\": too many rx or tx options\n", optarg); ret = -1; goto free_kvlist; } /* optional */ /* ring_size parsing and validation */ cnt1 = rte_kvargs_count(kvlist, PDUMP_RING_SIZE_ARG); if (cnt1 == 1) { v.min = 2; v.max = RTE_RING_SZ_MASK - 1; ret = rte_kvargs_process(kvlist, PDUMP_RING_SIZE_ARG, &parse_uint_value, &v); if (ret < 0) goto free_kvlist; pt->ring_size = (uint32_t)v.val; } else pt->ring_size = MR_PDUMP_RING_SIZE; /* mbuf_data_size parsing and validation */ cnt1 = rte_kvargs_count(kvlist, PDUMP_MSIZE_ARG); if (cnt1 == 1) { v.min = 1; v.max = UINT16_MAX; ret = rte_kvargs_process(kvlist, PDUMP_MSIZE_ARG, &parse_uint_value, &v); if (ret < 0) goto free_kvlist; // pt->mbuf_data_size = (uint16_t)v.val; /* Current Only Support Specify 'RTE_MBUF_DEFAULT_BUF_SIZE'*/ pt->mbuf_data_size = RTE_MBUF_DEFAULT_BUF_SIZE; } else pt->mbuf_data_size = RTE_MBUF_DEFAULT_BUF_SIZE; /* total_num_mbufs parsing and validation */ cnt1 = rte_kvargs_count(kvlist, PDUMP_NUM_MBUFS_ARG); if (cnt1 == 1) { v.min = 1025; v.max = UINT16_MAX; ret = rte_kvargs_process(kvlist, PDUMP_NUM_MBUFS_ARG, &parse_uint_value, &v); if (ret < 0) goto free_kvlist; // pt->total_num_mbufs = (uint16_t)v.val; /* Current Only Support Specify 'MBUFS_PER_POOL'*/ pt->total_num_mbufs = MBUFS_PER_POOL; } else pt->total_num_mbufs = MBUFS_PER_POOL; num_tuples++; if (num_tuples > MR_PDUMP_MEMPOOL_NUM) { fprintf(stderr, "Current Only Support Max Dev Num Is:%u\n", MR_PDUMP_MEMPOOL_NUM); ret = RT_ERR; } free_kvlist: rte_kvargs_free(kvlist); return ret; } /* Parse the argument given in the command line of the application */ static int launch_args_parse(int argc, char ** argv, char * prgname) { int opt, ret; int option_index; static struct option long_option[] = { {"pdump", 1, 0, 0}, {"dumpfile-path", 1, 0, 0}, {"dumpfile-count", 1, 0, 0}, {"bpf-rule", 1, 0, 0}, {"offset", 1, 0, 0}, {"use-ppi-header", 0, 0, 0}, {"snapshot-length", 1, 0, 0}, {"help", 0, 0, 0}, {NULL, 0, 0, 0}}; if (argc == 1) pdump_usage(prgname); /* Parse command line */ while ((opt = getopt_long(argc, argv, " ", long_option, &option_index)) != EOF) { switch (opt) { case 0: if (!strncmp(long_option[option_index].name, CMD_LINE_OPT_PDUMP, sizeof(CMD_LINE_OPT_PDUMP))) { ret = parse_pdump(optarg); if (ret) { pdump_usage(prgname); return -1; } } if (!strncmp(long_option[option_index].name, CMD_LINE_OPT_DUMPFILE_PATH, sizeof(CMD_LINE_OPT_DUMPFILE_PATH))) { snprintf(dumpfile_path, sizeof(dumpfile_path), "%s", optarg); } if (!strncmp(long_option[option_index].name, CMD_LINE_OPT_DUMPFILE_COUNT, sizeof(CMD_LINE_OPT_DUMPFILE_COUNT))) { dumpfile_count_set = true; sscanf(optarg, "%llu", &dumpfile_count); } if (!strncmp(long_option[option_index].name, CMD_LINE_OPT_BPF_RULE, sizeof(CMD_LINE_OPT_BPF_RULE))) { is_str_bpf_expr_set = true; snprintf(str_bpf_expr, sizeof(str_bpf_expr), "%s", optarg); } if (!strncmp(long_option[option_index].name, CMD_LINE_OPT_OFFSET, sizeof(CMD_LINE_OPT_OFFSET))) { sscanf(optarg, "%" SCNu32, &offset); } if (!strncmp(long_option[option_index].name, CMD_LINE_OPT_SNAPSHOT_LENGTH, sizeof(CMD_LINE_OPT_SNAPSHOT_LENGTH))) { sscanf(optarg, "%" SCNu32, &snaplen); } if (!strncmp(long_option[option_index].name, CMD_LINE_OPT_HELP, sizeof(CMD_LINE_OPT_HELP))) { pdump_usage(prgname); return 0; } break; default: pdump_usage(prgname); return -1; } } return 1; } static void print_pdump_stats(void) { int i; struct pdump_tuples * pt; for (i = 0; i < num_tuples; i++) { fprintf(stderr, "##### PDUMP DEBUG STATS #####\n"); pt = &pdump_t[i]; fprintf(stderr, " -packets dequeued: %" PRIu64 "\n", pt->stats.dequeue_pkts); // fprintf(stderr, " -packets rx: %" PRIu64 "\n", pt->stats.rx_pkts); // fprintf(stderr, " -packets tx: %" PRIu64 "\n", pt->stats.tx_pkts); fprintf(stderr, " -packets freed: %" PRIu64 "\n", pt->stats.freed_pkts); // fprintf(stderr, " -packets for bpf filter: %" PRIu64 "\n", pt->stats.bpf_filter_pkts); } } /* length of option including padding */ static uint16_t pcapng_optlen(uint16_t len) { return RTE_ALIGN(sizeof(struct pcapng_option) + len, sizeof(uint32_t)); } static struct pcapng_option * pcapng_add_option(struct pcapng_option * popt, uint16_t code, const void * data, uint16_t len) { popt->code = code; popt->length = len; memcpy(popt->data, data, len); return (struct pcapng_option *)((uint8_t *)popt + pcapng_optlen(len)); } /* Count how many segments are in this array of mbufs */ static unsigned int mbuf_burst_segs(struct rte_mbuf * pkts[], unsigned int n) { unsigned int i, iovcnt; for (iovcnt = 0, i = 0; i < n; i++) { const struct rte_mbuf * m = pkts[i]; __rte_mbuf_sanity_check(m, 1); iovcnt += m->nb_segs; } return iovcnt; } int mr_pcapng_add_opt(struct rte_mbuf * mbuf, uint16_t port_id, uint32_t rx_tx_flags) { /* check the epb info */ struct pcapng_enhance_packet_block * epb = rte_pktmbuf_mtod(mbuf, struct pcapng_enhance_packet_block *); if (unlikely(epb->block_type != PCAPNG_ENHANCED_PACKET_BLOCK || epb->block_length != rte_pktmbuf_data_len(mbuf))) { fprintf(stderr, "received packet with incorrect EPB information error.\n"); return RT_ERR; } /* pad the packet to 32 bit boundary */ uint32_t original_length = rte_pktmbuf_pkt_len(mbuf); uint32_t padding = RTE_ALIGN(original_length, sizeof(uint32_t)) - original_length; if (padding > 0) { void * tail = rte_pktmbuf_append(mbuf, padding); if (tail == NULL) { fprintf(stderr, "mbuf padding err.\n"); return RT_ERR; } memset(tail, 0, padding); } /* reserve trailing options and block length */ int len = 0; char str_rx_tx[METADATA_MAX_SIZE]; char str_sid_list[METADATA_MAX_SIZE]; char str_dir[METADATA_MAX_SIZE]; char str_sid_raw[METADATA_MAX_SIZE]; uint32_t str_maxlen = METADATA_MAX_SIZE - 1; struct mrb_metadata * mrb_metadata = mrbuf_cz_data(mbuf, MR_NODE_CTRLZONE_ID); if (rx_tx_flags == RTE_PDUMP_FLAG_RX) len = snprintf(str_rx_tx, str_maxlen, "rx"); else len = snprintf(str_rx_tx, str_maxlen, "tx"); len += snprintf(str_rx_tx + len, str_maxlen - len, ", port_ingress:%u", mrb_metadata->port_ingress); len += snprintf(str_rx_tx + len, str_maxlen - len, ", port_egress:%u", mrb_metadata->port_egress); len += snprintf(str_rx_tx + len, str_maxlen - len, ", is_nf_create:%u", mrb_metadata->packet_create_from_nf); len += snprintf(str_rx_tx + len, str_maxlen - len, ", measurement_type:%u", mrb_metadata->measurement_type); len += snprintf(str_rx_tx + len, str_maxlen - len, ", is_ctrlbuf:%u", mrb_metadata->is_ctrlbuf); len += snprintf(str_rx_tx + len, str_maxlen - len, ", payload_offset:%u", mrb_metadata->payload_offset); len += snprintf(str_rx_tx + len, str_maxlen - len, ", user0:%u", mrb_metadata->user_0); len += snprintf(str_rx_tx + len, str_maxlen - len, ", session_id:%lu", mrb_metadata->session_id); len += snprintf(str_rx_tx + len, str_maxlen - len, ", hash_usr:%u", mbuf->hash.usr); struct sid_list * sid_list = &mrb_metadata->sid_list; uint16_t sids[RTE_DIM(sid_list->sids)] = {}; len = snprintf(str_sid_list, str_maxlen, "sid list:"); sid_list_get(sid_list, sids, RTE_DIM(sid_list->sids)); for (int i = 0; i < RTE_DIM(sid_list->sids); i++) { len += snprintf(str_sid_list + len, str_maxlen - len, "[%u] ", sids[i]); } len = snprintf(str_dir, str_maxlen, "dir:%u", mrb_metadata->dir); len += snprintf(str_dir + len, str_maxlen - len, ", ef_link_id:%u", mrb_metadata->ef_link_id); len += snprintf(str_dir + len, str_maxlen - len, ", traffic_link_id:%u", mrb_metadata->traffic_link_id); len += snprintf(str_dir + len, str_maxlen - len, ", ef_peer_id:%u", mrb_metadata->ef_peer_index); len = snprintf(str_sid_raw, str_maxlen - len, "sid raw head:%u", sid_list->head); len += snprintf(str_sid_raw + len, str_maxlen - len, ", tail:%u", sid_list->tail); len += snprintf(str_sid_raw + len, str_maxlen - len, ", capacity:%u", sid_list->capacity); len += snprintf(str_sid_raw + len, str_maxlen - len, ", cur_sid:%u", mrb_metadata->cur_sid); len += snprintf(str_sid_raw + len, str_maxlen - len, ", sids:"); for (int i = 0; i < RTE_DIM(sid_list->sids); i++) { len += snprintf(str_sid_raw + len, str_maxlen - len, "[%u] ", sid_list->sids[i]); } uint16_t optlen = pcapng_optlen(strlen(str_rx_tx)); optlen += pcapng_optlen(strlen(str_sid_list)); optlen += pcapng_optlen(strlen(str_dir)); optlen += pcapng_optlen(strlen(str_sid_raw)); struct pcapng_option * opt = (struct pcapng_option *)rte_pktmbuf_append(mbuf, optlen + sizeof(uint32_t)); if (unlikely(opt == NULL)) { fprintf(stderr, "mbuf append opt err.\n"); return RT_ERR; } /* update the epb info */ struct timespec current_time; clock_gettime(CLOCK_REALTIME, ¤t_time); uint64_t ns = (uint64_t)current_time.tv_sec * 1000000000 + current_time.tv_nsec; epb->timestamp_hi = ns >> 32; epb->timestamp_lo = (uint32_t)ns; epb->block_length = rte_pktmbuf_pkt_len(mbuf); epb->interface_id = port_index[port_id]; /* set trailer of block length */ struct pcapng_option * opt_new = pcapng_add_option(opt, PCAPNG_OPT_COMMENT, str_rx_tx, strlen(str_rx_tx)); opt_new = pcapng_add_option(opt_new, PCAPNG_OPT_COMMENT, str_sid_list, strlen(str_sid_list)); opt_new = pcapng_add_option(opt_new, PCAPNG_OPT_COMMENT, str_dir, strlen(str_dir)); opt_new = pcapng_add_option(opt_new, PCAPNG_OPT_COMMENT, str_sid_raw, strlen(str_sid_raw)); *(uint32_t *)opt_new = epb->block_length; return RT_SUCCESS; } static inline void pdump_rxtx(struct pdump_tuples * pt, uint32_t rx_tx_flags) { uint64_t * rxtx_pkts = NULL; struct rte_ring * ring = NULL; struct pdump_stats * stats = &pt->stats; if (rx_tx_flags == RTE_PDUMP_FLAG_RX) { ring = pt->rx_ring; rxtx_pkts = &stats->rx_pkts; } else { ring = pt->tx_ring; rxtx_pkts = &stats->tx_pkts; } /* Set the expected number of dequeued packets based on dumpfile_count */ unsigned int nr_exp_pkts = BURST_SIZE; if (dumpfile_count_set) { if (dumpfile_count == 0) { quit_signal = 1; return; } if (dumpfile_count < BURST_SIZE) { nr_exp_pkts = dumpfile_count; } } /* First dequeue packets from ring of primary process */ struct rte_mbuf * rxtx_bufs[BURST_SIZE]; const uint16_t nb_in_deq = rte_ring_dequeue_burst(ring, (void *)rxtx_bufs, nr_exp_pkts, NULL); if (nb_in_deq == 0) return; /* Write packets to dumpfile */ /* Because 'shmdev name' needs to be recorded, 'rte_pcapng_write_packets' cannot be used. */ unsigned int cnt = 0; int iovcnt = mbuf_burst_segs(rxtx_bufs, nb_in_deq); struct iovec iov[iovcnt]; for (int i = 0; i < nb_in_deq; i++) { struct rte_mbuf * mbuf = rxtx_bufs[i]; int ret = mr_pcapng_add_opt(mbuf, pt->port_id, rx_tx_flags); if (ret == RT_SUCCESS) { do { iov[cnt].iov_base = rte_pktmbuf_mtod(mbuf, void *); iov[cnt].iov_len = rte_pktmbuf_data_len(mbuf); cnt++; } while ((mbuf = mbuf->next)); *rxtx_pkts += 1; } } /* Write pkt */ if (cnt > 0) { if (writev(dumpfile_fd, iov, cnt) <= 0) perror("Pcapng packets write error."); } /* Pkt free */ rte_pktmbuf_free_bulk(rxtx_bufs, nb_in_deq); /* Update the dumpfile_count */ if (dumpfile_count_set) { dumpfile_count -= nb_in_deq; } /* Update stats */ stats->dequeue_pkts += nb_in_deq; stats->freed_pkts += nb_in_deq; return; } static void signal_handler(int sig_num) { if ((sig_num == SIGINT) || (sig_num == SIGTERM) || (sig_num == SIGHUP)) { fprintf(stderr, "\n\nSignal %d received, preparing to exit...\n", sig_num); quit_signal = 1; } } static void err_value_dump(int32_t err_value) { switch (err_value) { case MR_PDUMP_ERR_FOR_MESSAGE_INVALID: fprintf(stderr, "Message err info : Mess arg invalid.\n"); break; case MR_PDUMP_ERR_FOR_PORT_INVALID: fprintf(stderr, "Message err info : Port invalid.\n"); break; case MR_PDUMP_ERR_FOR_MEMPOOL_CREATE: fprintf(stderr, "Message err info : Mempool create err.\n"); break; case MR_PDUMP_ERR_FOR_RX_RING_CREATE: fprintf(stderr, "Message err info : Rx ring create err.\n"); break; case MR_PDUMP_ERR_FOR_TX_RING_CREATE: fprintf(stderr, "Message err info : Tx ring create err.\n"); break; case MR_PDUMP_ERR_FOR_RX_RING_FREE: fprintf(stderr, "Message err info : Rx ring free err.\n"); break; case MR_PDUMP_ERR_FOR_TX_RING_FREE: fprintf(stderr, "Message err info : Tx ring free err.\n"); break; case MR_PDUMP_ERR_FOR_RX_ENABLE: fprintf(stderr, "Message err info : Rx enable err.\n"); break; case MR_PDUMP_ERR_FOR_TX_ENABLE: fprintf(stderr, "Message err info : Tx enable err.\n"); break; case MR_PDUMP_ERR_FOR_RX_DISABLE: fprintf(stderr, "Message err info : Rx disable err.\n"); break; case MR_PDUMP_ERR_FOR_TX_DISABLE: fprintf(stderr, "Message err info : Tx disable err.\n"); break; case MR_PDUMP_ERR_FOR_MEMPOOL_FREE: fprintf(stderr, "Message err info : Mempool free err.\n"); break; case MR_PDUMP_ERR_FOR_PCAPNG_CREATE: fprintf(stderr, "Message err info : Pcapng file create err.\n"); break; } } static void enable_pdump(void) { printf("Enable pdump.\n"); /* Set all device */ for (int i = 0; i < num_tuples; i++) { struct pdump_tuples * pt = &pdump_t[i]; if (pt->dir & RTE_PDUMP_FLAG_RX) { struct rte_mp_msg mp_req = {}; struct mr_pdump_request * req = (struct mr_pdump_request *)mp_req.param; /* Fill request struct */ memset(req, 0, sizeof(*req)); req->op = ENABLE; req->queue = pt->queue; req->flags = RTE_PDUMP_FLAG_RX; req->prm = prm; req->snaplen = snaplen; req->offset = offset; memcpy(req->dev_symbol, pt->dev_symbol, sizeof(pt->dev_symbol)); /* Fill request name */ rte_strscpy(mp_req.name, MR_PDUMP_MP, RTE_MP_MAX_NAME_LEN); mp_req.len_param = sizeof(*req); mp_req.num_fds = 0; /* Send request and wait reply */ struct rte_mp_reply mp_reply = {}; struct timespec ts = {.tv_sec = 5, .tv_nsec = 0}; if (rte_mp_request_sync(&mp_req, &mp_reply, &ts) != 0) rte_exit(EXIT_FAILURE, "Device:%s, send rx enable msg timeout.\n", pt->dev_symbol); /* Deal reply */ struct rte_mp_msg * mp_rep = &mp_reply.msgs[0]; struct mr_pdump_response * resp = (struct mr_pdump_response *)mp_rep->param; if (resp->err_value != MR_PDUMP_SUCCESS) { /* Dump error info */ err_value_dump(resp->err_value); free(mp_reply.msgs); rte_exit(EXIT_FAILURE, "Device:%s, rx enable err.\n", pt->dev_symbol); } /* Save dump info */ pt->rx_ring = resp->rx_ring; pt->mp = resp->mp; pt->port_id = resp->port_id; pt->drv_type = resp->drv_type; rte_ether_addr_copy(&resp->eth_addr, &pt->eth_addr); free(mp_reply.msgs); if (pt->drv_type != MR_DEV_DRV_TYPE_SHMDEV) { /* Phydev use dpdk pdump api */ int ret = rte_pdump_enable_bpf(pt->port_id, pt->queue, RTE_PDUMP_FLAG_RX | RTE_PDUMP_FLAG_PCAPNG, snaplen, offset, pt->rx_ring, pt->mp, prm); if (ret < 0) rte_exit(EXIT_FAILURE, "Phydev :%s, rx enable err.\n", pt->dev_symbol); } } if (pt->dir & RTE_PDUMP_FLAG_TX) { struct rte_mp_msg mp_req = {}; struct mr_pdump_request * req = (struct mr_pdump_request *)mp_req.param; /* Fill request struct */ memset(req, 0, sizeof(*req)); req->op = ENABLE; req->queue = pt->queue; req->flags = RTE_PDUMP_FLAG_TX; req->prm = prm; req->snaplen = snaplen; req->offset = offset; memcpy(req->dev_symbol, pt->dev_symbol, sizeof(pt->dev_symbol)); /* Fill request name */ rte_strscpy(mp_req.name, MR_PDUMP_MP, RTE_MP_MAX_NAME_LEN); mp_req.len_param = sizeof(*req); mp_req.num_fds = 0; /* Send request and wait reply */ struct timespec ts = {.tv_sec = 5, .tv_nsec = 0}; struct rte_mp_reply mp_reply = {}; if (rte_mp_request_sync(&mp_req, &mp_reply, &ts) != 0) rte_exit(EXIT_FAILURE, "Device:%s, send tx enable msg timeout.\n", pt->dev_symbol); struct rte_mp_msg * mp_rep = &mp_reply.msgs[0]; struct mr_pdump_response * resp = (struct mr_pdump_response *)mp_rep->param; if (resp->err_value != MR_PDUMP_SUCCESS) { /* Dump error info */ err_value_dump(resp->err_value); free(mp_reply.msgs); rte_exit(EXIT_FAILURE, "Devices:%s, tx enable err.\n", pt->dev_symbol); } pt->tx_ring = resp->tx_ring; pt->mp = resp->mp; pt->port_id = resp->port_id; pt->drv_type = resp->drv_type; rte_ether_addr_copy(&resp->eth_addr, &pt->eth_addr); free(mp_reply.msgs); if (pt->drv_type != MR_DEV_DRV_TYPE_SHMDEV) { /* Phydev use dpdk pdump api */ int ret = rte_pdump_enable_bpf(pt->port_id, pt->queue, RTE_PDUMP_FLAG_TX | RTE_PDUMP_FLAG_PCAPNG, snaplen, offset, pt->tx_ring, pt->mp, prm); if (ret < 0) rte_exit(EXIT_FAILURE, "Phydev:%s, tx enable err.\n", pt->dev_symbol); } } } } static int pcapng_add_interface(struct pdump_tuples * pt) { /* Compute length of interface block options */ uint32_t len = sizeof(struct pcapng_interface_block); const uint8_t tsresol = 9; len += pcapng_optlen(sizeof(tsresol)); /* timestamp */ len += pcapng_optlen(strlen(pt->dev_symbol)); /* ifname */ len += pcapng_optlen(RTE_ETHER_ADDR_LEN); /* macaddr */ len += pcapng_optlen(0); len += sizeof(uint32_t); /* 'alloca' does not require explicit memory deallocation because it allocates memory on the stack. */ void * buf = alloca(len); if (!buf) return -1; struct pcapng_interface_block * hdr = (struct pcapng_interface_block *)buf; *hdr = (struct pcapng_interface_block){ .block_type = PCAPNG_INTERFACE_BLOCK, .link_type = 1, /* DLT_EN10MB - Ethernet */ .block_length = len, }; struct pcapng_option * opt = (struct pcapng_option *)(hdr + 1); opt = pcapng_add_option(opt, PCAPNG_IFB_TSRESOL, &tsresol, sizeof(tsresol)); opt = pcapng_add_option(opt, PCAPNG_IFB_NAME, pt->dev_symbol, strlen(pt->dev_symbol)); opt = pcapng_add_option(opt, PCAPNG_IFB_MACADDR, &pt->eth_addr, RTE_ETHER_ADDR_LEN); opt = pcapng_add_option(opt, PCAPNG_OPT_END, NULL, 0); /* clone block_length after options */ memcpy(opt, &hdr->block_length, sizeof(uint32_t)); return write(dumpfile_fd, buf, len); } static void write_interface_info(void) { /* Save PCI devices */ uint16_t port_id = 0; RTE_ETH_FOREACH_DEV(port_id) { /* The list if ports in pcapng needs to be contiguous */ port_index[port_id] = interface_index++; } #if RTE_VERSION >= RTE_VERSION_NUM(23, 11, 0, 0) // In dpdk21-11, the rte_pcapng_fdopen() function automatically calls pcapng_add_interface // In dpdk-23, you need to manually call rte_pcapng_add_interface() port_id = 0; RTE_ETH_FOREACH_DEV(port_id) { /* The list if ports in pcapng needs to be contiguous */ if (rte_pcapng_add_interface(pcapng, port_id, NULL, NULL, NULL) < 0) { rte_exit(EXIT_FAILURE, "port id:%u, write interface info err.\n", port_id); } } #endif for (int i = 0; i < num_tuples; i++) { struct pdump_tuples * pt = &pdump_t[i]; if (pt->drv_type == MR_DEV_DRV_TYPE_SHMDEV) { if (pcapng_add_interface(pt) < 0) { rte_exit(EXIT_FAILURE, "Phydev:%s, write interface info err.\n", pt->dev_symbol); } port_index[pt->port_id] = interface_index++; } } } static void disable_pdump(void) { for (int i = 0; i < num_tuples; i++) { struct pdump_tuples * pt = &pdump_t[i]; if (pt->dir & RTE_PDUMP_FLAG_RX) { if (pt->drv_type != MR_DEV_DRV_TYPE_SHMDEV) { /* The Phydev use dpdk pdump api */ int ret = rte_pdump_disable(pt->port_id, pt->queue, RTE_PDUMP_FLAG_RX); if (ret < 0) rte_exit(EXIT_FAILURE, "Phydev:%s, rx disable err.\n", pt->dev_symbol); } struct rte_mp_msg mp_req = {}; struct mr_pdump_request * req = (struct mr_pdump_request *)mp_req.param; /* Fill request struct */ memset(req, 0, sizeof(*req)); req->op = DISABLE; req->flags = RTE_PDUMP_FLAG_RX; memcpy(req->dev_symbol, pt->dev_symbol, sizeof(pt->dev_symbol)); /* Fill request name */ rte_strscpy(mp_req.name, MR_PDUMP_MP, RTE_MP_MAX_NAME_LEN); mp_req.len_param = sizeof(*req); mp_req.num_fds = 0; /* Send request msg and wait reply */ struct rte_mp_reply mp_reply = {}; struct timespec ts = {.tv_sec = 5, .tv_nsec = 0}; if (rte_mp_request_sync(&mp_req, &mp_reply, &ts) != 0) rte_exit(EXIT_FAILURE, "Device:%s, send rx disable msg timeout.\n", pt->dev_symbol); /* Deal reply */ struct rte_mp_msg * mp_rep = &mp_reply.msgs[0]; struct mr_pdump_response * resp = (struct mr_pdump_response *)mp_rep->param; if (resp->err_value != MR_PDUMP_SUCCESS) { /* Dump error info */ err_value_dump(resp->err_value); free(mp_reply.msgs); rte_exit(EXIT_FAILURE, "Device:%s, disable err.\n", pt->dev_symbol); } /* Free rx ring */ while (rte_ring_count(pt->rx_ring) > 0) { struct rte_mbuf * mbufs[BURST_SIZE]; const uint16_t nb_rx = rte_ring_dequeue_burst(pt->rx_ring, (void *)mbufs, BURST_SIZE, NULL); rte_pktmbuf_free_bulk(mbufs, nb_rx); } /* Clear pt info */ pt->rx_ring = NULL; pt->mp = NULL; free(mp_reply.msgs); } if (pt->dir & RTE_PDUMP_FLAG_TX) { if (pt->drv_type != MR_DEV_DRV_TYPE_SHMDEV) { /* The Phydev use dpdk pdump api */ int ret = rte_pdump_disable(pt->port_id, pt->queue, RTE_PDUMP_FLAG_TX); if (ret < 0) rte_exit(EXIT_FAILURE, "Phydev:%s, tx disable err.\n", pt->dev_symbol); } struct rte_mp_msg mp_req = {}; struct mr_pdump_request * req = (struct mr_pdump_request *)mp_req.param; /* Fill request struct */ memset(req, 0, sizeof(*req)); req->op = DISABLE; req->flags = RTE_PDUMP_FLAG_TX; memcpy(req->dev_symbol, pt->dev_symbol, sizeof(pt->dev_symbol)); /* Fill request name */ rte_strscpy(mp_req.name, MR_PDUMP_MP, RTE_MP_MAX_NAME_LEN); mp_req.len_param = sizeof(*req); mp_req.num_fds = 0; /* Send request msg and wait reply */ struct rte_mp_reply mp_reply = {}; struct timespec ts = {.tv_sec = 5, .tv_nsec = 0}; if (rte_mp_request_sync(&mp_req, &mp_reply, &ts) != 0) rte_exit(EXIT_FAILURE, "Device:%s, send tx disable msg timeout.\n", pt->dev_symbol); /* Deal reply */ struct rte_mp_msg * mp_rep = &mp_reply.msgs[0]; struct mr_pdump_response * resp = (struct mr_pdump_response *)mp_rep->param; if (resp->err_value != MR_PDUMP_SUCCESS) { /* Dump error info */ err_value_dump(resp->err_value); free(mp_reply.msgs); rte_exit(EXIT_FAILURE, "Device:%s, disable err.\n", pt->dev_symbol); } /* Free tx ring */ while (rte_ring_count(pt->tx_ring) > 0) { struct rte_mbuf * mbufs[BURST_SIZE]; const uint16_t nb_tx = rte_ring_dequeue_burst(pt->tx_ring, (void *)mbufs, BURST_SIZE, NULL); rte_pktmbuf_free_bulk(mbufs, nb_tx); } /* Clear pt info */ pt->tx_ring = NULL; pt->mp = NULL; free(mp_reply.msgs); } } rte_pcapng_close(pcapng); rte_free(prm); } static inline void dump_packets(void) { while (!quit_signal) { for (int i = 0; i < num_tuples; i++) { struct pdump_tuples * pt = &pdump_t[i]; if (pt->dir & RTE_PDUMP_FLAG_RX) pdump_rxtx(pt, RTE_PDUMP_FLAG_RX); if (pt->dir & RTE_PDUMP_FLAG_TX) pdump_rxtx(pt, RTE_PDUMP_FLAG_TX); } } } /* Get os info */ static char * get_os_info(void) { struct utsname uts; char * os_name = NULL; if (uname(&uts) < 0) return NULL; if (asprintf(&os_name, "%s %s", uts.sysname, uts.release) == -1) return NULL; return os_name; } static inline void create_pcap_dumper() { /* Check dumpfile whether exist, if exist, delete it */ if (access(dumpfile_path, F_OK) == 0) { if (remove(dumpfile_path) != 0) { rte_exit(EXIT_FAILURE, "Remove %s err.\n", dumpfile_path); } } /* Create the pcapng file */ dumpfile_fd = open(dumpfile_path, O_WRONLY | O_CREAT, 0640); pcapng = rte_pcapng_fdopen(dumpfile_fd, get_os_info(), NULL, "mrpdump v1.0", NULL); if (pcapng == NULL) { rte_exit(EXIT_FAILURE, "Cannot create pcapng file %s\n", dumpfile_path); } /* Set bpf */ if (is_str_bpf_expr_set) { // ‘pcap_compile_nopcap’ is deprecated // int ret = pcap_compile_nopcap(65535, DLT_EN10MB, &bpf_prog, str_bpf_expr, 1, PCAP_NETMASK_UNKNOWN); pcap_t * handle = pcap_open_dead(DLT_EN10MB, 65535); int ret = pcap_compile(handle, &bpf_prog, str_bpf_expr, 1, PCAP_NETMASK_UNKNOWN); if (ret < 0) { rte_exit(EXIT_FAILURE, "Cannot compile bpf rule %s\n", str_bpf_expr); } prm = rte_bpf_convert(&bpf_prog); if (prm == NULL) rte_exit(EXIT_FAILURE, "Rte bpf convert failed\n"); } return; } int main(int argc, char ** argv) { int diag; int ret; int i; char c_flag[] = "-c1"; char n_flag[] = "-n4"; char mp_flag[] = "--proc-type=secondary"; char * argp[argc + 3]; /* catch ctrl-c so we can print on exit */ signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); signal(SIGHUP, signal_handler); /* Check help arg */ if (argc > 1 && !strcmp(argv[1], "--help")) { pdump_usage(argv[0]); return 0; } argp[0] = argv[0]; argp[1] = c_flag; argp[2] = n_flag; argp[3] = mp_flag; for (i = 1; i < argc; i++) argp[i + 3] = argv[i]; argc += 3; diag = rte_eal_init(argc, argp); if (diag < 0) rte_panic("Cannot init EAL\n"); argc -= diag; argv += (diag - 3); /* get default dumpfile path */ static char default_dir[PATH_MAX]; if (getcwd(default_dir, sizeof(default_dir)) == NULL) rte_exit(EXIT_FAILURE, "Get dumpfile path fail\n"); snprintf(dumpfile_path, sizeof(dumpfile_path), "%s/dumpfile.pcapng", default_dir); /* parse app arguments */ if (argc > 1) { ret = launch_args_parse(argc, argv, argp[0]); if (ret < 0) rte_exit(EXIT_FAILURE, "Invalid argument\n"); else if (ret == 0) rte_exit(EXIT_SUCCESS, " "); } /* create dumpfile writer */ create_pcap_dumper(); /* write interface info */ write_interface_info(); /* device enable */ enable_pdump(); /* dump packets */ dump_packets(); /* device disable */ disable_pdump(); /* dump debug stats */ print_pdump_stats(); return 0; }