web-dev-qa-db-ja.com

vethピアifindexのネットワーク名前空間を見つける方法は?

仕事

「ホリスティック」な推測をせずに、他のネットワーク名前空間にあるvethエンドのpeerネットワークインターフェースを見つける必要があります。

理論./。現実

ドキュメントはたくさんありますが、ここでもSOネットワークインターフェースのifindexインデックスは、ネットワーク名前空間全体でホストごとにグローバルに一意であると想定していますこれは多くの場合保持されませんifindex/iflinkあいまい。ループバックでさえ、これとは逆に、ネットワークネームスペースでifindexが1になっています。また、コンテナ環境によっては、ifindex数値は別の名前空間で再利用されます。これにより、特に多くのコンテナーとすべてが@ if3で終わるvethピアを持つホストブリッジを使用して、veth配線のトレースが悪夢になります...

例:link-netnsid0

Dockerコンテナーインスタンスを起動して、ホストネットワーク名前空間から新しいコンテナーネットワーク名前空間に接続する新しいvethペアを取得します...

 $ Sudo docker run -it debian /bin/bash

次に、ホストネットワークの名前空間にネットワークインターフェイスの一覧を表示します(この質問に関係のないインターフェイスは省略しました)。

 $ ip link show 
 1:lo:mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 
 link/loopback 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 
 link/ether 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 
 link/ether da:4c:f7:50:09:e2 brd ff:ff:ff:ff:ff:ff link-netnsid 0 

ご覧のとおり、iflinkは明確ですが、link-netnsidは0ですが、ピアエンドは別のネットワーク名前空間にあります。

参考までに、コンテナの名前のないネットワーク名前空間のnetnsidを確認してください。

 $ Sudo lsns -t net 
 NS TYPE NPROCS PID USER COMMAND 
 ... 
 ... 
 4026532469 net 1 29616 root /bin/bash

$ Sudo nsenter -t 29616 -n ip link show 
 1:lo:mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT groupデフォルトqlen 1000 
リンク/ループバック00:00:00:00:00:00 brd 00:00:00:00:00:00 
 15:eth0 @ if16:mtu 1500 qdisc noqueue state UPモードデフォルトグループデフォルト
 link/ether 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 /if/ ...を調べましたが、ifindex要素とiflink要素しか見つかりません。これらは十分に文書化されています。 「ip link show」も、有名な「@ if#」表記の形式でピアifindexのみを表示するようです。それとも、追加のネットワーク名前空間要素を見逃しましたか?

結論/質問

Vethペアのピアエンドの不足しているネットワーク名前空間情報を取得できるsyscallはありますか?

5
TheDiveO

特にnetnsidsのセマンティクスに関して、欠けている部分を埋めてくれた@ A.Bに感謝します。彼のPoCは非常に有益です。ただし、PoCで欠けている重要な要素は、ローカルnetnsidをグローバルに一意のネットワーク名前空間のiノード番号に関連付ける方法です。これにより、対応する正しいvethペアを明確に接続できます。

要約して小さなPythonの例:ip netnsに依存せずにプログラムで情報を収集する方法と、物をマウントする必要性の例:RTNETLINKは、ネットワークインターフェースのクエリ時に実際にnetnsidを返します。 。これはIFLA_LINK_NETNSID属性であり、必要なときにリンクの情報にのみ表示されます。存在しない場合は必要ありません。ピアインデックスが名前空間ローカルネットワークインターフェースを参照していると想定する必要があります。

家に持ち帰る重要な教訓は、netnsid/IFLA_LINK_NETSIDは、取得したネットワーク名前空間内でのみlocally定義されていることですRTNETLINKにリンク情報を要求する場合に使用します。異なるネットワーク名前空間で取得した同じ値のnetnsidは、異なるピア名前空間を識別する可能性があるため、名前空間の外でnetnsidを使用しないように注意してください。しかし、一意に識別可能なネットワーク名前空間(inode番号)は、どのnetnsidにマップされますか?

結局のところ、2018年3月現在の lsns の最新バージョンでは、ネットワークネームスペースのiノード番号の横に正しいnetnsidを表示できます。したがって、ローカルnetnsidsを名前空間のiノードにマップする方法はisですが、実際には逆です!そして、それはルックアップというよりもOracle(小文字のエルを持つ)です。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ネットワークインターフェイスについて、それが見つかったネットワーク名前空間に接続する必要があります。最初に列挙したすべてのネットワーク名前空間のnetnsidsを要求し(これは、Beforehandがどれであるかわからないため)、最後にnetnsidピアのvethvethの名前空間にアタッチした後、手順3で作成したローカルマップごとの名前空間のiノード番号。

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

この問題を理解する方法を見つけるために私がたどった方法は次のとおりです。利用可能なツールは名前空間部分に(ある程度のたたみ込みで)使用可能であるように見え、(更新済み)/ sys /を使用すると、ピアのインデックスを簡単に取得できます。かなり長いので、我慢してください。カスタムプログラムではなく、一般的なツールを使用して、2つの部分に分かれています(論理的な順序ではありませんが、名前空間は最初にインデックスの名前を説明するのに役立ちます)。

  • ネットワーク名前空間
  • インターフェイスインデックス

ネットワーク名前空間

この情報は、_link-netnsid_の出力のプロパティ_ip link_で利用でき、_ip netns_の出力のIDと照合できます。コンテナのネットワーク名前空間を_ip netns_に「関連付ける」ことができるため、特殊なツールとして_ip netns_を使用できます。もちろん、これのために特定のプログラムを実行する方が良いでしょう(各部分の最後にあるsyscallに関するいくつかの情報)。

Nsidの説明について、ここに_man ip netns_が伝えていること(私の強調):

ip netns set NAME NETNSID-ピアネットワーク名前空間にIDを割り当てます

このコマンドは、ピアネットワーク名前空間にIDを割り当てます。このIDは、現在のネットワーク名前空間でのみ有効です。 このIDは、一部のnetlinkメッセージでカーネルによって使用されます。カーネルがIDを必要とするときに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
_

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で始まり、使用可能な最小値は新しい名前空間でリサイクルされます。

Syscallsの使用について、straceから推測される情報は次のとおりです。

  • リンク部分の場合: _AF_NETLINK_ ソケット(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)で開かれる)を要求し、( sendmsg() )を要求しますメッセージタイプ _RTM_GETLINK_ のリンクの情報と、メッセージタイプ_RTM_NEWLINK_の返信を取得( recvmsg() )します。

  • netns nsidパートの場合:同じメソッド、クエリメッセージはタイプ _RTM_GETNSID_ で応答タイプ_RTM_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_のバグだと思います。ピアリンクを2回移動するだけで、ここで「解決」されます。これは、インデックスの変更を強制したためです。また、_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
_

[〜#〜] update [〜#〜]:OPの質問の情報を再度読み取ると、ピアのインデックス(nsidではない)が簡単かつ明確に利用できます_cat /sys/class/net/_interface_/iflink_を使用します。

UPDATE2

これらすべての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が2つ存在することはありません。

各ピアネットワークから反対の情報を見る場合:

_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、すべてが_ip netns_によってtestと呼ばれるネット名前空間にマップされる)であることが確認されたら、実際に比較できます。 。

Syscallsについて、引き続きstraceの結果を確認すると、情報は上記のように _RTM_GETLINK_ から取得されます。これで、すべての情報が利用できるはずです。

ローカル: SIOCGIFINDEX / _if_nametoindex_ のインターフェイスインデックス
peer: _RTM_GETLINK_ を使用したnsidとインターフェイスインデックスの両方。

これはおそらく libnl と一緒に使用する必要があります。

5
A.B

関連する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

0
Sam Stoelinga