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, любая помощь.

Обновить

  • o/p (strace -f java Networks 2>&1| grep ioctl).. blank (пусто).

  • java -version

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