たまにLANに接続している機器のIPアドレスを知りたい時がある。PCやゲーム機あたりなら本体の機能でIPアドレスを確認すれば済む。しかし相手がホームゲートウェイ/ブロードバンドルータや無線LANアクセスポイントやUSBメディアサーバで、ハードウェア本体にはIPアドレス表示機能がなく、Web管理画面からログインしたいのだけどアドレスが分からないので管理画面にすらたどり着けない──そんな時に、とりあえずLANに接続している機器のIPアドレスを列挙して、そこから絞り込んでいったりする。
(UPnPやDLNAやBonjour経由でWeb管理画面にたどり着けるとは限らない。古い機器なのでUPnP的な機能を持っていなかったり、クライアントPCがWindowsでいうところの「ネットワーク探索」を無効にしていたりすることもあるのだ)
他には、例えばLANにネットワーク対応のプリンタを接続して固定IPを割り振りたいのだけど既存のサーバやプリンタその他が使用しているIPアドレスが分からないとか、Raspberry Pi OSを入れたRaspberry Piの初期設定をしたいのだけど諸事情によりディスプレイもキーボードも接続できないので初っ端からssh接続したいとか、そういう場合にも同じようなことをしたくなる。
安直な解法としては、LANの全IPアドレスに対してpingを実行して反応を調べればよい。ファイアーウォール等でpingに応答しない設定にしている機器は列挙できないが、大抵の場合、応答しないのはPCだ(PCだから普通にログオンしてIPアドレスを確認すればよい)。
という訳でping(8)で絨毯爆撃するシェルスクリプトを書いてみた(IPアドレスは各自の環境に合わせて調整すること)。
#!/bin/sh # -*- coding: utf-8-unix -*- # vim:fileencoding=utf-8:ff=unix # # Worked on Ubuntu 20.04 readonly addrbase=192.0.2. for i in $(seq 1 254); do addr=$addrbase$i ping -c 1 -w 1 $addr >/dev/null [ $? -eq 0 ] && echo $addr done
環境によってping(8)のタイムアウトを設定するオプションが異なるので注意。
このスクリプトは、安直に「タイムアウト1秒の設定で1回だけICMP Echoを送信する」という処理をIPアドレスごとに順番に実行している。そのため非常に遅い。LANに接続している機器が少なかったり、接続していてもpingに反応しない機器が多いほど――つまりICMP Echo Replyが返ってくる可能性が低いほど遅くなる。最大で1秒かかる処理を254回実行するので、ワーストで4分13秒ぐらいかかる計算になる*1。
4分以上もかかるのは不便なので、高速化してみた。
#!/bin/sh # -*- coding: utf-8-unix -*- # vim:fileencoding=utf-8:ff=unix # # Worked on Ubuntu 20.04 readonly addrbase=192.0.2. # do_ping <IP address> do_ping () { ping -c 1 -w 1 $1 >/dev/null [ $? -eq 0 ] && echo $1 } { for i in $(seq 1 254); do do_ping $addrbase$i & done wait } | sort -V
バックグラウンドプロセスを使ってping(8)を並行実行させるようにしてみた。これなら1秒ちょっとで完了する。
並行実行の代償として、IPアドレスの出力順が不定となる。これをソートするのに、IPv4アドレスならGNU coreutilsのsort(1)のオプション-V
が使える。他の環境には-V
が無いので、別のオプションの組み合わせで対応することになる(例えばsort -t . -k 4,4 -n
だろうか)。
なおIPv6のことは考えていない。自宅や会社のLANがIPv6になってから考えるつもり。
*1:4分14秒ではなく4分13秒なのは、LANに接続している機器のうち1台は自分自身なので、ほぼ確実に数ミリ秒程度でレスポンスが帰ってくるからだ。