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

简介:

我们可以在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));
}
所以,到了POSTROUTING,就再也取不到原始网卡的index了!那么变通的办法就是将Linux协议栈的这段代码改了:
int ip_output(struct sk_buff *skb)
{
    struct net_device *orig_dev
    struct net_device *dev = skb_dst(skb)->dev;

    IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len);
    //保存原始入网卡的dev(反正都是forward包...)
    orig_dev = skb->dev
    skb->dev = dev;
    skb->protocol = htons(ETH_P_IP);
    //在HOOK调用的时候传入indev
    if (orig_dev && orig_dev->flags & IFF_LOOPBACK) {
        orig_dev = NULL;
    }
    return NF_HOOK_COND(PF_INET, NF_INET_POST_ROUTING, skb, orig_dev, dev,
                ip_finish_output,
                !(IPCB(skb)->flags & IPSKB_REROUTED));
}
我个人觉得,对于将Linux作为路由BOX来用的人来讲,对于FORWARDING的数据包,在POSTROUTING的时候能取到数据包从哪个网卡进入,可以实施更多的控制策略,这难道不更好吗?好吧,如果非要说这样改不好,我还有更加标准的做法,那就是在conntrack结构体中注册一个新的extend,其实就是一个结构体,将原始的入网卡作为一个字段放进去,在NVI接口的xmit中,conntrack重置为nf_conntrack_untracked之前,取出这个网卡,调用eth_type_trans接口即可,这样好了吧,我没有触动Linux kernel的主协议栈,还是基于Netfilter来做扩展!事实上,Netfilter的扩展能力是无限的!



 本文转自 dog250 51CTO博客,原文链接: http://blog.51cto.com/dog250/1304515
相关文章
|
1月前
|
安全 Linux 网络虚拟化
Linux网络名称空间和Veth虚拟设备的关系
在讨论Linux网络名称空间和veth(虚拟以太网对)之间的关系时,我们必须从Linux网络虚拟化的核心概念开始。Linux网络名称空间和veth是Linux网络虚拟化和容器化技术的重要组成部分,它们之间的关系密不可分,对于构建隔离、高效的网络环境至关重要。😊
|
1月前
|
Linux 网络安全 网络虚拟化
Linux虚拟网络设备:底层原理与性能优化深度解析
在深入探讨Linux虚拟网络设备的底层原理之前,重要的是要理解这些设备如何在Linux内核中实现,以及它们如何与操作系统的其他部分交互以提供高效且灵活的网络功能。虚拟网络设备在现代网络架构中发挥着关键作用🔑,特别是在云计算☁️、容器化📦和网络功能虚拟化(NFV)环境中。
Linux虚拟网络设备:底层原理与性能优化深度解析
|
1月前
|
Linux 网络虚拟化 虚拟化
Linux虚拟网络设备深度解析:使用场景、分类与开发者指南
Linux虚拟网络设备支撑着各种复杂的网络需求和配置,从基础的网络桥接到高级的网络隔离和加密🔐。以下是对主要Linux虚拟网络设备的介绍、它们的作用以及适用场景的概览,同时提出了一种合理的分类,并指出应用开发人员应该着重掌握的设备。
Linux虚拟网络设备深度解析:使用场景、分类与开发者指南
|
1月前
|
Cloud Native Linux 网络虚拟化
深入理解Linux veth虚拟网络设备:原理、应用与在容器化架构中的重要性
在Linux网络虚拟化领域,虚拟以太网设备(veth)扮演着至关重要的角色🌐。veth是一种特殊类型的网络设备,它在Linux内核中以成对的形式存在,允许两个网络命名空间之间的通信🔗。这篇文章将从多个维度深入分析veth的概念、作用、重要性,以及在容器和云原生环境中的应用📚。
深入理解Linux veth虚拟网络设备:原理、应用与在容器化架构中的重要性
|
13天前
|
Linux Python Windows
Linux创建虚拟环境
Linux创建虚拟环境
|
14天前
|
Ubuntu Linux 数据库
ubuntu linux 搭建ftp虚拟目录
ubuntu linux 搭建ftp虚拟目录
|
14天前
|
Ubuntu Linux
ubuntu linux配置bond 网卡绑定 多个bond配置多网关
ubuntu linux配置bond 网卡绑定 多个bond配置多网关
|
14天前
|
缓存 网络协议 Ubuntu
netperf网卡测速ubuntu linux 环境下测硬件网卡速度
netperf网卡测速ubuntu linux 环境下测硬件网卡速度
|
1月前
|
安全 Unix Linux
【专栏】`rmdir`命令在Linux和类Unix系统中用于删除空目录,不适用于非空目录
【4月更文挑战第28天】`rmdir`命令在Linux和类Unix系统中用于删除空目录,不适用于非空目录。基本语法为`rmdir [options] directory...`,常用选项包括`-p`(递归删除空父目录)和`--ignore-fail-on-non-empty`(忽略非空目录错误)。与`rm -r`相比,`rmdir`更安全,适用于知道目录为空的情况。在自动化脚本和清理构建目录等场景中,`rmdir`能有效管理空目录。使用时确保目录为空,避免误删,必要时结合`ls`和`sudo`检查或提升权限。
|
1月前
|
Ubuntu Linux
Linux(Ubuntu)系统临时IP以及静态IP配置(关闭、启动网卡等操作)
请注意,以上步骤是在临时基础上进行配置的。如果要永久保存静态IP地址,通常还需要修改 `/etc/network/interfaces`文件,以便在系统重启后保持配置。同时,确保备份相关配置文件以防止出现问题。
64 1