Java FFM的TUN虚拟网卡封装
This commit is contained in:
+19
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/graalvm-jdk-25.0.1+8.1">
|
||||
<attributes>
|
||||
<attribute name="module" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="lib" path="lib/pcap4j-core-1.8.2.jar"/>
|
||||
<classpathentry kind="lib" path="lib/pcap4j-distribution-1.8.2-src.zip"/>
|
||||
<classpathentry kind="lib" path="lib/pcap4j-packetfactory-propertiesbased-1.8.2.jar"/>
|
||||
<classpathentry kind="lib" path="lib/pcap4j-packetfactory-static-1.8.2.jar"/>
|
||||
<classpathentry kind="lib" path="lib/pcap4j-packettest-1.8.2-tests.jar"/>
|
||||
<classpathentry kind="lib" path="lib/pcap4j-sample-1.8.2.jar"/>
|
||||
<classpathentry kind="lib" path="lib/slf4j-api-2.0.9.jar"/>
|
||||
<classpathentry kind="lib" path="lib/slf4j-api-2.0.9-javadoc.jar"/>
|
||||
<classpathentry kind="lib" path="lib/slf4j-api-2.0.9-sources.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>JavaTUN</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -0,0 +1,2 @@
|
||||
eclipse.preferences.version=1
|
||||
encoding/<project>=UTF-8
|
||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,125 @@
|
||||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||
#include <jni.h>
|
||||
/* 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
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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<String> getAvailableTunDevices() throws IOException;
|
||||
|
||||
protected static native List<String> getAvailableTapDevices() throws IOException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package one.papachi.tuntap4j;
|
||||
|
||||
public class TapDevice extends NetworkDevice {
|
||||
|
||||
public TapDevice(String deviceName) {
|
||||
super(deviceName, Type.TAP);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package one.papachi.tuntap4j;
|
||||
|
||||
public class TunDevice extends NetworkDevice {
|
||||
|
||||
public TunDevice(String deviceName) {
|
||||
super(deviceName, Type.TUN);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<InetAddressRow> getIPAddress() throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIPAddress(List<InetAddressRow> 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<InetAddress> address) throws IOException {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InetAddress> getDNSAddress() throws SocketException {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
@@ -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;
|
||||
|
||||
}
|
||||
Binary file not shown.
@@ -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<InetAddressRow> getIPAddress() throws IOException;//查询网卡IP和掩码
|
||||
|
||||
public abstract void setIPAddress(List<InetAddressRow> 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<InetAddress> address) throws IOException ;//设置DNS服务器地址
|
||||
|
||||
public abstract List<InetAddress> getDNSAddress()throws SocketException;
|
||||
|
||||
}
|
||||
@@ -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<InetAddress> 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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> nameServers;
|
||||
private List<String> searchList;
|
||||
private boolean registrationEnabled;
|
||||
private boolean registerAdapterName;
|
||||
private boolean enableLLMNR;
|
||||
private boolean queryAdapterName;
|
||||
private String profileNameServer;
|
||||
private boolean disableUnconstrainedQueries;
|
||||
private List<String> 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<String> getNameServers() { return nameServers; }
|
||||
public void setNameServers(List<String> nameServers) { this.nameServers = nameServers; }
|
||||
|
||||
public List<String> getSearchList() { return searchList; }
|
||||
public void setSearchList(List<String> 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<String> getSupplementalSearchList() { return supplementalSearchList; }
|
||||
public void setSupplementalSearchList(List<String> 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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<UnicastIPAddressRow> 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<UnicastIPAddressRow> 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<UnicastIPAddressRow> getIpAddressesByInterface(long interfaceLuid) throws WindowsAPIException {
|
||||
List<UnicastIPAddressRow> allAddresses = new ArrayList<>();
|
||||
|
||||
// 获取IPv4地址
|
||||
try {
|
||||
List<UnicastIPAddressRow> ipv4Addresses = getUnicastIpAddressTable(AddressFamily.AF_INET);
|
||||
allAddresses.addAll(ipv4Addresses.stream().filter(row -> row.getInterfaceLuid() == interfaceLuid)
|
||||
.collect(Collectors.toList()));
|
||||
} catch (Exception e) {
|
||||
// 忽略错误,继续获取IPv6地址
|
||||
}
|
||||
|
||||
// 获取IPv6地址
|
||||
try {
|
||||
List<UnicastIPAddressRow> 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<UnicastIPAddressRow> ipv4Addresses = getUnicastIpAddressTable(AddressFamily.AF_INET);
|
||||
for (UnicastIPAddressRow row : ipv4Addresses) {
|
||||
System.out.println(" " + row);
|
||||
}
|
||||
|
||||
// 获取所有IPv6地址
|
||||
System.out.println("\nIPv6 Addresses:");
|
||||
List<UnicastIPAddressRow> 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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<LUID, GUID> getAllNetworkInterfaces() {
|
||||
// 注意:这个函数需要更多的 Windows API 调用
|
||||
// 这里只是示例,实际实现需要调用 GetAdaptersAddresses 等函数
|
||||
java.util.Map<LUID, GUID> 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();
|
||||
}
|
||||
}
|
||||
@@ -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<MibIfRow> getAllInterfaces(boolean order) throws WindowsAPIException {
|
||||
MemorySegment ifTable = null;
|
||||
try {
|
||||
// 获取接口表
|
||||
ifTable = getIfTable(order);
|
||||
|
||||
// 读取表头
|
||||
int numEntries = ifTable.get(DWORD, 0);
|
||||
MemorySegment tablePtr = ifTable.asSlice(8);
|
||||
|
||||
List<MibIfRow> 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<MibIfRow> 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<MibIfRow> 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<MibIfRow> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
@@ -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<InetAddress> 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<String>());
|
||||
|
||||
DnsInterfaceSettings dis4=new DnsInterfaceSettings();
|
||||
dis4.setVersion(WindowsDNSManager.DNS_INTERFACE_SETTINGS_VERSION1);
|
||||
dis4.setFlags(WindowsDNSManager.DNS_SETTING_NAMESERVER);
|
||||
dis4.setNameServers(new ArrayList<String>());
|
||||
|
||||
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<InetAddress> 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<String> strs=dis.getNameServers();
|
||||
|
||||
DnsInterfaceSettings dis4=new DnsInterfaceSettings();
|
||||
dis4.setVersion(WindowsDNSManager.DNS_INTERFACE_SETTINGS_VERSION1);
|
||||
dis4=WindowsDNSManager.getInterfaceDnsSettings(guid, dis4);
|
||||
List<String> strs4=dis4.getNameServers();
|
||||
|
||||
List<InetAddress>dnsaddrs=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<InetAddressRow> getIPAddress() throws IOException {
|
||||
List<InetAddressRow> result=new ArrayList<>();
|
||||
List<UnicastIPAddressRow>info= 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<InetAddressRow> rows) throws IOException {
|
||||
List<InetAddressRow> rowsinc=new ArrayList<>(rows);
|
||||
List<UnicastIPAddressRow>info= 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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||
#include <jni.h>
|
||||
/* 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
|
||||
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,270 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
*
|
||||
* Copyright (C) 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <ipexport.h>
|
||||
#include <ifdef.h>
|
||||
#include <ws2ipdef.h>
|
||||
|
||||
#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
|
||||
Reference in New Issue
Block a user