/* ********************************************************************************************** * File: pcap_live.cpp * Description: * Authors: Liu WenTan * Date: 2022-07-15 * Copyright: (c) 2018-2022 Geedge Networks, Inc. All rights reserved. *********************************************************************************************** */ #include #include #include #include "logger.h" #include "utils.h" #include "util_errors.h" #include "pio_pcap_live.h" #include "packet_io.h" #include "packet_io_util.h" #include "packet_io_internal.h" #define DEFAULT_MAX_PACKET_SIZE 65535 #define TIMEOUT_MS 500 static ssize_t pcap_live_init(struct pio_pcap_live_device_context *plive_dev_ctx, const char *dev_name) { if (nullptr == plive_dev_ctx) { return -1; } char errbuf[PCAP_ERRBUF_SIZE]; plive_dev_ctx->pcap_handle = pcap_create(dev_name, errbuf); if (nullptr == plive_dev_ctx->pcap_handle) { log_error(ST_ERR_PIO_PCAP_LIVE_DEVICE, "could not create pcap handle for %s, error %s", dev_name, errbuf); return -1; } if (g_packet_io_config.pcap.snaplen == 0) { plive_dev_ctx->pcap_snaplen = DEFAULT_MAX_PACKET_SIZE; } else { plive_dev_ctx->pcap_snaplen = g_packet_io_config.pcap.snaplen; } /* set snaplen */ ssize_t res = pcap_set_snaplen(plive_dev_ctx->pcap_handle, plive_dev_ctx->pcap_snaplen); if (res != 0) { log_error(ST_ERR_PCAP_SET_SNAPLEN, "could not set snaplen, error:%s", pcap_geterr(plive_dev_ctx->pcap_handle)); return -1; } log_info("set snaplen to %d for %s", plive_dev_ctx->pcap_snaplen, dev_name); /* set promisc */ res = pcap_set_promisc(plive_dev_ctx->pcap_handle, g_packet_io_config.pcap.promisc); if (res != 0) { log_error(ST_ERR_PCAP_SET_PROMISC, "could not set promisc mode, error:%s", pcap_geterr(plive_dev_ctx->pcap_handle)); return -1; } /* set timeout */ res = pcap_set_timeout(plive_dev_ctx->pcap_handle, TIMEOUT_MS); if (res != 0) { log_error(ST_ERR_PCAP_SET_TIMEOUT, "could not set timeout, error:%s", pcap_geterr(plive_dev_ctx->pcap_handle)); return -1; } res = pcap_activate(plive_dev_ctx->pcap_handle); if (res != 0) { log_error(ST_ERR_PCAP_ACTIVATE_HANDLE, "could not activate the pcap handle, error:%s", pcap_geterr(plive_dev_ctx->pcap_handle)); return -1; } plive_dev_ctx->pcap_state = PCAP_STATE_UP; /* set bpf filter */ if (strlen(g_packet_io_config.pcap.bpf_string) != 0) { res = strncpy_safe(plive_dev_ctx->bpf_string, g_packet_io_config.pcap.bpf_string, sizeof(plive_dev_ctx->bpf_string)); if (res < 0) { log_error(ST_ERR_STR_COPY, "plive_dev_ctx bpf string copy failed."); return -1; } if (pcap_compile(plive_dev_ctx->pcap_handle, &plive_dev_ctx->filter, plive_dev_ctx->bpf_string, 1, 0) < 0) { log_error(ST_ERR_BPF, "bpf compliation error %s", pcap_geterr(plive_dev_ctx->pcap_handle)); return -1; } if (pcap_setfilter(plive_dev_ctx->pcap_handle, &plive_dev_ctx->filter) < 0) { log_error(ST_ERR_BPF, "could not set bpf filter %s", pcap_geterr(plive_dev_ctx->pcap_handle)); return -1; } } plive_dev_ctx->data_link = pcap_datalink(plive_dev_ctx->pcap_handle); for (uint32_t i = 0; i < VIRTUAL_QUEUE_MAX_NUM; i++) { pio_packet_queue_init(&plive_dev_ctx->pkt_queues[i]); } return res; } ssize_t pio_pcap_live_device_open(struct packet_io_device *pdev) { ssize_t res = -1; if (nullptr == pdev) { log_error(ST_ERR_PIO_PCAP_LIVE_DEVICE, "invalid packet_io_device pointer."); return res; } pdev->entity.pcap_live_dev_ctx = CALLOC(struct pio_pcap_live_device_context, 1); if (nullptr == pdev->entity.pcap_live_dev_ctx) { log_error(ST_ERR_PIO_PCAP_LIVE_DEVICE, "alloc pcap_live_dev_ctx failed."); return res; } pthread_mutex_init(&pdev->entity.pcap_live_dev_ctx->handle_mutex, nullptr); pdev->entity.pcap_live_dev_ctx->pdev = pdev; res = pcap_live_init(pdev->entity.pcap_live_dev_ctx, pdev->dev_name); if (res < 0) { log_error(ST_ERR_PIO_PCAP_LIVE_DEVICE, "init pcap live failed."); FREE(pdev->entity.pcap_live_dev_ctx); return res; } return res; } ssize_t pio_pcap_live_device_close(struct packet_io_device *pdev) { if (nullptr == pdev) { log_error(ST_ERR_PIO_PCAP_FILE_DEVICE, "invalid pdev pointer, so close pcap live device failed."); return -1; } if (pdev->entity.pcap_live_dev_ctx != nullptr) { for (uint32_t i = 0; i < VIRTUAL_QUEUE_MAX_NUM; i++) { if (pdev->entity.pcap_live_dev_ctx->pkt_queues[i].len != 0) { release_pio_packet_queue(&pdev->entity.pcap_live_dev_ctx->pkt_queues[i]); } } FREE(pdev->entity.pcap_live_dev_ctx); } return 0; } static void pcap_live_pkt_callback_oneshot(char *user, struct pcap_pkthdr *pkt_hdr, u_char *pkt) { struct pio_pcap_live_device_context *plive_dev_ctx = (struct pio_pcap_live_device_context *)user; uint32_t p_len = 0; if (pkt_hdr->caplen < DEFAULT_MTU) { p_len = COMMON_SIZE_OF_PIO_PACKET; } else { p_len = MAX_SIZE_OF_PIO_PACKET; } struct pio_packet *p = (struct pio_packet *)malloc(p_len); if (nullptr == p) { return; } memset(p, 0, p_len); p->pkt_hdr = p; p->pkt_payload = (uint8_t *)p + CUSTOM_ZONE_LEN; p->pkt_len = pkt_hdr->caplen; p->data_link = plive_dev_ctx->data_link; if (packet_copy_data((uint8_t *)p->pkt_payload, (uint8_t *)pkt, pkt_hdr->caplen)) { FREE(p); return; } /* nr_rxq <= VIRTUAL_QUEUE_MAX_NUM */ uint16_t nr_rxq = plive_dev_ctx->pdev->rxq_num; uint16_t rxq_id = pio_packet_hash(p) % nr_rxq; /* hash to specific queue id and enqueue */ pthread_mutex_lock(&plive_dev_ctx->pkt_queues[rxq_id].mutex_q); pio_packet_enqueue(&plive_dev_ctx->pkt_queues[rxq_id], p); pthread_mutex_unlock(&plive_dev_ctx->pkt_queues[rxq_id].mutex_q); } ssize_t pio_pcap_live_device_receive(struct packet_io_device *pdev, uint32_t rxq_id, struct stellar_packet **pkts, size_t nr_pkts) { ssize_t res = -1; struct pio_pcap_live_device_context *plive_dev_ctx = pdev->entity.pcap_live_dev_ctx; if (nullptr == plive_dev_ctx) { log_error(ST_ERR_PIO_PCAP_LIVE_DEVICE, "invalid pcap_live_dev_ctx failed."); return res; } size_t packet_q_len = nr_pkts; pthread_mutex_lock(&plive_dev_ctx->handle_mutex); res = pcap_dispatch(plive_dev_ctx->pcap_handle, packet_q_len, (pcap_handler)pcap_live_pkt_callback_oneshot, (u_char *)plive_dev_ctx); pthread_mutex_unlock(&plive_dev_ctx->handle_mutex); if (res < 0) { log_error(ST_ERR_PCAP_DISPATCH, "error code %ld %s", res, pcap_geterr(plive_dev_ctx->pcap_handle)); } else { struct pio_packet *p = nullptr; size_t i = 0; uint32_t q_len = 0; pthread_mutex_lock(&plive_dev_ctx->pkt_queues[rxq_id].mutex_q); if (plive_dev_ctx->pkt_queues[rxq_id].len > 0) { do { p = pio_packet_dequeue(&plive_dev_ctx->pkt_queues[rxq_id]); q_len = plive_dev_ctx->pkt_queues[rxq_id].len; pkts[i] = (struct stellar_packet *)p; i++; } while ((q_len != 0) && (i < nr_pkts)); } pthread_mutex_unlock(&plive_dev_ctx->pkt_queues[rxq_id].mutex_q); if (q_len == 0) { res = i; } else { res = nr_pkts; } } return res; } ssize_t pio_pcap_live_device_send(struct packet_io_device *pdev, uint32_t txq_id, struct stellar_packet **pkts, size_t nr_pkts) { ssize_t res = -1; struct pio_pcap_live_device_context *plive_dev_ctx = pdev->entity.pcap_live_dev_ctx; if (nullptr == plive_dev_ctx) { log_error(ST_ERR_PIO_PCAP_LIVE_DEVICE, "invalid pcap_live_dev_ctx failed."); return res; } pthread_mutex_lock(&plive_dev_ctx->handle_mutex); for (size_t i = 0; i < nr_pkts; i++) { struct pio_packet *p = (struct pio_packet *)pkts[i]; res = pcap_sendpacket(plive_dev_ctx->pcap_handle, (u_char *)p->pkt_payload, p->pkt_len); FREE(pkts[i]); } pthread_mutex_unlock(&plive_dev_ctx->handle_mutex); return 0; } void pio_pcap_live_device_pkt_free(__unused struct packet_io_device *pdev, __unused uint32_t qid, struct stellar_packet **pkts, size_t nr_pkts) { void **pptr_pkts = (void **)pkts; for (size_t i = 0; i < nr_pkts; i++) { FREE(pptr_pkts[i]); } } ssize_t pio_pcap_live_instance_create(struct packet_io_instance *pinst) { if (nullptr == pinst) { log_error(ST_ERR_PIO_PCAP_LIVE_INSTANCE, "invalid pcap live instance pointer."); return -1; } pinst->entity.pcap_live_inst_ctx = CALLOC(struct pio_pcap_live_instance_context, 1); if (nullptr == pinst->entity.pcap_live_inst_ctx) { log_error(ST_ERR_PIO_PCAP_LIVE_INSTANCE, "alloc pcap_live_inst_ctx failed."); return -1; } return 0; } void pio_pcap_live_instance_destroy(struct packet_io_instance *pinst) { if (nullptr == pinst) { return; } FREE(pinst->entity.pcap_live_inst_ctx); struct packet_io_device *node = nullptr; while ((node = TAILQ_FIRST(&pinst->device_queue_head)) != nullptr) { TAILQ_REMOVE(&pinst->device_queue_head, node, next); pinst->dev_cnt--; pio_pcap_live_device_close(node); FREE(node); } } char *pio_pcap_live_device_buff_ctrlzone(struct stellar_packet *p, size_t *ctrlzone_len) { struct pio_packet *pkt = (struct pio_packet *)p; *ctrlzone_len = CUSTOM_ZONE_LEN; return (char *)pkt->pkt_hdr; } char *pio_pcap_live_device_buff_mtod(struct stellar_packet *p, size_t *data_len) { struct pio_packet *pkt = (struct pio_packet *)p; *data_len = pkt->pkt_len; return (char *)pkt->pkt_payload; }