Debian vs Ubuntu: ネットワークインタフェースの名前

Debian vs Ubuntu Logo

Debian と Ubuntu の違いについて深追いする超不定期連載第 2 回は ネットワークインタフェースの名前の付けかたについてです (なお第 1 回は 1 年半くらい前の記事 でした)。

今回は Debian/Ubuntu ばかりでなく Red Hat 系のディストリビューションも深追いしました。

2014/12/31 更新: Moriwaka さんのご指摘 をいただき Red Hat 系ディストリビューションの記述を修正しました。 biosdevname は RHEL 6.1 から導入されています。

永続的な名前・予測可能な名前

Linux カーネルのネットワークインタフェースは、 ドライバが検出した順に eth0 eth1 … といった名前がつけられるようになっています。

ただしこれだけでは各デバイスがプローブに応答するタイミングのずれや、 USB Ethernet などのホットプラグデバイスの存在によって、 起動毎に異なる名前がついてしまうという問題があります。 これに対処するため、 各 Linux ディストリビューションではカーネルがつけた名前を ユーザーランドで適切な名前に書き換える機能を提供しています。

従来のディストリビューションでは デバイスの MAC アドレスなどの属性と名前の対応を設定ファイルに保持することで 永続的な名前 (persistent names) をつけることが行われてきました。

その一方で、最近のディストリビューションでは システムのファームウェア/BIOSの登録情報、バス接続情報を元に生成した、 予測可能な名前 (predictable names) をつける機能を提供するようになっています。 例えば最近の Ubuntu ではオンボードのネットワークインタフェースには em1、 PCI スロットに増設したものには p4p1 といった名前がつけられます。

従来の馴染み深い eth0 eth1 といった名前から離れて 予測可能な名前を導入するねらいについては systemd/udev の wiki ページ Predictable Network Interface Names の説明が参考になります。

予測可能な名前のメリットとしてわかりやすい例は、 故障したハードウェアを交換してネットワークインタフェースの MAC アドレスが変わった場合でも同じ名前を維持できるといったことでしょう。 組み込み系のシステムでもメリットがありそうです。 永続的な名前の方式ではホストごとに設定ファイルをメンテナンスする作業が避けられません。

一方でデメリットとしては、 従来の名前 eth0 eth1 などを利用したシステム設定や管理 (iptables のルールなど) が使えなくなり、 それらを様々な構成のホスト間で共通化することも難しくなることなどがあげられます。

このように各方式には一長一短があるので、 新しいものを良いものとして無条件に受け入れるのではなく、 ネットワーク環境やサービスの種類、歴史的経緯を考慮して適切な方式を選択するのがよいでしょう。

ただ、永続的な名前・予測可能な名前それぞれに複数の実装が存在し、 ディストリビューションによってその組み合わせが異なるため、 現時点で大小さまざまな混乱を招いているように見えますので、 この記事ではそれらについて調査・整理してみました。

自分は普段 Red Hat 系のディストリビューションは使っていないのですが、 今回はこれまでの経緯やトレンド全体を把握するために CentOS 6/7 もインストールして調べてみました。 なにか間違いや見落としがあったら指摘いただけると助かります。

実装

概要

現在、ネットワークインタフェースの名前設定によく用いられる実装には次のものがあります。

実装 種類 Debian 7 Debian 8 Ubuntu 12.04 Ubuntu 14.04 Ubuntu 14.10 CentOS 6 CentOS 7
write_net_rules 永続的  
rename_device 永続的          
biosdevname 予測可能      
systemd/udev 予測可能        

凡例: ◎ … 利用可能、デフォルトで有効 / ○ … 利用可能

いずれの実装も udev フレームワークの中で rules スクリプトと共に利用されます。

write_net_rules

write_net_rules は従来より各ディストリビューションで使われてきた udev に含まれる実装です。

udev rules スクリプト 75-persistent-net-generator.rules によって呼び出され、 /etc/udev/rules.d/70-persistent-net.rules というファイルに MAC アドレスと割り当てた名前の組を登録します。 次は登録されたネットワークインタフェースの例です。

# PCI device 0x14e4:0x1639 (bnx2)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:22:19:XX:XX:XX", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0"

# PCI device 0x14e4:0x1639 (bnx2)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:22:19:YY:YY:YY", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth1"

各行の ATTR{address}=="..." で対象のネットワークインタフェースを MAC アドレスで指定し、 NAME="..." で新しい名前を設定しています。 これは eth0 eth1 といった標準的な名前だけでなく、 internet0 とか lan0 のようなわかりやすい名前でもかまいません。

書き出された 70-persistent-net.rules は次回以降の起動で直接実行されます。 このファイルで名前が決まったネットワークインタフェースについては、 その後で実行される 75-persistent-net-generator.rules は何もしません。

新しいネットワークインタフェースを追加した場合、 既存のものと同じ名前がつけられることはないため、 ハードウェアの故障で交換した場合など、 ネットワークインタフェースがひとつしかないのに その名前が eth0 でなく eth1 や eth2 になっていることがよくあります。

この状態を解消するには /etc/udev/rules.d/70-persistent-net.rules ファイルを編集するか、 あるいは単純に消してしまってからシステムを再起動します。

rename_device

rename_device は Red Hat 系のディストリビューションの initscripts パッケージに含まれる実装です。

udev rules スクリプト 60-net.rules によって呼び出され、 次の /etc/sysconfig/network-scripts/ifcfg-eth0 のようなネットワークインタフェース設定スクリプトを読み取ります。

DEVICE="eth0"
ONBOOT=yes
BOOTPROTO=dhcp
HWADDR="XX:XX:XX:XX:XX:XX"

スクリプト中の HWADDR= がインタフェースの MAC アドレスにマッチするものが存在する場合、 そのインタフェースを DEVICE= の名前に変更します。

biosdevname

biosdevname は DELL による予測可能な名前の実装です。

biosdevname プログラムはシステムのファームウェア/BIOS を読み取り ネットワークインタフェースの素性を割り出し、適切な名前を提案します。 /dev/mem にアクセスするため実行には root 権限が必要になります。

マニュアルによれば次のような命名規則になっています。 なお USB Ethernet のような非 PCI バスのデバイスには対応していません。

名前 説明
em<port> オンボード (embedded) の <port> 番目のネットワークインタフェース
p<slot>p<port> PCI バススロット <slot> の <port> 番目のネットワークインタフェース

biosdevname は udev rules スクリプト 71-biosdevname.rules から呼び出されます。

biosdevname の機能はカーネルコマンドラインで制御できます。 biosdevname=0 で無効に、 biosdevname=1 に有効になります。

systemd/udev

systemd バージョン 197 より ネットワークインタフェースに予測可能な名前をつける機能を udev 自身が提供するようになりました (freedesktop.org wiki を参照)。

この実装では libudev のビルトイン net_id が各ネットワークインタフェースについていくつかの名前の候補を udev の変数として生成します。 その規則は biosdevname のものとはだいぶ異なっていますが、 USB Ethernet も含む広範な種類のデバイスをカバーしています。 詳しくは net_id のソースコードを見てください。

名前 説明
ID_NET_NAME_ONBOARD eno1 ファームウェア/BIOS に登録されたオンボードデバイスの番号
ID_NET_NAME_SLOT ens1 ファームウェア/BIOS に登録された PCI ホットプラグスロットの番号
ID_NET_NAME_PATH enp2s0 システムバスの接続順
ID_NET_NAME_MAC enx78e7d1ea46da MAC アドレス

udev rules スクリプト 80-net-setup-link.rules (バージョン 208 以前は 80-net-name-slot.rules) はこの表の上から順に最初に利用可能なものを探して名前を決めます。 ただし ID_NET_NAME_MAC を使うにはスクリプトの書き換えが必要です。

net_id を実際のデバイスでテストするには udevadm test-builtin コマンドが使えます。 次の実行例は Ubuntu 14.04 のデスクトップ (systemd バージョン 204) で試しました。

$ udevadm test-builtin net_id /sys/class/net/em1
...
ID_NET_NAME_MAC=enxd43d7eXXXXXX
ID_OUI_FROM_DATABASE=Micro-Star Int'l Co, Ltd
ID_NET_NAME_ONBOARD=eno1
ID_NET_LABEL_ONBOARD=en LAN1
ID_NET_NAME_PATH=enp0s25
...

このネットワークインタフェースは udevd 式の命名規則では em1 でなく eno1 が使われることがわかります (ID_NET_NAME_ONBOARD が優先されるので)。 一方で適当な USB Ethernet デバイスを挿して試してみたときは次のようになりました。

$ udevadm test-builtin net_id /sys/class/net/eth0
...
ID_NET_NAME_MAC=enx00018eXXXXXX
ID_OUI_FROM_DATABASE=Logitec Corporation
ID_NET_NAME_PATH=enp0s26u1u1u4
...

このネットワークインタフェースは enp0s26u1u1u4 という名前になるのでしょう (長い!)。 当然のことながらこのデバイスを異なる USB ポートに挿せば また違った名前になります。

systemd/udev の予測可能な名前の機能はデフォルトで有効になっていますが、 カーネルコマンドラインに net.ifnames=0 と書くことで無効にできます。 なお Debian/Ubuntu ではデフォルトでは無効になるように変更されています (後述)。

各実装の混合使用

各実装はすべて udev フレームワークの rules スクリプトがトリガとなって実行されます。 各実装の rules スクリプトをまとめると次のようになります。

udev rules スクリプト 実装
/lib/udev/rules.d/60-net.rules rename_device
/etc/udev/rules.d/70-persistent-net.rules write_net_rules が生成
/lib/udev/rules.d/71-biosdevname.rules biosdevname
/lib/udev/rules.d/75-persistent-net-generator.rules write_net_rules
/lib/udev/rules.d/80-net-name-slot.rules systemd/udev (バージョン 208 以前)
/lib/udev/rules.d/80-net-setup-link.rules systemd/udev (バージョン 209 以降)

カーネルがデバイスを検出すると、ユーザーランドの udevd は /lib/udev/rules.d/ および /etc/udev/rules.d/ に格納された rules スクリプトを 名前の辞書順で順次実行していきます。

どの実装も、 自分が呼び出されるより先にネットワークインタフェースの名前が決定していた場合には それを上書きしないことになっています。 これにより、既存のシステムからアップグレードした場合でも ネットワークインタフェースの名前が突然変わってしまうことがないように考慮されています。

ディストリビューション

Debian

Debian はこれまでのところ予測可能な名前の導入は消極的です。 systemd/udev からは write_net_rules による永続的な名前のサポートが消えたにもかかわらず、 独自にこれを追加してデフォルトで有効にしています。

Debian 8 (jessie) において systemd パッケージはバージョン 215 と十分に新しいものに更新されていますが、 systemd/udev の予測可能な名前の機能はデフォルトで無効になるようにしてあります。 カーネルコマンドラインに net.ifnames=1 を渡すことで、 これを有効にすることができます。

Debian では biosdevname のサポートはなく、パッケージも存在しません。

Ubuntu

Ubuntu は Debian に比べて予測可能な名前の導入に積極的で、 biosdevname パッケージ を作成してそれを利用しています。

Ubuntu 12.10 (quantal) より、 インストーラが標準で biosdevname パッケージをインストールするようになりました (ubuntu-devel の投稿 を参照)。 パッケージとしては optional 扱いのため既存のシステムをアップグレードしても biosdevname がインストールされることはありません。

biosdevname を無効にするにはカーネルコマンドラインに biosdevname=0 を追加するか、 biosdevname パッケージをアンインストールします。 後者の方法をとる場合には update-initramfs -u を実行して initrd からも biosdevname を削除することを忘れないようにしてください。

Ubuntu のインストール時から biosdevname を利用したくない場合は インストーラ起動時のカーネルコマンドラインに biosdevname=0 を渡して起動します。

Ubuntu も Debian と同様に systemd/udev の予測可能な名前の機能はデフォルトで無効にされています。 Ubuntu 14.10 (utopic) 以降ではカーネルコマンドラインに biosdevname=0 net.ifnames=1 を渡すことで systemd/udev の命名規則を有効にできます。 biosdevname から systemd/udev への移行計画についてはいまのところ不明です。

Red Hat (Fedora/RHEL/etc.)

ネット上で調査した限りでは、 これまでの Red Hat の取り組みの変遷は次のようになっているようです。

CentOS 6.6 を調べたところ次のようになっていました。

CentOS 7.0.1406 では次のような状況です。

予測可能な名前を無効にしたい場合は、 カーネルコマンドラインに biosdevname=0 net.ifnames=0 を追加して biosdevname や systemd/udev の機能を殺す手もありますが、 それよりも sysconfig のインタフェース設定スクリプトで HWADDR= に MAC アドレスをきちんと書いて rename_device の機能を利用したほうがよいでしょう。

まとめ

Linux のネットワークインタフェースの名前について、 現在使われている各実装・各ディストリビューションの状況をまとめました。

この記事を書こうと思ったきっかけは 予測可能な名前に対する Debian と Ubuntu の温度差を感じたことからでした。 その仕組みや導入の経緯を調べるうちに、このトレンドの最上流である Red Hat 系ディストリビューションについても調べざるを得なくなり、 まとまるまでにずいぶん時間がかかってしまいました。 その分、これまであやふやだった udev フレームワークについての理解は深まりましたが。

Ubuntu は 14.04 LTS で biosdevname を導入しましたが、 今後 systemd/udev の実装にどう対応していくのか、 また Debian が予測可能な名前を導入することはあるのか、 引き続きウォッチしていきたいと思います。

2014/12/28 04:08:53 JST