限于 Vlan 测试环境的缺失, F-Stack 项目初期未对 Vlan 进行完全的支持,仅支持配置是否进行 Vlan 的硬件卸载,当交换机配置了服务器返回的包无需打 Vlan tag 时可以正常使用,但如果交换机要求回报需要打 Vlan tag 则无法正常工作。
近期收到了网易 dragonorloong 同学关于 Vlan 的 Pull request,并且找到了 Vlan 测试环境对 Vlan 进行了完整的支持和测试,对以上问题进行了修复。本文将简单介绍 F-Stack 支持 Vlan 所做的修改,如何使用以及相关注意事项。
以下所列为 F-Stack 支持 Vlan 所进行的修改,具体改动细节可查看 github 相关 commits。
ff_veth.c
中增加胶水函数ff_mbuf_set_vlan_info
用于将在开启vlan_strip
选项时将 DPDK mbuf 中的 vlan tci 信息传递给 BSD 内核, 并在ff_api.symlist
增加该接口voidff_mbuf_set_vlan_info(void *hdr, uint16_t vlan_tci) { struct mbuf *m = (struct mbuf *)hdr; m->m_pkthdr.ether_vtag = vlan_tci; m->m_flags |= M_VLANTAG; return;}
b. 在protocol_filter
中定义增加 Vlan 头的兼容处理,用于在关闭vlan_strip
时可以正确匹配 TCP/UDP 的端口号,解决 kni 端口号匹配失效的问题
uint16_t ether_type = rte_be_to_cpu_16(hdr->ether_type);data += ETHER_HDR_LEN;len -= ETHER_HDR_LEN;
if (ether_type == ETHER_TYPE_VLAN) { vlanhdr = (struct vlan_hdr *)data; ether_type = rte_be_to_cpu_16(vlanhdr->eth_proto); data += sizeof(struct vlan_hdr); len -= sizeof(struct vlan_hdr);}
c. 对通过ff_regist_packet_dispatcher
注册的packet_dispatcher
函数的返回值进行处理,在该包需要直接应答,并且开启了vlan_strip
选项时,需要在数据包中增加已卸载了的 Vlan tci 信息
int ret = (*packet_dispatcher)(data, &len, queue_id, nb_queues);if (ret == FF_DISPATCH_RESPONSE) { rte_pktmbuf_pkt_len(rtem) = rte_pktmbuf_data_len(rtem) = len;
/* * We have not support vlan out strip */ if (rtem->vlan_tci) { data = rte_pktmbuf_prepend(rtem, sizeof(struct vlan_hdr)); if (data != NULL) { memmove(data, data + sizeof(struct vlan_hdr), ETHER_HDR_LEN); struct ether_hdr *etherhdr = (struct ether_hdr *)data; struct vlan_hdr *vlanhdr = (struct vlan_hdr *)(data + ETHER_HDR_LEN); vlanhdr->vlan_tci = rte_cpu_to_be_16(rtem->vlan_tci); vlanhdr->eth_proto = etherhdr->ether_type; etherhdr->ether_type = rte_cpu_to_be_16(ETHER_TYPE_VLAN); } } send_single_packet(rtem, port_id); continue;}
【注意1】在初始化 DPDK 的 memory pool 时,每个 mbuf 都预留了128字节的空闲的 HEADROOM 空间,可供在原有数据之前附加少量信息而无需挪动整个数据包
【注意2】此处 F-Stack 并未支持 Vlan out strip, 所以需要将 Vlan tci 信息加回到报文中,否则不需要
2. 工具支持
原有 ifconfig
等工具已经支持 Vlan 配置,无需修改
下面给出 F-Stack 和 KNI 分别配置 Vlan 的命令参考,供参考并根据自己的网络情况实际进行配置。
假设有两个Vlan 10 和 Vlan 20,分别处理 IPv4 和 IPv6,可以按如下步骤配置
# 创建 vlan 10 并配置 IPv4 地址和路由信息
ff_ifconfig f-stack-0.10 create
ff_ifconfig f-stack-0.10 inet <ip address> netmask <netmask>
# 清除原网卡上的 IP 信息,或者原网卡配置的是无效 IP 则不必处理
ff_ifconfig f-stack-0 inet <ip address> remove
# 重新添加默认路由
ff_route add 0.0.0.0 <gw address>
# 创建 vlan 20 并配置 IPv6 地址和路由信息
ff_ifconfig f-stack-0.20 create
ff_ifconfig f-stack-0.20 inet6 <ipv6 address> autoconf defaultif auto_linklocal accept_rtadv
ff_route -6 add ::/0 <ipv6 gw address>
如需使用 kni,可参考如下命令在系统上进行 Vlan 相关配置
ifconfig veth0 up
vconfig add veth0 10
vconfig set_flag veth0.10 1 1
ifconfig veth0.10 <ip address> netmask <netmask> broadcast <broadcast> up
route add -net 0.0.0.0 gw <gw address> dev veth0.10
vconfig add veth0 20
vconfig set_flag veth0.20 1 1
ifconfig veth0.20 inet6 add <ipv6 address>/<prefix len> up
route -6 add ::/0 gw <ipv6 address> dev veth0.20
echo 1 > /sys/class/net/veth0/carrier
vlan_strip=1
选项即可,其他可以考虑不必处理,即保持 Vlan 相关的 commits 提交之前的状态vlan_strip=0
关闭 Vlan 卸载