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, любая помощь.
Обновить
версия java "1.7.0_21"
Java (TM) SE Runtime Environment (сборка 1.7.0_21-b11)
Java HotSpot (TM) 64-разрядная серверная VM (сборка 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)
Ответы
Ответ 1
Очевидно, что я был не прав в первую очередь: хотя оба ifconfig
и Java API используют
те же самые ioctl()
syscalls, они ведут себя по-другому.
Прежде всего, SIOCGIFCONF ioctl()
документируется следующим образом (см. http://linux.die.net/man/7/netdevice):
SIOCGIFCONF
Return a list of interface (transport layer) addresses.
...
The kernel fills the ifreqs with all current L3 interface
addresses that are running.
Таким образом, 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
.
По существу, существуют три разных подхода для получения аппаратных адресов устройств all в 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!!)
...
}
}
Ответ 2
При вызове getNetworkInterfaces
Java вернет
all the interfaces on this machine. Returns null if no network interfaces could be found on this machine.
Вы не единственная проблема с этой проблемой . По-видимому, на linux Java вернет только интерфейсы, у которых есть выделенный IP-адрес (например, настроенные адаптеры).
Но с вашей точки зрения приложения (если вы не создаете приложение сетевой конфигурации), имеющее интерфейс без IP-адреса, похоже на отсутствие его. Вам придется либо опросить интерфейсы, либо получить их каждый раз, когда вы получаете доступ, скажем, "Настройки сети" в своем приложении.
Ответ 3
Расширение ответа @Andreas, мы могли бы написать небольшую оболочку script как $ifconfg | grep "Link encap" > some_file
, а затем будет иметь меньший (только 3 строки) файл для синтаксического анализа и выбора первого токена в каждой строке. Подобная вещь для получения HWaddress. Мы напишем меньший код Java.
Другим вариантом может быть использование общедоступных сообщений Apache IOUtils.toString(new FileInputStream( <file_path>,US_ASCII))
для чтения настроек. Это устранит повторяющийся код ввода/вывода Java в его решении.