0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SONiC-VSのMACアドレス

Last updated at Posted at 2021-01-30

はじめに

ふだん仮想マシンを使うとき、MACアドレスについて気にすることはあまりないと思います。筆者もそうだったのですが、SONiCの仮想マシンを使っていてMACアドレスを気にせざるを得ない事象に遭遇してしまいました。原因と対応方法まで調べましたので共有したいと思います。

なお、物理マシン向けSONiCには上記のような事象はありませんので念のため。

きっかけ

SONiCはホワイトボックススイッチ用のOSで、仮想マシン版としてSONiC-VS (Virtual Switch)というものがあります。ホワイトボックススイッチはネットワーク機器なので単体で動かす意味はなく、仮想マシンであってもそれは同様です。

そんなわけで、最小構成としてSONiC-VSが動作する仮想マシンを2台用意して、仮想マシンの(2つめの)NIC同士を接続します。

トポロジーは下記のようになります。s0s1がVMです。defaultはホストマシンとつながるネットワークになります(DHCPでアドレスが降ってきます)。

(default)                      (default)
eth0|                              |eth0
 ---+---                        ---+---
 | s0  |                        | s1  |
 ---+---                        ---+---
    |10.0.0.0/31        10.0.0.1/31|
    |Ethernet0            Ethernet0|
    ---------------c1---------------

Ethernet0には手でIPアドレスを割り当てます。

s0
$ sudo config interface ip add Ethernet0 10.0.0.0/31
s1
$ sudo config interface ip add Ethernet0 10.0.0.1/31

インタフェースもupしてるし、pingくらい大丈夫だろう、と送ってみると……応答が返ってきません。tcpdumpで観測すると、arpを受けた相手がreplyを送ってるのが見えました。それなのにpingがとおらない? そんなことあるんだ……

状況確認

なぜpingが通らないのか

あちこち調べたのですが、結論としてはs1Ethernet0のMACアドレスと、s2Ethernet0のMACアドレスが同一だったため通信できませんでした。具体的には、どちらも52:54:00:12:34:56でした。

なぜMACアドレスが同一なのか

まず、QEMU/KVMで起動したVMでは、なにも指定がなければNICにつけられるMACアドレスが**52:54:00:12:34:56**固定になります。このこと自体は有名なようで、このMACアドレスで検索するとこの動作について言及しているWebページが山のように出てきます。

SONiCの設定ファイルである/etc/sonic/config_db.jsonには"mac"という設定項目があり、起動したSONiC-VSの中を覗き込むと、2つのVMでどちらも52:54:00:12:34:56となっているのがわかります。ip linkで確認できる、フロントパネルポートに割り当たっているMACアドレスもこの値です。

config_db.json
{
    "DEVICE_METADATA": {
        "localhost": {
            "hwsku": "Force10-S6000",
            "platform": "x86_64-kvm_x86_64-r0",
            "mac": "52:54:00:12:34:56",
            "hostname": "sonic",
            "type": "LeafRouter",
            "bgp_asn": "65100"
        }

/etc/sonic/config_db.json"mac"の値を書き換えて再起動すると、ip linkで見えるMACアドレスが変更された状態となりました。pingも通りましたので、この設定項目が直接原因で間違いないでしょう。

config_db.jsonはSONiCの初回起動時に生成されるので、起動時にどこかに付与されているMACアドレスを拾ってるんだろうなあ、という想像はつきます。なんとなくeth0なんだろうなあと思いつつ、追跡して間違いないか確認し、VM起動時に別の値を与えることで症状が改善するか確認してみます。

調査: 初期起動時のconfig_db.json生成の流れ

"mac"の値を取得するまでの流れは下記でした。

  1. /etc/rc.local起動
  2. firsttimeファイルがあればtouch /tmp/pending_config_initialization
  3. config-setup.service開始
  4. ExecStart=で指定されているconfig-setup bootが起動
  5. boot_config()/tmp/pending_config_initializationの有無がチェックされる
  6. 結果、do_config_initialization()が呼ばれる(戻ってきたら/tmp/...firsttimeファイルを削除)
  7. generate_config factoryが呼ばれる
  8. sonic-cfggenコマンドが呼ばれる
  9. device_info.get_system_mac()でMACアドレスを得ていた
  10. cat /sys/class/net/eth0/addressがMACアドレス!

つまり**eth0のMACアドレスが52:54:00:12:34:56であることが原因**で間違いないようです。

対策: MACアドレスを指定してみた

MACアドレスはVM起動時に-macaddr=で指定できますので、指定してみることにします。もちろん初回起動時じゃないとconfig_db.jsonは生成されませんので、新しいイメージを用意して。(terraform-provider-libvirtを使うときはnetwork_interfaceブロックにmac=を追加すれば指定できます)

起動したのでconfig_db.jsonを見てみます。

config_db.json
{
    "DEVICE_METADATA": {
        "localhost": {
            "hwsku": "Force10-S6000",
            "platform": "x86_64-kvm_x86_64-r0",
            "mac": "52:54:00:12:34:56",
            "hostname": "sonic",
            "type": "LeafRouter",
            "bgp_asn": "65100"
        }

……あれ?

指定を間違えたのかと思ってcat /sys/class/net/eth0/addressしてみます。

admin@sonic:~$ cat /sys/class/net/eth0/address 
52:54:00:12:00:00

指定した値になっているのに、config_db.json"mac"52:54:00:12:34:56のままです。これはいったいどういうこと?

調査: なぜ指定が無視されるのか

当初は、外部で指定していてもいったん52:54:00:12:34:56eth0が生えて、その後どこかのタイミングで値が変わるのか? と思い、sonic-cfggenに手を入れて/sys/class/net/eth0/addressの中身を呼ばれるたび吐き出すようにしてみたのですが、ここにはまったく52:64:00:12:34:56の痕跡なし。eth0は最初から指定した値になっていました。

じゃあもしかして自分の知らないルートでconfig_db.jsonが生成されている? と思い、あちこち調べてみると、結果として下記のことがわかりました。

  • /etc/rc.localが呼ばれたときにはすでにconfig_db.jsonは存在している
  • ビルドしたsonic-vs.imgの中にはすでに**firsttimeファイルがない**

firsttimeファイルがない? へ? そんなことってあるの?

firsttimeファイルはSONiC起動時に参照され、初期化が必要かの目印として使われます。初期化が終わると削除されるため、ふつうに使っていればその存在を意識することはありません。が、config_db.jsonの生成は初期化処理です。これは追跡する必要がありそうです。

firsttimeファイルはいつ消える?

ベースとなるファイルシステムの中身はbuild_debian.shによって作られます。中を見るとfirsttimeファイルは無条件に作成されているように見えます。

build_debian.sh
mkdir -p $FILESYSTEM_ROOT
mkdir -p $FILESYSTEM_ROOT/$PLATFORM_DIR
mkdir -p $FILESYSTEM_ROOT/$PLATFORM_DIR/x86_64-grub
touch $FILESYSTEM_ROOT/$PLATFORM_DIR/firsttime

しかし、下記手順でqcow2イメージをmountsonic-vs.imgの中身を起動前に調べてみると、たしかにありませんでした。

$ sudo modprobe nbd max_part=63
$ sudo qemu-nbd -c /dev/nbd0 sonic-vs.img
$ sudo mount /dev/nbd0p3 /mnt
$ ls /mnt/image-*/platform
x86_64-grub
$ sudo umount /mnt
$ sudo qemu-nbd -d /dev/nbd0

だとすると、どこかで誰かが消している? しかしソースコードのどこを見てもビルド時にfirsttimeファイルを削除するという記述はありません。

ん? まさか、ビルド時に一度起動している? ということは、まさか……ビルドしたsonic-vs.imgの中にはすでに**config_db.jsonがある? ……あった!!** orz

調査: どこで起動している?

ここまで簡単にまとめます。

  • SONiC-VSの仮想マシンイメージは、どうやらビルド中に一度起動している模様
  • そのためビルド済みの仮想マシンイメージにはすでにconfig_db.jsonが存在する
  • 初期化処理を示すfirsttimeファイルもビルド中の起動により削除されている

もしビルド中に起動しているのであれば、そのときのコマンドラインに-macaddr指定などありませんので、当然eth0のMACアドレスは52:54:00:12:34:56になります。これっぽいなー。ということで、どこで起動しているのか、そもそもなぜ起動しているのか、ビルド中の起動は本当に必要なのかを調べます。

仮想マシンイメージ作成フロー

ビルドしたばかりのイメージですでに起動済みということは、仮想マシンイメージを作成する手順のどこかで起動しているはず。調べてみると、scripts/build_kvm_imagee.shの中で起動しているっぽいのがわかりました。

build_kvm_image.sh
/usr/bin/kvm -m $MEM \
    -name "onie" \
    -boot "order=cd,once=d" -cdrom "$ONIE_RECOVERY_ISO" \
    -device e1000,netdev=onienet \
    -netdev user,id=onienet,hostfwd=:0.0.0.0:3041-:22 \
    -vnc 0.0.0.0:0 \
    -vga std \
    -drive file=$DISK,media=disk,if=virtio,index=0 \
    -drive file=$INSTALLER_DISK,if=virtio,index=1 \
    -serial telnet:127.0.0.1:$KVM_PORT,server > $kvm_log 2>&1 &

これはなんのためにやっているのだろう? 必要なの? と思ったのですが、これ自体は必要でした。具体的には下記のようなフローでSONiC-VSの仮想マシンイメージを作成していました。よって、ここを削ると正しくイメージを作れないということになります。

  1. qemu-img createで空のディスクイメージファイルを作る
  2. FATのディスクイメージにSONiCのインストーラーを書き込む
  3. 2つのディスクイメージとONIEリカバリーISOをつけた状態でKVM起動
  4. check_install.pyによりコンソール制御開始
  5. GRUBメニューが出たらEnterを送ってONIE(リカバリモード)起動
  6. リカバリモードの自動処理によってSONiCをインストール

フローがここまでなら問題ないのですが、そのあとcheck_install.pyは下記も実行していました。

  • 自動リブートのあとSONiCが起動するまで待つ
  • SONiCにログインする
  • インストールできているかコマンド実行で確認(プロンプトが返ってこないとビルドエラー扱い)

ここか!!

本命の対策

考えられる対策は2つあります。

  • ビルド時にSONiCの起動をやめる。ONIEによるインストールだけにする
  • check_isntall.pyの中で、firsttimeconfig_db.jsonを起動前の状態に戻す

前者はブートメディアの指定(order=cdorder=dに変更)でおそらくなんとかなります。

rc.local内の処理をざっと見た限り、初期起動シーケンスがもう一度動いても問題ないように見えましたので、今回は後者でやってみます。

ビルド時に生成したconfig_db.jsonの無効化

config_db.jsonを削除して、firsttimeファイルを用意します。

diff --git a/check_install.py b/check_install.py
index 08759db12..86950e73b 100755
--- a/check_install.py
+++ b/check_install.py
@@ -62,6 +62,10 @@ def main():
     p.expect([cmd_prompt])
     p.sendline('show ip bgp sum')
     p.expect([cmd_prompt])
+    p.sendline('sudo rm /etc/sonic/config_db.json')
+    p.expect([cmd_prompt])
+    p.sendline('cd /host/image-*/platform; sudo touch firsttime; cd -')
+    p.expect([cmd_prompt])
     p.sendline('sync')
     p.expect([cmd_prompt])
 

これで大丈夫なはずです。

対策の確認

SONiC-VSをビルドして(わりと時間がかかります)、できあがったsonic-vs.imgを使ってVMを起動してみます。もちろんMACアドレス指定を忘れずに。

そしてconfig_db.jsonを確認。

config_db.json
{
    "DEVICE_METADATA": {
        "localhost": {
            "hwsku": "Force10-S6000",
            "platform": "x86_64-kvm_x86_64-r0",
            "mac": "52:54:00:12:00:00",
            "hostname": "sonic",
            "type": "LeafRouter",
            "bgp_asn": "65100"

pingも無事通りました。めでたしめでたし。

おわりに

わかってみればなんてことはないのですが、調べるのに半日以上かかってしまいました。qcow2の中身をmountできなかったら、もっとかかっていたかもしれません。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?