实现一个做双向NAT的虚拟网卡

问题描述与解决方案

还是老问题,Linux系统中通过iptables配置的NAT无法在双向通信环境中使用,你无法配置一条NAT规则实现对两个方向主动发起的流量做NAT,解决这个问题的方案有好几种:

1.配置两条NAT规则

iptables的NAT配置本身就是先match再执行一个target,因此一条规则只能表示一种转换策略,要想实现“来自x的数据包的源地址转换为y,去往y的数据包的目标地址转为x”这样的逻辑,必须使用两条规则。那么为何不使用两条规则呢?因为iptables的nat配置是基于数据流的,它只对一个创建ip_conntrack结构体的那个数据包进行规则查找,因此在一个流已经创建并在数据传输的时候,加入一条nat配置是无效的。
       xtables-addons中有一个RAWNAT,不再基于ip_conntrack了,也就是它是基于数据包而不是数据流的NAT,即时生效问题解决了,但是由于它还是一个match-target规则,因此要想实现双向的NAT,还是要配置两条规则。

2.编写一个Netfilter HOOK

编写一个Netfilter HOOK模块不是什么难事,我自己写过好几个,但是,Netfilter框架是在协议栈的处理路径上拦截数据包进行检查-匹配/动作的,它对每一个经过协议栈的数据包都要进行检查,也就是说每一个数据包都要经过HOOK函数的过滤,在Netfilter HOOK过多的时候,大大降低了效率。

3.使用专门的虚拟设备

这是一种全新的理念,实现一个虚拟网卡,其xmit函数是这样的:

static netdev_tx_t sdnat_net_xmit(struct sk_buff *skb, struct net_device *dev)
{
    struct sdnat_struct *sdnat = netdev_priv(dev);
    unsigned int flags = sdnat->flags;
    struct nat_entry *entry;

    entry = find_sdnat_policy(skb, flags);
    if (unlikely(!entry)) {
        goto xmit;
    }
    if (flags & SNAT) {
        do_trans_src(entry, skb);
    } else if (flags & DNAT) {
        do_trans_dst(entry, skb);
    }
    // 此时skb的dst为将数据包导入NAT设备的dst_entry,
    // 为了防止循环路由,将其drop,NAT已经完成,已经没有用了
    skb_dst_drop(skb);
    // 清除mark,因为一般通过mark策略路由将数据包导入NAT设备
    // 这也是为了防止循环路由
    skb->mark = 0;
xmit:
    netif_rx_ni(skb);
drop:
    kfree_skb(skb);
    return NETDEV_TX_OK;
}

do_trans_src/dst完全可以通过一个函数实现,此处是为了使接口更加清晰。具体的转换就不多说了,很简单,修改掉IP报头的源地址或者目标地址,然后重新计算L3,L4的校验码。
       关键是如何组织nat规则。我使用一个nat_entry来保存每一条规则:

struct nat_entry {
    struct hlist_node hash_list;
    __be32 key1;  //对于SNAT即原始IP地址,对于DNAT即要转换到的IP地址
    __be32 key2;  //对于SNAT即要转换到的IP地址,对于DNAT即原始IP地址
    __be32 hash;  /数据包源IP或者目标IP的jhash_2words值
    int flags;
};

hash的计算如下:

static u32 keys_get_hash(__be32 key)
{
    return jhash_2words(key, 0x01, 0x0);
}

模块加载的时候,会创建两个虚拟网卡,一个负责SNAT,一个负责DNAT,同时系统中也会有两个sdnat_struct结构体,一个负责SNAT,一个负责DNAT:

struct sdnat_struct {
    int flags;
    struct net_device   *dev;
    struct hlist_head entrys[1024];
};

Linux上要配置就是两条策略路由:
a.从内网口进入往外发的数据包导入到SNAT网卡设备进行SANT;
b.从外网口进入到内网口的数据包导入到DNAT网卡设备进行DNAT。
这样就可以双向自动转换了,不管数据是从哪个首先发起的,实现了“来自x的数据包的源地址转换为y,去往y的数据包的目标地址转为x”。是不是和Cisco的static NAT有些类似呢?定义出入设备而不是靠iptables的match来过滤数据包。我比较喜欢使用procfs作为用户接口,因为它方便shell操作:
echo +192.168.1.1 9.24.100.1 >/proc/net/nat
上面的命令执行后,将会在两块网卡共享的hash表中添加一个nat_entry,key1为192.168.1.1,key2为9.24.100.1,在SNAT网卡设备中,将会用skb的iph->saddr做hash后查表匹配其key1,取出key2作为要转换的IP地址,在DNAT网卡设备中,将会用skb的iph->daddr做hash后查表匹配key2,取出key1作为要转换到的IP地址。如果想删除一条规则,那么就执行:
echo -192.168.1.1 9.24.100.1 >/proc/net/nat
策略路由规则如下:
ip rule add iif $内网口 table snat
ip rule add iif $外网口 table dnat
ip route add 0.0.0.0/0 dev snat0 table snat
ip route add 0.0.0.0/0 dev dnat0 table dnat
依靠路由来做是否要进行NAT的判断,是不是更加高效些呢?而不再需要通过Netfilter模块去匹配每一个数据包了,也不需要折腾低效率的ip_conntrack了!值得注意的是,sdnat设备的xmit函数最终执行了一个netif_rx_ni这相当于将数据包重新注入其本身,此时数据包的iif将不再是内网口或者外网口了,而是实实在在的sdant虚拟网卡设备,因此数据包再次到达路由模块的时候将不会再次进入sdnat设备。

引申出来的思想和含义

除了Netfilter框架之外,我们也可以使用Linux的网卡设备模型来构建另一套数据包过滤系统,是的,其思想就是上面展示的。我曾经写过几篇关于在路由项中保存信息,然后通过查路由表的方式获取信息的技巧,其中使用了自己定义的“路由表”,查询方式依然是最长前缀匹配法,只是路由项中保存的东西变了。在本文中,我给出的是使用Linux原生的路由表(不是自己定义的)+自定义的虚拟网卡设备实现数据包过滤的思想,按照这种思想,iptables的每一个target就是一个虚拟网卡设备,每一系列的matches就是一条路由,该路由的路由项就是将数据包导入对应的虚拟网卡设备,路由的方式来匹配数据包将比Netfilter的方式高效,因为它使用了hash/trie这类高效的数据结构,而不是像Netfilter那样遍历好几层的链表。
       事实上,这种思想很新吗?不!路由项不是有unreachable或者blackhole吗?它们不正是iptables的REJECT和DROP么?

实现一个做双向NAT的虚拟网卡,布布扣,bubuko.com

时间: 2024-08-07 04:06:43

实现一个做双向NAT的虚拟网卡的相关文章

CentOS设置虚拟网卡做NAT方式和Bridge方式桥接

http://www.centoscn.com/CentOS/config/2015/0225/4736.html 摘要:KVM虚拟机网络配置的两种方式:NAT方式和Bridge方式.Bridge方式的配置原理和步骤.Bridge方式适用于服务器主机的虚拟化.问题?客户机安装完成后,需要为其设置网络接口,以便和主机网络,客户机之间的网络通信.事实上,如果要在安装时使用网络通信,需要提前设置客户机的网络连接. KVM 客户机网络连接有两种方式: 用户网络(User Networking):让虚拟机

虚拟网卡实现一个网卡多个地址

实验背景:在linux的使用过程中,有时候我们可能需要使用多个ip地址做测试,但又不想添加网卡时,此时虚拟网卡是个不错的选择 环境:CentOS 6.8 配置步骤: 1 .配置原来的eth0网卡的配置文件 DEVICE="eth0" BOOTPROTO="dhcp"    //IP获取方式为dhcp 2.配置虚拟网卡 [[email protected] ~]# vi /etc/sysconfig/network-scripts/ifcfg-eth0:0 DEVIC

win7设置虚拟网卡以及如何设置静态IP

首先来说明一下我用的虚拟机是VMware-workstation-6.5.1物理机平台是Win7旗舰版(说实在现在很多教程都还是以古老的XP系统来讲解的我以我就试着在Win7下小试一下.),人家都说嵌入式并不难,但是为什么还是有很多对嵌入式感兴趣的小伙伴最后放弃了呢?以我的拙见可能就是大家对linux系统(温馨提示:此处我说的Linux系统是指PC机上的.)的不熟悉导致的,有些小伙伴就问了这个学嵌入式怎么跑到Linux系统了呢,多方面的原因其中最重要的是因为我们图便宜-系统开源免费呗!既然我们选

kvm虚拟化关闭虚拟网卡virbr0的方法

kvm虚拟化关闭虚拟网卡virbr0的方法 我们知道:kvm虚拟化环境安装好后,ifconfig会发现多了一个虚拟网卡virbr0这是由于安装和启用了libvirt服务后生成的,libvirt在服务器(host)上生成一个 virtual network switch (virbr0),host上所有的虚拟机(guests)通过这个 virbr0 连起来.默认情况下 virbr0 使用的是 NAT 模式(采用 IP Masquerade),所以这种情况下 guest 通过 host 才能访问外部

半截水晶头线接激活本地虚拟网卡

可有可无的小方法~ 很多人在用虚拟机的时候,有时虚拟网卡不够用,又没有可用网线插来激活本地网卡时,这时可以自制一个水晶头加半截网线(或者从水晶头还 能用的废掉的网线上截取下来如下图)来激活本地网卡,多增加一个可用虚拟网卡(虽然第一次在当时无法解决(^-^)) 制作方法:截取或制作成水晶头后,将线皮剥掉至漏出铜线,将线序颜色排列成T-568B的线序(如下图) 以10/100兆太网网卡为例的DTE类型接口引脚定义为(对应上图从左到右): 1-TX+Tranceive  Data+  (发信号+)  

虚拟网卡TUN/TAP 驱动程序设计原理

昨天韦哥写了<Linux下Tun/Tap设备通信原理>一文,只提到了两个使用Tun的用户进程之间的通信路径,并没有说明Tun虚拟网卡驱动是如何实现的,而正好看到了这里的一篇讲解这方面的文章,果断转载了,感谢作者,原文在这里:虚拟网卡TUN/TAP 驱动程序设计原理 简介 虚拟网卡Tun/tap驱动是一个开源项目,支持很多的类UNIX平台,OpenVPN和Vtun都是基于它实现隧道包封装.本文将介绍tun/tap驱动的使用并分析虚拟网卡tun/tap驱动程序在linux环境下的设计思路. tun

VM虚拟机虚拟网卡对于UDP发送广播包的影响

近期做一个项目需要用到UDP发送广播包给嵌入式端的程序并要求其作出相应的反馈,一开始非常顺利.但到了测试通用性的阶段发现有的电脑无法发送出UDP广播包,进而也就是无法跟嵌入式端交互(发现并将问题锁定在此就已经花费了非常非常非常多的心血!其中可以ping通嵌入式端这点让人纠结了很久).在问题锁定了之后第一个怀疑的是防火墙,然后是杀毒软件等等,将电脑裸奔满怀希望开启软件之后依然是那副尿性.最后百无聊赖百度了下,在一篇博客的一小段中提到了VM虚拟机的虚拟网络对于UDP发送广播包有影响,突然之间恍然大悟

单网卡绑定多个ip, 多个网卡绑定成一块虚拟网卡

Linux网卡配置与绑定 Redhat Linux的网络配置,基本上是通过修改几个配置文件来实现的,虽然也可以用ifconfig来设置IP,用route来配置默认网关,用hostname来配置主机名,但是重启后会丢失. 相关的配置文件 /ect/hosts 配置主机名和IP地址的对应 /etc/sysconfig/network 配置主机名和网关 /etc/sysconfig/network-scripts/ifcfg-eth0 eth0配置文件,eth1则文件名为ifcfg-eth1,以此类推

删除Windows中隐藏的物理网卡和网络虚拟化失败后的虚拟网卡

Windows环境下,在更换硬件服务器主板和网卡等硬件.恢复操作系统或者网络虚拟化失败后,可能会出现网卡方面的问题.例如,设备管理器中多了不应该存在的网卡:因命名冲突无法重命名当前网络连接:IP地址冲突却找不到那个与之冲突的网卡:网络虚拟化失败后出现无法删除的虚拟网卡等等.下面我们就来总结一下如何删除这些可见或不可见的Ghost一样的网卡. 一.识别各类网络设备和网络连接 由于网络虚拟化会在物理机上新增许多虚拟网络设备(包括虚拟网卡),情况会变得复杂,因而首先我们需要分清他们.当然如果没有进行网