packcap.c
#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_mbuf.h>
#include <rte_ether.h>
#include <rte_malloc.h>
#include <rte_timer.h>
#include <rte_log.h>
#include <rte_kni.h>
#include <stdio.h>
#include <math.h>
#include <arpa/inet.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include<sys/param.h>
#define ENABLE_ARP 1
#define ENABLE_ARP_REPLY 1
#define ENABLE_TIMER 1
#define ENABLE_RINGBUFFER 1
#define ENABLE_MULTHREAD 1
#define ENABLE_KNI_APP 1
#define NUM_MBUFS (4096-1)
#define BURST_SIZE 32
#define RING_SIZE 1024
#define MAX_PACKET_SIZE 2048
#define TIMER_RESOLUTION_CYCLES 120000000000ULL // 10ms * 1000 = 10s * 6
#if ENABLE_ARP
#define MAKE_IPV4_ADDR(a, b, c, d) (a + (b<<8) + (c<<16) + (d<<24))
static uint32_t gLocalIp = MAKE_IPV4_ADDR(192, 168, 1, 12);
static uint8_t gSrcMac[RTE_ETHER_ADDR_LEN];
#define ARP_ENTRY_STATUS_DYNAMIC 0
#define ARP_ENTRY_STATUS_STATIC 1
#define LL_ADD(item, list) do { \
item->prev = NULL; \
item->next = list; \
if (list != NULL) list->prev = item; \
list = item; \
} while(0)
#define LL_REMOVE(item, list) do { \
if (item->prev != NULL) item->prev->next = item->next; \
if (item->next != NULL) item->next->prev = item->prev; \
if (list == item) list = item->next; \
item->prev = item->next = NULL; \
} while(0)
struct arp_entry {
uint32_t ip;
uint8_t hwaddr[RTE_ETHER_ADDR_LEN];
uint8_t type;
//
struct arp_entry *next;
struct arp_entry *prev;
};
struct arp_table {
struct arp_entry *entries;
int count;
pthread_spinlock_t spinlock;
};
struct _packcap
{
char *sip;
int sport;
char *dip;
int dport;
char *protol;
struct rte_mempool *mbuf_pool;
};
static struct _packcap *p_packcap = NULL;
static struct arp_table *arpt = NULL;
static struct arp_table *arp_table_instance(void) {
if (arpt == NULL) {
arpt = rte_malloc("arp table", sizeof(struct arp_table), 0);
if (arpt == NULL) {
rte_exit(EXIT_FAILURE, "rte_malloc arp table failed\n");
}
memset(arpt, 0, sizeof(struct arp_table));
pthread_spin_init(&arpt->spinlock, PTHREAD_PROCESS_SHARED);
}
return arpt;
}
static uint8_t* ng_get_dst_macaddr(uint32_t dip) {
struct arp_entry *iter;
struct arp_table *table = arp_table_instance();
int count = table->count;
for (iter = table->entries; count-- != 0 && iter != NULL;iter = iter->next) {
if (dip == iter->ip) {
return iter->hwaddr;
}
}
return NULL;
}
static int ng_arp_entry_insert(uint32_t ip, uint8_t *mac) {
struct arp_table *table = arp_table_instance();
uint8_t *hwaddr = ng_get_dst_macaddr(ip);
if (hwaddr == NULL) {
struct arp_entry *entry = rte_malloc("arp_entry",sizeof(struct arp_entry), 0);
if (entry) {
memset(entry, 0, sizeof(struct arp_entry));
entry->ip = ip;
rte_memcpy(entry->hwaddr, mac, RTE_ETHER_ADDR_LEN);
entry->type = 0;
pthread_spin_lock(&table->spinlock);
LL_ADD(entry, table->entries);
table->count ++;
pthread_spin_unlock(&table->spinlock);
}
return 1; //
}
return 0;
}
#endif
#if ENABLE_KNI_APP
struct rte_kni *global_kni = NULL;
#endif
#if ENABLE_ARP_REPLY
static uint8_t gDefaultArpMac[RTE_ETHER_ADDR_LEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
#endif
#if ENABLE_RINGBUFFER
struct inout_ring {
struct rte_ring *in;
struct rte_ring *out;
};
static struct inout_ring *rInst = NULL;
static struct inout_ring *ringInstance(void) {
if (rInst == NULL) {
rInst = rte_malloc("in/out ring", sizeof(struct inout_ring), 0);
memset(rInst, 0, sizeof(struct inout_ring));
}
return rInst;
}
#endif
int gDpdkPortId = 0;
static const struct rte_eth_conf port_conf_default = {
.rxmode = {.max_rx_pkt_len = RTE_ETHER_MAX_LEN }
};
static void ng_init_port(struct rte_mempool *mbuf_pool) {
uint16_t nb_sys_ports= rte_eth_dev_count_avail(); //
if (nb_sys_ports == 0) {
rte_exit(EXIT_FAILURE, "No Supported eth found\n");
}
struct rte_eth_dev_info dev_info;
rte_eth_dev_info_get(gDpdkPortId, &dev_info); //
const int num_rx_queues = 1;
const int num_tx_queues = 1;
struct rte_eth_conf port_conf = port_conf_default;
rte_eth_dev_configure(gDpdkPortId, num_rx_queues, num_tx_queues, &port_conf);
if (rte_eth_rx_queue_setup(gDpdkPortId, 0 , 1024,
rte_eth_dev_socket_id(gDpdkPortId),NULL, mbuf_pool) < 0) {
rte_exit(EXIT_FAILURE, "Could not setup RX queue\n");
}
struct rte_eth_txconf txq_conf = dev_info.default_txconf;
txq_conf.offloads = port_conf.rxmode.offloads;
if (rte_eth_tx_queue_setup(gDpdkPortId, 0 , 1024,
rte_eth_dev_socket_id(gDpdkPortId), &txq_conf) < 0) {
rte_exit(EXIT_FAILURE, "Could not setup TX queue\n");
}
if (rte_eth_dev_start(gDpdkPortId) < 0 ) {
rte_exit(EXIT_FAILURE, "Could not start\n");
}
#if ENABLE_KNI_APP
rte_eth_promiscuous_enable(gDpdkPortId);
#endif
}
#if ENABLE_ARP
static int ng_encode_arp_pkt(uint8_t *msg, uint16_t opcode, uint8_t *dst_mac, uint32_t sip, uint32_t dip) {
// 1 ethhdr
struct rte_ether_hdr *eth = (struct rte_ether_hdr *)msg;
rte_memcpy(eth->s_addr.addr_bytes, gSrcMac, RTE_ETHER_ADDR_LEN);
if (!strncmp((const char *)dst_mac, (const char *)gDefaultArpMac, RTE_ETHER_ADDR_LEN)) {
uint8_t mac[RTE_ETHER_ADDR_LEN] = {0x0};
rte_memcpy(eth->d_addr.addr_bytes, mac, RTE_ETHER_ADDR_LEN);
} else {
rte_memcpy(eth->d_addr.addr_bytes, dst_mac, RTE_ETHER_ADDR_LEN);
}
eth->ether_type = htons(RTE_ETHER_TYPE_ARP);
// 2 arp
struct rte_arp_hdr *arp = (struct rte_arp_hdr *)(eth + 1);
arp->arp_hardware = htons(1);
arp->arp_protocol = htons(RTE_ETHER_TYPE_IPV4);
arp->arp_hlen = RTE_ETHER_ADDR_LEN;
arp->arp_plen = sizeof(uint32_t);
arp->arp_opcode = htons(opcode);
rte_memcpy(arp->arp_data.arp_sha.addr_bytes, gSrcMac, RTE_ETHER_ADDR_LEN);
rte_memcpy( arp->arp_data.arp_tha.addr_bytes, dst_mac, RTE_ETHER_ADDR_LEN);
arp->arp_data.arp_sip = sip;
arp->arp_data.arp_tip = dip;
return 0;
}
static struct rte_mbuf *ng_send_arp(struct rte_mempool *mbuf_pool, uint16_t opcode, uint8_t *dst_mac, uint32_t sip, uint32_t dip) {
const unsigned total_length = sizeof(struct rte_ether_hdr) + sizeof(struct rte_arp_hdr);
struct rte_mbuf *mbuf = rte_pktmbuf_alloc(mbuf_pool);
if (!mbuf) {
rte_exit(EXIT_FAILURE, "ng_send_arp rte_pktmbuf_alloc\n");
}
mbuf->pkt_len = total_length;
mbuf->data_len = total_length;
uint8_t *pkt_data = rte_pktmbuf_mtod(mbuf, uint8_t *);
ng_encode_arp_pkt(pkt_data, opcode, dst_mac, sip, dip);
return mbuf;
}
#endif
static void
print_ethaddr(const char *name, const struct rte_ether_addr *eth_addr)
{
char buf[RTE_ETHER_ADDR_FMT_SIZE];
rte_ether_format_addr(buf, RTE_ETHER_ADDR_FMT_SIZE, eth_addr);
printf("%s%s", name, buf);
}
#if ENABLE_TIMER
static void
arp_request_timer_cb(__attribute__((unused)) struct rte_timer *tim,
void *arg) {
struct rte_mempool *mbuf_pool = (struct rte_mempool *)arg;
struct inout_ring *ring = ringInstance();
#if 0
struct rte_mbuf *arpbuf = ng_send_arp(mbuf_pool, RTE_ARP_OP_REQUEST, ahdr->arp_data.arp_sha.addr_bytes,
ahdr->arp_data.arp_tip, ahdr->arp_data.arp_sip);
rte_eth_tx_burst(gDpdkPortId, 0, &arpbuf, 1);
rte_pktmbuf_free(arpbuf);
#endif
int i = 0;
for (i = 1;i <= 254;i ++) {
uint32_t dstip = (gLocalIp & 0x00FFFFFF) | (0xFF000000 & (i << 24));
/*
struct in_addr addr;
addr.s_addr = dstip;
printf("arp ---> src: %s \n", inet_ntoa(addr));
*/
struct rte_mbuf *arpbuf = NULL;
uint8_t *dstmac = ng_get_dst_macaddr(dstip);
if (dstmac == NULL) {
arpbuf = ng_send_arp(mbuf_pool, RTE_ARP_OP_REQUEST, gDefaultArpMac, gLocalIp, dstip);
} else {
arpbuf = ng_send_arp(mbuf_pool, RTE_ARP_OP_REQUEST, dstmac, gLocalIp, dstip);
}
//rte_eth_tx_burst(gDpdkPortId, 0, &arpbuf, 1);
//rte_pktmbuf_free(arpbuf);
rte_ring_mp_enqueue_burst(ring->out, (void**)&arpbuf, 1, NULL);
}
}
#endif
#if ENABLE_MULTHREAD
static void write_data(const char *data ,size_t len)
{
if(len < 1 || data == NULL)
return;
FILE *fp = NULL;
struct stat file_info;
char filename[256] ="packcap_data.txt";
char newfilename[256];
char datetime[14];
if (stat(filename, &file_info) != -1) {
if((file_info.st_size + len) > 1000 * 1024 * 1024)
{
memset(newfilename, 0,sizeof(newfilename));
memset(datetime, 0,sizeof(datetime));
time_t currentTime; // 存储当前时间的变量
struct tm *localTime; // 指向tm结构体的指针,包含了年、月、日等信息
// 获取当前时间
currentTime = time(NULL);
// 将currentTime转换为本地时间格式
localTime = localtime(¤tTime);
snprintf(datetime,sizeof(datetime), "%04d%02d%02d%02d%02d%02d\n",
(1900 + localTime->tm_year), (1 + localTime->tm_mon), localTime->tm_mday,
localTime->tm_hour, localTime->tm_min, localTime->tm_sec);
snprintf(newfilename, sizeof(newfilename), "%s_%s.bak",filename,datetime);
rename(filename , newfilename);
}
}
fp = fopen(filename, "a+");
if(fp == NULL)
{
return;
}
fwrite(data, len, 1, fp);
fwrite("\n", 1, 1, fp);
fclose(fp);
}
static int pkt_process(void *arg) {
//struct rte_mempool *mbuf_pool = (struct rte_mempool *)arg;
uint8_t proto_id = IPPROTO_IP;
struct _packcap *p_packcap = (struct _packcap *)arg;
if(memcmp(p_packcap->protol, "TCP", 3) == 0){
proto_id = IPPROTO_TCP;
}
//struct rte_mempool *mbuf_pool = p_packcap->mbuf_pool;
struct inout_ring *ring = ringInstance();
while (1) {
struct rte_mbuf *mbufs[BURST_SIZE];
unsigned num_recvd = rte_ring_mc_dequeue_burst(ring->in, (void**)mbufs, BURST_SIZE, NULL);
unsigned i = 0;
for (i = 0;i < num_recvd;i ++) {
struct rte_ether_hdr *ehdr = rte_pktmbuf_mtod(mbufs[i], struct rte_ether_hdr*);
if (ehdr->ether_type == rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)) {
struct rte_ipv4_hdr *iphdr = rte_pktmbuf_mtod_offset(mbufs[i], struct rte_ipv4_hdr *,
sizeof(struct rte_ether_hdr));
#if 1 // arp table
ng_arp_entry_insert(iphdr->src_addr, ehdr->s_addr.addr_bytes);
#endif
struct in_addr saddr;
struct in_addr daddr;
saddr.s_addr = iphdr->src_addr;
daddr.s_addr = iphdr->dst_addr;
if (iphdr->next_proto_id == proto_id &&
memcmp(inet_ntoa(saddr), p_packcap->sip, strlen(p_packcap->sip)) == 0) {
/*
&& memcmp(inet_ntoa(saddr), p_packcap->sip, strlen(p_packcap->sip)) == 0 &&
memcmp(inet_ntoa(daddr), p_packcap->dip, strlen(p_packcap->dip)) == 0
*/
struct rte_tcp_hdr *tcphdr = (struct rte_tcp_hdr *)(iphdr + 1);
printf("LINE(%d) Tcp ---> src(%s) dst(%s) sport(%d) dport(%d)\n", __LINE__,
inet_ntoa(saddr), inet_ntoa(daddr), ntohs(tcphdr->src_port), ntohs(tcphdr->dst_port));
if(ntohs(tcphdr->dst_port) == p_packcap->dport)
{
char *tcpdata = (char *)(tcphdr + 1);
write_data(tcpdata, strlen(tcpdata));
}
}
rte_kni_tx_burst(global_kni, mbufs, num_recvd);
//printf("tcp write --> rte_kni_handle_request\n");
} else {
// ifconfig vEth0 192.168.1.12 up
rte_kni_tx_burst(global_kni, mbufs, num_recvd);
printf("ip --> rte_kni_handle_request\n");
}
}
rte_kni_handle_request(global_kni);
}
return 0;
}
#endif
#if ENABLE_KNI_APP
// ifconfig vEth0 up/down
// config_network_if
// rte_kni_handle_request
static int ng_config_network_if(uint16_t port_id, uint8_t if_up) {
if (!rte_eth_dev_is_valid_port(port_id)) {
return -EINVAL;
}
int ret = 0;
if (if_up) {
rte_eth_dev_stop(port_id);
ret = rte_eth_dev_start(port_id);
} else {
rte_eth_dev_stop(port_id);
}
if (ret < 0) {
printf("Failed to start port : %d\n", port_id);
}
return 0;
}
static struct rte_kni *ng_alloc_kni(struct rte_mempool *mbuf_pool) {
struct rte_kni *kni_hanlder = NULL;
struct rte_kni_conf conf;
memset(&conf, 0, sizeof(conf));
snprintf(conf.name, RTE_KNI_NAMESIZE, "vEth%u", gDpdkPortId);
conf.group_id = gDpdkPortId;
conf.mbuf_size = MAX_PACKET_SIZE;
rte_eth_macaddr_get(gDpdkPortId, (struct rte_ether_addr *)conf.mac_addr);
rte_eth_dev_get_mtu(gDpdkPortId, &conf.mtu);
print_ethaddr("ng_alloc_kni: ", (struct rte_ether_addr *)conf.mac_addr);
/*
struct rte_eth_dev_info dev_info;
memset(&dev_info, 0, sizeof(dev_info));
rte_eth_dev_info_get(gDpdkPortId, &dev_info);
*/
struct rte_kni_ops ops;
memset(&ops, 0, sizeof(ops));
ops.port_id = gDpdkPortId;
ops.config_network_if = ng_config_network_if;
kni_hanlder = rte_kni_alloc(mbuf_pool, &conf, &ops);
if (!kni_hanlder) {
rte_exit(EXIT_FAILURE, "Failed to create kni for port : %d\n", gDpdkPortId);
}
return kni_hanlder;
}
#endif
static void init_daemon(void)
{
int pid=fork();
if(pid<0)
exit(1); //创建错误,退出
else if(pid>0) //父进程退出
exit(0);
setsid(); //使子进程成为组长
pid=fork();
if(pid>0)
exit(0); //再次退出,使进程不是组长,这样进程就不会打开控制终端
else if(pid<0)
exit(1);
//关闭进程打开的文件句柄
int i;
for(i=0;i<NOFILE;i++)
close(i);
if(chdir("/") == -1)
exit(1);
umask(0);//重设文件创建的掩码
}
int main(int argc, char *argv[]) {
init_daemon();
if (rte_eal_init(argc, argv) < 0) {
rte_exit(EXIT_FAILURE, "Error with EAL init\n");
}
if(argc < 6)
{
printf("examples: ./dpdk_packcap 192.168.1.12 8888 192.168.1.10 6666 TCP\n");
rte_exit(EXIT_FAILURE, "parameter is incorrect \n");
}
struct rte_mempool *mbuf_pool = rte_pktmbuf_pool_create("mbuf pool", NUM_MBUFS,
0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
if (mbuf_pool == NULL) {
rte_exit(EXIT_FAILURE, "Could not create mbuf pool\n");
}
#if ENABLE_KNI_APP
if (-1 == rte_kni_init(gDpdkPortId)) {
rte_exit(EXIT_FAILURE, "kni init failed\n");
}
ng_init_port(mbuf_pool);
// kni_alloc
global_kni = ng_alloc_kni(mbuf_pool);
#else
ng_init_port(mbuf_pool);
#endif
rte_eth_macaddr_get(gDpdkPortId, (struct rte_ether_addr *)gSrcMac);
#if ENABLE_TIMER
rte_timer_subsystem_init();
struct rte_timer arp_timer;
rte_timer_init(&arp_timer);
uint64_t hz = rte_get_timer_hz();
unsigned lcore_id = rte_lcore_id();
rte_timer_reset(&arp_timer, hz, PERIODICAL, lcore_id, arp_request_timer_cb, mbuf_pool);
#endif
#if ENABLE_RINGBUFFER
struct inout_ring *ring = ringInstance();
if (ring == NULL) {
rte_exit(EXIT_FAILURE, "ring buffer init failed\n");
}
if (ring->in == NULL) {
ring->in = rte_ring_create("in ring", RING_SIZE, rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ);
}
if (ring->out == NULL) {
ring->out = rte_ring_create("out ring", RING_SIZE, rte_socket_id(), RING_F_SP_ENQ | RING_F_SC_DEQ);
}
#endif
#if ENABLE_MULTHREAD
//unsigned lcore_id = rte_lcore_id();
lcore_id = rte_get_next_lcore(lcore_id, 1, 0);
p_packcap = rte_malloc("packcap", sizeof(struct _packcap), 0);
if (p_packcap == NULL) {
rte_exit(EXIT_FAILURE, "rte_malloc packcap failed\n");
}
p_packcap->sip = argv[1];
p_packcap->sport = atoi(argv[2]);
p_packcap->dip = argv[3];
p_packcap->dport = atoi(argv[4]);
p_packcap->protol = argv[5];
p_packcap->mbuf_pool = mbuf_pool;
rte_eal_remote_launch(pkt_process, p_packcap, lcore_id);
#endif
while (1) {
// rx
struct rte_mbuf *rx[BURST_SIZE];
unsigned num_recvd = rte_eth_rx_burst(gDpdkPortId, 0, rx, BURST_SIZE);
if (num_recvd > BURST_SIZE) {
rte_exit(EXIT_FAILURE, "Error receiving from eth\n");
} else if (num_recvd > 0) {
rte_ring_sp_enqueue_burst(ring->in, (void**)rx, num_recvd, NULL);
}
// tx
struct rte_mbuf *tx[BURST_SIZE];
unsigned nb_tx = rte_ring_sc_dequeue_burst(ring->out, (void**)tx, BURST_SIZE, NULL);
if (nb_tx > 0) {
rte_eth_tx_burst(gDpdkPortId, 0, tx, nb_tx);
unsigned i = 0;
for (i = 0;i < nb_tx;i ++) {
rte_pktmbuf_free(tx[i]);
}
}
#if ENABLE_TIMER
static uint64_t prev_tsc = 0, cur_tsc;
uint64_t diff_tsc;
cur_tsc = rte_rdtsc();
diff_tsc = cur_tsc - prev_tsc;
if (diff_tsc > TIMER_RESOLUTION_CYCLES) {
rte_timer_manage();
prev_tsc = cur_tsc;
}
#endif
}
}
Makefile
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2010-2014 Intel Corporation
# binary name
APP = dpdk_packcap
# all source are stored in SRCS-y
SRCS-y := packcap.c
# Build using pkg-config variables if possible
ifeq ($(shell pkg-config --exists libdpdk && echo 0),0)
all: shared
.PHONY: shared static
shared: build/$(APP)-shared
ln -sf $(APP)-shared build/$(APP)
static: build/$(APP)-static
ln -sf $(APP)-static build/$(APP)
PKGCONF=pkg-config --define-prefix
PC_FILE := $(shell $(PKGCONF) --path libdpdk)
CFLAGS += -O3 -lm -g $(shell $(PKGCONF) --cflags libdpdk)
CFLAGS += -DALLOW_EXPERIMENTAL_API
LDFLAGS_SHARED = $(shell $(PKGCONF) --libs libdpdk)
LDFLAGS_STATIC = -Wl,-Bstatic $(shell $(PKGCONF) --static --libs libdpdk)
build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build
$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED)
build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build
$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC)
build:
@mkdir -p $@
.PHONY: clean
clean:
rm -f build/$(APP) build/$(APP)-static build/$(APP)-shared
test -d build && rmdir -p build || true
else # Build using legacy build system
ifeq ($(RTE_SDK),)
$(error "Please define RTE_SDK environment variable")
endif
# Default target, detect a build directory, by looking for a path with a .config
RTE_TARGET ?= $(notdir $(abspath $(dir $(firstword $(wildcard $(RTE_SDK)/*/.config)))))
include $(RTE_SDK)/mk/rte.vars.mk
ifneq ($(CONFIG_RTE_EXEC_ENV_LINUX),y)
$(error This application can only operate in a linux environment, \
please change the definition of the RTE_TARGET environment variable)
endif
CFLAGS += -O3
CFLAGS += -DALLOW_EXPERIMENTAL_API
CFLAGS += $(WERROR_FLAGS)
include $(RTE_SDK)/mk/rte.extapp.mk
endif