以下是一个简单的DPDK程序示例,可以输出数据包的IP地址、MAC地址和TCP端口:
#include <stdio.h>
#include <stdint.h>
#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_mbuf.h>
#include <rte_udp.h>
#include <rte_tcp.h>
#include <arpa/inet.h>
#define RX_RING_SIZE 128
#define TX_RING_SIZE 512
#define NUM_MBUFS 8191
#define MBUF_CACHE_SIZE 250
#define BURST_SIZE 32
static const struct rte_eth_conf port_conf_default = {
.rxmode = {
.max_rx_pkt_len = RTE_ETHER_MAX_LEN,
},
};
int main(int argc, char *argv[])
{
int ret;
uint16_t nb_ports;
uint16_t portid;
struct rte_mempool *mbuf_pool;
struct rte_eth_dev_info dev_info;
struct rte_eth_txconf txconf;
uint64_t prev_tsc, diff_tsc, cur_tsc;
unsigned lcore_id, rx_lcore_id;
uint8_t *data;
struct rte_mbuf *mbuf;
struct ether_hdr *eth_hdr;
struct ipv4_hdr *ipv4_hdr;
struct tcp_hdr *tcp_hdr;
/* 初始化 EAL */
ret = rte_eal_init(argc, argv);
if (ret < 0)
rte_exit(EXIT_FAILURE, "Cannot init EAL\n");
argc -= ret;
argv += ret;
/* 获取端口数量 */
nb_ports = rte_eth_dev_count_avail();
if (nb_ports == 0)
rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n");
/* 创建内存池 */
mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
if (mbuf_pool == NULL)
rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
/* 配置端口 */
for (portid = 0; portid < nb_ports; portid++) {
/* 获取端口信息 */
rte_eth_dev_info_get(portid, &dev_info);
/* 配置端口 */
ret = rte_eth_dev_configure(portid, 1, 1, &port_conf_default);
if (ret < 0)
rte_exit(EXIT_FAILURE, "Cannot configure device: err=%d, port=%u\n",
ret, (unsigned) portid);
/* 分配并设置 RX/TX 队列 */
ret = rte_eth_rx_queue_setup(portid, 0, RX_RING_SIZE,
rte_eth_dev_socket_id(portid), NULL, mbuf_pool);
if (ret < 0)
rte_exit(EXIT_FAILURE, "Cannot allocate RX queue: err=%d, port=%u\n",
ret, (unsigned) portid);
txconf = dev_info.default_txconf;
txconf.txq_flags = ETH_TXQ_FLAGS_IGNORE;
ret = rte_eth_tx_queue_setup(portid, 0, TX_RING_SIZE,
rte_eth_dev_socket_id(portid), &txconf);
if (ret < 0)
rte_exit(EXIT_FAILURE, "Cannot allocate TX queue: err=%d, port=%u\n",
ret, (unsigned) portid);
/* 启动端口 */
ret = rte_eth_dev_start(portid);
if (ret < 0)
rte_exit(EXIT_FAILURE, "Cannot start device: err=%d, port=%u\n",
ret, (unsigned) portid);
/* 启用混杂模式 */
rte_eth_promiscuous_enable(portid);
}
/* 获取 RX 核心 ID */
rx_lcore_id = rte_get_next_lcore(-1, 0, 1);
if (rx_lcore_id == RTE_MAX_LCORE)
rte_exit(EXIT_FAILURE, "Cannot get RX core id\n");
printf("Core %u is receiving packets. [Ctrl+C to quit]\n", rx_lcore_id);
prev_tsc = cur_tsc = rte_rdtsc();
/* 接收数据包 */
while (!rte_atomic64_read(&force_quit)) {
cur_tsc = rte_rdtsc();
diff_tsc = cur_tsc - prev_tsc;
if (diff_tsc > TIMER_RESOLUTION_CYCLES) {
/* 输出统计信息 */
printf("[Port statistics]\n");
for (portid = 0; portid < nb_ports; portid++) {
struct rte_eth_stats stats;
rte_eth_stats_get(portid, &stats);
printf("Port %u: RX %lu packets (%lu bytes), TX %lu packets (%lu bytes)\n",
(unsigned) portid, stats.ipackets, stats.ibytes,
stats.opackets, stats.obytes);
}
prev_tsc = cur_tsc;
}
/* 接收和处理数据包 */
RTE_ETH_FOREACH_DEV(portid) {
if (rte_eth_rx_burst(portid, 0, &mbuf, 1) > 0) {
/* 从 Mbuf 中获取数据 */
data = rte_pktmbuf_mtod(mbuf, uint8_t *);
/* 解析 Ethernet 头 */
eth_hdr = (struct ether_hdr *) data;
/* 判断是否为 IPv4 数据包 */
if (eth_hdr->ether_type == rte_cpu_to_be_16(ETHER_TYPE_IPv4)) {
/* 解析 IPv4 头 */
ipv4_hdr = (struct ipv4_hdr *) (data + sizeof(struct ether_hdr));
/* 判断是否为 TCP 数据包 */
if (ipv4_hdr->next_proto_id == IPPROTO_TCP) {
/* 解析 TCP 头 */
tcp_hdr = (struct tcp_hdr *) (data + sizeof(struct ether_hdr) +
sizeof(struct ipv4_hdr));
/* 输出 IP 地址、MAC 地址和 TCP 端口 */
printf("IP: %s, MAC: %02X:%02X:%02X:%02X:%02X:%02X, TCP Port: %u\n",
inet_ntoa(*(struct in_addr *) &ipv4_hdr->dst_addr),
eth_hdr->s_addr.addr_bytes[0],
eth_hdr->s_addr.addr_bytes[1],
eth_hdr->s_addr.addr_bytes[2],
eth_hdr->s_addr.addr_bytes[3],
eth_hdr->s_addr.addr_bytes[4],
eth_hdr->s_addr.addr_bytes[5],
rte_be_to_cpu_16(tcp_hdr->dst_port));
}
}
/* 释放 Mbuf */
rte_pktmbuf_free(mbuf);
}
}
}
/* 停止所有端口 */
RTE_ETH_FOREACH_DEV(portid) {
rte_eth_dev_stop(portid);
rte_eth_dev_close(portid);
}
return 0;
}
这个程序可以在单个核心上运行,接收来自所有端口的数据包,并输出它们的IP地址、MAC地址和TCP端口。由于DPDK通过绕过操作系统内核来提高性能,因此需要以root权限运行。