什么是 IP 隧道,Linux 怎么实现隧道通信?
本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫。
通过之前的文章,我们知道 tun 是一个网络层的设备,也被叫做点对点设备,之所以叫这个名字,是因为 tun 常常被用来做隧道通信(tunnel)。
IP 隧道
Linux 原生支持多种三层隧道,其底层实现原理都是基于 tun 设备。我们可以通过命令 ip tunnel help
查看 IP 隧道的相关操作。
[root@localhost ~]# ip tunnel help
Usage: ip tunnel { add | change | del | show | prl | 6rd } [ NAME ]
[ mode { ipip | gre | sit | isatap | vti } ] [ remote ADDR ] [ local ADDR ]
[ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]
[ prl-default ADDR ] [ prl-nodefault ADDR ] [ prl-delete ADDR ]
[ 6rd-prefix ADDR ] [ 6rd-relay_prefix ADDR ] [ 6rd-reset ]
[ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]
Where: NAME := STRING
ADDR := { IP_ADDRESS | any }
TOS := { STRING | 00..ff | inherit | inherit/STRING | inherit/00..ff }
TTL := { 1..255 | inherit }
KEY := { DOTTED_QUAD | NUMBER }
可以看到,Linux 原生一共支持 5 种 IP 隧道。
ipip
:即IPv4 in IPv4
,在 IPv4 报文的基础上再封装一个 IPv4 报文。gre
:即通用路由封装(Generic Routing Encapsulation
),定义了在任意一种网络层协议上封装其他任意一种网络层协议的机制,IPv4 和 IPv6 都适用。sit
:和ipip
类似,不同的是sit
是用 IPv4 报文封装 IPv6 报文,即IPv6 over IPv4
。isatap
:即站内自动隧道寻址协议(Intra-Site Automatic Tunnel Addressing Protocol
),和sit
类似,也是用于 IPv6 的隧道封装。vti
:即虚拟隧道接口(Virtual Tunnel Interface
),是 cisco 提出的一种IPsec
隧道技术。
实践 IPIP 隧道
我们下面以 ipip
作为例子,来实践下 Linux 的隧道通信。本文以前文的 Linux 路由机制作为基础,不清楚 Linux 路由的可以先翻看下那篇文章再来看。
实践之前,需要知道的是,ipip
需要内核模块 ipip.ko
的支持,通过 lsmod | grep ipip
查看内核是否加载,若没有则用 modprobe ipip
先加载,正常加载应该显示:
[root@by ~]# modprobe ipip
[root@by ~]# lsmod | grep ipip
ipip 13465 0
tunnel4 13252 1 ipip
ip_tunnel 25163 1 ipip
加载 ipip
模块后,就可以创建隧道了,方法是先创建一个 tun 设备,然后将该 tun 设备绑定为一个 ipip
隧道即可。
我们的实验拓扑如下:
首先参照路由那篇文章,保证 v1 和 v2 能够通信,这里就不再赘述了。
然后创建 tun 设备,并设置为 ipip
隧道。
# 1) 在 ns1 上创建 tun1 和 ipip tunnel
ip netns exec ns1 ip tunnel add tun1 mode ipip remote 10.10.20.2 local 10.10.10.2
ip netns exec ns1 ip l s tun1 up
ip netns exec ns1 ip a a 10.10.100.10 peer 10.10.200.10 dev tun1
上面的命令是在 NS1 上创建 tun 设备 tun1,并设置隧道模式为 ipip
,然后还需要设置隧道端点,用 remote
和 local
表示,这是 隧道外层 IP,对应的还有 隧道内层 IP,用 ip addr xx peer xx
配置。大体的示意图如下所示。
同理,我们也在 NS2 上做如上配置。
# 1) 在 ns2 上创建 tun2 和 ipip tunnel
ip netns exec ns2 ip tunnel add tun2 mode ipip remote 10.10.10.2 local 10.10.20.2
ip netns exec ns2 ip l s tun2 up
ip netns exec ns2 ip a a 10.10.200.10 peer 10.10.100.10 dev tun2
当做完上述配置,两个 tun 设备端点就可以互通了,如下:
[root@by ~]# ip netns exec ns1 ping 10.10.200.10 -c 4
PING 10.10.200.10 (10.10.200.10) 56(84) bytes of data.
64 bytes from 10.10.200.10: icmp_seq=1 ttl=64 time=0.090 ms
64 bytes from 10.10.200.10: icmp_seq=2 ttl=64 time=0.148 ms
64 bytes from 10.10.200.10: icmp_seq=3 ttl=64 time=0.112 ms
64 bytes from 10.10.200.10: icmp_seq=4 ttl=64 time=0.110 ms
--- 10.10.200.10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3000ms
rtt min/avg/max/mdev = 0.090/0.115/0.148/0.020 ms
我们试着来分析下上述这个过程。
1、首先 ping 命令构建一个 ICMP 请求包,ICMP 包封装在 IP 包中,源目的 IP 地址分别为 tun1(10.10.100.10)
和 tun2(10.10.200.10)
的地址。
2、由于 tun1 和 tun2 不在同一网段,所以会查路由表,当通过 ip tunnel
命令建立 ipip
隧道之后,会自动生成一条路由,如下,表明去往目的地 10.10.200.10
的路由直接从 tun1 出去。
[root@by ~]# ip netns exec ns1 route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
10.10.10.0 0.0.0.0 255.255.255.0 U 0 0 0 v1
10.10.20.0 10.10.10.1 255.255.255.0 UG 0 0 0 v1
10.10.200.10 0.0.0.0 255.255.255.255 UH 0 0 0 tun1
3、由于配置了隧道端点,数据包出了 tun1,到达 v1,根据 ipip
隧道的配置,会封装上一层新的 IP 报头,源目的 IP 地址分别为 v1(10.10.10.2)
和 v2(10.10.20.2)
。
4、v1 和 v2 同样不在一个网段,同样查路由表,发现去往 10.10.20.0
网段可以从 10.10.10.1
网关发出。
5、Linux 打开了 ip_forward
,相当于一台路由器,10.10.10.0
和 10.10.20.0
是两条直连路由,所以直接查表转发,从 NS1 过渡到 NS2。
6、数据包到达 NS2 的 v2,解封装数据包,发现内层 IP 报文的目的 IP 地址是 10.10.200.10
,这正是自己配置的 ipip
隧道的 tun2 地址,于是就将报文交给 tun2 设备。至此,tun1 的 ping 请求包就成功到达 tun2。
7、由于 ICMP 报文的传输特性,有去必有回,所以 NS2 上会构造 ICMP 响应包,并根据以上相同步骤封装和解封装数据包,直至到达 tun1,整个 ping 过程完成。
以上便是大体的 ipip
隧道通信过程,下面我们可以再抓包进一步验证。
如下是通过 wireshark 抓取的 v1 口的包:
可以看到,有两层 IP 报文头,外层使用的 ipip
协议构成隧道的端点,内层是正常的通信报文,封装了 ICMP 报文作为 payload。
总结
现在的 Linux 内核原生支持 5 种隧道协议,它们底层实现都是采用 tun 虚拟设备。
我们熟知的各种 VPN 软件,其底层实现都离不开这 5 种隧道协议。
我们可以把上面的 ipip
改成其他隧道模式,其他不变,同样可以完成不同隧道的实验。
我的公众号 「Linux云计算网络」(id: cloud_dev) ,号内有 10T 书籍和视频资源,后台回复 「1024」 即可领取,分享的内容包括但不限于 Linux、网络、云计算虚拟化、容器Docker、OpenStack、Kubernetes、工具、SDN、OVS、DPDK、Go、Python、C/C++编程技术等内容,欢迎大家关注。
什么是 IP 隧道,Linux 怎么实现隧道通信?的更多相关文章
- 使用隧道技术进行C&C通信
一.C&C通信 这里的C&C服务器指的是Command & Control Server--命令和控制服务器,说白了就是被控主机的遥控端.一般C&C节点分为两种,C&a ...
- 通过登入IP记录Linux所有用户登录所操作的日志
通过登入IP记录Linux所有用户登录所操作的日志 对于Linux用户操作记录一般通过命令history来查看历史记录,但是如果在由于误操作而删除了重要的数据的情况下,history命令就不会有什么作 ...
- 获取本机IP(适用于Linux系统)
获取本机IP(适用于Linux系统) /** * @desc 获取本机IP(适用于Linux系统) * @return Ip */ public static String getLocalIP() ...
- java获取本机器的IP(linux和windows)
目录 描述 方案描述 获取Windows下的IP 获取linux下的IP 判断操作系统的类型 最后将上面三个方法进行整合 参考 描述 由于项目是部署在集群上的,需要项目能够自动采集各机器的信息.jav ...
- labview中的移位寄存器、循环隧道,自动索引隧道的区别
对于循环结构(For 循环.while循环)而言,循环体内的数据域外部数据的传递是通过以下三种方式: 1.移位寄存器2.循环隧道3.自动索引隧道 第一.各自的区别.作用 循环隧道,就是把数据传入传出循 ...
- Linux下多任务间通信和同步-信号
Linux下多任务间通信和同步-信号 嵌入式开发交流群280352802,欢迎加入! 1.概述 信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式.信号可以直接进行用户空间进程和内核进程之间的 ...
- Android模拟器的ip获取以及模拟器之间socket通信
Android模拟器的ip获取以及模拟器之间socket通信 http://kalogen.iteye.com/blog/1565507 作者:李波 实现网络五子棋时用到了两个设备 ...
- Linux下多任务间通信和同步-概述
Linux下多任务间通信和同步-概述 嵌入式开发交流群280352802,欢迎加入! 在前面,我们学习了两种多任务的实现手段:进程和线程.由于进程是工作在独立的内存空间中,不同的进程间不能直接访问到对 ...
- Linux下多任务间通信和同步-消息队列
Linux下多任务间通信和同步-消息队列 嵌入式开发交流群280352802,欢迎加入! 简介 消息队列简称为队列.消息队列就是一些消息的列表.用户可以在消息队列中添加消息和读取消息等.从这点上看,消 ...
随机推荐
- spring boot 配置 fastjson 替代 Jackson (并解决返回字符串带双引号问题)
注:以我遇到的情况,只要发出的请求参数是map格式的,都会在前后多加一个双引号 以下代码有两个功能:1.FastJson 替换 Spring 自带的 Jackson 2.解决返回的字符串带双引号问题 ...
- pg 生成数据字典
select (select relname||'--'||(select description from pg_description where objoid=oid and objsubid= ...
- Win7共享文件夹简单?这个共享问题可以难倒90%的人
信息化社会,没有哪个公司不用电脑办公了.一个办公室里面的同事相互之间利用系统的共享功能,共享一些文件和软件已经是司空见惯的了,这个不需要多么复杂的操作.我们使用最多的windows7操作系统就能很方便 ...
- 【Linux】在Win10上搭建WSL(适用于Linux的Windows子系统)
1.打开WSL ,控制面板 -- 程序 -- 程序和功能 -- 打开或关闭Windows功能 - 选中[适用于Linux的Windows子系统] 2.开启后重启电脑 3.在Win10自带的Micro ...
- Scrapy 扩展中间件: 针对特定响应状态码,使用代理重新请求
0.参考 https://doc.scrapy.org/en/latest/topics/downloader-middleware.html#module-scrapy.downloadermidd ...
- Oracle-分析函数之sum(...) over(...)
概述 Oracle函数 很久之前更新了一篇Oracle的函数博文http://blog.csdn.net/yangshangwei/article/details/51425458,分析函数并没有包括 ...
- c/c++再学习:C++中public、protect、private的访问权限控制
C++中public.protect.private的访问权限控制 访问权限 一个类的public成员变量.成员函数,可以通过类的成员函数.类的实例变量进行访问 一个类的protected成员变量.成 ...
- JS ArrayBuffer和String
http://blog.mn886.net/chenjianhua/show/6b02fa4173ed/index.html ArrayBuffer和String 互相转换 function str2 ...
- python 配置文件__ConfigParser
基础读取配置文件 -read(filename) 直接读取文件内容 -sections() 得到所有的section,并以列表的形 ...
- docker使用教程
Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源. Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级.可移植的容器中,然后发布到任何流行的 Li ...