1、样例功能
样例代码主要实现重组IP层分片报文,然后将重组后的报文转发出去,如类型为IPV4的数据包A(包总长度1434)、B(包总长度1434)、C(包总长度42)重组后得到数据包D(包总长度2842),集齐完毕之后将数据包A、B、C、D依次根据匹配规则转发到指定端口,流程结束。
如果实现IP重组功能,理论上需要一个API接口,每次接收一个数据包,首先判断这个报文是否是分片包,如果是分片包则调用重组API接口,主要实现传入的数据包如果是最后一个包(就差它就重组完成了),那么将重组后的数据包作为返回值从API接口中获取到,如果不是最后一个包,那么将这个数据包插入到类似于HASH表或者链表中,流程到此结束,继续解析下一个数据包。
实际上DPDK的IP重组样例所实现流程就是这样,下面根据代码流程逐步进行了解。
2、IP重组模块
在此之前需要考虑几个问题:
- 是否需要考虑超时删除及如何确定超时时间?(时间太短容易缺失,不可能将分片包一直保留这样内存就爆了)
2. 重组后的数据包是什么形式存在的?(已经重组好为一个数据包 或者 将分片的数据包有序链接为一个链表)
3. 如何确定最多能够实现多少个分片包的重组?
一、分片节点哈希表初始化
从main函数开始,对于EAL初始化、接收发送队列初始化这些和重组功能不相干的这里不再赘述,首先,调用setup_queue_tbl对分片报文所需要的ip_frag_table表进行初始化,流程如下:
这里调用rte_ip_frag_table_create用来创建ip_frag_table,接口功能解释如下:
/**
* Create a new IP fragmentation table.
*
* @param bucket_num
* Number of buckets in the hash table. // 哈希表桶数量
* @param bucket_entries
* Number of entries per bucket (e.g. hash associativity). // 每个桶下挂在的节点数量
* Should be power of two. // 应该为2的幂次方
* @param max_entries
* Maximum number of entries that could be stored in the table. // 哈希表存储的最大节点数量
* The value should be less or equal then bucket_num * bucket_entries. // 该值需小于或等于 桶数量 * 桶节点数量
* @param max_cycles
* Maximum TTL in cycles for each fragmented packet. // 每个分片包最长超时时间(TTL)
* @param socket_id
* The *socket_id* argument is the socket identifier in the case of
* NUMA. The value can be *SOCKET_ID_ANY* if there is no NUMA constraints.
* @return
* The pointer to the new allocated fragmentation table, on success. NULL on error. // 返回值为哈希表的地址,创建失败则为NULL
*/
struct rte_ip_frag_tbl * rte_ip_frag_table_create(uint32_t bucket_num,
uint32_t bucket_entries, uint32_t max_entries,
uint64_t max_cycles, int socket_id);
二、分片包重组接口
进入到循环处理接口main_loop
,对接收到的数据包调用reassemble
接口进行数据包重组。
这里重点介绍reassemble
接口流程
static inline void
reassemble(struct rte_mbuf *m, uint16_t portid, uint32_t queue,
struct lcore_queue_conf *qconf, uint64_t tms)
{
struct rte_ether_hdr *eth_hdr;
struct rte_ip_frag_tbl *tbl;
struct rte_ip_frag_death_row *dr;
struct rx_queue *rxq;
void *d_addr_bytes;
uint32_t next_hop;
uint16_t dst_port;
rxq = &qconf->rx_queue_list[queue];
eth_hdr = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
dst_port = portid;
/* if packet is IPv4 */ // 判断数据包类型IPV4 or IPV6
if (RTE_ETH_IS_IPV4_HDR(m->packet_type)) {
struct rte_ipv4_hdr *ip_hdr;
uint32_t ip_dst;
ip_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
/* if it is a fragmented packet, then try to reassemble. */
if (rte_ipv4_frag_pkt_is_fragmented(ip_hdr)) { // 判断IPV4数据包是否时分片包
struct rte_mbuf *mo;
tbl = rxq->frag_tbl;
dr = &qconf->death_row;
/* prepare mbuf: setup l2_len/l3_len. */ // 获取数据链路层