IPv6のicmpping(シンプルチェック)が失敗する
はじめまして。
■現象・状況
条件を満たしたときにIPv6アドレスが設定してあるホストのicmppingが失敗します。
条件は[b]「IPv6アドレスのホストと、そうでないホスト(IPv6で名前解決できないホスト)に対してほぼ同時にicmppingでチェックをする」[/b]です。
IPv6だけなら問題ありません。
また、OSやZABBIXのバージョンも関係ありません(OSはFreeBSD と CentOS、ZABBIXは1.6.6と1.8.1で確認済み)。
fpingのバージョンは、Version 2.4b2_to です。
失敗すると
Update IP [fping6: hostname nor servname provided, or not known]
というログが出ます。
後で書きますが、原因はたぶん分かりました。
そして、とりあえず解決はできたので、同様の現象で困っている人は参考にしてみてください。
今試している状況は、
・IPv4のホストとIPv6のホストを同時に監視しようとしている。
・各ホストの接続方法はIPアドレスで、IPv4のものとIPv6 のものがある。
・すべてのホストに対してicmppingのアイテムが設定されている(テンプレートで設定し、各ホストはそれにリンクしている)。
タイプ:シンプルチェック
キー:icmpping
更新間隔:30秒
zabbix-serverを起動した直後などは、全部のicmppingが一斉に実行されるので上記の「ほぼ同時に」という条件を満たします。
■ソースをざっと見てみました。
IPv6が有効になっている場合、src/libs/zbxicmpping/icmpping.cで
process_ping()内でtmpファイルにホストのアドレス一覧を書き込み、 (1)
シェルで fping < tmp; fping6 < tmp を実行し、その結果を読む。 (2)
ということが行われているようです。
process_ping()に渡されるホスト一覧は、途中は省略しますが、ざっと書くと
・各アイテムを見て、今チェックすべきものは何かを知る。 (3)
・そのうち、icmppingのものを抜き出す。
・そのホストを、重複を除いてリストにしたものがホスト一覧。
こんな感じで決めているみたいです。
(3)は、更新間隔や最後のチェック時刻から今チェックすべきかどうか調べるのだと思います(この辺よく分かりませんでした)。また、(3)がどのタイミングで実行されるのかまでは見ていません。
(1)のtmpファイルは、/tmp/zabbix_server_プロセスID.pingerです。このファイルの中身は以下のようにIPアドレスやホスト名(DNS名)が並んでいます。
192.0.2.1
2001:db8::1
test.example.jp
test6.example.jp
条件の「ほぼ同時に」というのは、一つのtmpファイルにまとめて並べられる程度にタイミングが合った場合、ということになると思います。このタイミングは、ZABBIXの設定では調整できないだろうと思っています(ひょっとして可能ですか?)。
(2)でこのtmpファイルをfping, fping6に渡しています。fping, fping6は各行を読み、それぞれに対してpingを実行し、その結果を出力します。
■fpingのソースも見てみました。
fping では相手がIPv4アドレスならリストに追加、そうでなければ名前解決しようとします(add_name()内)。IPv4アドレスが引ければそのアドレスをリストに追加しますが、アドレスが引けなかった場合は無視して次の行に進むようです。そして、全部の行を調べたら、リストに入っている各アドレスに向けてpingを実行します。
fping6でも同様にIPv6アドレスならリストに追加、そうでなければ名前解決、IPv6アドレスが引ければそのアドレスをリストに追加、という所までは同じです。そして、無事すべての行のチェックが終われば、リストに入っている各アドレスに向けてpingを実行する、これも同じ。
ですが、
名前解決できなかった場合、なんとその場でfping6自体が終了してしまうようなのです。IPv4アドレスが書かれていた場合、当然IPv6アドレスではないので名前解決しようとしますが、そんなのが登録されているわけもなく当然失敗します。
つまり、すべてが「IPv6アドレス」or「IPv6で名前解決できるホスト名」でないとすべて失敗するのです。
たとえば、
2001:db8::1
192.0.2.1
この二行が書いてあったとすると、2001:db8::1に対してpingを実行するよりも前に、192.0.2.1を読んだ時点で終了してしまいます。同じファイルをfpingに読ませると、2001:db8::1は失敗しますが192.0.2.1へのpingはきちんと実行されることが分かります。
fpingとfping6の違いがバグなのか仕様なのかは分かりませんが、ZABBIXではどちらもfpingのような挙動をするものだという前提で作ってあるように思えます。
#該当箇所のIPv4とIPv6で分岐している部分はfping→105行に対してfping6→18行しかないので、fping6の手抜き実装なのかも。
■というわけで・・・
条件を満たしたときにIPv6アドレスが設定してあるホストのicmppingが失敗することがあるのは、ZABBIXのバグというよりもfpingのバグ(or仕様)のせいのようです。
とはいえ、だからといってできないことに変わりはないしそれでは困る、しかしfping6の方は私には難しすぎる、というわけで、ZABBIX側のパッチを作ってみました(この投稿の一番最後に書きます)。
ZABBIX 側で対応すべきことなのかは悩ましいところですが・・・。
fping6にtmpをそのまま渡すのではなく、前もってtmpの各行についてIPv6で名前解決できるかどうかをチェックして、できるものだけを別のtmpファイル(/tmp/zabbix_server_プロセスID.pinger_v6)に保存し、fping6にはこちらのファイルを渡します。
これで試したところ、手元ではうまく動いているように見えます。が、ネットワークプログラミングはあまり経験が無いので、おかしなバグが紛れ込んでいるかもしれません(fping.cを参考に作りました)。[b]どなたか詳しくてお時間のある方、チェックしていただけると幸いです。[/b]
うまく動いた環境:
OS:FreeBSD 8.0-RELEASE-p2
ZABBIX:portsでインストール→ログには Zabbix 1.8.1 (revision 9702) と表示されます。
パッチ元のソース:/usr/ports/distfiles/zabbix-1.8.1.tar.gzを展開したものですが、おそらく本家サイトから直接ダウンロードしても同じだと思います。
fping, fping6:これもportsでインストール。fping -v で Version 2.4b2_to $Date: 2002/01/16 00:33:42 $ と表示されます。
FreeBSDで、このパッチを/usr/ports/net-mgmt/zabbix-server/files/patch-aaという名前で保存し、portupgrade -f zabbix-server を実行すれば、とりあえずは動きました(バージョン番号とかは変わりませんが)。CentOSではまだ試していません。
以上です。よろしくお願いします。
<code>↓↓↓↓↓ パッチここから ↓↓↓↓↓
--- src/libs/zbxicmpping/icmpping.c.orig 2010-01-28 06:22:44.000000000 +0900
+++ src/libs/zbxicmpping/icmpping.c 2010-03-01 11:15:29.000000000 +0900
@@ -42,6 +42,10 @@
#ifdef HAVE_IPV6
char *fping;
int family;
+
+ char filename6[MAX_STRING_LEN];
+ struct addrinfo hints, *res;
+ int not_v6;
#endif
assert(hosts);
@@ -68,6 +72,8 @@
zbx_get_thread_id());
#ifdef HAVE_IPV6
+ zbx_snprintf(filename6, sizeof(filename6), "%s_v6", filename);
+
if (access(CONFIG_FPING6_LOCATION, F_OK|X_OK) == -1)
{
zbx_snprintf(error, max_error_len, "%s: [%d] %s", CONFIG_FPING6_LOCATION, errno, strerror(errno));
@@ -96,7 +102,7 @@
filename,
CONFIG_FPING6_LOCATION,
params,
- filename);
+ filename6);
#else /* HAVE_IPV6 */
zbx_snprintf(tmp, sizeof(tmp), "%s %s 2>&1 <%s",
CONFIG_FPING_LOCATION,
@@ -119,12 +125,44 @@
fclose(f);
+#ifdef HAVE_IPV6
+ bzero(&hints, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_RAW;
+ hints.ai_protocol = IPPROTO_ICMPV6;
+
+ if (NULL == (f = fopen(filename6, "w"))) {
+ zbx_snprintf(error, max_error_len, "%s: [%d] %s", filename6, errno, strerror(errno));
+ return NOTSUPPORTED;
+ }
+
+ zabbix_log(LOG_LEVEL_DEBUG, "%s", filename6);
+
+ for (i = 0; i < hosts_count; i++)
+ {
+ not_v6 = getaddrinfo(hosts[i].addr, NULL, &hints, &res);
+ if (not_v6)
+ zabbix_log(LOG_LEVEL_DEBUG, "%s ... not IPv6", hosts[i].addr);
+ else
+ {
+ zabbix_log(LOG_LEVEL_DEBUG, "%s", hosts[i].addr);
+ fprintf(f, "%s\n", hosts[i].addr);
+ }
+ }
+
+ fclose(f);
+#endif /* HAVE_IPV6 */
+
zabbix_log(LOG_LEVEL_DEBUG, "%s", tmp);
if (0 == (f = popen(tmp, "r"))) {
zbx_snprintf(error, max_error_len, "%s: [%d] %s", tmp, errno, strerror(errno));
unlink(filename);
+#ifdef HAVE_IPV6
+ unlink(filename6);
+#endif /* HAVE_IPV6 */
return NOTSUPPORTED;
}
@@ -180,6 +218,9 @@
pclose(f);
unlink(filename);
+#ifdef HAVE_IPV6
+ unlink(filename6);
+#endif /* HAVE_IPV6 */
zabbix_log(LOG_LEVEL_DEBUG, "End of process_ping()");
↑↑↑↑↑ パッチここまで ↑↑↑↑↑</code>
kodai - 投稿数: 1341
こんにちは。
詳細な説明とパッチまで投稿していただいてありがとうございます。
みたところfping6のバグのように見受けられますね。
確認したいところではあるのですが、実は身近にIPv6の環境がないんです...。
試してみようとは思いますが、新規にLinux(CentOS)を構築して、Zabbixサーバ側でIPv6アドレスを利用してicmppingで監視を行い、かつそのIPv6アドレスが名前解決できないこと、という条件でテストすれば再現できるでしょうか?
heya - 投稿数: 319
こんにちは。
> みたところfping6のバグのように見受けられますね。
やっぱりそうですよね・・・。
自分で「バグ報告」に投稿しておいて何なんですが、ZABBIXのバグといってしまうのは違和感があります。
> Zabbixサーバ側でIPv6アドレスを利用してicmppingで監視を行い、
> かつそのIPv6アドレスが名前解決できないこと
それで大丈夫だと思います。あとそれに加えて、他にもホストを作成、そこでは存在しているIPv6アドレス(単独で手動fping6が成功するアドレス)を設定して、ついでに接続方法も「IPアドレス」にしておくといいと思います。そうすれば「名前解決できないホストのせいで、他のホストへのpingも巻き添えで失敗する」ことが分かりやすいので。
↑今まで接続方法を「DNS」にしたものは試したことが無かったので、ちょっとCentOSとFreeBSDで試してみました。
その他のホストとして
・名前解決できるDNS名をつけて接続方法を「DNS」にしたもの
・DNS名指定無しで接続方法を「IPアドレス」にしたもの
をそれぞれいくつか登録し、各ホストにicmppingのアイテムを追加してみたところ、なぜか「DNS」の方は毎回成功していました。「IPアドレス」の方はタイミングにより(ちゃんと?)巻き添えを食らっていましたが。
うーん、不思議です。
余談ですが、(少なくとも手元の環境では)FreeBSDでは設定の変更などをしない限りチェック間隔がほとんどずれないのに、CentOSだとすぐに数秒程度ばらついてずれてしまうようです(起動直後ですらばらついていました)。なのでタイミングが合うまでしばらく待たなければなりませんでした。名前解決の実装とかが微妙に違うんですかね・・・。
heya - 投稿数: 319
こんにちは。
以前、fpingをいじるのは難しすぎると敬遠していたのですが、別に完璧は求めなくてもいいかと開き直って、
> 名前解決できなかった場合、なんとその場でfping6自体が終了してしまうようなのです。
この部分だけ修正してみました(FreeBSDのみ)。
つまり、終了ではなくスキップするように書き換えてみたということです。
<code>--- fping.c.orig 2010-06-07 16:28:18.000000000 +0900
+++ fping.c 2010-06-07 16:54:43.000000000 +0900
@@ -2174,10 +2174,20 @@
hints.ai_protocol = IPPROTO_ICMPV6;
ret_ga = getaddrinfo(name, NULL, &hints, &res);
- if (ret_ga) errx(1, "%s", gai_strerror(ret_ga));
+ if (ret_ga) {
+ if( !quiet_flag )
+ fprintf( stderr, "%s address not found\n", name );
+ num_noaddress++;
+ return;
+ }
if (res->ai_canonname) hostname = res->ai_canonname;
else hostname = name;
- if (!res->ai_addr) errx(1, "getaddrinfo failed");
+ if (!res->ai_addr) {
+ if( !quiet_flag )
+ fprintf( stderr, "%s address not found\n", name );
+ num_noaddress++;
+ return;
+ }
(void)memcpy(&dst, res->ai_addr, sizeof(FPING_SOCKADDR)); /*res->ai_addrlen);*/
add_addr(name, name, &dst);
#endif</code>
FreeBSDで、このパッチを/usr/ports/net/fping+ipv6/files/patch-aaという名前で保存して(※)
portupgrade -f fping+ipv6 でパッチ適用できます。相変わらずバージョン番号とかは変わりません。
※他にpatch-02、patch-03というファイルがすでに存在していましたが、patch-aaという名前で大丈夫みたいです。
試しにIPv4アドレスとIPv6アドレスが混在したファイルを使って
fping6 < tmp のように実行しても期待通りの動きをしました。また、
インストール直後の(つまりソースは変更していない)ZABBIX + このパッチを当てたfipng6
でしばらく動かしていますが、今のところ問題なく動いています。
どうせソースをいじるなら、ZABBIXよりもfpingの方が更新頻度ははるかに少ない
(2007年以降更新されてない)ので、こっちを変更する方が手軽だと思います。
#souceforgeを見ても、fpingの作者さんやる気が無いのかな?と思えるし。
CentOS(yum)では、少し元ソースが違うかもしれません。
yumdownloader --source fping で取得したfping-2.4b2-7.el5.src.rpmをインストールして
そのソースを見たところ、/usr/src/redhat/SOURCES/fping-2.4b2_ipv6-fix.diffという
ファイルが存在していました。yum版はおそらくこのパッチが当たっているのだろうと思います。
fping.c自体は、FreeBSDのportsのものと全く同じでした。
kodai - 投稿数: 1341
いくつかのディストリビューションのパッケージを調べてみたのですが、Debian/Ubuntuのパッケージでは上記の対応も含め色々と改善されているようでした。
逆にFedoraやepelリポジトリのパッケージでは対応がされていないようです。
先ほどZABBIX-JPのRHEL/CentOS用のfpingパッケージにDebian/Ubuntuのパッチを含めてバージョンアップを行いました。
http://www.zabbix.jp/modules/news/article.php?storyid=184
パッチはこちらにありますのでご参考ください。
http://sourceforge.jp/projects/zabbix/svn/view/tags/fping/2.4-16/fping-2.4b2_to-ipv6-16.diff?root=zabbix&view=log
heya - 投稿数: 319
こんにちは。
> 先ほどZABBIX-JPのRHEL/CentOS用のfpingパッケージにDebian/Ubuntuのパッチを含めてバージョンアップを行いました。
試してみたところ、うまく動きました。ありがとうございます。
> パッチはこちらにありますのでご参考ください。
> http://sourceforge.jp/projects/zabbix/svn/view/tags/fping/2.4-16/fping-2.4b2_to-ipv6-16.diff?root=zabbix&view=log
このパッチをFreeBSDのports版にも使えるかなと思ったのですが、どうやらこちらはデフォルトで当てられているpatch-02、patch-03と衝突する部分があり、そのままだとパッチ当てに失敗するようです。そうなると、選択肢は手動でマージするかあきらめるかのどちらかになると思いますが(他の選択肢もある???)、実際どうするかはポリシーによって決まってくるのでしょうね。
あと、一つおせっかいを。
改行コードが、
fping.c・・・\n
fping.8・・・\r\n
patch-02(fping.cへのパッチ)・・・\n
patch-03(fping.8へのパッチ)・・・\n
fping-2.4b2_to-ipv6-16.diff・・・fping.cの部分は\n、fping.8の部分は\r\n
となっています。
portupgradeなどでは、make中に改行コードを強制的に\nにするようなので問題ないのですが、patch-03を(改行コードを変換せずにそのまま)手動でpatchコマンドを使ってパッチを当てようとすると、改行コードの違いでうまくいきません(一応 -l オプションで空白の違いを無視させることも可能ですが、混乱の元になるかも)。パッチの手動マージを試そうとする場合などにはまりやすいので注意してください。
kodai - 投稿数: 1341
ご指摘どうもありがとうございます。RPMでしかパッチを適用していなかったのであまり注意してませんでした :-P
fpingやその他のパッチについても気をつけて作成するようにします。