问题描写叙述与解决方式

还是老问题。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. 分享5个主流的HTML5开发工具

    HTML5被看做是web开发者创建流行web应用的利器,增加了对视频和Canvas 2D的支持.用HTML5的优点主要在于,这个技术可以进行跨平台的使用.比如你开发了一款HTML5的游戏,你可以很轻易 ...

  2. 文艺编程 Literate Programming

    (译注:这篇文章是 Donald Knuth 一篇1983年的论文:Literate Programming 的介绍部分.有人翻译为“字面编程”,是极其错误的说法,literate 根本就没有“字面” ...

  3. css案例学习之relative与absolute

    代码 <!DOCTYPE html PUBliC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.o ...

  4. CC++初学者编程教程(16) 搭建Xcode cocos2dx开发环境

    1.下载cocos2dx,也可以从共享目录复制 2.解压缩 3.进入目录 cd Desktop/cocos2d-x-2.2.0/tools/project-creator/ 4.创建项目 ./crea ...

  5. 一个简单java爬虫爬取网页中邮箱并保存

    此代码为一十分简单网络爬虫,仅供娱乐之用. java代码如下: package tool; import java.io.BufferedReader; import java.io.File; im ...

  6. 二探ListView

    使用draw9patch 打开内置terminal 输入CD C:\Users\Gaby\AppData\Local\Android\sdk 在该目录下输入draw9patch 导入图片,开始绘制 本 ...

  7. systemctl 命令完全指南

    http://www.linuxidc.com/Linux/2015-07/120833.htm Systemctl是一个systemd工具,主要负责控制systemd系统和服务管理器. System ...

  8. 第一次用shell脚本来自动运行带参程序

    将目录下的part-开头的文件带入程序处理 #!bin/sh FILES=/data5/follow_relation_list_part_00000/part-* file="" ...

  9. 面向对象继承实例(a如何继承b)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  10. Erp第二章:业务流程化、集成、规划

    1从全流程着眼,支持业务流程化优化,通过流程化优化提高工作效率和企业效益 2每个系统业务都相互依存.相互作用. 3.应用 程序(不用厂家)越多,信息集成难度越大 4信息集成.实时共享.实时企业 5信息 ...