NetworkInterface.getNetworkInterfaces() not listing all interfaces

后端 未结 3 1470
野的像风
野的像风 2020-12-03 05:29

I have three interfaces (eth0,Loopback,wlan0) on my machine and i want to get use Java-API to get the mac address.

  • I use this code.

    <         
    
    
            
3条回答
  •  無奈伤痛
    2020-12-03 05:48

    Obviously, I was wrong in the first place: even though both ifconfig and the Java API are using the same ioctl() syscalls, they behave differently.

    First of all, the SIOCGIFCONF ioctl() is documented as follows (see 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.
    

    So, the SIOCGIFCONF ioctl() which is used by both ifconfig and the JAVA API only returns the running interfaces. This can also be seen in the strace ifconfig ... output from the question - the very first ioctl only returns lo and wlan0, but not eth0.

    Then, where does ifconfig get the eth0 from at all? Checking the ifconfig source code (from the net-tools package on Debian/Ubuntu), we see that ifconfig is not using the result from the ioctl() as the basis for the network device enumeration, but first of all reads the /proc filesystem to determine all network interfaces. Then, it uses the ioctl() syscalls to determine further information about each interface.

    Unfortunately, the java.net.NetworkInterface.getByName() method does not even return a network interface object for an unconfigured interface if we explicitly pass the name, like eth0.

    Essentially, there remain three different approaches to get the hardware addresses of all devices on Linux:

    • Call ifconfig and parse the output (should be last resort)
    • Implement a JNI library to do the same what ifconfig does (requires an architecture dependent shared library)
    • Read the data directly from the /proc and the /sys filesystems.

    All of these approaches are system dependant and not portable. The benefit of the third approach is that it can be implemented in pure Java. The following is a sample implementation of the third approach which worked well in my environment:

    static void printHardwareAddresses() throws SocketException {
        if (System.getProperty("os.name").equals("Linux")) {
    
            // Read all available device names
            List 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!!)
            ...
        }
    }
    

提交回复
热议问题