commit 675fa2433141f8a66d81068adce4b6cb3d822006 Author: Administrator <2014539504@qq.com> Date: Fri Aug 21 15:35:09 2026 +0800 Java FFM的TUN虚拟网卡封装 diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..26c53d3 --- /dev/null +++ b/.classpath @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..e0e1ee7 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + JavaTUN + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..99f26c0 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/JavaTUN.dll b/JavaTUN.dll new file mode 100644 index 0000000..6174939 Binary files /dev/null and b/JavaTUN.dll differ diff --git a/bin/one/papachi/tuntap4j/NetworkDevice$Type.class b/bin/one/papachi/tuntap4j/NetworkDevice$Type.class new file mode 100644 index 0000000..5958e50 Binary files /dev/null and b/bin/one/papachi/tuntap4j/NetworkDevice$Type.class differ diff --git a/bin/one/papachi/tuntap4j/NetworkDevice.class b/bin/one/papachi/tuntap4j/NetworkDevice.class new file mode 100644 index 0000000..f04cffb Binary files /dev/null and b/bin/one/papachi/tuntap4j/NetworkDevice.class differ diff --git a/bin/one/papachi/tuntap4j/TapDevice.class b/bin/one/papachi/tuntap4j/TapDevice.class new file mode 100644 index 0000000..14abf5b Binary files /dev/null and b/bin/one/papachi/tuntap4j/TapDevice.class differ diff --git a/bin/one/papachi/tuntap4j/TunDevice.class b/bin/one/papachi/tuntap4j/TunDevice.class new file mode 100644 index 0000000..893f625 Binary files /dev/null and b/bin/one/papachi/tuntap4j/TunDevice.class differ diff --git a/bin/one/papachi/tuntap4j/TunTap4j.class b/bin/one/papachi/tuntap4j/TunTap4j.class new file mode 100644 index 0000000..b11ee25 Binary files /dev/null and b/bin/one/papachi/tuntap4j/TunTap4j.class differ diff --git a/bin/one/papachi/tuntap4j/Utils$OperatingSystemFamily.class b/bin/one/papachi/tuntap4j/Utils$OperatingSystemFamily.class new file mode 100644 index 0000000..270fd15 Binary files /dev/null and b/bin/one/papachi/tuntap4j/Utils$OperatingSystemFamily.class differ diff --git a/bin/one/papachi/tuntap4j/Utils.class b/bin/one/papachi/tuntap4j/Utils.class new file mode 100644 index 0000000..9483f8e Binary files /dev/null and b/bin/one/papachi/tuntap4j/Utils.class differ diff --git a/bin/org/kne/cloud/network/tun/App.class b/bin/org/kne/cloud/network/tun/App.class new file mode 100644 index 0000000..5dba11f Binary files /dev/null and b/bin/org/kne/cloud/network/tun/App.class differ diff --git a/bin/org/kne/cloud/network/tun/InetAddressRow.class b/bin/org/kne/cloud/network/tun/InetAddressRow.class new file mode 100644 index 0000000..4a23ff8 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/InetAddressRow.class differ diff --git a/bin/org/kne/cloud/network/tun/LinuxTUNChannel.class b/bin/org/kne/cloud/network/tun/LinuxTUNChannel.class new file mode 100644 index 0000000..c1d7fa9 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/LinuxTUNChannel.class differ diff --git a/bin/org/kne/cloud/network/tun/LinuxTUNNetworkDevice.class b/bin/org/kne/cloud/network/tun/LinuxTUNNetworkDevice.class new file mode 100644 index 0000000..5c445d5 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/LinuxTUNNetworkDevice.class differ diff --git a/bin/org/kne/cloud/network/tun/TUNChannel.class b/bin/org/kne/cloud/network/tun/TUNChannel.class new file mode 100644 index 0000000..a075fdc Binary files /dev/null and b/bin/org/kne/cloud/network/tun/TUNChannel.class differ diff --git a/bin/org/kne/cloud/network/tun/TUNNetworkDevice.class b/bin/org/kne/cloud/network/tun/TUNNetworkDevice.class new file mode 100644 index 0000000..8a65c23 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/TUNNetworkDevice.class differ diff --git a/bin/org/kne/cloud/network/tun/TestTUN.class b/bin/org/kne/cloud/network/tun/TestTUN.class new file mode 100644 index 0000000..8377b69 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/TestTUN.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsAPIException.class b/bin/org/kne/cloud/network/tun/WindowsAPIException.class new file mode 100644 index 0000000..dd11a0c Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsAPIException.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsDNSManager$DnsInterfaceSettings.class b/bin/org/kne/cloud/network/tun/WindowsDNSManager$DnsInterfaceSettings.class new file mode 100644 index 0000000..052a5fb Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsDNSManager$DnsInterfaceSettings.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsDNSManager.class b/bin/org/kne/cloud/network/tun/WindowsDNSManager.class new file mode 100644 index 0000000..1b01ebf Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsDNSManager.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsIPAddressManager$AddressFamily.class b/bin/org/kne/cloud/network/tun/WindowsIPAddressManager$AddressFamily.class new file mode 100644 index 0000000..18eb1cb Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsIPAddressManager$AddressFamily.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsIPAddressManager$DadState.class b/bin/org/kne/cloud/network/tun/WindowsIPAddressManager$DadState.class new file mode 100644 index 0000000..190a767 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsIPAddressManager$DadState.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsIPAddressManager$PrefixOrigin.class b/bin/org/kne/cloud/network/tun/WindowsIPAddressManager$PrefixOrigin.class new file mode 100644 index 0000000..7afaeb6 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsIPAddressManager$PrefixOrigin.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsIPAddressManager$SuffixOrigin.class b/bin/org/kne/cloud/network/tun/WindowsIPAddressManager$SuffixOrigin.class new file mode 100644 index 0000000..a2a5708 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsIPAddressManager$SuffixOrigin.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsIPAddressManager$UnicastIPAddressRow.class b/bin/org/kne/cloud/network/tun/WindowsIPAddressManager$UnicastIPAddressRow.class new file mode 100644 index 0000000..5061294 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsIPAddressManager$UnicastIPAddressRow.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsIPAddressManager.class b/bin/org/kne/cloud/network/tun/WindowsIPAddressManager.class new file mode 100644 index 0000000..9e9e7c4 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsIPAddressManager.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsIPInterfaceManager$LinkLocalAddressBehavior.class b/bin/org/kne/cloud/network/tun/WindowsIPInterfaceManager$LinkLocalAddressBehavior.class new file mode 100644 index 0000000..f0952b0 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsIPInterfaceManager$LinkLocalAddressBehavior.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsIPInterfaceManager$MibIpInterfaceRow.class b/bin/org/kne/cloud/network/tun/WindowsIPInterfaceManager$MibIpInterfaceRow.class new file mode 100644 index 0000000..d77d8bb Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsIPInterfaceManager$MibIpInterfaceRow.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsIPInterfaceManager$RouterDiscoveryBehavior.class b/bin/org/kne/cloud/network/tun/WindowsIPInterfaceManager$RouterDiscoveryBehavior.class new file mode 100644 index 0000000..e09bd8d Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsIPInterfaceManager$RouterDiscoveryBehavior.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsIPInterfaceManager.class b/bin/org/kne/cloud/network/tun/WindowsIPInterfaceManager.class new file mode 100644 index 0000000..c42d729 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsIPInterfaceManager.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsInterfaceConverter$GUID.class b/bin/org/kne/cloud/network/tun/WindowsInterfaceConverter$GUID.class new file mode 100644 index 0000000..4df3938 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsInterfaceConverter$GUID.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsInterfaceConverter$LUID.class b/bin/org/kne/cloud/network/tun/WindowsInterfaceConverter$LUID.class new file mode 100644 index 0000000..2df4a30 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsInterfaceConverter$LUID.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsInterfaceConverter$WindowsAPIException.class b/bin/org/kne/cloud/network/tun/WindowsInterfaceConverter$WindowsAPIException.class new file mode 100644 index 0000000..972eeb0 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsInterfaceConverter$WindowsAPIException.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsInterfaceConverter.class b/bin/org/kne/cloud/network/tun/WindowsInterfaceConverter.class new file mode 100644 index 0000000..e8d18e3 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsInterfaceConverter.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsInterfaceManager$MibIfRow.class b/bin/org/kne/cloud/network/tun/WindowsInterfaceManager$MibIfRow.class new file mode 100644 index 0000000..0465063 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsInterfaceManager$MibIfRow.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsInterfaceManager.class b/bin/org/kne/cloud/network/tun/WindowsInterfaceManager.class new file mode 100644 index 0000000..fe05ef5 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsInterfaceManager.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsInterfaceManager2$MibIfRow.class b/bin/org/kne/cloud/network/tun/WindowsInterfaceManager2$MibIfRow.class new file mode 100644 index 0000000..848ee13 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsInterfaceManager2$MibIfRow.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsInterfaceManager2.class b/bin/org/kne/cloud/network/tun/WindowsInterfaceManager2.class new file mode 100644 index 0000000..20a538b Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsInterfaceManager2.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsTUNChannel.class b/bin/org/kne/cloud/network/tun/WindowsTUNChannel.class new file mode 100644 index 0000000..3bf3e6f Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsTUNChannel.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsTUNNetworkDevice.class b/bin/org/kne/cloud/network/tun/WindowsTUNNetworkDevice.class new file mode 100644 index 0000000..e9862bf Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsTUNNetworkDevice.class differ diff --git a/bin/org/kne/cloud/network/tun/WindowsWaitHandle.class b/bin/org/kne/cloud/network/tun/WindowsWaitHandle.class new file mode 100644 index 0000000..483c639 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WindowsWaitHandle.class differ diff --git a/bin/org/kne/cloud/network/tun/WintunDriver$LoggerCallback.class b/bin/org/kne/cloud/network/tun/WintunDriver$LoggerCallback.class new file mode 100644 index 0000000..342a86f Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WintunDriver$LoggerCallback.class differ diff --git a/bin/org/kne/cloud/network/tun/WintunDriver$NativeHandle.class b/bin/org/kne/cloud/network/tun/WintunDriver$NativeHandle.class new file mode 100644 index 0000000..90c3a2f Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WintunDriver$NativeHandle.class differ diff --git a/bin/org/kne/cloud/network/tun/WintunDriver$Packet.class b/bin/org/kne/cloud/network/tun/WintunDriver$Packet.class new file mode 100644 index 0000000..69b2fb6 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WintunDriver$Packet.class differ diff --git a/bin/org/kne/cloud/network/tun/WintunDriver$SendPacket.class b/bin/org/kne/cloud/network/tun/WintunDriver$SendPacket.class new file mode 100644 index 0000000..e23a6c0 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WintunDriver$SendPacket.class differ diff --git a/bin/org/kne/cloud/network/tun/WintunDriver$WintunAdapterHandle.class b/bin/org/kne/cloud/network/tun/WintunDriver$WintunAdapterHandle.class new file mode 100644 index 0000000..19fc53e Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WintunDriver$WintunAdapterHandle.class differ diff --git a/bin/org/kne/cloud/network/tun/WintunDriver$WintunSessionHandle.class b/bin/org/kne/cloud/network/tun/WintunDriver$WintunSessionHandle.class new file mode 100644 index 0000000..32f7b34 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WintunDriver$WintunSessionHandle.class differ diff --git a/bin/org/kne/cloud/network/tun/WintunDriver.class b/bin/org/kne/cloud/network/tun/WintunDriver.class new file mode 100644 index 0000000..fd3e847 Binary files /dev/null and b/bin/org/kne/cloud/network/tun/WintunDriver.class differ diff --git a/bin/org/kne/util/SystemInfo.class b/bin/org/kne/util/SystemInfo.class new file mode 100644 index 0000000..efbf811 Binary files /dev/null and b/bin/org/kne/util/SystemInfo.class differ diff --git a/bin/org_kne_cloud_network_tun_WindowsTUNNetworkDevice.h b/bin/org_kne_cloud_network_tun_WindowsTUNNetworkDevice.h new file mode 100644 index 0000000..bfc0e9b --- /dev/null +++ b/bin/org_kne_cloud_network_tun_WindowsTUNNetworkDevice.h @@ -0,0 +1,125 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class org_kne_cloud_network_tun_WindowsTUNNetworkDevice */ + +#ifndef _Included_org_kne_cloud_network_tun_WindowsTUNNetworkDevice +#define _Included_org_kne_cloud_network_tun_WindowsTUNNetworkDevice +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: loadDriver + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_loadDriver + (JNIEnv *, jclass); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: unloadDriver + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_unloadDriver + (JNIEnv *, jclass, jlong); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: openTUNDevice + * Signature: (Ljava/lang/String;Ljava/lang/String;JJ)J + */ +JNIEXPORT jlong JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_openTUNDevice + (JNIEnv *, jobject, jstring, jstring, jlong, jlong); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: closeTUNDevice + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_closeTUNDevice + (JNIEnv *, jobject, jlong); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: openSession + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_openSession + (JNIEnv *, jobject, jlong); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: closeSession + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_closeSession + (JNIEnv *, jobject, jlong); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: readAvaliable + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_readAvaliable + (JNIEnv *, jobject, jlong); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: waitForReadAvaliable + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_waitForReadAvaliable + (JNIEnv *, jobject, jlong); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: read0 + * Signature: (JLjava/nio/ByteBuffer;)I + */ +JNIEXPORT jint JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_read0 + (JNIEnv *, jobject, jlong, jobject); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: writeAvaliable + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_writeAvaliable + (JNIEnv *, jobject, jlong); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: write0 + * Signature: (JLjava/nio/ByteBuffer;)I + */ +JNIEXPORT jint JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_write0 + (JNIEnv *, jobject, jlong, jobject); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: setTUNDeviceIPAddressV6 + * Signature: (J[BI)I + */ +JNIEXPORT jint JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_setTUNDeviceIPAddressV6 + (JNIEnv *, jobject, jlong, jbyteArray, jint); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: getMTU0 + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_getMTU0 + (JNIEnv *, jobject, jlong); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: setMTU0 + * Signature: (JI)V + */ +JNIEXPORT void JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_setMTU0 + (JNIEnv *, jobject, jlong, jint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/lib/pcap4j-core-1.8.2.jar b/lib/pcap4j-core-1.8.2.jar new file mode 100644 index 0000000..9663471 Binary files /dev/null and b/lib/pcap4j-core-1.8.2.jar differ diff --git a/lib/pcap4j-distribution-1.8.2-src.zip b/lib/pcap4j-distribution-1.8.2-src.zip new file mode 100644 index 0000000..4002cc7 Binary files /dev/null and b/lib/pcap4j-distribution-1.8.2-src.zip differ diff --git a/lib/pcap4j-packetfactory-propertiesbased-1.8.2.jar b/lib/pcap4j-packetfactory-propertiesbased-1.8.2.jar new file mode 100644 index 0000000..7f13634 Binary files /dev/null and b/lib/pcap4j-packetfactory-propertiesbased-1.8.2.jar differ diff --git a/lib/pcap4j-packetfactory-static-1.8.2.jar b/lib/pcap4j-packetfactory-static-1.8.2.jar new file mode 100644 index 0000000..e5cf3ab Binary files /dev/null and b/lib/pcap4j-packetfactory-static-1.8.2.jar differ diff --git a/lib/pcap4j-packettest-1.8.2-tests.jar b/lib/pcap4j-packettest-1.8.2-tests.jar new file mode 100644 index 0000000..ea12ea3 Binary files /dev/null and b/lib/pcap4j-packettest-1.8.2-tests.jar differ diff --git a/lib/pcap4j-sample-1.8.2.jar b/lib/pcap4j-sample-1.8.2.jar new file mode 100644 index 0000000..efc5b8a Binary files /dev/null and b/lib/pcap4j-sample-1.8.2.jar differ diff --git a/lib/slf4j-api-2.0.9-javadoc.jar b/lib/slf4j-api-2.0.9-javadoc.jar new file mode 100644 index 0000000..17f14f3 Binary files /dev/null and b/lib/slf4j-api-2.0.9-javadoc.jar differ diff --git a/lib/slf4j-api-2.0.9-sources.jar b/lib/slf4j-api-2.0.9-sources.jar new file mode 100644 index 0000000..9a2a2dc Binary files /dev/null and b/lib/slf4j-api-2.0.9-sources.jar differ diff --git a/lib/slf4j-api-2.0.9-tests.jar b/lib/slf4j-api-2.0.9-tests.jar new file mode 100644 index 0000000..2452fa4 Binary files /dev/null and b/lib/slf4j-api-2.0.9-tests.jar differ diff --git a/lib/slf4j-api-2.0.9.jar b/lib/slf4j-api-2.0.9.jar new file mode 100644 index 0000000..3796afe Binary files /dev/null and b/lib/slf4j-api-2.0.9.jar differ diff --git a/libtuntap4j.dylib b/libtuntap4j.dylib new file mode 100644 index 0000000..1566452 Binary files /dev/null and b/libtuntap4j.dylib differ diff --git a/libtuntap4j.so b/libtuntap4j.so new file mode 100644 index 0000000..7f527e6 Binary files /dev/null and b/libtuntap4j.so differ diff --git a/src/one/papachi/tuntap4j/NetworkDevice.java b/src/one/papachi/tuntap4j/NetworkDevice.java new file mode 100644 index 0000000..3683dbd --- /dev/null +++ b/src/one/papachi/tuntap4j/NetworkDevice.java @@ -0,0 +1,121 @@ +package one.papachi.tuntap4j; + +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.file.Paths; +import java.util.List; + +public abstract class NetworkDevice { + private static final String temporaryLibraryPath = System.getProperty("."); + //private static final String temporaryLibraryPath = System.getProperty("java.io.tmpdir"); + + static { + String library = Utils.getOperatingSystemFamily().getLibraryName(); + /* File file = Paths.get(temporaryLibraryPath, library).toFile(); + try (InputStream is = NetworkDevice.class.getResourceAsStream(library); OutputStream os = new FileOutputStream(file)) { + is.transferTo(os); + } catch (Exception e) { + } + System.load(file.getAbsolutePath());*/ + File f=new File(library); + System.out.println(f.getAbsoluteFile()); + System.load(f.getAbsolutePath()); + } + + enum Type { + TUN, TAP + } + + protected final String deviceName; + + protected final Type type; + + protected long deviceHandle = -1; + + protected NetworkDevice(String deviceName, Type type) { + this.deviceName = deviceName; + this.type = type; + } + + public boolean isOpen() throws IOException { + return isOpen(deviceName, deviceHandle); + } + + public void open() throws IOException { + deviceHandle = open(deviceName, type == Type.TAP); + } + + public int read(ByteBuffer dst) throws IOException { + return read(deviceName, deviceHandle, dst); + } + + public int write(ByteBuffer src) throws IOException { + return write(deviceName, deviceHandle, src); + } + + public void close() throws IOException { + close(deviceName, deviceHandle); + } + + public void setStatus(boolean isUp) throws IOException { + setStatus(deviceName, deviceHandle, isUp); + } + + public String getIPAddress() throws IOException { + return getIPAddress(deviceName, deviceHandle); + + } + + public void setIPAddress(String address, String mask) throws IOException { + setIPAddress(deviceName, deviceHandle, address, mask); + } + + public int getMTU() throws IOException { + return getMTU(deviceName, deviceHandle); + } + + public void setMTU(int mtu) throws IOException { + setMTU(deviceName, deviceHandle, mtu); + } + + public byte[] getMACAddress() throws IOException { + if (type == Type.TUN) + throw new UnsupportedOperationException("TUN device does not have MAC address"); + return getMACAddress(deviceName, deviceHandle); + } + + public void setMACAddress(byte[] macAddress) throws IOException { + if (type == Type.TUN) + throw new UnsupportedOperationException("TUN device does not have MAC address"); + setMACAddress(deviceName, deviceHandle, macAddress); + } + + protected static native boolean isOpen(String deviceName, long deviceHandle); + + protected static native long open(String deviceName, boolean isTAP) throws IOException; + + protected static native int read(String deviceName, long deviceHandle, ByteBuffer dst) throws IOException; + + protected static native int write(String deviceName, long deviceHandle, ByteBuffer src) throws IOException; + + protected static native void close(String deviceName, long deviceHandle) throws IOException; + + protected static native void setStatus(String deviceName, long deviceHandle, boolean isUp) throws IOException; + + protected static native String getIPAddress(String deviceName, long deviceHandle) throws IOException; + + protected static native void setIPAddress(String deviceName, long deviceHandle, String address, String mask) throws IOException; + + protected static native int getMTU(String deviceName, long deviceHandle) throws IOException; + + protected static native void setMTU(String deviceName, long deviceHandle, int mtu) throws IOException; + + protected static native byte[] getMACAddress(String deviceName, long deviceHandle) throws IOException; + + protected static native void setMACAddress(String deviceName, long deviceHandle, byte[] macAddress) throws IOException; + + protected static native List getAvailableTunDevices() throws IOException; + + protected static native List getAvailableTapDevices() throws IOException; + +} diff --git a/src/one/papachi/tuntap4j/TapDevice.java b/src/one/papachi/tuntap4j/TapDevice.java new file mode 100644 index 0000000..52c3880 --- /dev/null +++ b/src/one/papachi/tuntap4j/TapDevice.java @@ -0,0 +1,9 @@ +package one.papachi.tuntap4j; + +public class TapDevice extends NetworkDevice { + + public TapDevice(String deviceName) { + super(deviceName, Type.TAP); + } + +} diff --git a/src/one/papachi/tuntap4j/TunDevice.java b/src/one/papachi/tuntap4j/TunDevice.java new file mode 100644 index 0000000..084dca5 --- /dev/null +++ b/src/one/papachi/tuntap4j/TunDevice.java @@ -0,0 +1,8 @@ +package one.papachi.tuntap4j; + +public class TunDevice extends NetworkDevice { + + public TunDevice(String deviceName) { + super(deviceName, Type.TUN); + } +} diff --git a/src/one/papachi/tuntap4j/TunTap4j.java b/src/one/papachi/tuntap4j/TunTap4j.java new file mode 100644 index 0000000..6237adb --- /dev/null +++ b/src/one/papachi/tuntap4j/TunTap4j.java @@ -0,0 +1,17 @@ +package one.papachi.tuntap4j; + +public class TunTap4j { + + public static void main(String[] args) throws Exception { + TapDevice tapDevice = new TapDevice("tap0"); + tapDevice.open(); + System.out.println(tapDevice.isOpen()); + tapDevice.setStatus(true); + System.out.println(tapDevice.getMTU()); + System.out.println(tapDevice.getMACAddress()); + tapDevice.setIPAddress("10.0.0.1", "255.0.0.0"); + System.out.println(tapDevice.getIPAddress()); + Thread.sleep(10000); + } + +} diff --git a/src/one/papachi/tuntap4j/Utils.java b/src/one/papachi/tuntap4j/Utils.java new file mode 100644 index 0000000..bc3d07b --- /dev/null +++ b/src/one/papachi/tuntap4j/Utils.java @@ -0,0 +1,31 @@ +package one.papachi.tuntap4j; + +public class Utils { + + enum OperatingSystemFamily { + WINDOWS("tuntap4j.dll"), LINUX("libtuntap4j.so"), MAC("libtuntap4j.dylib"), UNKNOWN(null); + + private String libraryName; + + private OperatingSystemFamily(String libraryName) { + this.libraryName = libraryName; + } + + public String getLibraryName() { + return libraryName; + } + } + + public static OperatingSystemFamily getOperatingSystemFamily() { + String osName = System.getProperty("os.name").toLowerCase(); + if (osName.contains("win")) { + return OperatingSystemFamily.WINDOWS; + } else if (osName.contains("nix") || osName.contains("nux") || osName.contains("aix")) { + return OperatingSystemFamily.LINUX; + } else if (osName.contains("mac")) { + return OperatingSystemFamily.MAC; + } + return OperatingSystemFamily.UNKNOWN; + } + +} diff --git a/src/org/kne/cloud/network/tun/App.java b/src/org/kne/cloud/network/tun/App.java new file mode 100644 index 0000000..a4f3d3a --- /dev/null +++ b/src/org/kne/cloud/network/tun/App.java @@ -0,0 +1,24 @@ +package org.kne.cloud.network.tun; +import one.papachi.tuntap4j.TunDevice; + +import java.io.IOException; +import java.nio.ByteBuffer; + +class App { + + public static void main(String[] args) throws IOException { + String deviceName = "klalb0"; // it can be any name e.g. "mytun" + TunDevice device = new TunDevice(deviceName); // initialize a TUN device + device.open(); // open device for reading and writing + device.setStatus(true); // set device status to "up" + device.setIPAddress("10.0.0.0", "255.0.0.0"); // set IPv4 address and mask + int mtu = device.getMTU(); // get MTU of the device + ByteBuffer buffer = ByteBuffer.allocate(mtu); // allocate ByteBuffer to use in read operation (direct ByteBuffer is supported) + while (true) { + device.read(buffer.clear()); // read from the device into ByteBuffer + buffer.flip(); // prepare ByteBuffer for reading + // TODO process IP packet + } + } + +} \ No newline at end of file diff --git a/src/org/kne/cloud/network/tun/InetAddressRow.java b/src/org/kne/cloud/network/tun/InetAddressRow.java new file mode 100644 index 0000000..47a399d --- /dev/null +++ b/src/org/kne/cloud/network/tun/InetAddressRow.java @@ -0,0 +1,50 @@ +package org.kne.cloud.network.tun; + +import java.net.InetAddress; + +public class InetAddressRow { + private InetAddress address; + private int perfix; + public InetAddressRow(InetAddress address, int perfix) { + super(); + this.address = address; + this.perfix = perfix; + } + public InetAddress getAddress() { + return address; + } + public int getPerfix() { + return perfix; + } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((address == null) ? 0 : address.hashCode()); + result = prime * result + perfix; + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + InetAddressRow other = (InetAddressRow) obj; + if (address == null) { + if (other.address != null) + return false; + } else if (!address.equals(other.address)) + return false; + if (perfix != other.perfix) + return false; + return true; + } + @Override + public String toString() { + return "InetAddressRow [address=" + address + ", perfix=" + perfix + "]"; + } + +} diff --git a/src/org/kne/cloud/network/tun/LinuxTUNChannel.java b/src/org/kne/cloud/network/tun/LinuxTUNChannel.java new file mode 100644 index 0000000..ae704e2 --- /dev/null +++ b/src/org/kne/cloud/network/tun/LinuxTUNChannel.java @@ -0,0 +1,37 @@ +package org.kne.cloud.network.tun; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import one.papachi.tuntap4j.TunDevice; + +public class LinuxTUNChannel implements TUNChannel { + private TunDevice device; + protected LinuxTUNChannel(TunDevice device) throws IOException{ + this.device=device; + device.setStatus(true); + } + @Override + public int write(ByteBuffer src) throws IOException { + return device.write(src); + } + + @Override + public int read(ByteBuffer dst) throws IOException { + return device.read(dst); + } + + @Override + public boolean isOpen() { + return device!=null; + } + + @Override + public void close() throws IOException { + if(device!=null) { + device=null; + device.setStatus(false); + } + } + +} diff --git a/src/org/kne/cloud/network/tun/LinuxTUNNetworkDevice.java b/src/org/kne/cloud/network/tun/LinuxTUNNetworkDevice.java new file mode 100644 index 0000000..d5aa6ce --- /dev/null +++ b/src/org/kne/cloud/network/tun/LinuxTUNNetworkDevice.java @@ -0,0 +1,113 @@ +package org.kne.cloud.network.tun; + +import java.io.BufferedReader; +import java.io.IOException; +import java.net.InetAddress; +import java.net.SocketException; +import java.util.List; + +import one.papachi.tuntap4j.TunDevice; + +public class LinuxTUNNetworkDevice extends TUNNetworkDevice { + private TunDevice tun; + private String name; + public LinuxTUNNetworkDevice(String name, String type) throws IOException { + this.name=name; + tun=new TunDevice(name); + tun.open(); + } + + @Override + public boolean isOpen() { + try { + return tun.isOpen(); + } catch (IOException e) { + try { + tun.close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + e.printStackTrace(); + return false; + } + } + + @Override + public void close() throws IOException { + if(channel!=null&&channel.isOpen()) { + channel.close(); + channel=null; + } + TunDevice tund=tun; + tun=null; + if(tund!=null) + tund.close(); + } + private LinuxTUNChannel channel; + @Override + public TUNChannel openSession() throws IOException { + if(channel==null) { + channel=new LinuxTUNChannel(tun); + } + return channel; + } + + @Override + public List getIPAddress() throws IOException { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setIPAddress(List rows) throws IOException { + // TODO Auto-generated method stub + for(InetAddressRow row:rows) { + try { + Process proc=Runtime.getRuntime().exec(new String[] {"ip","addr","add",row.getAddress().getHostAddress()+"/"+row.getPerfix(),"dev",name}); + Thread t=new Thread(()->{ + BufferedReader br=new BufferedReader(proc.errorReader()); + String str=null; + try { + while((str=br.readLine())!=null) { + System.out.println(str); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + }); + t.start(); + int rez=proc.waitFor(); + if(rez!=0) { + throw new SocketException("set address failed!"); + } + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + @Override + public int getMTU() throws IOException { + return tun.getMTU(); + } + + @Override + public void setMTU(int mtu) throws IOException { + tun.setMTU(mtu); + } + + @Override + public void setDNSAddress(List address) throws IOException { + // TODO Auto-generated method stub + + } + + @Override + public List getDNSAddress() throws SocketException { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/src/org/kne/cloud/network/tun/SystemInfo.class b/src/org/kne/cloud/network/tun/SystemInfo.class new file mode 100644 index 0000000..3f2f40e Binary files /dev/null and b/src/org/kne/cloud/network/tun/SystemInfo.class differ diff --git a/src/org/kne/cloud/network/tun/TUNChannel.java b/src/org/kne/cloud/network/tun/TUNChannel.java new file mode 100644 index 0000000..b911aa6 --- /dev/null +++ b/src/org/kne/cloud/network/tun/TUNChannel.java @@ -0,0 +1,25 @@ +package org.kne.cloud.network.tun; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.Channel; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.WritableByteChannel; + +import org.kne.cloud.network.tun.WintunDriver.WintunSessionHandle; + +public interface TUNChannel extends Channel, ReadableByteChannel, WritableByteChannel { + + @Override + public int write(ByteBuffer src) throws IOException; + + @Override + public int read(ByteBuffer dst) throws IOException; + + @Override + public boolean isOpen(); + + @Override + public void close() throws IOException; + +} diff --git a/src/org/kne/cloud/network/tun/TUNNetworkDevice.class b/src/org/kne/cloud/network/tun/TUNNetworkDevice.class new file mode 100644 index 0000000..f459cbb Binary files /dev/null and b/src/org/kne/cloud/network/tun/TUNNetworkDevice.class differ diff --git a/src/org/kne/cloud/network/tun/TUNNetworkDevice.java b/src/org/kne/cloud/network/tun/TUNNetworkDevice.java new file mode 100644 index 0000000..f7fa773 --- /dev/null +++ b/src/org/kne/cloud/network/tun/TUNNetworkDevice.java @@ -0,0 +1,56 @@ +package org.kne.cloud.network.tun; + +import java.io.Closeable; +import java.io.IOException; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.WritableByteChannel; +import java.util.Enumeration; +import java.util.List; + +import org.kne.util.SystemInfo; + + +public abstract class TUNNetworkDevice implements Closeable,AutoCloseable{ + private static final SystemInfo sysiInfo=new SystemInfo(); + public static TUNNetworkDevice createDevice(String name,String type)throws IOException { + String osnamel=sysiInfo.getOsName().toLowerCase(); + if(osnamel.contains("windows")) { + try { + return new WindowsTUNNetworkDevice(name,type); + }catch(UnsatisfiedLinkError err) { + throw new IOException("Load native driver failed!",err); + } + }else if(osnamel.contains("linux")) { + try { + return new LinuxTUNNetworkDevice(name,type); + }catch(UnsatisfiedLinkError err) { + throw new IOException("Load native driver failed!",err); + } + }else { + throw new UnsupportedOperationException("unsupport Operating System"); + } + } + + public abstract boolean isOpen() ;//判断是否开启 + + public abstract void close() throws IOException;//关闭网卡 + + public abstract TUNChannel openSession() throws IOException;//开启IO会话 + + public abstract List getIPAddress() throws IOException;//查询网卡IP和掩码 + + public abstract void setIPAddress(List rows) throws IOException;//设置网卡IP和掩码 + + public abstract int getMTU() throws IOException;//查询MTU + + public abstract void setMTU(int mtu) throws IOException;//设置MTU + + public abstract void setDNSAddress(List address) throws IOException ;//设置DNS服务器地址 + + public abstract List getDNSAddress()throws SocketException; + +} diff --git a/src/org/kne/cloud/network/tun/TestTUN.java b/src/org/kne/cloud/network/tun/TestTUN.java new file mode 100644 index 0000000..c5567ac --- /dev/null +++ b/src/org/kne/cloud/network/tun/TestTUN.java @@ -0,0 +1,106 @@ +package org.kne.cloud.network.tun; + +import java.io.IOException; +import java.net.InetAddress; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + + +public class TestTUN { + public static void main(String[] args) throws IOException, InterruptedException { + TUNNetworkDevice tun = TUNNetworkDevice.createDevice("klalb1", "KLALB Decentralized SRv6 Network"); + InetAddressRow row4=new InetAddressRow(InetAddress.getByName("2486::1111"),120); + InetAddressRow row6=new InetAddressRow(InetAddress.getByName("3.3.3.3"), 32); + tun.setIPAddress(List.of(row4,row6)); + List alist=new ArrayList<>(); + alist.add(InetAddress.getByName("2486:1::8888")); + alist.add(InetAddress.getByName("8.8.8.8")); + tun.setDNSAddress(alist ); + System.out.println(tun.getDNSAddress()); + //System.out.println(tun.getMTU()); + tun.setMTU(9000); + TUNChannel channel=tun.openSession(); + new Thread(() -> { + try { + Thread.sleep(1000000); + tun.close(); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + }).start(); + new Thread(() -> { + try { + ByteBuffer bb = ByteBuffer.allocate(9000); + while (true) { + long t2 = 0,t3=0; + long t=System.nanoTime(); + channel.read(bb); + long t1=System.nanoTime(); + + bb.flip(); + /*try { + long tc=System.nanoTime(); + IpV6Packet ipp=IpV6Packet.newPacket(bb.array(), 0, bb.limit()); + if(ipp.getHeader().getVersion().equals(IpVersion.IPV6)) { + Packet pl=ipp.getPayload(); + if(pl instanceof IcmpV6CommonPacket) { + Packet req=pl.getPayload(); + if(req instanceof IcmpV6EchoRequestPacket) { + //System.out.println("recv:"); + //System.out.println(ipp+"\n"); + IcmpV6EchoReplyPacket.Builder ibdr=new IcmpV6EchoReplyPacket.Builder(); + ibdr.sequenceNumber(((IcmpV6EchoRequestPacket)req).getHeader().getSequenceNumber()); + ibdr.identifier(((IcmpV6EchoRequestPacket)req).getHeader().getIdentifier()); + ibdr.payloadBuilder(req.getPayload().getBuilder()); + IcmpV6CommonPacket.Builder icbd=new IcmpV6CommonPacket.Builder(); + icbd.type(IcmpV6Type.ECHO_REPLY); + icbd.code(IcmpV6Code.NO_CODE); + icbd.srcAddr(ipp.getHeader().getDstAddr()); + icbd.dstAddr(ipp.getHeader().getSrcAddr()); + icbd.correctChecksumAtBuild(true); + icbd.payloadBuilder(ibdr); + IpV6Packet.Builder ipv=new IpV6Packet.Builder(); + ipv.srcAddr(ipp.getHeader().getDstAddr()); + ipv.dstAddr(ipp.getHeader().getSrcAddr()); + ipv.trafficClass(ipp.getHeader().getTrafficClass()); + ipv.payloadBuilder(icbd); + ipv.version(IpVersion.IPV6); + ipv.flowLabel(IpV6SimpleFlowLabel.newInstance(0)); + ipv.nextHeader(IpNumber.ICMPV6); + ipv.correctLengthAtBuild(true); + ipv.hopLimit((byte) 20); + //System.out.println("send:"); + Packet resp=ipv.build(); + //System.out.println(resp+"\n"); + byte[]b=resp.getRawData(); + ByteBuffer bbe=ByteBuffer.wrap(b); + t2=System.nanoTime(); + channel.write(bbe); + t3=System.nanoTime(); + System.out.println("writetime:"+(t3-t2)); + System.out.println("decodetime:"+(t2-tc)); + } + } + //IcmpV6EchoReplyPacket.Builder ib=new IcmpV6EchoReplyPacket.Builder(); + //ib.sequenceNumber(0) + } + + + } catch (IllegalRawDataException e) { + e.printStackTrace(); + }*/ + + System.out.println("readtime:"+(t1-t)); + bb.clear(); + } + + } catch (IOException e) { + // TODO 自动生成的 catch 块 + e.printStackTrace(); + } + }).start(); + } +} diff --git a/src/org/kne/cloud/network/tun/WindowsAPIException.java b/src/org/kne/cloud/network/tun/WindowsAPIException.java new file mode 100644 index 0000000..510cc63 --- /dev/null +++ b/src/org/kne/cloud/network/tun/WindowsAPIException.java @@ -0,0 +1,26 @@ +package org.kne.cloud.network.tun; + /** + * Windows API 异常 + */ + public class WindowsAPIException extends RuntimeException { + /** + * + */ + private static final long serialVersionUID = -8395315684612977860L; + private final int errorCode; + + public WindowsAPIException(String message, int errorCode) { + super(message + " (Error code: " + errorCode + ")"); + this.errorCode = errorCode; + } + + public WindowsAPIException(String message) { + super(message); + this.errorCode = 0; + } + + public int getErrorCode() { + return errorCode; + } + } + \ No newline at end of file diff --git a/src/org/kne/cloud/network/tun/WindowsDNSManager.java b/src/org/kne/cloud/network/tun/WindowsDNSManager.java new file mode 100644 index 0000000..37dfb41 --- /dev/null +++ b/src/org/kne/cloud/network/tun/WindowsDNSManager.java @@ -0,0 +1,492 @@ +package org.kne.cloud.network.tun; + +import java.lang.foreign.*; +import java.lang.invoke.MethodHandle; +import java.nio.charset.Charset; +import java.util.*; + +import org.kne.cloud.network.tun.WindowsInterfaceConverter.GUID; + +/** + * Windows DNS 设置管理类 + * 支持获取、设置和修改网络接口的 DNS 配置 + */ +public class WindowsDNSManager { + + // 内存布局 + public static final AddressLayout POINTER = ValueLayout.ADDRESS; + public static final ValueLayout.OfInt DWORD = ValueLayout.JAVA_INT; + public static final ValueLayout.OfLong DWORD64 = ValueLayout.JAVA_LONG; + public static final ValueLayout.OfByte BYTE = ValueLayout.JAVA_BYTE; + public static final ValueLayout.OfShort WORD = ValueLayout.JAVA_SHORT; + + // DNS 设置常量 + public static final int DNS_SETTINGS_VERSION1 = 0x0001; + public static final int DNS_INTERFACE_SETTINGS_VERSION1 = 0x0001; + public static final int DNS_INTERFACE_SETTINGS_VERSION2 = 0x0002; + + // DNS 设置标志 + public static final long DNS_SETTING_IPV6 = 0x0001L; + public static final long DNS_SETTING_NAMESERVER = 0x0002L; + public static final long DNS_SETTING_SEARCHLIST = 0x0004L; + public static final long DNS_SETTING_REGISTRATION_ENABLED = 0x0008L; + public static final long DNS_SETTING_REGISTER_ADAPTER_NAME = 0x0010L; + public static final long DNS_SETTING_DOMAIN = 0x0020L; + public static final long DNS_SETTING_HOSTNAME = 0x0040L; + public static final long DNS_SETTINGS_ENABLE_LLMNR = 0x0080L; + public static final long DNS_SETTINGS_QUERY_ADAPTER_NAME = 0x0100L; + public static final long DNS_SETTING_PROFILE_NAMESERVER = 0x0200L; + public static final long DNS_SETTING_DISABLE_UNCONSTRAINED_QUERIES = 0x0400L; + public static final long DNS_SETTING_SUPPLEMENTAL_SEARCH_LIST = 0x0800L; + + // DNS_SETTINGS 结构体布局 (通用 DNS 设置) + public static final GroupLayout DNS_SETTINGS_LAYOUT = MemoryLayout.structLayout( + DWORD.withName("Version"), // ULONG Version + MemoryLayout.paddingLayout(4), + DWORD64.withName("Flags"), // ULONG64 Flags + POINTER.withName("Hostname"), // PWSTR Hostname + POINTER.withName("Domain"), // PWSTR Domain + POINTER.withName("SearchList") // PWSTR SearchList + ); + + // DNS_INTERFACE_SETTINGS 结构体布局 (接口特定 DNS 设置 V1) + public static final GroupLayout DNS_INTERFACE_SETTINGS_LAYOUT = MemoryLayout.structLayout( + DWORD.withName("Version"), // ULONG Version + MemoryLayout.paddingLayout(4), + DWORD64.withName("Flags"), // ULONG64 Flags + POINTER.withName("Domain"), // PWSTR Domain + POINTER.withName("NameServer"), // PWSTR NameServer + POINTER.withName("SearchList"), // PWSTR SearchList + DWORD.withName("RegistrationEnabled"), // ULONG RegistrationEnabled + DWORD.withName("RegisterAdapterName"), // ULONG RegisterAdapterName + DWORD.withName("EnableLLMNR"), // ULONG EnableLLMNR + DWORD.withName("QueryAdapterName"), // ULONG QueryAdapterName + POINTER.withName("ProfileNameServer") // PWSTR ProfileNameServer + ); + + // DNS_INTERFACE_SETTINGS_EX 结构体布局 (扩展版本) + public static final GroupLayout DNS_INTERFACE_SETTINGS_EX_LAYOUT = MemoryLayout.structLayout( + DNS_INTERFACE_SETTINGS_LAYOUT.withName("SettingsV1"), + DWORD.withName("DisableUnconstrainedQueries"), // ULONG DisableUnconstrainedQueries + MemoryLayout.paddingLayout(4), + POINTER.withName("SupplementalSearchList") // PWSTR SupplementalSearchList + ); + + private static final SymbolLookup LOOKUP; + private static final Linker LINKER = Linker.nativeLinker(); + + // 方法句柄 + private static final MethodHandle GET_INTERFACE_DNS_SETTINGS; + private static final MethodHandle FREE_INTERFACE_DNS_SETTINGS; + private static final MethodHandle SET_INTERFACE_DNS_SETTINGS; + private static final MethodHandle FREE_DNS_SETTINGS; + private static final MethodHandle GET_DNS_SETTINGS; + + static { + try { + // 加载 dnsapi.dll + System.loadLibrary("dnsapi"); + LOOKUP = SymbolLookup.loaderLookup(); + + // GetInterfaceDnsSettings 函数 + // DWORD GetInterfaceDnsSettings(GUID *Interface, DNS_INTERFACE_SETTINGS *Settings); + FunctionDescriptor getInterfaceDnsSettingsDesc = FunctionDescriptor.of( + DWORD, // 返回值: DWORD + POINTER, // Interface: GUID* + POINTER // Settings: DNS_INTERFACE_SETTINGS* + ); + + // SetInterfaceDnsSettings 函数 + // DWORD SetInterfaceDnsSettings(GUID *Interface, DNS_INTERFACE_SETTINGS *Settings); + FunctionDescriptor setInterfaceDnsSettingsDesc = FunctionDescriptor.of( + DWORD, // 返回值: DWORD + POINTER, // Interface: GUID* + POINTER // Settings: DNS_INTERFACE_SETTINGS* + ); + + FunctionDescriptor freeInterfaceDnsSettingsDesc = FunctionDescriptor.ofVoid( + POINTER + ); + + // FreeDnsSettings 函数 + // VOID FreeDnsSettings(DNS_SETTINGS *Settings); + FunctionDescriptor freeDnsSettingsDesc = FunctionDescriptor.ofVoid( + POINTER // Settings: DNS_SETTINGS* + ); + + // GetDnsSettings 函数 + // DWORD GetDnsSettings(DNS_SETTINGS *Settings); + FunctionDescriptor getDnsSettingsDesc = FunctionDescriptor.of( + DWORD, // 返回值: DWORD + POINTER // Settings: DNS_SETTINGS* + ); + + // 获取方法句柄 + GET_INTERFACE_DNS_SETTINGS = LINKER.downcallHandle( + LOOKUP.find("GetInterfaceDnsSettings").orElseThrow(), + getInterfaceDnsSettingsDesc + ); + + SET_INTERFACE_DNS_SETTINGS = LINKER.downcallHandle( + LOOKUP.find("SetInterfaceDnsSettings").orElseThrow(), + setInterfaceDnsSettingsDesc + ); + + FREE_DNS_SETTINGS = LINKER.downcallHandle( + LOOKUP.find("FreeDnsSettings").orElseThrow(), + freeInterfaceDnsSettingsDesc + ); + + FREE_INTERFACE_DNS_SETTINGS = LINKER.downcallHandle( + LOOKUP.find("FreeInterfaceDnsSettings").orElseThrow(), + freeDnsSettingsDesc + ); + + GET_DNS_SETTINGS = LINKER.downcallHandle( + LOOKUP.find("GetDnsSettings").orElseThrow(), + getDnsSettingsDesc + ); + + } catch (Throwable e) { + throw new RuntimeException("Failed to initialize Windows DNS manager", e); + } + } + + /** + * DNS 设置信息类 (Java 对象版本) + */ + public static class DnsInterfaceSettings { + private int version; + private long flags; + private String domain; + private List nameServers; + private List searchList; + private boolean registrationEnabled; + private boolean registerAdapterName; + private boolean enableLLMNR; + private boolean queryAdapterName; + private String profileNameServer; + private boolean disableUnconstrainedQueries; + private List supplementalSearchList; + + public DnsInterfaceSettings() { + this.version = DNS_INTERFACE_SETTINGS_VERSION1; + this.flags = 0; + } + + // Getters and Setters + public int getVersion() { return version; } + public void setVersion(int version) { this.version = version; } + + public long getFlags() { return flags; } + public void setFlags(long flags) { this.flags = flags; } + + public String getDomain() { return domain; } + public void setDomain(String domain) { this.domain = domain; } + + public List getNameServers() { return nameServers; } + public void setNameServers(List nameServers) { this.nameServers = nameServers; } + + public List getSearchList() { return searchList; } + public void setSearchList(List searchList) { this.searchList = searchList; } + + public boolean isRegistrationEnabled() { return registrationEnabled; } + public void setRegistrationEnabled(boolean registrationEnabled) { + this.registrationEnabled = registrationEnabled; + } + + public boolean isRegisterAdapterName() { return registerAdapterName; } + public void setRegisterAdapterName(boolean registerAdapterName) { + this.registerAdapterName = registerAdapterName; + } + + public boolean isEnableLLMNR() { return enableLLMNR; } + public void setEnableLLMNR(boolean enableLLMNR) { this.enableLLMNR = enableLLMNR; } + + public boolean isQueryAdapterName() { return queryAdapterName; } + public void setQueryAdapterName(boolean queryAdapterName) { + this.queryAdapterName = queryAdapterName; + } + + public String getProfileNameServer() { return profileNameServer; } + public void setProfileNameServer(String profileNameServer) { + this.profileNameServer = profileNameServer; + } + + public boolean isDisableUnconstrainedQueries() { return disableUnconstrainedQueries; } + public void setDisableUnconstrainedQueries(boolean disableUnconstrainedQueries) { + this.disableUnconstrainedQueries = disableUnconstrainedQueries; + } + + public List getSupplementalSearchList() { return supplementalSearchList; } + public void setSupplementalSearchList(List supplementalSearchList) { + this.supplementalSearchList = supplementalSearchList; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("DNS Interface Settings:\n"); + sb.append(" Version: ").append(version).append("\n"); + sb.append(" Flags: 0x").append(Long.toHexString(flags)).append("\n"); + sb.append(" Domain: ").append(domain != null ? domain : "(null)").append("\n"); + sb.append(" Name Servers: ").append(nameServers).append("\n"); + sb.append(" Search List: ").append(searchList).append("\n"); + sb.append(" Registration Enabled: ").append(registrationEnabled).append("\n"); + sb.append(" Register Adapter Name: ").append(registerAdapterName).append("\n"); + sb.append(" Enable LLMNR: ").append(enableLLMNR).append("\n"); + sb.append(" Query Adapter Name: ").append(queryAdapterName).append("\n"); + sb.append(" Profile Name Server: ").append(profileNameServer).append("\n"); + sb.append(" Disable Unconstrained Queries: ").append(disableUnconstrainedQueries).append("\n"); + sb.append(" Supplemental Search List: ").append(supplementalSearchList); + return sb.toString(); + } + } + + /** + * 从网卡获取 DNS 设置 + * @param interfaceGuid 网卡的 GUID + * @return DNS 设置对象 + */ + public static DnsInterfaceSettings getInterfaceDnsSettings(GUID interfaceGuid,DnsInterfaceSettings oldSettings) { + try (Arena arena = Arena.ofConfined()) { + // 创建 GUID 内存段 + MemorySegment guidSegment = interfaceGuid.toMemorySegment( arena); + + // 分配 DNS_INTERFACE_SETTINGS 结构体 (版本1) + MemorySegment dnsSettings = createDnsInterfaceSettingsMemory(oldSettings, arena); + + // 设置版本 + dnsSettings.set(DWORD, 0, DNS_INTERFACE_SETTINGS_VERSION1); + + // 调用 GetInterfaceDnsSettings + int result = (int) GET_INTERFACE_DNS_SETTINGS.invokeExact( + guidSegment, + dnsSettings + ); + + if (result != 0) { // NO_ERROR = 0 + throw new WindowsAPIException("GetInterfaceDnsSettings failed", result); + } + DnsInterfaceSettings resultv=parseDnsInterfaceSettings(dnsSettings, arena); + FREE_INTERFACE_DNS_SETTINGS.invokeExact(dnsSettings); + // 解析结构体为 Java 对象 + return resultv; + + } catch (Throwable e) { + throw new RuntimeException("Error getting interface DNS settings", e); + } + } + + /** + * 应用 DNS 设置到网卡 + * @param interfaceGuid 网卡的 GUID + * @param settings DNS 设置对象 + * @return 操作结果 (0 = 成功) + */ + public static void setInterfaceDnsSettings(GUID interfaceGuid, DnsInterfaceSettings settings) { + try (Arena arena = Arena.ofConfined()) { + // 创建 GUID 内存段 + MemorySegment guidSegment = interfaceGuid.toMemorySegment( arena); + + // 创建 DNS_INTERFACE_SETTINGS 结构体 + MemorySegment dnsSettings = createDnsInterfaceSettingsMemory(settings, arena); + + // 调用 SetInterfaceDnsSettings + int result= (int) SET_INTERFACE_DNS_SETTINGS.invokeExact( + guidSegment, + dnsSettings + ); + + if (result != 0) { // NO_ERROR = 0 + throw new WindowsAPIException("SetInterfaceDnsSettings failed", result); + } + } catch (Throwable e) { + throw new RuntimeException("Error setting interface DNS settings", e); + } + } + + /** + * 解析 DNS_INTERFACE_SETTINGS 结构体为 Java 对象 + */ + private static DnsInterfaceSettings parseDnsInterfaceSettings(MemorySegment dnsSettings, Arena arena) { + DnsInterfaceSettings settings = new DnsInterfaceSettings(); + + // 读取基本字段 + int version = dnsSettings.get(DWORD, 0); + settings.setVersion(version); + + long flags = dnsSettings.get(DWORD64, 8); + settings.setFlags(flags); + + // 读取字符串字段 + MemorySegment domainPtr = dnsSettings.get(POINTER, 16); + if (!domainPtr.equals(MemorySegment.NULL)) { + String domain = readWideString(domainPtr, arena); + settings.setDomain(domain); + }else { + settings.setDomain(null); + } + + // 读取 NameServer (可能是多个服务器,用空格分隔) + MemorySegment nameServerPtr = dnsSettings.get(POINTER, 24); + if (!nameServerPtr.equals(MemorySegment.NULL)) { + String nameServerStr = readWideString(nameServerPtr, arena); + if (nameServerStr != null && !nameServerStr.isEmpty()) { + String[] servers = nameServerStr.split("\\s+"); + settings.setNameServers(Arrays.asList(servers)); + } + }else { + settings.setNameServers(null); + } + + // 读取 SearchList (可能是多个域名,用空格分隔) + MemorySegment searchListPtr = dnsSettings.get(POINTER, 32); + if (!searchListPtr.equals(MemorySegment.NULL)) { + String searchListStr = readWideString(searchListPtr, arena); + if (searchListStr != null && !searchListStr.isEmpty()) { + String[] domains = searchListStr.split("\\s+"); + settings.setSearchList(Arrays.asList(domains)); + } + }else { + settings.setSearchList(null); + } + + // 读取布尔值字段 + int registrationEnabled = dnsSettings.get(DWORD, 40); + settings.setRegistrationEnabled(registrationEnabled != 0); + + int registerAdapterName = dnsSettings.get(DWORD, 44); + settings.setRegisterAdapterName(registerAdapterName != 0); + + int enableLLMNR = dnsSettings.get(DWORD, 48); + settings.setEnableLLMNR(enableLLMNR != 0); + + int queryAdapterName = dnsSettings.get(DWORD, 52); + settings.setQueryAdapterName(queryAdapterName != 0); + + // 读取 ProfileNameServer + MemorySegment profileNameServerPtr = dnsSettings.get(POINTER, 56); + if (!profileNameServerPtr.equals(MemorySegment.NULL)) { + String profileNameServer = readWideString(profileNameServerPtr, arena); + settings.setProfileNameServer(profileNameServer); + }else { + settings.setProfileNameServer(null); + } + + return settings; + } + + /** + * 创建 DNS_INTERFACE_SETTINGS 结构体的内存表示 + */ + private static MemorySegment createDnsInterfaceSettingsMemory(DnsInterfaceSettings settings, Arena arena) { + // 分配结构体内存 + MemorySegment dnsSettings = arena.allocate(DNS_INTERFACE_SETTINGS_LAYOUT); + + // 设置版本和标志 + dnsSettings.set(DWORD, 0, settings.getVersion()); + dnsSettings.set(DWORD64, 8, settings.getFlags()); + + // 设置 Domain + if (settings.getDomain() != null) { + MemorySegment domainStr = toWideString(settings.getDomain(), arena); + dnsSettings.set(POINTER, 16, domainStr); + } else { + dnsSettings.set(POINTER, 16, MemorySegment.NULL); + } + + // 设置 NameServer (多个服务器用空格分隔) + if (settings.getNameServers()!=null) { + String nameServerStr = String.join(" ", settings.getNameServers()); + MemorySegment nameServerSegment = toWideString(nameServerStr, arena); + dnsSettings.set(POINTER, 24, nameServerSegment); + } else { + dnsSettings.set(POINTER, 24, MemorySegment.NULL); + } + + // 设置 SearchList (多个域名用空格分隔) + if (settings.getSearchList()!=null) { + String searchListStr = String.join(" ", settings.getSearchList()); + MemorySegment searchListSegment = toWideString(searchListStr, arena); + dnsSettings.set(POINTER, 32, searchListSegment); + } else { + dnsSettings.set(POINTER, 32, MemorySegment.NULL); + } + + // 设置布尔值字段 + dnsSettings.set(DWORD, 40, settings.isRegistrationEnabled() ? 1 : 0); + dnsSettings.set(DWORD, 44, settings.isRegisterAdapterName() ? 1 : 0); + dnsSettings.set(DWORD, 48, settings.isEnableLLMNR() ? 1 : 0); + dnsSettings.set(DWORD, 52, settings.isQueryAdapterName() ? 1 : 0); + + // 设置 ProfileNameServer + if (settings.getProfileNameServer() != null ) { + MemorySegment profileNameServerSegment = toWideString(settings.getProfileNameServer(), arena); + dnsSettings.set(POINTER, 56, profileNameServerSegment); + } else { + dnsSettings.set(POINTER, 56, MemorySegment.NULL); + } + + return dnsSettings; + } + + + + + + + + + /** + * 将 Java String 转换为 Windows 宽字符串 (UTF-16 LE) + */ + private static MemorySegment toWideString(String str, Arena arena) { + if (str == null ) { + return MemorySegment.NULL; + } + + byte[] utf16Bytes = str.getBytes(java.nio.charset.StandardCharsets.UTF_16LE); + MemorySegment segment = arena.allocate(utf16Bytes.length + 2); + segment.copyFrom(MemorySegment.ofArray(utf16Bytes)); + + // 添加 null 终止符 + segment.set(ValueLayout.JAVA_BYTE, utf16Bytes.length, (byte) 0); + segment.set(ValueLayout.JAVA_BYTE, utf16Bytes.length + 1, (byte) 0); + + return segment; + } + + /** + * 读取 Windows 宽字符串 + */ + private static String readWideString(MemorySegment strPtr, Arena arena) { + if (strPtr.equals(MemorySegment.NULL)) { + return null; + } + strPtr= strPtr.reinterpret(0xffffffffL); + //return strPtr.getString(0,Charset.forName("UTF-16")); + try { + // 计算字符串长度 (找到 null 终止符) + long length = 0; + while (true) { + short ch = strPtr.get(ValueLayout.JAVA_SHORT, length * 2); + if (ch == 0) { + break; + } + length++; + } + + // 读取字符串数据 + byte[] bytes = new byte[(int) (length * 2)]; + MemorySegment.copy(strPtr, 0, MemorySegment.ofArray(bytes), 0, bytes.length); + + return new String(bytes, java.nio.charset.StandardCharsets.UTF_16LE); + } catch (Exception e) { + return null; + } + } + + + +} \ No newline at end of file diff --git a/src/org/kne/cloud/network/tun/WindowsIPAddressManager.java b/src/org/kne/cloud/network/tun/WindowsIPAddressManager.java new file mode 100644 index 0000000..0756bed --- /dev/null +++ b/src/org/kne/cloud/network/tun/WindowsIPAddressManager.java @@ -0,0 +1,772 @@ +package org.kne.cloud.network.tun; + +import java.lang.foreign.*; +import java.lang.invoke.MethodHandle; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.time.Instant; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Windows IP地址管理类 提供对MIB_UNICASTIPADDRESS_ROW和MIB_UNICASTIPADDRESS_TABLE的Java封装 + */ +public class WindowsIPAddressManager { + + // 内存布局定义 + public static final AddressLayout POINTER = ValueLayout.ADDRESS; + public static final ValueLayout.OfInt DWORD = ValueLayout.JAVA_INT; + public static final ValueLayout.OfLong DWORD64 = ValueLayout.JAVA_LONG; + public static final ValueLayout.OfByte BYTE = ValueLayout.JAVA_BYTE; + public static final ValueLayout.OfShort WORD = ValueLayout.JAVA_SHORT; + public static final ValueLayout.OfBoolean BOOLEAN_LAYOUT = ValueLayout.JAVA_BOOLEAN; + + // 常量定义 + public static final int NO_ERROR = 0; + public static final int ERROR_NOT_FOUND = 1168; + public static final int ERROR_OBJECT_ALREADY_EXISTS = 5010; + public static final int NET_IFINDEX_UNSPECIFIED = 0; + public static final long NET_IFLUID_UNSPECIFIED = 0; + + // SCOPE_ID 结构 (ULONG) + public static final GroupLayout SCOPE_ID_LAYOUT = MemoryLayout.structLayout(DWORD.withName("Value")); + + // LARGE_INTEGER 结构 (64位有符号整数) + public static final GroupLayout LARGE_INTEGER_LAYOUT = MemoryLayout.structLayout(DWORD64.withName("QuadPart")); + + // SOCKADDR_IN (IPv4地址) + public static final GroupLayout SOCKADDR_IN_LAYOUT = MemoryLayout.structLayout(WORD.withName("sin_family"), // ADDRESS_FAMILY + // (AF_INET + // = 2) + WORD.withName("sin_port"), // USHORT + MemoryLayout.sequenceLayout(4, BYTE).withName("sin_addr"), // IN_ADDR (4 bytes) + MemoryLayout.sequenceLayout(8, BYTE).withName("sin_zero") // 填充字节 + ); + + // IN6_ADDR (IPv6地址,16字节) + public static final GroupLayout IN6_ADDR_LAYOUT = MemoryLayout + .structLayout(MemoryLayout.sequenceLayout(16, BYTE).withName("s6_bytes")); + + // SOCKADDR_IN6 (IPv6地址结构) + public static final GroupLayout SOCKADDR_IN6_LAYOUT = MemoryLayout.structLayout(WORD.withName("sin6_family"), // ADDRESS_FAMILY + // (AF_INET6 + // = + // 23) + WORD.withName("sin6_port"), // USHORT + DWORD.withName("sin6_flowinfo"), // ULONG + IN6_ADDR_LAYOUT.withName("sin6_addr"), DWORD.withName("sin6_scope_id") // ULONG + ); + + // SOCKADDR_INET 联合 (IPv4或IPv6地址) + public static final GroupLayout SOCKADDR_INET_LAYOUT = MemoryLayout.structLayout(MemoryLayout + .unionLayout(SOCKADDR_IN_LAYOUT.withName("Ipv4"), SOCKADDR_IN6_LAYOUT.withName("Ipv6")).withName("u"), + WORD.withName("si_family") // 地址族 + ); + + // NET_LUID 结构 (64位网络接口标识符) + public static final GroupLayout NET_LUID_LAYOUT = MemoryLayout.structLayout(DWORD64.withName("Value")); + + // MIB_UNICASTIPADDRESS_ROW 结构体布局 + public static final GroupLayout MIB_UNICASTIPADDRESS_ROW_LAYOUT; + + static { + // 动态构建结构体布局 + MemoryLayout[] layouts = { + // 键结构 + SOCKADDR_INET_LAYOUT.withName("Address"), MemoryLayout.paddingLayout(2), + NET_LUID_LAYOUT.withName("InterfaceLuid"), DWORD.withName("InterfaceIndex"), + + // 读写字段 + DWORD.withName("PrefixOrigin"), DWORD.withName("SuffixOrigin"), DWORD.withName("ValidLifetime"), + DWORD.withName("PreferredLifetime"), BYTE.withName("OnLinkPrefixLength"), + BOOLEAN_LAYOUT.withName("SkipAsSource"), MemoryLayout.paddingLayout(2), + + // 只读字段 + DWORD.withName("DadState"), SCOPE_ID_LAYOUT.withName("ScopeId"), + LARGE_INTEGER_LAYOUT.withName("CreationTimeStamp") }; + + MIB_UNICASTIPADDRESS_ROW_LAYOUT = MemoryLayout.structLayout(layouts); + } + + // MIB_UNICASTIPADDRESS_TABLE 结构体布局 + public static final GroupLayout MIB_UNICASTIPADDRESS_TABLE_LAYOUT = MemoryLayout + .structLayout(DWORD.withName("NumEntries"), MemoryLayout.paddingLayout(4), POINTER.withName("Table") // 指向MIB_UNICASTIPADDRESS_ROW数组的指针 + ); + + // 前缀来源枚举 + public enum PrefixOrigin { + IpPrefixOriginOther(0), IpPrefixOriginManual(1), IpPrefixOriginWellKnown(2), IpPrefixOriginDhcp(3), + IpPrefixOriginRouterAdvertisement(4), IpPrefixOriginUnchanged(1 << 4); + + private final int value; + + PrefixOrigin(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static PrefixOrigin fromValue(int value) { + return Arrays.stream(values()).filter(e -> e.value == value).findFirst().orElse(IpPrefixOriginOther); + } + } + + // 后缀来源枚举 + public enum SuffixOrigin { + IpSuffixOriginOther(0), IpSuffixOriginManual(1), IpSuffixOriginWellKnown(2), IpSuffixOriginDhcp(3), + IpSuffixOriginLinkLayerAddress(4), IpSuffixOriginRandom(5), IpSuffixOriginUnchanged(1 << 4); + + private final int value; + + SuffixOrigin(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static SuffixOrigin fromValue(int value) { + return Arrays.stream(values()).filter(e -> e.value == value).findFirst().orElse(IpSuffixOriginOther); + } + } + + // DAD状态枚举 + public enum DadState { + IpDadStateInvalid(0), IpDadStateTentative(1), IpDadStateDuplicate(2), IpDadStateDeprecated(3), + IpDadStatePreferred(4); + + private final int value; + + DadState(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static DadState fromValue(int value) { + return Arrays.stream(values()).filter(e -> e.value == value).findFirst().orElse(IpDadStateInvalid); + } + } + + // 地址族枚举 + public enum AddressFamily { + AF_UNSPEC(0), AF_INET(2), AF_INET6(23); + + private final int value; + + AddressFamily(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static AddressFamily fromValue(int value) { + return Arrays.stream(values()).filter(e -> e.value == value).findFirst().orElse(AF_UNSPEC); + } + } + + // Native库加载 + private static final SymbolLookup LOOKUP; + private static final Linker LINKER = Linker.nativeLinker(); + + static { + try { + // 加载iphlpapi.dll + System.loadLibrary("iphlpapi"); + LOOKUP = SymbolLookup.loaderLookup(); + } catch (UnsatisfiedLinkError e) { + throw new RuntimeException("Failed to load iphlpapi.dll", e); + } + } + + // Native函数句柄 + private static final MethodHandle GET_UNICAST_IP_ADDRESS_TABLE; + private static final MethodHandle INITIALIZE_UNICAST_IP_ADDRESS_ENTRY; + private static final MethodHandle CREATE_UNICAST_IP_ADDRESS_ENTRY; + private static final MethodHandle DELETE_UNICAST_IP_ADDRESS_ENTRY; + private static final MethodHandle GET_UNICAST_IP_ADDRESS_ENTRY; + private static final MethodHandle FREE_MIB_TABLE; + + static { + try { + // 定义函数描述符 + + // DWORD GetUnicastIpAddressTable( + // ADDRESS_FAMILY Family, + // PMIB_UNICASTIPADDRESS_TABLE *Table + // ); + FunctionDescriptor getUnicastIpAddressTableDesc = FunctionDescriptor.of(DWORD, // 返回值: DWORD + DWORD, // Family: ADDRESS_FAMILY + POINTER // Table: PMIB_UNICASTIPADDRESS_TABLE* + ); + + // VOID InitializeUnicastIpAddressEntry( + // PMIB_UNICASTIPADDRESS_ROW Row + // ); + FunctionDescriptor initializeUnicastIpAddressEntryDesc = FunctionDescriptor.ofVoid(POINTER // Row: + // PMIB_UNICASTIPADDRESS_ROW + ); + + // DWORD CreateUnicastIpAddressEntry( + // const MIB_UNICASTIPADDRESS_ROW *Row + // ); + FunctionDescriptor createUnicastIpAddressEntryDesc = FunctionDescriptor.of(DWORD, // 返回值: DWORD + POINTER // Row: const MIB_UNICASTIPADDRESS_ROW* + ); + + // DWORD DeleteUnicastIpAddressEntry( + // const MIB_UNICASTIPADDRESS_ROW *Row + // ); + FunctionDescriptor deleteUnicastIpAddressEntryDesc = FunctionDescriptor.of(DWORD, // 返回值: DWORD + POINTER // Row: const MIB_UNICASTIPADDRESS_ROW* + ); + + // DWORD GetUnicastIpAddressEntry( + // PMIB_UNICASTIPADDRESS_ROW Row + // ); + FunctionDescriptor getUnicastIpAddressEntryDesc = FunctionDescriptor.of(DWORD, // 返回值: DWORD + POINTER // Row: PMIB_UNICASTIPADDRESS_ROW + ); + + // VOID FreeMibTable( + // PVOID Memory + // ); + FunctionDescriptor freeMibTableDesc = FunctionDescriptor.ofVoid(POINTER // Memory: PVOID + ); + + // 获取函数句柄 + GET_UNICAST_IP_ADDRESS_TABLE = LINKER.downcallHandle(LOOKUP.find("GetUnicastIpAddressTable").orElseThrow(), + getUnicastIpAddressTableDesc); + + INITIALIZE_UNICAST_IP_ADDRESS_ENTRY = LINKER.downcallHandle( + LOOKUP.find("InitializeUnicastIpAddressEntry").orElseThrow(), initializeUnicastIpAddressEntryDesc); + + CREATE_UNICAST_IP_ADDRESS_ENTRY = LINKER.downcallHandle( + LOOKUP.find("CreateUnicastIpAddressEntry").orElseThrow(), createUnicastIpAddressEntryDesc); + + DELETE_UNICAST_IP_ADDRESS_ENTRY = LINKER.downcallHandle( + LOOKUP.find("DeleteUnicastIpAddressEntry").orElseThrow(), deleteUnicastIpAddressEntryDesc); + + GET_UNICAST_IP_ADDRESS_ENTRY = LINKER.downcallHandle(LOOKUP.find("GetUnicastIpAddressEntry").orElseThrow(), + getUnicastIpAddressEntryDesc); + + FREE_MIB_TABLE = LINKER.downcallHandle(LOOKUP.find("FreeMibTable").orElseThrow(), freeMibTableDesc); + + } catch (Throwable e) { + throw new RuntimeException("Failed to initialize Windows IP address manager", e); + } + } + + /** + * 单播IP地址行(Java表示) + */ + public static class UnicastIPAddressRow { + private InetAddress address; + private long interfaceLuid; + private int interfaceIndex; + private PrefixOrigin prefixOrigin; + private SuffixOrigin suffixOrigin; + private long validLifetime; + private long preferredLifetime; + private int onLinkPrefixLength; + private boolean skipAsSource; + private DadState dadState; + private long scopeId; + private Instant creationTimestamp; + + // 构造方法 + public UnicastIPAddressRow() { + } + + public UnicastIPAddressRow(InetAddress address, long interfaceLuid) { + this.address = address; + this.interfaceLuid = interfaceLuid; + this.interfaceIndex = NET_IFINDEX_UNSPECIFIED; + this.prefixOrigin = PrefixOrigin.IpPrefixOriginManual; + this.suffixOrigin = SuffixOrigin.IpSuffixOriginManual; + this.validLifetime = 0xFFFFFFFFL; // 无限 + this.preferredLifetime = 0xFFFFFFFFL; // 无限 + this.onLinkPrefixLength = (address instanceof Inet4Address) ? 32 : 128; + this.skipAsSource = false; + this.dadState = DadState.IpDadStatePreferred; + this.scopeId = 0; + this.creationTimestamp = Instant.now(); + } + + // 从Native内存创建 + public static UnicastIPAddressRow fromMemorySegment(MemorySegment segment) { + UnicastIPAddressRow row = new UnicastIPAddressRow(); + + try (Arena arena = Arena.ofConfined()) { + // 读取地址 + int family = segment.get(WORD, 0); // SOCKADDR_INET中的si_family字段 + AddressFamily addressFamily = AddressFamily.fromValue(family); + + if (addressFamily == AddressFamily.AF_INET) { + // IPv4地址 + byte[] ipv4Bytes = new byte[4]; + for (int i = 0; i < 4; i++) { + ipv4Bytes[i] = segment.get(BYTE, 4 + i); // sin_addr偏移 + } + row.address = Inet4Address.getByAddress(ipv4Bytes); + row.onLinkPrefixLength = 32; + } else if (addressFamily == AddressFamily.AF_INET6) { + // IPv6地址 + byte[] ipv6Bytes = new byte[16]; + MemoryLayout ipv6Layout = SOCKADDR_IN6_LAYOUT; + long sin6AddrOffset = 8; // sin6_addr在SOCKADDR_IN6中的偏移 + + for (int i = 0; i < 16; i++) { + ipv6Bytes[i] = segment.get(BYTE, sin6AddrOffset + i); + } + row.address = Inet6Address.getByAddress(ipv6Bytes); + row.onLinkPrefixLength = 128; + } + + // 读取接口LUID + long luidOffset = SOCKADDR_INET_LAYOUT.byteSize() + 2; + row.interfaceLuid = segment.get(DWORD64, luidOffset); + + // 读取接口索引 + long indexOffset = luidOffset + NET_LUID_LAYOUT.byteSize(); + row.interfaceIndex = segment.get(DWORD, indexOffset); + + // 读取其他字段 + long prefixOriginOffset = indexOffset + DWORD.byteSize(); + row.prefixOrigin = PrefixOrigin.fromValue(segment.get(DWORD, prefixOriginOffset)); + + long suffixOriginOffset = prefixOriginOffset + DWORD.byteSize(); + row.suffixOrigin = SuffixOrigin.fromValue(segment.get(DWORD, suffixOriginOffset)); + + long validLifetimeOffset = suffixOriginOffset + DWORD.byteSize(); + row.validLifetime = segment.get(DWORD, validLifetimeOffset) & 0xFFFFFFFFL; + + long preferredLifetimeOffset = validLifetimeOffset + DWORD.byteSize(); + row.preferredLifetime = segment.get(DWORD, preferredLifetimeOffset) & 0xFFFFFFFFL; + + long onLinkPrefixLengthOffset = preferredLifetimeOffset + DWORD.byteSize(); + row.onLinkPrefixLength = segment.get(BYTE, onLinkPrefixLengthOffset) & 0xFF; + + long skipAsSourceOffset = onLinkPrefixLengthOffset + BYTE.byteSize(); + row.skipAsSource = segment.get(BOOLEAN_LAYOUT, skipAsSourceOffset); + + long dadStateOffset = skipAsSourceOffset + BOOLEAN_LAYOUT.byteSize() + 2; + row.dadState = DadState.fromValue(segment.get(DWORD, dadStateOffset)); + + long scopeIdOffset = dadStateOffset + DWORD.byteSize(); + row.scopeId = segment.get(DWORD, scopeIdOffset) & 0xFFFFFFFFL; + + // 创建时间戳(Windows FILETIME格式,100纳秒间隔自1601-01-01 UTC) + long timestampOffset = scopeIdOffset + SCOPE_ID_LAYOUT.byteSize(); + long filetime = segment.get(DWORD64, timestampOffset); + if (filetime != 0) { + // 转换为Java Instant (从1601-01-01 UTC到1970-01-01 UTC的间隔) + long windowsEpoch = 116444736000000000L; // 1601到1970的100纳秒间隔数 + long unixNanos = (filetime - windowsEpoch) * 100; + row.creationTimestamp = Instant.ofEpochSecond(unixNanos / 1_000_000_000, unixNanos % 1_000_000_000); + } + } catch (UnknownHostException e) { + throw new RuntimeException("Failed to parse IP address", e); + } + + return row; + } + + // 转换为Native内存 + public MemorySegment toMemorySegment(Arena arena) { + MemorySegment segment = arena.allocate(MIB_UNICASTIPADDRESS_ROW_LAYOUT); + + // 设置地址 + byte[] addressBytes = address.getAddress(); + if (address instanceof Inet4Address) { + // IPv4 + segment.set(WORD, 0, (short) AddressFamily.AF_INET.getValue()); + // 设置IPv4地址 + segment.set(WORD, 2, (short) 0); // sin_port + for (int i = 0; i < 4; i++) { + segment.set(BYTE, 4 + i, addressBytes[i]); + } + // sin_zero填充 + for (int i = 0; i < 8; i++) { + segment.set(BYTE, 8 + i, (byte) 0); + } + } else if (address instanceof Inet6Address) { + // IPv6 + segment.set(WORD, 0, (short) AddressFamily.AF_INET6.getValue()); + // 设置IPv6地址 + segment.set(WORD, 2, (short) 0); // sin6_port + segment.set(DWORD, 4, 0); // sin6_flowinfo + // sin6_addr + for (int i = 0; i < 16; i++) { + segment.set(BYTE, 8 + i, addressBytes[i]); + } + segment.set(DWORD, 24, 0); // sin6_scope_id + } + + // 设置接口LUID + long luidOffset = SOCKADDR_INET_LAYOUT.byteSize()+2; + segment.set(DWORD64, luidOffset, interfaceLuid); + + // 设置接口索引 + long indexOffset = luidOffset + NET_LUID_LAYOUT.byteSize(); + segment.set(DWORD, indexOffset, interfaceIndex); + + // 设置其他字段 + long prefixOriginOffset = indexOffset + DWORD.byteSize(); + segment.set(DWORD, prefixOriginOffset, prefixOrigin.getValue()); + + long suffixOriginOffset = prefixOriginOffset + DWORD.byteSize(); + segment.set(DWORD, suffixOriginOffset, suffixOrigin.getValue()); + + long validLifetimeOffset = suffixOriginOffset + DWORD.byteSize(); + segment.set(DWORD, validLifetimeOffset, (int) validLifetime); + + long preferredLifetimeOffset = validLifetimeOffset + DWORD.byteSize(); + segment.set(DWORD, preferredLifetimeOffset, (int) preferredLifetime); + + long onLinkPrefixLengthOffset = preferredLifetimeOffset + DWORD.byteSize(); + segment.set(BYTE, onLinkPrefixLengthOffset, (byte) onLinkPrefixLength); + + long skipAsSourceOffset = onLinkPrefixLengthOffset + BYTE.byteSize(); + segment.set(BOOLEAN_LAYOUT, skipAsSourceOffset, skipAsSource); + + long dadStateOffset = skipAsSourceOffset + BOOLEAN_LAYOUT.byteSize()+2; + segment.set(DWORD, dadStateOffset, dadState.getValue()); + + long scopeIdOffset = dadStateOffset + DWORD.byteSize(); + segment.set(DWORD, scopeIdOffset, (int) scopeId); + + // 设置创建时间戳 + long timestampOffset = scopeIdOffset + SCOPE_ID_LAYOUT.byteSize(); + if (creationTimestamp != null) { + long windowsEpoch = 116444736000000000L; + long unixNanos = creationTimestamp.getEpochSecond() * 1_000_000_000L + creationTimestamp.getNano(); + long filetime = windowsEpoch + (unixNanos / 100); + segment.set(DWORD64, timestampOffset, filetime); + } else { + segment.set(DWORD64, timestampOffset, 0L); + } + + return segment; + } + + // Getters和Setters + public InetAddress getAddress() { + return address; + } + + public void setAddress(InetAddress address) { + this.address = address; + } + + public long getInterfaceLuid() { + return interfaceLuid; + } + + public void setInterfaceLuid(long interfaceLuid) { + this.interfaceLuid = interfaceLuid; + } + + public int getInterfaceIndex() { + return interfaceIndex; + } + + public void setInterfaceIndex(int interfaceIndex) { + this.interfaceIndex = interfaceIndex; + } + + public PrefixOrigin getPrefixOrigin() { + return prefixOrigin; + } + + public void setPrefixOrigin(PrefixOrigin prefixOrigin) { + this.prefixOrigin = prefixOrigin; + } + + public SuffixOrigin getSuffixOrigin() { + return suffixOrigin; + } + + public void setSuffixOrigin(SuffixOrigin suffixOrigin) { + this.suffixOrigin = suffixOrigin; + } + + public long getValidLifetime() { + return validLifetime; + } + + public void setValidLifetime(long validLifetime) { + this.validLifetime = validLifetime; + } + + public long getPreferredLifetime() { + return preferredLifetime; + } + + public void setPreferredLifetime(long preferredLifetime) { + this.preferredLifetime = preferredLifetime; + } + + public int getOnLinkPrefixLength() { + return onLinkPrefixLength; + } + + public void setOnLinkPrefixLength(int onLinkPrefixLength) { + this.onLinkPrefixLength = onLinkPrefixLength; + } + + public boolean isSkipAsSource() { + return skipAsSource; + } + + public void setSkipAsSource(boolean skipAsSource) { + this.skipAsSource = skipAsSource; + } + + public DadState getDadState() { + return dadState; + } + + public void setDadState(DadState dadState) { + this.dadState = dadState; + } + + public long getScopeId() { + return scopeId; + } + + public void setScopeId(long scopeId) { + this.scopeId = scopeId; + } + + public Instant getCreationTimestamp() { + return creationTimestamp; + } + + public void setCreationTimestamp(Instant creationTimestamp) { + this.creationTimestamp = creationTimestamp; + } + + @Override + public String toString() { + return String.format("UnicastIPAddressRow[address=%s, luid=0x%016X, index=%d, prefix=%s, suffix=%s]", + address.getHostAddress(), interfaceLuid, interfaceIndex, prefixOrigin, suffixOrigin); + } + } + + /** + * 获取单播IP地址表 + */ + public static List getUnicastIpAddressTable(AddressFamily family) throws WindowsAPIException { + try (Arena arena = Arena.ofConfined()) { + // 分配指针来接收表 + MemorySegment tablePtrPtr = arena.allocate(POINTER); + + // 调用GetUnicastIpAddressTable + int result = (int) GET_UNICAST_IP_ADDRESS_TABLE.invokeExact(family.getValue(), tablePtrPtr); + + if (result != NO_ERROR) { + throw new WindowsAPIException("GetUnicastIpAddressTable failed", result); + } + + // 获取表指针 + MemorySegment tablePtr = tablePtrPtr.get(POINTER, 0); + if (tablePtr.equals(MemorySegment.NULL)) { + return Collections.emptyList(); + } + + // 读取表头 + tablePtr = tablePtr.reinterpret(8); + int numEntries = tablePtr.get(DWORD, 0); + MemorySegment rowsPtr = tablePtr.reinterpret(8 + numEntries * MIB_UNICASTIPADDRESS_ROW_LAYOUT.byteSize()) + .asSlice(8); + + List rows = new ArrayList<>(); + + // 遍历所有行 + for (int i = 0; i < numEntries; i++) { + MemorySegment rowPtr = rowsPtr.asSlice(i * MIB_UNICASTIPADDRESS_ROW_LAYOUT.byteSize()) + .reinterpret(MIB_UNICASTIPADDRESS_ROW_LAYOUT.byteSize()); + UnicastIPAddressRow row = UnicastIPAddressRow.fromMemorySegment(rowPtr); + rows.add(row); + } + FREE_MIB_TABLE.invokeExact(tablePtr); + + return rows; + } catch (Throwable e) { + throw new RuntimeException("Error getting unicast IP address table", e); + } + } + + /** + * 初始化单播IP地址行 + */ + public static void initializeUnicastIpAddressEntry(UnicastIPAddressRow row) throws WindowsAPIException { + try (Arena arena = Arena.ofConfined()) { + MemorySegment rowSegment = row.toMemorySegment(arena); + + int result = (int) INITIALIZE_UNICAST_IP_ADDRESS_ENTRY.invokeExact(rowSegment); + + if (result != NO_ERROR) { + throw new WindowsAPIException("InitializeUnicastIpAddressEntry failed", result); + } + } catch (Throwable e) { + throw new RuntimeException("Error initializing unicast IP address entry", e); + } + } + + /** + * 创建单播IP地址条目 + */ + public static void createUnicastIpAddressEntry(UnicastIPAddressRow row) throws WindowsAPIException { + try (Arena arena = Arena.ofConfined()) { + MemorySegment rowSegment = row.toMemorySegment(arena); + + int result = (int) CREATE_UNICAST_IP_ADDRESS_ENTRY.invokeExact(rowSegment); + + if (result != NO_ERROR) { + throw new WindowsAPIException("CreateUnicastIpAddressEntry failed", result); + } + } catch (Throwable e) { + throw new RuntimeException("Error creating unicast IP address entry", e); + } + } + + /** + * 删除单播IP地址条目 + */ + public static void deleteUnicastIpAddressEntry(UnicastIPAddressRow row) throws WindowsAPIException { + try (Arena arena = Arena.ofConfined()) { + MemorySegment rowSegment = row.toMemorySegment(arena); + + int result = (int) DELETE_UNICAST_IP_ADDRESS_ENTRY.invokeExact(rowSegment); + + if (result != NO_ERROR && result != ERROR_NOT_FOUND) { + throw new WindowsAPIException("DeleteUnicastIpAddressEntry failed", result); + } + } catch (Throwable e) { + throw new RuntimeException("Error deleting unicast IP address entry", e); + } + } + + /** + * 获取单播IP地址条目 + */ + public static UnicastIPAddressRow getUnicastIpAddressEntry(UnicastIPAddressRow row) throws WindowsAPIException { + try (Arena arena = Arena.ofConfined()) { + MemorySegment rowSegment = row.toMemorySegment(arena); + + int result = (int) GET_UNICAST_IP_ADDRESS_ENTRY.invokeExact(rowSegment); + + if (result != NO_ERROR) { + throw new WindowsAPIException("GetUnicastIpAddressEntry failed", result); + } + + // 从内存段读取更新后的行 + return UnicastIPAddressRow.fromMemorySegment(rowSegment); + } catch (Throwable e) { + throw new RuntimeException("Error getting unicast IP address entry", e); + } + } + + /** + * 为Wintun适配器添加IP地址 + */ + public static void addIpAddressToAdapter(long adapterLuid, InetAddress address, int prefix) + throws WindowsAPIException, UnknownHostException { + + // 创建IP地址行 + UnicastIPAddressRow row = new UnicastIPAddressRow(address, adapterLuid); + + // 设置前缀长度 + row.setOnLinkPrefixLength(prefix); + // 设置其他参数 + row.setPrefixOrigin(PrefixOrigin.IpPrefixOriginManual); + row.setSuffixOrigin(SuffixOrigin.IpSuffixOriginManual); + row.setValidLifetime(0xFFFFFFFFL); // 无限 + row.setPreferredLifetime(0xFFFFFFFFL); // 无限 + row.setSkipAsSource(false); + + // 创建IP地址条目 + createUnicastIpAddressEntry(row); + } + + /** + * 获取指定接口的所有IP地址 + */ + public static List getIpAddressesByInterface(long interfaceLuid) throws WindowsAPIException { + List allAddresses = new ArrayList<>(); + + // 获取IPv4地址 + try { + List ipv4Addresses = getUnicastIpAddressTable(AddressFamily.AF_INET); + allAddresses.addAll(ipv4Addresses.stream().filter(row -> row.getInterfaceLuid() == interfaceLuid) + .collect(Collectors.toList())); + } catch (Exception e) { + // 忽略错误,继续获取IPv6地址 + } + + // 获取IPv6地址 + try { + List ipv6Addresses = getUnicastIpAddressTable(AddressFamily.AF_INET6); + allAddresses.addAll(ipv6Addresses.stream().filter(row -> row.getInterfaceLuid() == interfaceLuid) + .collect(Collectors.toList())); + } catch (Exception e) { + // 忽略错误 + } + + return allAddresses; + } + + /** + * 测试方法 + */ + public static void main(String[] args) { + try { + System.out.println("=== Windows IP Address Manager Test ==="); + + // 获取所有IPv4地址 + System.out.println("\nIPv4 Addresses:"); + List ipv4Addresses = getUnicastIpAddressTable(AddressFamily.AF_INET); + for (UnicastIPAddressRow row : ipv4Addresses) { + System.out.println(" " + row); + } + + // 获取所有IPv6地址 + System.out.println("\nIPv6 Addresses:"); + List ipv6Addresses = getUnicastIpAddressTable(AddressFamily.AF_INET6); + for (UnicastIPAddressRow row : ipv6Addresses) { + System.out.println(" " + row); + } + + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 工具方法:将IP地址字符串转换为字节数组 + */ + public static byte[] ipStringToBytes(String ipAddress) throws UnknownHostException { + return InetAddress.getByName(ipAddress).getAddress(); + } + + /** + * 工具方法:将字节数组转换为IP地址字符串 + */ + public static String bytesToIpString(byte[] bytes) throws UnknownHostException { + return InetAddress.getByAddress(bytes).getHostAddress(); + } +} \ No newline at end of file diff --git a/src/org/kne/cloud/network/tun/WindowsIPInterfaceManager.java b/src/org/kne/cloud/network/tun/WindowsIPInterfaceManager.java new file mode 100644 index 0000000..3d01fe0 --- /dev/null +++ b/src/org/kne/cloud/network/tun/WindowsIPInterfaceManager.java @@ -0,0 +1,907 @@ +package org.kne.cloud.network.tun; +import java.lang.foreign.*; +import java.lang.invoke.MethodHandle; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Arrays; + +/** + * Windows IP 接口配置管理 + * 提供 GetIpInterfaceEntry 和 SetIpInterfaceEntry 的 Java 绑定 + */ +public class WindowsIPInterfaceManager { + + // 内存布局定义 + public static final AddressLayout POINTER = ValueLayout.ADDRESS; + public static final ValueLayout.OfInt DWORD = ValueLayout.JAVA_INT; + public static final ValueLayout.OfLong DWORD64 = ValueLayout.JAVA_LONG; + public static final ValueLayout.OfByte BYTE = ValueLayout.JAVA_BYTE; + public static final ValueLayout.OfShort WORD = ValueLayout.JAVA_SHORT; + public static final ValueLayout.OfBoolean BOOLEAN_LAYOUT = ValueLayout.JAVA_BOOLEAN; + + // 常量定义 + public static final int NO_ERROR = 0; + public static final int AF_INET = 2; + public static final int AF_INET6 = 23; + public static final int SCOPE_LEVEL_COUNT = 16; // Windows定义的 ScopeLevelCount + + // NL_ROUTER_DISCOVERY_BEHAVIOR 枚举 + public enum RouterDiscoveryBehavior { + RouterDiscoveryDisabled(0), + RouterDiscoveryEnabled(1), + RouterDiscoveryDhcp(2), + RouterDiscoveryUnchanged(-1); + + private final int value; + RouterDiscoveryBehavior(int value) { this.value = value; } + public int getValue() { return value; } + public static RouterDiscoveryBehavior fromValue(int value) { + for (RouterDiscoveryBehavior b : values()) { + if (b.value == value) return b; + } + return RouterDiscoveryDisabled; + } + } + + // NL_LINK_LOCAL_ADDRESS_BEHAVIOR 枚举 + public enum LinkLocalAddressBehavior { + LinkLocalAlwaysOff(0), + LinkLocalDelayed(1), + LinkLocalAlwaysOn(2), + LinkLocalUnchanged(-1); + + private final int value; + LinkLocalAddressBehavior(int value) { this.value = value; } + public int getValue() { return value; } + public static LinkLocalAddressBehavior fromValue(int value) { + for (LinkLocalAddressBehavior b : values()) { + if (b.value == value) return b; + } + return LinkLocalAlwaysOff; + } + } + + // NL_INTERFACE_OFFLOAD_ROD 结构体 (位字段) + public static final GroupLayout NL_INTERFACE_OFFLOAD_ROD_LAYOUT = MemoryLayout.structLayout( + BYTE.withName("Flags") // 位字段: 1=NlChecksumSupported, 2=NlOptionsSupported, etc. + ).withByteAlignment(1); + + // NET_LUID 结构 (64位网络接口标识符) + public static final GroupLayout NET_LUID_LAYOUT = MemoryLayout.structLayout( + DWORD64.withName("Value") + ).withByteAlignment(8); + + // MIB_IPINTERFACE_ROW 结构体布局 (64位系统,简化版本) + // 注意:实际结构体大小可能因Windows版本而异,这里使用简化的布局 + public static final GroupLayout MIB_IPINTERFACE_ROW_LAYOUT; + + static { + // 在64位系统上,结构体按照8字节对齐 + // 这是基于Windows SDK头文件的简化布局 + MIB_IPINTERFACE_ROW_LAYOUT = MemoryLayout.structLayout( + // 键字段 + WORD.withName("Family"), // ADDRESS_FAMILY (2字节) + MemoryLayout.paddingLayout(6), // 填充到8字节对齐 + + NET_LUID_LAYOUT.withName("InterfaceLuid"), + DWORD.withName("InterfaceIndex"), + + // IP接口属性 + DWORD.withName("MaxReassemblySize"), + DWORD64.withName("InterfaceIdentifier"), + DWORD.withName("MinRouterAdvertisementInterval"), + DWORD.withName("MaxRouterAdvertisementInterval"), + BOOLEAN_LAYOUT.withName("AdvertisingEnabled"), + BOOLEAN_LAYOUT.withName("ForwardingEnabled"), + BOOLEAN_LAYOUT.withName("WeakHostSend"), + BOOLEAN_LAYOUT.withName("WeakHostReceive"), + BOOLEAN_LAYOUT.withName("UseAutomaticMetric"), + BOOLEAN_LAYOUT.withName("UseNeighborUnreachabilityDetection"), + BOOLEAN_LAYOUT.withName("ManagedAddressConfigurationSupported"), + BOOLEAN_LAYOUT.withName("OtherStatefulConfigurationSupported"), + BOOLEAN_LAYOUT.withName("AdvertiseDefaultRoute"), + MemoryLayout.paddingLayout(3), // 填充到4字节对齐 + + DWORD.withName("RouterDiscoveryBehavior"), + + DWORD.withName("DadTransmits"), + DWORD.withName("BaseReachableTime"), + DWORD.withName("RetransmitTime"), + DWORD.withName("PathMtuDiscoveryTimeout"), + + DWORD.withName("LinkLocalAddressBehavior"), + + DWORD.withName("LinkLocalAddressTimeout"), + + // ZoneIndices 数组 (16个ULONG) + MemoryLayout.sequenceLayout(SCOPE_LEVEL_COUNT, DWORD).withName("ZoneIndices"), + + DWORD.withName("SitePrefixLength"), + DWORD.withName("Metric"), + DWORD.withName("NlMtu"), + + BOOLEAN_LAYOUT.withName("Connected"), + BOOLEAN_LAYOUT.withName("SupportsWakeUpPatterns"), + BOOLEAN_LAYOUT.withName("SupportsNeighborDiscovery"), + BOOLEAN_LAYOUT.withName("SupportsRouterDiscovery"), + + DWORD.withName("ReachableTime"), + + NL_INTERFACE_OFFLOAD_ROD_LAYOUT.withName("TransmitOffload"), + NL_INTERFACE_OFFLOAD_ROD_LAYOUT.withName("ReceiveOffload"), + MemoryLayout.paddingLayout(6), // 填充到8字节对齐 + + BOOLEAN_LAYOUT.withName("DisableDefaultRoutes"), + MemoryLayout.paddingLayout(7) // 填充到8字节对齐 + ).withByteAlignment(8); + } + + // Native库加载 + private static final SymbolLookup LOOKUP; + private static final Linker LINKER = Linker.nativeLinker(); + + // 方法句柄 + private static final MethodHandle GET_IP_INTERFACE_ENTRY; + private static final MethodHandle SET_IP_INTERFACE_ENTRY; + + static { + try { + // 加载iphlpapi.dll + System.loadLibrary("iphlpapi"); + LOOKUP = SymbolLookup.loaderLookup(); + + // 定义函数描述符 + + // DWORD GetIpInterfaceEntry( + // PMIB_IPINTERFACE_ROW Row + // ); + FunctionDescriptor getIpInterfaceEntryDesc = FunctionDescriptor.of( + DWORD, // 返回值: DWORD + POINTER // Row: PMIB_IPINTERFACE_ROW + ); + + // DWORD SetIpInterfaceEntry( + // PMIB_IPINTERFACE_ROW Row + // ); + FunctionDescriptor setIpInterfaceEntryDesc = FunctionDescriptor.of( + DWORD, // 返回值: DWORD + POINTER // Row: PMIB_IPINTERFACE_ROW + ); + + // 获取函数句柄 + GET_IP_INTERFACE_ENTRY = LINKER.downcallHandle( + LOOKUP.find("GetIpInterfaceEntry").orElseThrow(), + getIpInterfaceEntryDesc + ); + + SET_IP_INTERFACE_ENTRY = LINKER.downcallHandle( + LOOKUP.find("SetIpInterfaceEntry").orElseThrow(), + setIpInterfaceEntryDesc + ); + + } catch (Throwable e) { + throw new RuntimeException("Failed to initialize Windows IP interface manager", e); + } + } + + /** + * MIB_IPINTERFACE_ROW 的 Java 对象表示 + */ + public static class MibIpInterfaceRow { + private int family; + private long interfaceLuid; + private int interfaceIndex; + private int maxReassemblySize; + private long interfaceIdentifier; + private int minRouterAdvertisementInterval; + private int maxRouterAdvertisementInterval; + private boolean advertisingEnabled; + private boolean forwardingEnabled; + private boolean weakHostSend; + private boolean weakHostReceive; + private boolean useAutomaticMetric; + private boolean useNeighborUnreachabilityDetection; + private boolean managedAddressConfigurationSupported; + private boolean otherStatefulConfigurationSupported; + private boolean advertiseDefaultRoute; + private RouterDiscoveryBehavior routerDiscoveryBehavior; + private int dadTransmits; + private int baseReachableTime; + private int retransmitTime; + private int pathMtuDiscoveryTimeout; + private LinkLocalAddressBehavior linkLocalAddressBehavior; + private int linkLocalAddressTimeout; + private int[] zoneIndices; + private int sitePrefixLength; + private int metric; + private int nlMtu; + private boolean connected; + private boolean supportsWakeUpPatterns; + private boolean supportsNeighborDiscovery; + private boolean supportsRouterDiscovery; + private int reachableTime; + private byte transmitOffloadFlags; + private byte receiveOffloadFlags; + private boolean disableDefaultRoutes; + + // 空构造器 + public MibIpInterfaceRow() {} + + // 从内存段创建对象 + public static MibIpInterfaceRow fromMemorySegment(MemorySegment segment) { + MibIpInterfaceRow row = new MibIpInterfaceRow(); + + // 计算字段偏移量 + // 使用手动计算的偏移量,因为结构体布局复杂 + long offset = 0; + + row.family = segment.get(WORD, offset); + offset += 8; // 2字节 + 6字节填充 + + row.interfaceLuid = segment.get(DWORD64, offset); + offset += 8; + + row.interfaceIndex = segment.get(DWORD, offset); + offset += 4; // 4字节 + + row.maxReassemblySize = segment.get(DWORD, offset); + offset += 4; + + row.interfaceIdentifier = segment.get(DWORD64, offset); + offset += 8; + + row.minRouterAdvertisementInterval = segment.get(DWORD, offset); + offset += 4; + + row.maxRouterAdvertisementInterval = segment.get(DWORD, offset); + offset += 4; + + row.advertisingEnabled = segment.get(BOOLEAN_LAYOUT, offset++); + row.forwardingEnabled = segment.get(BOOLEAN_LAYOUT, offset++); + row.weakHostSend = segment.get(BOOLEAN_LAYOUT, offset++); + row.weakHostReceive = segment.get(BOOLEAN_LAYOUT, offset++); + row.useAutomaticMetric = segment.get(BOOLEAN_LAYOUT, offset++); + row.useNeighborUnreachabilityDetection = segment.get(BOOLEAN_LAYOUT, offset++); + row.managedAddressConfigurationSupported = segment.get(BOOLEAN_LAYOUT, offset++); + row.otherStatefulConfigurationSupported = segment.get(BOOLEAN_LAYOUT, offset++); + row.advertiseDefaultRoute = segment.get(BOOLEAN_LAYOUT, offset++); + + offset += 3; // 跳过3字节填充 + + row.routerDiscoveryBehavior = RouterDiscoveryBehavior.fromValue(segment.get(DWORD, offset)); + offset += 4; + + row.dadTransmits = segment.get(DWORD, offset); + offset += 4; + + row.baseReachableTime = segment.get(DWORD, offset); + offset += 4; + + row.retransmitTime = segment.get(DWORD, offset); + offset += 4; + + row.pathMtuDiscoveryTimeout = segment.get(DWORD, offset); + offset += 4; + + row.linkLocalAddressBehavior = LinkLocalAddressBehavior.fromValue(segment.get(DWORD, offset)); + offset += 4; + + row.linkLocalAddressTimeout = segment.get(DWORD, offset); + offset += 4; + + // ZoneIndices 数组 + row.zoneIndices = new int[SCOPE_LEVEL_COUNT]; + for (int i = 0; i < SCOPE_LEVEL_COUNT; i++) { + row.zoneIndices[i] = segment.get(DWORD, offset); + offset += 4; + } + + row.sitePrefixLength = segment.get(DWORD, offset); + offset += 4; + + row.metric = segment.get(DWORD, offset); + offset += 4; + + row.nlMtu = segment.get(DWORD, offset); + offset += 4; + + row.connected = segment.get(BOOLEAN_LAYOUT, offset++); + row.supportsWakeUpPatterns = segment.get(BOOLEAN_LAYOUT, offset++); + row.supportsNeighborDiscovery = segment.get(BOOLEAN_LAYOUT, offset++); + row.supportsRouterDiscovery = segment.get(BOOLEAN_LAYOUT, offset++); + + + row.reachableTime = segment.get(DWORD, offset); + offset += 4; // 4字节 + + row.transmitOffloadFlags = segment.get(BYTE, offset++); + offset += 1; // NL_INTERFACE_OFFLOAD_ROD 的填充 + + row.receiveOffloadFlags = segment.get(BYTE, offset++); + offset += 6; // NL_INTERFACE_OFFLOAD_ROD 的填充 + 填充到8字节对齐 + + row.disableDefaultRoutes = segment.get(BOOLEAN_LAYOUT, offset); + + return row; + } + + // 将对象转换为内存段 + public MemorySegment toMemorySegment(Arena arena) { + MemorySegment segment = arena.allocate(MIB_IPINTERFACE_ROW_LAYOUT); + long offset = 0; + + segment.set(WORD, offset, (short) family); + offset += 8; // 2字节 + 6字节填充 + + segment.set(DWORD64, offset, interfaceLuid); + offset += 8; + + segment.set(DWORD, offset, interfaceIndex); + offset += 4; // 4字节 + + segment.set(DWORD, offset, maxReassemblySize); + offset += 4; + + segment.set(DWORD64, offset, interfaceIdentifier); + offset += 8; + + segment.set(DWORD, offset, minRouterAdvertisementInterval); + offset += 4; + + segment.set(DWORD, offset, maxRouterAdvertisementInterval); + offset += 4; + + segment.set(BOOLEAN_LAYOUT, offset++, advertisingEnabled); + segment.set(BOOLEAN_LAYOUT, offset++, forwardingEnabled); + segment.set(BOOLEAN_LAYOUT, offset++, weakHostSend); + segment.set(BOOLEAN_LAYOUT, offset++, weakHostReceive); + segment.set(BOOLEAN_LAYOUT, offset++, useAutomaticMetric); + segment.set(BOOLEAN_LAYOUT, offset++, useNeighborUnreachabilityDetection); + segment.set(BOOLEAN_LAYOUT, offset++, managedAddressConfigurationSupported); + segment.set(BOOLEAN_LAYOUT, offset++, otherStatefulConfigurationSupported); + segment.set(BOOLEAN_LAYOUT, offset++, advertiseDefaultRoute); + + offset += 3; // 跳过3字节填充 + + if(routerDiscoveryBehavior!=null) + segment.set(DWORD, offset, routerDiscoveryBehavior.getValue()); + offset += 4; + + segment.set(DWORD, offset, dadTransmits); + offset += 4; + + segment.set(DWORD, offset, baseReachableTime); + offset += 4; + + segment.set(DWORD, offset, retransmitTime); + offset += 4; + + segment.set(DWORD, offset, pathMtuDiscoveryTimeout); + offset += 4; + if(linkLocalAddressBehavior!=null) + segment.set(DWORD, offset, linkLocalAddressBehavior.getValue()); + offset += 4; + + segment.set(DWORD, offset, linkLocalAddressTimeout); + offset += 4; + + // ZoneIndices 数组 + if(zoneIndices!=null) + for (int i = 0; i < SCOPE_LEVEL_COUNT; i++) { + segment.set(DWORD, offset, zoneIndices[i]); + offset += 4; + } + + segment.set(DWORD, offset, sitePrefixLength); + offset += 4; + + segment.set(DWORD, offset, metric); + offset += 4; + + segment.set(DWORD, offset, nlMtu); + offset += 4; + + segment.set(BOOLEAN_LAYOUT, offset++, connected); + segment.set(BOOLEAN_LAYOUT, offset++, supportsWakeUpPatterns); + segment.set(BOOLEAN_LAYOUT, offset++, supportsNeighborDiscovery); + segment.set(BOOLEAN_LAYOUT, offset++, supportsRouterDiscovery); + + + segment.set(DWORD, offset, reachableTime); + offset += 4; // 4字节 + + segment.set(BYTE, offset++, transmitOffloadFlags); + offset += 1; // NL_INTERFACE_OFFLOAD_ROD 的填充 + + segment.set(BYTE, offset++, receiveOffloadFlags); + offset += 6; // NL_INTERFACE_OFFLOAD_ROD 的填充 + 填充到8字节对齐 + + segment.set(BOOLEAN_LAYOUT, offset, disableDefaultRoutes); + + return segment; + } + + // 创建简单的构建器方法 + public static MibIpInterfaceRow createForInterface(long interfaceLuid, int family) { + MibIpInterfaceRow row = new MibIpInterfaceRow(); + row.family = family; + row.interfaceLuid = interfaceLuid; + row.interfaceIndex = 0; // 将由系统填充 + + // 设置合理的默认值 + row.maxReassemblySize = 65535; + row.minRouterAdvertisementInterval = 0; + row.maxRouterAdvertisementInterval = 0; + row.advertisingEnabled = false; + row.forwardingEnabled = false; + row.weakHostSend = false; + row.weakHostReceive = false; + row.useAutomaticMetric = true; + row.useNeighborUnreachabilityDetection = true; + row.managedAddressConfigurationSupported = false; + row.otherStatefulConfigurationSupported = false; + row.advertiseDefaultRoute = false; + row.routerDiscoveryBehavior = RouterDiscoveryBehavior.RouterDiscoveryDisabled; + row.dadTransmits = 0; + row.baseReachableTime = 0; + row.retransmitTime = 0; + row.pathMtuDiscoveryTimeout = 0; + row.linkLocalAddressBehavior = LinkLocalAddressBehavior.LinkLocalAlwaysOn; + row.linkLocalAddressTimeout = 0; + + row.zoneIndices = new int[SCOPE_LEVEL_COUNT]; + for (int i = 0; i < SCOPE_LEVEL_COUNT; i++) { + row.zoneIndices[i] = 0; + } + + row.sitePrefixLength = 0; + row.metric = 0; // 自动计算 + row.nlMtu = 1500; // 默认MTU + row.connected = true; + row.supportsWakeUpPatterns = false; + row.supportsNeighborDiscovery = true; + row.supportsRouterDiscovery = false; + row.reachableTime = 0; + row.transmitOffloadFlags = 0; + row.receiveOffloadFlags = 0; + row.disableDefaultRoutes = false; + + return row; + } + + // Getters 和 Setters (由于字段太多,只列出关键字段) + public int getFamily() { return family; } + public void setFamily(int family) { this.family = family; } + + public long getInterfaceLuid() { return interfaceLuid; } + public void setInterfaceLuid(long interfaceLuid) { this.interfaceLuid = interfaceLuid; } + + public int getInterfaceIndex() { return interfaceIndex; } + public void setInterfaceIndex(int interfaceIndex) { this.interfaceIndex = interfaceIndex; } + + public int getNlMtu() { return nlMtu; } + public void setNlMtu(int nlMtu) { this.nlMtu = nlMtu; } + + public int getMetric() { return metric; } + public void setMetric(int metric) { this.metric = metric; } + + public boolean isForwardingEnabled() { return forwardingEnabled; } + public void setForwardingEnabled(boolean forwardingEnabled) { this.forwardingEnabled = forwardingEnabled; } + + public boolean isAdvertisingEnabled() { return advertisingEnabled; } + public void setAdvertisingEnabled(boolean advertisingEnabled) { this.advertisingEnabled = advertisingEnabled; } + + public boolean isConnected() { return connected; } + public void setConnected(boolean connected) { this.connected = connected; } + + public int getMaxReassemblySize() { + return maxReassemblySize; + } + + public void setMaxReassemblySize(int maxReassemblySize) { + this.maxReassemblySize = maxReassemblySize; + } + + public long getInterfaceIdentifier() { + return interfaceIdentifier; + } + + public void setInterfaceIdentifier(long interfaceIdentifier) { + this.interfaceIdentifier = interfaceIdentifier; + } + + public int getMinRouterAdvertisementInterval() { + return minRouterAdvertisementInterval; + } + + public void setMinRouterAdvertisementInterval(int minRouterAdvertisementInterval) { + this.minRouterAdvertisementInterval = minRouterAdvertisementInterval; + } + + public int getMaxRouterAdvertisementInterval() { + return maxRouterAdvertisementInterval; + } + + public void setMaxRouterAdvertisementInterval(int maxRouterAdvertisementInterval) { + this.maxRouterAdvertisementInterval = maxRouterAdvertisementInterval; + } + + public boolean isWeakHostSend() { + return weakHostSend; + } + + public void setWeakHostSend(boolean weakHostSend) { + this.weakHostSend = weakHostSend; + } + + public boolean isWeakHostReceive() { + return weakHostReceive; + } + + public void setWeakHostReceive(boolean weakHostReceive) { + this.weakHostReceive = weakHostReceive; + } + + public boolean isUseAutomaticMetric() { + return useAutomaticMetric; + } + + public void setUseAutomaticMetric(boolean useAutomaticMetric) { + this.useAutomaticMetric = useAutomaticMetric; + } + + public boolean isUseNeighborUnreachabilityDetection() { + return useNeighborUnreachabilityDetection; + } + + public void setUseNeighborUnreachabilityDetection(boolean useNeighborUnreachabilityDetection) { + this.useNeighborUnreachabilityDetection = useNeighborUnreachabilityDetection; + } + + public boolean isManagedAddressConfigurationSupported() { + return managedAddressConfigurationSupported; + } + + public void setManagedAddressConfigurationSupported(boolean managedAddressConfigurationSupported) { + this.managedAddressConfigurationSupported = managedAddressConfigurationSupported; + } + + public boolean isOtherStatefulConfigurationSupported() { + return otherStatefulConfigurationSupported; + } + + public void setOtherStatefulConfigurationSupported(boolean otherStatefulConfigurationSupported) { + this.otherStatefulConfigurationSupported = otherStatefulConfigurationSupported; + } + + public boolean isAdvertiseDefaultRoute() { + return advertiseDefaultRoute; + } + + public void setAdvertiseDefaultRoute(boolean advertiseDefaultRoute) { + this.advertiseDefaultRoute = advertiseDefaultRoute; + } + + public RouterDiscoveryBehavior getRouterDiscoveryBehavior() { + return routerDiscoveryBehavior; + } + + public void setRouterDiscoveryBehavior(RouterDiscoveryBehavior routerDiscoveryBehavior) { + this.routerDiscoveryBehavior = routerDiscoveryBehavior; + } + + public int getDadTransmits() { + return dadTransmits; + } + + public void setDadTransmits(int dadTransmits) { + this.dadTransmits = dadTransmits; + } + + public int getBaseReachableTime() { + return baseReachableTime; + } + + public void setBaseReachableTime(int baseReachableTime) { + this.baseReachableTime = baseReachableTime; + } + + public int getRetransmitTime() { + return retransmitTime; + } + + public void setRetransmitTime(int retransmitTime) { + this.retransmitTime = retransmitTime; + } + + public int getPathMtuDiscoveryTimeout() { + return pathMtuDiscoveryTimeout; + } + + public void setPathMtuDiscoveryTimeout(int pathMtuDiscoveryTimeout) { + this.pathMtuDiscoveryTimeout = pathMtuDiscoveryTimeout; + } + + public LinkLocalAddressBehavior getLinkLocalAddressBehavior() { + return linkLocalAddressBehavior; + } + + public void setLinkLocalAddressBehavior(LinkLocalAddressBehavior linkLocalAddressBehavior) { + this.linkLocalAddressBehavior = linkLocalAddressBehavior; + } + + public int getLinkLocalAddressTimeout() { + return linkLocalAddressTimeout; + } + + public void setLinkLocalAddressTimeout(int linkLocalAddressTimeout) { + this.linkLocalAddressTimeout = linkLocalAddressTimeout; + } + + public int[] getZoneIndices() { + return zoneIndices; + } + + public void setZoneIndices(int[] zoneIndices) { + this.zoneIndices = zoneIndices; + } + + public int getSitePrefixLength() { + return sitePrefixLength; + } + + public void setSitePrefixLength(int sitePrefixLength) { + this.sitePrefixLength = sitePrefixLength; + } + + public boolean isSupportsWakeUpPatterns() { + return supportsWakeUpPatterns; + } + + public void setSupportsWakeUpPatterns(boolean supportsWakeUpPatterns) { + this.supportsWakeUpPatterns = supportsWakeUpPatterns; + } + + public boolean isSupportsNeighborDiscovery() { + return supportsNeighborDiscovery; + } + + public void setSupportsNeighborDiscovery(boolean supportsNeighborDiscovery) { + this.supportsNeighborDiscovery = supportsNeighborDiscovery; + } + + public boolean isSupportsRouterDiscovery() { + return supportsRouterDiscovery; + } + + public void setSupportsRouterDiscovery(boolean supportsRouterDiscovery) { + this.supportsRouterDiscovery = supportsRouterDiscovery; + } + + public int getReachableTime() { + return reachableTime; + } + + public void setReachableTime(int reachableTime) { + this.reachableTime = reachableTime; + } + + public byte getTransmitOffloadFlags() { + return transmitOffloadFlags; + } + + public void setTransmitOffloadFlags(byte transmitOffloadFlags) { + this.transmitOffloadFlags = transmitOffloadFlags; + } + + public byte getReceiveOffloadFlags() { + return receiveOffloadFlags; + } + + public void setReceiveOffloadFlags(byte receiveOffloadFlags) { + this.receiveOffloadFlags = receiveOffloadFlags; + } + + public boolean isDisableDefaultRoutes() { + return disableDefaultRoutes; + } + + public void setDisableDefaultRoutes(boolean disableDefaultRoutes) { + this.disableDefaultRoutes = disableDefaultRoutes; + } + + @Override + public String toString() { + return "MibIpInterfaceRow [family=" + family + ", interfaceLuid=" + interfaceLuid + ", interfaceIndex=" + + interfaceIndex + ", maxReassemblySize=" + maxReassemblySize + ", interfaceIdentifier=" + + interfaceIdentifier + ", minRouterAdvertisementInterval=" + minRouterAdvertisementInterval + + ", maxRouterAdvertisementInterval=" + maxRouterAdvertisementInterval + ", advertisingEnabled=" + + advertisingEnabled + ", forwardingEnabled=" + forwardingEnabled + ", weakHostSend=" + weakHostSend + + ", weakHostReceive=" + weakHostReceive + ", useAutomaticMetric=" + useAutomaticMetric + + ", useNeighborUnreachabilityDetection=" + useNeighborUnreachabilityDetection + + ", managedAddressConfigurationSupported=" + managedAddressConfigurationSupported + + ", otherStatefulConfigurationSupported=" + otherStatefulConfigurationSupported + + ", advertiseDefaultRoute=" + advertiseDefaultRoute + ", routerDiscoveryBehavior=" + + routerDiscoveryBehavior + ", dadTransmits=" + dadTransmits + ", baseReachableTime=" + + baseReachableTime + ", retransmitTime=" + retransmitTime + ", pathMtuDiscoveryTimeout=" + + pathMtuDiscoveryTimeout + ", linkLocalAddressBehavior=" + linkLocalAddressBehavior + + ", linkLocalAddressTimeout=" + linkLocalAddressTimeout + ", zoneIndices=" + + Arrays.toString(zoneIndices) + ", sitePrefixLength=" + sitePrefixLength + ", metric=" + metric + + ", nlMtu=" + nlMtu + ", connected=" + connected + ", supportsWakeUpPatterns=" + + supportsWakeUpPatterns + ", supportsNeighborDiscovery=" + supportsNeighborDiscovery + + ", supportsRouterDiscovery=" + supportsRouterDiscovery + ", reachableTime=" + reachableTime + + ", transmitOffloadFlags=" + transmitOffloadFlags + ", receiveOffloadFlags=" + receiveOffloadFlags + + ", disableDefaultRoutes=" + disableDefaultRoutes + "]"; + } + } + + /** + * 获取 IP 接口条目 + * @param row 包含接口标识信息的行,函数会填充其他字段 + * @return 填充后的接口行 + * @throws WindowsAPIException 如果操作失败 + */ + public static MibIpInterfaceRow getIpInterfaceEntry(MibIpInterfaceRow row) throws WindowsAPIException { + try (Arena arena = Arena.ofConfined()) { + // 创建内存段 + MemorySegment rowSegment = row.toMemorySegment(arena); + + // 调用 GetIpInterfaceEntry + int result = (int) GET_IP_INTERFACE_ENTRY.invokeExact(rowSegment); + + if (result != NO_ERROR) { + throw new WindowsAPIException("GetIpInterfaceEntry failed", result); + } + + // 从内存段读取更新后的行 + return MibIpInterfaceRow.fromMemorySegment(rowSegment); + + } catch (Throwable e) { + throw new RuntimeException("Error calling GetIpInterfaceEntry", e); + } + } + + /** + * 设置 IP 接口条目 + * @param row 要设置的接口行 + * @throws WindowsAPIException 如果操作失败 + */ + public static void setIpInterfaceEntry(MibIpInterfaceRow row) throws WindowsAPIException { + try (Arena arena = Arena.ofConfined()) { + // 创建内存段 + MemorySegment rowSegment = row.toMemorySegment(arena); + + // 调用 SetIpInterfaceEntry + int result = (int) SET_IP_INTERFACE_ENTRY.invokeExact(rowSegment); + + if (result != NO_ERROR) { + throw new WindowsAPIException("SetIpInterfaceEntry failed", result); + } + + } catch (Throwable e) { + throw new RuntimeException("Error calling SetIpInterfaceEntry", e); + } + } + + /** + * 获取指定接口的 IP 接口信息 + * @param interfaceLuid 接口 LUID + * @param family 地址族 (AF_INET 或 AF_INET6) + * @return 接口信息 + * @throws WindowsAPIException 如果操作失败 + */ + public static MibIpInterfaceRow getInterfaceInfo(long interfaceLuid, int family) throws WindowsAPIException { + // 创建只包含键字段的行 + MibIpInterfaceRow row = new MibIpInterfaceRow(); + row.setFamily(family); + row.setInterfaceLuid(interfaceLuid); + row.setInterfaceIndex(0); // 将由系统填充 + + // 获取完整信息 + return getIpInterfaceEntry(row); + } + + /** + * 设置接口的 MTU + * @param interfaceLuid 接口 LUID + * @param family 地址族 (AF_INET 或 AF_INET6) + * @param mtu MTU 值 + * @throws WindowsAPIException 如果操作失败 + */ + public static void setInterfaceMtu(long interfaceLuid, int family, int mtu) throws WindowsAPIException { + // 1. 获取当前配置 + MibIpInterfaceRow row = getInterfaceInfo(interfaceLuid, family); + row.setSitePrefixLength(0); + // 2. 修改 MTU + row.setNlMtu(mtu); + + // 3. 设置回系统 + setIpInterfaceEntry(row); + } + + /** + * 设置接口的路由度量值 + * @param interfaceLuid 接口 LUID + * @param family 地址族 (AF_INET 或 AF_INET6) + * @param metric 度量值 (越小优先级越高) + * @throws WindowsAPIException 如果操作失败 + */ + public static void setInterfaceMetric(long interfaceLuid, int family, int metric) throws WindowsAPIException { + // 1. 获取当前配置 + MibIpInterfaceRow row = getInterfaceInfo(interfaceLuid, family); + + // 2. 修改度量值 + row.setMetric(metric); + + // 3. 设置回系统 + setIpInterfaceEntry(row); + } + + /** + * 启用/禁用接口的 IP 转发 + * @param interfaceLuid 接口 LUID + * @param family 地址族 (AF_INET 或 AF_INET6) + * @param enable 是否启用转发 + * @throws WindowsAPIException 如果操作失败 + */ + public static void setInterfaceForwarding(long interfaceLuid, int family, boolean enable) throws WindowsAPIException { + // 1. 获取当前配置 + MibIpInterfaceRow row = getInterfaceInfo(interfaceLuid, family); + + // 2. 修改转发设置 + row.setForwardingEnabled(enable); + + // 3. 设置回系统 + setIpInterfaceEntry(row); + } + + + /** + * 测试方法 + */ + public static void main(String[] args) { + try { + System.out.println("=== Windows IP Interface Manager Test ==="); + + // 示例:假设我们有一个 Wintun 适配器的 LUID + long wintunLuid = 14918723638919168L; + + System.out.println("\n1. Testing IPv4 interface info:"); + try { + MibIpInterfaceRow ipv4Info = getInterfaceInfo(wintunLuid, AF_INET); + System.out.println(" " + ipv4Info); + System.out.println(" Current MTU: " + ipv4Info.getNlMtu()); + System.out.println(" Current metric: " + ipv4Info.getMetric()); + System.out.println(" Forwarding enabled: " + ipv4Info.isForwardingEnabled()); + } catch (WindowsAPIException e) { + System.out.println(" Failed to get IPv4 info: " + e.getMessage()); + } + + System.out.println("\n2. Testing IPv6 interface info:"); + try { + MibIpInterfaceRow ipv6Info = getInterfaceInfo(wintunLuid, AF_INET6); + System.out.println(" " + ipv6Info); + System.out.println(" Current MTU: " + ipv6Info.getNlMtu()); + System.out.println(" Current metric: " + ipv6Info.getMetric()); + System.out.println(" Forwarding enabled: " + ipv6Info.isForwardingEnabled()); + } catch (WindowsAPIException e) { + System.out.println(" Failed to get IPv6 info: " + e.getMessage()); + } + + System.out.println("\n3. Testing MTU setting:"); + try { + // 注意:需要管理员权限 + setInterfaceMtu(wintunLuid, AF_INET, 1800); + System.out.println(" Set IPv4 MTU to 1800"); + System.out.println(" (MTU setting requires administrator privileges)"); + } catch (Exception e) { + e.printStackTrace(); + } + + System.out.println("\nTest completed!"); + + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/org/kne/cloud/network/tun/WindowsInterfaceConverter.java b/src/org/kne/cloud/network/tun/WindowsInterfaceConverter.java new file mode 100644 index 0000000..2630000 --- /dev/null +++ b/src/org/kne/cloud/network/tun/WindowsInterfaceConverter.java @@ -0,0 +1,417 @@ +package org.kne.cloud.network.tun; +import java.lang.classfile.AnnotationValue.OfLong; +import java.lang.foreign.*; +import java.lang.invoke.MethodHandle; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Arrays; +import java.util.UUID; + +/** + * Windows 网络接口 LUID 和 GUID 转换工具类 + * 提供 ConvertInterfaceLuidToGuid 和 ConvertInterfaceGuidToLuid 的 Java 实现 + */ +public class WindowsInterfaceConverter { + + // Windows API 常量 + private static final long INVALID_LUID_VALUE = 0L; + + // 内存布局 + public static final AddressLayout POINTER = ValueLayout.ADDRESS; + public static final ValueLayout.OfLong DWORD64 = ValueLayout.JAVA_LONG; + + // GUID 结构布局 (128位,16字节) + public static final GroupLayout GUID_LAYOUT = MemoryLayout.structLayout( + ValueLayout.JAVA_INT.withName("Data1"), // unsigned long + ValueLayout.JAVA_SHORT.withName("Data2"), // unsigned short + ValueLayout.JAVA_SHORT.withName("Data3"), // unsigned short + MemoryLayout.sequenceLayout(8, ValueLayout.JAVA_BYTE).withName("Data4") // unsigned char[8] + ).withByteAlignment(4); + + // LUID 结构布局 (64位) + public static final GroupLayout LUID_LAYOUT = MemoryLayout.structLayout( + ValueLayout.JAVA_LONG.withName("Value") // unsigned __int64 + ); + + // NET_LUID 联合布局 (Windows中的NET_LUID可以是LUID或IF_INDEX) + public static final GroupLayout NET_LUID_LAYOUT = MemoryLayout.structLayout( + MemoryLayout.unionLayout( + MemoryLayout.structLayout( + ValueLayout.JAVA_LONG.withName("Value") // LUID + ).withName("Luid"), + MemoryLayout.structLayout( + MemoryLayout.paddingLayout(24), + ValueLayout.JAVA_INT.withName("Reserved"), // 保留字段 + ValueLayout.JAVA_INT.withName("NetLuidIndex"), // 网络LUID索引 + ValueLayout.JAVA_INT.withName("IfIndex") // 接口索引 + ).withName("Info") + ).withName("u") + ); + + private static final SymbolLookup LOOKUP; + private static final Linker LINKER = Linker.nativeLinker(); + private static final Arena GLOBAL_ARENA = Arena.ofAuto(); + + // 方法句柄 + private static final MethodHandle CONVERT_LUID_TO_GUID; + private static final MethodHandle CONVERT_GUID_TO_LUID; + + static { + try { + // 加载 iphlpapi.dll + System.loadLibrary("iphlpapi"); + LOOKUP = SymbolLookup.loaderLookup(); + + // 定义函数描述符 + // DWORD ConvertInterfaceLuidToGuid(const NET_LUID *Luid, GUID *Guid); + FunctionDescriptor luidToGuidDesc = FunctionDescriptor.of( + ValueLayout.JAVA_INT, // DWORD 返回值 + POINTER, // const NET_LUID *Luid + POINTER // GUID *Guid + ); + + // DWORD ConvertInterfaceGuidToLuid(const GUID *Guid, NET_LUID *Luid); + FunctionDescriptor guidToLuidDesc = FunctionDescriptor.of( + ValueLayout.JAVA_INT, // DWORD 返回值 + POINTER, // const GUID *Guid + POINTER // NET_LUID *Luid + ); + + // 获取方法句柄 + CONVERT_LUID_TO_GUID = LINKER.downcallHandle( + LOOKUP.find("ConvertInterfaceLuidToGuid").orElseThrow(), + luidToGuidDesc + ); + + CONVERT_GUID_TO_LUID = LINKER.downcallHandle( + LOOKUP.find("ConvertInterfaceGuidToLuid").orElseThrow(), + guidToLuidDesc + ); + + } catch (Throwable e) { + throw new RuntimeException("Failed to initialize Windows interface converter", e); + } + } + + /** + * LUID 结构 + */ + public static class LUID { + private final long value; + + public LUID(long value) { + this.value = value; + } + + public long getValue() { + return value; + } + + public static LUID fromMemorySegment(MemorySegment segment) { + if (segment.byteSize() < 8) { + throw new IllegalArgumentException("Memory segment too small for LUID"); + } + long value = segment.get(ValueLayout.JAVA_LONG, 0); + return new LUID(value); + } + + public MemorySegment toMemorySegment(Arena arena) { + MemorySegment segment = arena.allocate(LUID_LAYOUT); + segment.set(ValueLayout.JAVA_LONG, 0, value); + return segment; + } + + @Override + public String toString() { + return String.format("0x%016X", value); + } + } + + /** + * GUID 结构 + */ + public static class GUID { + private final UUID uuid; + + public GUID(UUID uuid) { + this.uuid = uuid; + } + + public GUID(String guidString) { + this.uuid = UUID.fromString(guidString); + } + + public GUID(int data1, short data2, short data3, byte[] data4) { + if (data4.length != 8) { + throw new IllegalArgumentException("Data4 must be exactly 8 bytes"); + } + + long mostSigBits = (((long) data1) << 32) | (((long)( data2&0xffff) )<< 16) | (long)(data3&0xffff); + ByteBuffer bb = ByteBuffer.wrap(data4).order(ByteOrder.BIG_ENDIAN); + long leastSigBits = bb.getLong(); + + this.uuid = new UUID(mostSigBits, leastSigBits); + } + + public UUID getUuid() { + return uuid; + } + + public static GUID fromMemorySegment(MemorySegment segment) { + if (segment.byteSize() < GUID_LAYOUT.byteSize()) { + throw new IllegalArgumentException("Memory segment too small for GUID"); + } + + int data1 = segment.get(ValueLayout.JAVA_INT, 0); + short data2 = segment.get(ValueLayout.JAVA_SHORT, 4); + short data3 = segment.get(ValueLayout.JAVA_SHORT, 6); + + byte[] data4 = new byte[8]; + MemorySegment data4Segment = segment.asSlice(8, 8); + for (int i = 0; i < 8; i++) { + data4[i] = data4Segment.get(ValueLayout.JAVA_BYTE, i); + } + + return new GUID(data1, data2, data3, data4); + } + + public MemorySegment toMemorySegment(Arena arena) { + MemorySegment segment = arena.allocate(GUID_LAYOUT); + + long mostSigBits = uuid.getMostSignificantBits(); + long leastSigBits = uuid.getLeastSignificantBits(); + + // Data1 = 高32位 + int data1 = (int) (mostSigBits >> 32); + // Data2 = 接下来的16位 + short data2 = (short) ((mostSigBits >> 16) & 0xFFFF); + // Data3 = 最后的16位 + short data3 = (short) (mostSigBits & 0xFFFF); + + segment.set(ValueLayout.JAVA_INT, 0, data1); + segment.set(ValueLayout.JAVA_SHORT, 4, data2); + segment.set(ValueLayout.JAVA_SHORT, 6, data3); + + // Data4 = 低64位的字节数组 + ByteBuffer bb = ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN); + bb.putLong(leastSigBits); + byte[] data4 = bb.array(); + + MemorySegment data4Segment = segment.asSlice(8, 8); + for (int i = 0; i < 8; i++) { + data4Segment.set(ValueLayout.JAVA_BYTE, i, data4[i]); + } + + return segment; + } + + @Override + public String toString() { + return uuid.toString().toUpperCase(); + } + + public String toRegistryFormat() { + return "{" + toString() + "}"; + } + } + + /** + * 将 LUID 转换为 GUID + * @param luid LUID 值 + * @return 对应的 GUID + */ + public static GUID convertLuidToGuid(LUID luid) { + try (Arena arena = Arena.ofConfined()) { + // 准备 NET_LUID 结构 + MemorySegment netLuid = arena.allocate(NET_LUID_LAYOUT); + netLuid.set(ValueLayout.JAVA_LONG, 0, luid.getValue()); + + // 准备 GUID 输出缓冲区 + MemorySegment guidBuffer = arena.allocate(GUID_LAYOUT); + + // 调用 Windows API + int result = (int) CONVERT_LUID_TO_GUID.invokeExact( + netLuid, + guidBuffer + ); + + // 检查结果 + if (result != 0) { // NO_ERROR = 0 + throw new WindowsAPIException("ConvertInterfaceLuidToGuid failed with error: " + result); + } + + return GUID.fromMemorySegment(guidBuffer); + } catch (Throwable e) { + throw new RuntimeException("Error converting LUID to GUID", e); + } + } + + /** + * 将 LUID 转换为 GUID(简化版本) + * @param luidValue LUID 的 long 值 + * @return 对应的 GUID + */ + public static GUID convertLuidToGuid(long luidValue) { + return convertLuidToGuid(new LUID(luidValue)); + } + + /** + * 将 GUID 转换为 LUID + * @param guid GUID + * @return 对应的 LUID + */ + public static LUID convertGuidToLuid(GUID guid) { + try (Arena arena = Arena.ofConfined()) { + // 准备 GUID 结构 + MemorySegment guidBuffer = guid.toMemorySegment(arena); + + // 准备 NET_LUID 输出缓冲区 + MemorySegment netLuidBuffer = arena.allocate(NET_LUID_LAYOUT); + + // 调用 Windows API + int result = (int) CONVERT_GUID_TO_LUID.invokeExact( + guidBuffer, + netLuidBuffer + ); + + // 检查结果 + if (result != 0) { // NO_ERROR = 0 + throw new WindowsAPIException("ConvertInterfaceGuidToLuid failed with error: " + result); + } + + // 从 NET_LUID 结构中提取 LUID 值 + long luidValue = netLuidBuffer.get(ValueLayout.JAVA_LONG, 0); + return new LUID(luidValue); + } catch (Throwable e) { + throw new RuntimeException("Error converting GUID to LUID", e); + } + } + + /** + * 将 GUID 字符串转换为 LUID + * @param guidString GUID 字符串 + * @return 对应的 LUID + */ + public static LUID convertGuidToLuid(String guidString) { + return convertGuidToLuid(new GUID(guidString)); + } + + /** + * 获取所有网络接口的 LUID 和 GUID 映射 + * @return LUID 到 GUID 的映射 + */ + public static java.util.Map getAllNetworkInterfaces() { + // 注意:这个函数需要更多的 Windows API 调用 + // 这里只是示例,实际实现需要调用 GetAdaptersAddresses 等函数 + java.util.Map interfaces = new java.util.HashMap<>(); + + try { + // 这里可以添加调用 GetAdaptersAddresses 的代码 + // 遍历所有适配器,获取它们的 LUID 和 GUID + } catch (Exception e) { + e.printStackTrace(); + } + + return interfaces; + } + + /** + * Windows API 异常 + */ + public static class WindowsAPIException extends RuntimeException { + private final int errorCode; + + public WindowsAPIException(String message) { + super(message); + this.errorCode = -1; + } + + public WindowsAPIException(String message, int errorCode) { + super(message + " (Error code: " + errorCode + ")"); + this.errorCode = errorCode; + } + + public int getErrorCode() { + return errorCode; + } + } + + /** + * 测试方法 + */ + public static void main(String[] args) { + try { + System.out.println("Windows Interface Converter Test"); + System.out.println("================================="); + + // 测试 1: 创建一个测试 GUID 并转换 + String testGuidStr = "12345678-1234-1234-1234-123456789012"; + System.out.println("Test GUID: " + testGuidStr); + + GUID testGuid = new GUID(testGuidStr); + System.out.println("Created GUID: " + testGuid); + + // 尝试将 GUID 转换为 LUID + try { + LUID luidFromGuid = convertGuidToLuid(testGuid); + System.out.println("LUID from GUID: " + luidFromGuid); + + // 再转换回 GUID + GUID guidFromLuid = convertLuidToGuid(luidFromGuid); + System.out.println("GUID from LUID: " + guidFromLuid); + System.out.println("Conversion successful: " + guidFromLuid.equals(testGuid)); + } catch (Exception e) { + System.out.println("Note: This GUID might not correspond to a real interface: " + e.getMessage()); + } + + System.out.println("\nTesting with Wintun adapter:"); + System.out.println("============================="); + + // 测试 2: 与 Wintun 驱动结合使用 + // 假设你已经有了一个 Wintun 适配器 + // 这里展示如何获取适配器的 LUID 并转换为 GUID + + // 这段代码需要在有 Wintun 适配器的情况下运行 + + WintunDriver.WintunAdapterHandle adapter = WintunDriver.openAdapter("MyWintun"); + long luid = WintunDriver.getAdapterLuid(adapter); + LUID adapterLuid = new LUID(luid); + System.out.println("Wintun Adapter LUID: " + adapterLuid); + + GUID adapterGuid = convertLuidToGuid(adapterLuid); + System.out.println("Wintun Adapter GUID: " + adapterGuid); + + // 清理 + WintunDriver.closeAdapter(adapter); + + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 辅助方法:从字节数组创建 GUID + */ + public static GUID createGuidFromBytes(byte[] bytes) { + if (bytes.length != 16) { + throw new IllegalArgumentException("GUID must be 16 bytes"); + } + + ByteBuffer bb = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); + long mostSigBits = bb.getLong(); + long leastSigBits = bb.getLong(); + + return new GUID(new UUID(mostSigBits, leastSigBits)); + } + + /** + * 辅助方法:将 GUID 转换为字节数组 + */ + public static byte[] guidToBytes(GUID guid) { + ByteBuffer bb = ByteBuffer.allocate(16).order(ByteOrder.BIG_ENDIAN); + bb.putLong(guid.getUuid().getMostSignificantBits()); + bb.putLong(guid.getUuid().getLeastSignificantBits()); + return bb.array(); + } +} \ No newline at end of file diff --git a/src/org/kne/cloud/network/tun/WindowsInterfaceManager.java b/src/org/kne/cloud/network/tun/WindowsInterfaceManager.java new file mode 100644 index 0000000..b03027a --- /dev/null +++ b/src/org/kne/cloud/network/tun/WindowsInterfaceManager.java @@ -0,0 +1,998 @@ +package org.kne.cloud.network.tun; + +import java.lang.foreign.*; +import java.lang.invoke.MethodHandle; +import java.util.*; + +import org.kne.cloud.network.tun.WindowsInterfaceManager2.MibIfRow; + +/** + * Windows 网络接口管理类 + * 实现 GetIfEntry、SetIfEntry、GetIfTable 和 FreeMibTable 功能 + */ +public class WindowsInterfaceManager { + + // 内存布局 + public static final AddressLayout POINTER = ValueLayout.ADDRESS; + public static final ValueLayout.OfInt DWORD = ValueLayout.JAVA_INT; + public static final ValueLayout.OfLong DWORD64 = ValueLayout.JAVA_LONG; + public static final ValueLayout.OfByte BYTE = ValueLayout.JAVA_BYTE; + public static final ValueLayout.OfShort WORD = ValueLayout.JAVA_SHORT; + + // 常量定义 + public static final int MAX_INTERFACE_NAME_LEN = 256; + public static final int MAXLEN_PHYSADDR = 32; + public static final int MAXLEN_IFDESCR = 256; + public static final int NO_ERROR = 0; + + // MIB_IFTABLE 结构体布局 + public static final GroupLayout MIB_IFTABLE_LAYOUT = MemoryLayout.structLayout( + DWORD.withName("dwNumEntries"), + MemoryLayout.paddingLayout(4), + POINTER.withName("table") // 指向MIB_IFROW数组的指针 + ).withByteAlignment(8); + + // MIB_IFROW 结构体布局 + public static final GroupLayout MIB_IFROW_LAYOUT; + + static { + // 手动计算偏移量以确保正确对齐 + // 注意:所有字段都是 4 字节对齐的 + long offset = 0; + + // 1. wszName: WCHAR[MAX_INTERFACE_NAME_LEN] (512 字节) + // 需要 2 字节对齐,但结构体开始通常是 4 字节对齐 + var wszNameLayout = MemoryLayout.sequenceLayout(MAX_INTERFACE_NAME_LEN, WORD) + .withName("wszName") + .withByteAlignment(2); + + offset += MAX_INTERFACE_NAME_LEN * 2; // 512 字节 + + // 2. dwIndex: IF_INDEX (4 字节) + // offset = 512, 是 4 的倍数 + var dwIndexLayout = DWORD + .withName("dwIndex") + .withByteAlignment(4); + + offset += 4; // 516 字节 + + // 3. dwType: IFTYPE (4 字节) + // offset = 516, 是 4 的倍数 + var dwTypeLayout = DWORD + .withName("dwType") + .withByteAlignment(4); + + offset += 4; // 520 字节 + + // 4. dwMtu: DWORD (4 字节) + // offset = 520, 是 4 的倍数 + var dwMtuLayout = DWORD + .withName("dwMtu") + .withByteAlignment(4); + + offset += 4; // 524 字节 + + // 5. dwSpeed: DWORD (4 字节) + // offset = 524, 是 4 的倍数 + var dwSpeedLayout = DWORD + .withName("dwSpeed") + .withByteAlignment(4); + + offset += 4; // 528 字节 + + // 6. dwPhysAddrLen: DWORD (4 字节) + // offset = 528, 是 4 的倍数 + var dwPhysAddrLenLayout = DWORD + .withName("dwPhysAddrLen") + .withByteAlignment(4); + + offset += 4; // 532 字节 + + // 7. bPhysAddr: UCHAR[MAXLEN_PHYSADDR] (32 字节) + // offset = 532, 是 1 的倍数,但数组本身 1 字节对齐 + // 实际上需要 4 字节对齐,532 是 4 的倍数 (532 % 4 = 0) + var bPhysAddrLayout = MemoryLayout.sequenceLayout(MAXLEN_PHYSADDR, BYTE) + .withName("bPhysAddr") + .withByteAlignment(1); + + offset += MAXLEN_PHYSADDR; // 564 字节 + + // 8. dwAdminStatus: DWORD (4 字节) + // offset = 564, 是 4 的倍数 + var dwAdminStatusLayout = DWORD + .withName("dwAdminStatus") + .withByteAlignment(4); + + offset += 4; // 568 字节 + + // 9. dwOperStatus: INTERNAL_IF_OPER_STATUS (4 字节) + // offset = 568, 是 4 的倍数 + var dwOperStatusLayout = DWORD + .withName("dwOperStatus") + .withByteAlignment(4); + + offset += 4; // 572 字节 + + // 10. dwLastChange: DWORD (4 字节) + // offset = 572, 是 4 的倍数 + var dwLastChangeLayout = DWORD + .withName("dwLastChange") + .withByteAlignment(4); + + offset += 4; // 576 字节 + + // 11. dwInOctets: DWORD (4 字节) + // offset = 576, 是 4 的倍数 + var dwInOctetsLayout = DWORD + .withName("dwInOctets") + .withByteAlignment(4); + + offset += 4; // 580 字节 + + // 12. dwInUcastPkts: DWORD (4 字节) + // offset = 580, 是 4 的倍数 + var dwInUcastPktsLayout = DWORD + .withName("dwInUcastPkts") + .withByteAlignment(4); + + offset += 4; // 584 字节 + + // 13. dwInNUcastPkts: DWORD (4 字节) + // offset = 584, 是 4 的倍数 + var dwInNUcastPktsLayout = DWORD + .withName("dwInNUcastPkts") + .withByteAlignment(4); + + offset += 4; // 588 字节 + + // 14. dwInDiscards: DWORD (4 字节) + // offset = 588, 是 4 的倍数 + var dwInDiscardsLayout = DWORD + .withName("dwInDiscards") + .withByteAlignment(4); + + offset += 4; // 592 字节 + + // 15. dwInErrors: DWORD (4 字节) + // offset = 592, 是 4 的倍数 + var dwInErrorsLayout = DWORD + .withName("dwInErrors") + .withByteAlignment(4); + + offset += 4; // 596 字节 + + // 16. dwInUnknownProtos: DWORD (4 字节) + // offset = 596, 是 4 的倍数 + var dwInUnknownProtosLayout = DWORD + .withName("dwInUnknownProtos") + .withByteAlignment(4); + + offset += 4; // 600 字节 + + // 17. dwOutOctets: DWORD (4 字节) + // offset = 600, 是 4 的倍数 + var dwOutOctetsLayout = DWORD + .withName("dwOutOctets") + .withByteAlignment(4); + + offset += 4; // 604 字节 + + // 18. dwOutUcastPkts: DWORD (4 字节) + // offset = 604, 是 4 的倍数 + var dwOutUcastPktsLayout = DWORD + .withName("dwOutUcastPkts") + .withByteAlignment(4); + + offset += 4; // 608 字节 + + // 19. dwOutNUcastPkts: DWORD (4 字节) + // offset = 608, 是 4 的倍数 + var dwOutNUcastPktsLayout = DWORD + .withName("dwOutNUcastPkts") + .withByteAlignment(4); + + offset += 4; // 612 字节 + + // 20. dwOutDiscards: DWORD (4 字节) + // offset = 612, 是 4 的倍数 + var dwOutDiscardsLayout = DWORD + .withName("dwOutDiscards") + .withByteAlignment(4); + + offset += 4; // 616 字节 + + // 21. dwOutErrors: DWORD (4 字节) + // offset = 616, 是 4 的倍数 + var dwOutErrorsLayout = DWORD + .withName("dwOutErrors") + .withByteAlignment(4); + + offset += 4; // 620 字节 + + // 22. dwOutQLen: DWORD (4 字节) + // offset = 620, 是 4 的倍数 + var dwOutQLenLayout = DWORD + .withName("dwOutQLen") + .withByteAlignment(4); + + offset += 4; // 624 字节 + + // 23. dwDescrLen: DWORD (4 字节) + // offset = 624, 是 4 的倍数 + var dwDescrLenLayout = DWORD + .withName("dwDescrLen") + .withByteAlignment(4); + + offset += 4; // 628 字节 + + // 24. bDescr: UCHAR[MAXLEN_IFDESCR] (256 字节) + // offset = 628, 是 4 的倍数 (628 % 4 = 0) + var bDescrLayout = MemoryLayout.sequenceLayout(MAXLEN_IFDESCR, BYTE) + .withName("bDescr") + .withByteAlignment(1); + + offset += MAXLEN_IFDESCR; // 884 字节 + + // 最终布局 + MIB_IFROW_LAYOUT = MemoryLayout.structLayout( + // wszName: 0-511 字节 (512 字节) + MemoryLayout.sequenceLayout(MAX_INTERFACE_NAME_LEN, WORD) + .withName("wszName") + .withByteAlignment(2), + + // dwIndex: 512-515 字节 (4 字节) + DWORD.withName("dwIndex"), + + // dwType: 516-519 字节 (4 字节) + DWORD.withName("dwType"), + + // dwMtu: 520-523 字节 (4 字节) + DWORD.withName("dwMtu"), + + // dwSpeed: 524-527 字节 (4 字节) + DWORD.withName("dwSpeed"), + + // dwPhysAddrLen: 528-531 字节 (4 字节) + DWORD.withName("dwPhysAddrLen"), + + // bPhysAddr: 532-563 字节 (32 字节) + MemoryLayout.sequenceLayout(MAXLEN_PHYSADDR, BYTE) + .withName("bPhysAddr") + .withByteAlignment(1), + + // dwAdminStatus: 564-567 字节 (4 字节) + DWORD.withName("dwAdminStatus"), + + // dwOperStatus: 568-571 字节 (4 字节) + DWORD.withName("dwOperStatus"), + + // dwLastChange: 572-575 字节 (4 字节) + DWORD.withName("dwLastChange"), + + // dwInOctets: 576-579 字节 (4 字节) + DWORD.withName("dwInOctets"), + + // dwInUcastPkts: 580-583 字节 (4 字节) + DWORD.withName("dwInUcastPkts"), + + // dwInNUcastPkts: 584-587 字节 (4 字节) + DWORD.withName("dwInNUcastPkts"), + + // dwInDiscards: 588-591 字节 (4 字节) + DWORD.withName("dwInDiscards"), + + // dwInErrors: 592-595 字节 (4 字节) + DWORD.withName("dwInErrors"), + + // dwInUnknownProtos: 596-599 字节 (4 字节) + DWORD.withName("dwInUnknownProtos"), + + // dwOutOctets: 600-603 字节 (4 字节) + DWORD.withName("dwOutOctets"), + + // dwOutUcastPkts: 604-607 字节 (4 字节) + DWORD.withName("dwOutUcastPkts"), + + // dwOutNUcastPkts: 608-611 字节 (4 字节) + DWORD.withName("dwOutNUcastPkts"), + + // dwOutDiscards: 612-615 字节 (4 字节) + DWORD.withName("dwOutDiscards"), + + // dwOutErrors: 616-619 字节 (4 字节) + DWORD.withName("dwOutErrors"), + + // dwOutQLen: 620-623 字节 (4 字节) + DWORD.withName("dwOutQLen"), + + // dwDescrLen: 624-627 字节 (4 字节) + DWORD.withName("dwDescrLen"), + + // bDescr: 628-883 字节 (256 字节) + MemoryLayout.sequenceLayout(MAXLEN_IFDESCR, BYTE) + .withName("bDescr") + .withByteAlignment(1) + ).withByteAlignment(4); // 整个结构体 4 字节对齐 + } + + private static final SymbolLookup LOOKUP; + private static final Linker LINKER = Linker.nativeLinker(); + + // 方法句柄 + private static final MethodHandle GET_IF_ENTRY; + private static final MethodHandle SET_IF_ENTRY; + private static final MethodHandle GET_IF_TABLE; + private static final MethodHandle FREE_MIB_TABLE; + + static { + try { + // 加载 iphlpapi.dll + System.loadLibrary("iphlpapi"); + LOOKUP = SymbolLookup.loaderLookup(); + + // GetIfEntry 函数 + // DWORD GetIfEntry(PMIB_IFROW pIfRow); + FunctionDescriptor getIfEntryDesc = FunctionDescriptor.of( + DWORD, // 返回值: DWORD + POINTER // pIfRow: PMIB_IFROW + ); + + // SetIfEntry 函数 + // DWORD SetIfEntry(const PMIB_IFROW pIfRow); + FunctionDescriptor setIfEntryDesc = FunctionDescriptor.of( + DWORD, // 返回值: DWORD + POINTER // pIfRow: const PMIB_IFROW + ); + + // GetIfTable 函数 + // DWORD GetIfTable( + // PMIB_IFTABLE pIfTable, + // PULONG pdwSize, + // BOOL bOrder + // ); + FunctionDescriptor getIfTableDesc = FunctionDescriptor.of( + DWORD, // 返回值: DWORD + POINTER, // pIfTable: PMIB_IFTABLE* + POINTER, // pdwSize: ULONG* + DWORD // bOrder: BOOL (int) + ); + + // FreeMibTable 函数 + // VOID FreeMibTable(PVOID Memory); + FunctionDescriptor freeMibTableDesc = FunctionDescriptor.ofVoid( + POINTER // Memory: PVOID + ); + + // 获取方法句柄 + GET_IF_ENTRY = LINKER.downcallHandle( + LOOKUP.find("GetIfEntry").orElseThrow(), + getIfEntryDesc + ); + + SET_IF_ENTRY = LINKER.downcallHandle( + LOOKUP.find("SetIfEntry").orElseThrow(), + setIfEntryDesc + ); + + GET_IF_TABLE = LINKER.downcallHandle( + LOOKUP.find("GetIfTable").orElseThrow(), + getIfTableDesc + ); + + FREE_MIB_TABLE = LINKER.downcallHandle( + LOOKUP.find("FreeMibTable").orElseThrow(), + freeMibTableDesc + ); + + } catch (Throwable e) { + throw new RuntimeException("Failed to initialize Windows interface manager", e); + } + } + + /** + * 获取网络接口表 + * + * @param order 是否按接口索引排序 (true=排序, false=不排序) + * @return 接口表指针,调用者需负责使用freeIfTable释放内存 + */ + public static MemorySegment getIfTable(boolean order) throws WindowsAPIException { + try (Arena arena = Arena.ofConfined()) { + // 第一次调用获取所需缓冲区大小 + MemorySegment sizePtr = arena.allocate(DWORD); + + int result = (int) GET_IF_TABLE.invokeExact( + MemorySegment.NULL, + sizePtr, + order ? 1 : 0 + ); + + int requiredSize = sizePtr.get(DWORD, 0); + + // 分配足够的内存 + MemorySegment ifTableBuffer = Arena.ofAuto().allocate(requiredSize); + + // 第二次调用获取实际数据 + result = (int) GET_IF_TABLE.invokeExact( + ifTableBuffer, + sizePtr, + order ? 1 : 0 + ); + + if (result != NO_ERROR) { + throw new WindowsAPIException("GetIfTable failed", result); + } + + // 返回表指针 + return ifTableBuffer; + } catch (Throwable e) { + throw new RuntimeException("Error getting interface table", e); + } + } + + /** + * 释放接口表内存 + * + * @param ifTable 接口表指针 + */ + public static void freeIfTable(MemorySegment ifTable) { + try { + FREE_MIB_TABLE.invokeExact(ifTable); + } catch (Throwable e) { + throw new RuntimeException("Error freeing interface table", e); + } + } + + /** + * 获取所有网络接口的列表 + * + * @param order 是否按接口索引排序 + * @return 接口列表 + */ + public static List getAllInterfaces(boolean order) throws WindowsAPIException { + MemorySegment ifTable = null; + try { + // 获取接口表 + ifTable = getIfTable(order); + + // 读取表头 + int numEntries = ifTable.get(DWORD, 0); + MemorySegment tablePtr = ifTable.asSlice(8); + + List interfaces = new ArrayList<>(); + + // 解析每个接口 + for (int i = 0; i < numEntries; i++) { + // 注意:这里需要根据实际的MIB_IFROW_LAYOUT来计算偏移量 + // 假设MIB_IFROW_LAYOUT存在且正确 + long rowOffset = i * MIB_IFROW_LAYOUT.byteSize(); // 根据之前计算的MIB_IFROW大小 + MemorySegment rowSegment = tablePtr.asSlice(rowOffset); + + MibIfRow ifRow = parseMibIfRow(rowSegment); + interfaces.add(ifRow); + } + + return interfaces; + } finally { + // 释放内存 + if (ifTable != null && !ifTable.equals(MemorySegment.NULL)) { + freeIfTable(ifTable); + } + } + } + + /** + * 查找指定名称的网络接口 + */ + public static MibIfRow findInterfaceByName(String name, boolean caseSensitive) throws WindowsAPIException { + List interfaces = getAllInterfaces(false); + + for (MibIfRow ifRow : interfaces) { + String interfaceName = ifRow.getName(); + if (caseSensitive) { + if (interfaceName.equals(name)) { + return ifRow; + } + } else { + if (interfaceName.equalsIgnoreCase(name)) { + return ifRow; + } + } + } + + return null; + } + + /** + * 查找指定索引的网络接口 + */ + public static MibIfRow findInterfaceByIndex(int index) throws WindowsAPIException { + List interfaces = getAllInterfaces(false); + + for (MibIfRow ifRow : interfaces) { + if (ifRow.getIndex() == index) { + return ifRow; + } + } + + return null; + } + + + + + + + // ========== 以下是原有代码的辅助方法 ========== + /** + * 获取接口信息 + * @param ifIndex 接口索引 + * @return 接口信息对象 + */ + public static MibIfRow getIfEntry(int ifIndex) { + try (Arena arena = Arena.ofConfined()) { + // 分配 MIB_IFROW 结构体 + MemorySegment ifRow = arena.allocate(MIB_IFROW_LAYOUT); + + // 清零初始化 + ifRow.fill((byte)0); + + // 设置接口索引 (dwIndex) + ifRow.set(DWORD, 512, ifIndex); + + // 调用 GetIfEntry + int result = (int) GET_IF_ENTRY.invokeExact(ifRow); + + if (result != NO_ERROR) { + throw new WindowsAPIException("GetIfEntry failed", result); + } + + // 解析结构体为 Java 对象 + return parseMibIfRow(ifRow); + + } catch (Throwable e) { + throw new RuntimeException("Error getting interface entry", e); + } + } + + /** + * 设置接口信息 (主要用来修改管理状态) + * @param ifRow 接口信息对象 + * @return 操作结果 (0 = 成功) + */ + public static int setIfEntry(MibIfRow ifRow) { + try (Arena arena = Arena.ofConfined()) { + // 分配 MIB_IFROW 结构体 + MemorySegment ifRowSegment = arena.allocate(MIB_IFROW_LAYOUT); + + // 清零初始化 + ifRowSegment.fill((byte)0); + + // 填充结构体 + fillMibIfRow(ifRowSegment, ifRow, arena); + + // 调用 SetIfEntry + return (int) SET_IF_ENTRY.invokeExact(ifRowSegment); + + } catch (Throwable e) { + throw new RuntimeException("Error setting interface entry", e); + } + } + /** + * 写入宽字符串 + */ + private static void writeWideString(MemorySegment segment, long offset, String str, int maxChars) { + if (str == null || str.isEmpty()) { + return; + } + + byte[] utf16Bytes = str.getBytes(java.nio.charset.StandardCharsets.UTF_16LE); + int copyLen = Math.min(utf16Bytes.length, (maxChars - 1) * 2); + + if (copyLen > 0) { + MemorySegment.copy(MemorySegment.ofArray(utf16Bytes), 0, segment, offset, copyLen); + } + + // 添加 null 终止符 + segment.set(WORD, offset + copyLen, (short)0); + } + + /** + * 填充 MIB_IFROW 结构体 + */ + private static void fillMibIfRow(MemorySegment ifRow, MibIfRow data, Arena arena) { + // 1. 设置接口索引 (必须) + ifRow.set(DWORD, 512, data.getIndex()); + + // 2. 设置接口名称 (如果需要) + if (data.getName() != null) { + writeWideString(ifRow, 0, data.getName(), MAX_INTERFACE_NAME_LEN); + } + + // 3. 设置接口类型 (如果需要) + ifRow.set(DWORD, 516, data.getType()); + + // 4. 设置 MTU (如果需要) + ifRow.set(DWORD, 520, data.getMtu()); + + // 5. 设置速度 (如果需要) + ifRow.set(DWORD, 524, (int)Math.min(data.getSpeed(), 0xFFFFFFFFL)); + + // 6. 设置物理地址 (如果需要) + if (data.getPhysAddr() != null && data.getPhysAddr().length > 0) { + int len = Math.min(data.getPhysAddr().length, MAXLEN_PHYSADDR); + ifRow.set(DWORD, 528, len); + MemorySegment.copy(MemorySegment.ofArray(data.getPhysAddr()), 0, ifRow, 532, len); + } + + // 7. 设置管理状态 (主要用途) + ifRow.set(DWORD, 564, data.getAdminStatus()); + + // 8. 设置操作状态 (通常只读,但这里也设置) + ifRow.set(DWORD, 568, data.getOperStatus()); + + // 9. 设置接口描述 (如果需要) + if (data.getDescription() != null && !data.getDescription().isEmpty()) { + byte[] descrBytes = data.getDescription().getBytes(java.nio.charset.StandardCharsets.UTF_8); + int len = Math.min(descrBytes.length, MAXLEN_IFDESCR); + ifRow.set(DWORD, 624, len); + MemorySegment.copy(MemorySegment.ofArray(descrBytes), 0, ifRow, 628, len); + } + } + /** + * 解析 MIB_IFROW 结构体为 Java 对象 + */ + private static MibIfRow parseMibIfRow(MemorySegment ifRow) { + MibIfRow result = new MibIfRow(); + ifRow=ifRow.reinterpret(MIB_IFROW_LAYOUT.byteSize()); + // 1. 解析接口名称 (宽字符串) + String name = readWideString(ifRow, 0, MAX_INTERFACE_NAME_LEN); + result.setName(name); + + // 2. 读取基本字段 + result.setIndex(ifRow.get(DWORD, 512)); + result.setType(ifRow.get(DWORD, 516)); + result.setMtu(ifRow.get(DWORD, 520)); + + // 3. 速度需要处理为无符号 + result.setSpeed(Integer.toUnsignedLong(ifRow.get(DWORD, 524))); + + // 4. 物理地址 + int physAddrLen = ifRow.get(DWORD, 528); + if (physAddrLen > 0 && physAddrLen <= MAXLEN_PHYSADDR) { + byte[] physAddr = new byte[physAddrLen]; + MemorySegment.copy(ifRow, 532, MemorySegment.ofArray(physAddr), 0, physAddrLen); + result.setPhysAddr(physAddr); + } + + // 5. 状态信息 + result.setAdminStatus(ifRow.get(DWORD, 564)); + result.setOperStatus(ifRow.get(DWORD, 568)); + result.setLastChange(Integer.toUnsignedLong(ifRow.get(DWORD, 572))); + + // 6. 输入统计 (需要处理为无符号) + result.setInOctets(Integer.toUnsignedLong(ifRow.get(DWORD, 576))); + result.setInUcastPkts(Integer.toUnsignedLong(ifRow.get(DWORD, 580))); + result.setInNUcastPkts(Integer.toUnsignedLong(ifRow.get(DWORD, 584))); + result.setInDiscards(Integer.toUnsignedLong(ifRow.get(DWORD, 588))); + result.setInErrors(Integer.toUnsignedLong(ifRow.get(DWORD, 592))); + result.setInUnknownProtos(Integer.toUnsignedLong(ifRow.get(DWORD, 596))); + + // 7. 输出统计 + result.setOutOctets(Integer.toUnsignedLong(ifRow.get(DWORD, 600))); + result.setOutUcastPkts(Integer.toUnsignedLong(ifRow.get(DWORD, 604))); + result.setOutNUcastPkts(Integer.toUnsignedLong(ifRow.get(DWORD, 608))); + result.setOutDiscards(Integer.toUnsignedLong(ifRow.get(DWORD, 612))); + result.setOutErrors(Integer.toUnsignedLong(ifRow.get(DWORD, 616))); + result.setOutQLen(Integer.toUnsignedLong(ifRow.get(DWORD, 620))); + + // 8. 接口描述 + int descrLen = ifRow.get(DWORD, 624); + if (descrLen > 0 && descrLen <= MAXLEN_IFDESCR) { + byte[] descrBytes = new byte[descrLen]; + MemorySegment.copy(ifRow, 628, MemorySegment.ofArray(descrBytes), 0, descrLen); + result.setDescription(new String(descrBytes, java.nio.charset.StandardCharsets.UTF_8)); + } + + return result; + } + + /** + * 读取宽字符串 + */ + private static String readWideString(MemorySegment segment, long offset, int maxChars) { + try { + // 查找 null 终止符 + long length = 0; + while (length < maxChars) { + short ch = segment.get(WORD, offset + length * 2); + if (ch == 0) { + break; + } + length++; + } + + if (length == 0) { + return ""; + } + + // 读取字符串数据 + byte[] bytes = new byte[(int)(length * 2)]; + MemorySegment.copy(segment, offset, MemorySegment.ofArray(bytes), 0, bytes.length); + + return new String(bytes, java.nio.charset.StandardCharsets.UTF_16LE); + } catch (Exception e) { + return ""; + } + } + + /** + * MIB_IFROW 结构的 Java 对象封装 + */ + public static class MibIfRow { + // 接口类型常量 (部分常见类型) + public static final int IF_TYPE_ETHERNET_CSMACD = 6; + public static final int IF_TYPE_ISO88025_TOKENRING = 9; + public static final int IF_TYPE_PPP = 23; + public static final int IF_TYPE_SOFTWARE_LOOPBACK = 24; + public static final int IF_TYPE_ATM = 37; + public static final int IF_TYPE_IEEE80211 = 71; + public static final int IF_TYPE_TUNNEL = 131; + public static final int IF_TYPE_IEEE1394 = 144; + + // 管理状态常量 + public static final int MIB_IF_ADMIN_STATUS_UP = 1; + public static final int MIB_IF_ADMIN_STATUS_DOWN = 2; + public static final int MIB_IF_ADMIN_STATUS_TESTING = 3; + + // 操作状态常量 + public static final int IF_OPER_STATUS_NON_OPERATIONAL = 0; + public static final int IF_OPER_STATUS_UNREACHABLE = 1; + public static final int IF_OPER_STATUS_DISCONNECTED = 2; + public static final int IF_OPER_STATUS_CONNECTING = 3; + public static final int IF_OPER_STATUS_CONNECTED = 4; + public static final int IF_OPER_STATUS_OPERATIONAL = 5; + + private String name; // 接口名称 + private int index; // 接口索引 + private int type; // 接口类型 + private int mtu; // 最大传输单元 + private long speed; // 接口速度 (bps) + private byte[] physAddr; // 物理地址 (MAC) + private int adminStatus; // 管理状态 + private int operStatus; // 操作状态 + private long lastChange; // 最后状态改变时间 + private long inOctets; // 接收字节数 + private long inUcastPkts; // 接收单播包数 + private long inNUcastPkts; // 接收非单播包数 + private long inDiscards; // 接收丢弃数 + private long inErrors; // 接收错误数 + private long inUnknownProtos; // 接收未知协议数 + private long outOctets; // 发送字节数 + private long outUcastPkts; // 发送单播包数 + private long outNUcastPkts; // 发送非单播包数 + private long outDiscards; // 发送丢弃数 + private long outErrors; // 发送错误数 + private long outQLen; // 发送队列长度 + private String description; // 接口描述 + + public MibIfRow() { + this.physAddr = new byte[0]; + this.description = ""; + } + + // Getters and Setters + public String getName() { return name; } + public void setName(String name) { this.name = name; } + + public int getIndex() { return index; } + public void setIndex(int index) { this.index = index; } + + public int getType() { return type; } + public void setType(int type) { this.type = type; } + + public int getMtu() { return mtu; } + public void setMtu(int mtu) { this.mtu = mtu; } + + public long getSpeed() { return speed; } + public void setSpeed(long speed) { this.speed = speed; } + + public byte[] getPhysAddr() { return physAddr; } + public void setPhysAddr(byte[] physAddr) { this.physAddr = physAddr; } + + public int getAdminStatus() { return adminStatus; } + public void setAdminStatus(int adminStatus) { this.adminStatus = adminStatus; } + + public int getOperStatus() { return operStatus; } + public void setOperStatus(int operStatus) { this.operStatus = operStatus; } + + public long getLastChange() { return lastChange; } + public void setLastChange(long lastChange) { this.lastChange = lastChange; } + + public long getInOctets() { return inOctets; } + public void setInOctets(long inOctets) { this.inOctets = inOctets; } + + public long getInUcastPkts() { return inUcastPkts; } + public void setInUcastPkts(long inUcastPkts) { this.inUcastPkts = inUcastPkts; } + + public long getInNUcastPkts() { return inNUcastPkts; } + public void setInNUcastPkts(long inNUcastPkts) { this.inNUcastPkts = inNUcastPkts; } + + public long getInDiscards() { return inDiscards; } + public void setInDiscards(long inDiscards) { this.inDiscards = inDiscards; } + + public long getInErrors() { return inErrors; } + public void setInErrors(long inErrors) { this.inErrors = inErrors; } + + public long getInUnknownProtos() { return inUnknownProtos; } + public void setInUnknownProtos(long inUnknownProtos) { this.inUnknownProtos = inUnknownProtos; } + + public long getOutOctets() { return outOctets; } + public void setOutOctets(long outOctets) { this.outOctets = outOctets; } + + public long getOutUcastPkts() { return outUcastPkts; } + public void setOutUcastPkts(long outUcastPkts) { this.outUcastPkts = outUcastPkts; } + + public long getOutNUcastPkts() { return outNUcastPkts; } + public void setOutNUcastPkts(long outNUcastPkts) { this.outNUcastPkts = outNUcastPkts; } + + public long getOutDiscards() { return outDiscards; } + public void setOutDiscards(long outDiscards) { this.outDiscards = outDiscards; } + + public long getOutErrors() { return outErrors; } + public void setOutErrors(long outErrors) { this.outErrors = outErrors; } + + public long getOutQLen() { return outQLen; } + public void setOutQLen(long outQLen) { this.outQLen = outQLen; } + + public String getDescription() { return description; } + public void setDescription(String description) { this.description = description; } + + /** + * 获取接口类型名称 + */ + private static String getTypeName(int type) { + switch (type) { + case IF_TYPE_ETHERNET_CSMACD: return "Ethernet"; + case IF_TYPE_ISO88025_TOKENRING: return "Token Ring"; + case IF_TYPE_PPP: return "PPP"; + case IF_TYPE_SOFTWARE_LOOPBACK: return "Loopback"; + case IF_TYPE_ATM: return "ATM"; + case IF_TYPE_IEEE80211: return "WiFi"; + case IF_TYPE_TUNNEL: return "Tunnel"; + case IF_TYPE_IEEE1394: return "FireWire"; + default: return "Unknown (" + type + ")"; + } + } + + /** + * 获取管理状态名称 + */ + private static String getAdminStatusName(int status) { + switch (status) { + case MIB_IF_ADMIN_STATUS_UP: return "UP"; + case MIB_IF_ADMIN_STATUS_DOWN: return "DOWN"; + case MIB_IF_ADMIN_STATUS_TESTING: return "TESTING"; + default: return "UNKNOWN (" + status + ")"; + } + } + + /** + * 获取操作状态名称 + */ + private static String getOperStatusName(int status) { + switch (status) { + case IF_OPER_STATUS_NON_OPERATIONAL: return "NON_OPERATIONAL"; + case IF_OPER_STATUS_UNREACHABLE: return "UNREACHABLE"; + case IF_OPER_STATUS_DISCONNECTED: return "DISCONNECTED"; + case IF_OPER_STATUS_CONNECTING: return "CONNECTING"; + case IF_OPER_STATUS_CONNECTED: return "CONNECTED"; + case IF_OPER_STATUS_OPERATIONAL: return "OPERATIONAL"; + default: return "UNKNOWN (" + status + ")"; + } + } + @Override + public String toString() { + HexFormat hex = HexFormat.of().withDelimiter(":"); + StringBuilder sb = new StringBuilder(); + sb.append("Interface: ").append(name).append("\n"); + sb.append(" Index: ").append(index).append("\n"); + sb.append(" Type: ").append(getTypeName(type)).append(" (").append(type).append(")\n"); + sb.append(" MTU: ").append(mtu).append("\n"); + sb.append(" Speed: ").append(formatSpeed(speed)).append("\n"); + if (physAddr != null && physAddr.length > 0) { + sb.append(" MAC: ").append(hex.formatHex(physAddr)).append("\n"); + } + sb.append(" Admin Status: ").append(getAdminStatusName(adminStatus)).append("\n"); + sb.append(" Oper Status: ").append(getOperStatusName(operStatus)).append("\n"); + sb.append(" Description: ").append(description).append("\n"); + sb.append(" Statistics:\n"); + sb.append(" In: ").append(formatBytes(inOctets)).append(", ") + .append(inUcastPkts).append(" unicast, ") + .append(inNUcastPkts).append(" non-unicast\n"); + sb.append(" Out: ").append(formatBytes(outOctets)).append(", ") + .append(outUcastPkts).append(" unicast, ") + .append(outNUcastPkts).append(" non-unicast\n"); + return sb.toString(); + } + + private String formatSpeed(long speed) { + if (speed >= 1_000_000_000L) { + return String.format("%.1f Gbps", speed / 1_000_000_000.0); + } else if (speed >= 1_000_000L) { + return String.format("%.1f Mbps", speed / 1_000_000.0); + } else if (speed >= 1_000L) { + return String.format("%.1f Kbps", speed / 1_000.0); + } else { + return speed + " bps"; + } + } + + private String formatBytes(long bytes) { + if (bytes >= 1_073_741_824L) { // 1 GiB + return String.format("%.2f GiB", bytes / 1_073_741_824.0); + } else if (bytes >= 1_048_576L) { // 1 MiB + return String.format("%.2f MiB", bytes / 1_048_576.0); + } else if (bytes >= 1_024L) { // 1 KiB + return String.format("%.2f KiB", bytes / 1_024.0); + } else { + return bytes + " bytes"; + } + } + } + /** + * 测试方法 + */ + /*public static void main(String[] args) { + try { + System.out.println("Windows Interface Manager"); + System.out.println("========================\n"); + + // 测试获取所有接口(需要先获取接口表,这里简化测试) + System.out.println("Testing GetIfEntry for interface index 1:"); + + + // 测试获取环回接口(通常是索引 1) + System.out.println("\nTesting with common interface indices:"); + + // 常见的接口索引范围 + for (int i = 1; i <= 100; i++) { + try { + MibIfRow ifRow = getIfEntry(i); + System.out.println("Interface " + i + ": " + ifRow.getName()); + System.out.println(ifRow); + + + } catch (Exception e) { + // 接口不存在是正常的 + } + } + + } catch (Exception e) { + e.printStackTrace(); + } + }*/ + + /** + * 测试方法 + */ + public static void main(String[] args) { + try { + System.out.println("=== Windows Interface Manager Test ==="); + + // 测试获取所有接口 + System.out.println("\nAll Network Interfaces:"); + List interfaces = getAllInterfaces(true); + + for (MibIfRow ifRow : interfaces) { + System.out.printf(" [%d] %s (Type: %d)%n", + ifRow.getIndex(), ifRow.getName(), ifRow.getType()); + } + + System.out.println("\nTotal interfaces found: " + interfaces.size()); + + + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/org/kne/cloud/network/tun/WindowsInterfaceManager2.java b/src/org/kne/cloud/network/tun/WindowsInterfaceManager2.java new file mode 100644 index 0000000..4b9489c --- /dev/null +++ b/src/org/kne/cloud/network/tun/WindowsInterfaceManager2.java @@ -0,0 +1,841 @@ +package org.kne.cloud.network.tun; +import java.lang.foreign.*; +import java.lang.invoke.MethodHandle; +import java.util.Arrays; +import java.util.HexFormat; + +/** + * Windows 网络接口管理类 + * 实现 GetIfEntry 和 SetIfEntry 功能,提供 MIB_IFROW 结构的 Java 对象封装 + */ +public class WindowsInterfaceManager2 { + + // 内存布局 + public static final AddressLayout POINTER = ValueLayout.ADDRESS; + public static final ValueLayout.OfInt DWORD = ValueLayout.JAVA_INT; + public static final ValueLayout.OfLong DWORD64 = ValueLayout.JAVA_LONG; + public static final ValueLayout.OfByte BYTE = ValueLayout.JAVA_BYTE; + public static final ValueLayout.OfShort WORD = ValueLayout.JAVA_SHORT; + + // 常量定义 + public static final int MAX_INTERFACE_NAME_LEN = 256; + public static final int MAXLEN_PHYSADDR = 32; + public static final int MAXLEN_IFDESCR = 256; + public static final int NO_ERROR = 0; + + // 接口类型常量 (部分常见类型) + public static final int IF_TYPE_ETHERNET_CSMACD = 6; + public static final int IF_TYPE_ISO88025_TOKENRING = 9; + public static final int IF_TYPE_PPP = 23; + public static final int IF_TYPE_SOFTWARE_LOOPBACK = 24; + public static final int IF_TYPE_ATM = 37; + public static final int IF_TYPE_IEEE80211 = 71; + public static final int IF_TYPE_TUNNEL = 131; + public static final int IF_TYPE_IEEE1394 = 144; + + // 管理状态常量 + public static final int MIB_IF_ADMIN_STATUS_UP = 1; + public static final int MIB_IF_ADMIN_STATUS_DOWN = 2; + public static final int MIB_IF_ADMIN_STATUS_TESTING = 3; + + // 操作状态常量 + public static final int IF_OPER_STATUS_NON_OPERATIONAL = 0; + public static final int IF_OPER_STATUS_UNREACHABLE = 1; + public static final int IF_OPER_STATUS_DISCONNECTED = 2; + public static final int IF_OPER_STATUS_CONNECTING = 3; + public static final int IF_OPER_STATUS_CONNECTED = 4; + public static final int IF_OPER_STATUS_OPERATIONAL = 5; + + // MIB_IFROW 结构体布局 + public static final GroupLayout MIB_IFROW_LAYOUT; + + static { + // 手动计算偏移量以确保正确对齐 + // 注意:所有字段都是 4 字节对齐的 + long offset = 0; + + // 1. wszName: WCHAR[MAX_INTERFACE_NAME_LEN] (512 字节) + // 需要 2 字节对齐,但结构体开始通常是 4 字节对齐 + var wszNameLayout = MemoryLayout.sequenceLayout(MAX_INTERFACE_NAME_LEN, WORD) + .withName("wszName") + .withByteAlignment(2); + + offset += MAX_INTERFACE_NAME_LEN * 2; // 512 字节 + + // 2. dwIndex: IF_INDEX (4 字节) + // offset = 512, 是 4 的倍数 + var dwIndexLayout = DWORD + .withName("dwIndex") + .withByteAlignment(4); + + offset += 4; // 516 字节 + + // 3. dwType: IFTYPE (4 字节) + // offset = 516, 是 4 的倍数 + var dwTypeLayout = DWORD + .withName("dwType") + .withByteAlignment(4); + + offset += 4; // 520 字节 + + // 4. dwMtu: DWORD (4 字节) + // offset = 520, 是 4 的倍数 + var dwMtuLayout = DWORD + .withName("dwMtu") + .withByteAlignment(4); + + offset += 4; // 524 字节 + + // 5. dwSpeed: DWORD (4 字节) + // offset = 524, 是 4 的倍数 + var dwSpeedLayout = DWORD + .withName("dwSpeed") + .withByteAlignment(4); + + offset += 4; // 528 字节 + + // 6. dwPhysAddrLen: DWORD (4 字节) + // offset = 528, 是 4 的倍数 + var dwPhysAddrLenLayout = DWORD + .withName("dwPhysAddrLen") + .withByteAlignment(4); + + offset += 4; // 532 字节 + + // 7. bPhysAddr: UCHAR[MAXLEN_PHYSADDR] (32 字节) + // offset = 532, 是 1 的倍数,但数组本身 1 字节对齐 + // 实际上需要 4 字节对齐,532 是 4 的倍数 (532 % 4 = 0) + var bPhysAddrLayout = MemoryLayout.sequenceLayout(MAXLEN_PHYSADDR, BYTE) + .withName("bPhysAddr") + .withByteAlignment(1); + + offset += MAXLEN_PHYSADDR; // 564 字节 + + // 8. dwAdminStatus: DWORD (4 字节) + // offset = 564, 是 4 的倍数 + var dwAdminStatusLayout = DWORD + .withName("dwAdminStatus") + .withByteAlignment(4); + + offset += 4; // 568 字节 + + // 9. dwOperStatus: INTERNAL_IF_OPER_STATUS (4 字节) + // offset = 568, 是 4 的倍数 + var dwOperStatusLayout = DWORD + .withName("dwOperStatus") + .withByteAlignment(4); + + offset += 4; // 572 字节 + + // 10. dwLastChange: DWORD (4 字节) + // offset = 572, 是 4 的倍数 + var dwLastChangeLayout = DWORD + .withName("dwLastChange") + .withByteAlignment(4); + + offset += 4; // 576 字节 + + // 11. dwInOctets: DWORD (4 字节) + // offset = 576, 是 4 的倍数 + var dwInOctetsLayout = DWORD + .withName("dwInOctets") + .withByteAlignment(4); + + offset += 4; // 580 字节 + + // 12. dwInUcastPkts: DWORD (4 字节) + // offset = 580, 是 4 的倍数 + var dwInUcastPktsLayout = DWORD + .withName("dwInUcastPkts") + .withByteAlignment(4); + + offset += 4; // 584 字节 + + // 13. dwInNUcastPkts: DWORD (4 字节) + // offset = 584, 是 4 的倍数 + var dwInNUcastPktsLayout = DWORD + .withName("dwInNUcastPkts") + .withByteAlignment(4); + + offset += 4; // 588 字节 + + // 14. dwInDiscards: DWORD (4 字节) + // offset = 588, 是 4 的倍数 + var dwInDiscardsLayout = DWORD + .withName("dwInDiscards") + .withByteAlignment(4); + + offset += 4; // 592 字节 + + // 15. dwInErrors: DWORD (4 字节) + // offset = 592, 是 4 的倍数 + var dwInErrorsLayout = DWORD + .withName("dwInErrors") + .withByteAlignment(4); + + offset += 4; // 596 字节 + + // 16. dwInUnknownProtos: DWORD (4 字节) + // offset = 596, 是 4 的倍数 + var dwInUnknownProtosLayout = DWORD + .withName("dwInUnknownProtos") + .withByteAlignment(4); + + offset += 4; // 600 字节 + + // 17. dwOutOctets: DWORD (4 字节) + // offset = 600, 是 4 的倍数 + var dwOutOctetsLayout = DWORD + .withName("dwOutOctets") + .withByteAlignment(4); + + offset += 4; // 604 字节 + + // 18. dwOutUcastPkts: DWORD (4 字节) + // offset = 604, 是 4 的倍数 + var dwOutUcastPktsLayout = DWORD + .withName("dwOutUcastPkts") + .withByteAlignment(4); + + offset += 4; // 608 字节 + + // 19. dwOutNUcastPkts: DWORD (4 字节) + // offset = 608, 是 4 的倍数 + var dwOutNUcastPktsLayout = DWORD + .withName("dwOutNUcastPkts") + .withByteAlignment(4); + + offset += 4; // 612 字节 + + // 20. dwOutDiscards: DWORD (4 字节) + // offset = 612, 是 4 的倍数 + var dwOutDiscardsLayout = DWORD + .withName("dwOutDiscards") + .withByteAlignment(4); + + offset += 4; // 616 字节 + + // 21. dwOutErrors: DWORD (4 字节) + // offset = 616, 是 4 的倍数 + var dwOutErrorsLayout = DWORD + .withName("dwOutErrors") + .withByteAlignment(4); + + offset += 4; // 620 字节 + + // 22. dwOutQLen: DWORD (4 字节) + // offset = 620, 是 4 的倍数 + var dwOutQLenLayout = DWORD + .withName("dwOutQLen") + .withByteAlignment(4); + + offset += 4; // 624 字节 + + // 23. dwDescrLen: DWORD (4 字节) + // offset = 624, 是 4 的倍数 + var dwDescrLenLayout = DWORD + .withName("dwDescrLen") + .withByteAlignment(4); + + offset += 4; // 628 字节 + + // 24. bDescr: UCHAR[MAXLEN_IFDESCR] (256 字节) + // offset = 628, 是 4 的倍数 (628 % 4 = 0) + var bDescrLayout = MemoryLayout.sequenceLayout(MAXLEN_IFDESCR, BYTE) + .withName("bDescr") + .withByteAlignment(1); + + offset += MAXLEN_IFDESCR; // 884 字节 + + // 最终布局 + MIB_IFROW_LAYOUT = MemoryLayout.structLayout( + // wszName: 0-511 字节 (512 字节) + MemoryLayout.sequenceLayout(MAX_INTERFACE_NAME_LEN, WORD) + .withName("wszName") + .withByteAlignment(2), + + // dwIndex: 512-515 字节 (4 字节) + DWORD.withName("dwIndex"), + + // dwType: 516-519 字节 (4 字节) + DWORD.withName("dwType"), + + // dwMtu: 520-523 字节 (4 字节) + DWORD.withName("dwMtu"), + + // dwSpeed: 524-527 字节 (4 字节) + DWORD.withName("dwSpeed"), + + // dwPhysAddrLen: 528-531 字节 (4 字节) + DWORD.withName("dwPhysAddrLen"), + + // bPhysAddr: 532-563 字节 (32 字节) + MemoryLayout.sequenceLayout(MAXLEN_PHYSADDR, BYTE) + .withName("bPhysAddr") + .withByteAlignment(1), + + // dwAdminStatus: 564-567 字节 (4 字节) + DWORD.withName("dwAdminStatus"), + + // dwOperStatus: 568-571 字节 (4 字节) + DWORD.withName("dwOperStatus"), + + // dwLastChange: 572-575 字节 (4 字节) + DWORD.withName("dwLastChange"), + + // dwInOctets: 576-579 字节 (4 字节) + DWORD.withName("dwInOctets"), + + // dwInUcastPkts: 580-583 字节 (4 字节) + DWORD.withName("dwInUcastPkts"), + + // dwInNUcastPkts: 584-587 字节 (4 字节) + DWORD.withName("dwInNUcastPkts"), + + // dwInDiscards: 588-591 字节 (4 字节) + DWORD.withName("dwInDiscards"), + + // dwInErrors: 592-595 字节 (4 字节) + DWORD.withName("dwInErrors"), + + // dwInUnknownProtos: 596-599 字节 (4 字节) + DWORD.withName("dwInUnknownProtos"), + + // dwOutOctets: 600-603 字节 (4 字节) + DWORD.withName("dwOutOctets"), + + // dwOutUcastPkts: 604-607 字节 (4 字节) + DWORD.withName("dwOutUcastPkts"), + + // dwOutNUcastPkts: 608-611 字节 (4 字节) + DWORD.withName("dwOutNUcastPkts"), + + // dwOutDiscards: 612-615 字节 (4 字节) + DWORD.withName("dwOutDiscards"), + + // dwOutErrors: 616-619 字节 (4 字节) + DWORD.withName("dwOutErrors"), + + // dwOutQLen: 620-623 字节 (4 字节) + DWORD.withName("dwOutQLen"), + + // dwDescrLen: 624-627 字节 (4 字节) + DWORD.withName("dwDescrLen"), + + // bDescr: 628-883 字节 (256 字节) + MemoryLayout.sequenceLayout(MAXLEN_IFDESCR, BYTE) + .withName("bDescr") + .withByteAlignment(1) + ).withByteAlignment(4); // 整个结构体 4 字节对齐 + } + + private static final SymbolLookup LOOKUP; + private static final Linker LINKER = Linker.nativeLinker(); + + // 方法句柄 + private static final MethodHandle GET_IF_ENTRY; + private static final MethodHandle SET_IF_ENTRY; + + static { + try { + // 加载 iphlpapi.dll + System.loadLibrary("iphlpapi"); + LOOKUP = SymbolLookup.loaderLookup(); + + // GetIfEntry 函数 + // DWORD GetIfEntry(PMIB_IFROW pIfRow); + FunctionDescriptor getIfEntryDesc = FunctionDescriptor.of( + DWORD, // 返回值: DWORD + POINTER // pIfRow: PMIB_IFROW + ); + + // SetIfEntry 函数 + // DWORD SetIfEntry(const PMIB_IFROW pIfRow); + FunctionDescriptor setIfEntryDesc = FunctionDescriptor.of( + DWORD, // 返回值: DWORD + POINTER // pIfRow: const PMIB_IFROW + ); + + // 获取方法句柄 + GET_IF_ENTRY = LINKER.downcallHandle( + LOOKUP.find("GetIfEntry").orElseThrow(), + getIfEntryDesc + ); + + SET_IF_ENTRY = LINKER.downcallHandle( + LOOKUP.find("SetIfEntry").orElseThrow(), + setIfEntryDesc + ); + + } catch (Throwable e) { + throw new RuntimeException("Failed to initialize Windows interface manager", e); + } + } + + /** + * MIB_IFROW 结构的 Java 对象封装 + */ + public static class MibIfRow { + private String name; // 接口名称 + private int index; // 接口索引 + private int type; // 接口类型 + private int mtu; // 最大传输单元 + private long speed; // 接口速度 (bps) + private byte[] physAddr; // 物理地址 (MAC) + private int adminStatus; // 管理状态 + private int operStatus; // 操作状态 + private long lastChange; // 最后状态改变时间 + private long inOctets; // 接收字节数 + private long inUcastPkts; // 接收单播包数 + private long inNUcastPkts; // 接收非单播包数 + private long inDiscards; // 接收丢弃数 + private long inErrors; // 接收错误数 + private long inUnknownProtos; // 接收未知协议数 + private long outOctets; // 发送字节数 + private long outUcastPkts; // 发送单播包数 + private long outNUcastPkts; // 发送非单播包数 + private long outDiscards; // 发送丢弃数 + private long outErrors; // 发送错误数 + private long outQLen; // 发送队列长度 + private String description; // 接口描述 + + public MibIfRow() { + this.physAddr = new byte[0]; + this.description = ""; + } + + // Getters and Setters + public String getName() { return name; } + public void setName(String name) { this.name = name; } + + public int getIndex() { return index; } + public void setIndex(int index) { this.index = index; } + + public int getType() { return type; } + public void setType(int type) { this.type = type; } + + public int getMtu() { return mtu; } + public void setMtu(int mtu) { this.mtu = mtu; } + + public long getSpeed() { return speed; } + public void setSpeed(long speed) { this.speed = speed; } + + public byte[] getPhysAddr() { return physAddr; } + public void setPhysAddr(byte[] physAddr) { this.physAddr = physAddr; } + + public int getAdminStatus() { return adminStatus; } + public void setAdminStatus(int adminStatus) { this.adminStatus = adminStatus; } + + public int getOperStatus() { return operStatus; } + public void setOperStatus(int operStatus) { this.operStatus = operStatus; } + + public long getLastChange() { return lastChange; } + public void setLastChange(long lastChange) { this.lastChange = lastChange; } + + public long getInOctets() { return inOctets; } + public void setInOctets(long inOctets) { this.inOctets = inOctets; } + + public long getInUcastPkts() { return inUcastPkts; } + public void setInUcastPkts(long inUcastPkts) { this.inUcastPkts = inUcastPkts; } + + public long getInNUcastPkts() { return inNUcastPkts; } + public void setInNUcastPkts(long inNUcastPkts) { this.inNUcastPkts = inNUcastPkts; } + + public long getInDiscards() { return inDiscards; } + public void setInDiscards(long inDiscards) { this.inDiscards = inDiscards; } + + public long getInErrors() { return inErrors; } + public void setInErrors(long inErrors) { this.inErrors = inErrors; } + + public long getInUnknownProtos() { return inUnknownProtos; } + public void setInUnknownProtos(long inUnknownProtos) { this.inUnknownProtos = inUnknownProtos; } + + public long getOutOctets() { return outOctets; } + public void setOutOctets(long outOctets) { this.outOctets = outOctets; } + + public long getOutUcastPkts() { return outUcastPkts; } + public void setOutUcastPkts(long outUcastPkts) { this.outUcastPkts = outUcastPkts; } + + public long getOutNUcastPkts() { return outNUcastPkts; } + public void setOutNUcastPkts(long outNUcastPkts) { this.outNUcastPkts = outNUcastPkts; } + + public long getOutDiscards() { return outDiscards; } + public void setOutDiscards(long outDiscards) { this.outDiscards = outDiscards; } + + public long getOutErrors() { return outErrors; } + public void setOutErrors(long outErrors) { this.outErrors = outErrors; } + + public long getOutQLen() { return outQLen; } + public void setOutQLen(long outQLen) { this.outQLen = outQLen; } + + public String getDescription() { return description; } + public void setDescription(String description) { this.description = description; } + + @Override + public String toString() { + HexFormat hex = HexFormat.of().withDelimiter(":"); + StringBuilder sb = new StringBuilder(); + sb.append("Interface: ").append(name).append("\n"); + sb.append(" Index: ").append(index).append("\n"); + sb.append(" Type: ").append(getTypeName(type)).append(" (").append(type).append(")\n"); + sb.append(" MTU: ").append(mtu).append("\n"); + sb.append(" Speed: ").append(formatSpeed(speed)).append("\n"); + if (physAddr != null && physAddr.length > 0) { + sb.append(" MAC: ").append(hex.formatHex(physAddr)).append("\n"); + } + sb.append(" Admin Status: ").append(getAdminStatusName(adminStatus)).append("\n"); + sb.append(" Oper Status: ").append(getOperStatusName(operStatus)).append("\n"); + sb.append(" Description: ").append(description).append("\n"); + sb.append(" Statistics:\n"); + sb.append(" In: ").append(formatBytes(inOctets)).append(", ") + .append(inUcastPkts).append(" unicast, ") + .append(inNUcastPkts).append(" non-unicast\n"); + sb.append(" Out: ").append(formatBytes(outOctets)).append(", ") + .append(outUcastPkts).append(" unicast, ") + .append(outNUcastPkts).append(" non-unicast\n"); + return sb.toString(); + } + + private String formatSpeed(long speed) { + if (speed >= 1_000_000_000L) { + return String.format("%.1f Gbps", speed / 1_000_000_000.0); + } else if (speed >= 1_000_000L) { + return String.format("%.1f Mbps", speed / 1_000_000.0); + } else if (speed >= 1_000L) { + return String.format("%.1f Kbps", speed / 1_000.0); + } else { + return speed + " bps"; + } + } + + private String formatBytes(long bytes) { + if (bytes >= 1_073_741_824L) { // 1 GiB + return String.format("%.2f GiB", bytes / 1_073_741_824.0); + } else if (bytes >= 1_048_576L) { // 1 MiB + return String.format("%.2f MiB", bytes / 1_048_576.0); + } else if (bytes >= 1_024L) { // 1 KiB + return String.format("%.2f KiB", bytes / 1_024.0); + } else { + return bytes + " bytes"; + } + } + } + + /** + * 获取接口信息 + * @param ifIndex 接口索引 + * @return 接口信息对象 + */ + public static MibIfRow getIfEntry(int ifIndex) { + try (Arena arena = Arena.ofConfined()) { + // 分配 MIB_IFROW 结构体 + MemorySegment ifRow = arena.allocate(MIB_IFROW_LAYOUT); + + // 清零初始化 + ifRow.fill((byte)0); + + // 设置接口索引 (dwIndex) + ifRow.set(DWORD, 512, ifIndex); + + // 调用 GetIfEntry + int result = (int) GET_IF_ENTRY.invokeExact(ifRow); + + if (result != NO_ERROR) { + throw new WindowsAPIException("GetIfEntry failed", result); + } + + // 解析结构体为 Java 对象 + return parseMibIfRow(ifRow); + + } catch (Throwable e) { + throw new RuntimeException("Error getting interface entry", e); + } + } + + /** + * 设置接口信息 (主要用来修改管理状态) + * @param ifRow 接口信息对象 + * @return 操作结果 (0 = 成功) + */ + public static int setIfEntry(MibIfRow ifRow) { + try (Arena arena = Arena.ofConfined()) { + // 分配 MIB_IFROW 结构体 + MemorySegment ifRowSegment = arena.allocate(MIB_IFROW_LAYOUT); + + // 清零初始化 + ifRowSegment.fill((byte)0); + + // 填充结构体 + fillMibIfRow(ifRowSegment, ifRow, arena); + + // 调用 SetIfEntry + return (int) SET_IF_ENTRY.invokeExact(ifRowSegment); + + } catch (Throwable e) { + throw new RuntimeException("Error setting interface entry", e); + } + } + + /** + * 启用接口 (设置管理状态为 UP) + * @param ifIndex 接口索引 + * @return 操作结果 (0 = 成功) + */ + public static int enableInterface(int ifIndex) { + MibIfRow ifRow = new MibIfRow(); + ifRow.setIndex(ifIndex); + ifRow.setAdminStatus(MIB_IF_ADMIN_STATUS_UP); + return setIfEntry(ifRow); + } + + /** + * 禁用接口 (设置管理状态为 DOWN) + * @param ifIndex 接口索引 + * @return 操作结果 (0 = 成功) + */ + public static int disableInterface(int ifIndex) { + MibIfRow ifRow = new MibIfRow(); + ifRow.setIndex(ifIndex); + ifRow.setAdminStatus(MIB_IF_ADMIN_STATUS_DOWN); + return setIfEntry(ifRow); + } + + /** + * 解析 MIB_IFROW 结构体为 Java 对象 + */ + private static MibIfRow parseMibIfRow(MemorySegment ifRow) { + MibIfRow result = new MibIfRow(); + + // 1. 解析接口名称 (宽字符串) + String name = readWideString(ifRow, 0, MAX_INTERFACE_NAME_LEN); + result.setName(name); + + // 2. 读取基本字段 + result.setIndex(ifRow.get(DWORD, 512)); + result.setType(ifRow.get(DWORD, 516)); + result.setMtu(ifRow.get(DWORD, 520)); + + // 3. 速度需要处理为无符号 + result.setSpeed(Integer.toUnsignedLong(ifRow.get(DWORD, 524))); + + // 4. 物理地址 + int physAddrLen = ifRow.get(DWORD, 528); + if (physAddrLen > 0 && physAddrLen <= MAXLEN_PHYSADDR) { + byte[] physAddr = new byte[physAddrLen]; + MemorySegment.copy(ifRow, 532, MemorySegment.ofArray(physAddr), 0, physAddrLen); + result.setPhysAddr(physAddr); + } + + // 5. 状态信息 + result.setAdminStatus(ifRow.get(DWORD, 564)); + result.setOperStatus(ifRow.get(DWORD, 568)); + result.setLastChange(Integer.toUnsignedLong(ifRow.get(DWORD, 572))); + + // 6. 输入统计 (需要处理为无符号) + result.setInOctets(Integer.toUnsignedLong(ifRow.get(DWORD, 576))); + result.setInUcastPkts(Integer.toUnsignedLong(ifRow.get(DWORD, 580))); + result.setInNUcastPkts(Integer.toUnsignedLong(ifRow.get(DWORD, 584))); + result.setInDiscards(Integer.toUnsignedLong(ifRow.get(DWORD, 588))); + result.setInErrors(Integer.toUnsignedLong(ifRow.get(DWORD, 592))); + result.setInUnknownProtos(Integer.toUnsignedLong(ifRow.get(DWORD, 596))); + + // 7. 输出统计 + result.setOutOctets(Integer.toUnsignedLong(ifRow.get(DWORD, 600))); + result.setOutUcastPkts(Integer.toUnsignedLong(ifRow.get(DWORD, 604))); + result.setOutNUcastPkts(Integer.toUnsignedLong(ifRow.get(DWORD, 608))); + result.setOutDiscards(Integer.toUnsignedLong(ifRow.get(DWORD, 612))); + result.setOutErrors(Integer.toUnsignedLong(ifRow.get(DWORD, 616))); + result.setOutQLen(Integer.toUnsignedLong(ifRow.get(DWORD, 620))); + + // 8. 接口描述 + int descrLen = ifRow.get(DWORD, 624); + if (descrLen > 0 && descrLen <= MAXLEN_IFDESCR) { + byte[] descrBytes = new byte[descrLen]; + MemorySegment.copy(ifRow, 628, MemorySegment.ofArray(descrBytes), 0, descrLen); + result.setDescription(new String(descrBytes, java.nio.charset.StandardCharsets.UTF_8)); + } + + return result; + } + + /** + * 填充 MIB_IFROW 结构体 + */ + private static void fillMibIfRow(MemorySegment ifRow, MibIfRow data, Arena arena) { + // 1. 设置接口索引 (必须) + ifRow.set(DWORD, 512, data.getIndex()); + + // 2. 设置接口名称 (如果需要) + if (data.getName() != null) { + writeWideString(ifRow, 0, data.getName(), MAX_INTERFACE_NAME_LEN); + } + + // 3. 设置接口类型 (如果需要) + ifRow.set(DWORD, 516, data.getType()); + + // 4. 设置 MTU (如果需要) + ifRow.set(DWORD, 520, data.getMtu()); + + // 5. 设置速度 (如果需要) + ifRow.set(DWORD, 524, (int)Math.min(data.getSpeed(), 0xFFFFFFFFL)); + + // 6. 设置物理地址 (如果需要) + if (data.getPhysAddr() != null && data.getPhysAddr().length > 0) { + int len = Math.min(data.getPhysAddr().length, MAXLEN_PHYSADDR); + ifRow.set(DWORD, 528, len); + MemorySegment.copy(MemorySegment.ofArray(data.getPhysAddr()), 0, ifRow, 532, len); + } + + // 7. 设置管理状态 (主要用途) + ifRow.set(DWORD, 564, data.getAdminStatus()); + + // 8. 设置操作状态 (通常只读,但这里也设置) + ifRow.set(DWORD, 568, data.getOperStatus()); + + // 9. 设置接口描述 (如果需要) + if (data.getDescription() != null && !data.getDescription().isEmpty()) { + byte[] descrBytes = data.getDescription().getBytes(java.nio.charset.StandardCharsets.UTF_8); + int len = Math.min(descrBytes.length, MAXLEN_IFDESCR); + ifRow.set(DWORD, 624, len); + MemorySegment.copy(MemorySegment.ofArray(descrBytes), 0, ifRow, 628, len); + } + } + + /** + * 读取宽字符串 + */ + private static String readWideString(MemorySegment segment, long offset, int maxChars) { + try { + // 查找 null 终止符 + long length = 0; + while (length < maxChars) { + short ch = segment.get(WORD, offset + length * 2); + if (ch == 0) { + break; + } + length++; + } + + if (length == 0) { + return ""; + } + + // 读取字符串数据 + byte[] bytes = new byte[(int)(length * 2)]; + MemorySegment.copy(segment, offset, MemorySegment.ofArray(bytes), 0, bytes.length); + + return new String(bytes, java.nio.charset.StandardCharsets.UTF_16LE); + } catch (Exception e) { + return ""; + } + } + + /** + * 写入宽字符串 + */ + private static void writeWideString(MemorySegment segment, long offset, String str, int maxChars) { + if (str == null || str.isEmpty()) { + return; + } + + byte[] utf16Bytes = str.getBytes(java.nio.charset.StandardCharsets.UTF_16LE); + int copyLen = Math.min(utf16Bytes.length, (maxChars - 1) * 2); + + if (copyLen > 0) { + MemorySegment.copy(MemorySegment.ofArray(utf16Bytes), 0, segment, offset, copyLen); + } + + // 添加 null 终止符 + segment.set(WORD, offset + copyLen, (short)0); + } + + /** + * 获取接口类型名称 + */ + private static String getTypeName(int type) { + switch (type) { + case IF_TYPE_ETHERNET_CSMACD: return "Ethernet"; + case IF_TYPE_ISO88025_TOKENRING: return "Token Ring"; + case IF_TYPE_PPP: return "PPP"; + case IF_TYPE_SOFTWARE_LOOPBACK: return "Loopback"; + case IF_TYPE_ATM: return "ATM"; + case IF_TYPE_IEEE80211: return "WiFi"; + case IF_TYPE_TUNNEL: return "Tunnel"; + case IF_TYPE_IEEE1394: return "FireWire"; + default: return "Unknown (" + type + ")"; + } + } + + /** + * 获取管理状态名称 + */ + private static String getAdminStatusName(int status) { + switch (status) { + case MIB_IF_ADMIN_STATUS_UP: return "UP"; + case MIB_IF_ADMIN_STATUS_DOWN: return "DOWN"; + case MIB_IF_ADMIN_STATUS_TESTING: return "TESTING"; + default: return "UNKNOWN (" + status + ")"; + } + } + + /** + * 获取操作状态名称 + */ + private static String getOperStatusName(int status) { + switch (status) { + case IF_OPER_STATUS_NON_OPERATIONAL: return "NON_OPERATIONAL"; + case IF_OPER_STATUS_UNREACHABLE: return "UNREACHABLE"; + case IF_OPER_STATUS_DISCONNECTED: return "DISCONNECTED"; + case IF_OPER_STATUS_CONNECTING: return "CONNECTING"; + case IF_OPER_STATUS_CONNECTED: return "CONNECTED"; + case IF_OPER_STATUS_OPERATIONAL: return "OPERATIONAL"; + default: return "UNKNOWN (" + status + ")"; + } + } + + + /** + * 测试方法 + */ + public static void main(String[] args) { + try { + System.out.println("Windows Interface Manager"); + System.out.println("========================\n"); + + // 测试获取所有接口(需要先获取接口表,这里简化测试) + System.out.println("Testing GetIfEntry for interface index 1:"); + + try { + MibIfRow ifRow = getIfEntry(1); + System.out.println(ifRow); + } catch (Exception e) { + System.out.println("Failed to get interface 1: " + e.getMessage()); + } + + // 测试获取环回接口(通常是索引 1) + System.out.println("\nTesting with common interface indices:"); + + // 常见的接口索引范围 + for (int i = 1; i <= 100; i++) { + try { + MibIfRow ifRow = getIfEntry(i); + System.out.println("Interface " + i + ": " + ifRow.getName()); + + // 如果是 Wintun 接口,可以尝试启用/禁用 + if (ifRow.getName().contains("Wintun") || ifRow.getType() == IF_TYPE_TUNNEL) { + System.out.println(" Found Wintun/tunnel interface!"); + + // 示例:启用接口 + // int result = enableInterface(i); + // System.out.println(" Enable result: " + result); + } + } catch (Exception e) { + // 接口不存在是正常的 + } + } + + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/org/kne/cloud/network/tun/WindowsTUNChannel.java b/src/org/kne/cloud/network/tun/WindowsTUNChannel.java new file mode 100644 index 0000000..f3d290b --- /dev/null +++ b/src/org/kne/cloud/network/tun/WindowsTUNChannel.java @@ -0,0 +1,67 @@ +package org.kne.cloud.network.tun; + +import java.io.IOException; +import java.lang.foreign.MemorySegment; +import java.net.SocketException; +import java.nio.ByteBuffer; + +import org.kne.cloud.network.tun.WintunDriver.Packet; +import org.kne.cloud.network.tun.WintunDriver.SendPacket; +import org.kne.cloud.network.tun.WintunDriver.WintunSessionHandle; + +public class WindowsTUNChannel implements TUNChannel { + + private WintunSessionHandle session; + private MemorySegment waitevent; + + protected WindowsTUNChannel(WintunSessionHandle session) { + this.session=session; + this.waitevent =WintunDriver.getReadWaitEvent(session); + } + + @Override + public int write(ByteBuffer src) throws IOException { + if(session==null) { + throw new SocketException("TUN session closed!"); + } + int rem=src.remaining(); + SendPacket sendPacket = WintunDriver.allocateSendPacket(session, rem); + if (sendPacket != null) { + sendPacket.data().asByteBuffer().put(src); + sendPacket.send(); + } + return rem; + } + + @Override + public int read(ByteBuffer dst) throws IOException { + while (true) { + if(session==null) { + throw new SocketException("TUN session closed!"); + } + WindowsWaitHandle.waitForSingleObject(waitevent, 10); + Packet packet = WintunDriver.receivePacket(session); + if (packet != null) { + ByteBuffer bytebuf=packet.data().asByteBuffer(); + int rem=Math.min(bytebuf.remaining(),dst.remaining()); + dst.put(bytebuf); + packet.release(); + return rem; + + } + } + } + + @Override + public boolean isOpen() { + return session!=null; + } + + @Override + public void close() throws IOException { + WintunSessionHandle wsh=session; + session=null; + WintunDriver.endSession(wsh); + } + +} diff --git a/src/org/kne/cloud/network/tun/WindowsTUNNetworkDevice.class b/src/org/kne/cloud/network/tun/WindowsTUNNetworkDevice.class new file mode 100644 index 0000000..7fec2cf Binary files /dev/null and b/src/org/kne/cloud/network/tun/WindowsTUNNetworkDevice.class differ diff --git a/src/org/kne/cloud/network/tun/WindowsTUNNetworkDevice.java b/src/org/kne/cloud/network/tun/WindowsTUNNetworkDevice.java new file mode 100644 index 0000000..3f4446c --- /dev/null +++ b/src/org/kne/cloud/network/tun/WindowsTUNNetworkDevice.java @@ -0,0 +1,183 @@ +package org.kne.cloud.network.tun; + +import java.io.IOException; +import java.lang.foreign.MemorySegment; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.nio.channels.DatagramChannel; +import java.util.ArrayList; +import java.util.List; + +import org.kne.cloud.network.tun.WindowsDNSManager.DnsInterfaceSettings; +import org.kne.cloud.network.tun.WindowsIPAddressManager.AddressFamily; +import org.kne.cloud.network.tun.WindowsIPAddressManager.UnicastIPAddressRow; +import org.kne.cloud.network.tun.WindowsIPInterfaceManager.MibIpInterfaceRow; +import org.kne.cloud.network.tun.WindowsInterfaceConverter.GUID; +import org.kne.cloud.network.tun.WintunDriver.SendPacket; +import org.kne.cloud.network.tun.WintunDriver.WintunAdapterHandle; +import org.kne.cloud.network.tun.WintunDriver.WintunSessionHandle; + +public class WindowsTUNNetworkDevice extends TUNNetworkDevice{ + + private WintunAdapterHandle adapter; + + private long luid; + private GUID guid; + + public WindowsTUNNetworkDevice(String name, String type) { + adapter = WintunDriver. createAdapter(name, type, null); + luid=WintunDriver.getAdapterLuid(adapter); + guid=WindowsInterfaceConverter .convertLuidToGuid(luid); + // System.out.println("创建虚拟网卡: LUID="+luid+" GUID="+guid); + } + + @Override + public boolean isOpen() { + return adapter!=null; + } + + @Override + public void close() throws IOException { + if(channel!=null&&channel.isOpen()) { + channel.close(); + channel=null; + } + WintunAdapterHandle adap=adapter; + adapter=null; + if(adap!=null) { + WintunDriver.closeAdapter(adap); + } + } + + + + + @Override + public int getMTU() throws IOException { + MibIpInterfaceRow ipv4Info =WindowsIPInterfaceManager. getInterfaceInfo(luid, WindowsIPInterfaceManager. AF_INET); + MibIpInterfaceRow ipv6Info = WindowsIPInterfaceManager.getInterfaceInfo(luid,WindowsIPInterfaceManager. AF_INET6); + + return Math.min(ipv4Info.getNlMtu(),ipv6Info. getNlMtu()); + } + + @Override + public void setMTU(int mtu) throws IOException { + /* + try { + int rez = Runtime.getRuntime() + .exec("netsh interface ipv6 set subinterface \"" + name + "\" mtu=" + mtu + " store=persistent") + .waitFor(); + int rezx = Runtime.getRuntime() + .exec("netsh interface ipv4 set subinterface \"" + name + "\" mtu=" + mtu + " store=persistent") + .waitFor(); + if (rez != 0 || rezx != 0) { + throw new IOException("set MTU failed!"); + } + } catch (InterruptedException e) { + e.printStackTrace(); + }*/ + WindowsIPInterfaceManager.setInterfaceMtu(luid, WindowsIPInterfaceManager. AF_INET, mtu); + WindowsIPInterfaceManager.setInterfaceMtu(luid, WindowsIPInterfaceManager. AF_INET6, mtu); + } + + @Override + public void setDNSAddress(List addresses) throws IOException { + DnsInterfaceSettings dis=new DnsInterfaceSettings(); + dis.setVersion(WindowsDNSManager.DNS_INTERFACE_SETTINGS_VERSION1); + dis.setFlags(WindowsDNSManager.DNS_SETTING_IPV6|WindowsDNSManager.DNS_SETTING_NAMESERVER); + dis.setNameServers(new ArrayList()); + + DnsInterfaceSettings dis4=new DnsInterfaceSettings(); + dis4.setVersion(WindowsDNSManager.DNS_INTERFACE_SETTINGS_VERSION1); + dis4.setFlags(WindowsDNSManager.DNS_SETTING_NAMESERVER); + dis4.setNameServers(new ArrayList()); + + for(InetAddress addr:addresses) { + if(addr instanceof Inet6Address) { + dis.getNameServers().add(addr.getHostAddress()); + }else { + dis4.getNameServers().add(addr.getHostAddress()); + } + } + WindowsDNSManager.setInterfaceDnsSettings(guid, dis); + WindowsDNSManager.setInterfaceDnsSettings(guid, dis4); + } + + @Override + public List getDNSAddress() throws SocketException { + DnsInterfaceSettings dis=new DnsInterfaceSettings(); + dis.setFlags(WindowsDNSManager.DNS_SETTING_IPV6); + dis.setVersion(WindowsDNSManager.DNS_INTERFACE_SETTINGS_VERSION1); + dis=WindowsDNSManager.getInterfaceDnsSettings(guid, dis); + List strs=dis.getNameServers(); + + DnsInterfaceSettings dis4=new DnsInterfaceSettings(); + dis4.setVersion(WindowsDNSManager.DNS_INTERFACE_SETTINGS_VERSION1); + dis4=WindowsDNSManager.getInterfaceDnsSettings(guid, dis4); + List strs4=dis4.getNameServers(); + + Listdnsaddrs=new ArrayList<>(); + for(String str:strs) { + try { + dnsaddrs.add(InetAddress.getByName(str)); + } catch (UnknownHostException e) { + e.printStackTrace(); + } + } + for(String str:strs4) { + try { + dnsaddrs.add(InetAddress.getByName(str)); + } catch (UnknownHostException e) { + e.printStackTrace(); + } + } + return dnsaddrs; + } + WindowsTUNChannel channel; + + @Override + public TUNChannel openSession() { + if(channel!=null&&channel.isOpen()) { + throw new IllegalStateException("Session already created!"); + } + WintunSessionHandle session=WintunDriver.startSession(adapter, WintunDriver.WINTUN_MAX_RING_CAPACITY); + channel=new WindowsTUNChannel(session); + return channel; + } + + @Override + public List getIPAddress() throws IOException { + List result=new ArrayList<>(); + Listinfo= WindowsIPAddressManager.getUnicastIpAddressTable(AddressFamily.AF_UNSPEC); + for(UnicastIPAddressRow cir:info) { + InetAddressRow rowc=new InetAddressRow(cir.getAddress(), cir.getOnLinkPrefixLength()); + result.add(rowc); + } + return result; + } + + @Override + public void setIPAddress(List rows) throws IOException { + List rowsinc=new ArrayList<>(rows); + Listinfo= WindowsIPAddressManager.getUnicastIpAddressTable(AddressFamily.AF_UNSPEC); + for(UnicastIPAddressRow cir:info) { + if(cir.getInterfaceLuid()!=luid) + continue; + InetAddressRow rowc=new InetAddressRow(cir.getAddress(), cir.getOnLinkPrefixLength()); + if(!rows.contains(rowc)) { + WindowsIPAddressManager.deleteUnicastIpAddressEntry(cir); + }else { + rowsinc.remove(rowc); + } + } + for(InetAddressRow row:rowsinc) { + InetAddress addr=row.getAddress(); + WindowsIPAddressManager.addIpAddressToAdapter(luid, addr, row.getPerfix()); + } + } + +} + diff --git a/src/org/kne/cloud/network/tun/WindowsWaitHandle.java b/src/org/kne/cloud/network/tun/WindowsWaitHandle.java new file mode 100644 index 0000000..fd78f07 --- /dev/null +++ b/src/org/kne/cloud/network/tun/WindowsWaitHandle.java @@ -0,0 +1,166 @@ +package org.kne.cloud.network.tun; +import java.lang.foreign.*; +import java.lang.invoke.MethodHandle; +import java.time.Duration; + +/** + * Windows WaitForSingleObject 函数封装 + * 专用于等待 WintunGetReadWaitEvent 返回的事件句柄 + */ +public class WindowsWaitHandle { + + // 常量定义 + public static final long INFINITE = 0xFFFFFFFFL; // 无限等待 + public static final long WAIT_OBJECT_0 = 0x00000000L; // 事件已发出信号 + public static final long WAIT_TIMEOUT = 0x00000102L; // 超时 + public static final long WAIT_FAILED = 0xFFFFFFFFL; // 失败 + + // 内存布局 + public static final AddressLayout POINTER = ValueLayout.ADDRESS; + public static final ValueLayout.OfInt DWORD = ValueLayout.JAVA_INT; + public static final ValueLayout.OfLong DWORD64 = ValueLayout.JAVA_LONG; + + // Native库加载 + private static final SymbolLookup LOOKUP; + private static final Linker LINKER = Linker.nativeLinker(); + + // 方法句柄 + private static final MethodHandle WAIT_FOR_SINGLE_OBJECT; + + static { + try { + // 加载 kernel32.dll + System.loadLibrary("kernel32"); + LOOKUP = SymbolLookup.loaderLookup(); + + // 定义函数描述符 + // DWORD WINAPI WaitForSingleObject( + // _In_ HANDLE hHandle, + // _In_ DWORD dwMilliseconds + // ); + FunctionDescriptor waitForSingleObjectDesc = FunctionDescriptor.of( + DWORD, // 返回值: DWORD + POINTER, // hHandle: HANDLE + DWORD // dwMilliseconds: DWORD + ); + + // 获取函数句柄 + WAIT_FOR_SINGLE_OBJECT = LINKER.downcallHandle( + LOOKUP.find("WaitForSingleObject").orElseThrow(), + waitForSingleObjectDesc + ); + + } catch (Throwable e) { + throw new RuntimeException("Failed to initialize WindowsWaitHandle", e); + } + } + + /** + * 等待单个对象(事件句柄) + * + * @param handle 要等待的事件句柄(MemorySegment) + * @param timeoutMillis 超时时间(毫秒),0表示立即返回,INFINITE表示无限等待 + * @return 等待结果:WAIT_OBJECT_0, WAIT_TIMEOUT, 或 WAIT_FAILED + */ + public static long waitForSingleObject(MemorySegment handle, long timeoutMillis) { + try { + int timeout; + if (timeoutMillis == INFINITE) { + timeout = (int)INFINITE; // 0xFFFFFFFF + } else if (timeoutMillis > 0xFFFFFFFFL) { + timeout = (int)INFINITE; // 超过DWORD最大值,使用INFINITE + } else { + timeout = (int)timeoutMillis; + } + + int result = (int) WAIT_FOR_SINGLE_OBJECT.invokeExact( + handle, + timeout + ); + + return result & 0xFFFFFFFFL; // 无符号转换 + } catch (Throwable e) { + throw new RuntimeException("Error calling WaitForSingleObject", e); + } + } + + /** + * 等待事件(使用Duration) + */ + public static long waitForSingleObject(MemorySegment handle, Duration timeout) { + if (timeout == null) { + return waitForSingleObject(handle, INFINITE); + } + + long millis = timeout.toMillis(); + if (millis <= 0) { + return waitForSingleObject(handle, 0); // 立即返回 + } + + return waitForSingleObject(handle, millis); + } + + /** + * 无限等待事件 + */ + public static long waitForSingleObjectInfinite(MemorySegment handle) { + return waitForSingleObject(handle, INFINITE); + } + + /** + * 非阻塞检查事件(立即返回) + */ + public static long waitForSingleObjectNoWait(MemorySegment handle) { + return waitForSingleObject(handle, 0); + } + + /** + * 等待事件,带重试机制 + */ + public static boolean waitWithRetry(MemorySegment handle, int maxRetries, long timeoutPerRetry) { + for (int i = 0; i < maxRetries; i++) { + long result = waitForSingleObject(handle, timeoutPerRetry); + + if (result == WAIT_OBJECT_0) { + return true; // 事件已发出信号 + } else if (result == WAIT_TIMEOUT) { + // 超时,继续重试 + continue; + } else if (result == WAIT_FAILED) { + // 失败,停止重试 + break; + } + } + return false; + } + + /** + * 检查等待结果 + */ + public static boolean isWaitSuccess(long waitResult) { + return waitResult == WAIT_OBJECT_0; + } + + public static boolean isWaitTimeout(long waitResult) { + return waitResult == WAIT_TIMEOUT; + } + + public static boolean isWaitFailed(long waitResult) { + return waitResult == WAIT_FAILED; + } + + /** + * 获取等待结果的描述 + */ + public static String getWaitResultDescription(long waitResult) { + if (waitResult == WAIT_OBJECT_0) { + return "Event signaled (WAIT_OBJECT_0)"; + } else if (waitResult == WAIT_TIMEOUT) { + return "Timeout (WAIT_TIMEOUT)"; + } else if (waitResult == WAIT_FAILED) { + return "Failed (WAIT_FAILED)"; + } else { + return String.format("Unknown result: 0x%08X", waitResult); + } + } +} \ No newline at end of file diff --git a/src/org/kne/cloud/network/tun/WintunDriver.java b/src/org/kne/cloud/network/tun/WintunDriver.java new file mode 100644 index 0000000..955f825 --- /dev/null +++ b/src/org/kne/cloud/network/tun/WintunDriver.java @@ -0,0 +1,564 @@ +package org.kne.cloud.network.tun; +import java.lang.foreign.*; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import org.kne.cloud.network.tun.WindowsDNSManager.DnsInterfaceSettings; +import org.kne.cloud.network.tun.WindowsInterfaceConverter.GUID; +public class WintunDriver { + + // Memory layouts matching C types + public static final ValueLayout.OfInt DWORD = ValueLayout.JAVA_INT; + public static final ValueLayout.OfLong DWORD64 = ValueLayout.JAVA_LONG; + public static final AddressLayout POINTER = ValueLayout.ADDRESS; + public static final ValueLayout.OfByte BYTE = ValueLayout.JAVA_BYTE; + public static final ValueLayout.OfShort WORD = ValueLayout.JAVA_SHORT; + + // Constants from the header + public static final int WINTUN_MIN_RING_CAPACITY = 0x20000; // 128kiB + public static final int WINTUN_MAX_RING_CAPACITY = 0x4000000; // 64MiB + public static final int WINTUN_MAX_IP_PACKET_SIZE = 0xFFFF; + + // Opaque handle types (represented as MemorySegment) + public static class WintunAdapterHandle extends NativeHandle { + WintunAdapterHandle(MemorySegment address) { + super(address); + } + } + + public static class WintunSessionHandle extends NativeHandle { + WintunSessionHandle(MemorySegment address) { + super(address); + } + } + + // Native library loading + private static final SymbolLookup LOOKUP; + private static final Linker LINKER = Linker.nativeLinker(); + + static { + // Load wintun.dll from system or current directory + System.loadLibrary("wintun"); + LOOKUP = SymbolLookup.loaderLookup(); + } + + // Native function handles + private static final MethodHandle WINTUN_CREATE_ADAPTER; + private static final MethodHandle WINTUN_OPEN_ADAPTER; + private static final MethodHandle WINTUN_CLOSE_ADAPTER; + private static final MethodHandle WINTUN_DELETE_DRIVER; + private static final MethodHandle WINTUN_GET_ADAPTER_LUID; + private static final MethodHandle WINTUN_GET_RUNNING_DRIVER_VERSION; + private static final MethodHandle WINTUN_SET_LOGGER; + private static final MethodHandle WINTUN_START_SESSION; + private static final MethodHandle WINTUN_END_SESSION; + private static final MethodHandle WINTUN_GET_READ_WAIT_EVENT; + private static final MethodHandle WINTUN_RECEIVE_PACKET; + private static final MethodHandle WINTUN_RELEASE_RECEIVE_PACKET; + private static final MethodHandle WINTUN_ALLOCATE_SEND_PACKET; + private static final MethodHandle WINTUN_SEND_PACKET; + + static { + try { + // Define function descriptors matching the C signatures + FunctionDescriptor createAdapterDesc = FunctionDescriptor.of( + POINTER, // return: WINTUN_ADAPTER_HANDLE + POINTER, // Name: LPCWSTR + POINTER, // TunnelType: LPCWSTR + POINTER // RequestedGUID: const GUID* + ); + + FunctionDescriptor openAdapterDesc = FunctionDescriptor.of( + POINTER, // return: WINTUN_ADAPTER_HANDLE + POINTER // Name: LPCWSTR + ); + + FunctionDescriptor closeAdapterDesc = FunctionDescriptor.ofVoid( + POINTER // Adapter: WINTUN_ADAPTER_HANDLE + ); + + // 修正这里:去掉额外的VOID参数 + FunctionDescriptor deleteDriverDesc = FunctionDescriptor.of( + ValueLayout.JAVA_INT // BOOL + ); + + FunctionDescriptor getAdapterLuidDesc = FunctionDescriptor.ofVoid( + POINTER, // Adapter: WINTUN_ADAPTER_HANDLE + POINTER // Luid: NET_LUID* + ); + + // 修正这里:去掉额外的VOID参数 + FunctionDescriptor getDriverVersionDesc = FunctionDescriptor.of( + DWORD // DWORD + ); + + FunctionDescriptor setLoggerDesc = FunctionDescriptor.ofVoid( + POINTER // NewLogger: WINTUN_LOGGER_CALLBACK + ); + + FunctionDescriptor startSessionDesc = FunctionDescriptor.of( + POINTER, // return: WINTUN_SESSION_HANDLE + POINTER, // Adapter: WINTUN_ADAPTER_HANDLE + DWORD // Capacity: DWORD + ); + + FunctionDescriptor endSessionDesc = FunctionDescriptor.ofVoid( + POINTER // Session: WINTUN_SESSION_HANDLE + ); + + FunctionDescriptor getReadWaitEventDesc = FunctionDescriptor.of( + POINTER, // HANDLE + POINTER // Session: WINTUN_SESSION_HANDLE + ); + + FunctionDescriptor receivePacketDesc = FunctionDescriptor.of( + POINTER, // return: BYTE* + POINTER, // Session: WINTUN_SESSION_HANDLE + POINTER // PacketSize: DWORD* + ); + + FunctionDescriptor releaseReceivePacketDesc = FunctionDescriptor.ofVoid( + POINTER, // Session: WINTUN_SESSION_HANDLE + POINTER // Packet: const BYTE* + ); + + FunctionDescriptor allocateSendPacketDesc = FunctionDescriptor.of( + POINTER, // return: BYTE* + POINTER, // Session: WINTUN_SESSION_HANDLE + DWORD // PacketSize: DWORD + ); + + FunctionDescriptor sendPacketDesc = FunctionDescriptor.ofVoid( + POINTER, // Session: WINTUN_SESSION_HANDLE + POINTER // Packet: const BYTE* + ); + + // Look up the symbols + WINTUN_CREATE_ADAPTER = LINKER.downcallHandle( + LOOKUP.find("WintunCreateAdapter").orElseThrow(), + createAdapterDesc + ); + + WINTUN_OPEN_ADAPTER = LINKER.downcallHandle( + LOOKUP.find("WintunOpenAdapter").orElseThrow(), + openAdapterDesc + ); + + WINTUN_CLOSE_ADAPTER = LINKER.downcallHandle( + LOOKUP.find("WintunCloseAdapter").orElseThrow(), + closeAdapterDesc + ); + + WINTUN_DELETE_DRIVER = LINKER.downcallHandle( + LOOKUP.find("WintunDeleteDriver").orElseThrow(), + deleteDriverDesc + ); + + WINTUN_GET_ADAPTER_LUID = LINKER.downcallHandle( + LOOKUP.find("WintunGetAdapterLUID").orElseThrow(), + getAdapterLuidDesc + ); + + WINTUN_GET_RUNNING_DRIVER_VERSION = LINKER.downcallHandle( + LOOKUP.find("WintunGetRunningDriverVersion").orElseThrow(), + getDriverVersionDesc + ); + + WINTUN_SET_LOGGER = LINKER.downcallHandle( + LOOKUP.find("WintunSetLogger").orElseThrow(), + setLoggerDesc + ); + + WINTUN_START_SESSION = LINKER.downcallHandle( + LOOKUP.find("WintunStartSession").orElseThrow(), + startSessionDesc + ); + + WINTUN_END_SESSION = LINKER.downcallHandle( + LOOKUP.find("WintunEndSession").orElseThrow(), + endSessionDesc + ); + + WINTUN_GET_READ_WAIT_EVENT = LINKER.downcallHandle( + LOOKUP.find("WintunGetReadWaitEvent").orElseThrow(), + getReadWaitEventDesc + ); + + WINTUN_RECEIVE_PACKET = LINKER.downcallHandle( + LOOKUP.find("WintunReceivePacket").orElseThrow(), + receivePacketDesc + ); + + WINTUN_RELEASE_RECEIVE_PACKET = LINKER.downcallHandle( + LOOKUP.find("WintunReleaseReceivePacket").orElseThrow(), + releaseReceivePacketDesc + ); + + WINTUN_ALLOCATE_SEND_PACKET = LINKER.downcallHandle( + LOOKUP.find("WintunAllocateSendPacket").orElseThrow(), + allocateSendPacketDesc + ); + + WINTUN_SEND_PACKET = LINKER.downcallHandle( + LOOKUP.find("WintunSendPacket").orElseThrow(), + sendPacketDesc + ); + + } catch (Throwable e) { + throw new RuntimeException("Failed to initialize native function handles", e); + } + } + + // Helper method to convert Java String to Windows wide string (UTF-16 LE) + private static MemorySegment toWideString(String str, Arena arena) { + if (str == null) return MemorySegment.NULL; + + // Convert to UTF-16LE bytes with null terminator + byte[] utf16Bytes = str.getBytes(java.nio.charset.StandardCharsets.UTF_16LE); + MemorySegment segment = arena.allocate(utf16Bytes.length + 2); + + // 使用 copyFrom 替代原来的 copy 方法 + // 注意参数顺序:源数组 -> 目标MemorySegment + segment.copyFrom(MemorySegment.ofArray(utf16Bytes)); + + // 添加 null 终止符 (两个字节) + segment.set(ValueLayout.JAVA_BYTE, utf16Bytes.length, (byte)0); + segment.set(ValueLayout.JAVA_BYTE, utf16Bytes.length + 1, (byte)0); + + return segment; + } + + // Java API methods + + public static WintunAdapterHandle createAdapter(String name, String tunnelType, MemorySegment requestedGuid) { + try (Arena arena = Arena.ofConfined()) { + MemorySegment nameSegment = toWideString(name, arena); + MemorySegment tunnelTypeSegment = toWideString(tunnelType, arena); + + MemorySegment result = (MemorySegment) WINTUN_CREATE_ADAPTER.invokeExact( + nameSegment, + tunnelTypeSegment, + (MemorySegment)( requestedGuid != null ? requestedGuid : MemorySegment.NULL) + ); + + if (result.equals(MemorySegment.NULL)) { + throw new WindowsAPIException("Failed to create adapter"); + } + + return new WintunAdapterHandle(result); + } catch (Throwable e) { + throw new RuntimeException("Error calling WintunCreateAdapter", e); + } + } + + public static WintunAdapterHandle openAdapter(String name) { + try (Arena arena = Arena.ofConfined()) { + MemorySegment nameSegment = toWideString(name, arena); + + MemorySegment result = (MemorySegment) WINTUN_OPEN_ADAPTER.invokeExact(nameSegment); + + if (result.equals(MemorySegment.NULL)) { + throw new WindowsAPIException("Failed to open adapter"); + } + + return new WintunAdapterHandle(result); + } catch (Throwable e) { + throw new RuntimeException("Error calling WintunOpenAdapter", e); + } + } + + public static void closeAdapter(WintunAdapterHandle adapter) { + try { + WINTUN_CLOSE_ADAPTER.invokeExact(adapter.address()); + } catch (Throwable e) { + throw new RuntimeException("Error calling WintunCloseAdapter", e); + } + } + + public static boolean deleteDriver() { + try { + int result = (int) WINTUN_DELETE_DRIVER.invokeExact(); + return result != 0; + } catch (Throwable e) { + throw new RuntimeException("Error calling WintunDeleteDriver", e); + } + } + + public static long getAdapterLuid(WintunAdapterHandle adapter) { + // 直接在全局Arena中分配 + + try ( Arena ara=Arena.ofConfined()){ + MemorySegment luidm = ara.allocate(ValueLayout.JAVA_LONG); + WINTUN_GET_ADAPTER_LUID.invokeExact(adapter.address(), luidm); + return luidm.get(ValueLayout.JAVA_LONG, 0); + } catch (Throwable e) { + throw new RuntimeException("Error calling WintunGetAdapterLuid", e); + } + } + + public static int getRunningDriverVersion() { + try { + return (int) WINTUN_GET_RUNNING_DRIVER_VERSION.invokeExact(); + } catch (Throwable e) { + throw new RuntimeException("Error calling WintunGetRunningDriverVersion", e); + } + } + + public static void setLogger(MemorySegment loggerCallback) { + try { + WINTUN_SET_LOGGER.invokeExact(loggerCallback); + } catch (Throwable e) { + throw new RuntimeException("Error calling WintunSetLogger", e); + } + } + + public static WintunSessionHandle startSession(WintunAdapterHandle adapter, int capacity) { + try { + if (capacity < WINTUN_MIN_RING_CAPACITY || capacity > WINTUN_MAX_RING_CAPACITY) { + throw new IllegalArgumentException( + String.format("Capacity must be between %d and %d", + WINTUN_MIN_RING_CAPACITY, WINTUN_MAX_RING_CAPACITY) + ); + } + + // Check if capacity is power of two + if ((capacity & (capacity - 1)) != 0) { + throw new IllegalArgumentException("Capacity must be a power of two"); + } + + MemorySegment result = (MemorySegment) WINTUN_START_SESSION.invokeExact( + adapter.address(), + capacity + ); + + if (result.equals(MemorySegment.NULL)) { + throw new WindowsAPIException("Failed to start session"); + } + + return new WintunSessionHandle(result); + } catch (Throwable e) { + throw new RuntimeException("Error calling WintunStartSession", e); + } + } + + public static void endSession(WintunSessionHandle session) { + try { + WINTUN_END_SESSION.invokeExact(session.address()); + } catch (Throwable e) { + throw new RuntimeException("Error calling WintunEndSession", e); + } + } + + public static MemorySegment getReadWaitEvent(WintunSessionHandle session) { + try { + return (MemorySegment) WINTUN_GET_READ_WAIT_EVENT.invokeExact(session.address()); + } catch (Throwable e) { + throw new RuntimeException("Error calling WintunGetReadWaitEvent", e); + } + } + + public static Packet receivePacket(WintunSessionHandle session) { + try (Arena arena = Arena.ofConfined()) { + MemorySegment packetSizePtr = arena.allocate(DWORD); + + MemorySegment packetPtr = (MemorySegment) WINTUN_RECEIVE_PACKET.invokeExact( + session.address(), + packetSizePtr + ); + + if (packetPtr.equals(MemorySegment.NULL)) { + // Check for specific errors via GetLastError if needed + return null; + } + + int packetSize = packetSizePtr.get(DWORD, 0); + MemorySegment packetData = packetPtr.reinterpret(packetSize); + + return new Packet(session, packetData, packetSize); + } catch (Throwable e) { + throw new RuntimeException("Error calling WintunReceivePacket", e); + } + } + + public static SendPacket allocateSendPacket(WintunSessionHandle session, int packetSize) { + try { + if (packetSize > WINTUN_MAX_IP_PACKET_SIZE) { + throw new IllegalArgumentException( + String.format("Packet size exceeds maximum of %d", WINTUN_MAX_IP_PACKET_SIZE) + ); + } + + MemorySegment packetPtr = (MemorySegment) WINTUN_ALLOCATE_SEND_PACKET.invokeExact( + session.address(), + packetSize + ); + + if (packetPtr.equals(MemorySegment.NULL)) { + return null; + } + + MemorySegment packetData = packetPtr.reinterpret(packetSize); + return new SendPacket(session, packetData, packetSize); + } catch (Throwable e) { + throw new RuntimeException("Error calling WintunAllocateSendPacket", e); + } + } + + // Helper classes + + public static class NativeHandle { + private final MemorySegment address; + + protected NativeHandle(MemorySegment address) { + this.address = address; + } + + public MemorySegment address() { + return address; + } + } + + public static class Packet { + private final WintunSessionHandle session; + private final MemorySegment data; + private final int size; + + Packet(WintunSessionHandle session, MemorySegment data, int size) { + this.session = session; + this.data = data; + this.size = size; + } + + public MemorySegment data() { + return data; + } + + public int size() { + return size; + } + + public void release() { + try { + WINTUN_RELEASE_RECEIVE_PACKET.invokeExact(session.address(), data); + } catch (Throwable e) { + throw new RuntimeException("Error releasing packet", e); + } + } + } + + public static class SendPacket { + private final WintunSessionHandle session; + private final MemorySegment data; + private final int size; + + SendPacket(WintunSessionHandle session, MemorySegment data, int size) { + this.session = session; + this.data = data; + this.size = size; + } + + public MemorySegment data() { + return data; + } + + public int size() { + return size; + } + + public void send() { + try { + WINTUN_SEND_PACKET.invokeExact(session.address(), data); + } catch (Throwable e) { + throw new RuntimeException("Error sending packet", e); + } + } + } + + + + // Logger callback interface for Java + @FunctionalInterface + public interface LoggerCallback { + void log(int level, long timestamp, String message); + } + + // Create a native logger callback from Java interface + public static MemorySegment createLoggerCallback(LoggerCallback callback, Arena arena) { + return MemorySegment.ofAddress(0); // Implementation would use upcall stub + // Note: Full implementation requires creating upcall stub with LINKER.upcallStub + } + + // Example usage + public static void main(String[] args) { + try { + // Check driver version + int version = getRunningDriverVersion(); + System.out.println("Wintun driver version: " + version); + + // Create adapter + WintunAdapterHandle adapter = createAdapter("MyWintun", "Wintun", null); + + + + // Start session + WintunSessionHandle session = startSession(adapter, WINTUN_MIN_RING_CAPACITY); + + + + // Get adapter LUID + GUID guid=null; + long luid=getAdapterLuid(adapter); + System.out.println("Adapter LUID: " + luid); + guid=WindowsInterfaceConverter.convertLuidToGuid(luid); + System.out.println("Adapter GUID: "+guid); + MemorySegment wintunGuid=guid.toMemorySegment(Arena.ofAuto()); + + + + System.out.println("\n2. Setting DNS for Wintun adapter:"); + + DnsInterfaceSettings dis= new DnsInterfaceSettings(); + dis.setNameServers(List.of("8.8.8.8")); + dis.setFlags(WindowsDNSManager.DNS_SETTING_NAMESERVER); + WindowsDNSManager.setInterfaceDnsSettings(guid, dis); + System.out.println(dis); + + // Get read wait event (could be used with Java NIO Selector) + MemorySegment readEvent = getReadWaitEvent(session); + + try { + // Example packet loop + while (true) { + Packet packet = receivePacket(session); + if (packet != null) { + try { + // Process packet data + byte[] packetData = packet.data().toArray(ValueLayout.JAVA_BYTE); + System.out.println("Received packet size: " + packet.size()); + + // Create response packet + SendPacket sendPacket = allocateSendPacket(session, packet.size()); + if (sendPacket != null) { + // Copy data to send buffer - Java 23 方式 + sendPacket.data().copyFrom(MemorySegment.ofArray(packetData)); + sendPacket.send(); + } + } finally { + packet.release(); + } + } + } + } finally { + // Cleanup + endSession(session); + closeAdapter(adapter); + } + + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/org/kne/util/SystemInfo.java b/src/org/kne/util/SystemInfo.java new file mode 100644 index 0000000..67c8484 --- /dev/null +++ b/src/org/kne/util/SystemInfo.java @@ -0,0 +1,32 @@ +package org.kne.util; + +public class SystemInfo { + private String osName; + private String osArch; + private String osVersion; + public String getOsName() { + return osName; + } + public String getOsArch() { + return osArch; + } + + public String getOsVersion() { + return osVersion; + } + public SystemInfo(String osName, String osArch, String osVersion) { + super(); + this.osName = osName; + this.osArch = osArch; + this.osVersion = osVersion; + } + public SystemInfo() { + this.osName = System.getProperty("os.name"); + this.osArch = System.getProperty("os.arch"); + this.osVersion = System.getProperty("os.version"); + } + @Override + public String toString() { + return "SystemInfo [osName=" + osName + ", osArch=" + osArch + ", osVersion=" + osVersion + "]"; + } +} diff --git a/src/org_kne_cloud_network_tun_WindowsTUNNetworkDevice.h b/src/org_kne_cloud_network_tun_WindowsTUNNetworkDevice.h new file mode 100644 index 0000000..bfc0e9b --- /dev/null +++ b/src/org_kne_cloud_network_tun_WindowsTUNNetworkDevice.h @@ -0,0 +1,125 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class org_kne_cloud_network_tun_WindowsTUNNetworkDevice */ + +#ifndef _Included_org_kne_cloud_network_tun_WindowsTUNNetworkDevice +#define _Included_org_kne_cloud_network_tun_WindowsTUNNetworkDevice +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: loadDriver + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_loadDriver + (JNIEnv *, jclass); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: unloadDriver + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_unloadDriver + (JNIEnv *, jclass, jlong); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: openTUNDevice + * Signature: (Ljava/lang/String;Ljava/lang/String;JJ)J + */ +JNIEXPORT jlong JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_openTUNDevice + (JNIEnv *, jobject, jstring, jstring, jlong, jlong); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: closeTUNDevice + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_closeTUNDevice + (JNIEnv *, jobject, jlong); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: openSession + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_openSession + (JNIEnv *, jobject, jlong); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: closeSession + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_closeSession + (JNIEnv *, jobject, jlong); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: readAvaliable + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_readAvaliable + (JNIEnv *, jobject, jlong); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: waitForReadAvaliable + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_waitForReadAvaliable + (JNIEnv *, jobject, jlong); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: read0 + * Signature: (JLjava/nio/ByteBuffer;)I + */ +JNIEXPORT jint JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_read0 + (JNIEnv *, jobject, jlong, jobject); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: writeAvaliable + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_writeAvaliable + (JNIEnv *, jobject, jlong); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: write0 + * Signature: (JLjava/nio/ByteBuffer;)I + */ +JNIEXPORT jint JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_write0 + (JNIEnv *, jobject, jlong, jobject); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: setTUNDeviceIPAddressV6 + * Signature: (J[BI)I + */ +JNIEXPORT jint JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_setTUNDeviceIPAddressV6 + (JNIEnv *, jobject, jlong, jbyteArray, jint); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: getMTU0 + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_getMTU0 + (JNIEnv *, jobject, jlong); + +/* + * Class: org_kne_cloud_network_tun_WindowsTUNNetworkDevice + * Method: setMTU0 + * Signature: (JI)V + */ +JNIEXPORT void JNICALL Java_org_kne_cloud_network_tun_WindowsTUNNetworkDevice_setMTU0 + (JNIEnv *, jobject, jlong, jint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/tuntap4j.dll b/tuntap4j.dll new file mode 100644 index 0000000..b63010c Binary files /dev/null and b/tuntap4j.dll differ diff --git a/wintun.dll b/wintun.dll new file mode 100644 index 0000000..aee04e7 Binary files /dev/null and b/wintun.dll differ diff --git a/wintun.h b/wintun.h new file mode 100644 index 0000000..6b7e0da --- /dev/null +++ b/wintun.h @@ -0,0 +1,270 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT + * + * Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ALIGNED +# if defined(_MSC_VER) +# define ALIGNED(n) __declspec(align(n)) +# elif defined(__GNUC__) +# define ALIGNED(n) __attribute__((aligned(n))) +# else +# error "Unable to define ALIGNED" +# endif +#endif + +/* MinGW is missing this one, unfortunately. */ +#ifndef _Post_maybenull_ +# define _Post_maybenull_ +#endif + +#pragma warning(push) +#pragma warning(disable : 4324) /* structure was padded due to alignment specifier */ + +/** + * A handle representing Wintun adapter + */ +typedef struct _WINTUN_ADAPTER *WINTUN_ADAPTER_HANDLE; + +/** + * Creates a new Wintun adapter. + * + * @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1 + * characters. + * + * @param TunnelType Name of the adapter tunnel type. Zero-terminated string of up to MAX_ADAPTER_NAME-1 + * characters. + * + * @param RequestedGUID The GUID of the created network adapter, which then influences NLA generation deterministically. + * If it is set to NULL, the GUID is chosen by the system at random, and hence a new NLA entry is + * created for each new adapter. It is called "requested" GUID because the API it uses is + * completely undocumented, and so there could be minor interesting complications with its usage. + * + * @return If the function succeeds, the return value is the adapter handle. Must be released with + * WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call + * GetLastError. + */ +typedef _Must_inspect_result_ +_Return_type_success_(return != NULL) +_Post_maybenull_ +WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_CREATE_ADAPTER_FUNC) +(_In_z_ LPCWSTR Name, _In_z_ LPCWSTR TunnelType, _In_opt_ const GUID *RequestedGUID); + +/** + * Opens an existing Wintun adapter. + * + * @param Name The requested name of the adapter. Zero-terminated string of up to MAX_ADAPTER_NAME-1 + * characters. + * + * @return If the function succeeds, the return value is the adapter handle. Must be released with + * WintunCloseAdapter. If the function fails, the return value is NULL. To get extended error information, call + * GetLastError. + */ +typedef _Must_inspect_result_ +_Return_type_success_(return != NULL) +_Post_maybenull_ +WINTUN_ADAPTER_HANDLE(WINAPI WINTUN_OPEN_ADAPTER_FUNC)(_In_z_ LPCWSTR Name); + +/** + * Releases Wintun adapter resources and, if adapter was created with WintunCreateAdapter, removes adapter. + * + * @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter. + */ +typedef VOID(WINAPI WINTUN_CLOSE_ADAPTER_FUNC)(_In_opt_ WINTUN_ADAPTER_HANDLE Adapter); + +/** + * Deletes the Wintun driver if there are no more adapters in use. + * + * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To + * get extended error information, call GetLastError. + */ +typedef _Return_type_success_(return != FALSE) +BOOL(WINAPI WINTUN_DELETE_DRIVER_FUNC)(VOID); + +/** + * Returns the LUID of the adapter. + * + * @param Adapter Adapter handle obtained with WintunCreateAdapter or WintunOpenAdapter + * + * @param Luid Pointer to LUID to receive adapter LUID. + */ +typedef VOID(WINAPI WINTUN_GET_ADAPTER_LUID_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _Out_ NET_LUID *Luid); + +/** + * Determines the version of the Wintun driver currently loaded. + * + * @return If the function succeeds, the return value is the version number. If the function fails, the return value is + * zero. To get extended error information, call GetLastError. Possible errors include the following: + * ERROR_FILE_NOT_FOUND Wintun not loaded + */ +typedef _Return_type_success_(return != 0) +DWORD(WINAPI WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC)(VOID); + +/** + * Determines the level of logging, passed to WINTUN_LOGGER_CALLBACK. + */ +typedef enum +{ + WINTUN_LOG_INFO, /**< Informational */ + WINTUN_LOG_WARN, /**< Warning */ + WINTUN_LOG_ERR /**< Error */ +} WINTUN_LOGGER_LEVEL; + +/** + * Called by internal logger to report diagnostic messages + * + * @param Level Message level. + * + * @param Timestamp Message timestamp in in 100ns intervals since 1601-01-01 UTC. + * + * @param Message Message text. + */ +typedef VOID(CALLBACK *WINTUN_LOGGER_CALLBACK)( + _In_ WINTUN_LOGGER_LEVEL Level, + _In_ DWORD64 Timestamp, + _In_z_ LPCWSTR Message); + +/** + * Sets logger callback function. + * + * @param NewLogger Pointer to callback function to use as a new global logger. NewLogger may be called from various + * threads concurrently. Should the logging require serialization, you must handle serialization in + * NewLogger. Set to NULL to disable. + */ +typedef VOID(WINAPI WINTUN_SET_LOGGER_FUNC)(_In_ WINTUN_LOGGER_CALLBACK NewLogger); + +/** + * Minimum ring capacity. + */ +#define WINTUN_MIN_RING_CAPACITY 0x20000 /* 128kiB */ + +/** + * Maximum ring capacity. + */ +#define WINTUN_MAX_RING_CAPACITY 0x4000000 /* 64MiB */ + +/** + * A handle representing Wintun session + */ +typedef struct _TUN_SESSION *WINTUN_SESSION_HANDLE; + +/** + * Starts Wintun session. + * + * @param Adapter Adapter handle obtained with WintunOpenAdapter or WintunCreateAdapter + * + * @param Capacity Rings capacity. Must be between WINTUN_MIN_RING_CAPACITY and WINTUN_MAX_RING_CAPACITY (incl.) + * Must be a power of two. + * + * @return Wintun session handle. Must be released with WintunEndSession. If the function fails, the return value is + * NULL. To get extended error information, call GetLastError. + */ +typedef _Must_inspect_result_ +_Return_type_success_(return != NULL) +_Post_maybenull_ +WINTUN_SESSION_HANDLE(WINAPI WINTUN_START_SESSION_FUNC)(_In_ WINTUN_ADAPTER_HANDLE Adapter, _In_ DWORD Capacity); + +/** + * Ends Wintun session. + * + * @param Session Wintun session handle obtained with WintunStartSession + */ +typedef VOID(WINAPI WINTUN_END_SESSION_FUNC)(_In_ WINTUN_SESSION_HANDLE Session); + +/** + * Gets Wintun session's read-wait event handle. + * + * @param Session Wintun session handle obtained with WintunStartSession + * + * @return Pointer to receive event handle to wait for available data when reading. Should + * WintunReceivePackets return ERROR_NO_MORE_ITEMS (after spinning on it for a while under heavy + * load), wait for this event to become signaled before retrying WintunReceivePackets. Do not call + * CloseHandle on this event - it is managed by the session. + */ +typedef HANDLE(WINAPI WINTUN_GET_READ_WAIT_EVENT_FUNC)(_In_ WINTUN_SESSION_HANDLE Session); + +/** + * Maximum IP packet size + */ +#define WINTUN_MAX_IP_PACKET_SIZE 0xFFFF + +/** + * Retrieves one or packet. After the packet content is consumed, call WintunReleaseReceivePacket with Packet returned + * from this function to release internal buffer. This function is thread-safe. + * + * @param Session Wintun session handle obtained with WintunStartSession + * + * @param PacketSize Pointer to receive packet size. + * + * @return Pointer to layer 3 IPv4 or IPv6 packet. Client may modify its content at will. If the function fails, the + * return value is NULL. To get extended error information, call GetLastError. Possible errors include the + * following: + * ERROR_HANDLE_EOF Wintun adapter is terminating; + * ERROR_NO_MORE_ITEMS Wintun buffer is exhausted; + * ERROR_INVALID_DATA Wintun buffer is corrupt + */ +typedef _Must_inspect_result_ +_Return_type_success_(return != NULL) +_Post_maybenull_ +_Post_writable_byte_size_(*PacketSize) +BYTE *(WINAPI WINTUN_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _Out_ DWORD *PacketSize); + +/** + * Releases internal buffer after the received packet has been processed by the client. This function is thread-safe. + * + * @param Session Wintun session handle obtained with WintunStartSession + * + * @param Packet Packet obtained with WintunReceivePacket + */ +typedef VOID( + WINAPI WINTUN_RELEASE_RECEIVE_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet); + +/** + * Allocates memory for a packet to send. After the memory is filled with packet data, call WintunSendPacket to send + * and release internal buffer. WintunAllocateSendPacket is thread-safe and the WintunAllocateSendPacket order of + * calls define the packet sending order. + * + * @param Session Wintun session handle obtained with WintunStartSession + * + * @param PacketSize Exact packet size. Must be less or equal to WINTUN_MAX_IP_PACKET_SIZE. + * + * @return Returns pointer to memory where to prepare layer 3 IPv4 or IPv6 packet for sending. If the function fails, + * the return value is NULL. To get extended error information, call GetLastError. Possible errors include the + * following: + * ERROR_HANDLE_EOF Wintun adapter is terminating; + * ERROR_BUFFER_OVERFLOW Wintun buffer is full; + */ +typedef _Must_inspect_result_ +_Return_type_success_(return != NULL) +_Post_maybenull_ +_Post_writable_byte_size_(PacketSize) +BYTE *(WINAPI WINTUN_ALLOCATE_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ DWORD PacketSize); + +/** + * Sends the packet and releases internal buffer. WintunSendPacket is thread-safe, but the WintunAllocateSendPacket + * order of calls define the packet sending order. This means the packet is not guaranteed to be sent in the + * WintunSendPacket yet. + * + * @param Session Wintun session handle obtained with WintunStartSession + * + * @param Packet Packet obtained with WintunAllocateSendPacket + */ +typedef VOID(WINAPI WINTUN_SEND_PACKET_FUNC)(_In_ WINTUN_SESSION_HANDLE Session, _In_ const BYTE *Packet); + +#pragma warning(pop) + +#ifdef __cplusplus +} +#endif