Linux虛擬網絡技術

近幾年,Docker、Kubernetes等容器化技術和容器編排工具的興起使技術人員從應用部署和維護的泥淖中解脫出來,同時也改變了很多很多互聯網公司的技術架構。筆者近期也在學習Docker和Kubernetes,對這些新技術所帶來的便捷性和安全性非常著迷,其中尤其對容器化技術的網絡實現方式更為好奇。今天就把近期的對Linux虛擬網絡技術的學習成果分享出來,希望能和大家一起交流學習。

Network Namespace

Network Namespace 是 Linux 內核提供的功能,是實現網絡虛擬化的重要功能,它能創建多個隔離的網絡空間,它們有獨自網絡棧信息。不管是虛擬機還是容器,運行的時候仿佛自己都在獨立的網絡中。而且不同Network Namespace的資源相互不可見,彼此之間無法通信。如下圖所示:

Network Namespace

ip netns命令

可以借助ip netns命令來完成對 Network Namespace 的各種操作。ip netns命令來自于iproute2安裝包,一般系統會默認安裝,如果沒有的話,讀者自行安裝。

注意:ip netns命令修改網絡配置時需要 sudo 權限。

可以通過ip netns命令完成對Network Namespace 的相關操作,可以通過ip netns help查看命令幫助信息:

$ ip netns help
Usage: ip netns list
       ip netns add NAME
       ip netns set NAME NETNSID
       ip [-all] netns delete [NAME]
       ip netns identify [PID]
       ip netns pids NAME
       ip [-all] netns exec [NAME] cmd ...
       ip netns monitor
       ip netns list-id

默認情況下,Linux系統中是沒有任何 Network Namespace的,所以ip netns list命令不會返回任何信息。

創建Network Namespace

下面,我們通過命令創建一個名為ns0的命名空間:

$ ip netns add ns0
$ ip netns list
ns0

新創建的 Network Namespace 會出現在/var/run/netns/目錄下。如果相同名字的 namespace 已經存在,命令會報Cannot create namespace file "/var/run/netns/ns0": File exists的錯誤。

對于每個 Network Namespace 來說,它會有自己獨立的網卡、路由表、ARP 表、iptables 等和網絡相關的資源。

操作Network Namespace

ip命令提供了ip netns exec子命令可以在對應的 Network Namespace 中執行命令。

  1. 查看新創建 Network Namespace 的網卡信息
$ ip netns exec ns0 ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

可以看到,新創建的Network Namespace中會默認創建一個lo回環網卡,此時網卡處于關閉狀態。此時,嘗試去 ping 該lo回環網卡,會提示Network is unreachable

$ ip netns exec ns0 ping 127.0.0.1
connect: Network is unreachable

通過下面的命令啟用lo回環網卡:

ip netns exec ns0 ip link set lo up

然后再次嘗試去 ping 該lo回環網卡:

$ ip netns exec ns0 ping -c 3 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.048 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.031 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.029 ms

--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.029/0.036/0.048/0.008 ms

轉移設備

我們可以在不同的 Network Namespace 之間轉移設備(如veth)。由于一個設備只能屬于一個 Network Namespace ,所以轉移后在這個 Network Namespace 內就看不到這個設備了。

其中,veth設備屬于可轉移設備,而很多其它設備(如lo、vxlan、ppp、bridge等)是不可以轉移的。

veth pair

veth pair 全稱是 Virtual Ethernet Pair,是一個成對的端口,所有從這對端口一 端進入的數據包都將從另一端出來,反之也是一樣。
引入veth pair是為了在不同的 Network Namespace 直接進行通信,利用它可以直接將兩個 Network Namespace 連接起來。
整個veth的實現非常簡單,有興趣的讀者可以參考源代碼drivers/net/veth.c的實現。

veth pair

創建veth pair

$ sudo ip link add type veth
$ ip addr
61: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether e6:39:e1:e0:3a:a0 brd ff:ff:ff:ff:ff:ff
62: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether be:41:49:42:23:6a brd ff:ff:ff:ff:ff:ff

可以看到,此時系統中新增了一對veth pair,將veth0和veth1兩個虛擬網卡連接了起來,此時這對 veth pair 處于”未啟用“狀態。

如果我們想指定 veth pair 兩個端點的名稱,可以使用下面的命令:

ip link add vethfoo type veth peer name vethbar

實現Network Namespace間通信

下面我們利用veth pair實現兩個不同的 Network Namespace 之間的通信。剛才我們已經創建了一個名為ns0的 Network Namespace,下面再創建一個信息Network Namespace,命名為ns1

$ ip netns add ns1
$ ip netns list
ns1
ns0

然后我們將veth0加入到ns0,將veth1加入到ns1,如下所示:

$ ip link set veth0 netns ns0
$ ip link set veth1 netns ns1

然后我們分別為這對veth pair配置上ip地址,并啟用它們:

$ ip netns exec ns0 iplink set veth0 up
$ ip netns exec ns0 ip addr add 10.0.1.1/24 dev veth0
$ ip netns exec ns1 iplink set veth1 up
$ ip netns exec ns1 ip addr add 10.0.1.2/24 dev veth1

查看這對veth pair的狀態

$ ip netns exec ns0 ip addr
61: veth0@if62: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether e6:39:e1:e0:3a:a0 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet 10.0.1.1/24 scope global veth0
       valid_lft forever preferred_lft forever
    inet6 fe80::e439:e1ff:fee0:3aa0/64 scope link
       valid_lft forever preferred_lft forever

$ ip netns exec ns1 ip addr
62: veth1@if61: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether be:41:49:42:23:6a brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.0.1.2/24 scope global veth1
       valid_lft forever preferred_lft forever
    inet6 fe80::bc41:49ff:fe42:236a/64 scope link
       valid_lft forever preferred_lft forever

從上面可以看出,我們已經成功啟用了這個veth pair,并為每個veth設備分配了對應的ip地址。我們嘗試在ns1中訪問ns0中的ip地址:

$ ip netns exec ns1 ping -c 3 10.0.1.1
sudo: unable to resolve host zormshogu
PING 10.0.1.1 (10.0.1.1) 56(84) bytes of data.
64 bytes from 10.0.1.1: icmp_seq=1 ttl=64 time=0.091 ms
64 bytes from 10.0.1.1: icmp_seq=2 ttl=64 time=0.035 ms
64 bytes from 10.0.1.1: icmp_seq=3 ttl=64 time=0.037 ms

--- 10.0.1.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.035/0.054/0.091/0.026 ms

可以看到,veth pair成功實現了兩個不同Network Namespace之間的網絡交互。

veth查看對端

一旦將veth pair的peer段放入另一個Network Namespace,我們在當前Namespace中就看不到它了。那么,我們怎么才能知道這個veth pair的對端在哪里呢?
可以通過ethtool工具來查看(當Network Namespace很多時,操作會比較麻煩):

$ ip netns exec ns1 ethtool -S veth1
NIC statistics:
    peer_ifindex: 5

得知另一端的接口設備序列號是5,我們再到另一個命名空間中查看序列號5代表什么設備:

$ ip netns exec ns0 ip link | grep 5
veth0

網橋

veth pair打破了 Network Namespace 的限制,實現了不同 Network Namespace 之間的通信。但veth pair有一個明顯的缺陷,就是只能實現兩個網絡接口之間的通信。
如果我們想實現多個網絡接口之間的通信,就可以使用下面介紹的網橋(Bridge)技術。
簡單來說,網橋就是把一臺機器上的若干個網絡接口“連接”起來。其結果是,其中一個網口收到的報文會被復制給其他網口并發送出去。以使得網口之間的報文能夠互相轉發。

網橋的工作原理

網橋對報文的轉發基于MAC地址。網橋能夠解析收發的報文,讀取目標MAC地址的信息,和自己記錄的MAC表結合,來決策報文的轉發目標網口。

為了實現這些功能,網橋會學習源MAC地址,在轉發報文時,網橋只需要向特定的網口進行轉發,從而避免不必要的網絡交互。

如果它遇到一個自己從未學習到的地址,就無法知道這個報文應該向哪個網口轉發,就將報文廣播給所有的網口(報文來源的網口除外)。

網橋

網橋的實現

Linux內核是通過一個虛擬的網橋設備(Net Device)來實現橋接的。這個虛擬設備可以綁定若干個以太網接口設備,從而將它們橋接起來。如下圖所示:

網橋的位置

如上圖所示,網橋設備 br0 綁定了 eth0 和 eth1。

對于網絡協議棧的上層來說,只看得到 br0,上層協議棧需要發送的報文被送到 br0,網橋設備的處理代碼判斷報文該被轉發到 eth0 還是 eth1,或者兩者皆轉發;反過來,從eth0 或 eth1 接收到的報文被提交給網橋的處理代碼,在這里會判斷報文應該被轉發、丟棄還是提交到協議棧上層。

而有時eth0、eth1 也可能會作為報文的源地址或目的地址,直接參與報文的發送與接收,從而繞過網橋。

brctl

和網橋有關的操作可以使用命令 brctl,這個命令來自 bridge-utils 這個包。

  1. 創建網橋
# 創建網橋
brctl addbr br0 
  1. 刪除網橋
# 刪除網橋
brctl delbr br0
  1. 綁定網口
    建立一個邏輯網段之后,我們還需要為這個網段分配特定的端口。在Linux 中,一個端口實際上就是一個物理或虛擬網卡。而每個網卡的名稱則分別為eth0 ,eth1 ,eth2 。我們需要把每個網卡一一和br0 這個網段聯系起來,作為br0 中的一個端口。
# 讓eth0 成為br0 的一個端口
brctl addif br0 eth0  
# 讓eth1 成為br0 的一個端口
brctl addif br0 eth1
# 讓eth2 成為br0 的一個端口
brctl addif br0 eth2

iptables/netfilter

iptables是Linux實現的軟件防火墻,用戶可以通過iptables設置請求準入和拒絕規則,從而保護系統的安全。
我們也可以把iptables理解成一個客戶端代理,用戶通過iptables這個代理,將用戶安全設定執行到對應的安全框架中,這個“安全框架”才是真正的防火墻,這個框架的名字叫netfilter
iptables其實是一個命令行工具,位于用戶空間。

iptables/netfilter(以下簡稱iptables)組成了Linux平臺下的包過濾防火墻,可以完成封包過濾、封包重定向和網絡地址轉換(NAT)等功能。

消息處理鏈

iptables不僅要處理本機接收到的消息,也要處理本機發出的消息。這些消息需要經過一系列的”關卡“才能被本機應用層接收,或者從本機發出,每個”關卡“擔負著不同的工作。這里的”關卡“被稱為”鏈“。

  • INPUT:進來的數據包應用此規則鏈中的策規則;
  • OUTPUT:外出的數據包應用此規則鏈中的規則;
  • FORWARD:轉發數據包時應用此規則鏈中的規則;
  • PREROUTING:對數據包作路由選擇前應用此鏈中的規則(所有的數據包進來的時侯都先由這個鏈處理);
  • POSTROUTING:對數據包作路由選擇后應用此鏈中的規則(所有的數據包出來的時侯都先由這個鏈處理);

數據包經過各個鏈的處理過程大致如下圖所示:

消息處理鏈

規則表

從上面我們知道,iptables是按照規則來辦事的,這些規則就是網絡管理員預定義的條件。規則一般的定義為:如果數據包頭符合這樣的條件,就這樣處理“。這些規則并不是嚴格按照添加順序排列在一張規則表中,而是按照功能進行分類,存儲在不同的表中,每個表存儲一類規則:

  • Filter
    主要用來過濾數據,用來控制讓哪些數據可以通過,哪些數據不能通過,它是最常用的表。
  • NAT
    用來處理網絡地址轉換的,控制要不要進行地址轉換,以及怎樣修改源地址或目的地址,從而影響數據包的路由,達到連通的目的。
  • Mangle
    主要用來修改IP數據包頭,比如修改TTL值,同時也用于給數據包添加一些標記,從而便于后續其它模塊對數據包進行處理(這里的添加標記是指往內核skb結構中添加標記,而不是往真正的IP數據包上加東西)。
  • Raw
    在Netfilter里面有一個叫做鏈接跟蹤的功能,主要用來追蹤所有的連接,而raw表里的rule的功能是給數據包打標記,從而控制哪些數據包不做鏈接跟蹤處理,從而提高性能;優先級最高。

表和鏈的關系

表和鏈共同完成了iptables對數據包的處理。但并不是每個鏈都包含所有類型的表,所以,有些鏈是天生不具備某些功能的。就像我們去車站乘車的時候,”關卡A“只負責檢查身份證,”B關卡”只負責檢查行李,而“C關卡”功能比較齊全,即負責檢查身份證,又負責檢查行李。二者的關系如下圖所示:

表和鏈的關系

總結

今天我們共同學習了一些常見的Linux虛擬網絡技術。其中,Linux通過Network Namespace實現了網絡的隔離,使網絡協議棧之間互不干擾;并通過veth pair和網橋實現了相同主機上多個Network Namespace之間的數據通信;iptables則可以幫助我們實現網絡安全和數據包的路由轉發功能,從而使主機和主機、容器與容器、容器和宿主機之間可以相互收發消息。在這些技術的共同協作下,才有了現在安全、穩定的虛擬網絡。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
禁止轉載,如需轉載請通過簡信或評論聯系作者。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,923評論 6 535
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,740評論 3 420
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,856評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,175評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,931評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,321評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,383評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,533評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,082評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,891評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,618評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,319評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,732評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,987評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,794評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,076評論 2 375