查询ARPcaching以获取MAC ID
我需要获取networking中主机的MAC ID。 为此,如果我ping到该IP并查询ARPcachingarp -a
,我能够获得MAC ID。 我只是想知道我是否可以得到任何API来查询ARP并获得MAC ID。
此外,如果有更好的方法从IP地址获取MAC ID,请build议。
PS:我在JAVA工作。
谢谢。
Java没有提供直接的方法来查询networking中主机的MAC地址,因为这是由Java的套接字库抽象出来的。
在某种程度上,这是有道理的,因为主机的MAC地址实际上说的很less。 没有主机的“MAC”地址。
- 许多主机将有几个网卡,都有一个单独的MAC地址,他们可以连接到networking。 我目前使用的计算机有一个有线以太网适配器,一个WiFi适配器和一个火线适配器,并且它们都有自己的MAC地址。 这意味着主机没有明确的MAC地址。
- 如果主机位于不同的子网上,ARP实际上会为您提供数据包通过的最后一个路由器的MAC地址,而不是您正在扫描的主机的MAC地址。
将这两个问题放在一起,这意味着一台主机可能有许多不同的MAC地址(如果它有多个NIC),一个MAC地址可能代表许多不同的主机(如果stream量通过路由器)。
假设你知道所有这些,并且你仍然需要获得一个主机的MAC地址,那么在Java中这样做的唯一方法就是“本地化”:
- 原生于运行程序的客户端:
- 您可以启动一个ARP命令行工具并parsing其输出。
- 你可以使用某种JNI调用。 但是我对JNI不太熟悉,所以我不能帮你。
- 编写一个单独的小型本地应用程序,您可以通过Telnet或某些此类协议从Java访问,并将为您运行ARP命令。
- 原生到您要扫描的主机:
- 您可以使用SNMP,因为这个线程的一些其他答案build议。 为了使你的工作顺利,我遵循这些答案。 SNMP是一个很好的协议,但请注意SNMP的OID可以是平台相关的,也可以是厂商相关的。 适用于Windows的OID并不总是适用于Linux,反之亦然。
- 如果您知道您的主机运行Windows,则可以使用WMI 。 Win32_NetworkAdapter类包含所需的信息,但请注意,这将返回所有主机网卡,即使是Windows组成的网卡。 另外,它需要pipe理员凭据给你正在扫描的主机。 Google会告诉你如何从Java连接到WMI。
- 如果您知道您的主机运行的是OS X,则可以通过SSH连接到机器并parsing
system_profile
命令的输出。 - 对于Linux,可能存在类似于OS X的
system_profile
的工具。
您可以通过以下方式获得自己的 MAC地址:
Enumeration<NetworkInterface> it = NetworkInterface.getNetworkInterfaces(); while ( it.hasMoreElements() ) { byte[] macAddress = it.nextElement().getHardwareAddress(); }
绝对没有办法通过香草java获得另一台主机的MAC地址。 你将不得不使用进程执行或本地库来做到这一点。
如果你控制其他机器,你可以让他们查询自己的MAC并通过TCP / IP通道发送回去,但我猜这不是你想要的。 欲了解更多细节,请参阅jqno的答案。
arpcaching在可用的SNMP数据集中作为标准提供。 您可以使用SNMP4J编写一个简单的代理来查询这些数据。
例如从命令行的SNMP工具集
snmpwalk ${hostname} 1.3.6.1.2.1.4.22.1.2
(这个巨大的以句点分隔的string是SNMP缓冲区的OID或标识符,这对SNMP的所有实现都适用)
ARP是将 IP地址映射到MAC地址的方式。 这就是IP栈如何做到的。
我不确定是否有可移植的方式来获取这些信息,因为它通常只对内核开发人员和系统pipe理员很重要。
从大量的networkingsearch,看起来像使用SNMP可以得到一个路由器的ARP表,但我没有find很多关于如何做的具体信息。 尽pipe我在这里find了一个免费的Java库。 通过那里的一些探索可能certificate是有成效的。
有一个更简单的方法:
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 ));
而且你可以得到在每行上都有IP和HW的完整ARP表。
然后,您可以将表拆分为单独的string行,并在每行上使用正则expression式来匹配HW和IP地址。 你完成了。
这在Java的背景下可能是不可解决的(因为它是独立于平台的),但是您也应该考虑是否可以通过系统服务获取MAC地址。 有可能你无法通过ARP可靠地findMAC地址,这取决于你为什么需要MAC地址。
正如其他人所说,ARP是要走的路。 以下是基于GitSpot上这个例子的jqnos第二个build议的实现。
需要两个库:
- 用于networkingstream量捕获的系统库:
- 基于Linux:可从www.tcpdump.org获得libpcap
- Windows:可从www.winpcap.org获得winpcap
-
jpcap sourceforge站点提供的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; } }
受到绿色和平的启发回答我想出了这个代码,将使用IP和CMD命令使用指定的IP查询MAC地址 。
请注意,此代码在Windows上工作,我相信它也可以在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 }