什么是 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,然后還需要設置隧道端點,用 remotelocal 表示,這是 隧道外層 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.010.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++編程技術等內容,歡迎大家關注。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM