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

Jas*_*n C 5 java windows windows-7 jpcap

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

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

然而,以下几点给我带来了麻烦:

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

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

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

我还没有在 Linux 上尝试过这个。我怀疑这个问题是 Windows 特有的,它NetworkInterface#getName()PacketCapture#open().

我如何获取 jpcap 打开设备所需的信息NetworkInterface(或相反 - 获取NetworkInterface给定的设备路径),或者是否有另一种方法可以让我获得一个好的显示名称和 IP 地址每个设备都直接来自 jpcap?


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\services\<guid>\Parameters\Tcpip包含类似信息。
  4. 搜索 中子项的所有子项HKLM\SYSTEM\CurrentControlSet\Control\Class\。如果发现一个子项包含一个NetCfgInstanceId值为<guid>的键,那么其余的键将包含驱动程序信息 - 漂亮的显示名称、供应商信息等。

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

Jas*_*n C 0

独立于平台的网络接口

这是一个替代解决方案,尽管仅提供已启动接口的信息,但它应该独立于平台。注册表解决方案是我的第一次尝试,效果很好,但我相信只要不需要有关下行接口的信息,这是一个更好的解决方案。

方法

  1. PacketCapture可以在给定设备字符串的情况下提供网络地址和子网掩码(但它是实例方法,而不是静态方法)。对于 中的每个设备字符串PacketCapture.lookupDevices()
  2. 从实例中获取其网络地址和掩码PacketCapture(捕获不需要打开)。
  3. 搜索由 jpcap 返回的所有网络接口NetworkInterface.getNetworkInterfaces(),并找到其地址与 jpcap 为设备返回的网络地址和掩码指定的同一网络上的接口。
  4. NetworkInterface(可能)对应于设备字符串。

执行

先决条件:

  • jpcap之外没有任何依赖项。使用版本 0.01.16 进行测试。

问题:

  • 虽然与平台无关,但与基于注册表的解决方案不同,它只能找到已启动的接口。
  • 字节顺序很奇怪。我不太明白 SourceForge 上的 jpcap 讨论论坛,但似乎有人指出了这一点。因此我认为未来总是会发生变化。
  • 可能有很多边缘情况会导致返回我尚未测试的错误结果。

代码

代码如下。根据 SO 的CC 归属共享许可证,可以免费使用。它是独立的,所以我没有把它放在 github 上。

import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import net.sourceforge.jpcap.capture.CaptureDeviceLookupException;
import net.sourceforge.jpcap.capture.PacketCapture;

public class JpcapInterfaceInfo {


    /**
     * Get a list of interface information for all devices returned by jpcap.
     * @param capture An instance of PacketCapture to use for getting network address and mask info. If null,
     *                a new instance will be created.
     * @return List of information.
     * @throws CaptureDeviceLookupException
     */
    public static List<InterfaceInfo> listInterfaces (PacketCapture capture) throws CaptureDeviceLookupException {

        if (capture == null)
            capture = new PacketCapture();

        List<InterfaceInfo> infos = new ArrayList<InterfaceInfo>();
        for (String device : PacketCapture.lookupDevices())
            infos.add(getInterfaceInfo(capture, device));

        return infos;

    }


    /**
     * Get a list of interface information for all devices returned by jpcap.
     * @return List of information.
     * @throws CaptureDeviceLookupException
     */
    public static List<InterfaceInfo> listInterfaces () throws CaptureDeviceLookupException {
        return listInterfaces(null);
    }




    /**
     * Utility to check if an interface address matches a jpcap network address and mask.
     * @param address An InetAddress to check.
     * @param jpcapAddr Network address.
     * @param jpcapMask Network mask.
     * @return True if address is an IPv4 address on the network given by jpcapAddr/jpcapMask,
     *         false otherwise.
     */
    private static boolean networkMatches (InetAddress address, int jpcapAddr, int jpcapMask) {

        if (!(address instanceof Inet4Address))
            return false;

        byte[] address4 = address.getAddress();
        if (address4.length != 4)
            return false;

        int addr = ByteBuffer.wrap(address4).order(ByteOrder.LITTLE_ENDIAN).getInt();        
        return ((addr & jpcapMask) == jpcapAddr);

    }


    /**
     * Get an InterfaceInfo that corresponds to the given jpcap device string. The interface must be
     * up in order to query info about it; if it is not then the NetworkInterface in the returned
     * InterfaceInfo will be null.
     * @param capture A PacketCapture instance used to get network address and mask info.
     * @param jpcapDeviceString String from PacketCapture.lookupDevices().
     * @return InterfaceInfo.
     */
    public static InterfaceInfo getInterfaceInfo (PacketCapture capture, String jpcapDeviceString) {

        InterfaceInfo info = null;
        String deviceName = jpcapDeviceString.replaceAll("\n.*", "").trim();

        try {

            int netAddress = capture.getNetwork(deviceName);
            int netMask = capture.getNetmask(deviceName);

            // go through all addresses of all interfaces and try to find a match.

            Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
            while (e.hasMoreElements() && info == null) {
                NetworkInterface iface = e.nextElement();
                Enumeration<InetAddress> ae = iface.getInetAddresses();
                while (ae.hasMoreElements() && info == null) {
                    if (networkMatches(ae.nextElement(), netAddress, netMask))
                        info = new InterfaceInfo(iface, deviceName);
                }
            }

        } catch (Exception x) {

            System.err.println("While querying info for " + deviceName + ":");
            x.printStackTrace(System.err);

        }

        if (info == null)
            info = new InterfaceInfo(null, deviceName);

        return info;

    }


    /**
     * Information about a network interface for jpcap, which is basically just a NetworkInterface
     * with details, and the jpcap device name for use with PacketCapture.
     */
    public static class InterfaceInfo {

        private final NetworkInterface iface;
        private final String deviceName;

        InterfaceInfo (NetworkInterface iface, String deviceName) {
            this.iface = iface;
            this.deviceName = deviceName;
        }

        /**
         * Get NetworkInterface for this interface.
         * @return May return null if no matching NetworkInterface was found.
         */
        public final NetworkInterface getIface () {
            return iface;
        }

        /**
         * Get jpcap device name for this interface. This can be passed to PacketCapture.open().
         * @return Device name for interface.
         */
        public final String getDeviceName () {
            return deviceName;
        }

        @Override public final String toString () {
            return deviceName + " : " + iface;
        }

    }


}
Run Code Online (Sandbox Code Playgroud)

例子

这是一个例子:

import java.util.List;

import net.sourceforge.jpcap.capture.PacketCapture;

public class JpcapInterfaceInfoTest {

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

        // Info can be queried from jpcap device list.
        List<JpcapInterfaceInfo.InterfaceInfo> infos = JpcapInterfaceInfo.listInterfaces();

        // Info can be displayed.
        for (JpcapInterfaceInfo.InterfaceInfo info : infos)
            System.out.println(info);

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

    }

}
Run Code Online (Sandbox Code Playgroud)

在我的机器上(与注册表解决方案相同的设置),输出:

\设备\NPF_{691D289D-7EE5-4BD8-B5C1-3C4729A852D5}:空
\Device\NPF_{39966C4C-3728-4368-AE92-1D36ACAF6634} :名称:net5(1x1 11b/g/n 无线 LAN PCI Express 半迷你卡适配器)

我没有使输出像其他解决方案一样漂亮。请注意,“虚拟 wifi 微型端口适配器”(第一个)为空NetworkInterface,因为它未启动,因此无法找到匹配项(不存在 IP 地址和网络地址)。