以太网帧结构
+-------------------+---------------------+---------------------+
| 目的MAC地址(6字节) | 源MAC地址(6字节) | 以太网类型/长度(2字节) |
+-------------------+---------------------+---------------------+
| 数据负载(46-1500字节) |
+-------------------+---------------------+---------------------+
| 帧校验序列(FCS,4字节) |
+---------------------------------------------------------------+
在 DPDK 中,以太网头部由 rte_ether_hdr 结构表示。以下是 rte_ether_hdr 结构的定义:
struct rte_ether_hdr {
struct rte_ether_addr d_addr; /**< Destination address. */
struct rte_ether_addr s_addr; /**< Source address. */
uint16_t ether_type; /**< Frame type. */
};
rte_ether_addr 结构表示一个 MAC 地址:
struct rte_ether_addr {
uint8_t addr_bytes[6]; /**< Addr bytes in tx order */
};
代码示例:根据 MAC 地址转发数据包
常见的 ether_type 值
- IPv4 (Internet Protocol version 4)
ether_type 值:0x0800
描述:表示以太网帧的负载是一个 IPv4 数据包。 - ARP (Address Resolution Protocol)
ether_type 值:0x0806
描述:表示以太网帧的负载是一个 ARP 数据包。 - Wake-on-LAN
ether_type 值:0x0842
描述:用于 Wake-on-LAN 魔术包。 - IETF TRILL Protocol
ether_type 值:0x22F3
描述:表示以太网帧的负载是 TRILL 数据包。 - DECnet Phase IV
ether_type 值:0x6003
描述:表示以太网帧的负载是 DECnet Phase IV 数据包。 - RARP (Reverse Address Resolution Protocol)
ether_type 值:0x8035
描述:表示以太网帧的负载是一个 RARP 数据包。 - AppleTalk (Ethertalk)
ether_type 值:0x809B
描述:表示以太网帧的负载是 AppleTalk 数据包。 - AppleTalk ARP (AARP)
ether_type 值:0x80F3
描述:表示以太网帧的负载是 AppleTalk ARP 数据包。 - VLAN-tagged frame (IEEE 802.1Q) & Shortest Path Bridging IEEE 802.1aq
ether_type 值:0x8100
描述:表示以太网帧包含 VLAN 标记。 - IPX (Internetwork Packet Exchange)
ether_type 值:0x8137
描述:表示以太网帧的负载是 IPX 数据包。 - QNX Qnet
ether_type 值:0x8204
描述:表示以太网帧的负载是 QNX Qnet 数据包。 - IPv6 (Internet Protocol version 6)
ether_type 值:0x86DD
描述:表示以太网帧的负载是一个 IPv6 数据包。 - Ethernet loopback packet
ether_type 值:0x9000
描述:用于以太网回环测试。 - MPLS unicast
ether_type 值:0x8847
描述:表示以太网帧的负载是 MPLS 单播数据包。 - MPLS multicast
ether_type 值:0x8848
描述:表示以太网帧的负载是 MPLS 多播数据包。 - PPP over Ethernet (PPPoE) Discovery Stage
ether_type 值:0x8863
描述:表示以太网帧的负载是 PPPoE 发现阶段的数据包。 - PPP over Ethernet (PPPoE) Session Stage
ether_type 值:0x8864
描述:表示以太网帧的负载是 PPPoE 会话阶段的数据包。 - Jumbo Frames
ether_type 值:0x8870
描述:用于支持巨型帧。 - EAP over LAN (IEEE 802.1X)
ether_type 值:0x888E
描述:表示以太网帧的负载是 EAPoL 数据包。 - PROFINET
ether_type 值:0x8892
描述:表示以太网帧的负载是 PROFINET 实时数据包。
#include <rte_eal.h>
#include <rte_ethdev.h>
#include <rte_mbuf.h>
#include <rte_lcore.h>
#include <rte_ether.h>
#include <stdio.h>
#define MAX_PKT_BURST 32
#define MEMPOOL_CACHE_SIZE 256
static const struct rte_eth_conf port_conf_default = {
.rxmode = {
.max_rx_pkt_len = RTE_ETHER_MAX_LEN,
},
};
// 初始化端口函数
static int port_init(uint16_t port, struct rte_mempool *mbuf_pool) {
struct rte_eth_conf port_conf = port_conf_default;
const uint16_t rx_rings = 1, tx_rings = 1;
uint16_t nb_rxd = 1024;
uint16_t nb_txd = 1024;
int retval;
uint16_t q;
// 检查端口是否有效
if (port >= rte_eth_dev_count_avail())
return -1;
// 配置端口
retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
if (retval != 0)
return retval;
// 分配和设置 RX 队列
for (q = 0; q < rx_rings; q++) {
retval = rte_eth_rx_queue_setup(port, q, nb_rxd,
rte_eth_dev_socket_id(port), NULL, mbuf_pool);
if (retval < 0)
return retval;
}
// 分配和设置 TX 队列
for (q = 0; q < tx_rings; q++) {
retval = rte_eth_tx_queue_setup(port, q, nb_txd,
rte_eth_dev_socket_id(port), NULL);
if (retval < 0)
return retval;
}
// 启动端口
retval = rte_eth_dev_start(port);
if (retval < 0)
return retval;
// 启用混杂模式(可选)
rte_eth_promiscuous_enable(port);
return 0;
}
// 主循环函数
static void l2fwd_main_loop(void) {
uint16_t port;
struct rte_mbuf *bufs[MAX_PKT_BURST];
unsigned lcore_id = rte_lcore_id();
// 检查每个端口的 NUMA 节点
for (port = 0; port < rte_eth_dev_count_avail(); port++) {
if (rte_eth_dev_socket_id(port) != (int)rte_socket_id()) {
printf("Warning: port %u is on remote NUMA node to polling thread.\n"
"Performance will not be optimal.\n", port);
}
}
// 无限循环处理数据包
while (1) {
// 遍历所有端口
for (port = 0; port < rte_eth_dev_count_avail(); port++) {
// 从接收队列中读取数据包
const uint16_t nb_rx = rte_eth_rx_burst(port, 0, bufs, MAX_PKT_BURST);
if (nb_rx == 0)
continue;
// 遍历接收到的数据包
for (uint16_t i = 0; i < nb_rx; i++) {
struct rte_mbuf *m = bufs[i];
struct rte_ether_hdr *eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
// 根据目的 MAC 地址决定转发端口
uint16_t dst_port = (eth_hdr->d_addr.addr_bytes[5] % rte_eth_dev_count_avail());
// 发送数据包
const uint16_t nb_tx = rte_eth_tx_burst(dst_port, 0, &m, 1);
// 释放未能发送的数据包
if (nb_tx < 1) {
rte_pktmbuf_free(m);
}
}
}
}
}
int main(int argc, char **argv) {
struct rte_mempool *mbuf_pool;
unsigned nb_ports;
uint16_t portid;
// 初始化 DPDK 环境
int ret = rte_eal_init(argc, argv);
if (ret < 0)
rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
argc -= ret;
argv += ret;
// 检查可用端口数量
nb_ports = rte_eth_dev_count_avail();
if (nb_ports < 2)
rte_exit(EXIT_FAILURE, "Error: number of ports must be >= 2\n");
// 创建内存池
mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", 8192 * nb_ports,
MEMPOOL_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
if (mbuf_pool == NULL)
rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
// 初始化所有端口
RTE_ETH_FOREACH_DEV(portid) {
if (port_init(portid, mbuf_pool) != 0)
rte_exit(EXIT_FAILURE, "Cannot init port %" PRIu16 "\n", portid);
}
// 启动主循环
l2fwd_main_loop();
return 0;
}
备注:
假设有一个目的 MAC 地址 00:1A:2B:3C:4D:5E,其字节数组表示如下:
addr_bytes = [0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E]
在这个例子中,addr_bytes[5] 的值是 0x5E(十进制为94)。如果有4个可用端口,则计算如下:
uint16_t dst_port = (0x5E % 4); // 94 % 4 = 2
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。