bdhar
bdhar

Reputation: 22983

Query ARP cache to get MAC ID

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

Answers (9)

scugxl
scugxl

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

Abdelsalam Shahlol
Abdelsalam Shahlol

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

greenspand
greenspand

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

Marco Borchert
Marco Borchert

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:

  1. system library for network traffic capture:
  2. 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

Brian Agnew
Brian Agnew

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

Reinier Zwitserloot
Reinier Zwitserloot

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

jqno
jqno

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.

  • 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.

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":

  • 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_NetworkAdapter class holds the information you want, but be aware that this returns all of 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.

Upvotes: 11

benc
benc

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

T.E.D.
T.E.D.

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

Related Questions