Linux

無法創建嵌套網路命名空間

  • April 5, 2019

似乎無法從網路名稱空間創建網路名稱空間。它導致“錯誤:對等網路引用無效。”。

這是一個錯誤還是有某種我不知道的限制?

下面是我對錯誤的 cmd 跟踪。

# ip netns add foo1
# ip netns exec foo1 ip netns add foo2
# ip netns
Error: Peer netns reference is invalid.
Error: Peer netns reference is invalid.
foo2
foo1
# ip netns exec foo2 /bin/bash
setting the network namespace "foo2" failed: Invalid argument

TL;DR:雖然看起來很奇怪,但這實際上不是網路命名空間問題,而是掛載命名空間問題,這是意料之中的。

您應該創建所有新的“ip netns 命名空間”(含義見下文),即ip netns add ...從初始(主機)“ip netns 命名空間”執行所有命令,而不是從已輸入的“ip netns 命名空間”內部執行ip netns exec ...。只要您不創建它們,您就可以隨意在它們之間切換,包括從一個到另一個嵌套命令,使用ip netns exec ....

詳細說明,分步範例如下…


ip netns專門研究網路命名空間,但要處理所有功能,還必須與掛載命名空間混合,原因有兩個(至少,我知道):

  • 綁定掛載/etc/netns/FOO/SOMESERVICE/etc/SOMESERVICE管理備用服務/守護程序配置

該功能可以方便地在其他網路名稱空間中輕鬆執行一些(網路相關)守護程序,但除此之外它仍然是“主機”的一部分。您可以在 UL 上查看我關於該問題的回答:Namespace management with ip netns (iproute2)。它的使用需要和下面的特性一樣的處理,這裡不再贅述。

  • 重新掛載/sys以在其層次結構中公開新的網路命名空間的網路設備

這是一項強制性功能。暴露問題的範例:

從“初始主機”:

# ip link add dev dummy9 type dummy
# ip -br link show dummy9 
dummy9           DOWN           f6:f6:48:9c:12:b9 <BROADCAST,NOARP> 
# ls -l /sys/class/net/dummy9
lrwxrwxrwx. 1 root root 0 Apr  4 22:09 /sys/class/net/dummy9 -> ../../devices/virtual/net/dummy9

使用較低級別的工具更改為其他(臨時)網路命名空間:

# unshare --net ip -br link show dummy9 
Device "dummy9" does not exist.
# unshare --net ls -l /sys/class/net/dummy9
lrwxrwxrwx. 1 root root 0 Apr  4 22:13 /sys/class/net/dummy9 -> ../../devices/virtual/net/dummy9

這就是問題所在:/sys仍然公開初始主機的介面,而不是新的網路命名空間的介面。這就是網路命名空間和與掛載之間存在互動的地方/sys:如果/sys從新的網路命名空間掛載,它將切換到在選擇目錄層次結構(例如/sys/class/net/sys/devices/virtual/net)中公開新的網路介面。這僅在安裝時完成,而不是動態完成。一些高級網路設置很容易通過在此處讀取或寫入來獲得,因此必須提供它們,反之亦然:在新網路環境中執行的隔離程序不應該能夠看到或更改初始主機的介面。

因此ip netns exec FOO ...(但不是)通過取消共享掛載命名空間並在其中重新掛載ip netns add FOO來解決此問題,以免破壞初始主機的網路命名空間。但重要的是,這個掛載命名空間本身是短暫的:當您分別執行兩個命令時,它們最終不會在同一個掛載命名空間中。他們每個人都有自己的,重新安裝在那裡指向同一個網路命名空間。/sys/``ip netns exec FOO ...``/sys

到現在為止,沒問題。當這種情況發生時,我將其稱為“ip netns 名稱空間”,因為現在涉及兩種類型的名稱空間。到目前為止,我們有:

術語1:

# ip netns add FOO
# ls -l /proc/$$/ns/{mnt,net}
lrwxrwxrwx. 1 root root 0 Apr  4 22:28 /proc/1712/ns/mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 root root 0 Apr  4 22:28 /proc/1712/ns/net -> net:[4026531992]
# ip netns exec FOO bash
# ls -l /proc/$$/ns/{mnt,net}
lrwxrwxrwx. 1 root root 0 Apr  4 22:33 /proc/1864/ns/mnt -> mnt:[4026532618]
lrwxrwxrwx. 1 root root 0 Apr  4 22:33 /proc/1864/ns/net -> net:[4026532520]

術語2:

# ls -l /proc/$$/ns/{mnt,net}
lrwxrwxrwx. 1 root root 0 Apr  4 22:32 /proc/1761/ns/mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 root root 0 Apr  4 22:32 /proc/1761/ns/net -> net:[4026531992]
# ip netns exec FOO bash
# ls -l /proc/$$/ns/{mnt,net}
lrwxrwxrwx. 1 root root 0 Apr  4 22:33 /proc/1866/ns/mnt -> mnt:[4026532821]
lrwxrwxrwx. 1 root root 0 Apr  4 22:33 /proc/1866/ns/net -> net:[4026532520]

請注意在更改 ip netns 命名空間後,雖然term1term2的新網路命名空間相同,但新的掛載命名空間彼此不同(以及與初始主機不同)。

現在,當您在term1中創建一個新的 ip netns 命名空間時會發生什麼?讓我們來看看:

術語1:

# ip netns add BAR
# ip netns ls
BAR
FOO

術語2:

# ip netns ls
Error: Peer netns reference is invalid.
Error: Peer netns reference is invalid.
BAR
FOO

這是因為較新的命名空間 BAR,在沒有程序的情況下保持存在,與其他命名空間一樣,安裝在(新創建的空文件)上/var/run/netns/BAR(再次,請參見前面的連結以獲取範例)。雖然掛載命名空間不同,但它們具有相同的根目錄:初始主機的根目錄。所以當然,這個新創建的空文件在創建時/var/run/netns/BAR隨處可見(初始,term1的掛載 ns,term2的掛載 ns)。

唉,在它上面的掛載,在term1的 FOO 的掛載命名空間上完成,只能在term1上看到,而不是在term2或其他任何地方,因為它是一個不同的掛載命名空間。因此,雖然在 term1 (的 FOO ip netns 命名空間)/var/run/netns/BAR是屬於nsfs偽文件系統的偽文件:

術語1:

# stat -f -c %T /var/run/netns/BAR
nsfs

它是其他任何地方tmpfs(來自實際安裝)的空文件:/run

術語2:

# stat -f -c %T /var/run/netns/BAR
tmpfs

任何其他終端:

$ stat -f -c %T /var/run/netns/BAR
tmpfs

只要不退出目前的“ip netns 命名空間”,它仍然可以在 term1 中看到。如果從term1仍然切換 ip netns namespaces ,它仍然可以,因為新的非共享臨時掛載命名空間是前一個的副本,包括所有掛載。

如果退出,則該掛載點將失去(這意味著如果不再有程序或文件描述符使用它,則 BAR 的相應網路命名空間將消失,因為它僅由該掛載點持有)。在此之後,任何ip netns ls命令都會在任何地方抱怨。您可以刪除陳舊且現在無用的文件/run/netns/BAR來修復它。

在這個逐步解釋之後,要記住的是,您不應該在目前使用. 您應該從初始(主機)命名空間創建它們,然後您可以從任何 ip netns 命名空間隨意切換它們。ip netns add``ip netns exec

當然,如果/var/run/netns/(即掛載點/run)在(保持模糊的)命名空間之間是不同的,那麼就沒有互動,並且每個ip netns呼叫都將與其他呼叫隔離,不會看到也不會與其他呼叫互動。這通常發生在哪裡?在完整容器中,掛載和網路命名空間都是分開的,並且從一開始就指向不同的資源。


更新:如評論中所問,我檢查瞭如何“修復”這個問題,但找不到任何簡單的解決方案。

首先有一個先決條件:如上所述,一旦在 FOO 內部創建了新的“ip netns”命名空間 BAR,並且留下了 FOO,那麼對 BAR 的唯一引用就會消失,從而使 BAR 也消失。還需要一些東西。

實際上有三種方法可以保持對命名空間的引用

  • process:這是主要方法,而且大多數時候這就是命名空間的使用方式
  • 掛載點(這是 所使用的方法ip netns):允許在沒有任何程序的情況下保留命名空間,很好地擁有一個內部只有網路設置的命名空間(介面、網橋、tc 規則、防火牆規則,…)
  • 打開文件描述符:很少,在創建命名空間時使用,但很少保留,除了應用程序同時處理多個命名空間並使用文件描述符切換它們的一些執行緒以便於參考。

我們可以使用第一種或第三種方法。在找到有用的東西之前,這裡有各種失敗的嘗試……

如前所述,不起作用:

# ip netns add FOO
# ip netns exec FOO ip netns add BAR

只需在第一個“ip netns”命名空間中臨時執行一個程序*,作為其臨時掛載命名空間部分,以保留對新*“ip netns”命名空間的網路命名空間的所需引用,並稍後從外部(從初始命名空間)重用它。

也不行:

# ip netns add FOO
# ip netns exec FOO sh -c 'ip netns add BAR; sleep 999 < /var/run/netns/BAR & echo $!'
28344
# strace -e trace=readlink,mount mount --bind /proc/6295/fd/0 /var/run/netns/BAR
readlink("/proc/6295/fd/0", "/run/netns/BAR", 4095) = 14
readlink("/var/run", "/run", 4095)      = 4
mount("/run/netns/BAR", "/run/netns/BAR", 0x55c88c9cccb0, MS_BIND, NULL) = 0
+++ exited with 0 +++
# stat -f -c %T /run/netns/BAR
tmpfs

正如命令所看到strace的那樣,mount當它不應該用於這個案例時,它會跟隨符號連結(注意:掛載仍然以某種方式連結到睡眠程序,必須被殺死才能解除安裝它)。

這(進入sleep掛載命名空間,以訪問隱藏在那裡的 BAR 的掛載網路命名空間)有效,但依賴於繼續存在sleep或任何繼續使用的程序:

# ip netns add FOO
# ip netns exec FOO sh -c 'ip netns add BAR; ip -n BAR link add dummy8 type dummy; sleep 999 & echo $!'
12916
# nsenter --target=12916 --mount ip -n -brief BAR link show
lo               DOWN           00:00:00:00:00:00 <LOOPBACK> 
dummy8           DOWN           8e:ce:b3:d1:9c:bb <BROADCAST,NOARP> 

奇怪的是(使用 mount 命名空間快捷方式/proc/pid/root/)不起作用(我真的不知道為什麼):

# stat -f -c %T /proc/12916/root/var/run/netns/BAR 
tmpfs

最後什麼會起作用:

# ip netns add FOO
# ip netns exec FOO sh -c 'ip netns add BAR; ip -n BAR link add dummy8 type dummy; ip netns exec BAR sh -c '\''sleep 999 & echo $!'\'
14124
# mount --bind /proc/14124/ns/net /var/run/netns/BAR
# ip -n BAR -brief link show
lo               DOWN           00:00:00:00:00:00 <LOOPBACK> 
dummy8           DOWN           3a:48:65:20:68:c1 <BROADCAST,NOARP> 

所以最終可以使用這樣的東西。如果您嘗試在 sleep 命令結束之前立即刪除它們,可能會出現競爭條件。

# ip netns add FOO
# mount --bind /proc/$(ip netns exec FOO sh -c 'ip netns add BAR; ip netns exec BAR bash -c '\''sleep 5 </dev/null >/dev/null 2>&1 & echo $!; disown'\')/ns/net /var/run/netns/BAR

如何使用這樣的構造?我不知道,因為沒有給出遇到嵌套“ip netns”問題之前的原始問題。也許無需嘗試創建“嵌套網路名稱空間”就可以獲得更簡單的解決方案。

引用自:https://serverfault.com/questions/961504