最近OVS-DPDK做分片重组的时候,遇到一些问题,所以现在大致看了一下分片思路和代码,现在记录一下。DPDK版本为16.11.1
分片试验
因为涉及到封装,所以我的分片不是基于标准的MTU 1500,而是基于1422,我采用ICMP报文,长度5000。以下记录了一下打印信息。从打印的信息可以看到,分片并尽量不使用copy,所以导致于新的分片都都几个段组成,中间记录了一些偏移量。
in_packet
buf_addr 0x7f49901b8180, data_off 128, buf_len 2184, nb_segs 4, pkt_len 5042, data_len 1514
buf_addr 0x7f49908ac680, data_off 162, buf_len 2184, nb_segs 3, pkt_len 1480, data_len 1480
buf_addr 0x7f49908abb40, data_off 162, buf_len 2184, nb_segs 2, pkt_len 1480, data_len 1480
buf_addr 0x7f49908ab000, data_off 162, buf_len 2184, nb_segs 1, pkt_len 568, data_len 568
out_packet
buf_addr 0x7f49908aa4c0, data_off 114, buf_len 2184, nb_segs 2, pkt_len 1456, data_len 34
buf_addr 0x7f49901b8180, data_off 162, buf_len 2184, nb_segs 1, pkt_len 1500, data_len 1422
out_packet
buf_addr 0x7f49908a8e40, data_off 114, buf_len 2184, nb_segs 3, pkt_len 1456, data_len 34
buf_addr 0x7f49901b8180, data_off 1584, buf_len 2184, nb_segs 1, pkt_len 1500, data_len 58
buf_addr 0x7f49908ac680, data_off 162, buf_len 2184, nb_segs 1, pkt_len 1480, data_len 1364
out_packet
buf_addr 0x7f49908a6c80, data_off 114, buf_len 2184, nb_segs 3, pkt_len 1456, data_len 34
buf_addr 0x7f49908ac680, data_off 1526, buf_len 2184, nb_segs 1, pkt_len 1480, data_len 116
buf_addr 0x7f49908abb40, data_off 162, buf_len 2184, nb_segs 1, pkt_len 1480, data_len 1306
out_packet
buf_addr 0x7f49908a4ac0, data_off 114, buf_len 2184, nb_segs 3, pkt_len 776, data_len 34
buf_addr 0x7f49908abb40, data_off 1468, buf_len 2184, nb_segs 1, pkt_len 1480, data_len 174
buf_addr 0x7f49908ab000, data_off 162, buf_len 2184, nb_segs 1, pkt_len 568, data_len 568
入报文,长度位5042(icmp 5000+8+20+14)
第一个报文是偏移162开始L4数据,长度1480
接下来的2个报文也都是偏移162开始,长度1480
最后一个报文偏移162开始,长度568
mtu 1442
出报文4个,总长度5144
第一个报文新建一个L2和L3的头,接着使用入报文1的L4开始的数据,长度位1422,总长度34+1422=1456
第二个报文新建一个L2和L3的头,接着使用入报文1的偏移1584(1422+162),长度58(报文长度1422+58=1480),接着使用入报文2的偏移162,长度1364,所以总长度34+58+1364=1456
第三个报文新建一个L2和L3的头,接着使用入报文2的偏移1526(1364+162),长度116(报文长度1364+116=1480),接着使用入报文3的偏移162,长度1306,所以总长度34+116+1306=1456
第四个报文新建一个L2和L3的头,接着使用入报文3的偏移1468(1306+162),长度174(报文长度1306+174=1480),接着使用入报文4的偏移162,长度568,所以总长度34+174+568=776
代码查看出现的问题
int32_t
rte_ipv4_fragment_packet(struct rte_mbuf *pkt_in,
struct rte_mbuf **pkts_out,
uint16_t nb_pkts_out,
uint16_t mtu_size,
struct rte_mempool *pool_direct,
struct rte_mempool *pool_indirect)
{
...
//此处问题就是分片大小计算的时候默认IP协议头是20,不包含option
//以下还有几处存在这个问题,需要注意
frag_size = (uint16_t)(mtu_size - sizeof(struct ipv4_hdr));
//此处要求分片大小必须是8的倍数,但是非debug版本这块是忽略的
//函数外保证这一点的话,会比较麻烦,因为mtu_size和ip头长度一起决定的
//分片大小反正是这里计算的,所以计算的时候保证是8的倍数比较好
RTE_ASSERT((frag_size & IPV4_HDR_FO_MASK) == 0);
//所以以上两个问题推荐合并两行代码为以下一行代码
frag_size = (uint16_t)((mtu_size - ((in_hdr->version_ihl & 0xf) << 2)) & 0xfff8);
...
}