QEMU 環境下のマシンに DHCP で IP アドレスが降ってこない時の対処法

# TL;DR:

QEMU の Windows ゲストで DHCP サーバ IPv4 アドレスが降ってこないという問題が発生した。
最終的に, ホスト・マシンに入れていた `ufw` が `virbr0`（デフォルトの NAT ネットワークのブリッジ）経由の DHCP 通信をブロックしていたことがわかった。
そのため次のコマンドを用いて通信を許可したところ, 正常に IP アドレスが割り当てられ, 正常に通信ができるようになった：
```shell-session
$ sudo ufw allow in on virbr0
$ sudo ufw allow out on virbr0
```

# はじめに

QEMU で Windows ゲストを動かしているとき, 標準のNATネットワーク（virbr0＋dnsmasq）を使っているのにもかかわらず Windows のネットワーク状態が「IPv4 Connectivity: No network access」かつ「Unidentified network」と表示され, DHCP で IP アドレスが取得できない問題に遭遇した。

この記事では, 原因の切り分けから解決までの流れを備忘録として残しておこうと思う。

## 環境概要

+ ホスト OS: `Kali linux`
+ 仮想化ソフトウェア: `libvirt + QEMU`
+ ゲスト OS: `Windows 10 22H2`
+ ネットワーク構成： libvirt 標準の `default` NAT （virbr0) を 
  `e1000` 仮想 NIC として使用
+ DHCP サーバ：ホストマシン上の `dnsmasq` が, `192.168.122.1:53` にて稼働

## 発生した問題

+ Windows ゲストの IPv4 アドレスが `169.254.x.x` と,  APIPA アドレスが設定されている
  => DHCP を用いた自動割り当てに失敗
+ `ipconfig /renew` がタイムアウトし, IP の取得ができない
+ Windows ネットワークの状態が `Unidentified Network`

## 原因の切り分け

1. Windows ゲストの IP が DHCP から取得できていないため, ネットワークが使えない
  => そもそも DHCP パケットが返ってきていないことが `tcpdump` からわかった。
2. Windows のネットワーク・アダプタ・ドライバは e1000 で正常に認識している
3. ホスト側の仮想ネットワークと `dnsmasq` は正常に動作している
    ```shell-session
    $ ip a
     ...
    5: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc htb state DOWN group default qlen 1000
        link/ether 52:54:00:6b:3c:58 brd ff:ff:ff:ff:ff:ff
        inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
           valid_lft forever preferred_lft forever
           
    $ ps aux | grep masq
    nobody      2075  0.0  0.0  10452  2596 ?        S    08:41   0:00 /usr/sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/default.conf --leasefile-ro --dhcp-script=/usr/libexec/libvirt_leaseshelper
    root        2076  0.0  0.0  10424  1100 ?        S    08:41   0:00 /usr/sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/default.conf --leasefile-ro --dhcp-script=/usr/libexec/libvirt_leaseshelper
    nobody      2120  0.0  0.0  10424  1964 ?        S    08:41   0:00 /usr/sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/hostonly.conf --leasefile-ro --dhcp-script=/usr/libexec/libvirt_leaseshelper
    root        2121  0.0  0.0  10424  1168 ?        S    08:41   0:00 /usr/sbin/dnsmasq --conf-file=/var/lib/libvirt/dnsmasq/hostonly.conf --leasefile-ro --dhcp-script=/usr/libexec/libvirt_leaseshelper
    
    $ netstat -tulpn | grep 53
    tcp        0      0 192.168.122.1:53        0.0.0.0:*               LISTEN      -
    udp        0      0 192.168.122.1:53        0.0.0.0:*                           -
    ```

### 補足：なぜファイアウォールを疑ったか？

+ DHCP の Discover は飛んでいるが, 返答がない状況にあった
+ NIC や dnsmasq は正常に動作しているので
+ 通信経路が遮断されていることが視野に入る
+ NAT ブリッジ型の `virbr0` は, `ufw` や `firewalld` の餌食になることがある

## 解決方法

`firewalld` を使っている場合は `virbr0` を TRUSTED ゾーンに追加する：
```shell-session
$ sudo firewall-cmd --zone=trusted --add-interface=virbr0 --permanent
$ sudo firewall-cmd --reload
```

`ufw` を使っている場合は `virbr0` の通信を許可する：
```shell-session
$ sudo ufw allow in on virbr0
$ sudo ufw allow out on virbr0
```

私の場合は UFW 環境下のマシンであったので, 後者のコマンドで解決した。

## まとめ

QEMU の NAT+dnsmasq で Windows ゲストが DHCP で IP を取得できない場合、ホストのファイアウォールが意外と盲点であった。
virbr0` などの仮想ブリッジインターフェースは, ファイアウォール設定で通信ブロックされやすい。
そのためまずはファイアウォール設定を疑い, 必要に応じて適切に緩和しましょう