预备知识
dpdk中一些关键宏定义与结构体定义
以太网帧相关
宏RTE_ETHER_ADDR_LEN mac地址长度,6字节48位
宏RTE_ETHER_TYPE_IPV4 代表ipv4
struct rte_ether_hdr 以太网帧头结构体,包含了三个成员变量,目的地址,源地址,IP类型。
ip相关
struct rte_ipv4_hdr ipv4头结构体, 包含版本,ttl,next_proto_id,源地址,目标地址等字段
rte_ipv4_cksum()计算校验和的函数
tcp/udp相关
宏IPPROTO_UDP 代表udp协议
IPPROTO_TCP 代表udp协议
struct rte_udp_hdr udp头结构体,包含源端口,目的端口,数据长度以及校验和
rte_ipv4_udptcp_cksum(ip头地址,udp头地址)计算校验和的函数
udp头
struct rte_udp_hdr {
rte_be16_t src_port; /**< 源端口 (16位,大端格式) */
rte_be16_t dst_port; /**< 目标端口 (16位,大端格式) */
rte_be16_t dgram_len; /**< UDP数据报总长度 (16位,大端格式,包括头部) */
rte_be16_t dgram_cksum;/**< 校验和 (16位,大端格式) */
};
tcp头
struct rte_ipv4_hdr {
uint8_t version_ihl; /**< 版本号 (4 bit) + 头部长度 (4 bit) */
uint8_t type_of_service; /**< 服务类型 */
rte_be16_t total_length; /**< 整个 IPv4 数据包的总长度 (以字节为单位) */
rte_be16_t packet_id; /**< 包标识符 */
rte_be16_t fragment_offset; /**< 分片偏移量和标志位 */
uint8_t time_to_live; /**< 生存时间 (TTL) */
uint8_t next_proto_id; /**< 上层协议 (如 TCP=6, UDP=17) */
rte_be16_t hdr_checksum; /**< IPv4 头部的校验和 */
rte_be32_t src_addr; /**< 源 IP 地址 */
rte_be32_t dst_addr; /**< 目标 IP 地址 */
}
内存池中 mbuf结构体的定义
struct rte_mbuf {
void *buf_addr; /* 缓冲区的起始地址 */
uint16_t buf_len; /* 缓冲区的总长度 */
uint16_t data_off; /* 数据在缓冲区中的偏移量 */
uint64_t ol_flags; /* Offload 标志,例如 checksum 校验等 */
uint16_t pkt_len; /* 数据包的总长度 */
uint16_t data_len; /* 数据包中有效数据的长度 */
uint32_t pkt_type; /* 数据包类型(如 IPv4, TCP, UDP 等) */
uint64_t timestamp; /* 时间戳 */
struct rte_mempool *pool; /* 指向内存池的指针 */
struct rte_mbuf *next; /* 链接到下一个 mbuf(用于分段数据包) */
uint16_t refcnt; /* 引用计数器(用于共享 mbuf) */
uint8_t port; /* 接收端口编号 */
uint8_t nb_segs; /* 数据包的段数量(如多段数据包) */
};
内存池中的mbuf并不会直接存储数据包的信息,而是存储数据包的地址,大小等信息。可以通过buf_addr + data_off的方式,计算出实际数据的地址,读取数据信息。所以dpdk提供了相应的宏来进行实际数据地址的计算
rte_pktmbuf_mtod(mbuf, int*),把mbuf对应的缓冲区的地址转换为int*类型。
udp数据编码
其实就是如何封装一个以太网帧,把内容存放到mbuf中,通过dpdk发送到网卡的发送队列里。
static int ustack_encode_udp_pkt(uint8_t *msg, uint8_t *data, uint16_t total_len){
//ether header
struct rte_ether_hdr *eth = (struct rte_ether_hdr*) msg;
rte_memcpy(eth->d_addr.addr_bytes, global_dmac, RTE_ETHER_ADDR_LEN);
rte_memcpy(eth->s_addr.addr_bytes, global_smac, RTE_ETHER_ADDR_LEN);
eth->ether_type = htons(RTE_ETHER_TYPE_IPV4);
//ip header
struct rte_ipv4_hdr *iphdr = (struct rte_ipv4_hdr *) (eth + 1); //msg + sizeof(eht)
iphdr->version_ihl = 0x45;
iphdr->type_of_service = 0;
iphdr->total_length = htons(total_len - sizeof(struct rte_ether_hdr));
iphdr->packet_id = 0;
iphdr->fragment_offset = 0;
iphdr->time_to_live = 64;
iphdr->next_proto_id = IPPROTO_UDP;
iphdr->src_addr = global_sip;
iphdr->dst_addr = global_dip;
iphdr->hdr_checksum = 0;
iphdr->hdr_checksum = rte_ipv4_cksum(iphdr);
//udp header
struct rte_udp_hdr * udphdr = (struct rte_udp_hdr *)(iphdr + 1);
udphdr->src_port = global_sport;
udphdr->dst_port = global_dport;
uint16_t udplen = total_len - sizeof(struct rte_ether_hdr) - sizeof(struct rte_ipv4_hdr);
udphdr->dgram_len = htons(udplen);
//填充数据
rte_memcpy((uint8_t *)(udphdr + 1), data, udplen);
udphdr->dgram_cksum = 0;
udphdr->dgram_cksum = rte_ipv4_udptcp_cksum(iphdr, udphdr);
return 0;
}
tcp数据的解码与编码
tcp数据的解码:从mbuf中获取实际信息的地址,再对信息进行解析。
与udp类似,都包含以太网头、ip头、tcp头等
static int ustack_decode_tcp_pkt(uint8_t *msg, uint16_t total_len){
//ether header
struct rte_ether_hdr *eth = (struct rte_ether_hdr*) msg;
rte_memcpy(eth->d_addr.addr_bytes, global_dmac, RTE_ETHER_ADDR_LEN);
rte_memcpy(eth->s_addr.addr_bytes, global_smac, RTE_ETHER_ADDR_LEN);
eth->ether_type = htons(RTE_ETHER_TYPE_IPV4);
//ip header
struct rte_ipv4_hdr *iphdr = (struct rte_ipv4_hdr *) (eth + 1); //msg + sizeof(eht)
iphdr->version_ihl = 0x45;
iphdr->type_of_service = 0;
iphdr->total_length = htons(total_len - sizeof(struct rte_ether_hdr));
iphdr->packet_id = 0;
iphdr->fragment_offset = 0;
iphdr->time_to_live = 64;
iphdr->next_proto_id = IPPROTO_TCP;
iphdr->src_addr = global_sip;
iphdr->dst_addr = global_dip;
iphdr->hdr_checksum = 0;
iphdr->hdr_checksum = rte_ipv4_cksum(iphdr);
//tcp header
struct rte_tcp_hdr * tcphdr = (struct rte_tcp_hdr *)(iphdr + 1);
tcphdr->src_port = global_sport;
tcphdr->dst_port = global_dport;
tcphdr->sent_seq = htonl(12345);
tcphdr->recv_ack = 0x0;
tcphdr->data_off = 0x50;
tcphdr->tcp_flags = 0x1 << 1;
tcphdr->rx_win = htons(4096);
tcphdr->cksum = 0;
tcphdr->cksum = rte_ipv4_udptcp_cksum(iphdr, tcphdr);
return 0;
}
在主函数中调用该函数,进行解析
if(iphdr->next_proto_id == IPPROTO_TCP){
struct rte_tcp_hdr *tcphdr = (struct rte_tcp_hdr *)(iphdr + 1);
struct in_addr addr;
addr.s_addr = iphdr->src_addr;
printf("tcp\n src: %s:%d ----> ", inet_ntoa(addr), ntohs(tcphdr->src_port));
addr.s_addr = iphdr->dst_addr;
printf("des: %s:%d\n", inet_ntoa(addr), ntohs(tcphdr->dst_port));
rte_memcpy(global_smac, ethhdr->d_addr.addr_bytes, RTE_ETHER_ADDR_LEN);
rte_memcpy(global_dmac, ethhdr->s_addr.addr_bytes, RTE_ETHER_ADDR_LEN);
rte_memcpy(&global_sip, &iphdr->dst_addr, sizeof(uint32_t));
rte_memcpy(&global_dip, &iphdr->src_addr, sizeof(uint32_t));
rte_memcpy(&global_sport, &tcphdr->dst_port, sizeof(uint32_t));
rte_memcpy(&global_dport, &tcphdr->src_port, sizeof(uint32_t));
uint16_t total_len = sizeof(struct rte_tcp_hdr) + sizeof(struct rte_ipv4_hdr) + sizeof(struct rte_ether_hdr);
struct rte_mbuf *mbuf = rte_pktmbuf_alloc(mbuf_pool);
mbuf->pkt_len = total_len;
mbuf->data_len = total_len;
uint8_t *msg = rte_pktmbuf_mtod(mbuf, uint8_t *);
ustack_decode_tcp_pkt(msg, total_len);
rte_eth_tx_burst(global_portid, 0, &mbuf, 1);
}
项目地址:www.github.com/0voice