NetworkInterface.getNetworkInterfaces() не перечисляет все интерфейсы
У меня три интерфейса (eth0,Loopback,wlan0)
на моей машине, и я хочу использовать Java-API, чтобы получить MAC-адрес.
Я использую этот код.
Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces(); for (NetworkInterface netint : Collections.list(nets)) displayInterfaceInformation(netint); } static void displayInterfaceInformation(NetworkInterface netint) throws SocketException { System.out.println("Display name: " + netint.getDisplayName()); System.out.println("Hardware address: " + Arrays.toString(netint.getHardwareAddress())); }
Но этот код печати
wlan0,loopback
, но пропустил eth0.- Моя ОС Ubuntu, любая помощь.
Обновить
О / п (
strace -f java Networks 2>&1| grep ioctl
).. пусто (пусто).java -version
Java-версия "1.7.0_21" Java(TM) SE Runtime Environment (сборка 1.7.0_21-b11) Java HotSpot(TM) 64-битная виртуальная машина сервера (сборка 23.21-b01, смешанный режим)
- strace ifconfig 2> & 1 | grep ioctl
ioctl(4, SIOCGIFCONF, {80, {{"lo", {AF_INET, inet_addr("127.0.0.1")}}, {"wlan0", {AF_INET, inet_addr("192.168.1.101")}}}}) = 0 ioctl(5, SIOCGIFFLAGS, {ifr_name="eth0", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_MULTICAST}) = 0 ioctl(5, SIOCGIFHWADDR, {ifr_name="eth0", ifr_hwaddr=-----------------}) = 0 ioctl(5, SIOCGIFMETRIC, {ifr_name="eth0", ifr_metric=0}) = 0 ioctl(5, SIOCGIFMTU, {ifr_name="eth0", ifr_mtu=1500}) = 0 ioctl(5, SIOCGIFMAP, {ifr_name="eth0", ifr_map={mem_start=0, mem_end=0, base_addr=0, irq=0, dma=0, port=0}}) = 0 ioctl(5, SIOCGIFMAP, {ifr_name="eth0", ifr_map={mem_start=0, mem_end=0, base_addr=0, irq=0, dma=0, port=0}}) = 0 ioctl(5, SIOCGIFTXQLEN, {ifr_name="eth0", ifr_qlen=1000}) = 0 ioctl(4, SIOCGIFADDR, {ifr_name="eth0", ???}) = -1 EADDRNOTAVAIL (Cannot assign requested address) ioctl(5, SIOCGIFFLAGS, {ifr_name="lo", ifr_flags=IFF_UP|IFF_LOOPBACK|IFF_RUNNING}) = 0 ioctl(5, SIOCGIFHWADDR, {ifr_name="lo", ifr_hwaddr=00:00:00:00:00:00}) = 0 ioctl(5, SIOCGIFMETRIC, {ifr_name="lo", ifr_metric=0}) = 0 ioctl(5, SIOCGIFMTU, {ifr_name="lo", ifr_mtu=16436}) = 0 ioctl(5, SIOCGIFMAP, {ifr_name="lo", ifr_map={mem_start=0, mem_end=0, base_addr=0, irq=0, dma=0, port=0}}) = 0 ioctl(5, SIOCGIFMAP, {ifr_name="lo", ifr_map={mem_start=0, mem_end=0, base_addr=0, irq=0, dma=0, port=0}}) = 0 ioctl(5, SIOCGIFTXQLEN, {ifr_name="lo", ifr_qlen=0}) = 0 ioctl(4, SIOCGIFADDR, {ifr_name="lo", ifr_addr={AF_INET, inet_addr("127.0.0.1")}}) = 0 ioctl(4, SIOCGIFDSTADDR, {ifr_name="lo", ifr_dstaddr={AF_INET, inet_addr("127.0.0.1")}}) = 0 ioctl(4, SIOCGIFBRDADDR, {ifr_name="lo", ifr_broadaddr={AF_INET, inet_addr("0.0.0.0")}}) = 0 ioctl(4, SIOCGIFNETMASK, {ifr_name="lo", ifr_netmask={AF_INET, inet_addr("255.0.0.0")}}) = 0 ioctl(5, SIOCGIFFLAGS, {ifr_name="wlan0", ifr_flags=IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_MULTICAST}) = 0 ioctl(5, SIOCGIFHWADDR, {ifr_name="wlan0", ifr_hwaddr=---------------}) = 0 ioctl(5, SIOCGIFMETRIC, {ifr_name="wlan0", ifr_metric=0}) = 0 ioctl(5, SIOCGIFMTU, {ifr_name="wlan0", ifr_mtu=1500}) = 0 ioctl(5, SIOCGIFMAP, {ifr_name="wlan0", ifr_map={mem_start=0, mem_end=0, base_addr=0, irq=0, dma=0, port=0}}) = 0 ioctl(5, SIOCGIFMAP, {ifr_name="wlan0", ifr_map={mem_start=0, mem_end=0, base_addr=0, irq=0, dma=0, port=0}}) = 0 ioctl(5, SIOCGIFTXQLEN, {ifr_name="wlan0", ifr_qlen=1000}) = 0 ioctl(4, SIOCGIFADDR, {ifr_name="wlan0", ifr_addr={AF_INET, inet_addr("192.168.1.101")}}) = 0 ioctl(4, SIOCGIFDSTADDR, {ifr_name="wlan0", ifr_dstaddr={AF_INET, inet_addr("192.168.1.101")}}) = 0 ioctl(4, SIOCGIFBRDADDR, {ifr_name="wlan0", ifr_broadaddr={AF_INET, inet_addr("192.168.1.255")}}) = 0 ioctl(4, SIOCGIFNETMASK, {ifr_name="wlan0", ifr_netmask={AF_INET, inet_addr("255.255.255.0")}}) = 0
Ifconfig
$ ifconfig eth0 Link encap:Ethernet HWaddr ------------- UP BROADCAST MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:16436 Metric:1 RX packets:1695 errors:0 dropped:0 overruns:0 frame:0 TX packets:1695 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:129949 (129.9 KB) TX bytes:129949 (129.9 KB) wlan0 Link encap:Ethernet HWaddr ------------------- inet addr:192.168.1.101 Bcast:192.168.1.255 Mask:255.255.255.0 inet6 addr: fe80::-------------- Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:8396 errors:0 dropped:0 overruns:0 frame:0 TX packets:5524 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:3959941 (3.9 MB) TX bytes:1513934 (1.5 MB)
3 ответа
Очевидно, я был неправ в первую очередь: хотя оба ifconfig
и API Java используют то же самое ioctl()
Системные вызовы, они ведут себя по-разному.
Прежде всего, SIOCGIFCONF ioctl()
задокументировано следующим образом (см. http://linux.die.net/man/7/netdevice):
SIOCGIFCONF Возвращает список адресов интерфейса (транспортного уровня).... Ядро заполняет ifreqs всем текущим интерфейсом L3 адреса , которые работают.
Итак SIOCGIFCONF ioctl()
который используется обоими ifconfig
а JAVA API возвращает только работающие интерфейсы. Это также можно увидеть в strace ifconfig ...
выход из вопроса - самый первый ioctl
только возвращается lo
а также wlan0
, но не eth0.
Тогда где же ifconfig
получить eth0
из всех? Проверка ifconfig
исходный код (из net-tools
пакет на Debian/Ubuntu), мы видим, что ifconfig
не использует результат из ioctl()
в качестве основы для перечисления сетевых устройств, но в первую очередь читает /proc
файловая система для определения всех сетевых интерфейсов. Затем он использует ioctl()
системные вызовы для определения дополнительной информации о каждом интерфейсе.
К сожалению, java.net.NetworkInterface.getByName()
Метод даже не возвращает объект сетевого интерфейса для ненастроенного интерфейса, если мы явно передаем имя, например eth0
,
По сути, остаются три разных подхода для получения аппаратных адресов всех устройств в Linux:
- Вызов
ifconfig
и разобрать вывод (должно быть в крайнем случае) - Реализуйте библиотеку JNI, чтобы сделать то же самое, что
ifconfig
делает (требует зависимой от архитектуры разделяемой библиотеки) - Читайте данные прямо из
/proc
и/sys
файловые системы.
Все эти подходы зависят от системы и не являются переносимыми. Преимущество третьего подхода заключается в том, что он может быть реализован на чистой Java. Ниже приведен пример реализации третьего подхода, который хорошо работал в моей среде:
static void printHardwareAddresses() throws SocketException {
if (System.getProperty("os.name").equals("Linux")) {
// Read all available device names
List<String> devices = new ArrayList<>();
Pattern pattern = Pattern.compile("^ *(.*):");
try (FileReader reader = new FileReader("/proc/net/dev")) {
BufferedReader in = new BufferedReader(reader);
String line = null;
while( (line = in.readLine()) != null) {
Matcher m = pattern.matcher(line);
if (m.find()) {
devices.add(m.group(1));
}
}
} catch (IOException e) {
e.printStackTrace();
}
// read the hardware address for each device
for (String device : devices) {
try (FileReader reader = new FileReader("/sys/class/net/" + device + "/address")) {
BufferedReader in = new BufferedReader(reader);
String addr = in.readLine();
System.out.println(String.format("%5s: %s", device, addr));
} catch (IOException e) {
e.printStackTrace();
}
}
} else {
// use standard API for Windows & Others (need to test on each platform, though!!)
...
}
}
При звонке getNetworkInterfaces
Java вернется
all the interfaces on this machine. Returns null if no network interfaces could be found on this machine.
Вы не единственный с этой проблемой. По-видимому, в linux Java будет возвращать только те интерфейсы, которым назначен IP-адрес (то есть настроенные адаптеры).
Но с точки зрения вашего приложения (если вы не создаете приложение для конфигурации сети) наличие интерфейса без IP-адреса похоже на отсутствие его вообще. Вам придется либо запрашивать интерфейсы, либо получать их каждый раз, когда вы обращаетесь, например, к "Сетевым настройкам" в своем приложении.
Расширяя ответ @Andreas, мы могли бы написать небольшой скрипт$ifconfg | grep "Link encap" > some_file
и затем будет иметь файл меньшего размера (всего 3 строки) для анализа и выбора первого токена в каждой строке. Аналогичная вещь для получения HWaddress. Мы напишем меньший код Java.
Другим вариантом может быть использование Apache Commons IOUtils.toString(new FileInputStream( <file_path>,US_ASCII))
читать настройки. Это устранит повторяющийся код ввода-вывода Java в его решении.