Запросите кэш ARP для получения MAC ID - PullRequest
13 голосов
/ 06 августа 2009

Мне нужно получить MAC-идентификатор хоста в моей сети. Для этого, если я пинг до этого IP и запросить кэш ARP arp -a, я могу получить MAC ID. Мне просто интересно, могу ли я получить какой-либо API для запроса ARP и получить идентификатор MAC.

Кроме того, если есть лучший способ получить MAC-идентификатор с IP-адреса, предложите.

П.С .: Я работаю в JAVA.

Спасибо.

Ответы [ 8 ]

10 голосов
/ 14 августа 2009

Java не предоставляет прямого способа запрашивать MAC-адрес хоста в вашей сети, поскольку он абстрагируется библиотеками сокетов Java.

В некотором смысле это имеет смысл, потому что MAC-адрес хоста на самом деле говорит очень мало. Не существует такого понятия, как «адрес» MAC хоста.

  • Многие хосты будут иметь несколько сетевых карт, каждый с отдельным MAC-адресом, с помощью которого они могут подключаться к сети. Компьютер, на котором я сейчас работаю, имеет проводной адаптер Ethernet, адаптер WiFi и адаптер Firewire, и все они имеют свой собственный MAC-адрес. Это означает, что для хоста нет определенного MAC-адреса.
  • Если хост находится в другой подсети, ARP фактически даст вам MAC-адрес последнего маршрутизатора, через который прошел ваш пакет, вместо MAC-адреса сканируемого хоста.

Поместите обе эти проблемы вместе, и это означает, что один хост может иметь много разных MAC-адресов (если у него более одного сетевого адаптера), и один MAC-адрес может представлять много разных хостов (если трафик проходит через маршрутизатор).

Предполагая, что вы знаете все это, и вам все еще нужно получить MAC-адрес хоста, единственный способ сделать это в Java - "перейти к нативному":

  • Родной для клиента, который запускает вашу программу:
    • Вы можете запустить инструмент командной строки ARP и проанализировать его вывод.
    • Вы можете использовать какой-то вызов JNI. Я не слишком знаком с JNI, поэтому не могу помочь вам с этим.
    • Напишите отдельное маленькое нативное приложение, к которому вы можете получить доступ из Java через Telnet или другой подобный протокол и которое запустит команду ARP.
  • Родной для хоста, который вы хотите сканировать:
    • Вы можете использовать SNMP, как предлагают другие ответы в этой теме. Я полагаюсь на эти ответы для того, чтобы сделать это для вас. SNMP - отличный протокол, но имейте в виду, что OID SNMP могут зависеть как от платформы, так и от поставщика. OID, которые работают для Windows, не всегда работают для Linux и наоборот.
    • Если вы знаете, что ваш хост работает под управлением Windows, вы можете использовать WMI . Класс Win32_NetworkAdapter содержит необходимую информацию, но имейте в виду, что она возвращает всех сетевых адаптеров хостов, даже тех, которые Windows создает. Кроме того, требуются учетные данные администратора для сканируемого хоста. Google расскажет вам, как подключиться к WMI из Java.
    • Если вы знаете, что на вашем хосте работает OS X, вы можете подключиться к компьютеру по SSH и проанализировать вывод команды system_profile.
    • Для Linux, вероятно, существует инструмент, аналогичный OS X system_profile.
4 голосов
/ 16 августа 2009

Вы можете получить свой собственный MAC-адрес через:

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

Определенно нет способа получить MAC-адрес другого хоста через vanilla java. Для этого вам нужно использовать выполнение процесса или собственную библиотеку.

Если вы контролируете другие машины, вы можете позволить им запрашивать их собственный MAC и отправлять его обратно по каналу TCP / IP, но я предполагаю, что это не то, что вам нужно. Подробнее см. Ответ jqno.

4 голосов
/ 06 августа 2009

ARP - это способ для сопоставления IP-адресов с MAC-адресами. Вот как это делает стек IP.

Я не уверен, что есть переносимый способ получения этой информации, поскольку она обычно важна только для разработчиков ядра и системных администраторов.

По результатам многих поисков в Интернете можно получить таблицу ARP маршрутизатора с использованием SNMP, но я не нашел много конкретной информации о том, как это сделать. Я нашел бесплатную библиотеку Java для SNMP здесь . Некоторые разговоры там могут оказаться продуктивными.

4 голосов
/ 06 августа 2009

Arp-кеш предоставляется в качестве стандартного набора доступных данных SNMP. Вы можете использовать SNMP4J , чтобы написать тривиальный агент для запроса этих данных.

например. из командной строки SNMP toolset

snmpwalk ${hostname} 1.3.6.1.2.1.4.22.1.2

(эта огромная строка, разделенная точками, является OID или идентификатором кэша ARP в терминах SNMP. Это будет работать для всех реализаций SNMP)

3 голосов
/ 05 июля 2015

Есть гораздо более простой способ:

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

И вы получите новую таблицу ARP с IP и HW, отсортированными в каждом ряду.

Затем вы можете разбить таблицу на отдельные строки String и использовать регулярные выражения в каждой строке для соответствия HW и IP-адресу. И все готово.

2 голосов
/ 02 июня 2014

Как уже говорили другие, ARP - это путь. Ниже приведена реализация второго предложения jqnos на основе этого примера на GitSpot .

Требуются две библиотеки:

  1. системная библиотека для захвата сетевого трафика:
  2. java-библиотека jpcap, доступная с sourceforge-сайта jpcap , который предоставляет высокоуровневый интерфейс для первой библиотеки через 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;
    }
    
    }
    
2 голосов
/ 10 августа 2009

Это может быть не решаемо в контексте Java (потому что оно не зависит от платформы), но вы также должны подумать, можете ли вы получить MAC-адреса через системную службу. Вероятно, существуют ситуации, когда вы не можете надежно найти MAC-адрес через ARP, это зависит от того, зачем вам нужен MAC-адрес.

0 голосов
/ 09 февраля 2017

Вдохновленный ответом greenspand Я придумал этот код, который будет запрашивать MAC-адрес 1004 * с использованием IP и команду CMD с указанным IP.

Обратите внимание, что этот код работает на 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

    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...