2009-08-06 42 views
12

我需要獲取網絡中主機的MAC ID。爲此,如果我ping到該IP並查詢ARP緩存arp -a,我能夠獲得MAC ID。我只是想知道我是否可以獲取任何API來查詢ARP並獲取MAC ID。查詢ARP緩存以獲取MAC ID

此外,如果有更好的方法從IP地址獲取MAC ID,請提出建議。

P.S:我正在使用JAVA。

謝謝。

回答

9

Java沒有提供直接的方法來查詢網絡中主機的MAC地址,因爲它被Java的套接字庫抽象掉了。

從某種意義上說,這是有道理的,因爲主機的MAC地址實際上說的很少。沒有主機的「MAC」地址。

  • 許多主機將有幾個網卡,都有一個單獨的MAC地址,他們可以連接到網絡。我目前使用的計算機有一個有線以太網適配器,一個WiFi適配器和一個Firewire適配器,並且它們都有自己的MAC地址。這意味着主機沒有明確的MAC地址。
  • 如果主機位於不同的子網上,ARP實際上會爲您提供數據包通過的最後一個路由器的MAC地址,而不是您掃描的主機的MAC地址。

把這兩個問題一起,這意味着一臺主機可以具有許多不同的MAC地址(如果它有多個NIC),以及一個MAC地址可代表許多不同的主機(如果流量通過路由器)。

假設你知道這一切,你仍然需要獲得主機的MAC地址,這樣做在Java中的唯一途徑是通過「去本土化」:

  • 原產於運行在客戶端的程序:
    • 您可以啓動ARP命令行工具並解析其輸出。
    • 你可以使用某種JNI調用。不過,我對JNI不太熟悉,所以我無法幫助你。
    • 編寫一個單獨的小型本地應用程序,您可以通過Telnet或某些此類協議從Java訪問,並且可以爲您運行ARP命令。
  • 原產於要掃描主機:
    • 您可以使用SNMP,因爲一些其他的答案,這個線程的建議。我將這些答案推遲給你。 SNMP是一個很好的協議,但請注意,SNMP的OID可以是平臺相關的,也可以是廠商相關的。適用於Windows的OID不一定適用於Linux,反之亦然。
    • 如果您知道您的主機運行Windows,您可以使用WMIWin32_NetworkAdapter類擁有您想要的信息,但請注意,這將返回主機網卡的所有的,即使是Windows組成的。此外,它需要管理員憑據給您正在掃描的主機。 Google會告訴你如何從Java連接到WMI。
    • 如果您知道您的主機運行的是OS X,那麼您可以通過SSH連接到計算機並解析system_profile命令的輸出。
    • 對於Linux,可能存在類似於OS X的system_profile的工具。
+1

請注意,ARP緩存的SNMP OID與供應商無關。它不是企業MIB的一部分。 – 2009-10-01 16:27:40

4

arp緩存在可用的SNMP數據集中作爲標準提供。您可以使用SNMP4J編寫一個簡單的代理來查詢此數據。

例如從命令行SNMP工具集

snmpwalk ${hostname} 1.3.6.1.2.1.4.22.1.2 

(即巨大週期分隔的字符串是OID,或標識符,在SNMP術語ARP高速緩存的。這將所有SNMP實現工作)

3

ARP是將IP地址映射到MAC地址的方式爲。這就是IP堆棧的作用。

我不確定是否有可移植的方式來獲取該信息,因爲它通常只對內核開發人員和系統管理員很重要。

從大量的網絡搜索中,它可能使用SNMP獲取路由器的ARP表,但我沒有找到關於如何去做的大量具體信息。我確實找到了一個免費的用於SNMP here的Java庫。通過那裏的一些探索可能證明是有成效的。

2

這可能無法在Java環境下解決(因爲它與平臺無關),但是您還應該考慮是否可以通過系統服務獲取MAC地址。有可能你無法通過ARP可靠地找到MAC地址,這取決於你爲什麼需要MAC地址。

+0

最好的情況是,ARP表最多有**這個**機器聽說過的**和MAC最近的**關聯。如果您的遠程計算機更改IP,但還沒有將任何網絡流量發送到此計算機(或廣播)的理由,那麼此計算機上的ARP表已過時,只是還不知道它。 – 2015-01-29 20:54:04

5

你可以通過自己 MAC地址:

Enumeration<NetworkInterface> it = NetworkInterface.getNetworkInterfaces(); 
while (it.hasMoreElements()) { 
    byte[] macAddress = it.nextElement().getHardwareAddress(); 
} 

肯定是有沒有辦法,你可以通過java的香草拿到另一臺主機的MAC地址。您必須使用Process執行或本機庫來執行此操作。

如果你控制其他機器,你可以讓他們查詢自己的MAC並通過TCP/IP通道發送回去,但我猜這不是你想要的。有關更多詳細信息,請參閱jqno的答案。

+0

問題是:「獲取我的網絡中主機的MAC ID」,即不是您自己的。恐怕我不得不冷靜地回答這個問題。 – 2014-11-17 08:34:52

2

正如其他人所說,ARP是要走的路。以下是基於this example on GitSpot的jqnos第二個建議的實現。

兩個庫要求:對網絡流量捕捉

  1. 系統庫:
  2. 可從the jpcap sourceforge site獲得jpcap java庫,通過JNI

    public class GetMACAddress { 
    
    /** 
    * 
    * @param ip address containing an IP 
    * @return MAC-Address as formatted String 
    * @throws IOException 
    * @throws IllegalArgumentException 
    */ 
    public static String getMACAdressByIp(Inet4Address ip) throws IOException, IllegalArgumentException { 
    
        byte[] mac = GetMACAddress.getMACAddressByARP(ip); 
    
        StringBuilder formattedMac = new StringBuilder(); 
        boolean first = true; 
        for (byte b : mac) { 
         if (first) { 
          first = false; 
         } else { 
          formattedMac.append(":"); 
         } 
         String hexStr = Integer.toHexString(b & 0xff); 
         if (hexStr.length() == 1) { 
          formattedMac.append("0"); 
         } 
         formattedMac.append(hexStr); 
        } 
    
        return formattedMac.toString(); 
    } 
    
    private static byte[] getMACAddressByARP(Inet4Address ip) throws IOException, IllegalArgumentException { 
    
        NetworkInterface networkDevice = getNetworkDeviceByTargetIP(ip); 
    
        JpcapCaptor captor = JpcapCaptor.openDevice(networkDevice, 2000, false, 3000); 
        captor.setFilter("arp", true); 
        JpcapSender sender = captor.getJpcapSenderInstance(); 
    
        InetAddress srcip = null; 
        for (NetworkInterfaceAddress addr : networkDevice.addresses) 
         if (addr.address instanceof Inet4Address) { 
          srcip = addr.address; 
          break; 
         } 
    
        byte[] broadcast = new byte[] { (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255 }; 
        ARPPacket arp = new ARPPacket(); 
        arp.hardtype = ARPPacket.HARDTYPE_ETHER; 
        arp.prototype = ARPPacket.PROTOTYPE_IP; 
        arp.operation = ARPPacket.ARP_REQUEST; 
        arp.hlen = 6; 
        arp.plen = 4; 
        arp.sender_hardaddr = networkDevice.mac_address; 
        arp.sender_protoaddr = srcip.getAddress(); 
        arp.target_hardaddr = broadcast; 
        arp.target_protoaddr = ip.getAddress(); 
    
        EthernetPacket ether = new EthernetPacket(); 
        ether.frametype = EthernetPacket.ETHERTYPE_ARP; 
        ether.src_mac = networkDevice.mac_address; 
        ether.dst_mac = broadcast; 
        arp.datalink = ether; 
    
        sender.sendPacket(arp); 
    
        while (true) { 
         ARPPacket p = (ARPPacket) captor.getPacket(); 
         if (p == null) { 
          throw new IllegalArgumentException(ip + " is not a local address"); 
         } 
         if (Arrays.equals(p.target_protoaddr, srcip.getAddress())) { 
          return p.sender_hardaddr; 
         } 
        } 
    } 
    
    private static NetworkInterface getNetworkDeviceByTargetIP(Inet4Address ip) throws IllegalArgumentException { 
    
        NetworkInterface networkDevice = null; 
        NetworkInterface[] devices = JpcapCaptor.getDeviceList(); 
    
        loop: for (NetworkInterface device : devices) { 
         for (NetworkInterfaceAddress addr : device.addresses) { 
          if (!(addr.address instanceof Inet4Address)) { 
           continue; 
          } 
          byte[] bip = ip.getAddress(); 
          byte[] subnet = addr.subnet.getAddress(); 
          byte[] bif = addr.address.getAddress(); 
          for (int i = 0; i < 4; i++) { 
           bip[i] = (byte) (bip[i] & subnet[i]); 
           bif[i] = (byte) (bif[i] & subnet[i]); 
          } 
          if (Arrays.equals(bip, bif)) { 
           networkDevice = device; 
           break loop; 
          } 
         } 
        } 
    
        if (networkDevice == null) { 
         throw new IllegalArgumentException(ip + " is not a local address"); 
        } 
    
        return networkDevice; 
    } 
    
    } 
    
3

h級接口的第一個庫還有一個更簡單的方法:

private static final String ARP_GET_IP_HW = "arp -a"; 

public String getARPTable(String cmd) throws IOException { 
      Scanner s = new Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A"); 
       return s.hasNext() ? s.next() : ""; 
    } 

System.out.println(getARPTable(ARP_GET_IP_HW)); 

,你會得到eintire ARP表中的IP和硬件上排序的每一行。

然後,您可以將表拆分爲單獨的String行,並在每行上使用正則表達式來匹配HW和IP地址。你完成了。

0

通過啓發greenspand答案我想出了這個代碼將使用查詢使用指定的IP IP和CMD命令MAC地址

注意,在此代碼的工作視窗,我相信它可以用很少的修改上Linux的工作了。

public static String getARPTable(String ip) throws IOException { 
     String systemInput = ""; 
//to renew the system table before querying 
     Runtime.getRuntime().exec("arp -a"); 
     Scanner s = new Scanner(Runtime.getRuntime().exec("arp -a " + ip).getInputStream()).useDelimiter("\\A"); 
     systemInput = s.next(); 
     String mac = ""; 
     Pattern pattern = Pattern.compile("\\s{0,}([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})"); 
     Matcher matcher = pattern.matcher(systemInput); 
     if (matcher.find()) { 
      mac = mac + matcher.group().replaceAll("\\s", ""); 
     } else { 
      System.out.println("No string found"); 
     } 
     return mac; 
    } 

    public static void main(String[] args) throws IOException { 

     System.out.println(getARPTable("192.168.1.23")); 
     // prints 74-d4-35-76-11-ef 

    }