toru.kabasawa
2022年1月19日 17時03分
外部のPCとLANケーブルで接続したArmadilloでサービスとして登録したtcpdumpを起動し、ファイル保存時のプログラムに自作のシェルスクリプトを指定して動作させています。通常は問題なく動作するのですが、ファイル保存サイズを小さくした状態で受信データのトラフィックが高くなったり、他の負荷の高い処理を行ったときなどに、シェルスクリプトがゾンビプロセスとして残ってしまう事があります。
■受信データのトラフィック
200Mbps
■tcpdumpのオプション
「-C:ファイル保存サイズ」:10MB
「-z:ファイル保存時に実行するプログラム」:自作のシェルスクリプト
■具体例
root@hostname:/home/user# ps -x | grep pcap_notice | grep -v grep
8116 ? Rs 470:23 /usr/sbin/tcpdump -i eth0 -n -w /data/capture/.tmp/capfile -C 10 -z /usr/local/bin/pcap_notice.sh -B 393216
12233 ? ZN 0:00 [pcap_notice.sh]
12235 ? ZN 0:00 [pcap_notice.sh]
12237 ? ZN 0:00 [pcap_notice.sh]
これについて現在対策方法を検討しております。
現状は、定期的にtcpdumpを再起動するか、負荷が掛からない用にトラフィックを制限するぐらいしか方法がないかと考えていますが、理想は、ゾンビプロセスを発生させない方法があれば良いと思っています。
もし良い案がありそうでしたら、アドバイスを頂けるとありがたいです。
コメント
toru.kabasawa
at_shinya.koga
アットマークテクノの古賀です。
toru.kabasawaさん:
>外部のPCとLANケーブルで接続したArmadilloでサービスとして登録したtcpdumpを起動し、ファイル保存時のプログラムに自作のシェルスクリプトを指定して動作させています。通常は問題なく動作するのですが、ファイル保存サイズを小さくした状態で受信データのトラフィックが高くなったり、他の負荷の高い処理を行ったときなどに、シェルスクリプトがゾンビプロセスとして残ってしまう事があります。
…
>これについて現在対策方法を検討しております。
>現状は、定期的にtcpdumpを再起動するか、負荷が掛からない用にトラフィックを制限するぐらいしか方法がないかと考えていますが、理想は、ゾンビプロセスを発生させない方法があれば良いと思っています。
>
>もし良い案がありそうでしたら、アドバイスを頂けるとありがたいです。
根本的な対策は、マルティネさんが書いたように、tcpdump を修正する(SIGCHLD シグナルのハンドラで単純に wait() しているのを修正し、子プロセスを全て終了待ちするようにする)ことですね。
tcpdump を修正しない方策でお手軽なのは、pcap_notice.sh をデーモンとして実行する wrapper スクリプトを作り、tcpdump の -z オプションには、wrapper スクリプトを渡す、という方策かなと思います。wrapper スクリプトは、次のようにすればよいでしょう:
https://stackoverflow.com/a/19235243
https://man7.org/linux/man-pages/man1/setsid.1.html
#!/binsh setsid /usr/local/bin/pcap_notice.sh >/dev/null 2 >&1 </dev/null&
toru.kabasawa
at_dominique.m…
toru.kabasawaさん、
古賀さんの提案ですと、子プロセスが別のsidを取得してもwrapperスクリプト自体はまだtcpdumpにreapされないとゾンビに残りますので、これだけだと原書が消えないかもしれません。
具体的な原因はこういうパターンです:
- tcpdumpがロテートしてpcap_notice.shを起動させます
- tcpdumpがもう一度ロテートして、1つ目のプロセスがまだ存在するときに2つ目のpcap_notice.shを起動させます。
- どれかのpcap_notice.shが終わって、tcpdumpにSIGCHLDを送ります
- もう一つのpcap_notice.shもtcpdumpがSIGCHLDをhandleする前に終わります。
- tcpdumpがSIGCHLDで起きて、一つのwait()でどれかのプロセスをreapします。
- 残ったpcap_notice.shはゾンビとして残ります。
- (次にロテートが起きて新しいプロセスが起動させて終わったら、前のゾンビがwait()でreapされて新しい方がゾンビとして残ります)
chrtでtcpdumpのpriorityを上げても、子プロセスのpriorityも同じく上げたままで動きますので、それに合わせてpcap_notice.shのpriorityを下げたら再現率が下がるかもしれませんが、tcpdumpの修正の方は確実ですね。
どうしてもワークアラウンドが必要でしたら、子プロセスが同時に終わらないためにスクリプトの最後のところに例えば「flock /tmp/tcpdump-rotate-lock sleep 1」を実行すればプロセスの死の間に一秒が開くので一個ずつwaitできる確率が上がります…が、大量に発生されたらゾンビではなくflockが間に合わない可能性もありますので、万能ではありません。
よろしくお願いします
toru.kabasawa
マルティネ様
ご丁寧に説明ありがとうございます。
お手軽な方法で対策できればと思っておりましたが、結局は急がば回れでtcpdumpの修正が一番確実なようですね。
chrtで優先度を上げて土日連続動作させた結果、ゾンビプロセスは残らなかったものの、以下の様にドロップの数がすごい数になっておりNGでした。
2022-01-24T09:14:01.982681+09:00 hostname systemd[1]: Stopping tcpdump...
2022-01-24T09:14:01.987866+09:00 hostname chrt[3568]: 1512399433 packets captured
2022-01-24T09:14:02.074899+09:00 hostname chrt[3568]: 2739117604 packets received by filter
2022-01-24T09:14:02.077851+09:00 hostname kernel: device eth0 left promiscuous mode
2022-01-24T09:14:02.079156+09:00 hostname chrt[3568]: 1226290222 packets dropped by kernel
tcpdumpの修正の方向で進めさせて頂こうと思います。
ありがとうございました。
toru.kabasawa
マルティネ様
tcpdumpの修正版でビルドして動作確認を行っておりますが、ビルド方法についてご教授頂けませんでしょうか?
# tcpdump -h
tcpdump version 4.9.3
libpcap version 1.8.1
OpenSSL 1.0.2u 20 Dec 2019
上記のバージョンのソースコードを取得し、バーチャルマシンで構築したDebian9で以下のビルドを実施しましたが、
ビルド方法の指定に誤りの個所などありますでしょうか?
■libpcap
CC=arm-linux-gnueabihf-gcc ac_cv_linux_vers=4 ./configure --host=arm-linux --with-pcap=linux
make
■tcpdump
CC=arm-linux-gnueabihf-gcc ac_cv_linux_vers=4 ./configure --host=arm-linux --with-pcap=linux
make
上記のビルドで目的のゾンビプロセスの残留は、おかげさまで今のところ発生していないのですが、
上記のままですとOpenSSlがtcpdumpに含まれておりませんので含めたいと思っております。
まず、クロスコンパイル環境にOpenSSlを入れる為、フォーラムの以下のページを見て
「apt-get install libssl-dev:armhf」を実行してみましたが、
「E: パッケージ libssl-dev:armhf が見つかりません」となってしまいました。
ビルド環境をDebian9からATDEに変えれば、取得できるのでしょうか?
https://armadillo.atmark-techno.com/forum/armadillo/2103
at_dominique.m…
toru.kabasawaさん
> 上記のバージョンのソースコードを取得し
tcpdump -hだけで分かりませんので、一応聞いておきます。debianのstretchパッケージにいくつかのセキュリティパッチが入ってますが、そちらも当てたんでしょうか?
ちょっと手間をかかりますが、私でしたらdebianパッケージとしてリビルドします。
https://packages.debian.org/stretch/tcpdump から origとdebianのファイルを取得して、パッチを追加してビルドすればopensslも問題なく入りますので、
以下の手順を試してみて下さい。
curl -O http://security.debian.org/debian-security/pool/updates/main/t/tcpdump/tcpdump_4.9.3.orig.tar.gz
curl -O http://security.debian.org/debian-security/pool/updates/main/t/tcpdump/tcpdump_4.9.3-1~deb9u2.debian.tar.xz
tar xf tcpdump_4.9.3.orig.tar.gz
cd tcpdump-4.9.3/
tar xf ../tcpdump_4.9.3-1~deb9u2.debian.tar.xz
cd debian/patches/
curl -L -o child_cleanup.patch https://github.com/the-tcpdump-group/tcpdump/pull/972/commits/04ef843b0777b96a9270d5204fa0e7e70112bac5.patch
sed -i -e 's/void/RETSIGTYPE/' child_cleanup.patch # バージョンの差で少し直さないと使えません
echo child_cleanup.patch >> series
cd ../..
dch -l zombie # パッケージを区別できるようにバージョンを更新します。
# dchコマンドがなかったらdebian/changelogファイルに一番目の行を編集して4.9.3-1~deb9u2zombie1などに変更してもいいです
sudo apt install libpcap0.8-dev:armhf libssl1.0-dev:armhf
CC=arm-linux-gnueabihf-gcc dpkg-buildpackage -uc -us -aarmhf
ls ../*deb # ../tcpdump-dbgsym_4.9.3-1~deb9u2zombie1_armhf.deb ../tcpdump_4.9.3-1~deb9u2zombie1_armhf.deb
libpcapに変更をしなかったので、tcpdumpだけをリビルドすれば最低限の変更で現象を直せます。
> apt-get install libssl-dev:armhf
あの記事がちょっと古いのでパッケージの名前が変わりました。
apt-get install libssl1.0-dev:armhf
で試してみて下さい。インストールしないとdpkg-buildpackageの際に「dpkg-checkbuilddeps: error: Unmet build dependencies: libpcap0.8-dev (>= 1.8) libssl1.0-dev」のようなエラーが出ますので、そこにかかれてるパッケージの名前を参考にしながら :armhf
を追加してインストールできるはずです。
よろしくお願いします
toru.kabasawa
マルティネ様
セキュリティーパッチはあてておりませんでした。
具体的な手順を示して頂き大変助かっております。
あと、もう少しといったところなんですが、
ビルドされたバイナリがdebパッケージに格納されません。dpkg-buildpackageでフォルダの指定などが必要でしょうか?
-rwxr-xr-x 1 root root 2804748 1月 25 20:52 /home/fsl/tcpdump-4.9.3/tcpdump ← ビルドされたもの
-rwxr-xr-x 1 root root 712888 1月 25 20:53 /home/fsl/tcpdump-4.9.3/debian/tcpdump/usr/sbin/tcpdump ← オリジナルのファイル?
root@debian9:/home/fsl/tcpdump-4.9.3# dpkg -c ../tcpdump_4.9.3-1~deb9u2zombie1_armhf.deb
drwxr-xr-x root/root 0 2020-11-10 23:22 ./
drwxr-xr-x root/root 0 2020-11-10 23:22 ./usr/
drwxr-xr-x root/root 0 2020-11-10 23:22 ./usr/sbin/
-rwxr-xr-x root/root 712888 2020-11-10 23:22 ./usr/sbin/tcpdump ← オリジナルのファイル?
at_dominique.m…
toru.kabasawaさん
> ビルドされたバイナリがdebパッケージに格納されません。dpkg-buildpackageでフォルダの指定などが必要でしょうか?
大変分かりにくいですが、「reproducible build」のために時間はdebian/changelogの最新バージョンのリリース時間になっていますので、オリジナルのファイルではないと思います。
サイズもまったく同じのは分かりにくいですが…
確認できる範囲では、以前のコードはwaitの関数を使っていたが、新しいコードにwaitpidに変わりました。以下の手順で確認できます(armadilloでやりましたら、普通のobjdumpで行えます)
atmark@atde7:~$ dpkg-deb -x tcpdump_4.9.3-1~deb9u2zombie1_armhf.deb tcpdump_files atmark@atde7:~$ arm-linux-gnueabihf-objdump -d tcpdump_files/usr/sbin/tcpdump | grep wait 0000eef4 <waitpid@plt>: 12c36: f7fc e95e blx eef4 <waitpid@plt>
waitpidを使われていたら大丈夫です。
よろしくお願いします
toru.kabasawa
at_dominique.m…
2022年1月21日 10時37分
アットマークテクノのマルティネです。
こちらで再現できました。tcpdumpは確かにゾンビを残す可能性がありますね。
修正は割と簡単でしたので、アップストリームに送っておきました: https://github.com/the-tcpdump-group/tcpdump/pull/972
お客様の環境に届くまではずいぶんと時間がかかると思いますので、それまで自分で修正を入れてtcpdumpをリビルドするか、考えた通りに定期的にtcpdumpを再起動させればいいと思います(問題としては負荷が高い時にsigchldの処理が新しいファイルの作成より遅いということなので、制限でもすれば発生しなくなります)。
よろしくお願い致します