From 455139dfe918639d4a54f0275c717863a3acab3f Mon Sep 17 00:00:00 2001
From: SerinaNya <34389622+SerinaNya@users.noreply.github.com>
Date: Fri, 21 Aug 2026 23:34:41 +0800
Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20=E6=96=B0=E5=A2=9E=E8=AE=BE?=
=?UTF-8?q?=E5=A4=87=E4=BF=A1=E6=81=AF=E4=B8=8E=E9=A2=9D=E5=A4=96=E8=B7=AF?=
=?UTF-8?q?=E7=94=B1=E8=AE=BE=E7=BD=AE=EF=BC=8C=E6=94=AF=E6=8C=81=E8=AE=BE?=
=?UTF-8?q?=E5=A4=87=E5=90=8D=E5=B9=BF=E6=92=AD=E5=8F=8ATUN=E6=98=BE?=
=?UTF-8?q?=E5=BC=8F=E7=A6=81=E7=94=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 设置页新增设备名称(单行)、设备描述(多行)、额外路由(CIDR列表)设置项
- RouterInfo 广播携带 deviceName,并在拓扑图节点与节点概览中展示;deviceDescription 保持本地概览显示
- 拓扑图节点标签支持多行文本绘制
- 节点概览面板新增 IP 地址及设备详情展示
- 支持将 TUNName 设为 null/"null"/空字符串时完全跳过 Wintun 虚拟网卡创建
- .classpath 容器改为标准 JavaSE-25 执行环境以同时兼容 JDK 25 与 26
- 新增 AGENTS.md 与 VS Code 调试及运行配置
BREAKING CHANGE: RouterInfo 在序列化末尾追加了 deviceName 字段(writeUTF),新旧版本节点混连时路由协议解析异常,需同步升级
---
.classpath | 2 +-
.project | 11 +++
.vscode/launch.json | 27 +++++++
.vscode/settings.json | 9 +++
AGENTS.md | 55 +++++++++++++
klalb-config.json | 5 +-
src/klalb_en_US.properties | 4 +
src/klalb_zh_CN.properties | 4 +
.../cloud/network/klalb/KLALBController.java | 8 +-
.../klalb/KLALBControllerConfigItem.java | 34 +++++++-
.../kne/cloud/network/klalb/KLALBUtils.java | 21 +++++
.../cloud/network/klalb/ui/GraphPanel.java | 11 ++-
.../network/klalb/ui/KLALBStateGUI3.java | 77 ++++++++++++++++++-
.../network/klalb/ui/NetworkGraphPanel.java | 22 +++++-
.../klalb/ui/NodeInformationPanel.java | 23 ++++++
.../network/srv6/KLALBRoutingProtocol.java | 14 ++++
.../kne/cloud/network/srv6/RouterInfo.java | 11 +++
.../kne/cloud/network/srv6/SRv6Router.java | 9 +++
18 files changed, 335 insertions(+), 12 deletions(-)
create mode 100644 .vscode/launch.json
create mode 100644 .vscode/settings.json
create mode 100644 AGENTS.md
diff --git a/.classpath b/.classpath
index 60354f2..7200fd3 100644
--- a/.classpath
+++ b/.classpath
@@ -24,7 +24,7 @@
-
+
diff --git a/.project b/.project
index b5f524a..0359fa5 100644
--- a/.project
+++ b/.project
@@ -14,4 +14,15 @@
org.eclipse.jdt.core.javanature
+
+
+ 1787317094049
+
+ 30
+
+ org.eclipse.core.resources.regexFilterMatcher
+ node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__
+
+
+
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..75587bd
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,27 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "java",
+ "name": "KLALBMain (VS Code 自动构建)",
+ "request": "launch",
+ "mainClass": "org.kne.cloud.network.klalb.KLALBMain",
+ "projectName": "KLALB",
+ "cwd": "${workspaceFolder}",
+ "vmArgs": "--enable-native-access=ALL-UNNAMED --add-opens=java.base/jdk.internal.misc=ALL-UNNAMED"
+ },
+ {
+ "type": "java",
+ "name": "KLALBMain (直接用 bin 目录)",
+ "request": "launch",
+ "mainClass": "org.kne.cloud.network.klalb.KLALBMain",
+ "cwd": "${workspaceFolder}",
+ "classPaths": [
+ "${workspaceFolder}/bin",
+ "${workspaceFolder}/src",
+ "${workspaceFolder}/lib/*"
+ ],
+ "vmArgs": "--enable-native-access=ALL-UNNAMED --add-opens=java.base/jdk.internal.misc=ALL-UNNAMED"
+ }
+ ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..907ed05
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,9 @@
+{
+ "java.configuration.runtimes": [
+ {
+ "name": "JavaSE-25",
+ "path": "C:\\Program Files\\Eclipse Adoptium\\jdk-25.0.4.7-hotspot",
+ "default": true
+ }
+ ]
+}
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..62671b6
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,55 @@
+# AGENTS.md
+
+KLALB ("KLALB Decentralized SRv6 Network") — Java load-balancing/tunnel system that merges multiple WAN links into one virtual IPv6/SRv6 network. Version constant lives in `src/org/kne/cloud/network/klalb/CONST.java`. Protocol specs and manuals are the Chinese `.docx` files in the repo root.
+
+## Build & run
+
+No Maven/Gradle. Plain Eclipse/IntelliJ project: dependencies are vendored jars in `lib/`, output goes to `bin/` (gitignored). When adding a jar, update **both** `.classpath` and `KLALB.iml`.
+
+Compile (verified; `-encoding UTF-8` is mandatory — sources contain Chinese text):
+
+```powershell
+& javac -encoding UTF-8 -cp "lib/*" -d bin (Get-ChildItem -Recurse src -Filter *.java | ForEach-Object FullName)
+```
+
+Run from the repo root — CWD matters:
+- reads `klalb-config.json` from CWD
+- loads native libs from CWD: `tuntap4j.dll/.so/.dylib`, `wintun.dll`, `fastcopy.dll` (TUN device support)
+- classpath must include `src` as well as `bin`: i18n bundles (`/klalb_*.properties`) and images (`/assets/*`) are classpath resources that Eclipse copies to `bin` but manual `javac` does not
+
+```powershell
+java --enable-native-access=ALL-UNNAMED "--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED" -cp "bin;src;lib/*" org.kne.cloud.network.klalb.KLALBMain
+```
+
+IDE metadata targets JDK 26 (`jdk-26.0.1`); the tree also compiles cleanly on JDK 25. `.classpath` now references the standard container `JavaSE-25` — an execution-environment spec that any JDK ≥25 satisfies, so it works unchanged on JDK 26 machines too (the original named `jdk-26.0.1` VM broke VS Code import on machines without it). `.vscode/settings.json` maps `JavaSE-25` to the locally installed Adoptium JDK; register every installed JDK there when adding another one. Keep compiler compliance ≤25 (`.settings` pins 19) so both JDKs stay usable.
+
+Runtime gotchas (all verified):
+- On JDK 25, `KNEOptimize.jar`'s `FastLib` reflects into `jdk.internal.misc.Unsafe`; without the two JVM flags above it throws `InaccessibleObjectException` at startup (app still runs).
+- Creating the SRv6 TUN adapter (`WintunCreateAdapter`) requires an elevated shell; without admin rights it logs "创建虚拟网卡失败" and continues with only the `inLoopBack` interface — links/bridges still work.
+- To disable TUN creation completely (e.g. for non-admin UI/routing testing), set `"TUNName": null` in `klalb-config.json` or leave the TUN Name empty in GUI settings. If omitted, it defaults to `"KLALB_SRv6"`.
+- Routing broadcast (`RouterInfo`) transmits `deviceName` across the network, which topology and node overview panels display. `deviceDescription` and `ExtraRoutes` remain local controller configs.
+
+## Verification
+
+No test suite, no CI. Classes named `*Test*` (`nathole/`, `ntp/`) are manual `main()` harnesses requiring real network peers. Practical check = compile succeeds + app launches.
+
+## Architecture
+
+- Entrypoint `org.kne.cloud.network.klalb.KLALBMain`: load config → build `KLALBProxySystem` → open Swing GUI (`KLALBStateGUI3`) unless `"nogui": true` → interactive console (`help`, `links-state`, `route`, `kperf`, ...).
+- `org.kne.cloud.network` — generic socket framework: `VirtualSocket*` hierarchy, `SocketBridge` port-forwarding proxies, `ProtocolDetector` (multi-protocol mux on one port), `MultiProtocolSocketAddress` = URI-style addresses (`tcp://`, `udp://`, `kltp://`, `ntp://`) dispatched through the `SocketType` registry.
+- `...network.klalb` — app core: `KLALBController` (the virtual SRv6 network), `KLALBRemoteLink` (WAN lines), `*Packet` wire-format classes, virtual socket implementations.
+- `...network.congestion` — pluggable congestion control (BBR, Vegas2, DCTCP...), chosen via `"congestionAlgorithm"` in config.
+- `...network.kltp` — custom reliable transport protocol (packets/streams).
+- `...network.ipv6`, `...network.srv6` — packet codecs, route table, Dijkstra path computation.
+- `...network.frpc` — frp client integration.
+- `...klalb.ui` — all Swing UI code.
+
+## Config
+
+`klalb-config.json` is an array of items discriminated by their `"Type"` field. Adding a new item type requires a `KLALBConfigItem` subclass **plus** new cases in both `KLALBConfigItem.getDefaultJsonDeserializer()` and `getDefaultJsonSerializer()`; unknown types are preserved as `UnknownKLALBConfigItem`. Any Gson instance handling config must register these adapters via `registerToGsonBuilder` (see `KLALBProxySystem`).
+
+## Conventions
+
+- Sources are UTF-8; comments, log/UI strings, and commit messages are largely Chinese.
+- UI strings go through `UIEnv.getRsb().getString(...)`; add keys to **both** `src/klalb_zh_CN.properties` and `src/klalb_en_US.properties`.
+- `client.cfg`, `server.cfg`, `linetable.txt` at the root are example line-table/port-rule files loaded via the GUI file picker — not hardwired paths.
diff --git a/klalb-config.json b/klalb-config.json
index 2d06737..9be7de6 100644
--- a/klalb-config.json
+++ b/klalb-config.json
@@ -37,6 +37,7 @@
"ntp://ntp.ntsc.ac.cn",
"ntp://us.ntp.org.cn"
],
+ "ExtraRoutes": [],
"denyLineTableQuery": false,
"denyLineTableBroadcast": false,
"congestionAlgorithm": "BBR",
@@ -46,8 +47,10 @@
"nagleDelayTime": 1000000,
"linkNagleDelayTime": 0,
"linkConnectionsCount": 1,
- "TUNName": "KLALB_SRv6",
+ "TUNName": null,
"performanceStrategy": "multiscatter",
+ "DeviceName": "SerinaNya PC",
+ "DeviceDescription": "desc",
"NetworkInterfaceExcepts": [],
"Type": "KLALBController"
},
diff --git a/src/klalb_en_US.properties b/src/klalb_en_US.properties
index a4a76cd..97ed089 100644
--- a/src/klalb_en_US.properties
+++ b/src/klalb_en_US.properties
@@ -3,7 +3,10 @@ networkgraph=Network graph
language=Language
basicsettings=Basic settings
ipv6addr=IPv6 address
+devicename=Device name
+devicedescription=Device description
dnsserver=DNS server
+extraroutes=Extra routes
asnumber=AS number
tcplistening=TCP listening
udplistening=UDP listening
@@ -34,6 +37,7 @@ saveconfigsuccess=Save config success
warning=Warning
invaildipv6addr=IPv6 address:Invaild Input
invailddnsserver=DNS server:Invaild Input
+invaildextraroutes=Extra routes:Invaild Input
invaildasnumber=AS number:Invaild Input
invaildtcplisten=TCP listen:Invaild Input
invaildudplisten=UDP listen:Invaild Input
diff --git a/src/klalb_zh_CN.properties b/src/klalb_zh_CN.properties
index dc97fe8..c1257cd 100644
--- a/src/klalb_zh_CN.properties
+++ b/src/klalb_zh_CN.properties
@@ -3,7 +3,10 @@ networkgraph=网络拓扑图
language=语言
basicsettings=基本设置
ipv6addr=IPv6地址
+devicename=设备名称
+devicedescription=设备描述
dnsserver=DNS服务器
+extraroutes=额外路由
asnumber=AS号码
tcplistening=TCP监听端口
udplistening=UDP监听端口
@@ -34,6 +37,7 @@ saveconfigsuccess=保存配置成功
warning=警告
invaildipv6addr=IPv6地址:非法输入
invailddnsserver=DNS服务器:非法输入
+invaildextraroutes=额外路由:非法输入
invaildasnumber=AS号码:非法输入
invaildtcplisten=TCP监听端口:非法输入
invaildudplisten=UDP监听端口:非法输入
diff --git a/src/org/kne/cloud/network/klalb/KLALBController.java b/src/org/kne/cloud/network/klalb/KLALBController.java
index dd6a8e1..3eb1857 100644
--- a/src/org/kne/cloud/network/klalb/KLALBController.java
+++ b/src/org/kne/cloud/network/klalb/KLALBController.java
@@ -637,6 +637,7 @@ public class KLALBController {
srv6Router = new SRv6Router(selfx, clock);
if(configItem!=null) {
srv6Router.setPerformanceStrategy(PerformanceStrategy.fromDescription(configItem.getPerformanceStrategy()));
+ srv6Router.setDeviceName(configItem.getDeviceName());
}
srv6Router.runKLALBRouteProtocol();
routingProtocol = srv6Router.getKlalbRouteProtol();
@@ -647,10 +648,11 @@ public class KLALBController {
System.out.println(" Loaded: SRv6 Stack");
String name=(configItem!=null)?configItem.getTUNName():CONST.KLALB_S_RV6;
- if (enableVirtualAdapter&&(name!=null)) {
+ boolean enableTUN = enableVirtualAdapter && (name != null) && !name.trim().isEmpty() && !name.trim().equalsIgnoreCase("null");
+ if (enableTUN) {
Thread t=new Thread(()->{
try {
- IPv6TUNLoopbackNetworkLink tunlink = new IPv6TUNLoopbackNetworkLink(name,
+ IPv6TUNLoopbackNetworkLink tunlink = new IPv6TUNLoopbackNetworkLink(name.trim(),
new IPv6AddressGroup(srv6Router.getLocator().getAddress(), 32), SRv6Router.MTU, dnsAddresses);
tunlink.setMonitor(datatMonitor);
// srv6Router.getLinkTabel().add(tunlink);
@@ -664,7 +666,7 @@ public class KLALBController {
});
t.start();
}else {
- System.out.println(" Tun Adapter Disbaled");
+ System.out.println(" Tun Adapter Disabled");
}
udpr=new UDPProtocolRegister(this);
diff --git a/src/org/kne/cloud/network/klalb/KLALBControllerConfigItem.java b/src/org/kne/cloud/network/klalb/KLALBControllerConfigItem.java
index 9b7966a..76d6bfa 100644
--- a/src/org/kne/cloud/network/klalb/KLALBControllerConfigItem.java
+++ b/src/org/kne/cloud/network/klalb/KLALBControllerConfigItem.java
@@ -19,6 +19,7 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
private ListLineTable=new ArrayList<>();
private ListConnectLineTable=new ArrayList<>();
private ListntpServerTable=new ArrayList<>();
+ private ListExtraRoutes=new ArrayList<>();
private boolean denyLineTableQuery=false;
private boolean denyLineTableBroadcast=false;
private String congestionAlgorithm="BBR";
@@ -30,6 +31,8 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
private int linkConnectionsCount=1;
private String TUNName=CONST.KLALB_S_RV6;
private String performanceStrategy="multifill";
+ private String DeviceName;
+ private String DeviceDescription;
public String getPerformanceStrategy() {
return performanceStrategy;
@@ -39,6 +42,22 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
this.performanceStrategy = performanceStrategy;
}
+ public String getDeviceName() {
+ return DeviceName;
+ }
+
+ public void setDeviceName(String deviceName) {
+ DeviceName = deviceName;
+ }
+
+ public String getDeviceDescription() {
+ return DeviceDescription;
+ }
+
+ public void setDeviceDescription(String deviceDescription) {
+ DeviceDescription = deviceDescription;
+ }
+
public void setNetworkInterfaceExcepts(List networkInterfaceExcepts) {
NetworkInterfaceExcepts = networkInterfaceExcepts;
}
@@ -193,6 +212,14 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
this.ntpServerTable = ntpServerTable;
}
+ public List getExtraRoutes() {
+ return ExtraRoutes;
+ }
+
+ public void setExtraRoutes(List extraRoutes) {
+ ExtraRoutes = extraRoutes;
+ }
+
@@ -328,6 +355,7 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
", LineTable=" + LineTable +
", ConnectLineTable=" + ConnectLineTable +
", ntpServerTable=" + ntpServerTable +
+ ", ExtraRoutes=" + ExtraRoutes +
", denyLineTableQuery=" + denyLineTableQuery +
", denyLineTableBroadcast=" + denyLineTableBroadcast +
", congestionAlgorithm='" + congestionAlgorithm + '\'' +
@@ -339,6 +367,8 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
", linkConnectionsCount=" + linkConnectionsCount +
", TUNName='" + TUNName + '\'' +
", performanceStrategy='" + performanceStrategy + '\'' +
+ ", DeviceName='" + DeviceName + '\'' +
+ ", DeviceDescription='" + DeviceDescription + '\'' +
", NetworkInterfaceExcepts=" + NetworkInterfaceExcepts +
'}';
}
@@ -348,11 +378,11 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
KLALBControllerConfigItem that = (KLALBControllerConfigItem) o;
- return nogui == that.nogui && denyLineTableQuery == that.denyLineTableQuery && denyLineTableBroadcast == that.denyLineTableBroadcast && Double.compare(burstLimit, that.burstLimit) == 0 && Double.compare(delayUpperBound, that.delayUpperBound) == 0 && Double.compare(delayLowerBound, that.delayLowerBound) == 0 && nagleDelayTime == that.nagleDelayTime && linkNagleDelayTime == that.linkNagleDelayTime && linkConnectionsCount == that.linkConnectionsCount && Objects.equals(language, that.language) && Objects.equals(VirtualAddress, that.VirtualAddress) && Objects.equals(VirtualASN, that.VirtualASN) && Objects.equals(DNS, that.DNS) && Objects.equals(TCPListen, that.TCPListen) && Objects.equals(UDPListen, that.UDPListen) && Objects.equals(VirtualSocketName, that.VirtualSocketName) && Objects.equals(LineTable, that.LineTable) && Objects.equals(ConnectLineTable, that.ConnectLineTable) && Objects.equals(ntpServerTable, that.ntpServerTable) && Objects.equals(congestionAlgorithm, that.congestionAlgorithm) && Objects.equals(TUNName, that.TUNName) && Objects.equals(performanceStrategy, that.performanceStrategy) && Objects.equals(NetworkInterfaceExcepts, that.NetworkInterfaceExcepts);
+ return nogui == that.nogui && denyLineTableQuery == that.denyLineTableQuery && denyLineTableBroadcast == that.denyLineTableBroadcast && Double.compare(burstLimit, that.burstLimit) == 0 && Double.compare(delayUpperBound, that.delayUpperBound) == 0 && Double.compare(delayLowerBound, that.delayLowerBound) == 0 && nagleDelayTime == that.nagleDelayTime && linkNagleDelayTime == that.linkNagleDelayTime && linkConnectionsCount == that.linkConnectionsCount && Objects.equals(language, that.language) && Objects.equals(VirtualAddress, that.VirtualAddress) && Objects.equals(VirtualASN, that.VirtualASN) && Objects.equals(DNS, that.DNS) && Objects.equals(TCPListen, that.TCPListen) && Objects.equals(UDPListen, that.UDPListen) && Objects.equals(VirtualSocketName, that.VirtualSocketName) && Objects.equals(LineTable, that.LineTable) && Objects.equals(ConnectLineTable, that.ConnectLineTable) && Objects.equals(ntpServerTable, that.ntpServerTable) && Objects.equals(ExtraRoutes, that.ExtraRoutes) && Objects.equals(congestionAlgorithm, that.congestionAlgorithm) && Objects.equals(TUNName, that.TUNName) && Objects.equals(performanceStrategy, that.performanceStrategy) && Objects.equals(DeviceName, that.DeviceName) && Objects.equals(DeviceDescription, that.DeviceDescription) && Objects.equals(NetworkInterfaceExcepts, that.NetworkInterfaceExcepts);
}
@Override
public int hashCode() {
- return Objects.hash(super.hashCode(), language, nogui, VirtualAddress, VirtualASN, DNS, TCPListen, UDPListen, VirtualSocketName, LineTable, ConnectLineTable, ntpServerTable, denyLineTableQuery, denyLineTableBroadcast, congestionAlgorithm, burstLimit, delayUpperBound, delayLowerBound, nagleDelayTime, linkNagleDelayTime, linkConnectionsCount, TUNName, performanceStrategy, NetworkInterfaceExcepts);
+ return Objects.hash(super.hashCode(), language, nogui, VirtualAddress, VirtualASN, DNS, TCPListen, UDPListen, VirtualSocketName, LineTable, ConnectLineTable, ntpServerTable, ExtraRoutes, denyLineTableQuery, denyLineTableBroadcast, congestionAlgorithm, burstLimit, delayUpperBound, delayLowerBound, nagleDelayTime, linkNagleDelayTime, linkConnectionsCount, TUNName, performanceStrategy, DeviceName, DeviceDescription, NetworkInterfaceExcepts);
}
}
diff --git a/src/org/kne/cloud/network/klalb/KLALBUtils.java b/src/org/kne/cloud/network/klalb/KLALBUtils.java
index 1bedf45..7322633 100644
--- a/src/org/kne/cloud/network/klalb/KLALBUtils.java
+++ b/src/org/kne/cloud/network/klalb/KLALBUtils.java
@@ -51,6 +51,27 @@ public class KLALBUtils {
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)));
}
diff --git a/src/org/kne/cloud/network/klalb/ui/GraphPanel.java b/src/org/kne/cloud/network/klalb/ui/GraphPanel.java
index 6a611d5..7da8456 100644
--- a/src/org/kne/cloud/network/klalb/ui/GraphPanel.java
+++ b/src/org/kne/cloud/network/klalb/ui/GraphPanel.java
@@ -346,7 +346,16 @@ public class GraphPanel extends JPanel {
g.setFont(UIEnv.getFont().deriveFont(10.0f));
//g.drawString(text, (int)(x+nodesize/2), (int)(y-nodesize/3));
- g.drawString(text, (int)(x-image.getWidth(null)/2-10), (int)(y+image.getHeight(null)/2+10));
+ int imgw=(image!=null)?image.getWidth(null):(int)nodesize;
+ int imgh=(image!=null)?image.getHeight(null):(int)nodesize;
+ java.awt.FontMetrics fm=g.getFontMetrics();
+ int lineHeight=fm.getHeight()+2;
+ int textx=(int)(x-imgw/2-10);
+ int texty=(int)(y+imgh/2+10)+fm.getAscent();
+ String[] lines=text.split("\n");
+ for (int i = 0; i < lines.length; i++) {
+ g.drawString(lines[i], textx, texty+i*lineHeight);
+ }
}
public Vector2 getPositionVec2() {
diff --git a/src/org/kne/cloud/network/klalb/ui/KLALBStateGUI3.java b/src/org/kne/cloud/network/klalb/ui/KLALBStateGUI3.java
index 410c8a6..468c927 100644
--- a/src/org/kne/cloud/network/klalb/ui/KLALBStateGUI3.java
+++ b/src/org/kne/cloud/network/klalb/ui/KLALBStateGUI3.java
@@ -80,6 +80,10 @@ public class KLALBStateGUI3 extends XFrame {
private JTextField textFieldLocate; // 定位地址输入框
private JTextField asnField2; // ASN显示框
private JTextArea dnsAreaSet; // DNS服务器设置区域
+ private JTextArea extraRoutesSet; // 额外路由设置区域
+ private JTextField deviceNameSet; // 设备名称设置框
+ private JTextArea deviceDescriptionSet; // 设备描述设置区域
+ private KLALBController kcontroller; // 关联的控制器
private JComboBox comboPerformance; // 性能策略选择框
private JComboBox comboCongestion; // 拥塞控制算法选择框
private JTextField tcpListeningSet; // TCP监听设置
@@ -138,6 +142,7 @@ public class KLALBStateGUI3 extends XFrame {
* 初始化UI界面
*/
private void initUI(KLALBController kc, String title) {
+ kcontroller = kc;
// 设置窗口基本属性
setIconImage(UIEnv.getIcon());
setTitleColor(UIEnv.getDefaultTitleColor());
@@ -785,12 +790,30 @@ public class KLALBStateGUI3 extends XFrame {
addressFieldSet = addr6.getTextField();
settings.getView().add(addr6);
+ // 设备名称设置
+ TextSettingItem deviceName = new TextSettingItem(UIEnv.getRsb().getString("devicename"),
+ CONST.itemwidth, CONST.settingheight);
+ deviceNameSet = deviceName.getTextField();
+ settings.getView().add(deviceName);
+
+ // 设备描述设置
+ TextAreaSettingItem deviceDescription = new TextAreaSettingItem(UIEnv.getRsb().getString("devicedescription"),
+ CONST.itemwidth, CONST.settingheight * 5);
+ deviceDescriptionSet = deviceDescription.getTextArea();
+ settings.getView().add(deviceDescription);
+
// DNS服务器设置
TextAreaSettingItem dns = new TextAreaSettingItem(UIEnv.getRsb().getString("dnsserver"),
CONST.itemwidth, CONST.settingheight * 5);
dnsAreaSet = dns.getTextArea();
settings.getView().add(dns);
+ // 额外路由设置
+ TextAreaSettingItem extraRoutes = new TextAreaSettingItem(UIEnv.getRsb().getString("extraroutes"),
+ CONST.itemwidth, CONST.settingheight * 5);
+ extraRoutesSet = extraRoutes.getTextArea();
+ settings.getView().add(extraRoutes);
+
// ASN设置
TextSettingItem asn = new TextSettingItem(UIEnv.getRsb().getString("asnumber"),
CONST.itemwidth, CONST.settingheight);
@@ -1022,6 +1045,26 @@ public class KLALBStateGUI3 extends XFrame {
kck.setNogui(nogui.isSelected());
+ // 保存设备名称
+ String dnametext = deviceNameSet.getText().trim();
+ if (dnametext.equals("")) {
+ kck.setDeviceName(null);
+ } else {
+ kck.setDeviceName(dnametext);
+ }
+ if (kcontroller != null && kcontroller.getIpv6Router() != null) {
+ kcontroller.getIpv6Router().setDeviceName(kck.getDeviceName());
+ }
+
+ // 保存设备描述
+ String ddesctext = deviceDescriptionSet.getText().trim();
+ if (ddesctext.equals("")) {
+ kck.setDeviceDescription(null);
+ } else {
+ kck.setDeviceDescription(ddesctext);
+ }
+
+
// 保存IPv6地址
String ttext = addressFieldSet.getText().trim();
if (ttext.equals("")) {
@@ -1058,6 +1101,24 @@ public class KLALBStateGUI3 extends XFrame {
}
kck.setDNS(iaddr);
+ // 保存额外路由列表
+ String[] spltEr = extraRoutesSet.getText().split("\n");
+ List eroutes = new ArrayList<>();
+ for (String str : spltEr) {
+ str = str.trim();
+ if (!str.isEmpty()) {
+ try {
+ eroutes.add(KLALBUtils.parseCidr(str));
+ } catch (RuntimeException e) {
+ e.printStackTrace();
+ JOptionPane.showMessageDialog(this, UIEnv.getRsb().getString("invaildextraroutes"),
+ UIEnv.getRsb().getString("warning"), JOptionPane.WARNING_MESSAGE);
+ return;
+ }
+ }
+ }
+ kck.setExtraRoutes(eroutes);
+
// 保存ASN
String asntext = asnFieldSet.getText();
if (asntext.equals("")) {
@@ -1073,8 +1134,8 @@ public class KLALBStateGUI3 extends XFrame {
}
}
- String tunNameText=tunDeviceName.getText();
- if(tunNameText.equals("")) {
+ String tunNameText=tunDeviceName.getText().trim();
+ if(tunNameText.equals("") || tunNameText.equalsIgnoreCase("null")) {
kck.setTUNName(null);
}else {
kck.setTUNName(tunNameText);
@@ -1453,6 +1514,14 @@ public class KLALBStateGUI3 extends XFrame {
nogui.setSelected( kck.isNogui());
+ // 加载设备名称
+ String dname = kck.getDeviceName();
+ deviceNameSet.setText(dname != null ? dname : "");
+
+ // 加载设备描述
+ String ddesc = kck.getDeviceDescription();
+ deviceDescriptionSet.setText(ddesc != null ? ddesc : "");
+
// 加载IPv6地址
String vaddr = kck.getVirtualAddress();
addressFieldSet.setText(vaddr != null ? vaddr : "");
@@ -1461,6 +1530,10 @@ public class KLALBStateGUI3 extends XFrame {
List dns = kck.getDNS();
dnsAreaSet.setText(listToStr(dns));
+ // 加载额外路由
+ List ers = kck.getExtraRoutes();
+ extraRoutesSet.setText(listToStr2(ers));
+
// 加载ASN
Long vasn = kck.getVirtualASN();
asnFieldSet.setText(vasn != null ? vasn.toString() : "");
diff --git a/src/org/kne/cloud/network/klalb/ui/NetworkGraphPanel.java b/src/org/kne/cloud/network/klalb/ui/NetworkGraphPanel.java
index ebf3f44..9a88786 100644
--- a/src/org/kne/cloud/network/klalb/ui/NetworkGraphPanel.java
+++ b/src/org/kne/cloud/network/klalb/ui/NetworkGraphPanel.java
@@ -47,7 +47,7 @@ public class NetworkGraphPanel extends GraphPanel {
}
public InetGraphNode(IPv6Address address, Color color, double x, double y, boolean ismarked) {
- super(getText(address), color, x, y, ismarked);
+ super(getNodeText(address), color, x, y, ismarked);
this.address=address;
initign();
}
@@ -137,17 +137,33 @@ public class NetworkGraphPanel extends GraphPanel {
return address2.toCompressedString();
}
+ public void updateLabel() {
+ setText(getNodeText(address));
+ }
+
public IPv6Address getAddress() {
return address;
}
public void setAddress(IPv6Address address) {
this.address = address;
- super.setText(getText(address));
+ super.setText(getNodeText(address));
}
}
+ /**
+ * 节点标签:显示广播得知的设备名称(若有)+IP地址
+ */
+ private String getNodeText(IPv6Address address) {
+ StringBuilder sb=new StringBuilder();
+ String dname=controller.getIpv6Router().getKlalbRouteProtol().getDeviceName(address);
+ if(dname!=null)
+ sb.append(dname).append('\n');
+ sb.append(InetGraphNode.getText(address));
+ return sb.toString();
+ }
+
private double nsPerPixel=1000L;
private class InetGraphEdgeGroup extends GraphEdgeGroup{
public InetGraphEdgeGroup(GraphNode nodeA, GraphNode nodeB) {
@@ -178,6 +194,8 @@ public class NetworkGraphPanel extends GraphPanel {
IPv6Address inet6Address = (IPv6Address) iterator.next();
if(!addr.containsKey(inet6Address)) {
iterator.remove();
+ }else {
+ ((InetGraphNode)getNodes().get(inet6Address)).updateLabel();
}
}
//System.out.println("------------------------------------");
diff --git a/src/org/kne/cloud/network/klalb/ui/NodeInformationPanel.java b/src/org/kne/cloud/network/klalb/ui/NodeInformationPanel.java
index dba1cc1..e09549e 100644
--- a/src/org/kne/cloud/network/klalb/ui/NodeInformationPanel.java
+++ b/src/org/kne/cloud/network/klalb/ui/NodeInformationPanel.java
@@ -10,10 +10,12 @@ import java.util.List;
import javax.swing.Icon;
import javax.swing.ImageIcon;
+import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
@@ -65,6 +67,27 @@ public class NodeInformationPanel extends JPanel {
panel.setLayout(new BorderLayout(0, 0));
tabbedPane.addTab(UIEnv.getRsb().getString("overview"), null, panel, null);
+ JTextArea overviewArea = new JTextArea();
+ overviewArea.setEditable(false);
+ overviewArea.setLineWrap(true);
+ overviewArea.setWrapStyleWord(true);
+ overviewArea.setFont(UIEnv.getFont().deriveFont(14.0f));
+ overviewArea.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
+ StringBuilder sb=new StringBuilder();
+ sb.append(UIEnv.getRsb().getString("ipv6addr")).append(": ").append(address.toCompressedString()).append('\n');
+ String dname=controller.getIpv6Router().getKlalbRouteProtol().getDeviceName(address);
+ if(dname!=null) {
+ sb.append('\n').append(UIEnv.getRsb().getString("devicename")).append(": ").append(dname).append('\n');
+ }
+ if(address.equals(controller.getIpv6Router().getLocator().getAddress())&&controller.getConfigItem()!=null) {
+ String ddesc=controller.getConfigItem().getDeviceDescription();
+ if(ddesc!=null&&!ddesc.isEmpty()) {
+ sb.append('\n').append(UIEnv.getRsb().getString("devicedescription")).append(":\n").append(ddesc).append('\n');
+ }
+ }
+ overviewArea.setText(sb.toString());
+ panel.add(new JScrollPane(overviewArea), BorderLayout.CENTER);
+
JPanel panel_1 = new JPanel();
panel_1.setLayout(new BorderLayout(0, 0));
panel_1.add(scrollPane);
diff --git a/src/org/kne/cloud/network/srv6/KLALBRoutingProtocol.java b/src/org/kne/cloud/network/srv6/KLALBRoutingProtocol.java
index db71a24..372a6cd 100644
--- a/src/org/kne/cloud/network/srv6/KLALBRoutingProtocol.java
+++ b/src/org/kne/cloud/network/srv6/KLALBRoutingProtocol.java
@@ -545,6 +545,7 @@ public class KLALBRoutingProtocol extends Thread{
Listlinks=router.getLinkTabel();
Object[] nls=links.toArray();
RouterInfo ri=new RouterInfo(System.currentTimeMillis(),router.getLocator(),router.getASN());
+ ri.setDeviceName(router.getDeviceName());
for(int i=0;i getAddresses() {
return addresses;
}
+
+ /**
+ * 查询某地址广播的设备名称(未知返回null)
+ */
+ public String getDeviceName(IPv6Address address) {
+ RouterInfo ri=netmap.get(address);
+ if(ri!=null) {
+ String dn=ri.getDeviceName();
+ if(dn!=null&&!dn.isEmpty())
+ return dn;
+ }
+ return null;
+ }
public Map> getPaths() {
return paths;
}
diff --git a/src/org/kne/cloud/network/srv6/RouterInfo.java b/src/org/kne/cloud/network/srv6/RouterInfo.java
index ceffabd..ae46e1f 100644
--- a/src/org/kne/cloud/network/srv6/RouterInfo.java
+++ b/src/org/kne/cloud/network/srv6/RouterInfo.java
@@ -60,6 +60,7 @@ public class RouterInfo implements Serializable{
return Objects.equals(locator, other.locator) && Objects.equals(neighborAddresses, other.neighborAddresses);
}
private List neighborAddresses=new ArrayList<>();
+ private String deviceName=""; // 设备名称(广播时携带,描述不广播)
private long createTime;
@@ -90,6 +91,14 @@ public class RouterInfo implements Serializable{
return neighborAddresses;
}
+ public String getDeviceName() {
+ return deviceName;
+ }
+
+ public void setDeviceName(String deviceName) {
+ this.deviceName = deviceName;
+ }
+
public RouterInfo( long createTime,IPv6AddressGroup locator,long asn) {
this.createTime = createTime;
this.locator=locator;
@@ -106,6 +115,7 @@ public class RouterInfo implements Serializable{
for( NeighborInfo neighborInfo :neighborAddresses) {
neighborInfo.writeToStream(out);
}
+ out.writeUTF(deviceName==null?"":deviceName);
}
protected void writeToChannel(WritableByteChannel dto) throws IOException {
@@ -126,6 +136,7 @@ public class RouterInfo implements Serializable{
neighborAddresses.add(nif);
}
+ deviceName=in.readUTF();
}
}
diff --git a/src/org/kne/cloud/network/srv6/SRv6Router.java b/src/org/kne/cloud/network/srv6/SRv6Router.java
index 5653179..ea513ca 100644
--- a/src/org/kne/cloud/network/srv6/SRv6Router.java
+++ b/src/org/kne/cloud/network/srv6/SRv6Router.java
@@ -294,6 +294,15 @@ public class SRv6Router {
private IPv6AddressGroup locator; // SRv6定位器
private long asn = new SecureRandom().nextLong(1, Long.MAX_VALUE); // 自治系统号
+ private volatile String deviceName; // 设备名称(随路由信息广播)
+
+ public String getDeviceName() {
+ return deviceName;
+ }
+
+ public void setDeviceName(String deviceName) {
+ this.deviceName = deviceName;
+ }
public IPv6AddressGroup getLocator() {
return locator;