Java 查询 ARP 缓存以获取 MAC ID
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1238963/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Query ARP cache to get MAC ID
提问by bdhar
I need to get the MAC ID of a host in my network. For that, if I ping to that IP and query the ARP cache arp -a
, I am able to get the MAC ID. I just wonder if I can get any API to query the ARP and get the MAC id.
我需要获取网络中主机的 MAC ID。为此,如果我 ping 那个 IP 并查询 ARP 缓存arp -a
,我就能够获得 MAC ID。我只是想知道是否可以获取任何 API 来查询 ARP 并获取 MAC id。
Also, if there is a better method to get the MAC ID from IP address, please suggest.
另外,如果有更好的方法从IP地址获取MAC ID,请提出建议。
P.S: I am working in JAVA.
PS:我在JAVA工作。
Thanks.
谢谢。
采纳答案by jqno
Java provides no direct way to query the MAC address of a host in your network, as this is abstracted away by Java's socket libraries.
Java 没有提供直接的方法来查询网络中主机的 MAC 地址,因为这是由 Java 的套接字库抽象出来的。
In a way, this makes sense, because the MAC address of a host actually says very little. There is no such thing as "the" MAC address of a host.
在某种程度上,这是有道理的,因为主机的 MAC 地址实际上很少说明。没有主机的“MAC 地址”这样的东西。
- Many hosts will have several NICs, all with a separate MAC address, with which they can connect to the network. The computer I'm on at the moment has a wired ethernet adapter, a WiFi adapter, and a Firewire adapter, and they all have their own MAC address. This means that there is no definitive MAC address for a host.
- If the host is on a different subnet, ARP will actually give you the MAC address for the last router your packet passed through, instead of the MAC address of the host you're scanning.
- 许多主机将有多个 NIC,它们都有一个单独的 MAC 地址,它们可以通过它们连接到网络。我现在使用的计算机有一个有线以太网适配器、一个 WiFi 适配器和一个 Firewire 适配器,它们都有自己的 MAC 地址。这意味着主机没有明确的 MAC 地址。
- 如果主机在不同的子网上,ARP 实际上会给你你的数据包通过的最后一个路由器的 MAC 地址,而不是你正在扫描的主机的 MAC 地址。
Put both of these issues together, and that means that one host may have many different MAC addresses (if it has more than one NIC), and one MAC address may represent many different hosts (if traffic passes through a router).
将这两个问题放在一起,这意味着一台主机可能有许多不同的 MAC 地址(如果它有多个 NIC),而一个 MAC 地址可能代表许多不同的主机(如果流量通过路由器)。
Assuming you know all this and you still need to get the MAC address of a host, the only way to do that in Java is by "going native":
假设您知道所有这些并且您仍然需要获取主机的 MAC 地址,那么在 Java 中做到这一点的唯一方法是“本地化”:
- Native to the client that runs your program:
- You could launch an ARP command-line tool and parse its output.
- You could use some sort of JNI call. I'm not too familiar with JNI, though, so I can't help you with that.
- Write a separate, small native app that you can access from Java via Telnet or some such protocol, and which will run the ARP command for you.
- Native to the host that you want to scan:
- You could use SNMP, as some of the other answers to this thread suggest. I defer to these answers for making that work for you. SNMP is a great protocol, but be aware that SNMP's OIDs can be both platform-dependent and vendor-dependent. OIDs that work for Windows don't always work for Linux and vice versa.
- If you know that your host runs Windows, you could use WMI. The Win32_NetworkAdapterclass holds the information you want, but be aware that this returns allof the hosts NICs, even the ones Windows makes up. Also, it requires administrator credentials to the host you are scanning. Google will tell you how to connect to WMI from Java.
- If you know your host runs OS X, you might be able to SSH into the machine and parse the output of the
system_profile
command. - For Linux, a tool similar to OS X's
system_profile
probably exists.
- 原生于运行您的程序的客户端:
- 您可以启动 ARP 命令行工具并解析其输出。
- 您可以使用某种 JNI 调用。不过,我对 JNI 不太熟悉,所以我无法帮助你。
- 编写一个单独的小型本机应用程序,您可以通过 Telnet 或某些此类协议从 Java 访问该应用程序,该应用程序将为您运行 ARP 命令。
- 本地到您要扫描的主机:
- 您可以使用 SNMP,正如该线程的其他一些答案所建议的那样。我遵循这些答案,以便为您服务。SNMP 是一个很棒的协议,但要注意 SNMP 的 OID 既可以依赖于平台,也可以依赖于供应商。适用于 Windows 的 OID 并不总是适用于 Linux,反之亦然。
- 如果您知道您的主机运行 Windows,则可以使用WMI。该Win32_NetworkAdapter类拥有你想要的信息,但要注意,这将返回所有的主机网卡,即使是那些视窗组成。此外,它还需要您扫描的主机的管理员凭据。Google 会告诉你如何从 Java 连接到 WMI。
- 如果您知道您的主机运行 OS X,您也许能够通过 SSH 连接到机器并解析
system_profile
命令的输出。 - 对于 Linux,
system_profile
可能存在类似于 OS X 的工具。
回答by Brian Agnew
The arp cache is provided as standard in the set of SNMP data available. You can use SNMP4Jto write a trivial agent to query this data.
arp 缓存在可用的 SNMP 数据集中作为标准提供。您可以使用SNMP4J编写一个简单的代理来查询此数据。
e.g. from a command line SNMP toolset
例如从命令行 SNMP 工具集
snmpwalk ${hostname} 1.3.6.1.2.1.4.22.1.2
(that huge period-delimited string is the OID, or identifier, of the ARP cache in SNMP terms. That will work for all SNMP implementations)
(那个巨大的以句点分隔的字符串是 SNMP 术语中 ARP 缓存的 OID 或标识符。这将适用于所有 SNMP 实现)
回答by T.E.D.
ARP is theway to map IP addresses to MAC addresses. That's how the IP stack does it.
ARP 是将IP 地址映射到 MAC 地址的方法。这就是 IP 堆栈的工作方式。
I'm not sure there is a portable way to get that info, since it is typically only important for kernel developers and system administrators.
我不确定是否有一种可移植的方式来获取该信息,因为它通常只对内核开发人员和系统管理员很重要。
From a lot of web searching, it looks like it is possible to get a router's ARP table using SNMP, but I didn't find a lot of specific info on how to do it. I did find a free Java library for SNMP herethough. Some spelunking through there might prove productive.
从大量的网络搜索来看,似乎可以使用 SNMP 获取路由器的 ARP 表,但我没有找到很多关于如何执行此操作的具体信息。不过,我确实在这里找到了一个用于 SNMP 的免费 Java 库。在那里进行一些探索可能会很有成效。
回答by benc
This may not be solvable in the context of Java (because it is platform independent), but you should also consider whether or not you can get the MAC addresses via a system service. There are probably situations where you cannot reliably find the MAC address via ARP, it depends on why you would need the MAC address.
这在 Java 上下文中可能无法解决(因为它与平台无关),但您还应该考虑是否可以通过系统服务获取 MAC 地址。在某些情况下,您可能无法通过 ARP 可靠地找到 MAC 地址,这取决于您为什么需要 MAC 地址。
回答by Reinier Zwitserloot
You can get your ownMAC address via:
您可以通过以下方式获取自己的MAC 地址:
Enumeration<NetworkInterface> it = NetworkInterface.getNetworkInterfaces();
while ( it.hasMoreElements() ) {
byte[] macAddress = it.nextElement().getHardwareAddress();
}
There is definitely no way you can get the MAC address of another host via vanilla java. You'd have to use Process execution or a native library to do it.
您绝对无法通过 vanilla java 获取另一台主机的 MAC 地址。您必须使用 Process execution 或本机库来执行此操作。
If you control the other machines, you can let them query their own MAC and send it back across a TCP/IP channel, but I'm guessing that's not what you want. For more details, see jqno's answer.
如果您控制其他机器,您可以让他们查询自己的 MAC 并通过 TCP/IP 通道将其发回,但我猜这不是您想要的。更多细节请看jqno的回答。
回答by Marco Borchert
As others have said, ARP is the way to go. Following is an implementation of jqnos second suggestion based on this example on GitSpot.
正如其他人所说,ARP 是必经之路。以下是基于GitSpot 示例的 jqnos 第二个建议的实现。
Two libraries are required:
需要两个库:
- system library for network traffic capture:
- Linux-based: libpcap available from www.tcpdump.org
- Windows: winpcap available from www.winpcap.org
the jpcap java library available from the jpcap sourceforge site, which provides a high-level interface to the first library through 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; } }
- 用于网络流量捕获的系统库:
- 基于 Linux:libpcap 可从www.tcpdump.org 获得
- 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; } }
回答by greenspand
There is a much simpler way:
有一个更简单的方法:
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 ));
And you get the eintire ARP Table with IP and HW sorted on each row.
你会得到完整的 ARP 表,IP 和硬件在每一行排序。
Then you can split the table into separate String rows and use regular expressions on each row to match both HW and IP Adress. And you're done.
然后,您可以将表拆分为单独的字符串行,并在每行上使用正则表达式来匹配硬件和 IP 地址。你已经完成了。
回答by Abdelsalam Shahlol
Inspired by greenspandanswer i came up with this code that will query for the MAC addressusing IP and CMDcommand using specified IP.
受greenspand答案的启发,我想出了这个代码,它将使用 IP 和CMD命令使用指定的 IP查询MAC 地址。
Note that this code work on Windowsand i believe it can work on Linuxtoo with little modifications.
请注意,此代码在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
}
回答by scugxl
I provided a fully production ready method by using pcap4j+libpcap to detect IPV4 and ipv6 mac address here: https://github.com/gaoxingliang/mac-address-detector-java
我提供了一个完全生产就绪的方法,使用 pcap4j+libpcap 在这里检测 IPV4 和 ipv6 mac 地址:https: //github.com/gaoxingliang/mac-address-detector-java