LoginSignup
6
4

More than 3 years have passed since last update.

PowerShellでarpingもどき

Last updated at Posted at 2018-12-18

目的

PowerShell で Linux ライクな arping コマンドを実現する。

背景

Windows でも ARP エントリ(ARP キャッシュ)を調べるコマンド(ARP -aGet-NetNeighbor)は存在するが、実際に ARP を送出してレスポンスを調べるためのコマンドがない。

コード

IP Helper API(iphlpapi.dll)の SendARP 関数を実行しているだけです。
中身はほぼ PowerShellスクリプトですが、Windows バッチファイルとして実行できます(先頭行をコメントアウトまたは削除して拡張子を .ps1 に変更すると PowerShell スクリプトになります)。PowerShellのコンソール上で使用する場合は、arping 関数を直接使用してください。

arping.bat
@Powershell -NoP -C "&([ScriptBlock]::Create((gc '%~f0'|?{$_.ReadCount -gt 1}|Out-String)))" %* & exit/b
# by earthdiver1
[CmdletBinding(PositionalBinding=$False)]
Param(
    [parameter(Position=0)][String]$Target,  # IP address of the target host
    [String]$Source,                         # if you have multiple interfaces (optional, default value: 0 (all interfaces))
    [Alias("n")][Int]$Count=4,               # the number of ARP requests to send (optional, default value: 4)
    [Int]$Interval=1                         # the interval between ARP requests, in seconds (optional, default value: 1)
)

Function arping {
    [CmdletBinding(PositionalBinding=$False)]
    Param(
        [parameter(Position=0)][String]$Target,  # IP address of the target host
        [String]$Source,                         # if you have multiple interfaces (optional, default value: 0 (all interfaces))
        [Alias("n")][Int]$Count=4,               # the number of ARP requests to send (optional, default value: 4)
        [Int]$Interval=1                         # the interval between ARP requests, in seconds (optional, default value: 1)
    )
    if (-not $Target) {
        Write-Output "Usage: arping <target IP address> [-Source <source IP address>]"
        Write-Output "                                  [-Count/-n <number of ARP requests>] [-Interval <interval between ARP requests>]"
        exit
    }
    if ($Count -lt 1) { exit }
    $code = '[DllImport("iphlpapi.dll")]public static extern int SendARP(UInt32 DestIP,UInt32 SrcIP,byte[] pMacAddr,ref UInt32 PhyAddrLen);'
    $type = Add-Type -MemberDefinition $code -Name Win32SendARP -PassThru
    try {
        $DstIP = [UInt32][System.Net.IPAddress]::Parse($Target).Address
    } catch {
        Write-Output "Target IP address $Target is invalid. Please check it and try again."
        exit
    }
    if ($Source) {
        try {
            $SrcIP = [UInt32][System.Net.IPAddress]::Parse($Source).Address
        } catch {
            Write-Output "Source IP address $Source is invalid. Please check it and try again."
            exit
        }
    } else {
        $SrcIP = [UInt32]0
    }
    $MacAddr = New-Object Byte[] 6
    $n_sent = 0
    $n_received = 0
    $maxT = 0.
    $minT = 9999.
    $sumT = 0.
    Write-Output ""
    Write-Output "ARPING ${Target}:"
    for($i=1; $i -le $Count; $i++) {
        $n_sent++
        $t = (Measure-Command { $result = $type::SendARP($DstIP, $SrcIP, $MacAddr, [ref][UInt32]$MacAddr.Length) }).TotalMilliseconds
        if ($result -eq 0) { # NO_ERROR
            $n_received++
            if ($t -gt $maxT) { $maxT = $t }
            if ($t -lt $minT) { $minT = $t }
            $sumT += $t        
            Write-Output "Reply from $(($MacAddr | %{ '{0:x2}' -F $_ }) -Join '-') (as $Target): index=$i, time=$(($t).ToString('0'))ms"
        } elseif ($result -eq 67) { # ERROR_BAD_NET_NAME
            Write-Output "Request timed out."
        } else {
            Write-Output "SendARP failed with error: $result"
        }
        if ($i -lt $Count) { Start-Sleep -Milliseconds ([Math]::Max(($Interval*1000 - [Int]$t), 0)) }
    }
    Write-Output ""
    Write-Output "Ping statistics for $Target/arp:"
    Write-Output "    Packets: Sent=$n_sent, Received=$n_received, Lost=$($n_sent-$n_received) ($((($n_sent-$n_received)/$n_sent*100).ToString('0'))% loss)"
    if ($n_received -gt 0) {
        Write-Output "Approximate round trip times in milli-seconds:"
        Write-Output "    Minimum = $(($minT).ToString('0'))ms, Maximum = $(($maxT).ToString('0'))ms, Average = $(($sumT/$n_received).ToString('0'))ms"
    }
}

# update $PSBoundParameters with default parameter values
foreach ($p in $MyInvocation.MyCommand.ScriptBlock.Ast.ParamBlock.Parameters) {
    $key = $p.Name.VariablePath.UserPath
    if ($PSBoundParameters.ContainsKey($key)) { Continue }
    $value = (Get-Variable $key).Value
    if ($value) { $PSBoundParameters.Add($key, $value) }
}

arping @PSBoundParameters

実行例

C:\Users\earthdiver1\Desktop> arping 192.168.0.1

ARPing 192.168.0.1:
Reply from 00-3a-9d-80-ed-0c (as 192.168.0.1): index=1, time=26ms
Reply from 00-3a-9d-80-ed-0c (as 192.168.0.1): index=2, time=2ms
Reply from 00-3a-9d-80-ed-0c (as 192.168.0.1): index=3, time=1ms
Reply from 00-3a-9d-80-ed-0c (as 192.168.0.1): index=4, time=2ms

Ping statistics for 192.168.0.1/arp:
    Packets: Sent=4, Received=4, Lost=0 (0% loss)
Approximate round trip times in milli-seconds:
    Minimum = 1ms, Maximum = 26ms, Average = 8ms

 
 
クリエイティブ・コモンズ 表示 - 継承 4.0 国際
 

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4