实现一个做双向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全然能够通过一个函数实现,此处是为了使接口更加清晰。详细的转换就不多说了,非常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的虚拟网卡的更多相关文章
- tunctl和虚拟网卡
1 tun/tap驱动 1.1 网卡驱动 同tcp/ip协议栈打交道,接受和发送数据包 1.2 字符驱动 内核和用户态通过字符设备交换数据包. 2 tun虚拟网卡的创建 tunctl -t tun0 ...
- 转【Ubuntu】添加虚拟网卡的三种方式
原文:https://blog.csdn.net/White_Idiot/article/details/82934338 ------------------------------ 1. ifco ...
- Linux添加虚拟网卡的多种方法
Linux添加虚拟网卡的多种方法有时候,一台服务器需要设置多个ip,但又不想添加多块网卡,那就需要设置虚拟网卡.这里介绍几种方式在linux服务器上添加虚拟网卡. 我们向eth0中添加一块虚拟网卡: ...
- 【转】Linux添加虚拟网卡
转自:https://blog.csdn.net/hzhsan/article/details/44677867 有时候,一台服务器需要设置多个ip,但又不想添加多块网卡,那就需要设置虚拟网卡.这里介 ...
- CentOS设置虚拟网卡做NAT方式和Bridge方式桥接
CentOS设置虚拟网卡做NAT方式和Bridge方式桥接 http://www.centoscn.com/CentOS/config/2015/0225/4736.html 摘要:KVM虚拟机网络配 ...
- 通过桥接虚拟网卡使VMWare和宿主机实现双向通讯
0.为什么选择虚拟网卡和桥接模式 首先虚拟机网络设置为NAT,虚拟机实现上网是很方便的,但是宿主机访问虚拟机就比较麻烦了(需要单独配置端口转发),桥接就能很好的解决这个问题,桥接模式会把虚拟机当做宿主 ...
- 【强烈推荐】利用NAT、Host-Only双虚拟网卡,实现Virtual Box中CentOS6.3联网
问题背景: 先前都是在Virtual Box中以“网络共享”方式,让里面的Linux虚拟机Host-Only方式联网,参考如下: Virtual Box下配置Host-Only联网方式详解 但最近被公 ...
- 【荐】利用NAT、Host-Only双虚拟网卡,实现Virtual Box中CentOS5.x联网
一.虚拟机与主机互联,通常有三种方式,详细介绍请看: VMware虚拟机三种网络模式(Bridged,Nat,Host-only)区别详解 二.通过网络共享,Host-Only联网,详细案例请看: W ...
- 虚拟机Linux与本地虚拟网卡配置---NAT链接方式
虚拟机Linux与本地虚拟网卡配置---NAT链接方式 **********这是我亲自尝试多次实践出来的结果,不是复制粘贴************************* 首先进行初始化,这样避免有 ...
随机推荐
- poj 1940 Wine Trading in Gergovia_贪心
在一条街上有许多房屋,每间屋子里都住着人,并且都是做葡萄酒生意的商人,他们每天都要决定买卖多少瓶葡萄酒.有趣的地方是,供需总是完美地一致.商人总是能买到自己需要的葡萄酒,并且,他们从来不介意是从哪个商 ...
- 能上QQ无法打开网页
能上QQ无法上网电脑故障解决方法 Winsock协议配置故障解决方法 第1步 :单击开始菜单中的运行,并在打开的运行窗口中键入“cmd”并回车确定,打死命令提示符窗口. 第2步 :在打开的命令提示符窗 ...
- nodejs面试
1. PM2相关 1. PM2的主要功能?*答案:在Node.js进程挂掉以后自动重启进程,并且能够方便的实现Node.js的集群模式* 2. 如何查看当前是否适合重启服务?*答案:pm2 monit ...
- Cocoapod安装使用
近期换工作.项目中推荐使用Cocoapod,把经常使用的第三方库增加到project中,大致说一下流程,标记一下. 第一步:安装 CocoaPods是跑在Ruby的软件,安装可能须要几分钟.打开你的终 ...
- 月赛-Crackhash
Crackhash 这个题目是我为月赛出的,完全仿照自mma 1st simple_hash. 这道题目比较有意思的地方在于在32位的程序中模拟了64位的算术运算. 题目的思路很清晰.要求输入全为数字 ...
- SVG-1
<rect>矩形 <circle>圆 <ellipse>椭圆 <line>直线 <polyline>折线 <polygon>标签 ...
- Type.GetType()在跨程序集反射时返回null的解决方法
在开发中,经常会遇到这种情况,在程序集A.dll中需要反射程序集B.dll中的类型.如果使用稍有不慎,就会产生运行时错误.例如使用Type.GetType("BNameSpace.Class ...
- SQL Server 2012 Enterprise Edition安装过程详解(包含每一步设置的含义)
一.启动安装程序,点击“安装”选项卡,选择“全新SQL Server独立安装或向现有安装添加功能”.(首次安装数据库系统或向现有数据库系统添加功能,均选择此选项) 二.随后,安装程序进行“安装程序支持 ...
- 利用Oracle数据库的UTL_SMTP发送HTML 邮件
Ok, that looks hard, but if you use this procedure I wrote, its really quite easy, it does all of th ...
- 一个Socket连接管理池(心跳机制)
一个Socket连接管理池(心跳机制) http://cuisuqiang.iteye.com/blog/1489661