Ubuntu 22.04 で tinydns が応答しなくなる問題
背景
Ubuntu 22.04 をインストールして、tinydns もソースコードからインストールしたところ、tinydns の動作が不安定になる症状が生じた。
Ubuntu 20.04 ではそのような不具合はなかったので調べることにした。
TL;DR 結論
tinydns インストール前に tinydns-conf.c
の -d300000
を -d400000
に変更してから tinydns-conf
コマンドで /etc/tinydns
へインストールを行う。
既に tinydns インストール済みの場合は、/etc/tinydns/run
を編集して -d300000
を -d400000
に変更してから tinydns を再起動する。
再起動は OS ごと sudo reboot
で再起動してもよいし、tinydns だけ再起動する場合は svc -k /etc/tinydns
を実行する。
症状
tinydns 起動後、dig の問い合わせに対して最初の数回は応答が返るものの、その後一切応答が返らなくなる(ゾーンが存在しないドメイン名に問い合わせたかのような応答となる)。
検証
/etc/tinydns/log/main/current
のクエリログを確認すると、異変が確認できた。
@40000000631c8eee1bf62a3c 856a2f32:af6a:37e3 + 0010 test.lavoscore.work @40000000631c8eef035ff9bc 856a2f32:8847:8c0e + 0010 test.lavoscore.work @40000000631c8eef2687bf24 856a2f32:8c3b:a435 + 0010 test.lavoscore.work @40000000631c8ef00ff8cc6c 856a2f32:975a:0000 / 0000 . @40000000631c8ef50f8916c4 856a2f32:975a:0000 / 0000 .
test.lavoscore.work
には TXT レコードを設定してあるが、これを問い合わせると応答が返る間は + 0010 test.lavoscore.work
とログが残るものの、返されなくなった時点でのログが 0000 / 0000 .
に変わっている。
デバッグしてみる
ソースコードを書き換えて、動作を変更しながらデバッグを試みる。
動作を変更するには svc -d /etc/tinydns
でサービスを止めてから再度 make setup check
を実行し、svc -u /etc/tinydns
でサービスを再開すればよい。make
は tinydns のソースコードがあるディレクトリに移動してから実行する。
server.c
0000 / 0000 .
をログ出力しているのは server.c である。
NOQ というラベルがあり、そこで 0000 / 0000 .
のログが記録される。NOQ には不正なクエリや異常が生じたときに飛ぶようになっている。NOQ に飛ぶコードのうち server.c の 41 行目で NOQ に飛んでいることが分かった。
dns_packet.c
前述の 41 行目では dns_packet_getname
が呼ばれている。その定義は dns_packet.c に書かれている。dns_packet.c の 69 行目で return 0
つまりエラー相当の結果になっていることが分かった。
dns_domain.c
前述の 69 行目では dns_domain_copy
が呼ばれている。その定義は dns_domain.c に書かれている。dns_domain.c の 33 行目で return 0
されていることが分かった。alloc の結果が失敗している。
alloc.c
alloc の実装をみると、avail
つまり SPACE
に達しない間はその領域を使い、avail
を使い切ったら malloc
を実行するようになっている。
症状で示した通り、最初の数回は応答が返る。しかし avail
を使い切ると、malloc
しようとするがこれが失敗して応答が返せなくなっていた。
原因
libc6 (glibc) が 2.32 以上の場合、この不具合が生じる。Ubuntu では apt-cache policy libc6
とコマンドを実行すればバージョンを確認できる。Ubuntu 20.04 では 2.31.x が使われているが、Ubuntu 22.04 では 2.35.x が使われていた。
libc6 の 2.32 では以前よりデータセグメントのサイズが大きくなったようで、その影響でプログラムが supervise の run ファイルで指定されている softlimit を超えてしまい、malloc
でのメモリ確保に失敗してしまうらしい。
回避策
tinydns インストール前に tinydns-conf.c
の -d300000
を -d400000
に変更してから tinydns-conf
コマンドで /etc/tinydns
へインストールを行う。
既に tinydns インストール済みの場合は、/etc/tinydns/run
を編集して -d300000
を -d400000
に変更してから tinydns を再起動する。
再起動は OS ごと sudo reboot
で再起動してもよいし、tinydns だけ再起動する場合は svc -k /etc/tinydns
を実行する。
-d400000
という値は Debian Bug report で修正されたパッチを参考にしている。実際に値を変更したところ、何度も問い合わせても応答し続けるようになった。
参考情報
- A weirdness with tinydns - infrequent oscillations
- #996807 - tinydns stops replying to queries after a few hours - Debian Bug report logs
横からこんばんは。
— hchbaw (@hchbaw) 2022年9月9日
私の場合これ↓で対処しました。(なんでそうなるのか不明だそうですが…)https://t.co/uWouITY1c6
それ以降、落ちることは無く、およそ一年くらい経ちました。
Debian の djbdns には patch が当たっていました…お手間を取らせてしまって申し訳ありません。(ちゃんと調べておけば良かった…)https://t.co/U35Y7H9lVV
— hchbaw (@hchbaw) 2022年9月10日