加入收藏 | 设为首页 | 会员中心 | 我要投稿 济南站长网 (https://www.0531zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 服务器 > 搭建环境 > Linux > 正文

教你编写一个Linux虚拟网卡来实现类NVI

发布时间:2016-09-11 04:50:35 所属栏目:Linux 来源:站长网
导读:我们可以在Linux上使用loopback接口来模拟两个阶段的路由抉择,第一个阶段是走一遍PRE/POST ROUTING流程,将NAT实施完毕,第二阶段完成单纯路由转发。然而需要

我们可以在Linux上使用loopback接口来模拟两个阶段的路由抉择,第一个阶段是走一遍PRE/POST ROUTING流程,将NAT实施完毕,第二阶段完成单纯路由转发。然而需要在Netfilter上挂钩子,以便取消关联在skb上的路由项,并且取消关联在skb上的conntrack信息,因为在第二阶段的单纯路由流程里面,我不希望再有什么基于conntrack的动作,因此如果需要有基于conntrack的操作,务必在第一阶段内和NAT一并完成。

回过头来看loopback的实现,不是那么完美,因为像在Netfilter上挂载钩子完成的这种事完全可以在虚拟网卡的xmit操作中完成,因此有必要重新写一个虚拟网卡,之所以最终还是考虑重新写,是因为这个模块超级简单,基本可以照搬loopback.c的实现,所不同的是xmit的操作:

static netdev_tx_t nvi_xmit(struct sk_buff *skb,

                            struct net_device *dev)

{

int len;

//注意,我把原始的数据包入接口写在了skb的mark中了,为何能这么做呢?因为...

struct net_device * real_dev = dev_get_by_index(dev_net(dev), skb->mark);

skb_orphan(skb);

skb->protocol = eth_type_trans(skb, real_dev);

//取消关联的路由项,以便可以在ip_input的时候重新policy routing

skb_dst_drop(skb);

//取消conntrack,因为它的任务在第一阶段已经完成了

skb->nfct = &nf_conntrack_untracked.ct_general;

skb->nfctinfo = IP_CT_NEW;

nf_conntrack_get(skb->nfct);

   len = skb->len;

   if (likely(netif_rx(skb) == NET_RX_SUCCESS)) {

   ...//做点什么好呢?统计?

} else {

   ...//...

}

return NETDEV_TX_OK;

}

对NVI接口的注册也非常简单:

dev = alloc_netdev(0, "nvi", nvi_setup);

为何可以使用skb的mark来保存入接口index呢?实际上在32位的机器上它完全可以保存原始入网卡dev的地址,强转成net_device类型指针即可。我并不是一开始就直接把入网卡的index保存在mark中了,因为可能Netfilter钩子还要用这个mark,我也没有使用mark的掩码掩去一些位来保存index,因为不知情者可能会误用。我采用的方式是在“确认不可能有Netfilter钩子使用mark的时候再将其覆盖成入网卡的index,那么何时合适呢?精通Netfilter的都知道,在POSTROUTING的最后做这件事比较合适,因此我就把这个HOOK安置于POSTROUTING的nf_confirm之后。是否会有流控用到mark我不管,毕竟流控是在物理网卡上做的,和第一轮的路由无关。然而问题是,到了POSTROUTING的时候我还能取到原始的入网卡的index吗?Oh,NO!:

int ip_output(struct sk_buff *skb)

{

struct net_device *dev = skb_dst(skb)->dev;

IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len);

//在此处,POSTROUTING前替换了skb的dev...

skb->dev = dev;

skb->protocol = htons(ETH_P_IP);

return NF_HOOK_COND(PF_INET, NF_INET_POST_ROUTING, skb, NULL, dev,

           ip_finish_output,

           !(IPCB(skb)->flags & IPSKB_REROUTED));

}

(编辑:济南站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读