ip netns的使用及network namespace 簡介

network namespace 是實現網絡虛擬化的重要功能,它能創建多個隔離的網絡空間,它們有獨自的網絡棧信息。不管是虛擬機還是容器,運行的時候彷彿自己就在獨立的網絡中。這篇文章介紹 network namespace 的基本概念和用法,network namespace 是 linux 內核提供的功能,這篇文章藉助 ip 命令來完成各種操作。

ip 命令管理的功能很多, 和 network namespace 有關的操作都是在子命令 ip netns 下進行的,可以通過 ip netns help 查看所有操作的幫助信息。

默認情況下,使用 ip netns 是沒有網絡 namespace 的,所以 ip netns ls 命令看不到任何輸出。

# 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
# ip netns ls

使用 ip netns add創建 network namespace

# ip netns add net1
# ip netns ls
net1

ip netns 命令創建的 network namespace 會出現在 /var/run/netns/ 目錄下

對於每個 network namespace 來說,它會有自己獨立的網卡、路由表、ARP 表、iptables 等和網絡相關的資源。ip 命令提供了 ip netns exec 子命令可以在對應的 network namespace 中執行命令。

# ip netns exec net1 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

還可以通過bash進入network namespace 並更新命令行提示符

# ip netns exec net1 /bin/bash --rcfile <(echo "PS1=\"namespace net1> \"")
namespace net1> 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
namespace net1> 

默認情況下,network namespace 是不能和主機網絡,或者其他 network namespace 通信的。

1. network namespace 之間通信

有了不同 network namespace 之後,也就有了網絡的隔離,但是如果它們之間沒有辦法通信,也沒有實際用處。要把兩個網絡連接起來,linux 提供了 veth pair 。可以把 veth pair 當做是雙向的 pipe(管道),從一個方向發送的網絡數據,可以直接被另外一端接收到;或者也可以想象成兩個 namespace 直接通過一個特殊的虛擬網卡連接起來,可以直接通信。

使用上面提到的方法,我們再創建另外一個 network namespace,這裏我們使用 net0net1 兩個名字。網絡拓撲結構如下:
在這裏插入圖片描述
我們可以使用 ip link add type veth 來創建一對 veth pair 出來, veth pair 無法單獨存在,刪除其中一個,另一個也會自動消失。

# ip link add type veth
# ip link
4: veth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
    link/ether 36:88:73:83:c9:64 brd ff:ff:ff:ff:ff:ff
5: veth1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000
    link/ether fe:7e:75:4d:79:2e brd ff:ff:ff:ff:ff:ff

NOTE: 創建 veth pair 的時候可以自己指定它們的名字,比如 ip link add vethfoo type veth peer name vethbar 創建出來的兩個名字就是 vethfoovethbar 。因爲這裏我們對名字沒有特殊要求,所以就直接使用系統自動生成的名字。如果 pair 的一端接口處於 DOWN 狀態,另一端能自動檢測到這個信息,並把自己的狀態設置爲 NO-CARRIER

創建結束之後,我們能看到名字爲 veth0veth1 兩個網絡接口,名字後面的數字是系統自動生成的。接下來,要做的是把這對 veth pair 分別放到已經兩個 namespace 裏面,這個可以使用 ip link set DEV netns NAME 來實現:

# ip link set veth0 netns net0
# ip link set veth1 netns net1
# ip netns exec net0 ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
4: veth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether 36:88:73:83:c9:64 brd ff:ff:ff:ff:ff:ff
# ip netns exec net1 ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
5: veth1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
    link/ether fe:7e:75:4d:79:2e brd ff:ff:ff:ff:ff:ff

最後,我們給這對 veth pair 配置上 ip 地址,並啓用它們。

# ip netns exec net0 ip link set veth0 up
# ip netns exec net0 ip addr add 10.0.1.1/24 dev veth0
# ip netns exec net0 ip route
10.0.1.0/24 dev veth0  proto kernel  scope link  src 10.0.1.1

# ip netns exec net1 ip link set veth1 up
# ip netns exec net1 ip addr add 10.0.1.2/24 dev veth1

可以看到,最每個 namespace 中,在配置玩 ip 之後,還自動生成了對應的路由表信息,網絡 10.0.1.0/24 數據報文都會通過 veth pair 進行傳輸。使用 ping 命令可以驗證它們的連通性:

# ip netns exec net0 ping -c 3 10.0.1.2
PING 10.0.1.2 (10.0.1.2) 56(84) bytes of data.
64 bytes from 10.0.1.2: icmp_seq=1 ttl=64 time=0.039 ms
64 bytes from 10.0.1.2: icmp_seq=2 ttl=64 time=0.039 ms
64 bytes from 10.0.1.2: icmp_seq=3 ttl=64 time=0.139 ms

--- 10.0.1.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 0.039/0.072/0.139/0.047 ms

2. 使用 bridge 連接不同的 namespace

雖然 veth pair 可以實現兩個 network namespace 之間的通信,但是當多個 namespace 需要通信的時候,就無能爲力了。
講到多個網絡設備通信,我們首先想到的交換機和路由器。因爲這裏要考慮的只是同個網絡,所以只用到交換機的功能。linux 當然也提供了虛擬交換機的功能,我們還是用 ip 命令來完成所有的操作。

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

下圖是這部分網絡的拓撲結構:
在這裏插入圖片描述

首先我們來創建需要的 bridge,簡單起見名字就叫做 br0。

# ip link add br0 type bridge
# ip link set dev br0 up

下面只演示一個 namespace 的操作,其他 namespace 要做的事情和這個類似。創建 veth pair:

# ip link add type veth

把其中一個 veth(veth1) 放到 net0 裏面,設置它的 ip 地址並啓用它:

# ip link set dev veth1 netns net0
# ip netns exec net0 ip link set dev veth1 name eth0
# ip netns exec net0 ip addr add 10.0.1.1/24 dev eth0
# ip netns exec net0 ip link set dev eth0 up

最後,把另一個 veth(veth0)連接到創建的 bridge 上,並啓用它:

# ip link set dev veth0 master br0
# ip link set dev veth0 up

可以通過 bridge命令(也是 iproute2 包自帶的命令)來查看 bridge 管理的 link 信息:

# bridge link
17: veth0 state UP : <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br0 state forwarding priority 32 cost 2

最後通過 ping 命令來測試網絡的連通性:

# ip netns exec net0 ping -c 3 10.0.1.3
PING 10.0.1.3 (10.0.1.3) 56(84) bytes of data.
64 bytes from 10.0.1.3: icmp_seq=1 ttl=64 time=0.251 ms
64 bytes from 10.0.1.3: icmp_seq=2 ttl=64 time=0.047 ms
64 bytes from 10.0.1.3: icmp_seq=3 ttl=64 time=0.046 ms

--- 10.0.1.3 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2008ms

如果對 docker 網絡熟悉的話,其實這和 docker 默認的 bridge 網絡模型非常相似。當然要實現每個 namespace 對外網的訪問還需要額外的配置(設置默認網關,開啓 ip_forward,爲網絡添加 NAT 規則等)。

參考:
https://blog.kghost.info/2013/03/01/linux-network-emulator/
http://blog.scottlowe.org/2013/09/04/introducing-linux-network-namespaces/
https://cizixs.com/2017/02/10/network-virtualization-network-namespace/
https://blog.csdn.net/sld880311/article/details/77650937

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章