NetworkInterface.getNetworkInterfaces()未列出所有接口 [英] NetworkInterface.getNetworkInterfaces() not listing all interfaces
问题描述
我的机器上有三个接口(eth0,Loopback,wlan0)
,我想使用Java-API来获取mac地址。
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.
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 。
更新
-
o / p(
strace -f java Networks 2>& 1 | grep ioctl
).. 空白(空)。
java -version
java版1.7.0_21
Java(TM)SE运行时环境(版本1.7.0_21-b11)
Java HotSpot(TM)64位服务器VM(版本23.21-b01,混合模式)
java version "1.7.0_21" Java(TM) SE Runtime Environment (build 1.7.0_21-b11) Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode)
- strace ifconfig 2>& 1 | grep ioctl
- 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)
推荐答案
显然,我首先错了:即使 ifconfig
和Java API都使用
相同的 ioctl()
系统调用,它们的行为不同。
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.
首先, SIOCGIFCONF ioctl()
记录如下(参见 http://linux.die.net/man/ 7 / netdevice ):
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.
因此, SIOCGIFCONF ioctl()
由<$ c使用$ c> ifconfig 且JAVA API仅返回正在运行的接口。这也可以在问题的 strace ifconfig ...
输出中看到 - 第一个 ioctl
只返回 lo
和 wlan0
,但不 eth0。
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.
然后, ifconfig
从哪里获取 eth0
?检查 ifconfig
源代码(来自Debian / Ubuntu上的 net-tools
包),我们看到
那个 ifconfig
没有使用 ioctl()
的结果作为网络设备枚举的基础,
但首先读取 / proc
文件系统以确定所有网络接口。然后,它使用 ioctl()
系统调用来确定有关每个接口的更多信息。
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.
不幸的是, java.net.NetworkInterface.getByName()
如果我们显式传递名称,方法甚至不会为未配置的接口返回网络接口对象
,例如 eth0
。
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
.
基本上,有三种不同的方法可以获取Linux上所有设备的硬件地址:
Essentially, there remain three different approaches to get the hardware addresses of all devices on Linux:
- 调用
ifconfig
并解析输出(应该是最后的手段) - 实现JNI库以执行与
ifconfig
相同的操作(需要依赖于体系结构的共享库) - 直接从
/ proc
和/ sys
文件系统中读取数据。
- 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.
所有这些方法都依赖于系统而且不可移植。第三种方法的好处是它可以用纯Java实现
。以下是在我的环境中运行良好的第三种方法的示例实现:
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<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!!)
...
}
}
这篇关于NetworkInterface.getNetworkInterfaces()未列出所有接口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!