Кэш запроса ARP для получения идентификатора MAC
Мне нужно получить идентификатор MAC хоста в моей сети. Для этого, если я пинг на этот IP-адрес и запрошу кеш ARP arp -a
, я могу получить идентификатор MAC. Мне просто интересно, могу ли я получить API для запроса ARP и получить идентификатор MAC.
Кроме того, если есть лучший способ получить идентификатор MAC из IP-адреса, пожалуйста, предложите.
P.S: Я работаю в JAVA.
Спасибо.
Ответы
Ответ 1
Java не предоставляет прямого способа запроса MAC-адреса хоста в вашей сети, поскольку это абстрагируется библиотеками сокетов Java.
В некотором смысле это имеет смысл, потому что MAC-адрес хоста фактически говорит очень мало. MAC-адрес хоста не существует.
- Многие хосты будут иметь несколько сетевых адаптеров, все с отдельным MAC-адресом, с которым они могут подключиться к сети. На компьютере, на котором я сейчас работаю, есть проводной адаптер ethernet, адаптер WiFi и адаптер Firewire, и все они имеют свой собственный MAC-адрес. Это означает, что для хоста нет окончательного MAC-адреса.
- Если хост находится в другой подсети, ARP фактически предоставит вам MAC-адрес для последнего маршрутизатора, через который проходит ваш пакет, вместо MAC-адреса хоста, который вы сканируете.
Сопоставьте обе эти проблемы вместе, а это означает, что на одном хосте может быть много разных MAC-адресов (если у них несколько NIC), а один 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
.
Ответ 2
Кэш arp предоставляется в качестве стандарта в наборе доступных данных SNMP. Вы можете использовать SNMP4J, чтобы написать тривиальный агент для запроса этих данных.
например. из набора инструментов SNMP командной строки
snmpwalk ${hostname} 1.3.6.1.2.1.4.22.1.2
(эта огромная строка с разделителями периодов - это OID или идентификатор кэша ARP в терминах SNMP, что будет работать для всех реализаций SNMP)
Ответ 3
ARP - это способ сопоставления IP-адресов с MAC-адресами. То, как это делает IP-стек.
Я не уверен, что есть переносимый способ получить эту информацию, поскольку она обычно важна только для разработчиков ядра и системных администраторов.
Из большого количества веб-поиска, похоже, можно получить таблицу ARP-маршрутизатора с использованием SNMP, но я не нашел много конкретной информации о том, как это сделать. Однако я нашел бесплатную библиотеку Java для SNMP здесь. Некоторые из них могут оказаться полезными.
Ответ 4
Вы можете получить свой собственный MAC-адрес через:
Enumeration<NetworkInterface> it = NetworkInterface.getNetworkInterfaces();
while ( it.hasMoreElements() ) {
byte[] macAddress = it.nextElement().getHardwareAddress();
}
Определенно, вы не можете получить MAC-адрес другого хоста через vanilla java. Для этого вам нужно будет использовать выполнение процесса или собственную библиотеку.
Если вы управляете другими машинами, вы можете позволить им запросить свой собственный MAC-адрес и отправить его обратно по каналу TCP/IP, но я предполагаю, что это не то, что вы хотите. Для получения дополнительной информации см. Ответ jqno.
Ответ 5
Существует гораздо более простой способ:
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 eintire с IP и HW, отсортированную по каждой строке.
Затем вы можете разбить таблицу на отдельные строки строк и использовать регулярные выражения для каждой строки для соответствия как HW, так и IP-адресу. И все готово.
Ответ 6
Это может быть не разрешимо в контексте Java (потому что оно независимо от платформы), но вы также должны учитывать, сможете ли вы получать MAC-адреса через системную службу. Вероятно, есть ситуации, когда вы не можете надежно найти MAC-адрес через ARP, это зависит от того, зачем вам нужен MAC-адрес.
Ответ 7
Как говорили другие, ARP - это путь. Ниже приведена реализация второго предложения jqnos на основе этого примера в GitSpot.
Требуются две библиотеки:
- системная библиотека для захвата сетевого трафика:
-
java-библиотека jpcap, доступная из сайта jpcap sourceforge, которая обеспечивает интерфейс высокого уровня для первой библиотеки через 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;
}
}
Ответ 8
В ответ на greenspand ответ я придумал этот код, который будет запрашивать MAC-адрес с помощью 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
}
Ответ 9
Я предоставил полностью готовый к использованию метод с использованием pcap4j + libpcap для обнаружения MAC-адресов IPV4 и ipv6 здесь: https://github.com/gaoxingliang/mac-address-detector-java