일

"전체" 추측 없이 명시적으로 찾아야 합니다.동료다른 네트워크 네임스페이스의 veth 측에 있는 네트워크 인터페이스입니다.

이론. /. 현실

여기에는 많은 문서와 답변이 있지만 네트워크 인터페이스의 ifindex는 네트워크 네임스페이스의 호스트마다 전역적으로 고유하다고 가정합니다.이는 많은 경우에 사실이 아닙니다.:ifindex/iflink 희미한. 루프백도 그 반대를 보여주었습니다. 즉, 모든 네트워크 네임스페이스에서 ifindex가 1입니다. 또한, 컨테이너 환경에 따라ifindex숫자는 다른 네임스페이스에서 재사용됩니다.. 이로 인해 veth 연결을 추적하는 것이 악몽이 됩니다. 특히 veth 피어가 모두 @if3 정도로 끝나는 수많은 컨테이너 및 호스트 브리지의 경우 더욱 그렇습니다.

예: link-netnsid0

veth호스트 네트워크 네임스페이스에서 새 컨테이너 네트워크 네임스페이스로 새 쌍을 가져오기 위해 Docker 컨테이너 인스턴스를 시작합니다 .

$ sudo docker run -it debian /bin/bash

이제 호스트 네트워크 네임스페이스의 네트워크 인터페이스를 나열합니다(이 질문과 관련 없는 인터페이스는 생략했습니다).

$ IP 링크 표시
1: lo: mtu 65536 qdisc noqueue status 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 그룹 기본값
    link/ether02:42:34:23:81:f0 brd ff:ff:ff:ff:ff:ff
...
16: vethfc8d91e@if15: mtu 1500 qdisc noqueue master docker0 상태 UP 모드 DEFAULT 그룹 기본값
    link/etherda:4c:f7:50:09:e2 brd ff:ff:ff:ff:ff:ff link-netnsid 0

보시다시피 피어가 다른 네트워크 네임스페이스에 있더라도 명시적인iflink 0이 있습니다.link-netnsid

참고로 컨테이너의 명명되지 않은 네트워크 네임스페이스에서 netnsid를 확인하세요.

$ sudo lsns -t 네트워크
        NS 유형 NPROCS PID 사용자 명령
...
...
4026532469 네트워크 1 29616 루트/bin/bash

$ sudo nsenter -t 29616 -n IP 링크 표시
1: lo: mtu 65536 qdisc noqueue status 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 netnsnetnsid가 즉시 생성되지는 않지만 절반이 다른 네임스페이스(현재 네임스페이스에서는 "host"일 가능성이 높음)로 설정될 때마다 생성됩니다. 따라서 항상 일반적인 컨테이너에 대해 설정됩니다.

다음은 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 부분의 경우: 동일한 방법, 쿼리 메시지 유형은 다음과 같습니다.RTM_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 Peer, 브리지, 본드 등)에 있는 인터페이스에 대한 관계입니다. 그렇다면 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는 모호해 보일 수 있지만 독특한 점은 iflink 단독이 아닌 nsid와 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)을 확인하면 실제로 비교할 수 있습니다.

시스템 호출에 대해서는 여전히 추적 결과를 보면서 위에서 정보를 검색했습니다.RTM_GETLINK. 이제 모든 정보를 사용할 수 있습니다.

로컬: 인터페이스 인덱스SIOCGIFINDEX/if_nametoindex
피어: nsid 및 인터페이스 인덱스RTM_GETLINK.

이 모든 것이 아마도 다음과 결합되어야 할 것입니다.도서관.

답변2

2023년 업데이트:지멘스가 출시했습니다.가장자리 상어OSS로서 컨테이너, 호스트 등의 네트워크 인터페이스 간의 관계를 나타내는 아름다운 그래픽 웹 UI를 제공합니다. 더 많은 기능과 함께 이 답변에 설명된 접근 방식의 Go 기반 구현을 사용합니다.

특히 s 의 의미와 관련하여 누락된 부분을 채워준 @AB에게 큰 감사를 드립니다 netnsid. 그의 PoC는 매우 계몽적입니다. 그러나 PoC에서 누락된 핵심 부분은 netnsid로컬 네임스페이스를 전역적으로 고유한 네트워크 네임스페이스 inode 번호와 연결하는 방법입니다. 그래야만 올바른 해당 쌍을 명확하게 연결할 수 있기 때문입니다 veth.

ip netns종속성 및 마운트 필요성 없이 프로그래밍 방식으로 정보를 수집하는 방법에 대한 간단한 Python 예제를 요약하고 제공하기 위해 RTNETLINK는 네트워크 인터페이스를 쿼리할 때 실제로 netnsid를 반환합니다. IFLA_LINK_NETNSID필요할 때만 연결된 정보에 나타나는 속성 입니다 . 존재하지 않으면 필요하지 않습니다. 그리고 피어 인덱스가 네임스페이스 로컬 네트워크 인터페이스를 참조한다고 가정해야 합니다.

기억해야 할 중요한 교훈 netnsidIFLA_LINK_NETSID현지의링크 정보를 위해 RTNETLINK를 요청할 때 얻은 네트워크 네임스페이스에 정의됩니다. 다른 네트워크 네임스페이스에서 얻은 동일한 값이 다른 피어 네임스페이스를 식별할 수 있으므로 자체 네임스페이스 외부를 netnsid사용하지 않도록 주의하세요 . netnsid그러나 고유하게 식별 가능한 네트워크 네임스페이스( inode번호)는 어느 것에 매핑됩니까 netnsid?

최신 버전인 것으로 밝혀졌습니다.lsns2018년 3월 현재 에서는 netnsid네트워크 네임스페이스 inode 번호 옆에 올바른 정보를 표시할 수 있습니다! 에프로컬을 네임스페이스 inode로 매핑하는 방법이지만 netnsid실제로는 그 반대입니다! 이는 조회라기보다는 오라클(소문자 ell 사용)에 가깝습니다. RTM_GETNSID는 네트워크 네임스페이스 식별자를 PID 또는 FD(네트워크 네임스페이스에 대한)로 가져와서 반환합니다 netnsid.https://stackoverflow.com/questions/50196902/retriving-the-netnsid-of-a-network-namespace-in-pythonLinux 네트워크 네임스페이스 oracle을 쿼리하는 방법의 예입니다.

따라서 사용 가능한 네트워크 네임스페이스( /proc및/또는 를 통해)를 열거한 다음 찾은 네트워크 네임스페이스에 연결된 /var/run/netns특정 네트워크 인터페이스에 대해 처음에 열거한 모든 네트워크 네임스페이스의 s를 요청해야 합니다((왜냐하면 절대 어느 것인지 미리 알고) 마지막으로 네임스페이스에 연결한 후 3단계에서 생성한 로컬 매핑에 따라 피어를 네임스페이스 인덱스 노드 번호에 매핑합니다.vethnetnsidnetnsidvethveth

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

관련 정보