Consultar caché ARP para obtener la identificación MAC

Necesito obtener la identificación MAC de un host en mi red. Para eso, si hago ping a esa IP y consulto ARP cache arp -a , puedo obtener la MAC ID. Me pregunto si puedo obtener cualquier API para consultar el ARP y obtener la identificación MAC.

Además, si hay un método mejor para obtener la identificación MAC de la dirección IP, sugiérala.

PD: estoy trabajando en JAVA.

Gracias.

Java no proporciona una forma directa de consultar la dirección MAC de un host en su red, ya que esto es abstraído por las bibliotecas de socket de Java.

En cierto modo, esto tiene sentido, porque la dirección MAC de un host realmente dice muy poco. No hay tal cosa como “la” dirección MAC de un host.

  • Muchos hosts tendrán varias NIC, todas con una dirección MAC separada, con la que se pueden conectar a la red. La computadora en la que estoy ahora tiene un adaptador de Ethernet con cable, un adaptador WiFi y un adaptador Firewire, y todos ellos tienen su propia dirección MAC. Esto significa que no hay una dirección MAC definitiva para un host.
  • Si el host se encuentra en una subred diferente, ARP realmente le dará la dirección MAC del último enrutador por el que pasó su paquete, en lugar de la dirección MAC del host que está escaneando.

Combine ambos problemas, y eso significa que un host puede tener muchas direcciones MAC diferentes (si tiene más de una NIC), y una dirección MAC puede representar muchos hosts diferentes (si el tráfico pasa por un enrutador).

Suponiendo que sepa todo esto y aún necesita obtener la dirección MAC de un host, la única manera de hacerlo en Java es “yendo nativo”:

  • Originario del cliente que ejecuta su progtwig:
    • Podría lanzar una herramienta de línea de comandos ARP y analizar su salida.
    • Podría usar algún tipo de llamada JNI. Aunque no estoy demasiado familiarizado con JNI, no puedo ayudarte con eso.
    • Escriba una aplicación nativa pequeña y separada a la que pueda acceder desde Java a través de Telnet o algún otro protocolo, y que ejecutará el comando ARP por usted.
  • Originario del host que desea escanear:
    • Puede usar SNMP, como sugieren algunas de las otras respuestas a este hilo. Me remito a estas respuestas para hacer que funcione para usted. SNMP es un excelente protocolo, pero tenga en cuenta que los OID de SNMP pueden ser dependientes de la plataforma y dependientes del proveedor. Los OID que funcionan para Windows no siempre funcionan para Linux y viceversa.
    • Si sabe que su host ejecuta Windows, podría usar WMI . La clase Win32_NetworkAdapter contiene la información que desea, pero tenga en cuenta que esto devuelve todas las NIC de hosts, incluso las que compone Windows. Además, requiere credenciales de administrador para el host que está escaneando. Google le dirá cómo conectarse a WMI desde Java.
    • Si sabe que su host ejecuta OS X, es posible que pueda SSH en la máquina y system_profile el system_profile comando system_profile .
    • Para Linux, probablemente exista una herramienta similar a system_profile OS X.

Puede obtener su propia dirección MAC a través de:

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

Definitivamente no hay forma de que pueda obtener la dirección MAC de otro host a través de vainilla java. Tendría que usar la ejecución del proceso o una biblioteca nativa para hacerlo.

Si controlas las otras máquinas, puedes dejar que consulten su propio MAC y lo envíen a través de un canal TCP / IP, pero supongo que eso no es lo que quieres. Para más detalles, vea la respuesta de jqno.

La memoria caché de arp se proporciona como estándar en el conjunto de datos SNMP disponibles. Puede usar SNMP4J para escribir un agente trivial para consultar estos datos.

por ejemplo, desde un conjunto de herramientas SNMP de línea de comando

 snmpwalk ${hostname} 1.3.6.1.2.1.4.22.1.2 

(esa gran cadena delimitada por un período es el OID, o identificador, de la memoria caché ARP en términos de SNMP. Eso funcionará para todas las implementaciones de SNMP)

ARP es la forma de asignar direcciones IP a direcciones MAC. Así es como lo hace la stack IP.

No estoy seguro de que haya una forma portátil de obtener esa información, ya que normalmente solo es importante para los desarrolladores del kernel y los administradores del sistema.

A partir de una gran cantidad de búsquedas en la web, parece que es posible obtener una tabla ARP de un enrutador usando SNMP, pero no encontré mucha información específica sobre cómo hacerlo. Sin embargo, encontré una biblioteca gratuita de Java para SNMP. Alguna espeleología podría ser productivo.

Hay una manera mucho más simple:

 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 )); 

Y obtienes la tabla ARP eintire con IP y HW ordenados en cada fila.

A continuación, puede dividir la tabla en filas de cadenas separadas y usar expresiones regulares en cada fila para que coincidan con las direcciones HW y IP. Y tu estas listo.

Es posible que esto no se pueda solucionar en el contexto de Java (porque es independiente de la plataforma), pero también debe considerar si puede o no obtener las direcciones MAC a través de un servicio del sistema. Probablemente haya situaciones en las que no pueda encontrar de manera confiable la dirección MAC a través de ARP, depende de por qué necesitaría la dirección MAC.

Como otros han dicho, ARP es el camino a seguir. Lo que sigue es una implementación de la segunda sugerencia de jqnos basada en este ejemplo en GitSpot .

Se requieren dos bibliotecas:

  1. biblioteca del sistema para la captura del tráfico de red:
  2. la biblioteca jpcap java disponible desde el sitio jpcap sourceforge , que proporciona una interfaz de alto nivel para la primera biblioteca a través de 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; } } 

Inspirado por la respuesta de greenspand, se me ocurrió este código que consultará la dirección MAC usando el comando IP y CMD utilizando una IP especificada.

Tenga en cuenta que este código funciona en Windows y creo que también puede funcionar en Linux con pequeñas modificaciones.

  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 }