テスト用のMACアドレスは適当な乱数でいいのか — U/Lビットと、実機に当たらない値の作り方
ドキュメントに書くIPアドレスには 192.0.2.0/24 を使う、というのは広く知られている。RFC 5737 が文書用に予約した範囲で、ここに書いた値が誰かの実機を指すことはない。
ではMACアドレスはどうしているか。手元の資料を見返すと、00:11:22:33:44:55 のような手癖の値か、その場で作った乱数がそのまま載っていることが多い。前者は実在のOUIに当たる可能性があり、後者は当たるかどうかがその時の運になる。
MACアドレスにも同じ役割を果たす領域がある。ただし「文書用に予約された範囲」という形ではなく、先頭バイトの1ビットで表現されている。ハシトシステムのMACアドレス生成・形式変換ツールを作るにあたって整理した内容を、実装込みで書いておく。
48ビットの内訳
MACアドレス(正確には EUI-48)は48ビット、6バイトである。先頭3バイトがOUI(Organizationally Unique Identifier)で、IEEE が製造者に割り当てる。残り3バイトを製造者が機器ごとに振る。
1 | |
ここまでは教科書どおりだが、テストデータの話で効いてくるのは、先頭バイトの下位2ビットに意味が割り当てられているという点である。
| ビット | 値 | 名前 | 意味 |
|---|---|---|---|
| bit 0(最下位) | 0x01 |
I/G | 1ならマルチキャスト、0ならユニキャスト |
| bit 1 | 0x02 |
U/L | 1ならローカル管理、0ならIEEEが割り当てたグローバル |
U/L ビットが1のアドレスは「ローカル管理アドレス(Locally Administered Address)」と呼ばれ、IEEE が割り当てるOUIの範囲とは重ならない。つまり U/L を立てた値は、定義上どのベンダーの実機とも一致しない。これが 192.0.2.0/24 に相当する性質である。
実際、この領域は仮想NIC、コンテナのブリッジ、MACアドレスをランダム化するWi-Fiクライアントなどが日常的に使っている。手元の環境でも ip link を眺めると、仮想インターフェースの先頭バイトが 02 や 06 や 0a になっているのが見つかるはずだ。これらはすべて U/L が立っている(0x02 のビットが1)。
「先頭を 02 にする」では足りない
ローカル管理アドレスを作る、と聞いて先頭バイトを 02 に固定する実装をよく見る。それでも動くのだが、先頭バイトの残り6ビットまで固定してしまうと、生成した値の先頭バイトが常に同じになる。生成結果を並べたときに不自然だし、先頭バイトが偶然 03(U/L=1 かつ I/G=1)になる事故も防げない。
正しくは、乱数バイトに対してビット演算で U/L を立て、I/G を落とす。
1 | |
| 0x02 で U/L を1にし、& 0xfe で最下位ビット(I/G)を0にする。この2手で「ローカル管理のユニキャスト」という条件が満たされる。先頭バイトの残り6ビットは乱数のまま残るので、02 06 0a 0e … と散らばる。
I/G を落とす処理を省くと、生成した値の約半分がマルチキャストアドレスになる。マルチキャストのMACアドレスは、機器のインターフェースアドレスとしては使われない。そうと知らずにテストデータへ混ぜると、「なぜかこのレコードだけ弾かれる」という調査に半日使うことになる。
上のコードは Node.js(18以降のグローバル crypto)でもブラウザでもそのまま動く。確認するならこれで足りる。
1 | |
検証側も同じ2ビットを見る
テストデータを作る話の裏返しとして、受け取った値がローカル管理かどうかを判定したい場面がある。これも同じ2ビットを見るだけである。
1 | |
区切り文字をいったん全部捨ててから読む形にしてあるのは、MACアドレスの表記が現場ごとに違うためである。
| 表記 | 例 | よく見る場所 |
|---|---|---|
| コロン区切り | 00:1a:2b:3c:4d:5e |
Linux の ip / ifconfig、macOS |
| ハイフン区切り | 00-1A-2B-3C-4D-5E |
Windows の ipconfig /all |
| ドット区切り(4桁ごと) | 001a.2b3c.4d5e |
Cisco 機器の show 系コマンド |
| 区切りなし | 001a2b3c4d5e |
設定ファイル、ログ、CSV |
4つはどれも同じ48ビットで、区切りと大小文字が違うだけである。入力を受け取る側でこの4種を吸収しておかないと、「Windowsからコピペしたら弾かれる」という報告が必ず来る。逆に出力するときは、貼り付け先が受け付ける表記に合わせる必要がある。
OUIを固定したいときの注意
ベンダーごとの挙動を再現したい、といった理由で先頭3バイトを実在のOUIに固定したい場面はある。そのときに気をつけるのは、固定したOUIとローカル管理ビットは両立しないという点である。実在のOUIは定義上 U/L が0なので、U/L を立てた瞬間にそれは「そのベンダーのアドレス」ではなくなる。
つまり選択は二択になる。
- 実機と衝突しないことを優先する → ローカル管理(U/L=1)。資料・スクリーンショット・公開するテストデータはこちら
- 特定ベンダーらしさを優先する → 実在のOUIを固定(U/L=0)。閉じた環境での再現テストに限る
公開物に後者を混ぜると、無関係な機器を名指ししているように読まれる。IPアドレスの資料に他人のグローバルIPを書かないのと同じ理由で、避けたほうがよい。
なお、ローカル管理アドレスは「衝突しない」のではなく「IEEEの割り当てと衝突しない」である。同じLAN内で誰かが同じローカル管理アドレスを使う可能性は残る。48ビットのうち乱数が効くのは46ビットなので、実務で問題になる確率ではないが、原理としてはそういうものだと押さえておく。
まとめ
- MACアドレスの先頭バイトには I/G(
0x01)と U/L(0x02)の2ビットが埋まっている - テスト・資料用の値は
bytes[0] = (bytes[0] | 0x02) & 0xfeで作る。U/L を立て、I/G を落とす - U/L を立てただけで「IEEEの割り当てと衝突しない」ことが規格上保証される。IPの
192.0.2.0/24に相当する - I/G を落とし忘れると、生成値の約半分がマルチキャストになる
- 表記は4種類あるが値は同じ。入力は全部受ける、出力は貼り付け先に合わせる
ブラウザで生成と表記変換を済ませたい場合は、MACアドレス生成・形式変換ツールにこの記事の実装がそのまま入っている。件数を指定してまとめて生成でき、既存のアドレスを4種類の表記へ変換することもできる。処理はすべてブラウザ内で完結する。