Consultar cache ARP para obter o ID MAC

Eu preciso obter o MAC ID de um host na minha rede. Para isso, se eu pingar para esse IP e consultar o cache ARP arp -a , eu posso obter o ID MAC. Pergunto-me se posso obter qualquer API para consultar o ARP e obter o ID MAC.

Além disso, se houver um método melhor para obter o ID MAC do endereço IP, por favor sugerir.

PS: Eu estou trabalhando em Java.

Obrigado.

O Java não fornece uma maneira direta de consultar o endereço MAC de um host em sua rede, pois isso é abstraído pelas bibliotecas de soquete do Java.

De certa forma, isso faz sentido, porque o endereço MAC de um host realmente diz muito pouco. Não existe tal coisa como “o” endereço MAC de um host.

  • Muitos hosts terão vários NICs, todos com um endereço MAC separado, com o qual podem se conectar à rede. O computador que eu estou no momento tem um adaptador ethernet com fio, um adaptador WiFi e um adaptador Firewire, e todos eles têm seu próprio endereço MAC. Isso significa que não há endereço MAC definitivo para um host.
  • Se o host estiver em uma sub-rede diferente, o ARP fornecerá o endereço MAC do último roteador pelo qual o pacote passou, em vez do endereço MAC do host que você está digitalizando.

Colocar esses dois problemas juntos, e isso significa que um host pode ter muitos endereços MAC diferentes (se tiver mais de uma NIC) e um endereço MAC pode representar muitos hosts diferentes (se o tráfego passar por um roteador).

Supondo que você saiba tudo isso e ainda precise obter o endereço MAC de um host, a única maneira de fazer isso em Java é “indo nativo”:

  • Nativo para o cliente que executa seu programa:
    • Você pode iniciar uma ferramenta de linha de comando ARP e analisar sua saída.
    • Você poderia usar algum tipo de chamada JNI. Eu não estou muito familiarizado com o JNI, então eu não posso te ajudar com isso.
    • Escreva um aplicativo nativo pequeno e separado que você possa acessar a partir do Java via Telnet ou algum desses protocolos, e que executará o comando ARP para você.
  • Nativo para o host que você deseja verificar:
    • Você poderia usar o SNMP, como sugerem algumas das outras respostas a este tópico. Eu aceito essas respostas por fazer esse trabalho para você. O SNMP é um excelente protocolo, mas esteja ciente de que os OIDs do SNMP podem ser dependentes da plataforma e do fornecedor. Os OIDs que funcionam para o Windows nem sempre funcionam para o Linux e vice-versa.
    • Se você sabe que seu host executa o Windows, você pode usar o WMI . A class Win32_NetworkAdapter contém as informações desejadas, mas esteja ciente de que isso retorna todas as NICs dos hosts, mesmo as que o Windows compõe. Além disso, requer credenciais de administrador para o host que você está verificando. O Google dirá a você como se conectar ao WMI a partir do Java.
    • Se você souber que seu host executa o OS X, você poderá executar o SSH na máquina e analisar a saída do comando system_profile .
    • Para o Linux, provavelmente existe uma ferramenta semelhante ao system_profile do OS X.

Você pode obter seu próprio endereço MAC via:

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

Definitivamente não há como você obter o endereço MAC de outro host via java vanilla. Você teria que usar a execução do processo ou uma biblioteca nativa para fazer isso.

Se você controlar as outras máquinas, poderá deixá-las consultar seu próprio MAC e enviá-lo de volta através de um canal TCP / IP, mas acredito que não seja o que você deseja. Para mais detalhes, veja a resposta de jqno.

O cache de arp é fornecido como padrão no dataset SNMP disponíveis. Você pode usar o SNMP4J para gravar um agente comum para consultar esses dados.

por exemplo, a partir de um conjunto de ferramentas SNMP de linha de comando

 snmpwalk ${hostname} 1.3.6.1.2.1.4.22.1.2 

(essa enorme cadeia delimitada por período é o OID, ou identificador, do cache ARP em termos SNMP. Isso funcionará para todas as implementações SNMP)

O ARP é o caminho para mapear endereços IP para endereços MAC. É assim que a pilha de IP faz isso.

Não tenho certeza se existe uma maneira portátil de obter essas informações, já que isso geralmente é importante apenas para desenvolvedores de kernel e administradores de sistemas.

De muitas pesquisas na web, parece que é possível obter a tabela ARP de um roteador usando o SNMP, mas não encontrei muitas informações específicas sobre como fazer isso. Eu encontrei uma biblioteca Java livre para SNMP aqui . Alguns spelunking por lá podem ser produtivos.

Existe uma maneira muito mais simples:

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

E você obtém a tabela ARP eintire com IP e HW classificados em cada linha.

Em seguida, você pode dividir a tabela em linhas String separadas e usar expressões regulares em cada linha para corresponder a HW e IP Adress. E pronto.

Isso pode não ser solucionável no contexto do Java (porque é independente da plataforma), mas você também deve considerar se pode ou não obter os endereços MAC através de um serviço do sistema. Provavelmente existem situações em que você não consegue encontrar o endereço MAC de maneira confiável via ARP, isso depende de por que você precisaria do endereço MAC.

Como outros já disseram, o ARP é o caminho a percorrer. Segue-se uma implementação da segunda sugestão do jqnos com base neste exemplo no GitSpot .

Duas bibliotecas são necessárias:

  1. biblioteca do sistema para captura de tráfego de rede:
  2. a biblioteca jpcap java disponível no site jpcap sourceforge , que fornece uma interface de alto nível para a primeira biblioteca através do 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 pela resposta greenspand eu criei este código que irá consultar o endereço MAC usando o comando IP e CMD usando o IP especificado.

Note que este código funciona no Windows e eu acredito que ele pode funcionar no Linux também com pequenas modificações.

  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 }