テスト用の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
00:1A:2B : 3C:4D:5E
└─ OUI ─┘ └─ 機器固有部 ─┘

ここまでは教科書どおりだが、テストデータの話で効いてくるのは、先頭バイトの下位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 を眺めると、仮想インターフェースの先頭バイトが 02060a になっているのが見つかるはずだ。これらはすべて U/L が立っている(0x02 のビットが1)。

「先頭を 02 にする」では足りない

ローカル管理アドレスを作る、と聞いて先頭バイトを 02 に固定する実装をよく見る。それでも動くのだが、先頭バイトの残り6ビットまで固定してしまうと、生成した値の先頭バイトが常に同じになる。生成結果を並べたときに不自然だし、先頭バイトが偶然 03(U/L=1 かつ I/G=1)になる事故も防げない。

正しくは、乱数バイトに対してビット演算で U/L を立て、I/G を落とす

1
2
3
4
5
6
7
8
9
// 6バイトの乱数から、ローカル管理のユニキャストMACアドレスを作る
function randomLocalMac() {
const bytes = new Uint8Array(6);
crypto.getRandomValues(bytes);
bytes[0] = (bytes[0] | 0x02) & 0xfe; // U/L を立て、I/G を落とす
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join(":");
}

console.log(randomLocalMac()); // 例: 06:a6:01:4c:0d:1b

| 0x02 で U/L を1にし、& 0xfe で最下位ビット(I/G)を0にする。この2手で「ローカル管理のユニキャスト」という条件が満たされる。先頭バイトの残り6ビットは乱数のまま残るので、02 06 0a 0e … と散らばる。

I/G を落とす処理を省くと、生成した値の約半分がマルチキャストアドレスになる。マルチキャストのMACアドレスは、機器のインターフェースアドレスとしては使われない。そうと知らずにテストデータへ混ぜると、「なぜかこのレコードだけ弾かれる」という調査に半日使うことになる。

上のコードは Node.js(18以降のグローバル crypto)でもブラウザでもそのまま動く。確認するならこれで足りる。

1
node -e 'const b=new Uint8Array(6);crypto.getRandomValues(b);b[0]=(b[0]|2)&0xfe;console.log([...b].map(x=>x.toString(16).padStart(2,"0")).join(":"),"U/L=",!!(b[0]&2),"unicast=",!(b[0]&1))'

検証側も同じ2ビットを見る

テストデータを作る話の裏返しとして、受け取った値がローカル管理かどうかを判定したい場面がある。これも同じ2ビットを見るだけである。

1
2
3
4
5
6
7
8
9
10
11
12
function classifyMac(mac) {
const hex = mac.replace(/[^0-9a-fA-F]/g, "");
if (hex.length !== 12) throw new Error("MACアドレスは16進12桁です");
const first = parseInt(hex.slice(0, 2), 16);
return {
multicast: (first & 0x01) === 0x01,
locallyAdministered: (first & 0x02) === 0x02,
};
}

classifyMac("06:a6:01:4c:0d:1b"); // { multicast: false, locallyAdministered: true }
classifyMac("00:1a:2b:3c:4d:5e"); // { multicast: false, locallyAdministered: false }

区切り文字をいったん全部捨ててから読む形にしてあるのは、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種類の表記へ変換することもできる。処理はすべてブラウザ内で完結する。


テスト用のMACアドレスは適当な乱数でいいのか — U/Lビットと、実機に当たらない値の作り方
https://blog.hashito.biz/2026/08/11/locally-administered-mac-address-test-data/
著者
hashito
作成日
2026年8月11日
著作権