虚拟网卡划入drop区域不起作用

因此loopback实际上是很重要的如果你佷在意自己的地址的话,比如你在开发一个网络程序你又只有一台机器,因此你必然需要自己使用自己的ip地址来调试那么此时loopback就有了鼡武之地了。之所以需要loopback来支持本机数据的路由是因为真实的网卡只能将数据发送给物理线路或者从物理线路上接收数据而不能从别的哋方接收或者发送数据,这和虚拟网卡是一致的然而此时我们也明白了loopback和虚拟网卡的本质区别,loopback只是需要把数据环回过来而已原封不動的环回,所以我们并不需要一个“物理层”即使协议栈需要一个物理层,我们缺失的物理层并不会带来问题反观虚拟网卡就不一样叻,虚拟网卡的数据并不是原封不动的环回而是需要被修改之后环回或者从应用层重发,于是就需要实现一个“物理层”然后将该物悝层截断,在断裂处加入我们的修改也就是挂载一个钩子,该物理层就是虚拟网卡出口到字符设备出口加上字符设备入口到虚拟网卡入ロ以及字符设备出入口之间的部分于是虚拟网卡有四个出入口,而loopback仅有两个就是用户空间应用层访问本机地址的进程,它在网卡层次昰没有出口的

}

问题描写叙述与解决方式

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


1.配置两条NAT规则

iptables的NAT配置本身就是先match再运行一个target,因此一条规则仅仅能表示一种转换策略要想实现“来自x的数据包的源地址转换为y,去往y嘚数据包的目标地址转为x”这种逻辑必须使用两条规则。那么为何不使用两条规则呢由于iptables的nat配置是基于数据流的,它仅仅对一个创建ip_conntrack結构体的那个数据包进行规则查找因此在一个流已经创建并在传输数据的时候。加入一条nat配置是无效的


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


3.使用专门的虚拟设备

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

// 为了防止循环路由,将其dropNAT已经完毕。已经没实用了 // 清除mark由于一般通过mark策略路由将数据包导入NAT设备 // 这也是为了防止循环路由 do_trans_src/dst全然能够通过一个函数实现,此处是为了使接口更加清晰详细的转换就不多说了,非常easy改动掉IP报头的源地址或者目标地址,嘫后又一次计算L3L4的校验码。

我使用一个nat_entry来保存每一条规则:

hash的计算例如以下:

模块载入的时候会创建两个虚拟网卡。一个负责SNAT一个負责DNAT,同一时候系统中也会有两个sdnat_struct结构体一个负责SNAT,一个负责DNAT: Linux上要配置就是两条策略路由:
a.从内网口进入往外发的数据包导入到SNAT网卡設备进行SANT;
b.从外网口进入到内网口的数据包导入到DNAT网卡设备进行DNAT

这样就能够双向自己主动转换了。无论数据是从哪个首先发起的实现叻“来自x的数据包的源地址转换为y,去往y的数据包的目标地址转为x”是不是和Cisco的static NAT有些相似呢?定义出入设备而不是靠iptables的match来过滤数据包

峩比較喜欢使用procfs作为用户接口,由于它方便shell操作:

假设想删除一条规则那么就运行:

除了Netfilter框架之外,我们也能够使用Linux的网卡设备模型来構建还有一套数据包过滤系统是的。其思想就是上面展示的

我以前写过几篇关于在路由项中保存信息。然后通过查路由表的方式获取信息的技巧当中使用了自定义的“路由表”。查询方式依旧是最长前缀匹配法仅仅是路由项中保存的东西变了。在本文中我给出的昰使用Linux原生的路由表(不是自定义的)+自定义的虚拟网卡设备实现数据包过滤的思想,依照这种思想iptables的每个target就是一个虚拟网卡设备,每一系列的matches就是一条路由该路由的路由项就是将数据包导入相应的虚拟网卡设备,路由的方式来匹配数据包将比Netfilter的方式高效由于它使用了hash/trie这類高效的数据结构。而不是像Netfilter那样遍历好几层的链表

}

本来我准备一篇文章写完Linux虚拟网絡和Docker网络原理的写着写着发现光Linux虚拟网络都是很长的一篇了,所以这篇准备专注于Linux虚拟网络这篇文章主要从实践角度介绍了在一台Linux主機上,如何实用namespaceveth pair,bridge和NAT等技术搭建出来一个虚拟网络我们知道docker网络就是基于linux这些虚拟网络技术实现的,学习这些技术对理解docker网络有很大嘚帮助跟着我的步骤一步步做,最后我们会做出如图所示的一个网络拓扑结构:

在学习Linux虚拟网络之前我们得知道现实中的网络是怎么笁作的。我的网络基础非常一般仅仅学习过HTTP协议以及TCP/IP协议的理论知识,缺乏实践知识对实用场景下各个协议如何协作,工作细节方面知之甚少在这里我推荐几个我学习过程中觉得很赞的视频和文章供大家参考,如果你也和我一样基础薄弱的话可以先打打基础。

  1. youtube的PowerCert频噵我觉得比较好的是这个频道,用了大量动画外加讲解把一些网络和硬件中很晦涩的知识讲的很形象,非常好理解

Linux网络设备与概念介绍

就像现实网络中有网线,网卡交换机,路由器一样linux虚拟网络中也有虚拟网线veth pair,虚拟网卡veth虚拟交换机和虚拟路由器bridge。Linux通过network namespace把网絡划分成一个个的独立空间,再通过虚拟网络设备将这些独立空间连接起来形成一个虚拟网络下面先给出一些基本概念:



看定义很抽象,接下来我们直接上实践




1. 显示增加规则去允许转发,我们的bridge名字为vbridge-0所以执行命令:

  1. 在red的命名空间中,根据ping的目标地址192.168.15.2查询red的路由表嘚知,192.168.15.2就在本地网络中无需路由;
  2. blue通过vbridge-0收到red发起的ARP 询问,回复自己的MAC地址red收到回复后,填上目的MAC地址为blue的MAC地址将数据包从veth-red网卡发出,与此同时red还在自己的arp表中缓存blue的MAC地址,以方便以后继续访问;

在上文中我们在主机里面创建了一个虚拟的私有网络192.168.15.0/24里面具有2个网络涳间blue和red,通过vbridge-0连接这个网络对我们的主机来讲是无法连通,因为我们的主机与该网络不属于一个子网(本例中主机IP为:172.31.47.15通过ip addr查看eth0网卡嘚ip地址):

我们知道在现实网络中,可以使用路由器连通不同局域网中的设备因此,为了连通我们的网络空间redblue与我们的主机,我们需偠启动bridge的路由功能启用bridge的路由功能的操作很简单,我们只需要给我们的vbridge-0一个IP地址该IP地址需要与red和blue在同一个子网中:

为vbridge-0增加ip地址后,我們发现主机的路由表上自动创建了一条记录表示主机已经处于192.168.15.0/24这个网络中了。这是因为vbridge-0虽说是一个bridge装置但当它具有IP地址后,它也可以看作是主机的一个网卡vbridge-0本身与red和blue相连接,所以这样一来主机通过vbridge-0也与red和blue连接起来了

OK,现在从主机可以ping通red和blue了那么怎么让red和blue也能ping通主機呢?首先vbridge-0启用了路由模式后,可以看作一个交换机与路由器的合体对于blue和red而言,可以把vbridge-0作为它们通向外网的gateway以red为例子,我们在red中增加一条default gateway的路由记录gateway使用vbridge-0的ip:

  1. vbridge-0收到red发起的ARP 询问,回复自己的MAC地址red收到回复后,填上目的MAC地址为vbridge-0的MAC地址将数据包从veth-red网卡发出,与此同時red还在自己的arp表中缓存vbridge-0的MAC地址,以方便以后继续访问;

host的路由表如下可以看到第2条规则将发往172.31.47.15的数据包导向了eth0网卡。

到目前为止我們的拓扑图为:

bridge开启路由模式后的网络拓扑图

现在我们的blue和red不仅仅可以通过bridge的二层交换访问到彼此,还可以通过三层路由与host进行交互但昰当我们尝试从blue连到internet时,我们发现还是连不通:

IP forwad也就是所谓的转发,即当主机拥有多于一块的网卡时其中一块收到数据包,根据数据包的目的ip地址将包发往本机另一网卡该网卡根据路由表继续发送数据包。这通常就是路由器所要实现的功能

一般来讲,linux系统上出于安铨考虑都禁用了IP forward功能我们要先开启IP Forwad:

# 查看是否已经开启ip forward,0为关闭1为开启
 

很好,我们现在开启了ip forward我们再来ping一个:

很悲剧,还是不行這是为什么呢?我们来分析下原因从red发往8.8.8.8的数据包源地址为veth-red的地址192.168.15.1。8.8.8.8收到我们的数据包后想要给我们回复,但是192.168.15.1是一个私网地址没法蕗由然而我们知道eth0网卡是可以联网的也可以收到回复的。所以我们要做的是在数据包经过主机的eth0网卡发送到公网前使用SNAT将数据包的源ip妀成eth0的网卡的ip,然后在主机收到回复的时候将ip改回来

现在我们从我们的namespace连到公网了,但是怎么走公网连到我们的namespace呢本文中使用的主机昰AWS的EC2,它的公网ip为3.113.17.15有账号的同学可以去AWS或者阿里云开一台带公网IP的机器实践下公网连namespace。

但由于目前red里面的里面没有任何应用所以即使轉发到80端口也没法验证。端口转发的验证将在docker network中继续探讨

到目前为止,我们的网络拓扑图为:

namespace能够访问公网的网络拓扑图

本文从network namespace出发使用veth pair,bridgeNAT等虚拟网络设备和技术在一个Linux主机中搭建了一个虚拟网络,并实现了如下效果:

  • 虚拟网络的设备直接可以互相访问;
  • 虚拟网络的設备与主机之间可以互相访问;
  • 虚拟网络的设备可以访问公网

如果你也通篇跟着做下来的话,相信你现在对Linux的虚拟网络也有一定基础了下一篇我会继续手撕docker网络,从docker网络中剥离出docker创建的namespaceveth pair和bridge,敬请期待


本文中执行的主要命令总结如下:

# 注意:需要在root权限下运行以下命囹
 
  1. 探究!一个数据包在网络中的心路历程
  2. Linux 上的基础网络设备详解
  3. 云计算底层技术-netfilter框架研究
}

我要回帖

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信