任务
我需要明确且没有“整体”猜测的情况下在另一个网络命名空间中找到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 对的对等端丢失的网络命名空间信息?
这是我遵循的方法来了解如何理解这个问题。可用的工具似乎可以用于命名空间部分(带有一些卷积),并且(已更新)使用 /sys/ 可以轻松获取对等点的索引。所以它很长,请耐心等待。它分为两部分(不按逻辑顺序,但命名空间首先有助于解释索引命名),使用通用工具,而不是任何自定义程序:
网络命名空间
此信息可通过
link-netnsid
的输出中的属性获得,ip link
并且可以与 的输出中的 id 匹配ip netns
。可以将容器的网络命名空间“关联”到ip netns
,从而将ip netns
其用作专用工具。当然为此做一个特定的程序会更好(每个部分末尾有关系统调用的一些信息)。关于 nsid 的描述,以下是
man ip netns
说明(强调我的):虽然创建命名空间
ip netns
不会立即创建 netnsid,但只要将 veth half 设置为另一个命名空间,就会创建它(在当前命名空间上,可能是“主机”)。所以它总是为一个典型的容器设置。这是一个使用 LXC 容器的示例:
出现了一个新的 veth 链接
veth9RPX4M
(可以用 跟踪ip monitor link
)。以下是详细信息:这个链接有属性
link-netnsid 4
,告诉对方在网络命名空间中,nsid 4。如何验证它是 LXC 容器?获取此信息的最简单方法是通过执行 manpage 中提示的操作ip netns
来相信它创建了容器的网络命名空间。UPDATE3:我不明白找回全局名称是个问题。这里是:
现在通过以下方式检索信息:
它确认 veth 的对等点位于具有相同 nsid = 4 = link-netnsid 的网络命名空间中。
可以删除容器/
ip netns
“关联”(只要容器正在运行,就无需删除命名空间):注意: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的主题。
接口索引
现在更容易理解为什么索引似乎具有随机行为。让我们做一个实验:
首先输入一个新的网络命名空间以获得一个干净的(索引)石板:
正如 OP 所指出的, lo 从索引 1 开始。
让我们添加 5 个网络命名空间,创建 veth 对,然后在它们上添加一个 veth 结尾:
当它为它们中的每一个显示@if2 时,很明显它是对等的命名空间接口索引和索引不是全局的,而是每个命名空间的。当它显示一个实际的接口名称时,它是与同一名称空间中的一个接口的关系(无论是 veth 的对等点、网桥、键...)。那么为什么 veth0 没有显示对等点呢?
ip link
当索引与自身相同时,我相信这是一个错误。只需移动两次对等链接就可以在这里“解决”它,因为它会强制更改索引。我也确定有时ip link
会造成其他混淆,而不是显示@ifXX,而是在当前命名空间中显示一个具有相同索引的接口。更新:再次阅读 OP 问题中的信息,同行的索引(但不是 nsid)很容易且明确地可用.
cat /sys/class/net/
interface
/iflink
更新2:
所有这些 iflink 2 可能看起来模棱两可,但独特的是 nsid 和 iflink 的组合,而不是单独的 iflink。对于上面的例子是:
在这个命名空间(即命名空间
test
)中,永远不会有两个相同的 nsid:pair 。如果要从每个对等网络中查看相反的信息:
但请记住,
0:
每一个都有一个单独的 0,它恰好映射到同一个对等命名空间(即:命名空间test
,甚至不是主机)。它们无法直接比较,因为它们与它们的命名空间相关联。因此,整个可比较且唯一的信息应该是:一旦确认 "test0:0" == "test1:0" 等(在这个例子中是真的,都映射到由 调用的网络命名空间
test
),ip netns
那么它们就可以真正进行比较。关于系统调用,仍然查看 strace 结果,信息如上从
RTM_GETLINK
. 现在应该有所有可用的信息:本地:带有
SIOCGIFINDEX
/ 对等的接口索引:nsid 和带有 . 的接口索引。if_nametoindex
RTM_GETLINK
所有这些都应该与libnl一起使用。
非常感谢@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
我创建了一个简单的脚本,列出了所有具有关联 veth 接口的容器:https ://github.com/samos123/docker-veth/blob/master/docker-veth.sh
让我解释一下它是如何工作的:
nsenter
您会注意到
eth0@ifX
容器网络命名空间内有一个接口。X 告诉您主机网络上的接口索引。然后可以使用该索引来确定哪个 veth 属于容器。运行以下命令找到 veth 接口:
包含更多详细信息的博客文章:http: //samos-it.com/posts/enter-namespace-of-other-containers-from-a-pod.html