rtnetlink で物理 ethernet リンクをフィルタする方法

rtnetlink の RTM_GETLINK ダンプはシステム上のすべてのネットワークインターフェースを返す:ループバック、物理 NIC、ブリッジ、veth ペア、VLAN、WireGuard トンネル、TUN デバイスなど。物理有線 Ethernet アダプタだけが欲しい場合、3つのカテゴリをフィルタで除去する必要がある:

  1. 非 Ethernet リンクタイプ — ループバック(ARPHRD_LOOPBACK)、WireGuard/TUN(ARPHRD_NONE)など。
  2. 仮想 Ethernet インターフェース — ブリッジ、veth、VLAN、bond、macvlan など。これらはすべて ifi_type == ARPHRD_ETHER を報告するが、kind を識別する IFLA_LINKINFO 属性を持つ。
  3. ワイヤレスインターフェース — Wi-Fi アダプタも ARPHRD_ETHER を報告し IFLA_LINKINFO を持たないため、netlink だけでは有線 Ethernet と区別できない。

この記事では3つのフィルタすべてを適用し、物理有線 Ethernet リンクのみを出力するコンパクトな C++ プログラムを示す。

3つのフィルタ

フィルタ1:ifi_type == ARPHRD_ETHER

struct ifinfomsg の ifi_type フィールドは ARP ハードウェアタイプを保持。有線 Ethernet、Wi-Fi、ブリッジ、veth はすべて ARPHRD_ETHER(1)を報告。ループバックは 772(ARPHRD_LOOPBACK)、トンネルインターフェース(WireGuard、TUN)は 65534(ARPHRD_NONE)を報告。このフィルタは L2 で Ethernet フレーム化されないすべてを除去する。

フィルタ2:IFLA_LINKINFO なし

仮想 Ethernet インターフェース——ブリッジ(Kind: bridge)、veth(Kind: veth)、VLAN(Kind: vlan)、bond(Kind: bond)、macvlan など——はすべて、内部に IFLA_INFO_KIND を持つネストされた IFLA_LINKINFO 属性を持つ。物理インターフェース(有線・無線両方)は IFLA_LINKINFO を一切持たない。

つまり:属性リストに IFLA_LINKINFO が存在すれば、そのインターフェースは仮想でありスキップすべき。

フィルタ3:/sys/class/net/<name>/wireless なし

Wi-Fi アダプタは IFLA_LINKINFO を持たない物理デバイスなので、フィルタ1と2を通過する。カーネルは rtnetlink リンクダンプで「ワイヤレスかどうか」フラグを公開しない(IFLA_WIRELESS 属性は wireless extension イベントでのみ送信され、ダンプ応答には含まれない)。

Wi-Fi を検出する確実な方法は /sys/class/net/<name>/wireless の存在確認——カーネルはこのディレクトリをワイヤレスインターフェースにのみ作成する。候補インターフェースごとに stat() 呼び出しが必要だが、通常はごく少数なので無視できる。

プログラム

注意:<linux/if_arp.h> と <net/if.h> は両方とも IFF_* フラグを定義し、一緒にインクルードすると衝突する。このプログラムは <linux/if_arp.h> の ARPHRD_ETHER のみ必要なので <net/if.h> は省略。IFF_* フラグ定数が必要なら <net/if.h> のみをインクルードし、ARPHRD_ETHER を 1 とハードコードする。

ビルドと実行

特別な権限は不要。

出力例

ループバック、Wi-Fi、複数の Docker ブリッジと veth ペア、WireGuard トンネル、1つの USB Ethernet アダプタがあるホストで、プログラムは物理有線 Ethernet リンクのみを出力:

比較として、同じホストの完全な rtnetlink ダンプには30以上のインターフェースがある——すべて上記3つのチェックで除去される。

なぜインターフェース名だけをチェックしないのか?

インターフェース名は慣例に従う(enX = 有線 Ethernet、wlX = Wi-Fi、brX = ブリッジ、vethX = veth ペア)が、これは慣例であり保証ではない。USB Ethernet アダプタはデフォルトで enx<MAC> を使うが、udev ルールや ip link set name でリネームできる。IFLA_LINKINFO + sysfs アプローチは、ユーザーが付けた名前ではなくカーネルがインターフェースについて報告するものを検査するため、リネームに対して堅牢。

このフィルタの派生元である完全なリンクダンププログラムは rtnetlink API ですべてのネットワークリンクを一覧する方法、補完となる VLAN フィルタは rtnetlink で VLAN インターフェースをフィルタする方法、属性走査イディオムは rtnetlink rtattr 属性を反復する方法 も参照。


同じカテゴリの類似記事をチェック: C/C++ Linux