web-dev-qa-db-ja.com

コンテナーのインターフェースを含め、Linuxで構成されているすべてのインターフェースを見つけるにはどうすればよいですか?

ip a showを実行すると、インターフェースを表示できることを知っています。ホストが表示できるインターフェイスのみが表示されますが、コンテナによって構成された仮想インターフェイスはこのリストに表示されません。 ip netnsも使用してみましたが、どちらも表示されません。 iproute2の別のバージョンを再コンパイルする必要がありますか? /proc/net/fb_trieでは、転送データベースの用途として、のローカル/ブロードキャストアドレスを確認できます。

この情報、またはコンテナを含むすべてのインターフェイスを一覧表示するコマンドはどこにありますか?

これをテストするには、コンテナを起動します。私の場合、それはスナップ上のlxcコンテナーです。 ip aまたはip lを実行します。ホストマシンのビューが表示されますが、コンテナ構成のインターフェースは表示されません。コンテナはcgroupedプロセスであるため、procfsをgrepしていますが、fib_trieとarpエントリ以外は取得しません。 netns名前空間の難読化が原因である可能性があると思いましたが、ip netnsも何も表示しません。

Lxdはパケットの転送を追跡する接続が必要なため、conntrack -Lを使用して、確立されたすべての着信接続と発信接続を表示できますが、システムで構成されているすべてのIPアドレスをリストします。 netstatまたはlsofを使用して見分けることができます。

5
munchkin

ある時点で、インターフェースは1つに属します ネットワーク名前空間 そして1つだけです。破棄されたネットワーク名前空間の物理インターフェイスを継承することを除いて、初期(初期)ネットワーク名前空間には他のネットワーク名前空間に対する特別な機能はありません。それらのインターフェイスを直接見ることはできません。 initのpidおよびmount名前空間にいる限り、/procから入手できるさまざまな情報を使用してネットワーク名前空間を検索し、最後にそれらのネットワーク名前空間を入力してインターフェイスを表示できます。

シェルで例を示します。

  • ネットワーク名前空間を列挙します

    このためには、それらの名前空間がどのように存在するかを知る必要があります。リソースがそれらを維持している限り。ここでのリソースは、プロセス(実際にはプロセスのスレッド)、マウントポイント、またはオープンファイル記述子(fd)です。これらのリソースはすべて/proc/で参照され、すべての名前空間を列挙するnsfs疑似ファイルシステムの抽象疑似ファイルを指します。このファイルの唯一の意味のある情報は、ネットワークの名前空間を表すiノードですが、iノードを単独で操作することはできません。ファイルでなければなりません。そのため、後でiノード値(stat -c %i /proc/some/fileで指定)だけを保持することはできません。重複およびファイル名を削除できるようにiノードを保持します。 nsenter 後で使用できる参照を引き続き保持します。

    • プロセス(実際にはスレッド)

      最も一般的なケース:通常のコンテナの場合。各スレッドのネットワーク名前空間は、参照/proc/pid/ns/netを介して知ることができます。statだけで、すべての一意の名前空間を列挙します。 2>/dev/nullは、statが一時的なプロセスを見つけることができなくなったときに非表示にします。

      find /proc/ -mindepth 1 -maxdepth 1 -name '[1-9]*' | while read -r procpid; do
              stat -L -c '%20i %n' $procpid/ns/net
      done 2>/dev/null
      

      これは、名前空間を処理する特殊な lsns コマンドを使用すると、より高速に実行できますが、プロセスのみを処理するように見えます(後で説明するようにマウントポイントやオープンfdは処理しません)。

      lsns -n -u -t net -o NS,PATH
      

      (後でlsns -n -u -t net -o NS,PATH | while read inode path; do printf '%20u %s\n' $inode "$path"; doneとして再フォーマットする必要があります)

    • マウントポイント

      これらは主にip netns addコマンドによって使用されます。これは、それらをマウントすることによって永続的なネットワーク名前空間を作成します。これにより、プロセスやfdリソースがそれらを維持しない場合に表示されなくなり、ルーター、ファイアウォール、ブリッジなどを実行できるようになります。リンクされたプロセスのないネットワーク名前空間。

      マウントされた名前空間(mountおよびおそらくpid名前空間の処理はおそらくもっと複雑ですが、とにかくネットワーク名前空間にのみ関心があります)は、ファイルシステムタイプがnsfs/proc/mountsの他のマウントポイントと同じように表示されます。 Shellでは、ネットワーク名前空間を他のタイプの名前空間と簡単に区別する方法はありませんが、同じファイルシステム(ここではnsfs)の2つの疑似ファイルは同じiノードを共有しないため、すべてを選択し、非ネットワーク名前空間参照をネットワーク名前空間として使用しようとするときに、インターフェイス手順の後半でエラーを無視します。申し訳ありませんが、スペースを含む特殊文字が含まれているマウントポイントは、/proc/mountsの出力で既にエスケープされているため(他の言語では簡単です)、正しくマウントポイントを処理できません。 nullで終了する行を使用します。

      awk '$3 == "nsfs" { print $2 }' /proc/mounts | while read -r mount; do
              stat -c '%20i %n' "$mount"
      done
      
    • ファイル記述子を開く

      これらは、一時的に名前空間の作成時を除いて、マウントポイントよりもさらにまれである可能性がありますが、コンテナ化テクノロジなど、複数の名前空間を処理する特殊なアプリケーションによって保持および使用される場合があります。

      すべての/proc/pid/fd/で利用可能なすべてのfdを検索し、statを使用してnsfs名前空間を指していることを確認し、それが本当にネットワーク名前空間であるかどうかを今のところ気にしないよりも良い方法を考案することはできませんでした。私はもっ​​と最適化されたループがあると確信していますが、これは少なくともどこにでもさまよったり、プロセスの最大制限を想定したりすることはありません。

      find /proc/ -mindepth 1 -maxdepth 1 -name '[1-9]*' | while read -r procpid; do
              find $procpid/fd -mindepth 1 | while read -r procfd; do
                      if [ "$(stat -f -c %T $procfd)" = nsfs ]; then
                              stat -L -c '%20i %n' $procfd 
                      fi
              done
      done 2>/dev/null
      

    ここで、以前の結果から重複するネットワーク名前空間参照をすべて削除します。たとえば、前の3つの結果(特に、開いているファイル記述子の部分)を組み合わせた出力でこのフィルターを使用すると、次のようになります。

    sort -k 1n | uniq -w 20
    
  • 各名前空間でインターフェースを列挙します

    これで、すべての既存のネットワーク名前空間(および無視するいくつかの非ネットワーク名前空間)への参照ができました。参照を使用してそれぞれに入力し、インターフェースを表示します。

    前に説明したように、ネットワーク以外の名前空間によって引き起こされたエラーを無視しながら、前のコマンドの出力をこのループへの入力として取得して、インターフェイスを列挙します(OPの質問に従って、アドレスを表示することを選択します)。

    while read -r inode reference; do
        if nsenter --net="$reference" ip -br address show 2>/dev/null; then
                printf 'end of network %d\n\n' $inode
        fi
    done
    

Initネットワークのiノードは、参照としてpid1を使用して印刷できます。

echo -n 'INIT NETWORK: ' ; stat -L -c %i /proc/1/ns/net

実行中のLXCコンテナを使用した(実際の編集済みの)出力の例、未接続のブリッジインターフェースを持つip netns add ...で作成された空の「マウントされた」ネットワーク名前空間、他のdummy0インターフェースを持つネットワーク名前空間、このネットワーク名前空間ではプロセスではないが、その上にオープンfdを保持します。

unshare --net sh -c 'ip link add dummy0 type dummy; ip address add dev dummy0 10.11.12.13/24; sleep 3' & sleep 1; sleep 999 < /proc/$!/ns/net &

および接続されていないネットワーク名前空間(それらすべてのloインターフェース)の「Webコンテンツ」スレッドのそれぞれを分離する実行中のFirefox:

 lo UNKNOWN 127.0.0.1/8 :: 1/128 
 eth0 UP 192.0.2.2/24 2001:db8:0:1:bc5c:95c7:4ea6:f94f/64 fe80 :: b4f0 :7aff:fe76:76a8/64 
 wlan0 DOWN 
 dummy0 UNKNOWN 198.51.100.2/24 fe80 :: 108a:83ff:fe05:e0da/64 
 lxcbr0 UP 10.0.3.1/ 24 2001:db8:0:4 :: 1/64 fe80 :: 216:3eff:fe00:0/64 
 virbr0 DOWN192.168.122.1/24
virbr0-nicDOWN 
 vethSOEPSH @ if9 UP fe80 :: fc8e:ff:fe85:476f/64 
ネットワークの終わり4026531992 
 
 lo DOWN 
ネットワークの終わり4026532418 
 
 lo DOWN 
 end of network 4026532518 
 
 lo DOWN 
 end of network 4026532618 
 
 lo DOWN 
ネットワークの終わり4026532718 
 
 lo UNKNOWN 127.0.0.1/8 :: 1/128 
 eth0 @ if10 UP 10.0.3.66/24 fe80 :: 216:3eff:fe6a:c1e9/64 
 end of network 4026532822 
 
 lo DOWN 
 bridge0 UNKNOWN fe80: :b884:44ff:feaf:dca3/64 
ネットワークの終わり4026532923 
 
 lo DOWN 
 dummy0 DOWN10.11.12.13/24
endof networkネットワーク4026533021 
 
 INIT NETWORK:4026531992 
3
A.B

_ip netns list_は、ip-netns(8)ユーティリティを介して構成されたネットワーク名前空間のみをリストします。

_util-linux_パッケージのlsns(1)プログラムは、_/proc/<pid>/ns/_ファイルを介してアクセスできるネームスペースのみをリストし、バインドマウントまたは開いているファイル記述子によって保持されているネームスペースを省略します。

これは明らかに不十分です。次のデモスクリプトは、これを修正しようとします。_/proc/<pid>/mountinfo_ファイルを介してバインドマウントを検索し、_/proc/<pid>/fd_ファイルを介して開いているfdsを検索します。

名前空間ごとに、アクセス可能なパスを出力します。

_# Perl ./lsnsx.pl
...
mnt      3
   4026531840  /proc/1/ns/mnt
   4026531860  /proc/30/ns/mnt
   4026532374  /proc/3119/ns/mnt
net      6
   4026531992  /proc/1/ns/net
   4026532376  /proc/25781/fd/9
   4026532465  /proc/28373/fd/7
...
_

次に、そのパスをnsenter(1)で使用できます。

_nsenter --net=/proc/28373/fd/7 ip link
_

スクリプトは、それ自体を実行するように簡単に変更したり、名前空間を使用するプロセスのリスト全体などの他の情報を表示したりできます。

パスにアクセスできない場合は、親/マウントIDと、パスが見つかった_/proc/<pid>/mountinfo_ファイルが続きます。エスケープされた改行、タブ、スペースはそのままになります。

_net      9
   ...
   4026532732  /v/net\040ns   /proc/3119/mountinfo 60 41
_

_lsnsx.pl_:

_#! /usr/bin/Perl
use strict;
my %nstype = (
    # the CLONE_NEW* from sched.h
    0x02000000 => "cgroup", 0x04000000 => "uts", 0x08000000 => "ipc",
    0x10000000 => "user", 0x20000000 => "pid", 0x40000000 => "net",
    0x00020000 => "mnt" # CLONE_NEWNS
);
my %ns;
sub unescape { $_[0] =~ s/\\([0-7]{3})/chr oct $1/ger }
sub devino { join ".", (stat unescape $_[0])[0, 1] }
my $nsfs_dev = (stat "/proc/self/ns/mnt")[0];
for(</proc/{[0-9]*/{ns/*,fd/*},[0-9]*/mountinfo}>){
    if(my ($procpid) = m{^(.*)/mountinfo$}){
        open my $h, $_ or next;
        LOOP: while(<$h>){
            next unless (my @s = split)[2] eq "0:$nsfs_dev";
            if(my($t, $i) = $s[3] =~ /^(\w+):\[(\d+)\]$/){
                next if exists $ns{$t}{$i};
                for my $p ($s[4], "$procpid/root$s[4]"){
                    if(devino($p) eq "$nsfs_dev.$i"){
                        $ns{$t}{$i} = $p; next LOOP
                    }
                }
                $ns{$t}{$i} = "@s[4, 0, 1] $procpid/mountinfo"
            }
        }
    }elsif(m{/ns/}){
        $ns{$1}{$2} //= $_ if readlink =~ /^(\w+):\[(\d+)\]$/;
    }else{
        use constant NS_GET_NSTYPE => 0xb7 << 8 | 3;
        next unless my ($dev, $ino) = stat $_;
        next unless $dev == $nsfs_dev;
        next unless open my $h, $_;
        next unless my $t = ioctl $h, NS_GET_NSTYPE, 0;
        $ns{$nstype{$t} // '???'}{$ino} //= $_;
    }
}
for(sort keys %ns){
    my $h = $ns{$_}; my @i = sort {$a<=>$b} keys %$h;
    printf "%-8s %d\n", $_, scalar @i;
    printf "   %-11d %s\n", $_, $$h{$_} for @i;
}
_
0
mosvy