static netdev_tx_t tc_xmit(struct sk_buff *skb,
struct net_device *dev)
{
skb_orphan(skb);
// 直接通过第二层!
skb->protocol = eth_type_trans(skb, dev);
skb_reset_network_header(skb);
skb_reset_transport_header(skb);
skb->mac_len = skb->network_header - skb->mac_header;
// 本地接收
ip_local_deliver(skb);
return NETDEV_TX_OK;
}
接下来考虑如何将数据包导入到该虚拟网卡。有3种方案可选:
方案1:如果不想设置arp相关的东西,就要修改内核了。在此我引入了一个路由标志,RT_F_INGRESS_TC,凡是有该标志的路由,全部将其导入到构建的虚拟网卡中,为了策略化,我并没有在代码中这么写,而是改变了RT_F_INGRESS_TC路由的查找顺序,优先查找策略路由表,然后再查找local表,这样就可以用策略路由将数据包导入到虚拟网卡了。
方案2:构建一个Netfilter HOOK,在其target中将希望流控的数据NF_QUEUE到虚拟网卡,即在queue的handler中设置skb->dev为虚拟网卡,调用dev_queue_xmit(skb)即可,而该虚拟网卡则不再符合上面的图示,新的原理图比较简单,只需要在虚拟网卡的hard_xmit中reinject数据包即可。(事实上,后来我才知道,原来IMQ就是这么实现的,幸亏没有动手去做无用功)
方案3:这是个快速测试方案,也就是我最原始的想法,即将目标IP地址从local表删除,然后手动arping,我的测试也是基于这个方案,效果不错。
不管上面的方案如何变化,终究都是一个效果,那就是既然网卡的ingress不能流控,那就在egress上做,而又不能使用物理网卡,虚拟网卡恰好可以自定义其实现,可以满足任意需求。我们可以看到,虚拟网卡是多么的功能强大,tun,lo,nvi,tc...所有这一切,精妙之处全在各自不同的xmit函数。