Files
KLALB/src/org/kne/cloud/network/klalb/KLALBUtils.java
T
SerinaNya 455139dfe9 feat: 新增设备信息与额外路由设置,支持设备名广播及TUN显式禁用
- 设置页新增设备名称(单行)、设备描述(多行)、额外路由(CIDR列表)设置项
- RouterInfo 广播携带 deviceName,并在拓扑图节点与节点概览中展示;deviceDescription 保持本地概览显示
- 拓扑图节点标签支持多行文本绘制
- 节点概览面板新增 IP 地址及设备详情展示
- 支持将 TUNName 设为 null/"null"/空字符串时完全跳过 Wintun 虚拟网卡创建
- .classpath 容器改为标准 JavaSE-25 执行环境以同时兼容 JDK 25 与 26
- 新增 AGENTS.md 与 VS Code 调试及运行配置

BREAKING CHANGE: RouterInfo 在序列化末尾追加了 deviceName 字段(writeUTF),新旧版本节点混连时路由协议解析异常,需同步升级
2026-08-21 23:34:41 +08:00

441 lines
14 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package org.kne.cloud.network.klalb;
import java.awt.Color;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.SecureRandom;
import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jctools.counters.CountersFactory;
import org.jctools.counters.FixedSizeStripedLongCounter;
import org.kne.cloud.network.MultiProtocolSocketAddress;
import org.kne.cloud.network.ipv6.IPv6Address;
public class KLALBUtils {
public static Inet6Address uuidToIP(UUID uuid) {
ByteArrayOutputStream bos=new ByteArrayOutputStream(8);
DataOutputStream dos=new DataOutputStream(bos);
try {
long l=uuid.getMostSignificantBits();
l=(l&0x0000FFFFFFFFFFFFL)|0x2486000000000000L;
dos.writeLong(l);
dos.writeLong(uuid.getLeastSignificantBits());
dos.close();} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
try {
return (Inet6Address) Inet6Address.getByAddress(bos.toByteArray());
} catch (UnknownHostException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
return null;
}
public static IPv6Address randomKLALBIPv6Address() {
SecureRandom sc = new SecureRandom();
byte[] v = new byte[16];
sc.nextBytes(v);
v[0] = (byte) 0x24;
v[1] = (byte) 0x86;
v[2] = 0x00;
v[3] = 0x01;
return IPv6Address.valueOf(v);
}
public static String parseCidr(String cidr) {
int idx = cidr.indexOf('/');
if (idx <= 0 || idx == cidr.length() - 1)
throw new IllegalArgumentException("not a cidr: " + cidr);
int prefix;
try {
prefix = Integer.parseInt(cidr.substring(idx + 1));
} catch (NumberFormatException e) {
throw new IllegalArgumentException("bad prefix: " + cidr);
}
try {
InetAddress addr = InetAddress.getByName(cidr.substring(0, idx));
int max = (addr instanceof Inet6Address) ? 128 : 32;
if (prefix < 0 || prefix > max)
throw new IllegalArgumentException("prefix out of range: " + cidr);
return addr.getHostAddress() + "/" + prefix;
} catch (UnknownHostException e) {
throw new IllegalArgumentException("bad address: " + cidr);
}
}
public static void main(String[] args) {
System.out.println(uuidToIP(new UUID(-1,-1)));
}
public static String convertUintNanoDefalut(long v) {
if(v>=1000L*1000L*1000L*1000L*1000L) {
return format( v/(1000.0*1000.0*1000.0*1000.0*1000.0))+"M";
}else if(v>=1000L*1000L*1000L*1000L) {
return format (v/(1000.0*1000.0*1000.0*1000.0))+"K";
}else if(v>=1000L*1000L*1000L) {
return format( v/(1000.0*1000.0*1000.0));
}else if(v>=1000L*1000L) {
return format( v/(1000.0*1000.0))+"m";
}else if(v>=1000L) {
return format( v/(1000.0))+"u";
}else {
return Long.toString(v)+"n" ;
}
}
public static String convertUintDefalut(long v) {
if(v>=1000L*1000L*1000L*1000L*1000L) {
return format( v/(1000.0*1000.0*1000.0*1000.0*1000.0))+"P";
}else if(v>=1000L*1000L*1000L*1000L) {
return format (v/(1000.0*1000.0*1000.0*1000.0))+"T";
}else if(v>=1000L*1000L*1000L) {
return format( v/(1000.0*1000.0*1000.0))+"G";
}else if(v>=1000L*1000L) {
return format( v/(1000.0*1000.0))+"M";
}else if(v>=1000L) {
return format( v/(1000.0))+"K";
}else {
return Long.toString(v) ;
}
}
public static String convertIBUint(long v) {
return convertIUintDefalut(v)+"B";
/*if (v >= 1024L * 1024 * 1024 * 1024 * 1024) {
return String.format("%.2f", v / (1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0)) + "PB";
} else if (v >= 1024L * 1024 * 1024 * 1024) {
return String.format("%.2f", v / (1024.0 * 1024.0 * 1024.0 * 1024.0)) + "TB";
} else if (v >= 1024L * 1024 * 1024) {
return String.format("%.2f", v / (1024.0 * 1024.0 * 1024.0)) + "GB";
} else if (v >= 1024L * 1024) {
return String.format("%.2f", v / (1024.0 * 1024.0)) + "MB";
} else if (v >= 1024L) {
return String.format("%.2f", v / (1024.0)) + "KB";
} else {
return v + "B";
}*/
}
public static String convertIBUintSimp(long v) {
if(v>=1024L*1024*1024*1024*1024) {
return format( v/(1024.0*1024.0*1024.0*1024.0*1024.0))+"P";
}else if(v>=1024L*1024*1024*1024) {
return format (v/(1024.0*1024.0*1024.0*1024.0))+"T";
}else if(v>=1024L*1024*1024) {
return format( v/(1024.0*1024.0*1024.0))+"G";
}else if(v>=1024L*1024) {
return format( v/(1024.0*1024.0))+"M";
}else if(v>=1024L) {
return format( v/(1024.0))+"K";
}else {
return v+"B";
}
}
public static String convertIUintDefalut(long v) {
if(v>=1024L*1024*1024*1024*1024) {
return format( v/(1024.0*1024.0*1024.0*1024.0*1024.0))+"Pi";
}else if(v>=1024L*1024*1024*1024) {
return format (v/(1024.0*1024.0*1024.0*1024.0))+"Ti";
}else if(v>=1024L*1024*1024) {
return format( v/(1024.0*1024.0*1024.0))+"Gi";
}else if(v>=1024L*1024) {
return format( v/(1024.0*1024.0))+"Mi";
}else if(v>=1024L) {
return format( v/(1024.0))+"Ki";
}else {
return Long.toString(v) ;
}
}
private static String format( double d) {
String fmt=String.format( "%.2f",d);
if(fmt.length()>4) {
fmt=fmt.substring(0, 4);
}
if(fmt.endsWith(".")) {
fmt=fmt.substring(0, fmt.length()-1);
}
return fmt;
}
public static int binarySearchDATATPacketNumber(List<DATATPacket>lst,long number) {
int low =0;
int high=lst.size()-1;
int middle=0;
if(low>high||number<lst.get(low).getNumber()||number>lst.get(high).getNumber()) {
return -1;
}
while(low<=high) {
middle=(low+high)/2;
long xn=lst.get(middle).getNumber();
if(xn>number) {
high=middle-1;
}else if(xn<number) {
low=middle+1;
}else {
return middle;
}
}
return -1;
}
public static Color getColorByLoadPercentage(float f) {
int r=0;
int g=255;
if(f<50f) {
r+=f/50f*255f;
}else {
r=255;
g-=(f-50f)/50f*255f;
}
if(r>255)
r=255;
if(r<0)
r=0;
if(g>255)
g=255;
if(g<0)
g=0;
Color col=new Color( r,g,0);
return col;
}
public static Color getColorByLoadPercentagex(float f) {
int r=0;
int g=200;
if(f<50f) {
r+=f/50f*200f;
}else {
r=200;
g-=(f-50f)/50f*200f;
}
if(r>200)
r=200;
if(r<0)
r=0;
if(g>200)
g=200;
if(g<0)
g=0;
Color col=new Color( r,g,0);
return col;
}
public static KLALBPacketLink createKLALBPacketLink(MultiProtocolSocketAddress bindAddress, MultiProtocolSocketAddress targetAddress, boolean buffered)
throws IOException {
if (targetAddress.isStream()) {
if (targetAddress.supportNIO()) {
if (bindAddress != null) {
return new StreamChannelKLALBPacketLink(targetAddress
.connectSocketChannel(InetAddress.getByName(bindAddress.getHost()), bindAddress.getPort()),buffered);
} else {
return new StreamChannelKLALBPacketLink(targetAddress.connectSocketChannel(),buffered);
}
} else {
if (bindAddress != null) {
return new StreamKLALBPacketLink(
targetAddress.connectSocket(InetAddress.getByName(bindAddress.getHost()), bindAddress.getPort()));
} else {
return new StreamKLALBPacketLink(targetAddress.connectSocket());
}
}
} else {
if (bindAddress != null) {
return new SplitedDatagramKLALBPacketLink(
targetAddress.connectDatagramSocket(InetAddress.getByName(bindAddress.getHost()), bindAddress.getPort()));
} else {
return new SplitedDatagramKLALBPacketLink(targetAddress.connectDatagramSocket());
}
}
}
public static KLALBPacketLink createKLALBPacketLink(MultiProtocolSocketAddress bindAddress,
MultiProtocolSocketAddress targetAddress, boolean buffered, int timeout) throws UnknownHostException, IOException {
if (targetAddress.isStream()) {
if (targetAddress.supportNIO()) {
if (bindAddress != null) {
if(buffered) {
return new StreamChannelKLALBPacketLink(targetAddress
.connectSocketChannel(InetAddress.getByName(bindAddress.getHost()), bindAddress.getPort(),timeout),buffered);
}else {
return new StreamChannelKLALBPacketLink(targetAddress
.connectSocketChannel(InetAddress.getByName(bindAddress.getHost()), bindAddress.getPort(),timeout),buffered);
}
} else {
if(buffered) {
return new StreamChannelKLALBPacketLink(targetAddress.connectSocketChannel(timeout));
}else {
return new StreamChannelKLALBPacketLink(targetAddress.connectSocketChannel(timeout));
}
}
} else {
if (bindAddress != null) {
return new StreamKLALBPacketLink(
targetAddress.connectSocket(InetAddress.getByName(bindAddress.getHost()), bindAddress.getPort(),timeout));
} else {
return new StreamKLALBPacketLink(targetAddress.connectSocket(timeout));
}
}
} else {
if (bindAddress != null) {
return new SplitedDatagramKLALBPacketLink(
targetAddress.connectDatagramSocket(InetAddress.getByName(bindAddress.getHost()), bindAddress.getPort()));
} else {
return new SplitedDatagramKLALBPacketLink(targetAddress.connectDatagramSocket());
}
}
}
public static final String IPv6Reg = "([\\da-fA-F]{1,4}:){7}[\\da-fA-F]{1,4}$"
+ "|^:((:[\\da-fA-F]{1,4}){1,6}|:)$"
+ "|^[\\da-fA-F]{1,4}:((:[\\da-fA-F]{1,4}){1,5}|:)$"
+ "|^([\\da-fA-F]{1,4}:){2}((:[\\da-fA-F]{1,4}){1,4}|:)$"
+ "|^([\\da-fA-F]{1,4}:){3}((:[\\da-fA-F]{1,4}){1,3}|:)$"
+ "|^([\\da-fA-F]{1,4}:){4}((:[\\da-fA-F]{1,4}){1,2}|:)$"
+ "|^([\\da-fA-F]{1,4}:){5}:([\\da-fA-F]{1,4})?$"
+ "|^([\\da-fA-F]{1,4}:){6}:"; // IPv6地址的格式
/**
* 将一个IPv6地址转为全写格式,全写中的前导0省略
* 例:将1ade:03da:0::转为1ade:3da:0:0:0:0:0:0
*
* @param IPv6Str
* @return fullIPv6
*/
public static String parseFullIPv6(String IPv6Str) {
// 判断IPv6地址的格式是否正确
if (!IPv6Str.matches(IPv6Reg)) {
return "";
}
String[] arr = new String[]{"0", "0", "0", "0", "0", "0", "0", "0"};
// 将IPv6地址用::分开
// 如果IPv6地址为::tempArr.length==0
// 如果不包含::或以::结尾,tempArr.length==1
// 如果以::开头或::在中间,tempArr.length==2
String[] tempArr = IPv6Str.split("::");
// tempArr[0]用:分开,填充到arr前半部分
if (tempArr.length > 0) {
// new String[0]为空数组,因为"".split(":")为{""},如果tempArr[0]=="",此时数组包含一个元素
String[] tempArr0 = tempArr[0].isEmpty() ? new String[0] : tempArr[0].split(":");
for (int i = 0; i < tempArr0.length; i++) {
// 如果是纯数字,用parseInt去除前导0,如果包含字母,用正则去除前导0
arr[i] = tempArr0[i].matches("\\d+")
? (Integer.parseInt(tempArr0[i]) + "")
: tempArr0[i].replaceAll("^(0+)", "");
}
}
// tempArr[1]用:分开,填充到arr后半部分
if (tempArr.length > 1) {
String[] tempArr1 = tempArr[1].isEmpty() ? new String[0] : tempArr[1].split(":");
for (int i = 0; i < tempArr1.length; i++) {
arr[i + arr.length - tempArr1.length] = tempArr1[i].matches("\\d+")
? (Integer.parseInt(tempArr1[i]) + "")
: tempArr1[i].replaceAll("^(0+)", "");
}
}
return join(arr, ":");
}
private static String join(String[] arr, String string) {
StringBuilder sb=new StringBuilder();
for (int i = 0; i < arr.length; i++) {
sb.append(arr[i]);
if(i+1<arr.length) {
sb.append(string);
}
}
return sb.toString();
}
/**
* 将一个IPv6地址转为简写格式
* 例:将1ade:03da:0::转为1ade:3da::
*
* @param IPv6Str
* @return AbbrIPv6
*/
public static String parseAbbrIPv6(String IPv6Str) {
// 将一个IPv6地址转为全写格式,全写中的前导0省略
String fullIPv6 = parseFullIPv6(IPv6Str);
if (fullIPv6.isEmpty()) { // 如果IPv6地址的格式不正确
return "";
}
// 将1ade:3da:0:0:0:0:0:99转为{"-","-","0","0","0","0","0","-"}
String[] arr = fullIPv6.split(":");
for (int i = 0, len = arr.length; i < len; i++) {
if (!"0".equals(arr[i])) {
arr[i] = "-";
}
}
// 找到最长的连续的0
Pattern pattern = Pattern.compile("0{2,}");
Matcher matcher = pattern.matcher(join(arr, ""));
String maxStr = "";
int start = -1;
int end = -1;
while (matcher.find()) {
if (maxStr.length() < matcher.group().length()) {
maxStr = matcher.group();
start = matcher.start();
end = matcher.end();
}
}
// 合并
arr = fullIPv6.split(":");
if (maxStr.length() > 0) {
for (int i = start; i < end; i++) {
arr[i] = ":";
}
}
return join(arr, ":").replaceAll(":{2,}", "::");
}
private static class UUIDGenarator{
private final long id;
private long counter=0;
private long randUUIDh;
private long randUUIDl;
private static SecureRandom sr=new SecureRandom();
public UUIDGenarator(long threadId) {
this.id=threadId;
randUUIDh=sr.nextLong();
randUUIDl=sr.nextLong();
}
public UUID genarateUUID() {
long h=randUUIDh^id;
long l=randUUIDl^counter;
UUID ret=new UUID(h, l);
counter++;
return ret;
}
}
private static ThreadLocal<UUIDGenarator> gens=ThreadLocal.withInitial(()->{
return new UUIDGenarator(Thread.currentThread().threadId());
});
public static UUID createGlobalUUID() {
//return UUID.randomUUID();
return gens.get().genarateUUID();
}
public static FixedSizeStripedLongCounter createCounter() {
return CountersFactory.createFixedSizeStripedCounter(Runtime.getRuntime().availableProcessors());
}
}