Gマイナー志向

とくに意味はありません

Nagiosのcheck_pingが遅いのをなんとかする

Nagiosのcheck_pingコマンド、やたら遅いですよね。

# time sudo -u nagios /usr/lib64/nagios/plugins/check_ping -H 127.0.0.1 -w 200,40% -c 500,80%
PING OK - Packet loss = 0%, RTA = 0.05 ms|rta=0.053000ms;200.000000;500.000000;0.000000 pl=0%;40;80;0

real    0m4.171s
user    0m0.028s
sys     0m0.112s

ほんと遅い。なんでこんなに遅いの。調べてみました。

check_pingの仕組み

check_pingのソースを読んだところpingコマンドの実行結果をパースしているだけでした。

check_pingnagios-pluginsのconfigure時に指定されたpingコマンドを使ってコマンドを実行します。
特に指定がなかったり、rpmなどからインストールした場合は標準のpingコマンドが使用されます。
RHEL環境の場合、デフォルトのpingコマンドは次のようになるようです。

/bin/ping -n -U -w %d -c %d %s

引数の意味ですが、-nはDNS逆引きをしない、-Uは古いフォーマットで出力、-w %dはタイムアウト設定、-c %dは回数、%sはping対象ホストが入ります。

このpingコマンドにデフォルトのタイムアウト10秒、回数5回を設定して打ってみると、

# time /bin/ping -n -U -w 10 -c 5 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.031 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.068 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.154 ms
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.045 ms
64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=4.51 ms

--- 127.0.0.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4044ms
rtt min/avg/max/mdev = 0.031/0.963/4.518/1.778 ms

real    0m4.063s
user    0m0.006s
sys     0m0.025s

あーなるほど。デフォルト設定だと1秒間隔で5回打つので、4秒+αになるんですね。そりゃ遅いわ。

解決方法を調べてみました。今のところ3つあります。

check_icmpもしくはcheck_fpingを使う

ping先がIPv4なアドレスなら、check_icmpもしくはcheck_fpingを使いましょう。

check_icmpを一般ユーザーが使う場合、setuidが必要です。rpmなどでインストールしていればすでに設定されていると思いますが、設定されていなければ手動で設定しましょう。以下はnagiosユーザで使用する例。

# ls -l check_icmp 
-rwx--x--- 1 root nagios 51840 Dec  7 16:20 check_icmp
# chown root:nagios check_icmp
# chmod u+s check_icmp
# ls -l check_icmp
-rws--x--- 1 root nagios 51840 Dec  7 16:20 check_icmp
# sudo -u nagios check_icmp 127.0.0.1
OK - 127.0.0.1: rta 0.021ms, lost 0%|rta=0.021ms;200.000;500.000;0; pl=0%;40;80;; rtmax=0.064ms;;;; rtmin=0.009ms;;;; 

Nagiosは実コマンドとコマンド定義を分離できるため、定義ファイルを修正するだけで対応が可能です。check_pingをcheck_icmpやcheck_fpingで置き換える場合は-pを-nに変えるだけで大体ok。

--- objects/commands.cfg.orig   2012-12-05 06:03:02.000000000 +0900
+++ objects/commands.cfg        2012-12-29 15:52:22.197402219 +0900
@@ -55,7 +55,7 @@
 # 'check-host-alive' command definition
 define command{
         command_name    check-host-alive
-        command_line    $USER1$/check_ping -H $HOSTADDRESS$ -w 3000.0,80% -c 5000.0,100% -p 5
+        command_line    $USER1$/check_icmp -H $HOSTADDRESS$ -w 3000.0,80% -c 5000.0,100% -n 5
         }
 
 
@@ -165,7 +165,7 @@
 # 'check_ping' command definition
 define command{
         command_name    check_ping
-        command_line    $USER1$/check_ping -H $HOSTADDRESS$ -w $ARG1$ -c $ARG2$ -p 5
+        command_line    $USER1$/check_icmp -H $HOSTADDRESS$ -w $ARG1$ -c $ARG2$ -n 5
         }


ただし、check_pingIPv6に対応していますが、check_icmpやcheck_fpingはIPv6に対応していません。

ping実行時に-i 0.2を入れるようnagios-pluginsを作り直す

最近のpingping間隔を設定する-iオプションに1以下の数字を設定できます*1。お使いの環境でまず-iオプションが使えるか調べてみましょう。

$ ping -i 0.2 -c 5 127.0.0.1

0.2秒間隔でpingが打てれば問題ないです。check_pingが実行するpingに-i 0.2を設定してやればいいことになります。

そこで、nagios-pluginsのconfigure時に以下のように設定すればokです。

# ./configure \
  --with-ping-command="/bin/ping -n -U -w %d -c %d -i 0.2 %s" \
  --with-ping6-command="/bin/ping6 -n -U -w %d -c %d -i 0.2 %s"
# make

specファイルからrpmを作るならspecファイルに追加してrpmbuildですね。

これで生成されたcheck_pingは5回試行する場合でも約1秒程度で処理ができます。

# time sudo -u nagios /usr/lib64/nagios/plugins/check_ping -H 127.0.0.1 -w 200,40% -c 500,80%
PING OK - Packet loss = 0%, RTA = 2.16 ms|rta=2.164000ms;200.000000;500.000000;0.000000 pl=0%;40;80;0

real    0m0.932s
user    0m0.019s
sys     0m0.075s

やったね!

check_ping脆弱性を突いて引数インジェクションで-i 0.2を無理矢理ねじ込む

check_pingは引数チェックの実装が甘く、ホスト名指定の部分で引数インジェクションが可能です。これを使って再コンパイルすることなく無理矢理オプションを追加することが可能です。

# time sudo -u nagios /usr/lib64/nagios/plugins/check_ping -H "127.0.0.1 -i 0.2" -w 200,40% -c 500,80%
PING OK - Packet loss = 0%, RTA = 2.16 ms|rta=2.164000ms;200.000000;500.000000;0.000000 pl=0%;40;80;0

real    0m0.932s
user    0m0.019s
sys     0m0.075s

コマンド定義するならこんな感じ。

--- objects/commands.cfg.orig   2012-12-05 06:03:02.000000000 +0900
+++ objects/commands.cfg        2012-12-29 17:13:12.356065243 +0900
@@ -55,7 +55,7 @@
 # 'check-host-alive' command definition
 define command{
         command_name    check-host-alive
-        command_line    $USER1$/check_ping -H $HOSTADDRESS$ -w 3000.0,80% -c 5000.0,100% -p 5
+        command_line    $USER1$/check_ping -H "$HOSTADDRESS$ -i 0.2" -w 3000.0,80% -c 5000.0,100% -p 5
         }
 
 
@@ -165,7 +165,7 @@
 # 'check_ping' command definition
 define command{
         command_name    check_ping
-        command_line    $USER1$/check_ping -H $HOSTADDRESS$ -w $ARG1$ -c $ARG2$ -p 5
+        command_line    $USER1$/check_ping -H "$HOSTADDRESS$ -i 0.2" -w $ARG1$ -c $ARG2$ -p 5
         }
 
 

ただし、将来のバージョンで脆弱性が修正されるかもしれないので未来はありません。

まとめ

  • IPv4で完結するのであれば、check_fpingもしくはcheck_icmpを使いましょう
  • IPv6も使うのであれば、nagios-pluginsを作りなおせば良いですよ
  • nagios-pluginsを作りなおすのが面倒であれば、check_ping脆弱性を突けばいいと思いますよ

*1:ただしroot権限でなければ0.2秒より短くできません