问题描写叙述与解决方式

还是老问题。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全然能够通过一个函数实现,此处是为了使接口更加清晰。详细的转换就不多说了,非常easy,改动掉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的虚拟网卡的更多相关文章

  1. tunctl和虚拟网卡

    1 tun/tap驱动 1.1 网卡驱动 同tcp/ip协议栈打交道,接受和发送数据包 1.2 字符驱动 内核和用户态通过字符设备交换数据包. 2 tun虚拟网卡的创建 tunctl -t tun0 ...

  2. 转【Ubuntu】添加虚拟网卡的三种方式

    原文:https://blog.csdn.net/White_Idiot/article/details/82934338 ------------------------------ 1. ifco ...

  3. Linux添加虚拟网卡的多种方法

    Linux添加虚拟网卡的多种方法有时候,一台服务器需要设置多个ip,但又不想添加多块网卡,那就需要设置虚拟网卡.这里介绍几种方式在linux服务器上添加虚拟网卡. 我们向eth0中添加一块虚拟网卡: ...

  4. 【转】Linux添加虚拟网卡

    转自:https://blog.csdn.net/hzhsan/article/details/44677867 有时候,一台服务器需要设置多个ip,但又不想添加多块网卡,那就需要设置虚拟网卡.这里介 ...

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

    CentOS设置虚拟网卡做NAT方式和Bridge方式桥接 http://www.centoscn.com/CentOS/config/2015/0225/4736.html 摘要:KVM虚拟机网络配 ...

  6. 通过桥接虚拟网卡使VMWare和宿主机实现双向通讯

    0.为什么选择虚拟网卡和桥接模式 首先虚拟机网络设置为NAT,虚拟机实现上网是很方便的,但是宿主机访问虚拟机就比较麻烦了(需要单独配置端口转发),桥接就能很好的解决这个问题,桥接模式会把虚拟机当做宿主 ...

  7. 【强烈推荐】利用NAT、Host-Only双虚拟网卡,实现Virtual Box中CentOS6.3联网

    问题背景: 先前都是在Virtual Box中以“网络共享”方式,让里面的Linux虚拟机Host-Only方式联网,参考如下: Virtual Box下配置Host-Only联网方式详解 但最近被公 ...

  8. 【荐】利用NAT、Host-Only双虚拟网卡,实现Virtual Box中CentOS5.x联网

    一.虚拟机与主机互联,通常有三种方式,详细介绍请看: VMware虚拟机三种网络模式(Bridged,Nat,Host-only)区别详解 二.通过网络共享,Host-Only联网,详细案例请看: W ...

  9. 虚拟机Linux与本地虚拟网卡配置---NAT链接方式

    虚拟机Linux与本地虚拟网卡配置---NAT链接方式 **********这是我亲自尝试多次实践出来的结果,不是复制粘贴************************* 首先进行初始化,这样避免有 ...

随机推荐

  1. [转]ArcGIS移动客户端离线地图的几种解决方案

    原文地址:http://blog.chinaunix.net/uid-10914615-id-3023158.html 移动GIS中,通常将数据分为两大类:basemap layer和operatio ...

  2. fseek/ftell/rewind/fgetpos/fsetpos函数使用-linux

    程序: #include<stdio.h> int main(int argc,char *argv[]) { FILE * stream; fpos_t pos; stream = fo ...

  3. ORACLE 主要后台进程1

    Oracle Database Background Processes: 1.Database writer (DBWn)The database writer writes modified bl ...

  4. 常用Java Web 服务器

    Java Web应用程序需要部署在Java web服务器中运行,常用的Java Web服务器有Tomcat.GlassFish.WebLogic.JBoss.WebSphere.Jetty.JRun等 ...

  5. 关于offsetTop offsetHeight clientHeight scrollHeight scrollTop的区别研究

    我是以chrome浏览器做的研究. 先看一段代码: <script> window.addEventListener('DOMContentLoaded',function(){ var ...

  6. js实现超过长度的字符截取指定长度(中文字符算2个字符),超出部分以...显示

    //超过长度的字符截取指定长度,超出部分以...显示 function subString(str, len) { var newLength = 0; var newStr = "&quo ...

  7. MySql 环境配置

    关键词 MySQL 5.6.17  phpmyadmin   [下载MySQL]  MySQL Community Server MySQL社区版免费 注:需要oracle账户登录下载 [安装MySQ ...

  8. Handle 消息机制

    android中Handle类的主要作用: 1.在新启动的线程中发送给消息 2.在主线程获取.处理消息 为什么要用Handle这样的一个机制: 因为在Android系统中UI操作并不是线程安全的,如果 ...

  9. android studio SVN的搭建

    android studio 安装 SVN:http://www.it165.net/pro/html/201404/11412.html http://jingyan.baidu.com/album ...

  10. Composer加速

    在composer.json中添加{ "repositories": [ {"type": "composer", "url&qu ...