Ubuntu20.04でDHCPv4がキレまくったのに困惑

おうちPCのOSをUbuntu 20.04(beta)に上げたところ、頻繁にwifiが切れるようになりました。(正確にはIPv4接続が切れる)
もともとwifiルータがかなりイマイチな代物で 他のクライアントデバイスでもなんだか繋がらんなあということが結構あるので、とくにUbuntu動かしてるPCが悪いとも思わず、 切れるたびwifi接続を手動で無効にしてから有効にするという絵に描いたようなアレっぷりを披露していたわけなんですが、いくらなんでも頻繁すぎることに気づいたので対処してみました。


# 環境

~~~ bash
# lspci
<snip>
02:00.0 Network controller: Intel Corporation Wireless 8265 / 8275 (rev 78)

# uname -a
Linux xxxxxxx 5.4.0-24-generic #28-Ubuntu SMP Thu Apr 9 22:16:42 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

# lsb_release -d
Description:    Ubuntu 20.04 LTS

# ethtool -i wlp2s0
driver: iwlwifi
version: 5.4.0-24-generic
firmware-version: 36.77d01142.0
~~~
wifiデバイスはintel 8265ってやつでfirmwareも最新でした。
(今まで知らなかったんですが、主だったデバイスのファームは/lib/firmwareに突っ込んでなんとかする時代になってるんですね・・・・)


# 何が起きているのか
何はともあれログです。
切れるときになにやらjournaldに出力してました。

~~~ bash
# LANG=C journalctl -b0 --no-pager -q | grep -i wlp2s0
Apr 18 00:07:05 xxxxxxx avahi-daemon[1057]: Withdrawing address record for 192.168.1.13 on wlp2s0.
Apr 18 00:07:05 xxxxxxx avahi-daemon[1057]: Leaving mDNS multicast group on interface wlp2s0.IPv4 with address 192.168.1.13.
Apr 18 00:07:05 xxxxxxx avahi-daemon[1057]: Interface wlp2s0.IPv4 no longer relevant for mDNS.
Apr 18 00:07:05 xxxxxxx NetworkManager[1061]: <info>  [1587136025.9189] dhcp4 (wlp2s0): state changed bound -> expire
Apr 18 00:07:05 xxxxxxx NetworkManager[1061]: <info>  [1587136025.9191] device (wlp2s0): DHCPv4: trying to acquire a new lease within 90 seconds
Apr 18 00:08:36 xxxxxxx NetworkManager[1061]: <info>  [1587136116.0761] device (wlp2s0): DHCPv4: grace period expired
~~~
DHCPv4がlease/renewのタイミングかなにかでしくっています。

ちなみにwifi接続後、毎度一時間間隔で切れていたようです。

~~~ bash
# LANG=C journalctl --no-pager -q | grep dhcp4  | grep state
Apr 05 08:45:00 xxxxxxx NetworkManager[1052]: <info>  [1586043900.6220] dhcp4 (wlp2s0): state changed unknown -> bound
Apr 05 09:44:58 xxxxxxx NetworkManager[1052]: <info>  [1586047498.7583] dhcp4 (wlp2s0): state changed bound -> expire
<snip>
Apr 17 23:07:05 xxxxxxx NetworkManager[1061]: <info>  [1587132425.9559] dhcp4 (wlp2s0): state changed unknown -> bound
Apr 18 00:07:05 xxxxxxx NetworkManager[1061]: <info>  [1587136025.9189] dhcp4 (wlp2s0): state changed bound -> expire
~~~

IP取得時にはこんなのも吐いてまして

~~~ bash
Apr 18 01:20:13 xxxxxxx NetworkManager[1061]: <info>  [1587140413.9716] dhcp4 (wlp2s0): option dhcp_lease_time      => '3600'
~~~
一時間てのはコイツですな。
ちょっと遡ってログを眺めてみると、

~~~ bash
Apr 04 18:58:11 xxxxxxx NetworkManager[1389]: <info>  [1585994291.0431] dhcp-init: Using DHCP client 'dhclient'
Apr 04 19:22:04 xxxxxxx NetworkManager[1344]: <info>  [1585995724.5607] dhcp-init: Using DHCP client 'dhclient'
Apr 04 21:05:37 xxxxxxx NetworkManager[8571]: <info>  [1586001937.4680] dhcp-init: Using DHCP client 'internal'
Apr 04 22:06:28 xxxxxxx NetworkManager[1047]: <info>  [1586005588.8066] dhcp-init: Using DHCP client 'internal'
~~~
4/4はアップグレードのタイミングです。NetworkManager(aptで1.22.10-1ubuntu1)のデフォルトDHCP clientがinternalなものに変わったようです。


# NetworkManagerのinternal dhcp client

最初はログの出力順を見て、avahi-daemonが悪さしているのかなぁと思ったんですが止めてみても再発したため、dhcp clientをなんとかせねばならんということになりました。
で、internal DHCP clientの設定を弄ってなんとかしてみようと思ったんですが
(どうもnmcliって好きになれない・・・・でも仕方ないっすね)

~~~ bash
# LANG=C nmcli connection edit UUID
nmcli> print
connection.autoconnect:                 yes
connection.autoconnect-retries:         -1 (default)
connection.auth-retries:                -1
connection.wait-device-timeout:         -1
ipv4.dhcp-timeout:                      0 (default)
ipv4.never-default:                     no
ipv4.may-fail:                          yes
ipv4.dad-timeout:                       -1 (default)
~~~
上記いろいろ省いてますが、renewの動作を制御する設定はなさげです。
(renewに失敗したときのパラメータならある)

と思いつつ適当にパラメータ弄って1時間待つ、を何度か繰り返した挙げ句
(dhcp_lease_timeをどうやって短くするのかもよくわからなかった)

辛くなってしまい

結局

**「馴染みのdhclientを使う」**
という結論に至りました。

# dhclient
internalのDHCP clientからdhclientに変更するのは簡単で

- 設定ファイルを書く
- NetworkManager再起動

でOKです。

~~~ bash
# cat /etc/NetworkManager/conf.d/dhcp-client.conf 
[main]
dhcp=dhclient
~~~

上記設定後、NetworkManagerを再起動してやると

~~~ bash
Apr 18 16:44:41 xxxxxxx NetworkManager[1078]: <info>  [1587195881.3113] dhcp-init: Using DHCP client 'dhclient'
~~~

こんなログが出力されます。
ちなみにinternalの場合はdhcp用の常駐プロセスはいなかったっぽいですが、dhclientは

~~~ bash
# pgrep -f -l dhcli
1410 dhclient
~~~
こんな感じでひっそり常駐してます。

dhclientに変更することで、**「一時間たつとwifi接続が切れる」ということはなくなって安心して生活できております。**

なお、

~~~ bash
Apr 18 05:28:54 xxxxxxx kernel: audit: type=1400 audit(1587155334.410:96): apparmor="DENIED" 
operation="mknod" profile="/{,usr/}sbin/dhclient" name="/run/NetworkManager/dhclient-wlp2s0.pid" 
pid=96291 comm="dhclient" requested_mask="c" denied_mask="c" fsuid=0 ouid=0
~~~
なんていうログが出てて気持ち悪いので

~~~ bash
# ln -s /etc/apparmor.d/sbin.dhclient /etc/apparmor.d/disable
# apparmor_parser -R /etc/apparmor.d/sbin.dhclient
~~~
とカツを入れてますが正しい対処なのかはよくわかりません。

20.04の正式リリースは4/23だそうですので、apt upgradeしてみてもう一度internalの動作を見てみるつもりです。

(4/27追記)
apt upgradeしてみましたがNetworkManagerのバージョンは上がってないので、たぶんinternal DHCP clientだとあかんままだと思われます。
なお、変更できなかったdhcp lease timeですが、DHCPはRFC2131で言及されてまして、その3.5章を読むと
クライアントがDHCPDISCOVERで提案できる -> サーバは無視していい
ということらしく、dhclientならば設定方法は容易にわかったので1800秒に設定して試してみたところ

~~~ bash:dhclient.conf
<snip>
send dhcp-lease-time 1800;
<snip>
~~~

tcpdumpで覗いたらリクエストには反映されてる

~~~ bash
23:56:50.040902 XX:XX:XX:0e:49:26 > ff:ff:ff:ff:ff:ff, ethertype IPv4 (0x0800), length 342: (tos 0x10, ttl 128, id 0, offset 0, flags [none], proto UDP (17), length 328)
    0.0.0.0.68 > 255.255.255.255.67: [udp sum ok] BOOTP/DHCP, Request from XX:XX:XX:0e:49:26, length 300, xid 
<snip>
            Lease-Time Option 51, length 4: 1800
<snip>
~~~

DHCPACKでは3600で返ってきてる

~~~ bash
23:56:50.699113 ZZ:ZZ:ZZ:7c:f9:4c > ff:ff:ff:ff:ff:ff, ethertype IPv4 (0x0800), length 590: (tos 0x0, ttl 32, id 34961, offset 0, flags [DF], proto UDP (17), length 576)
<snip>
            Lease-Time Option 51, length 4: 3600
<snip>
~~~

ということで、おうちルーター付属のdhcpサーバはlease timeを華麗に無視する実装でした。

NetworkManagerのinternal dhcp clientに対してlease timeオプションをどこで設定するかはわかってませんが、今の環境だと調べても苦労の甲斐がないのでここでやめときます。

参考:
https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/networking_guide/configuring_the_dhcp_client_behavior
https://developer.gnome.org/NetworkManager/stable/settings-connection.html
https://developer.gnome.org/NetworkManager/stable/settings-ipv4.html
https://tools.ietf.org/html/rfc2131