このエントリーをはてなブックマークに追加

centos 5(6も?) arping バグ

ちょっと前に調べたので。rstに慣れる意味で書いてみた。

今となっては使っている人はいないかもしれないけど centos 5 の arping はバグってるというお話。 ちなみに、 centos 6 はちゃんと調査してませんが、同じパッチがあたってるようなのでダメかもしれません。 というか centos 6 でも、後述する -c 偶数指定で指定した半分しかarpを出さない問題は残ってました。

実際に見た方が早いと思うので。

-bash-3.2$ date; sudo ./arping -I eth1 -b -c 3 2.2.2.2; echo $?
2012年  9月  1日 土曜日 20:34:21 JST
ARPING 2.2.2.2 from 10.1.1.220 eth1
Sent 3 probes (3 broadcast(s))
Received 0 response(s)
1
-bash-3.2$ date; sudo ./arping -I eth1 -b -c 3 2.2.2.2; echo $?
2012年  9月  1日 土曜日 20:34:26 JST
ARPING 2.2.2.2 from 10.1.1.220 eth1
0
-bash-3.2$ date; sudo ./arping -I eth1 -b -c 3 2.2.2.2; echo $?
2012年  9月  1日 土曜日 20:34:30 JST
ARPING 2.2.2.2 from 10.1.1.220 eth1
Sent 3 probes (3 broadcast(s))
Received 0 response(s)
1
-bash-3.2$ date; sudo ./arping -I eth1 -b -c 3 2.2.2.2; echo $?
2012年  9月  1日 土曜日 20:34:35 JST
ARPING 2.2.2.2 from 10.1.1.220 eth1
0

2.2.2.2 はありもしないアドレスなので、成功するはずが無いはずなのに、正常終了しています。 ちなみに、この現象は割とホストがたくさんぶら下がっていて arp がとびかってる環境で発生します。

なにはともあれ、src.rpmを展開してソースを見てみると、

$ mkdir ipuitls
$ wget ftp://ftp.pbone.net/mirror/vault.centos.org/5.8/os/SRPMS/iputils-20020927-46.el5.src.rpm
$ rpm2cpio iputils-20020927-46.el5.src.rpm | cpio -id
$ mkdir SOURCES
$ cp * SOURCES
$ rpmbuild -bp --define "_topdir `pwd`" iputils.spec
$ cd BUILD/iputils
$ make arping
$ less arping.c
 .
 . snip
 .
184         if ((timeout && MS_TDIFF(tv,start) > timeout*1000 + 500) ||
185                 ((count == 0) && (!timeout)))
186                 finish();
187
188         if (last.tv_sec==0 || MS_TDIFF(tv,last) > 500) {
189                 count--;
190                 send_pack(s, src, dst, &me[0], &he[0]);
191                 if (count == 0 && unsolicited)
192                         finish();
193         }
194         alarm(1);
 .
 . snip
 .
597                 if ((cc = recvfrom(s, packet, sizeof(packet), 0,
598                                    (struct sockaddr *)&from[0], (socklen_t*)&alen)) < 0) {
599                         perror("arping: recvfrom");
600                         continue;
601                 }
602                 sigemptyset(&sset);
603                 sigaddset(&sset, SIGALRM);
604                 sigaddset(&sset, SIGINT);
605                 sigprocmask(SIG_BLOCK, &sset, &osset);
606                 recv_pack(packet, cc, &from[0]);
607                 if(received == count)
608                         exit(0);
 .
 . snip
 .

この辺りがすべてを物語っていて、パケットを送信するとcountがデクリメントされて、 なにかしら arp っぽいものを受信していれば count の値と比較して同じなら正常終了する。 ちなみに、 received はちゃんとした reply の arp を受け取ればインクリメントされるので、 この場合、ありもしないホストからちゃんとした reply が帰ってくる事は無いので、常に0。 count が 0 になったときに arp っぽいなにかを受信すれば、正常終了となるバグ。 じゃー、なぜちゃんと失敗する場合があるのかというと、 count が 0 になった状態で SIGALRM をうければ finish() が呼ばれてちゃんと失敗と見なされる。 つまり、最後の最後に、 SIGALRM を受けるのが先か arp っぽいパケットを受けるのが先かで結果が変わる。

補足ですが、しれっと arp っぽいとか書いてますが、これはrecvfromで拾えたパケットという事です。 sokcet自体はPF_PACKETを指定しているので、その対象になる生パケットならなんでもよいという事です。

もう、勘のいい人は気づいたと思いますが。

-bash-3.2$ date; sudo ./arping -I eth1 -b -c 5 10.1.1.1
2012年  9月  1日 土曜日 21:20:23 JST
ARPING 10.1.1.1 from 10.1.1.220 eth1
Unicast reply from 10.1.1.1 [00:1B:FC:BD:CF:AA]  0.605ms
Unicast reply from 10.1.1.1 [00:1B:FC:BD:CF:AA]  0.631ms
Unicast reply from 10.1.1.1 [00:1B:FC:BD:CF:AA]  0.619ms
Unicast reply from 10.1.1.1 [00:1B:FC:BD:CF:AA]  0.648ms
Unicast reply from 10.1.1.1 [00:1B:FC:BD:CF:AA]  0.685ms
Sent 5 probes (5 broadcast(s))
Received 5 response(s)

-bash-3.2$ date; sudo ./arping -I eth1 -b -c 6 10.1.1.1
2012年  9月  1日 土曜日 21:20:30 JST
ARPING 10.1.1.1 from 10.1.1.220 eth1
Unicast reply from 10.1.1.1 [00:1B:FC:BD:CF:AA]  0.655ms
Unicast reply from 10.1.1.1 [00:1B:FC:BD:CF:AA]  0.639ms
Unicast reply from 10.1.1.1 [00:1B:FC:BD:CF:AA]  0.682ms

存在するホストに対して、 -c のカウントを偶数回にすると半分しか arp を送りません。 で、この問題を引き起こしているパッチは iputils-20020927-12-arping.patch のようです。

解決策ですが、ひとまず、以下のパッチをあてると count = 0 で正常終了する問題は回避できました。

--- arping.c.orig       2012-09-01 21:28:07.443016686 +0900
+++ arping.c    2012-09-01 21:29:22.710022302 +0900
@@ -373,6 +373,7 @@
 {
        int socket_errno;
        int ch;
+       int lcount;
        uid_t uid = getuid();

        s = socket(PF_PACKET, SOCK_DGRAM, 0);
@@ -400,7 +401,7 @@
                        quiet++;
                        break;
                case 'c':
-                       count = atoi(optarg);
+                       count = lcount = atoi(optarg);
                        break;
                case 'w':
                        timeout = atoi(optarg);
@@ -604,7 +605,7 @@
                sigaddset(&sset, SIGINT);
                sigprocmask(SIG_BLOCK, &sset, &osset);
                recv_pack(packet, cc, &from[0]);
-               if(received == count)
+               if(received == lcount)
                        exit(0);
                sigprocmask(SIG_SETMASK, &osset, NULL);
        }

ちなみに、

if(received == lcount)
      exit(0);

このexit(0)はfinish()が正解なんじゃないかと思うのだけれど、、、今回はそのままにしておきました。

centos 6 はというと

[root@hoge iputils]# cat /etc/issue
CentOS release 6.2 (Final)
Kernel \r on an \m

[root@hoge iputils]# arping -b -c 5 49.212.138.1 ; echo $?
ARPING 49.212.138.1 from 49.212.138.173 eth0
Unicast reply from 49.212.138.1 [00:00:5E:00:01:6B]  2.760ms
Unicast reply from 49.212.138.1 [00:00:5E:00:01:6B]  6.674ms
Unicast reply from 49.212.138.1 [00:00:5E:00:01:6B]  5.571ms
Unicast reply from 49.212.138.1 [00:00:5E:00:01:6B]  7.492ms
Unicast reply from 49.212.138.1 [00:00:5E:00:01:6B]  2.950ms
Sent 5 probes (5 broadcast(s))
Received 5 response(s)
0
[root@hoge iputils]# arping -b -c 4 49.212.138.1 ; echo $?
ARPING 49.212.138.1 from 49.212.138.173 eth0
Unicast reply from 49.212.138.1 [00:00:5E:00:01:6B]  2.727ms
Unicast reply from 49.212.138.1 [00:00:5E:00:01:6B]  2.550ms
0

えーっと、、、、、

どうやら、同じパッチがあたっているようです。

どなたに言えばいいんでしょう?

こういうの。

name:
email:
comment: