Linux实现基于Loopback的NVI(NAT Virtual Interface)

Loopback实际上是个hole

但是如果它不是一个hole,它确实可以做一些事,类似Cisco的NVI那样。既然前期是“如果它不是一个hole”,那就需要对代码进行一些修改。在修改之前,你必须明白的是,Linux的loopback接口为什么是一个hole。

标准规定,所有试图经过loopback接口去往其它地方(非本机)的数据包要全部丢弃。Linux使用loop hole做到了这一点。Linux的限制loopback流量在本机范围的方式是,所有的loopback流量肯定经由本机发送,那么在ip_output的时候就会将其设置为loopback_dst,然后进行入IP接收例程的时候,它已经有关联的路由项了,进而就不会再去查询路由表,因此凡是进入ip_input逻辑的数据包都不是本机发出的,于是在其内部就可以做比较狠的判断的,凡是源地址是本机地址的,一律丢弃!这样本机发出的包就不会先经由loopback口然后去往外部,下面我们看一下外部进入的包是否能经由loopback口去往外部。答案无疑是否定的,看下面的流程:数据包从物理网卡进入->被路由到lo口->将loopback_dst这个路由项关联给数据包->loopback接口xmit数据包->模拟loopback接口接收数据包->进入ip_input路由判断->由于已经有了路由项故按照路由项转发。路由项的转发方式有两种,对于外部进入的数据包,将不断调用ip_forward,直到TTL变为0。因此只要进入了loopback,要么直接丢弃,要么疯狂loop,是绝对出不去的。

下面我就来说一下如何来破除这些约束。首先说一下本机发出的数据包如何先经由loopback再出去,然后说明外部进入的数据包如何先经由loopback再出去,最后说明,当做NAT的时候会碰到什么问题以及如何结合上述针对本机发包以及外部发包两种场景的措施来解决NAT问题。

1.本机发包经由loopback发出

修改代码是不必可少的了,因为我这是在破坏原则。幸运的是,代码只是修改一点点而已。修改的部分就是将这种“经由loopback发往别处”的包识别出来,然后删除其关联的路由项。这个用Netfilter在PREROUTING上做比较简单。另外就是将表示该本机地址的Local路由从Local表删除,然后作为unicast路由加入main表中,这样在做反向路由查询的时候,就不会匹配到Local表的路由了(Linux要求反向路由的类型必须是unicast的),到此即OK!

2.外部发包经由loopback转发

对于这种情况,只要是删除了数据包的loopback路由项关联,即可被顺利转发。因为数据包的源IP地址不可能是本机的IP,因此也就不可能是Local,如果数据流想原路返回的话,它就一定有反向的unicast路由。

3.NAT的问题

在配置了SNAT的情况下,要看SNAT成了什么地址,如果是SANT成了本机地址,那就面临上述第1节的问题,解决方法就是将该地址从Local表中删除,但是删除了之后会导致其它机器arp该地址的时候,本机不再回复,因此删除了之后还要显式arping一下该地址的arp更新;如果SNAT成了别的地址,就涉及到了反向可达性的问题,因为下一跳不一定知道该地址的可达性。

4.NAT问题的解决

NAT的问题仅仅是在SNAT成了别的地址时才会存在。这里又分为两种情况,第一种情况就是SNAT成了一个不相关的其它网段的地址,这样仅仅要求下一跳配置到该地址的路由就可以保证数据流的反向包能返回到此BOX,这个路由配置在简单环境下可以手工配置,复杂环境下可以用动态路由的方式进行SNAT地址的宣告;第二种情况就是SNAT的地址是和下一跳同一网段的情况,这会导致数据流反向包返回到下一跳的的时候,该SNAT的地址此时成了目标地址,由于处于同一网段,所以会被直接ARP,因此需要添加一条ARP转换规则:

arptables -t mangle -A OUTPUT -d 下一跳网关地址 -j mangle --mangle-ip-s SNAT成的地址

知道了问题所在以及解决方案,现在就可以动手了。本文的目标是实现一个类似Cisco NVI的东西,也就是一个虚拟网卡,在虚拟网卡的发送流程中实现NAT。鉴于有loopback这么好的现成的东西,我也就不再写虚拟网卡了,直接用loopback模拟一个也好。大体流程如下:

数据包从物理网卡进入->执行DNAT->路由到loopback->执行SNAT->loopback口发出->策略路由->物理网卡发出

可以看到,路由执行了两次,第一次是为了NAT,第二次是真正的路由。

除了使用loopback,编写一个类似veth的虚拟网卡是一个更不错的选择:

Veth stands for Virtual ETHernet. It is a simple tunnel driver that works at the link layer and looks like a pair of ethernet devices interconnected

with each other.

比loopback好的是,这基本可以不修改代码实现NVI,并且可以很容易取到数据包原始的进入接口。该驱动的逻辑非常简单,即一个pair中包含一个主接口和一个辅助接口,数据包从主接口进入被路由到该主接口的辅助接口,注意,不改变skb的接收接口,这个所谓的路由只是为了搞一次“从物理网卡接收到发送到某另一个网卡的动作”,此时PREROUTING/POSTROUTING都已经完成了,真正的路由之后就可以从另一个主接口发出了。

这次先不急着自己写虚拟网卡,先折腾完loopback再说,那么现在动手吧!

1.对代码的修改:

重新封装RAW表的NF_INET_PRE_ROUTING钩子函数,在ipt_hook的调用前调用下面的逻辑:

//判断有点太鲁莽,正常应该可以设计成一个匹配算法的

if (skb->dev->flags & IFF_LOOPBACK && skb->nfct) {

   skb->nfct = &nf_conntrack_untracked.ct_general;

   skb->nfctinfo = IP_CT_NEW;

   nf_conntrack_get(skb->nfct);

   skb_dst_drop(skb);

   return NF_ACCEPT;

}

这段代码的意思是说,如果是数据包从物理网卡进入,显然是需要匹配和应用规则(比如NAT)的,如果这件事做完了,数据也就是要通过路由导入loopback接口了,此时就不要再使用conntrack了,然而此时skb的nfct可能已经被设置了,于是将其NOTRACK,并且将skb的路由缓存丢弃。Linux的IP路由是这么对待loopback的,如果路由查询的结果出口是loopback接口,就是直接设置dst,loopback的xmit将数据包发出,调用一个netif_rx重新接收,到达ip_rcv_finish的时候,由于已经有了dst,就不必再查询路由了。但是如果这样的话,我们的第二次路由查询-实际上是策略路由的查询将无法实现,因此必须drop掉原有的dst。

时间: 2024-08-18 08:23:54

Linux实现基于Loopback的NVI(NAT Virtual Interface)的相关文章

linux下基于C语言的信号编程实例_C 语言

本文实例讲述了linux下基于C语言的信号编程方法.分享给大家供大家参考.具体如下: #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> void sig_handler(int sig_no, siginfo_t *info, void *ctext){ printf("receive si

为什么Linux安全 基于它的安卓不安全呢?

1. 确切的说安卓不是 Linux 大家都知道安卓是基于 Linux 内核,而且大家也知道 Linux 的安全性是公认的,那为什么和 Linux 有着类似嫡系关系的安卓却一直被人诟病不安全呢?要想说清楚这个问题,我们需要了解一下安卓和 Linux 到底是什么关系,而且这两个系统到底安全不安全,背后的原因是什么.当然,在此需要表明一个观点:世界上没有绝对安全的操作系统. 安卓基于 Linux 内核这句话,大家千万不要解读为安卓等同于 Linux.虽然安卓基于 Linux,但安卓与 Linux 之间

Linux下基于源码方式安装MySQL 5.6

    MySQL为开源数据库,因此可以基于源码实现安装.基于源码安装有更多的灵活性.也就是说我们可以针对自己的硬件平台选用合适的编译器来优化编译后的二进制代码,根据不同的软件平台环境调整相关的编译参数,选择自身需要选择不同的安装组件,设定需要的字符集等等一些可以根据特定应用场景所作的各种调整.本文描述了如何在源码方式下安装MySQL.   1.安装环境及介质#安装环境SZDB:~ # cat /etc/issueWelcome to SUSE Linux Enterprise Server 1

嵌入式Linux中基于framebuffer设备的jpeg格式在本地LCD屏显示

在基于Linux的视频监控采集系统中,摄像头采集到的一帧视频图像数据一般都是经过硬件自动压缩成jpeg格式的,然后再保存到摄像头设备的缓冲区.如果要把采集到的jpeg格式显示在本地LCD屏上,由于我们的Linux系统没有移植任何GUI系统,就要考虑以下方面: 1. 将jpeg格式解压缩为位图格式,也就是jpeg解码. 2. 将解码出来的位图格式输出到本地的LCD屏上. 在Linux系统下是通过写入帧缓冲(framebuffer)来实现的. 3. framebuffer相当于为LCD设备提供一个统

为什么Linux安全 基于它的安卓系统反而不安全

确切的说安卓不是Linux 大家都知道安卓是基于Linux内核,而且大家也知道Linux的安全性是公认的,那为什么和Linux有着类似嫡系关系的安卓却一直被人诟病不安全呢?要想说清楚这个问题,我们需要了解一下安卓和Linux到底是什么关系,而且这两个系统到底安全不安全,背后的原因是什么.当然,在此需要表明一个观点:世界上没有绝对安全的操作系统. 安卓基于Linux内核这句话,大家千万不要解读为安卓等同于Linux.虽然安卓基于Linux,但安卓与Linux之间还是有比较大的区别. 我们经常说的L

【TTS】传输表空间Linux -&gt;AIX 基于rman

[TTS]传输表空间Linux asm -> AIX asm 基于rman 一.1  BLOG文档结构图       一.2  前言部分   一.2.1  导读和注意事项 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你所不知道的知识,~O(∩_∩)O~: ① 异构平台下传输表空间的实施 ② 传输表空间基于表空间的read only和rman2种方式 ③ 平台字节序.自包含概念 ④ expdp/impdp的应用     Tips:        ① 若文章代码格式有错乱,推

Linux下基于DNS的多机均衡负载的实现

前二天为一个客户添加了一台服务器.共同承担一个论坛的运作.但是.要实现负载均衡.的确是件难事. 之前有试过几个方法, 一.DNS轮值..优点:简单实现.缺点:无法检测各个服务器的负荷.难控制访客的访问地址(一般DNS服务器都有缓存,所以.效果不明显) 二.前台程序..使用了一个类似Zeus Load Balancer的服务程序.在前台接受访问请求.再分流到各个实际的服务器. 但以上二个方法都不太适用我的情况.. 第一种.无法正确分配访客流量...常发生一台服务器的负荷很大.而另一台就很低...

smem – Linux 下基于进程和用户的内存占用报告

Linux 系统的内存管理工作中,内存使用情况的监控是十分重要的,在各种 Linux 发行版上你会找到许多这种工具.它们的工作方式多种多样,在这里,我们将会介绍如何安装和使用这样的一个名为 smem 的工具软件. Smem 是一款命令行下的内存使用情况报告工具,它能够给用户提供 Linux 系统下的内存使用的多种报告.和其它传统的内存报告工具不同的是,它有个独特的功能,可以报告 PSS(按比例占用大小Proportional Set Size),这种内存使用量表示方法对于那些在虚拟内存中的应用和

Linux下基于socket多线程并发通信的实现

pthread_server.c /*************************************************** * 文件名:pthread_server.c * 文件描述:创建子线程来接收客户端的数据 ***************************************************/ #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #in