プログラマー探偵の事件簿:TWSNMPがブロードキャストPINGを送信
TWSNMPが驚くべきネットワーク上の迷惑行為をしていた事件を解決した話である。今日は雨がずっと降っている。助手の猫は定位置のソファーに戻って仕事して(寝て)いる。
事件の始まり
TWSNMPのデバックログを見ていると時々、
ERROR[07/04 06:23:16.492]pingBackend target=192.168.1.255 src=192.168.1.21:0
ERROR[07/04 06:23:16.493]pingBackend target=192.168.1.255 src=192.168.1.211:0
ERROR[07/04 06:23:16.493]pingBackend target=192.168.1.255 src=192.168.1.16:0
ERROR[07/04 06:23:16.503]pingBackend target=192.168.1.255 src=192.168.1.23:0
ERROR[07/04 06:23:16.554]pingBackend target=192.168.1.255 src=192.168.1.15:0
ERROR[07/04 06:23:16.607]pingBackend target=192.168.1.255 src=192.168.1.20:0
ERROR[07/04 06:23:19.472]pingBackend target=192.168.1.255 src=192.168.1.21:0
ERROR[07/04 06:23:19.473]pingBackend target=192.168.1.255 src=192.168.1.211:0
ERROR[07/04 06:23:19.473]pingBackend target=192.168.1.255 src=192.168.1.16:0
ERROR[07/04 06:23:19.477]pingBackend target=192.168.1.255 src=192.168.1.23:0
ERROR[07/04 06:23:19.523]pingBackend target=192.168.1.255 src=192.168.1.15:0
ERROR[07/04 06:23:19.577]pingBackend target=192.168.1.255 src=192.168.1.20:0
のようなログが表示されていた。気になっていたが調べるのは後回しにしていた。
このログの意味
このログは私がプログラムの中に組み込んでいたのもで、送信したPINGのリクエストの応答が送信先以外から返った場合に出力するものである。今回のログだとtarget=192.168.1.255が送信先、応答を返したのは、src=192.168.1.20など複数である。つまり一つのPINGの送信先のリクエストに複数の応答があったということだ。絵にすると
のような感じである。
192.168.1.255はブロードキャスト
なぜ、沢山の応答があったかというと送信先のアドレス(192.168.1.255)がブロードキャストアドレスだからだ。ブロードキャストアドレスはネットワークの全員に送信するという魔法のアドレスだ。そんなアドレスになぜPINGのリクエストを送信したのか?
犯人は私だ!
今回は自分で作ったログなのでGoogleに聞いても教えてくれない。TWSNMPがPINGを送信するのは私が作った処理なので、どう考えても他の人が犯人ではない。真面目に自分の作ったソースコードを調べた。
接続しているネットワーク上の全ノードをリストアップするために付けたARP監視の機能から送信されていることがわかった。何という迷惑行為!
ブロードキャストを除外していない
ARP監視ではTWSNMPが稼働するパソコンが接続されているネットワークの全アドレスにPINGを送信する処理を実施している。この全アドレスに、ブロードキャストが含まれていないのが原因だった。問題の場所は、
for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); incIP(ip) {
if !ip.IsGlobalUnicast() || ip.IsMulticast() ||
ip.Equal(ip.Mask(ipnet.Mask)) {
continue
}
// PING送信
IsGlobalUnicast()関数
でブロードキャストは除外しているつもりだった。でも詳しくこの関数のソースコードを読むと
var IPv4bcast = IPv4(255, 255, 255, 255) // limited broadcast
func (ip IP) IsGlobalUnicast() bool {
return (len(ip) == IPv4len || len(ip) == IPv6len) &&
!ip.Equal(IPv4bcast) &&
!ip.IsUnspecified() &&
!ip.IsLoopback() &&
!ip.IsMulticast() &&
!ip.IsLinkLocalUnicast()
}
除外しているブロードキャストは、255.255.255.255のlimited broadcastだけだった。192.168.1.255のようなネットワーク指定のブロードキャストは除外していなかった。落とし穴だった。
ブロードキャストアドレスの判断
GO言語のnetパッケージのマニュアル
を調べたがIsBroadcast()のような便利な関数は見つからない。困ったここでGoogleに「golang ip mask reverse」と聞いてみて見つけた
おかげで、
mask := ipnet.Mask
broadcast := net.IP(make([]byte, 4))
for i := range ip {
broadcast[i] = ip[i] | ^mask[i]
}
astiLogger.Infof("arpWatch Check IP %s %s", cidr, broadcast)
for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); incIP(ip) {
if !ip.IsGlobalUnicast() || ip.IsMulticast() ||
ip.Equal(ip.Mask(ipnet.Mask)) || ip.Equal(broadcast) {
continue
}
// PING送信
のように修正して、この事件を解決した。
開発のための諸経費(機材、Appleの開発者、サーバー運用)に利用します。 ソフトウェアのマニュアルをnoteの記事で提供しています。 サポートによりnoteの運営にも貢献できるのでよろしくお願います。