<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Linux 虛擬網(wǎng)絡(luò)設(shè)備之 tun/tap

          共 6997字,需瀏覽 14分鐘

           ·

          2021-08-11 01:21

          在現(xiàn)在的云時(shí)代,到處都是虛擬機(jī)和容器,它們背后的網(wǎng)絡(luò)管理都離不開虛擬網(wǎng)絡(luò)設(shè)備,所以了解虛擬網(wǎng)絡(luò)設(shè)備有利于我們更好的理解云時(shí)代的網(wǎng)絡(luò)結(jié)構(gòu)。

          虛擬設(shè)備和物理設(shè)備的區(qū)別

          在Linux網(wǎng)絡(luò)數(shù)據(jù)包的接收過程和數(shù)據(jù)包的發(fā)送過程這兩篇文章中,介紹了數(shù)據(jù)包的收發(fā)流程,知道了Linux內(nèi)核中有一個(gè)網(wǎng)絡(luò)設(shè)備管理層,處于網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)和協(xié)議棧之間,負(fù)責(zé)銜接它們之間的數(shù)據(jù)交互。驅(qū)動(dòng)不需要了解協(xié)議棧的細(xì)節(jié),協(xié)議棧也不需要了解設(shè)備驅(qū)動(dòng)的細(xì)節(jié)。

          對(duì)于一個(gè)網(wǎng)絡(luò)設(shè)備來說,就像一個(gè)管道(pipe)一樣,有兩端,從其中任意一端收到的數(shù)據(jù)將從另一端發(fā)送出去。

          比如一個(gè)物理網(wǎng)卡eth0,它的兩端分別是內(nèi)核協(xié)議棧(通過內(nèi)核網(wǎng)絡(luò)設(shè)備管理模塊間接的通信)和外面的物理網(wǎng)絡(luò),從物理網(wǎng)絡(luò)收到的數(shù)據(jù),會(huì)轉(zhuǎn)發(fā)給內(nèi)核協(xié)議棧,而應(yīng)用程序從協(xié)議棧發(fā)過來的數(shù)據(jù)將會(huì)通過物理網(wǎng)絡(luò)發(fā)送出去。

          那么對(duì)于一個(gè)虛擬網(wǎng)絡(luò)設(shè)備呢?首先它也歸內(nèi)核的網(wǎng)絡(luò)設(shè)備管理子系統(tǒng)管理,對(duì)于Linux內(nèi)核網(wǎng)絡(luò)設(shè)備管理模塊來說,虛擬設(shè)備和物理設(shè)備沒有區(qū)別,都是網(wǎng)絡(luò)設(shè)備,都能配置IP,從網(wǎng)絡(luò)設(shè)備來的數(shù)據(jù),都會(huì)轉(zhuǎn)發(fā)給協(xié)議棧,協(xié)議棧過來的數(shù)據(jù),也會(huì)交由網(wǎng)絡(luò)設(shè)備發(fā)送出去,至于是怎么發(fā)送出去的,發(fā)到哪里去,那是設(shè)備驅(qū)動(dòng)的事情,跟Linux內(nèi)核就沒關(guān)系了,所以說虛擬網(wǎng)絡(luò)設(shè)備的一端也是協(xié)議棧,而另一端是什么取決于虛擬網(wǎng)絡(luò)設(shè)備的驅(qū)動(dòng)實(shí)現(xiàn)。

          tun/tap的另一端是什么?

          先看圖再說話:

          +----------------------------------------------------------------+
          | |
          | +--------------------+ +--------------------+ |
          | | User Application A | | User Application B |<-----+ |
          | +--------------------+ +--------------------+ | |
          | | 1 | 5 | |
          |...............|......................|...................|.....|
          | ↓ ↓ | |
          | +----------+ +----------+ | |
          | | socket A | | socket B | | |
          | +----------+ +----------+ | |
          | | 2 | 6 | |
          |.................|.................|......................|.....|
          | ↓ ↓ | |
          | +------------------------+ 4 | |
          | | Newwork Protocol Stack | | |
          | +------------------------+ | |
          | | 7 | 3 | |
          |................|...................|.....................|.....|
          | ↓ ↓ | |
          | +----------------+ +----------------+ | |
          | | eth0 | | tun0 | | |
          | +----------------+ +----------------+ | |
          | 10.32.0.11 | | 192.168.3.11 | |
          | | 8 +---------------------+ |
          | | |
          +----------------|-----------------------------------------------+

          Physical Network

          上圖中有兩個(gè)應(yīng)用程序A和B,都在用戶層,而其它的socket、協(xié)議棧(Newwork Protocol Stack)和網(wǎng)絡(luò)設(shè)備(eth0和tun0)部分都在內(nèi)核層,其實(shí)socket是協(xié)議棧的一部分,這里分開來的目的是為了看的更直觀。

          tun0是一個(gè)Tun/Tap虛擬設(shè)備,從上圖中可以看出它和物理設(shè)備eth0的差別,它們的一端雖然都連著協(xié)議棧,但另一端不一樣,eth0的另一端是物理網(wǎng)絡(luò),這個(gè)物理網(wǎng)絡(luò)可能就是一個(gè)交換機(jī),而tun0的另一端是一個(gè)用戶層的程序,協(xié)議棧發(fā)給tun0的數(shù)據(jù)包能被這個(gè)應(yīng)用程序讀取到,并且應(yīng)用程序能直接向tun0寫數(shù)據(jù)。

          這里假設(shè)eth0配置的IP是10.32.0.11,而tun0配置的IP是192.168.3.11.

          這里列舉的是一個(gè)典型的tun/tap設(shè)備的應(yīng)用場景,發(fā)到192.168.3.0/24網(wǎng)絡(luò)的數(shù)據(jù)通過程序B這個(gè)隧道,利用10.32.0.11發(fā)到遠(yuǎn)端網(wǎng)絡(luò)的10.33.0.1,再由10.33.0.1轉(zhuǎn)發(fā)給相應(yīng)的設(shè)備,從而實(shí)現(xiàn)VPN。

          下面來看看數(shù)據(jù)包的流程:

          1. 應(yīng)用程序A是一個(gè)普通的程序,通過socket A發(fā)送了一個(gè)數(shù)據(jù)包,假設(shè)這個(gè)數(shù)據(jù)包的目的IP地址是192.168.3.1
          2. socket將這個(gè)數(shù)據(jù)包丟給協(xié)議棧
          3. 協(xié)議棧根據(jù)數(shù)據(jù)包的目的IP地址,匹配本地路由規(guī)則,知道這個(gè)數(shù)據(jù)包應(yīng)該由tun0出去,于是將數(shù)據(jù)包交給tun0
          4. tun0收到數(shù)據(jù)包之后,發(fā)現(xiàn)另一端被進(jìn)程B打開了,于是將數(shù)據(jù)包丟給了進(jìn)程B
          5. 進(jìn)程B收到數(shù)據(jù)包之后,做一些跟業(yè)務(wù)相關(guān)的處理,然后構(gòu)造一個(gè)新的數(shù)據(jù)包,將原來的數(shù)據(jù)包嵌入在新的數(shù)據(jù)包中,最后通過socket B將數(shù)據(jù)包轉(zhuǎn)發(fā)出去,這時(shí)候新數(shù)據(jù)包的源地址變成了eth0的地址,而目的IP地址變成了一個(gè)其它的地址,比如是10.33.0.1.
          6. socket B將數(shù)據(jù)包丟給協(xié)議棧
          7. 協(xié)議棧根據(jù)本地路由,發(fā)現(xiàn)這個(gè)數(shù)據(jù)包應(yīng)該要通過eth0發(fā)送出去,于是將數(shù)據(jù)包交給eth0
          8. eth0通過物理網(wǎng)絡(luò)將數(shù)據(jù)包發(fā)送出去

          10.33.0.1收到數(shù)據(jù)包之后,會(huì)打開數(shù)據(jù)包,讀取里面的原始數(shù)據(jù)包,并轉(zhuǎn)發(fā)給本地的192.168.3.1,然后等收到192.168.3.1的應(yīng)答后,再構(gòu)造新的應(yīng)答包,并將原始應(yīng)答包封裝在里面,再由原路徑返回給應(yīng)用程序B,應(yīng)用程序B取出里面的原始應(yīng)答包,最后返回給應(yīng)用程序A

          這里不討論Tun/Tap設(shè)備tun0是怎么和用戶層的進(jìn)程B進(jìn)行通信的,對(duì)于Linux內(nèi)核來說,有很多種辦法來讓內(nèi)核空間和用戶空間的進(jìn)程交換數(shù)據(jù)。

          從上面的流程中可以看出,數(shù)據(jù)包選擇走哪個(gè)網(wǎng)絡(luò)設(shè)備完全由路由表控制,所以如果我們想讓某些網(wǎng)絡(luò)流量走應(yīng)用程序B的轉(zhuǎn)發(fā)流程,就需要配置路由表讓這部分?jǐn)?shù)據(jù)走tun0。

          tun/tap設(shè)備有什么用?

          從上面介紹過的流程可以看出來,tun/tap設(shè)備的用處是將協(xié)議棧中的部分?jǐn)?shù)據(jù)包轉(zhuǎn)發(fā)給用戶空間的應(yīng)用程序,給用戶空間的程序一個(gè)處理數(shù)據(jù)包的機(jī)會(huì)。于是比較常用的數(shù)據(jù)壓縮,加密等功能就可以在應(yīng)用程序B里面做進(jìn)去,tun/tap設(shè)備最常用的場景是VPN,包括tunnel以及應(yīng)用層的IPSec等,比較有名的項(xiàng)目是VTun,有興趣可以去了解一下。

          tun和tap的區(qū)別

          用戶層程序通過tun設(shè)備只能讀寫IP數(shù)據(jù)包,而通過tap設(shè)備能讀寫鏈路層數(shù)據(jù)包,類似于普通socket和raw socket的差別一樣,處理數(shù)據(jù)包的格式不一樣。

          示例

          示例程序

          這里寫了一個(gè)程序,它收到tun設(shè)備的數(shù)據(jù)包之后,只打印出收到了多少字節(jié)的數(shù)據(jù)包,其它的什么都不做,如何編程請(qǐng)參考后面的參考鏈接。

          #include <net/if.h>
          #include <sys/ioctl.h>
          #include <sys/stat.h>
          #include <fcntl.h>
          #include <string.h>
          #include <sys/types.h>
          #include <linux/if_tun.h>
          #include<stdlib.h>
          #include<stdio.h>

          int tun_alloc(int flags)
          {

          struct ifreq ifr;
          int fd, err;
          char *clonedev = "/dev/net/tun";

          if ((fd = open(clonedev, O_RDWR)) < 0) {
          return fd;
          }

          memset(&ifr, 0, sizeof(ifr));
          ifr.ifr_flags = flags;

          if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0) {
          close(fd);
          return err;
          }

          printf("Open tun/tap device: %s for reading...\n", ifr.ifr_name);

          return fd;
          }

          int main()
          {

          int tun_fd, nread;
          char buffer[1500];

          /* Flags: IFF_TUN - TUN device (no Ethernet headers)
          * IFF_TAP - TAP device
          * IFF_NO_PI - Do not provide packet information
          */
          tun_fd = tun_alloc(IFF_TUN | IFF_NO_PI);

          if (tun_fd < 0) {
          perror("Allocating interface");
          exit(1);
          }

          while (1) {
          nread = read(tun_fd, buffer, sizeof(buffer));
          if (nread < 0) {
          perror("Reading from interface");
          close(tun_fd);
          exit(1);
          }

          printf("Read %d bytes from tun/tap device\n", nread);
          }
          return 0;
          }

          演示

          #--------------------------第一個(gè)shell窗口----------------------
          #將上面的程序保存成tun.c,然后編譯
          dev@debian:~$ gcc tun.c -o tun

          #啟動(dòng)tun程序,程序會(huì)創(chuàng)建一個(gè)新的tun設(shè)備,
          #程序會(huì)阻塞在這里,等著數(shù)據(jù)包過來
          dev@debian:~$ sudo ./tun
          Open tun/tap device tun1 for reading...
          Read 84 bytes from tun/tap device
          Read 84 bytes from tun/tap device
          Read 84 bytes from tun/tap device
          Read 84 bytes from tun/tap device

          #--------------------------第二個(gè)shell窗口----------------------
          #啟動(dòng)抓包程序,抓經(jīng)過tun1的包
          # tcpdump -i tun1
          tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
          listening on tun1, link-type RAW (Raw IP), capture size 262144 bytes
          19:57:13.473101 IP 192.168.3.11 > 192.168.3.12: ICMP echo request, id 24028, seq 1, length 64
          19:57:14.480362 IP 192.168.3.11 > 192.168.3.12: ICMP echo request, id 24028, seq 2, length 64
          19:57:15.488246 IP 192.168.3.11 > 192.168.3.12: ICMP echo request, id 24028, seq 3, length 64
          19:57:16.496241 IP 192.168.3.11 > 192.168.3.12: ICMP echo request, id 24028, seq 4, length 64

          #--------------------------第三個(gè)shell窗口----------------------
          #./tun啟動(dòng)之后,通過ip link命令就會(huì)發(fā)現(xiàn)系統(tǒng)多了一個(gè)tun設(shè)備,
          #在我的測試環(huán)境中,多出來的設(shè)備名稱叫tun1,在你的環(huán)境中可能叫tun0
          #新的設(shè)備沒有ip,我們先給tun1配上IP地址
          dev@debian:~$ sudo ip addr add 192.168.3.11/24 dev tun1

          #默認(rèn)情況下,tun1沒有起來,用下面的命令將tun1啟動(dòng)起來
          dev@debian:~$ sudo ip link set tun1 up

          #嘗試ping一下192.168.3.0/24網(wǎng)段的IP,
          #根據(jù)默認(rèn)路由,該數(shù)據(jù)包會(huì)走tun1設(shè)備,
          #由于我們的程序中收到數(shù)據(jù)包后,啥都沒干,相當(dāng)于把數(shù)據(jù)包丟棄了,
          #所以這里的ping根本收不到返回包,
          #但在前兩個(gè)窗口中可以看到這里發(fā)出去的四個(gè)icmp echo請(qǐng)求包,
          #說明數(shù)據(jù)包正確的發(fā)送到了應(yīng)用程序里面,只是應(yīng)用程序沒有處理該包
          dev@debian:~$ ping -c 4 192.168.3.12
          PING 192.168.3.12 (192.168.3.12) 56(84) bytes of data.

          --- 192.168.3.12 ping statistics ---
          4 packets transmitted, 0 received, 100% packet loss, time 3023ms

          結(jié)束語

          平時(shí)我們用到tun/tap設(shè)備的機(jī)會(huì)不多,不過由于其結(jié)構(gòu)比較簡單,拿它來了解一下虛擬網(wǎng)絡(luò)設(shè)備還不錯(cuò),為后續(xù)理解Linux下更復(fù)雜的虛擬網(wǎng)絡(luò)設(shè)備(比如網(wǎng)橋)做個(gè)鋪墊。

          原文鏈接:https://segmentfault.com/a/1190000009249039


          瀏覽 72
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美最大操逼网站在线 | 91aaa国产 | 久久高凊无码免费一区 | 日韩精品无码一级 | 日韩丝袜足交 |