gpt4 book ai didi

java - 从(或将 NetworkInterface 映射到)jpcap 设备路径获取接口(interface)名称/地址

转载 作者:可可西里 更新时间:2023-11-01 14:14:39 24 4
gpt4 key购买 nike

我正在尝试执行以下操作:

  1. 向用户显示人类可读的网络接口(interface)名称及其 IP 地址的列表。
  2. 在用户选择的接口(interface)上启动 jpcap 数据包捕获。

但是,以下几点给我带来了麻烦:

  • jpcap 只提供PacketCapture.lookupDevices() ,它返回一个 Windows 的 NPF 驱动程序设备路径列表到接口(interface)(例如 \Device\NPF_{39966C4C-3728-4368-AE92-1D36ACAF6634} )和一个相当平淡的显示字符串(例如 Microsoft ),没有其他信息。所以我不能用它来构造UI界面列表。
  • NetworkInterface.getNetworkInterfaces()提供了系统上的接口(interface)列表,其中包含我需要的 UI 信息,但是 NetworkInterface不提供NDF驱动设备路径,只显示名称,以及“net5”、“lo”等设备名称
  • jpcap 的 PacketCapture#open()只接受设备路径。

列表NetworkInterface既启动又不回环的 s 确实对应于 jpcap 返回的设备列表,尽管它们的顺序不同。

因此,我在 NetworkInterface 中找不到任何内容可以传递给 PacketCapture#open() ,而且我不知道如何从 PacketCapture#lookupDevices() 返回的设备路径中获取适合 UI 的信息. PacketCapture不接受NetworkInterface#getName() .因此,我被卡住了。

我还没有在 Linux 上试过这个。我怀疑这个问题是 Windows 独有的,NetworkInterface#getName()不对应于 PacketCapture#open() 识别的设备路径.

如何从 NetworkInterface 获取 jpcap 打开设备所需的信息? (或者反过来 - 在给定设备路径的情况下获取 NetworkInterface),或者是否有另一种方法可以让我直接从 jpcap 为每个设备获取一个漂亮的显示名称和 IP 地址?


Windows 注册表:我一直在进行一些挖掘,至少在注册表中找到了有关 NPF 设备的信息。给定一个 jpcap 设备路径,并使用 one of the techniques here或 native 库、一个漂亮的适配器名称(相当于 NetworkInterface 返回的名称)和当前 IP 地址可以从注册表中获取,如下所示:

  1. 从路径中提取 GUID(例如上例中的 {39966C4C-3728-4368-AE92-1D36ACAF6634})。保留花括号并将其命名为
  2. HKLM\SYSTEM\CurrentControlSet\services\Tcpip\Parameters\Interfaces\<guid>包含设备的当前 IP 地址以及一些其他配置信息。
  3. HKLM\SYSTEM\CurrentControlSet\services\<guid>\Parameters\Tcpip包含类似的信息。
  4. 搜索HKLM\SYSTEM\CurrentControlSet\Control\Class\中子键的所有子键.如果找到包含键 NetCfgInstanceId 的子键其值为 ,则那里的其余键将包含驱动程序信息 - 漂亮的显示名称、供应商信息等。

我不知道 IPv6 是如何影响上述因素的(有一些注册表区域带有单独的 Tcpip6 信息 block )。我也不知道这些键在 Windows 7 之外是否相同,但我怀疑它们是相同的。如果没有更好的答案,我会将上面的内容转换为带有示例代码的答案。我仍在寻找更直接(最好是独立于平台且无需注册)的方式。

最佳答案

使用 Windows 注册表的间接解决方案

我至少在注册表中找到了有关 NPF 设备的信息,并将我的问题的最后一点扩展为答案。

方法

给定一个 jpcap 设备路径,一个漂亮的适配器名称(相当于 NetworkInterface 返回的名称)和当前 IP 地址可以从注册表中获取,如下所示:

  1. 从路径中提取 GUID(例如上例中的 39966C4C-3728-4368-AE92-1D36ACAF6634)。
  2. HKLM\SYSTEM\CurrentControlSet\services\Tcpip\Parameters\Interfaces\{<guid>}包含设备的当前 IP 地址以及一些其他配置信息。
  3. 搜索HKLM\SYSTEM\CurrentControlSet\Control\Class\中子键的所有子键.如果找到包含键 NetCfgInstanceId 的子键其值为 { } ,然后那里的其余键将包含驱动程序信息 - 漂亮的显示名称、供应商信息等。

实现

先决条件:

问题:

  • java.util.prefs.WindowsPreferences (因此 WinRegistry )只能读取字符串键,不能读取整数。因此,下面的代码无法可靠地确定是否启用了 DHCP。作为 hack,使用的逻辑是检查静态 IP/掩码,如果为空,则回退到 DHCP IP/掩码(值单独存储在注册表中)。
  • IP 地址是 REG_MULTI_SZ,大概也是为了考虑 IPv6 地址(验证?)。下面的代码很简单,并没有说明这一点。我没有测试过 IPv6 + IPv4。
  • 除了 Windows 7(Windows 8,有人验证过吗?),我没有在任何其他版本的 Windows 上进行过测试。
  • 针对 jpcap 0.01.16 返回的设备字符串进行了测试.
  • Linux/OSX 实现留给读者作为练习。

代码

代码如下。完整代码,包括 WinRegistry(下面未出现),也是 available on github .在 SO 的 CC attribution-sharealike license 下免费使用.

import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
* Gets information about network interface given a jpcap device string, on Windows. Makes
* use of WinRegistry class from https://stackoverflow.com/a/6163701/616460. This is tested
* against jpcap 0.01.16, which is available for download at http://sourceforge.net/projects/jpcap/.
*
* All getters return empty strings rather than null if the information is unavailable.
*
* @author https://stackoverflow.com/users/616460/jason-c
*/
public class NetworkDeviceInfo {


private static final int DRIVER_CLASS_ROOT = WinRegistry.HKEY_LOCAL_MACHINE;
private static final String DRIVER_CLASS_PATH = "SYSTEM\\CurrentControlSet\\Control\\Class";
private static final String NETCFG_INSTANCE_KEY = "NetCfgInstanceId";
private static final int IFACE_ROOT = WinRegistry.HKEY_LOCAL_MACHINE;
private static final String IFACE_PATH = "SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters\\Interfaces";


private final String jpcapDeviceName;
private final String jpcapDisplayName;
private final String guid;
private final String driverName;
private final String driverVendor;
private final String interfaceAddress;
private final String interfaceSubnetMask;


/**
* Construct from a jpcap device string.
* @param jpcapDeviceString Device string from jpcap.
* @throws IllegalArgumentException If the device string could not be parsed.
* @throws UnsupportedOperationException If the Windows registry could not be read.
*/
public NetworkDeviceInfo (String jpcapDeviceString) throws IllegalArgumentException, UnsupportedOperationException {

// extract jpcap device and display name, and guid, from jpcap device string

String[] jpcapParts = jpcapDeviceString.split("\n", 2);

jpcapDeviceName = (jpcapParts.length > 0) ? jpcapParts[0].trim() : "";
jpcapDisplayName = (jpcapParts.length > 1) ? jpcapParts[1].replaceAll("\n", " ").trim() : "";

Matcher matcher = Pattern.compile("\\{(\\S*)\\}").matcher(jpcapDeviceName);
guid = matcher.find() ? matcher.group(1) : null;
if (guid == null)
throw new IllegalArgumentException("Could not parse GUID from jpcap device name '" + jpcapDeviceName + "'");

try {

// search registry for driver details:
// Search all subkeys of subkeys in HKLM\SYSTEM\CurrentControlSet\Control\Class\. If a subkey
// is found that contains a key NetCfgInstanceId whose value is {guid}, then the rest of the keys
// there will contain driver info - the nice display name, vendor info, etc.

String theDriverName = "";
String theDriverVendor = "";

for (String driverClassSubkey : WinRegistry.readStringSubKeys(DRIVER_CLASS_ROOT, DRIVER_CLASS_PATH)) {
for (String driverSubkey : WinRegistry.readStringSubKeys(DRIVER_CLASS_ROOT, DRIVER_CLASS_PATH + "\\" + driverClassSubkey)) {
String path = DRIVER_CLASS_PATH + "\\" + driverClassSubkey + "\\" + driverSubkey;
String netCfgInstanceId = WinRegistry.readString(DRIVER_CLASS_ROOT, path, NETCFG_INSTANCE_KEY);
if (netCfgInstanceId != null && netCfgInstanceId.equalsIgnoreCase("{" + guid + "}")) {
theDriverName = trimOrDefault(WinRegistry.readString(DRIVER_CLASS_ROOT, path, "DriverDesc"), "");
theDriverVendor = trimOrDefault(WinRegistry.readString(DRIVER_CLASS_ROOT, path, "ProviderName"), "");
// other interesting keys: DriverVersion, DriverDate
break;
}
}
if (!theDriverName.isEmpty())
break;
}

driverName = trimOrDefault(theDriverName, jpcapDisplayName);
driverVendor = trimOrDefault(theDriverVendor, "Unknown");

// read tcp/ip configuration details (HKLM\SYSTEM\CCS\services\Tcpip\Parameters\Interfaces\{guid})
// there is an integer key EnableDHCP, but java.util.prefs.WindowsPreferences (and therefore
// WinRegistry) supports reading string keys only, therefore we'll have to hack it to decide on
// DHCP vs. static IP address and hope it's correct.
// also note the ip addresses are REG_MULTI_SZ, presumably to also hold ipv6 addresses. the results
// here may not be quite correct, then. that's why I'm leaving addresses as strings instead of
// converting them to InetAddresses.

String ifPath = IFACE_PATH + "\\{" + guid + "}";
String dhcpIp = trimOrDefault(WinRegistry.readString(IFACE_ROOT, ifPath, "DhcpIPAddress"), "");
String dhcpMask = trimOrDefault(WinRegistry.readString(IFACE_ROOT, ifPath, "DhcpSubnetMask"), "");
// if static set, use it, otherwise use dhcp
interfaceAddress = trimOrDefault(WinRegistry.readString(IFACE_ROOT, ifPath, "IPAddress"), dhcpIp);
interfaceSubnetMask = trimOrDefault(WinRegistry.readString(IFACE_ROOT, ifPath, "SubnetMask"), dhcpMask);

} catch (Exception x) {
throw new UnsupportedOperationException("Information could not be read from the Windows registry.", x);
}


}


/**
* @param str A string.
* @param def A default string.
* @return Returns def if str is null or empty (after trim), otherwise returns str, trimmed.
*/
private final static String trimOrDefault (String str, String def) {
str = (str == null) ? "" : str.trim();
return str.isEmpty() ? def : str;
}


/**
* Gets the jpcap device name, which can be passed to PacketCapture.
* @return Device name from jpcap. Pass this string to PacketCapture to specify this device.
*/
public final String getJpcapDeviceName () {
return jpcapDeviceName;
}


/**
* Gets the jpcap display name. Usually this is pretty bland.
* @return Display name from jpcap.
*/
public final String getJpcapDisplayName () {
return jpcapDisplayName;
}


/**
* Gets the interface GUID.
* @return Interface GUID.
*/
public final String getGuid () {
return guid;
}


/**
* Get a nice display name for the interface driver. Display this in GUIs.
* @return Interface driver name.
*/
public final String getDriverName () {
return driverName;
}


/**
* Get the interface driver vendor name. Could be displayed in GUIs.
* @return Interface driver vendor name.
*/
public final String getDriverVendor () {
return driverVendor;
}


/**
* Get the interface's IP address.
* @return Interface's IP address.
* @bug This may not be correct for interfaces with multiple IP addresses. For this reason, it is
* left as a raw string rather than being converted to an InetAddress.
*/
public final String getInterfaceAddress () {
return interfaceAddress;
}


/**
* Get the interface's subnet mask.
* @return Interface's subnet mask.
* @bug Same issue as getInterfaceAddress().
*/
public final String getInterfaceSubnetMask () {
return interfaceSubnetMask;
}


/**
* Get a display string, for debugging.
* @return Display string, for debugging.
*/
@Override public String toString () {
return String.format("%s (%s) {%s} @ %s/%s", driverName, driverVendor, guid, interfaceAddress, interfaceSubnetMask);
}


}

例子

这是一个例子:

import java.util.ArrayList;
import java.util.List;

import net.sourceforge.jpcap.capture.PacketCapture;

public class NetworkDeviceInfoTest {

public static void main (String[] args) throws Exception {

List<NetworkDeviceInfo> infos = new ArrayList<NetworkDeviceInfo>();

// Info can be queried from jpcap device string.
for (String jpcapDevice : PacketCapture.lookupDevices())
infos.add(new NetworkDeviceInfo(jpcapDevice));

// Info can be displayed.
for (NetworkDeviceInfo info : infos) {
System.out.println(info.getJpcapDeviceName() + ":");
System.out.println(" Description: " + info.getDriverName());
System.out.println(" Vendor: " + info.getDriverVendor());
System.out.println(" Address: " + info.getInterfaceAddress());
System.out.println(" Subnet Mask: " + info.getInterfaceSubnetMask());
System.out.println(" jpcap Display: " + info.getJpcapDisplayName());
System.out.println(" GUID: " + info.getGuid());
}

// Device names from NetworkDeviceInfo can be passed directly to jpcap:
NetworkDeviceInfo selected = infos.get(0);
PacketCapture capture = new PacketCapture();
capture.open(selected.getJpcapDeviceName(), true);

}

}

在我的机器上输出:

PacketCapture: loading native library jpcap.. ok\Device\NPF_{691D289D-7EE5-4BD8-B5C1-3C4729A852D5}:  Description:   Microsoft Virtual WiFi Miniport Adapter  Vendor:        Microsoft  Address:       0.0.0.0  Subnet Mask:   255.0.0.0  jpcap Display: Microsoft  GUID:          691D289D-7EE5-4BD8-B5C1-3C4729A852D5\Device\NPF_{39966C4C-3728-4368-AE92-1D36ACAF6634}:  Description:   1x1 11b/g/n Wireless LAN PCI Express Half Mini Card Adapter  Vendor:        Realtek Semiconductor Corp.  Address:       192.168.1.23  Subnet Mask:   255.255.255.0  jpcap Display: Microsoft  GUID:          39966C4C-3728-4368-AE92-1D36ACAF6634

希望这对您有所帮助。欢迎改进。也欢迎提出不使用注册表的更直接方法的更好建议。

关于java - 从(或将 NetworkInterface 映射到)jpcap 设备路径获取接口(interface)名称/地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27353784/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com