forked from KNEMC/KLALB
- 设置页新增设备名称(单行)、设备描述(多行)、额外路由(CIDR列表)设置项 - RouterInfo 广播携带 deviceName,并在拓扑图节点与节点概览中展示;deviceDescription 保持本地概览显示 - 拓扑图节点标签支持多行文本绘制 - 节点概览面板新增 IP 地址及设备详情展示 - 支持将 TUNName 设为 null/"null"/空字符串时完全跳过 Wintun 虚拟网卡创建 - .classpath 容器改为标准 JavaSE-25 执行环境以同时兼容 JDK 25 与 26 - 新增 AGENTS.md 与 VS Code 调试及运行配置 BREAKING CHANGE: RouterInfo 在序列化末尾追加了 deviceName 字段(writeUTF),新旧版本节点混连时路由协议解析异常,需同步升级
441 lines
14 KiB
Java
441 lines
14 KiB
Java
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());
|
||
}
|
||
}
|