STM32のハードウェアIDからMACアドレスを生成する

先日作成したNUCLEO-F767ZIとMicroPython用のEthernetドライバですが、MACアドレスがソースコードにハードコーディングされていました。
これはCubeMX付属のサンプルがそうなっていたのをそのまま使ってしまっているからですが、これでは1つのネットワークに1台のMicroPythonマシンしか接続できないので、実用上は問題があります。

そこで、STM32がもともと持っている96bitのハードウェアIDからMACアドレスを生成しようと考えました。

MACアドレスは48bitですが、最初の24bitはベンダごとに異なる固定の値(ベンダーID)です。
ベンダーIDには、IPアドレス同様にプライベートアドレスがあり、1バイト目のbit 1が1ならプライベートアドレスです。具体的には「X2:XX:XX、X6:XX:XX、XA:XX:XX、XE:XX:XX」になります。

virtualization – What range of MAC addresses can I safely use for my virtual machines? – Server Fault

プライベートアドレスのどれを使ってもいいのですが、CubeMXのv1.8.1のサンプルでは「02:01:00」が使用されていました。
これはそのまま残すことにし、残り24bitをハードウェアIDから生成します。

ハードウェアIDは96bitですが、ランダムな値ではなく、チップを製造する際のある種の「通し番号」になっています。そのフォーマットは以下の資料に記載されていますが、
「ロット番号」「ウエハー番号」「ウエハー上での座標値(X, Y)」
でそれぞれ32bitずつ割り当てられているようです。

en.STM32L4_System_eSign.pdf

ハードウェアIDは、MicroPythonからは「machine.info()」で取得することができる一連の情報に含まれています。先日購入したNUCLEO-F767ZIでは

>>> import machine
>>> machine.info()
ID=33001f00:07513635:32333134

となり、もっと以前に購入したNUCLEO-F401REでは

>>> import machine
>>> machine.info()
ID=33005100:15513432:36363539

となりました。

上のPDF資料にもありますが、ハードウェアIDは全てのビットが使われているわけではなく、値の範囲が決まっているパラメータもあるので、一部のビットだけを抜き出して使うのが良さそうです。
上の2つの値を見比べただけでも、(違う型番のCPUで購入時期も異なるのに)
・1バイト目が一致(33)
・6バイト目が一致(51)
・2バイト目と4バイト目が0
・7バイト目~12バイト目は上位8ビットが全て3
といったことに気づきます。
上位8ビットが3というのは、おそらく数値をAsciiコードで表している(0=0x30、9=0x39)からだと思われます。
つまり、このハードウェアIDが文字列だと思って眺めてみると
767ZI: ”3…”, “.Q65”, “2314”
401RE: ”3.Q.”, “.Q42”, “6659”
となるわけです。

乱数に使うわけではないので、規則性そのものは問題にはならないのですが、複数のボードで一致する可能性が高い部分はMACアドレスには使わないほうが望ましいと考えられます。
ということであれこれ考えてみたのですが、頻繁に行う計算でもありませんし、この96ビットからハッシュ値を計算してしまうのが手っ取り早いという結論になりました。

ハッシュ値としては24bitあればいいので、軽量なハッシュアルゴリズムであるFNV-1(→Wikipediaを参照)を用いて計算した32bitのハッシュ値の下位24bitを使うことにしました。
Pythonで書くとこんな感じになります。埋め込まれている「0x811c9dc5」と「16777619」はそれぞれFNV_offset_basisとFNV_primeになります(上記のWikipediaを参照してください)。

これで先に挙げた2つのハードウェアIDからMACアドレスを計算すると、それぞれ以下のようになりました。

33001f00:07513635:32333134 → ec:f2:04
33005100:15513432:36363539 → 8c:4a:41

コメント