Reputation: 22983
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.
Also, if there is a better method to get the MAC ID from IP address, please suggest.
P.S: I am working in JAVA.
Thanks.
Upvotes: 13
Views: 29970
Reputation: 327
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
Upvotes: 0
Reputation: 1769
Inspired by greenspand answer i came up with this code that will query for the MAC address using IP and CMD command using specified IP.
Note that this code work on Windows and i believe it can work on Linux too with little modifications.
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
}
Upvotes: 2
Reputation: 749
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.
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.
Upvotes: 7
Reputation: 1048
As others have said, ARP is the way to go. Following is an implementation of jqnos second suggestion based on this example on GitSpot.
Two libraries are required:
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;
}
}
Upvotes: 2
Reputation: 272287
The arp cache is provided as standard in the set of SNMP data available. You can use SNMP4J to write a trivial agent to query this data.
e.g. from a command line SNMP toolset
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)
Upvotes: 4
Reputation: 386
You can get your own MAC address via:
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.
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.
Upvotes: 4
Reputation: 15520
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.
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.
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).
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":
system_profile
command.system_profile
probably exists.Upvotes: 11
Reputation: 2051
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.
Upvotes: 2
Reputation: 44804
ARP is the way to map IP addresses to MAC addresses. That's how the IP stack does it.
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 here though. Some spelunking through there might prove productive.
Upvotes: 5