任务

任务

任务

我需要明确地、没有“整体”猜测地找到同行另一个网络命名空间中 veth 端的网络接口。

理论 。/。现实

尽管这里有很多文档和答案,但假设网络接口的 ifindex 索引在网络命名空间中每个主机都是全局唯一的,这在很多情况下并不成立ifindex/iflink 含糊不清。甚至环回也已经显示出相反的情况,在任何网络命名空间中 ifindex 均为 1。另外,根据容器环境,ifindex数字在不同的命名空间中被重用。这使得追踪 veth 连接成为一场噩梦,特别是有大量容器和主机桥,veth 对等点都以 @if3 左右结尾......

示例:link-netnsid0

启动一个 Docker 容器实例,只是为了获得veth从主机网络命名空间连接到新容器网络命名空间的新对......

$ sudo docker run -it debian /bin/bash

现在,在主机网络命名空间中列出网络接口(我省略了与此问题无关的那些接口):

$ ip 链接显示
1: lo: mtu 65536 qdisc noqueue 状态 UNKNOWN 模式 DEFAULT 组默认 qlen 1000
    链接/环回 00:00:00:00:00:00 brd 00:00:00:00:00:00
...
4: docker0: mtu 1500 qdisc noqueue 状态 UP 模式 DEFAULT 组默认
    链接/以太 02:42:34:23:81:f0 brd ff:ff:ff:ff:ff:ff
...
16:vethfc8d91e@if15:mtu 1500 qdisc noqueue master docker0状态UP模式DEFAULT组默认
    链接/以太 da:4c:f7:50:09:e2 brd ff:ff:ff:ff:ff:ff link-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 状态 UNKNOWN 模式 DEFAULT 组默认 qlen 1000
    链接/环回 00:00:00:00:00:00 brd 00:00:00:00:00:00
15: eth0@if16: mtu 1500 qdisc noqueue 状态 UP 模式 DEFAULT 组默认
    链接/以太 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0

因此,对于两个 veth 端ip link show(和 RTNETLINK fwif),它们告诉我们它们与 netnsid 0 位于同一网络命名空间中。在假设 link-netnsid 是本地而不是全局的情况下,这要么是错误的,要么是正确的。我找不到任何文档可以明确说明 link-netnsids 的范围。

/sys/class/net/...不去救援?

我研究过 /sys/class/net/如果/...但只能找到ifindex和iflink元素;这些都有详细记录。 “ip link show”似乎也只以著名的“@if#”符号的形式显示对等 ifindex。或者我错过了一些额外的网络命名空间元素?

底线/问题

是否有任何系统调用允许检索 veth 对对等端丢失的网络命名空间信息?

答案1

这是我用来寻找如何理解这个问题的方法。可用的工具似乎可用于命名空间部分(带有一些卷积),并且(已更新)使用 /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 容器?获取此信息的最简单方法是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

更新3:我不明白找回全局名称是一个问题。这里是:

# 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部分:同样的方法,查询消息是typeRTM_GETNSID与回复类型RTM_NEWNSID

我认为处理这个问题的更高级别的库存在:。无论如何,这是一个话题所以

接口索引

现在可以更容易地理解为什么该指数似乎具有随机行为。我们来做一个实验:

首先输入一个新的网络命名空间以获得一个干净的(索引)石板:

# 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

在这个命名空间(即namespace 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”等(在本例中为 true,所有映射到test由调用的网络命名空间ip netns),那么它们就可以真正进行比较。

关于系统调用,仍然查看 strace 结果,信息是从上面检索的RTM_GETLINK。现在应该有所有可用信息:

local:接口索引SIOCGIFINDEX/if_nametoindex
对等体:nsid 和接口索引RTM_GETLINK

所有这些可能应该与

答案2

2023 年更新:西门子现已发布边缘鲨作为 OSS,它提供了一个漂亮的图形 Web UI,呈现容器、主机等中网络接口的关系。它使用本答案中概述的方法的基于 Go 的实现,并具有更多功能。

非常感谢@AB,他为我填补了一些缺失的部分,特别是关于 s 的语义netnsid。他的 PoC 非常有启发性。然而,他的 PoC 中关键的缺失部分是如何将本地名称空间netnsid与其全局唯一的网络命名空间 inode 编号相关联,因为只有这样我们才能明确连接正确的对应对veth

总结并给出一个小的 Python 示例,如何以编程方式收集信息,而无需依赖ip netns及其需要挂载的东西:RTNETLINK 在查询网络接口时实际上返回 netnsid。它是IFLA_LINK_NETNSID属性,仅在需要时出现在链接的信息中。如果它不存在,那么就不需要它——并且我们必须假设对等索引引用命名空间本地网络接口。

要记住的重要一课netnsidIFLA_LINK_NETSID本地在向 RTNETLINK 请求链接信息时获得它的网络命名空间中定义。在不同的网络命名空间中获取的相同值netnsid可能会标识不同的对等命名空间,因此请注意不要使用netnsid其命名空间之外的名称空间。但是哪个唯一可识别的网络命名空间(inode编号)映射到哪个netnsid

事实证明,最新版本的lsns截至 2018 年 3 月,能够netnsid在其网络命名空间 inode 编号旁边显示正确的信息!所以有一种将 local 映射到命名空间 inode 的方法netnsid,但实际上是倒退的!它更像是一个预言(带有小写的 ell)而不是查找:RTM_GETNSID 需要一个网络名称空间标识符作为 PID 或 FD(到网络名称空间),然后返回netnsid.看https://stackoverflow.com/questions/50196902/retriving-the-netnsid-of-a-network-namespace-in-python有关如何询问 Linux 网络命名空间 oracle 的示例。

因此,您需要枚举可用的网络命名空间(通过/proc和/或/var/run/netns),然后对于给定的veth网络接口附加到您找到它的网络命名空间,询问您netnsid在开始时枚举的所有网络命名空间的 s (因为你事先永远不知道哪个是哪个),最后在附加到 的命名空间后,根据您在步骤 3 中创建的本地映射将对等点netnsid的映射到命名空间索引节点号。vethveth

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))

答案3

我创建了一个简单的脚本,列出了具有关联 veth 接口的所有容器:https://github.com/samos123/docker-veth/blob/master/docker-veth.sh

让我解释一下它是如何工作的:

  1. 查找容器的PID
pid=$(docker inspect --format '{{.State.Pid}}' $containerID)
  1. 使用输入网络命名空间nsenter
nsenter -t $pid -n ip a

eth0@ifX您会注意到容器网络命名空间内有一个接口。 X 告诉您主机网络上的接口索引。然后可以使用该索引来确定哪个 veth 属于该容器。

运行以下命令查找 veth 接口:

ifindex=$(nsenter -t $pid -n ip link | sed -n -e 's/.*eth0@if\([0-9]*\):.*/\1/p')
veth=$(ip -o link | grep ^$ifindex | sed -n -e 's/.*\(veth[[:alnum:]]*@if[[:digit:]]*\).*/\1/p')
echo $veth

包含更多详细信息的博客文章:http://samos-it.com/posts/enter-namespace-of-other-containers-from-a-pod.html

相关内容