如何找到 veth 對等體 ifindex 的網路命名空間?
任務
我需要明確且沒有“整體”猜測的情況下在另一個網路命名空間中找到veth 端的對等網路介面。
理論 。/。現實
儘管有很多文件和關於 SO 的答案都假設網路介面的 ifindex 索引在網路名稱空間中每個主機都是全域唯一的,但這在許多情況下並不成立:
ifindex/iflink
是模棱兩可的。甚至環回也已經表明相反的情況,在任何網路命名空間中的 ifindex 都是 1。此外,根據容器環境,ifindex
數字會在不同的命名空間中重用。這使得跟踪 veth 佈線成為一場噩夢,尤其是有很多容器和一個帶有 veth 對等點的主機橋都以 @if3 左右結尾……範例:
link-netnsid
是0
啟動一個 Docker 容器實例,只是為了獲得一個
veth
從主機網路命名空間連接到新容器網路命名空間的新對……$ sudo docker run -it debian /bin/bash
現在,在主機網路命名空間列表中的網路介面(我忽略了那些對這個問題不感興趣的介面):
$ ip連結顯示 1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 連結/環回 00:00:00:00:00:00 brd 00:00:00:00:00:00 ... 4:docker0:mtu 1500 qdisc noqueue state UP mode DEFAULT group default 連結/乙太 02:42:34:23:81:f0 brd ff:ff:ff:ff:ff:ff ... 16: vethfc8d91e@if15: mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default 連結/乙太 da:4c:f7:50:09:e2 brd ff:ff:ff:ff:ff:ff 連結-netnsid 0
如您所見,雖然
iflink
是明確的,但link-netnsid
是 0,儘管對等端位於不同的網路命名空間中。作為參考,請檢查容器的未命名網路命名空間中的 netnsid:
$ sudo lsns -t 網路 NS 類型 NPROCS PID 使用者命令 ... ... 4026532469 網路 1 29616 根 /bin/bash $ sudo nsenter -t 29616 -n ip 連結顯示 1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 連結/環回 00:00:00:00:00:00 brd 00:00:00:00:00:00 15: eth0@if16: mtu 1500 qdisc noqueue state UP mode DEFAULT group default 連結/乙太 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff 連結-netnsid 0
因此,對於兩個 veth 端
ip link show
(和 RTNETLINK fwif)都告訴我們它們與 netnsid 0 在同一個網路命名空間中。在 link-netnsids 是本地而不是全域的假設下,這是錯誤的或正確的。我找不到任何文件來明確說明 link-netnsids 應該具有的範圍。
/sys/class/net/...
不去救援?我查看了 /sys/class/net/ if /… 但只能找到 ifindex 和 iflink 元素;這些都有據可查。“ip link show”似乎也只以(in)著名的“@if#”符號的形式顯示對等 ifindex。還是我錯過了一些額外的網路命名空間元素?
底線/問題
是否有任何系統呼叫允許檢索 veth 對的對等端失去的網路命名空間資訊?
非常感謝@AB,他為我填補了一些缺失的部分,特別是關於
netnsid
s 的語義。他的 PoC 很有啟發性。然而,他的 PoC 中關鍵缺失的部分是如何將本地netnsid
與其全域唯一的網路命名空間 inode 編號相關聯,因為只有這樣我們才能明確地連接正確的veth
對應對。總結並給出一個小的 Python 範例,如何以程式方式收集資訊而無需依賴
ip netns
及其需要掛載的東西:RTNETLINK 在查詢網路介面時實際上返回 netnsid。它是IFLA_LINK_NETNSID
屬性,僅在需要時出現在連結的資訊中。如果它不存在,那麼就不需要它——我們必須假設對等索引指的是命名空間本地網路介面。要帶回家的重要教訓是,
netnsid
/IFLA_LINK_NETSID
僅在您向 RTNETLINK 詢問連結資訊時獲得的網路命名空間內**本地定義。**在不同的網路命名空間中獲得相同值的 Anetnsid
可能會標識不同的對等命名空間,因此請注意不要使用netnsid
其命名空間之外的名稱。但是哪個唯一可辨識的網路命名空間(inode
編號)映射到哪個netnsid
?事實證明,
lsns
截至 2018 年 3 月的最新版本能夠很好地netnsid
在其網路命名空間 inode 編號旁邊顯示正確!所以有一種方法可以將 local 映射netnsid
到命名空間 inode,但實際上是倒退的!它更像是一個 oracle(帶有小寫 ell)而不是查找:RTM_GETNSID 需要一個網路名稱空間標識符作為 PID 或 FD(到網路名稱空間),然後返回netnsid
. 有關如何詢問 Linux 網路命名空間 oracle 的範例,請參閱https://stackoverflow.com/questions/50196902/retrieving-the-netnsid-of-a-network-namespace-in-python 。因此,您需要列舉可用的網路命名空間(通過
/proc
和/或/var/run/netns
),然後對於給定的veth
網路介面附加到您找到它的網路命名空間,詢問netnsid
您在開始時列舉的所有網路命名空間的 s(因為您永遠不會事先知道哪個是哪個),最後根據您在第 3 步中創建的本地映射,在附加到 的命名空間後netnsid
,將對等節點映射到命名空間 inode 編號。veth``veth
import psutil import os import pyroute2 from pyroute2.netlink import rtnl, NLM_F_REQUEST from pyroute2.netlink.rtnl import nsidmsg from nsenter import Namespace # phase I: gather network namespaces from /proc/[0-9]*/ns/net netns = dict() for proc in psutil.process_iter(): netnsref= '/proc/{}/ns/net'.format(proc.pid) netnsid = os.stat(netnsref).st_ino if netnsid not in netns: netns[netnsid] = netnsref # phase II: ask kernel "oracle" about the local IDs for the # network namespaces we've discovered in phase I, doing this # from all discovered network namespaces for id, ref in netns.items(): with Namespace(ref, 'net'): print('inside net:[{}]...'.format(id)) ipr = pyroute2.IPRoute() for netnsid, netnsref in netns.items(): with open(netnsref, 'r') as netnsf: req = nsidmsg.nsidmsg() req['attrs'] = [('NETNSA_FD', netnsf.fileno())] resp = ipr.nlm_request(req, rtnl.RTM_GETNSID, NLM_F_REQUEST) local_nsid = dict(resp[0]['attrs'])['NETNSA_NSID'] if local_nsid != 2**32-1: print(' net:[{}] <--> nsid {}'.format(netnsid, local_nsid))
這是我遵循的方法來了解如何理解這個問題。可用的工具似乎可以用於命名空間部分(帶有一些卷積),並且(已更新)使用 /sys/ 可以輕鬆獲取對等點的索引。所以它很長,請耐心等待。它分為兩部分(不按邏輯順序,但命名空間首先有助於解釋索引命名),使用通用工具,而不是任何自定義程序:
- 網路命名空間
- 介面索引
網路命名空間
此資訊可通過
link-netnsid
的輸出中的屬性獲得,ip link
並且可以與 的輸出中的 id 匹配ip netns
。可以將容器的網路命名空間“關聯”到ip netns
,從而將ip netns
其用作專用工具。當然為此做一個特定的程序會更好(每個部分末尾有關係統呼叫的一些資訊)。關於 nsid 的描述,以下是
man ip netns
說明(強調我的):ip netns set NAME NETNSID - 將 id 分配給對等網路命名空間
此命令將 id 分配給對等網路命名空間。此 id 僅在目前網路命名空間中有效。這個 id 將被核心在一些 netlink 消息中使用。如果核心需要的時候沒有分配id,就會由核心自動分配。一旦分配,就無法更改它。
雖然創建命名空間
ip netns
不會立即創建 netnsid,但只要將 veth half 設置為其他命名空間,就會創建它(在目前命名空間上,可能是“主機”)。所以它總是為一個典型的容器設置。這是一個使用 LXC 容器的範例:
# lxc-start -n stretch-amd64
出現了一個新的 veth 連結
veth9RPX4M
(可以用 跟踪ip monitor link
)。以下是詳細資訊:# ip -o link show veth9RPX4M 44: veth9RPX4M@if43: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue master lxcbr0 state LOWERLAYERDOWN mode DEFAULT group default qlen 1000 link/ether fe:25:13:8a:00:f8 brd ff:ff:ff:ff:ff:ff link-netnsid 4
這個連結有屬性
link-netnsid 4
,告訴對方在網路命名空間中,nsid 4。如何驗證它是 LXC 容器?獲取此資訊的最簡單方法是通過執行 manpage 中提示的操作ip netns
來相信它創建了容器的網路命名空間。# mkdir -p /var/run/netns # touch /var/run/netns/stretch-amd64 # mount -o bind /proc/$(lxc-info -H -p -n stretch-amd64)/ns/net /var/run/netns/stretch-amd64
UPDATE3:我不明白找回全域名稱是個問題。這裡是:
# ls -l /proc/$(lxc-info -H -p -n stretch-amd64)/ns/net lrwxrwxrwx. 1 root root 0 mai 5 20:40 /proc/17855/ns/net -> net:[4026532831] # stat -c %i /var/run/netns/stretch-amd64 4026532831
現在通過以下方式檢索資訊:
# ip netns | grep stretch-amd64 stretch-amd64 (id: 4)
它確認 veth 的對等點位於具有相同 nsid = 4 = link-netnsid 的網路命名空間中。
可以刪除容器/
ip netns
“關聯”(只要容器正在執行,就無需刪除命名空間):# ip netns del stretch-amd64
注意:nsid 命名是每個網路命名空間,通常第一個容器以 0 開頭,可用的最低值與新命名空間一起回收。
關於使用系統呼叫,以下是從 strace 猜測的資訊:
- 對於連結部分:它需要一個
AF_NETLINK
套接字(打開socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)
),詢問(sendmsg()
)帶有消息類型的連結資訊RTM_GETLINK
並檢索(recvmsg()
)帶有消息類型的回复RTM_NEWLINK
。- 對於 netns nsid 部分:同樣的方法,查詢消息是 type
RTM_GETNSID
with reply typeRTM_NEWNSID
。我認為處理這個問題的稍微更高級別的庫在那裡:libnl。無論如何,這是SO的主題。
介面索引
現在更容易理解為什麼索引似乎具有隨機行為。讓我們做一個實驗:
首先輸入一個新的網路命名空間以獲得一個乾淨的(索引)石板:
# ip netns add test # ip netns exec test bash # ip netns id test # ip -o link 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000\ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
正如 OP 所指出的, lo 從索引 1 開始。
讓我們添加 5 個網路命名空間,創建 veth 對,然後在它們上添加一個 veth 結尾:
# for i in {0..4}; do ip netns add test$i; ip link add type veth peer netns test$i ; done # ip -o link|sed 's/^/ /' 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000\ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: veth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000\ link/ether e2:83:4f:60:5a:30 brd ff:ff:ff:ff:ff:ff link-netnsid 0 3: veth1@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000\ link/ether 22:a7:75:8e:3c:95 brd ff:ff:ff:ff:ff:ff link-netnsid 1 4: veth2@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000\ link/ether 72:94:6e:e4:2c:fc brd ff:ff:ff:ff:ff:ff link-netnsid 2 5: veth3@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000\ link/ether ee:b5:96:63:62:de brd ff:ff:ff:ff:ff:ff link-netnsid 3 6: veth4@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000\ link/ether e2:7d:e2:9a:3f:6d brd ff:ff:ff:ff:ff:ff link-netnsid 4
當它為它們中的每一個顯示@if2 時,很明顯它是對等的命名空間介面索引和索引不是全域的,而是每個命名空間的。當它顯示一個實際的介面名稱時,它是與同一名稱空間中的一個介面的關係(無論是 veth 的對等點、網橋、鍵…)。那麼為什麼 veth0 沒有顯示對等點呢?
ip link
當索引與自身相同時,我相信這是一個錯誤。只需移動兩次對等連結就可以在這裡“解決”它,因為它會強制更改索引。我也確定有時ip link
會造成其他混淆,而不是顯示@ifXX,而是在目前命名空間中顯示一個具有相同索引的介面。# ip -n test0 link set veth0 name veth0b netns test # ip link set veth0b netns test0 # ip -o link 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000\ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: veth0@if7: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000\ link/ether e2:83:4f:60:5a:30 brd ff:ff:ff:ff:ff:ff link-netnsid 0 3: veth1@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000\ link/ether 22:a7:75:8e:3c:95 brd ff:ff:ff:ff:ff:ff link-netnsid 1 4: veth2@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000\ link/ether 72:94:6e:e4:2c:fc brd ff:ff:ff:ff:ff:ff link-netnsid 2 5: veth3@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000\ link/ether ee:b5:96:63:62:de brd ff:ff:ff:ff:ff:ff link-netnsid 3 6: veth4@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000\ link/ether e2:7d:e2:9a:3f:6d brd ff:ff:ff:ff:ff:ff link-netnsid 4
更新:再次閱讀 OP 問題中的資訊,同行的索引(但不是 nsid)很容易且明確地可用.
cat /sys/class/net/
interface
/iflink
更新2:
所有這些 iflink 2 可能看起來模棱兩可,但獨特的是 nsid 和 iflink 的組合,而不是單獨的 iflink。對於上面的例子是:
interface nsid:iflink veth0 0:7 veth1 1:2 veth2 2:2 veth3 3:2 veth4 4:2
在這個命名空間(即命名空間
test
)中,永遠不會有兩個相同的 nsid:pair 。如果要從每個對等網路中查看相反的資訊:
namespace interface nsid:iflink test0 veth0 0:2 test1 veth0 0:3 test2 veth0 0:4 test3 veth0 0:5 test4 veth0 0:6
但請記住,
0:
每一個都有一個單獨的 0,它恰好映射到同一個對等命名空間(即:命名空間test
,甚至不是主機)。它們無法直接比較,因為它們與它們的命名空間相關聯。因此,整個可比較且唯一的資訊應該是:test0:0:2 test1:0:3 test2:0:4 test3:0:5 test4:0:6
一旦確認 “test0:0” == “test1:0” 等(在這個例子中是真的,都映射到由 呼叫的網路命名空間
test
),ip netns
那麼它們就可以真正進行比較。關於系統呼叫,仍然查看 strace 結果,資訊如上從
RTM_GETLINK
. 現在應該有所有可用的資訊:本地:帶有
SIOCGIFINDEX
/ 對等的介面索引:nsid 和帶有 . 的介面索引。if_nametoindex
RTM_GETLINK
所有這些都應該與libnl一起使用。