見出し画像

TWSNMPでネットワークを隠れて使っている機器を漏れなく探す技!

TWSNMPにはARPテーブルをモニタしてネットワークを使っている機器(パソコン、スマフォ)を探す機能があります。接続している機器のリストは、ネットワークを管理する上で、一番基本的な情報だと思います。
TWSNMPで最初に作ったのはARPパケットを受信した時だけ見つけるような受け身の方法だったので全ての機器を漏れなく探すことができませんでした。これを改善したアイデアを書いておきます。

ARPについて

ARPについては

を見てください。
ARPは、現在使われているネットワーク(有線、無線問わず)で必ず通信する時に利用するプロトコルです。パソコンのファイヤーフォールの設定でPINGなどの応答しないようにしてもARPは止めることができません。なので、ネットワークを使用している機器を探すにはうってつけの方法だと思っています。

ARPをモニタする

ARPをモニタするためにはパケットキャプチャーするようなことは必要ありません。大抵のOSでは、

% arp -a
ntt.setup (192.168.1.1) at 0:25:36:ab:77:53 on en3 ifscope [ethernet]
? (192.168.1.2) at 84:af:ec:f1:88:d0 on en3 ifscope [ethernet]
? (192.168.1.4) at 84:af:ec:ab:bc:50 on en3 ifscope [ethernet]
? (192.168.1.5) at 18:66:da:c:43:a1 on en3 ifscope [ethernet]

というコマンドで表示されます。この情報からIPアドレスとMACアドレスをの関係をリストアップすればネットワークを使っている機器を調べられます。TWSNMPでは、

を参考にして、この機能を実現しました。
このモニタの結果を集計すれば、

画像1

のようなレポートが作成できます。

漏れはないかという心配

ARPをモニタする方法では、ARPパケットを受信しない限り機器を見つけることはできません。当然です。TWSNMPと通信しない機器は見逃すのではないか?という心配が生まれました。ずっと気になっていて、良い方法がないか考えていました。

自分から通信すればよい

問題なのはTWSNMPを通信しないことなのでTWSNMPから定期的に通信すればよいはずです。自動発見で機器を探す時には検索範囲のIPアドレスに対して最初にPINGを実施しているのでARPの通信も発生しています。その時点のネットワーク機器は発見できています。でも、これを定期的に繰り返すのは、ネットワークに負荷かけるのとTWSNMPが不審者になってしまいます。
そこで、もう少しゆったりした間隔でTWSNNPが直接接続されているネットワークの全アドレスにPINGを実施する方法を作ることにしました。

直接接続されているネットワークの全アドレス

TWSNMPの利用者に設定してもらう方法が簡単ですが、それでは芸がない。自動的に決められないか?と考えるのがプログラマの性。
考えました。記事に書いて置きたいアイデアは、ここからです。

・TWSNMPを動かしているパソコンのLANポートをリストアップする。
・使用していないLANポートは無視する。
・そのLANポートのネットワークアドレスを調べる。
・ネットワークアドレスに含まれるIPアドレスをリストアップする。

という考えで実現できました。作ったプログラムは、

var arpTable = make(map[string]string)
var localCheckAddrs []string

func makeLoacalCheckAddrs() {
	ifs, err := net.Interfaces()
	if err != nil {
		return
	}
	localIPCount := 0
	localHitCount := 0
	for _, i := range ifs {
		if (i.Flags&net.FlagLoopback) == net.FlagLoopback ||
			(i.Flags&net.FlagUp) != net.FlagUp {
			continue
		}
		addrs, err := i.Addrs()
		if err != nil {
			continue
		}
		for _, a := range addrs {
			cidr := a.String()
			ip, ipnet, err := net.ParseCIDR(cidr)
			if err != nil {
				continue
			}
			if ip.To4() == nil {
				continue
			}
			for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); incIP(ip) {
				if !ip.IsGlobalUnicast() {
					continue
				}
				localIPCount++
				sa := ip.String()
				if _, ok := arpTable[sa]; ok {
					localHitCount++
				}
				localCheckAddrs = append(localCheckAddrs, sa)
			}
		}
	}
	lau := 0.0
	if localIPCount > 0 {
		lau = 100.0 * float64(localHitCount) / float64(localIPCount)
	}
	addEventLog(eventLogEnt{
		Type:  "system",
		Level: "info",
		Event: fmt.Sprintf("ARP監視 ローカルアドレス使用量 %d/%d %.2f%%", localHitCount, localIPCount, lau),
	})
}

func incIP(ip net.IP) {
	for j := len(ip) - 1; j >= 0; j-- {
		ip[j]++
		if ip[j] > 0 {
			break
		}
	}
}

です。1日試した限りでは、うまくいっています。

つづく

開発のための諸経費(機材、Appleの開発者、サーバー運用)に利用します。 ソフトウェアのマニュアルをnoteの記事で提供しています。 サポートによりnoteの運営にも貢献できるのでよろしくお願います。