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 в его решении.

Другие вопросы по тегам