WindowsのコマンドでLAN内のデバイスを探す
文●塩田紳二 編集● ASCII
2021年08月22日 10時00分
自宅のネットワークなどで、接続しているPCを全部見つけたいことがある。エクスプローラーのネットワークから見つけることもできるが、スクリプトなどで処理したい場合に手入力するのは面倒だ。そんなときには、PowerShellのGet-NetNeighborコマンドが利用できる。
Get-NetNeighborでアクセス可能なIPアドレスを探す
LAN内のネットワークノードを探すのは面倒な処理だ。たとえば、クラスCのプライベートネットワークならノード数は多くても254個なので、すべてのアドレスに対して、Pingコマンドを実行して応答の有無でノードの存在を確認することも不可能ではない。しかし、実際にスクリプトを書くとなると結構面倒なものになる。なので、PowerShellに組み込まれているネットワーク機能を利用するのが簡単。
Get-NetNeighborは、arpなどを使って得られたIPアドレスの情報を返すコマンドだ。
●Get-NetNeighbor (NetTCPIP)
https://docs.microsoft.com/ja-jp/powershell/module/nettcpip/get-netneighbor?view=windowsserver2019-ps
このコマンドでは、Windowsがネットワークを探索して見つけたアクセス可能なIPアドレスを返す。LANに接続していて電源が入っているPCなら、この中にIPアドレスが含まれているはずだ。ただし、Get-NetNeighborが返すIPアドレスには、PC以外のもの、たとえば、プリンターやルーター、テレビにDVDレコーダーなど、同じLANに接続している「ノード」のほとんどが含まれる。
まずは使い方だが、コマンドを単独で実行すると、すべてのネットワークインターフェースが対象になってしまう。Windows 10では、WSL2のような仮想マシンも動作しているため、多数のネットワークインターフェースが接続しているはずだ。
コントロールパネルの「ネットワークとインターネット」→「アダプター設定の変更」で表示される「ネットワーク接続」を見ると、多数のネットワーク(アダプター)が存在する場合がある。Get-NetNeighborはすべてのネットワークアダプターの情報を返すので、まずは、ANに接続しているアダプターを特定する必要がある。このとき、インターフェースのインデックス番号または名前を指定しなければならない。
ネットワークインターフェースの一覧は、Get-NetAdapterで得ることができる。
このコマンドが出力するオブジェクトの“ifIndex”プロパティがGet-NetNeighborの“-InterfaceIndex”で指定する番号に、“Name”が“-InterfaceAlias”で指定するインターフェース名になる。仮想ネットワークインターフェースを排除して、かつ動作しているインターフェースだけを表示させるには、
Get-NetAdapter -Physical | Where-Object {$_.Status -eq "UP" } | select-object Name,ifIndex
とする。これで、イーサネットや無線LANのインターフェースが表示されるはずだ。どっちを使うのかは、どちらでLANを使っているのかに依存するので各自で判断してほしい。
インターフェース番号(ここでは4とした)が確定したら、Get-NetNeighborを以下のように起動する。なお、ここでは、IPアドレスをIPv4のものに限定した。IPv6のアドレスもあるはずだが、話がややこしくなるので、ここでは、IPv4に限定して話をすすめる。
Get-NetNeighbor -InterfaceIndex 4 -AddressFamily IPv4 -State Stale,Reachable
これで、LAN内のIPv4アドレスが列挙されるはずだ。ただし、IPv4アドレスの中には特殊なIPアドレスがあり、これは除外する必要がある。「224.0.0.0~239.255.255.255」の範囲はマルチキャストアドレスなので無視する。同様に255.255.255.255は、ネットワークブロードキャストアドレス(ローカルブロードキャスト)なので、PCなどに割り当てられることはない。これらは、Stateプロパティが“Permanent”なので区別がしやすい。また、Stateが“Unreachable”は、現在では到達できないことが確定しているため無視して構わない。
このため、“-State”オプションには、“Stale,Reachable”の2つを指定する。“Reachable”は、「到達可能」を意味し、直近のarpに応答があり、存在が確認されたもの。Staleは、それから15~45秒(ランダムに選択される)経過すると、Stale状態に変わる。
なお、注意が必要な点がある。イーサネットと無線LANの両方が接続状態になっているPCは2つのIPアドレスが含まれる点だ。エクスプローラーなどでは、同一のマシンかどうかを判別しているが、Get-NetNeighborはあくまでも個別のIPアドレスとして列挙する点だ。
もうひとつは、PCがスリープ状態に入るなどして一定時間ARPに応答しないとリストから消えてしまう点だ。このため、IPアドレスの探索は、少なくとも対象のPCが起動している状態でしなければならない。
ネットワーク上のPCの名前を知りたい
Get-NetNeighborで得られたIPアドレスには、以下のものが含まれている。
PCのネットワークインターフェースに割り当てられたIPアドレス
プリンター
NAS
スマートフォン
ルーター
テレビやDVDレコーダーなど
このうち、Windowsネットワークから見えるPCやプリンター、NASなどについては、Windowsで「名前解決」が可能になっており、多くの場合、IPアドレスから名前(コンピュータ名)を探すことができる。それには、Resolve-DnsNameコマンドを使う。
●Resolve-DnsName (DnsClient)
https://docs.microsoft.com/en-us/powershell/module/dnsclient/resolve-dnsname?view=windowsserver2019-ps
これは基本的には、ドメイン名サーバー(DNSサーバー)を使って、名前からIPアドレスを得るものだが、LLMNR(Link-Local Multicast Name Resolution)やNETBIOSを使って名前解決をすることができる。LLMNRはMicrosoftがIPv6などでも利用可能なローカルでの名前解決として提案したプロトコルだ。RFCとして公開されているプロトコルだが、Windows以外ではプリンターやNASが実装している程度だ。
具体的には、以下のようにGet-Netneighborの出力に対してforeach-objectで受けてresolve-DnsNameを実行する。本来Resolve-DnsNameはコンピュータ名を指定するが、IPアドレスを指定すると、IPアドレスからの名前解決(IPアドレスを逆順にして.in-addr.arpaをつける)をしてくれる。
Get-NetNeighbor -InterfaceIndex 4 -AddressFamily IPv4 -State Stale,Reachable | ForEach-Object { Resolve-DnsName $_.IPAddress -ErrorAction SilentlyContinue -QuickTimeout }
少なくともResolve-DnsNameを使えば、WindowsマシンはIPアドレスから名前を検索できる。ただし、すべてのネットワークノードがLLMNRを実装しているわけではないので、LLMNRに応答しないものもある。
このためLLMNRによる問い合わせでは、タイムアウト処理をして、一定時間内に応答がないものはエラーにする。前記コマンドの「-ErrorAction SilentlyContinue」は、そのエラーを無視して次に処理を進めるためのもの。「-QuickTimeout」はタイムアウト時間を短く設定するものだ。実際、筆者宅のネットワークでは、Get-NetNeighborの実行が0.2秒程度なのに対して、Resolve-DnsNameをその後にすると100秒程度かかってしまう。
SMBv1が廃止になって、スクリプトなどから、ネットワーク内のPCを列挙するのが面倒になったが、Get-NetNeighborとResolve-DnsNameを使えば、コンピュータ名や稼働中のネットワークノードのIPアドレスを調べることが可能だ。
■関連記事