背景
在使用dpdk做流转发的时候,期望将部分流量转到tap设备,以供Linux协议栈消费。
做测试的时候发现两个问题,
问题一: 报文超过MTU 1500的报文,在tap设备上抓不到报文。
问题二: ifconfig 修改tap设备的MTU为 2000,在tap设备上依旧抓不到报文,猜测这个地方只是修改了Linux本身的限制,并没有修改dpdk运行时代码的限制。
dpdk版本: 22.11
发包分析
dpdk发送报文到tap设备的接口为rte_eth_tx_burst,对应的tap pmd的接口为 pmd_tx_burst,查看代码发现报文长度大于MTU长度是直接返回,不执行后面的发包操作。所以问题一,问题二都得到了解释,dpdk发包报文大小是根据自己的MTU配置来的。
pmd_tx_burst 代码位置 drivers/net/tap/rte_eth_tap.c
static uint16_t
pmd_tx_burst(void *queue, struct rte_mbuf **bufs, uint16_t nb_pkts)
{
...
max_size = *txq->mtu + (RTE_ETHER_HDR_LEN + RTE_ETHER_CRC_LEN + 4);
for (i = 0; i < nb_pkts; i++) {
/* stats.errs will be incremented */
if (rte_pktmbuf_pkt_len(mbuf_in) > max_size)
break; //报文长度大于MTU长度是直接返回,不执行后面的发包操作
ret = tap_write_mbufs(txq, num_mbufs, mbuf,
&num_packets, &num_tx_bytes);
}
...
}
解决方法
修改tap设备的MTU为2000。
通过调用 rte_eth_dev_configure 来修改MTU,结果MTU合法性检查失败,导致MTU修改失败。
int
rte_eth_dev_configure(uint16_t port_id, uint16_t nb_rx_q, uint16_t nb_tx_q,
const struct rte_eth_conf *dev_conf)
{
...
ret = rte_eth_dev_info_get(port_id, &dev_info); //获取设备信息
...
ret = eth_dev_validate_mtu(port_id, &dev_info,
dev->data->dev_conf.rxmode.mtu); //MTU合法性检查失败,导致MTU修改失败
if (ret != 0)
goto rollback;
...
}
//MTU合法性检查函数
static int
eth_dev_validate_mtu(uint16_t port_id, struct rte_eth_dev_info *dev_info,
uint16_t mtu) {
...
overhead_len = eth_dev_get_overhead_len(dev_info->max_rx_pktlen,
dev_info->max_mtu);
frame_size = mtu + overhead_len;
if (frame_size > dev_info->max_rx_pktlen) { //修改后报文的大小比设备支持的最大报文长度还大,非法
RTE_ETHDEV_LOG(ERR,
"Frame size (%u) > device max frame size (%u) for port_id %u\n",
frame_size, dev_info->max_rx_pktlen, port_id);
return -EINVAL;
}
...
}
static int
tap_dev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
{
...
dev_info->max_rx_pktlen = (uint32_t)RTE_ETHER_MAX_VLAN_FRAME_LEN; //tap设备支持的最大报文长度
...
}
最终方案
需要修改tap设备支持的最大报文长度,才能修改tap设备的MTU值。
static int
tap_dev_info(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
{
...
dev_info->max_rx_pktlen = (uint32_t)UINT32_MAX;
...
}