概要

  • 指定した IPv4 ゾーンに対して MAC アドレスを収集するコマンドラインツール。
  • IP アドレスを指定しなかった場合は、自身のホストアドレスを元に収集する。
  • ファイアウォール等で ping 応答を返さない端末についても収集する。
  • 電源が落ちている端末については収集出来ない。
  • 要 .NET Framework 4.0

実行ファイルのみ

ソース

  • scanNetArp.zip
    /**
         @file Program.cs
    
        @mainpage scan Net Arp
        - 指定した IPv4 ゾーンに対して MAC アドレスを収集するコマンドラインツール。
        - IP アドレスを指定しなかった場合は、自身のホストアドレスを元に収集する。
        - ファイアウォール等で ping 応答を返さない端末についても収集する。
        - 電源が落ちている端末については収集出来ない。
        - 要 .NET Framework 4.0
    
        - ARPによるMACアドレスの取得 @ SIN\@SAPPOROWORKSの覚書<br/>
          http://d.hatena.ne.jp/spw0022/20111024/1319575443
    
        - マルチスレッド @ ++C++;// 未確認飛行 C<br/>
          http://ufcpp.net/study/csharp/sp_thread.html
    */
    
    using System;
    using System.Collections.Generic;
    using System.Net;
    using System.Runtime.InteropServices;
    using System.Text.RegularExpressions;
    using System.Threading.Tasks;
    
    namespace TakeAsh.scanNetArp {
        class Program {
    
            /// <summary>
            /// SendARP function<br/>
            /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa366358.aspx
            /// </summary>
            /// <param name="dstIp">The destination IPv4 address</param>
            /// <param name="srcIp">he source IPv4 address of the sender</param>
            /// <param name="mac">A pointer to an array of ULONG variables</param>
            /// <param name="macLen">a pointer to a ULONG value 
            /// that specifies the maximum buffer size, in bytes</param>
            /// <returns>
            /// - If the function succeeds, the return value is NO_ERROR.
            /// - If the function fails, the return value is one of the following error codes.
            /// </returns>
            [DllImport("IPHlpApi.dll", ExactSpelling = true)]
            private static extern uint SendARP(uint dstIp, uint srcIp, Byte[] mac, ref uint macLen);
    
            /// <summary>
            /// ステータスコードとシンボルの対応 (WinError.h)
            /// </summary>
            static Dictionary<uint, string> StatusCode = new Dictionary<uint, string>(){
                { 0,    "NO_ERROR" },
                { 67,   "ERROR_BAD_NET_NAME" },
                { 111,  "ERROR_BUFFER_OVERFLOW" },
                { 31,   "ERROR_GEN_FAILURE" },
                { 87,   "ERROR_INVALID_PARAMETER" },
                { 1784, "ERROR_INVALID_USER_BUFFER" },
                { 1168, "ERROR_NOT_FOUND" },
                { 50,   "ERROR_NOT_SUPPORTED" },
            };
    
            /// <summary>
            /// IP アドレス最大値
            /// </summary>
            const int ipMax = 254;
    
            /// <summary>
            /// ARP問い合わせ結果を格納するためのクラス
            /// </summary>
            class ARPresult {
    
                /// <summary>
                /// IPアドレス
                /// </summary>
                public string ip;
    
                /// <summary>
                /// MACアドレス
                /// </summary>
                public Byte[] mac;
    
                /// <summary>
                /// MACアドレスのバイト数
                /// </summary>
                public uint macLen;
    
                /// <summary>
                /// ARP問い合わせ結果
                /// </summary>
                public uint status;
            }
    
            /// <summary>
            /// ヘルプメッセージの表示および終了
            /// </summary>
            static void showHelpMessage() {
                Console.WriteLine(
                    "usage: scanNetArp [aa.bb.cc.dd]\n" +
                    "listup MAC addresses in zone aa.bb.cc.xx or localnet."
                );
                Environment.Exit(-1);
            }
    
            /// <summary>
            /// ARP 情報の収集を行う
            /// </summary>
            /// <param name="zone">対象ゾーン</param>
            /// <returns>収集結果の配列</returns>
            static ARPresult[] getArpTable(IPAddress zone) {
                ARPresult[] results = new ARPresult[ipMax];
                Byte[] zoneByte = zone.GetAddressBytes();
    
                Parallel.For(0, ipMax, ip => {
                    ARPresult result = new ARPresult();
                    result.ip = string.Format("{0}.{1}.{2}.{3}",
                        zoneByte[0], zoneByte[1], zoneByte[2], ip + 1);
                    result.mac = new Byte[sizeof(uint) * 2];
                    result.macLen = (uint)result.mac.Length;
                    IPAddress dst = IPAddress.Parse(result.ip);
                    Console.Error.WriteLine(string.Format("Request:{0}", dst.ToString()));
                    Byte[] addrByte = dst.GetAddressBytes();
                    uint addr = 0;
                    for(int i = addrByte.Length - 1; i >= 0; --i) {
                        addr <<= 8;
                        addr |= addrByte[i];
                    }
                    result.status = SendARP(addr, 0, result.mac, ref result.macLen);
                    results[ip] = result;
                });
                Console.Error.WriteLine();
    
                return results;
            }
    
            /// <summary>
            /// ARP 情報の表示<br/>
            /// 収集ステータスが NO_ERROR だったもののみを表示する。
            /// </summary>
            /// <param name="results">ARP 情報テーブル</param>
            static void printArpTable(ARPresult[] results) {
                Console.WriteLine("IPAddr\tMACAddr");
                for(int ip = 0; ip < ipMax; ++ip) {
                    if(results[ip].status != 0) {
                        // NO_ERROR でなければスキップする
                        continue;
                    }
                    string macStr = "";
                    for(uint i = 0; i < results[ip].macLen; ++i) {
                        macStr += string.Format("{0:x2}:", results[ip].mac[i]);
                    }
                    /*
                        uint statusCode = results[ip].status;
                        var status = StatusCode.ContainsKey( statusCode )
                            ? StatusCode[statusCode]
                            : statusCode.ToString();
                    */
                    Console.WriteLine(string.Format("{0}\t{1}",
                        results[ip].ip, macStr.TrimEnd(':')));
                }
                Console.WriteLine();
            }
    
            static void Main(string[] args) {
                IPAddress[] addrList = new IPAddress[] { new IPAddress(0) };
                if(args.Length > 0) {
                    if(!IPAddress.TryParse(args[0], out addrList[0])) {
                        showHelpMessage();
                    } else {
                        Console.WriteLine("IPAddr:\t" + addrList[0].ToString());
                    }
                } else {
                    string hostName = Dns.GetHostName();
                    Console.WriteLine("Host:\t" + hostName);
                    IPAddress[] addrV4V6List = Dns.GetHostAddresses(hostName);
                    List<IPAddress> addrV4List = new List<IPAddress>();
                    Regex regIPv4 = new Regex(@"^\d+\.\d+\.\d+\.\d+$");
                    for(int i = 0; i < addrV4V6List.Length; ++i) {
                        string addrStr = addrV4V6List[i].ToString();
                        if(regIPv4.IsMatch(addrStr)) {
                            addrV4List.Add(addrV4V6List[i]);
                            Console.WriteLine("IPAddr:\t" + addrStr);
                        }
                    }
                    addrList = addrV4List.ToArray();
                }
                Console.WriteLine("Date:\t" + DateTime.Now.ToString("G") + "\r\n");
    
                foreach(IPAddress address in addrList) {
                    ARPresult[] results = getArpTable(address);
                    printArpTable(results);
                }
            }
        }
    }

リンク