impl Web UI (dashboard) #2

Merged
KNEMC merged 11 commits from SerinaNya/KLALB:patch-2 into master 2026-08-26 19:57:29 +08:00
16 changed files with 265 additions and 322 deletions
Showing only changes of commit e0296e0ccd - Show all commits
+11 -3
View File
@@ -29,7 +29,7 @@ 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 `"enableTUN": false` in `klalb-config.json` or toggle off "启用 TUN 虚拟网卡" in GUI/Web settings.
- Routing broadcast (`RouterInfo`) transmits `deviceName` across the network, which topology and node overview panels display. `deviceDescription` and `ExtraRoutes` remain local controller configs.
- Routing broadcast (`RouterInfo`) transmits `deviceName`, which topology and node overview panels display. `deviceDescription` is NOT broadcast — it only leaves the node in full node-info query responses (see srv6 API below); `ExtraRoutes` remain local controller configs.
## Verification
@@ -46,12 +46,18 @@ No test suite, no CI. Classes named `*Test*` (`nathole/`, `ntp/`) are manual `ma
- `...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.
- **Node-info query API** (`...network.srv6`, JSON datagrams on `KLALBRoutingProtocol.DEFAULT_PORT=1001`): `KLALBRoutingProtocolAPIServer/Client` speak two request types —
- `nodeinfotinyreq/resp` → device name ONLY; never gated by any flag (name is public via broadcast anyway).
- `nodeinfofullreq/resp` → externalEndpoints + deviceName + deviceDescription. `denyExternalEndpointQuery=true` hides ONLY the endpoint list (`data=null`); name/description still answer.
- GUI rule: opening `NodeInformationPanel` = Full query; use `requestNodeInfoTiny` for lightweight/background lookups. Legacy `openlines*` message types were removed — mixed-version meshes get silence, so upgrade the whole network together.
- `JsonDataPacket` stores its UTF-8 payload length in a 2-byte header field: keep every JSON message under 64 KiB.
- `...network.frpc` — frp client integration.
- `...klalb.ui` — all Swing UI code.
## Frontend (Dashboard)
Located in `dashboard/`:
- **Git layout**: `dashboard/` is a separate git repo wired in as a submodule (own origin on `git.code.cq.cn`). Commit frontend changes inside `dashboard/` first, then bump the submodule pointer in the parent repo — parent-repo commits alone do not capture them.
- **Stack**: Vite + React 19 + TypeScript + Tailwind CSS v4 + `@base-ui/react` (style: `base-nova`, icons: `lucide-react`, toasts: `@base-ui/react/toast`).
- **Routing**: Hash-based routing (`#/overview`, `#/connections`, `#/topology`, `#/settings`, etc.) for seamless SPA hosting under Java `KLALBWebServer`.
- **Package Manager**: `pnpm` (run all commands from `dashboard/` directory).
@@ -72,12 +78,14 @@ Located in `dashboard/`:
`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`).
Key controller config fields:
- `openConnections` / `autoConnections`: published vs auto-connect endpoint lists (replaces legacy `LineTable` / `ConnectLineTable`).
- `externalEndpoints` / `autoConnections`: published vs auto-connect endpoint lists (renamed from `openConnections`, which itself replaced legacy `LineTable`; the old name was a developer naming mistake — these addresses are this node's externally published endpoints, not "connections").
- `ntpServers`: time server list (replaces `ntpServerTable`).
- `denyConnectionQuery` / `denyConnectionBroadcast`: query & discovery broadcast safety flags (replaces `denyLineTableQuery` / `denyLineTableBroadcast`).
- `denyExternalEndpointQuery` / `denyExternalEndpointBroadcast`: safety flags — the query flag hides ONLY the external-endpoint list in full node-info responses (device name/description still answer; Tiny queries are never gated), the broadcast flag disables LAN multicast discovery (renamed from `denyConnectionQuery` / `denyConnectionBroadcast`, which replaced `denyLineTableQuery` / `denyLineTableBroadcast`).
- `enableTUN`: boolean flag for TUN interface creation (`"TUNName"` configures device name).
- `webPort`: default `4665`.
Legacy JSON keys are still accepted on load: `KLALBConfigItem.getDefaultJsonDeserializer()` normalizes old key names (`openConnections`/`LineTable`, `denyConnectionQuery`, `denyLineTable*`, ...) before reflective deserialization (manual rewrite because gson-2.1 has no `@SerializedName(alternate=...)`), and `handleConfig` in the web server accepts them too. New saves always write canonical names.
Gson quirks:
- **gson-2.1 (vendored) is ancient**: its `JSON_ELEMENT` adapter factory only matches exact `JsonElement.class`, NOT subclasses. Calling `gson.toJson(Object)` with a runtime `JsonObject`/`JsonArray` reflectively serializes the internal field as `{"members": {...}}`. `KLALBWebServer.sendJsonResponse` guards against this by using `JsonElement.toString()` for JsonElement instances — keep that guard when adding new response paths. SSE avoids the issue entirely via `JsonObject.toString()`.
- `/api/config` GET/POST is parsed field-by-field in `KLALBWebServer.handleConfig` (NOT whole-object Gson reflection) because polymorphic fields (`List<InetAddress>`, `List<MultiProtocolSocketAddress>`) break reflective mapping. Keep new config fields in sync there, accepting both legacy and new JSON key names.
+3 -3
View File
@@ -10,7 +10,7 @@
"TCPListen": "tcp://0.0.0.0:4565",
"UDPListen": "udp://0.0.0.0:4572",
"VirtualSocketName": "kltp",
"openConnections": [
"externalEndpoints": [
"tcp://kne03.yoyo250.fun:4565",
"tcp://kne04.yoyo250.fun:4565",
"tcp://07f4acdef99b.ofalias.net:4565",
@@ -41,8 +41,8 @@
"ntp://us.ntp.org.cn"
],
"ExtraRoutes": [],
"denyConnectionQuery": false,
"denyConnectionBroadcast": false,
"denyExternalEndpointQuery": false,
"denyExternalEndpointBroadcast": false,
"congestionAlgorithm": "BBR",
"burstLimit": 2.0,
"delayUpperBound": 1.2,
@@ -61,6 +61,7 @@ public class KLALBConfigItem {
String s=jobj.get("Type").getAsString();
switch(s) {
case "KLALBController":
normalizeLegacyControllerKeys(jobj);
return arg2.deserialize(arg0, new TypeToken<KLALBControllerConfigItem>() {}.getType());
case "SocketBridge":
return arg2.deserialize(arg0, new TypeToken<SocketBridgeConfigItem>() {}.getType());
@@ -70,6 +71,34 @@ public class KLALBConfigItem {
}
throw new JsonParseException("not a object:"+arg0);
}
/**
* 将历史命名错误的配置键归一化为当前键名(仅在当前键名不存在时生效):
* openConnections/lineTable → externalEndpoints
* denyConnectionQuery/denyLineTableQuery → denyExternalEndpointQuery
* denyConnectionBroadcast/denyLineTableBroadcast → denyExternalEndpointBroadcast。
*/
private void normalizeLegacyControllerKeys(JsonObject jobj) {
renameLegacyKey(jobj,"externalEndpoints","openConnections","OpenConnections","lineTable","LineTable");
renameLegacyKey(jobj,"denyExternalEndpointQuery","denyConnectionQuery","denyLineTableQuery");
renameLegacyKey(jobj,"denyExternalEndpointBroadcast","denyConnectionBroadcast","denyLineTableBroadcast");
}
private void renameLegacyKey(JsonObject jobj, String newKey, String... legacyKeys) {
if (jobj.has(newKey)) {
for (String legacyKey : legacyKeys) {
jobj.remove(legacyKey);
}
return;
}
for (String legacyKey : legacyKeys) {
if (jobj.has(legacyKey)) {
jobj.add(newKey, jobj.get(legacyKey));
jobj.remove(legacyKey);
return;
}
}
}
};
}
public static JsonSerializer<KLALBConfigItem>getDefaultJsonSerializer(){
@@ -76,7 +76,7 @@ public class KLALBController {
new HashMapTimestampMonitor<UUID>(HighAccuracyClock.SYSTEM_CLOCK, "up", 100, TIME_WINDOW),
new HashMapTimestampMonitor<UUID>(HighAccuracyClock.SYSTEM_CLOCK, "down", 100, TIME_WINDOW));
private List<MultiProtocolSocketAddress> selflineTable = new ArrayList<>();
private List<MultiProtocolSocketAddress> externalEndpoints = new ArrayList<>();
private List<MultiProtocolSocketAddress> listensSocketAddress = new CopyOnWriteArrayList<>();
@@ -137,9 +137,9 @@ public class KLALBController {
MultiProtocolSocketAddress bind = new MultiProtocolSocketAddress(tcpl.getProtocol(),
inetAddress.getHostAddress(), tcpl.getPort());
// System.out.println(bind);
synchronized (selflineTable) {
if (!selflineTable.contains(bind)) {
selflineTable.add(bind);
synchronized (externalEndpoints) {
if (!externalEndpoints.contains(bind)) {
externalEndpoints.add(bind);
}
}
}
@@ -210,7 +210,7 @@ public class KLALBController {
for (Iterator<IPMulticastDiscovery> iterator = ipmd.iterator(); iterator.hasNext(); ) {
IPMulticastDiscovery ipMulticastDiscovery = (IPMulticastDiscovery) iterator.next();
if (ipMulticastDiscovery.isClosed() || (!ipMulticastDiscovery.getInterface().isUp())
|| (configItem != null && configItem.isDenyConnectionBroadcast())) {
|| (configItem != null && configItem.isDenyExternalEndpointBroadcast())) {
iterator.remove();
try {
ipMulticastDiscovery.close();
@@ -222,7 +222,7 @@ public class KLALBController {
}
}
if (configItem != null && configItem.isDenyConnectionBroadcast()) {
if (configItem != null && configItem.isDenyExternalEndpointBroadcast()) {
} else {
List<NetworkInterface> interfaceList = networkInterfaceManager.getAllAvaliableNetworkInterface();
@@ -299,7 +299,7 @@ public class KLALBController {
private boolean checkIsSelf(MultiProtocolSocketAddress inetAddress) throws UnknownHostException {
return inetAddress.getInetAddress().isAnyLocalAddress() || inetAddress.getInetAddress().isLoopbackAddress()
|| selflineTable.contains(inetAddress);
|| externalEndpoints.contains(inetAddress);
}
private boolean checkIsSelfLocator(InetAddress inetAddress) {
@@ -339,9 +339,9 @@ public class KLALBController {
try {
InetSocketAddress iaddr = new InetSocketAddress(neighbor.getAddress().toInet6Address(),
KLALBRoutingProtocol.DEFAULT_PORT);
apiClient.requestOpenLines(iaddr, (v) -> {
apiClient.requestNodeInfoFull(iaddr, (v) -> {
ThreadTool.makeVDaemonThreadIfSupport("线路添加任务", () -> {
for (MultiProtocolSocketAddress msa : v) {
for (MultiProtocolSocketAddress msa : v.getOpenLines()) {
// System.out.print(msa);
addRemoteLines(msa);
}
@@ -401,8 +401,8 @@ public class KLALBController {
}
public List<MultiProtocolSocketAddress> getSelflineTable() {
return selflineTable;
public List<MultiProtocolSocketAddress> getExternalEndpoints() {
return externalEndpoints;
}
@@ -593,18 +593,18 @@ public class KLALBController {
lineslock.writeLock().lock();
try {
krs.startIO();
String selflineTable = generateSelfLineTable();
if (selflineTable != null && !selflineTable.equals(""))
krs.sendPacket(new ADDLINESPacket(selflineTable));
String externalEndpointsText = generateExternalEndpointsString();
if (externalEndpointsText != null && !externalEndpointsText.equals(""))
krs.sendPacket(new ADDLINESPacket(externalEndpointsText));
srv6Router.getLinkTabel().add(krs);
} finally {
lineslock.writeLock().unlock();
}
}
private String generateSelfLineTable() {
private String generateExternalEndpointsString() {
StringBuilder sbd = new StringBuilder();
for (Iterator<MultiProtocolSocketAddress> iterator = selflineTable.iterator(); iterator.hasNext();) {
for (Iterator<MultiProtocolSocketAddress> iterator = externalEndpoints.iterator(); iterator.hasNext();) {
MultiProtocolSocketAddress klalbRemoteLine = (MultiProtocolSocketAddress) iterator.next();
sbd.append(klalbRemoteLine.toString());
sbd.append('\n');
@@ -801,9 +801,9 @@ public class KLALBController {
getIpv6Router().setASN(vasn);
}
List<MultiProtocolSocketAddress> linele = configItem.getOpenConnections();
List<MultiProtocolSocketAddress> linele = configItem.getExternalEndpoints();
if (linele != null) {
getSelflineTable().addAll(linele);
getExternalEndpoints().addAll(linele);
}
List<MultiProtocolSocketAddress> linetoc = configItem.getAutoConnections();
if (linetoc != null) {
@@ -16,12 +16,12 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
private MultiProtocolSocketAddress TCPListen=new MultiProtocolSocketAddress("0.0.0.0",4565);
private MultiProtocolSocketAddress UDPListen=new MultiProtocolSocketAddress("udp","0.0.0.0",4565);
private String VirtualSocketName;
private List<MultiProtocolSocketAddress> openConnections = new ArrayList<>();
private List<MultiProtocolSocketAddress> externalEndpoints = new ArrayList<>();
private List<MultiProtocolSocketAddress> autoConnections = new ArrayList<>();
private List<MultiProtocolSocketAddress> ntpServers = new ArrayList<>();
private List<String> ExtraRoutes = new ArrayList<>();
private boolean denyConnectionQuery = false;
private boolean denyConnectionBroadcast = false;
private boolean denyExternalEndpointQuery = false;
private boolean denyExternalEndpointBroadcast = false;
private String congestionAlgorithm="BBR";
private double burstLimit=1.50;
private double delayUpperBound=1.20;
@@ -204,20 +204,12 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
public List<MultiProtocolSocketAddress> getOpenConnections() {
return openConnections;
public List<MultiProtocolSocketAddress> getExternalEndpoints() {
return externalEndpoints;
}
public void setOpenConnections(List<MultiProtocolSocketAddress> openConnections) {
this.openConnections = openConnections;
}
public List<MultiProtocolSocketAddress> getLineTable() {
return openConnections;
}
public void setLineTable(List<MultiProtocolSocketAddress> lineTable) {
this.openConnections = lineTable;
public void setExternalEndpoints(List<MultiProtocolSocketAddress> externalEndpoints) {
this.externalEndpoints = externalEndpoints;
}
public List<MultiProtocolSocketAddress> getAutoConnections() {
@@ -320,36 +312,20 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
public boolean isDenyConnectionQuery() {
return denyConnectionQuery;
public boolean isDenyExternalEndpointQuery() {
return denyExternalEndpointQuery;
}
public void setDenyConnectionQuery(boolean denyConnectionQuery) {
this.denyConnectionQuery = denyConnectionQuery;
public void setDenyExternalEndpointQuery(boolean denyExternalEndpointQuery) {
this.denyExternalEndpointQuery = denyExternalEndpointQuery;
}
public boolean isDenyLineTableQuery() {
return denyConnectionQuery;
public boolean isDenyExternalEndpointBroadcast() {
return denyExternalEndpointBroadcast;
}
public void setDenyLineTableQuery(boolean denyLineTableQuery) {
this.denyConnectionQuery = denyLineTableQuery;
}
public boolean isDenyConnectionBroadcast() {
return denyConnectionBroadcast;
}
public void setDenyConnectionBroadcast(boolean denyConnectionBroadcast) {
this.denyConnectionBroadcast = denyConnectionBroadcast;
}
public boolean isDenyLineTableBroadcast() {
return denyConnectionBroadcast;
}
public void setDenyLineTableBroadcast(boolean denyLineTableBroadcast) {
this.denyConnectionBroadcast = denyLineTableBroadcast;
public void setDenyExternalEndpointBroadcast(boolean denyExternalEndpointBroadcast) {
this.denyExternalEndpointBroadcast = denyExternalEndpointBroadcast;
}
@@ -405,12 +381,12 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
", TCPListen=" + TCPListen +
", UDPListen=" + UDPListen +
", VirtualSocketName='" + VirtualSocketName + '\'' +
", openConnections=" + openConnections +
", externalEndpoints=" + externalEndpoints +
", autoConnections=" + autoConnections +
", ntpServers=" + ntpServers +
", ExtraRoutes=" + ExtraRoutes +
", denyConnectionQuery=" + denyConnectionQuery +
", denyConnectionBroadcast=" + denyConnectionBroadcast +
", denyExternalEndpointQuery=" + denyExternalEndpointQuery +
", denyExternalEndpointBroadcast=" + denyExternalEndpointBroadcast +
", congestionAlgorithm='" + congestionAlgorithm + '\'' +
", burstLimit=" + burstLimit +
", delayUpperBound=" + delayUpperBound +
@@ -432,11 +408,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 && enableTUN == that.enableTUN && denyConnectionQuery == that.denyConnectionQuery && denyConnectionBroadcast == that.denyConnectionBroadcast && 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(openConnections, that.openConnections) && Objects.equals(autoConnections, that.autoConnections) && Objects.equals(ntpServers, that.ntpServers) && 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);
return nogui == that.nogui && enableTUN == that.enableTUN && denyExternalEndpointQuery == that.denyExternalEndpointQuery && denyExternalEndpointBroadcast == that.denyExternalEndpointBroadcast && 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(externalEndpoints, that.externalEndpoints) && Objects.equals(autoConnections, that.autoConnections) && Objects.equals(ntpServers, that.ntpServers) && 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, openConnections, autoConnections, ntpServers, ExtraRoutes, denyConnectionQuery, denyConnectionBroadcast, congestionAlgorithm, burstLimit, delayUpperBound, delayLowerBound, nagleDelayTime, linkNagleDelayTime, linkConnectionsCount, enableTUN, TUNName, performanceStrategy, DeviceName, DeviceDescription, NetworkInterfaceExcepts);
return Objects.hash(super.hashCode(), language, nogui, VirtualAddress, VirtualASN, DNS, TCPListen, UDPListen, VirtualSocketName, externalEndpoints, autoConnections, ntpServers, ExtraRoutes, denyExternalEndpointQuery, denyExternalEndpointBroadcast, congestionAlgorithm, burstLimit, delayUpperBound, delayLowerBound, nagleDelayTime, linkNagleDelayTime, linkConnectionsCount, enableTUN, TUNName, performanceStrategy, DeviceName, DeviceDescription, NetworkInterfaceExcepts);
}
}
@@ -61,11 +61,6 @@ public class KLALBMain {
}
dtb.putTime("UI");
//dtb.print();
/*MultipurposeSocketAddress mpa=new MultipurposeSocketAddress("127.9.9.9", 49573);
kpcje.enableRemoteManagement(mpa);
System.out.println("远程管理端口已在"+mpa+"端口上开启");*/
/*if(true)
return;*/
ServerSocketChannel kpsvr=KLALBVirtualServerSocketChannel.open(kpcje.getKlalbController());
kpsvr.bind(new InetSocketAddress("::0", 4564));
SocketChannelListener stlr=new SocketChannelListener(kpsvr);
@@ -32,7 +32,6 @@ import com.google.gson.JsonParser;
public class KLALBProxySystem {
private Set<Proxy> proxys=new HashSet<>();
private KLALBController klalbController;
private KLALBRemoteManagement krm;
private KLALBWebServer webServer;
private KLALBConfig config;
private Gson gson;
@@ -85,33 +84,6 @@ public class KLALBProxySystem {
public KLALBProxySystem() {
}
public void enableRemoteManagement() throws IOException {
if(krm==null) {
krm=new KLALBRemoteManagement(this);
}else {
throw new IllegalStateException("Remote Management already enabled!");
}
}
public void enableRemoteManagement(MultiProtocolSocketAddress bind) throws IOException {
if(krm==null) {
krm=new KLALBRemoteManagement(this,bind);
}else {
throw new IllegalStateException("Remote Management already enabled!");
}
}
public boolean isRemoteManagementEnabled() {
return krm!=null;
}
public void disableRemoteManagement() {
if(krm!=null) {
krm.close();
krm=null;
}
}
public void enableWebServer() throws IOException {
int port = 4665;
KLALBControllerConfigItem cci = getControllerConfig();
@@ -1,174 +0,0 @@
package org.kne.cloud.network.klalb;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.List;
import org.kne.cloud.network.MultiProtocolSocketAddress;
import org.kne.cloud.network.SocketListener;
import org.kne.cloud.network.ipv6.IPv6NetworkLink;
import org.kne.cloud.network.monitor.LinkStatus;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
public class KLALBRemoteManagement {
SocketListener slr;
private KLALBProxySystem klalbProxySystem;
public KLALBRemoteManagement(KLALBProxySystem klalbProxySystem) throws IOException {
this(klalbProxySystem,new MultiProtocolSocketAddress("127.9.9.9", 49573));
}
public KLALBRemoteManagement(KLALBProxySystem klalbProxySystem, MultiProtocolSocketAddress listen) throws IOException {
this.klalbProxySystem=klalbProxySystem;
slr=new SocketListener(listen);
slr.setCon((srcv)->{
try {
srcv.setSoTimeout(10000);
byte[]input= srcv.getInputStream().readAllBytes();
srcv.shutdownInput();
String req=new String(input,Charset.forName("UTF-8"));
System.out.println("远程管理请求:"+req);
String rsp=processSignal(req);
System.out.println("远程管理响应:"+rsp);
srcv.getOutputStream().write(rsp.getBytes(Charset.forName("UTF-8")));
srcv.shutdownOutput();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
srcv.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
public KLALBProxySystem getKlalbProxySystem() {
return klalbProxySystem;
}
private String processSignal(String req) {
JsonObject jreq= (JsonObject) new JsonParser().parse(req);
JsonObject jrsp=new JsonObject();
String reqt=jreq.getAsJsonPrimitive("REQ").getAsString();
jrsp.addProperty("RSP", reqt);
switch (reqt) {
case "GETLINES":
JsonArray lines=new JsonArray();
List<IPv6NetworkLink>lineslist= klalbProxySystem.getKlalbController().getLines();
synchronized (lineslist) {
for (Iterator<IPv6NetworkLink> iterator = lineslist.iterator(); iterator.hasNext();) {
IPv6NetworkLink link=iterator.next();
if(!(link instanceof KLALBRemoteLink)) {
continue;
}
KLALBRemoteLink klalbRemoteLine = (KLALBRemoteLink) link;
if(klalbRemoteLine.getSocketAddress()==null)
continue;
JsonObject jklbrl=new JsonObject();
jklbrl.addProperty("ipport", klalbRemoteLine.getSocketAddress().toString());
jklbrl.addProperty("state",LinkStatus.stateToString( klalbRemoteLine.getState()));
jklbrl.addProperty("Vaddr", klalbRemoteLine.getRemoteVaddr().getAddress().toString());
jklbrl.addProperty("uploadspeed", klalbRemoteLine.getMonitor().getOutSpeed());
jklbrl.addProperty("downloadspeed", klalbRemoteLine.getMonitor().getInSpeed());
jklbrl.addProperty("uploadtraffic", klalbRemoteLine.getMonitor().getOutTraffic());
jklbrl.addProperty("downloadtraffic", klalbRemoteLine.getMonitor().getInTraffic());
jklbrl.addProperty("uploaddelay",klalbRemoteLine.getMonitor().getOutDelay() );
jklbrl.addProperty("downloaddelay", klalbRemoteLine.getMonitor().getInDelay());
jklbrl.addProperty("uploaddelaymin",klalbRemoteLine.getMonitor().getOutDelayMin() );
jklbrl.addProperty("downloaddelaymin", klalbRemoteLine.getMonitor().getInDelayMin());
lines.add(jklbrl);
}
}
jrsp.add("table", lines);
break;
case "ADDLINE":
String mip=jreq.getAsJsonPrimitive("ipport").getAsString();
try {
jrsp.addProperty ("Vaddr",klalbProxySystem.getKlalbController().getRemoteVaddrBySocketAddress(new MultiProtocolSocketAddress(mip)).getHostAddress());
} catch (SocketTimeoutException e) {
e.printStackTrace();
}
break;
case "GETSELFLINES":
JsonArray lines2=new JsonArray();
List<MultiProtocolSocketAddress>selflineslist=klalbProxySystem.getKlalbController().getSelflineTable();
synchronized (selflineslist) {
for (Iterator<MultiProtocolSocketAddress> iterator = selflineslist.iterator(); iterator.hasNext();) {
MultiProtocolSocketAddress multiProtocolSocketAddress = (MultiProtocolSocketAddress) iterator.next();
lines2.add(new JsonPrimitive(multiProtocolSocketAddress.toString()));
}
}
jrsp.add("table", lines2);
break;
case "ADDSELFLINE":
String mips=jreq.getAsJsonPrimitive("ipport").getAsString();
List<MultiProtocolSocketAddress>selflineslist2=klalbProxySystem.getKlalbController().getSelflineTable();
synchronized (selflineslist2) {
selflineslist2.add(new MultiProtocolSocketAddress(mips));
}
break;
case "REMOVESELFLINE":
String mipsr=jreq.getAsJsonPrimitive("ipport").getAsString();
List<MultiProtocolSocketAddress>selflineslist21=klalbProxySystem.getKlalbController().getSelflineTable();
synchronized (selflineslist21) {
selflineslist21.add(new MultiProtocolSocketAddress(mipsr));
}
break;
case "GETLINKMONITOR":
jrsp.addProperty("uploadspeed", klalbProxySystem.getKlalbController().getLinkMonitor().getOutSpeed());
jrsp.addProperty("downloadspeed", klalbProxySystem.getKlalbController().getLinkMonitor().getInSpeed());
jrsp.addProperty("uploadtraffic", klalbProxySystem.getKlalbController().getLinkMonitor().getOutTraffic());
jrsp.addProperty("downloadtraffic", klalbProxySystem.getKlalbController().getLinkMonitor().getInTraffic());
break;
case "OPENMONITORUI":
klalbProxySystem.getKLALBGUI().setVisible(true);
break;
/*case "GETSOCKETBRIDGE":
JsonArray bridges=new JsonArray();
Set<Proxy> pxy=klalbProxySystem.getProxys();
synchronized (pxy) {
for (Iterator<Proxy> iterator = pxy.iterator(); iterator.hasNext();) {
Proxy proxy = (Proxy) iterator.next();
bridges.add(klalbProxySystem.createJsonObjectByProxy(proxy));
}
}
jrsp.add("table", bridges);
break;
case "ADDSOCKETBRIDGE":
JsonObject jpxy= jreq.getAsJsonObject("socketbridge");
Set<Proxy> pxy2=klalbProxySystem.getProxys();
synchronized (pxy2) {
try {
pxy2.add(klalbProxySystem.createProxyByJson(jpxy));
} catch (IOException e) {
e.printStackTrace();
}
}
break;*/
default:
System.out.println("未知请求类型:"+reqt);
break;
}
return jrsp.toString();
}
public void close() {
slr.close();
}
}
@@ -1190,7 +1190,7 @@ public class KLALBStateGUI3 extends XFrame {
}
}
}
kck.setLineTable(iaddr1);
kck.setExternalEndpoints(iaddr1);
// 保存自动连接线路表
String[] splt11 = connectLineTabelSet.getText().split("\n");
@@ -1243,9 +1243,9 @@ public class KLALBStateGUI3 extends XFrame {
kck.setLinkConnectionsCount(linkConnectionsCount.getSlider().getValue());
kck.setDenyLineTableQuery(denyQuery.isSelected());
kck.setDenyExternalEndpointQuery(denyQuery.isSelected());
kck.setDenyLineTableBroadcast(denyBroadcast.isSelected());
kck.setDenyExternalEndpointBroadcast(denyBroadcast.isSelected());
PerformanceStrategyItem psi=((PerformanceStrategyItem)comboPerformance.getSelectedItem());
if(psi!=null) {
@@ -1550,7 +1550,7 @@ public class KLALBStateGUI3 extends XFrame {
udpListeningSet.setText(mpau != null ? mpau.toString() : "");
// 加载开放线路表
List<MultiProtocolSocketAddress> linet = kck.getLineTable();
List<MultiProtocolSocketAddress> linet = kck.getExternalEndpoints();
openLineTabelSet.setText(listToStr2(linet));
// 加载自动连接线路表
@@ -1579,9 +1579,9 @@ public class KLALBStateGUI3 extends XFrame {
}
linkConnectionsCount.getSlider().setValue(conc);
denyQuery.setSelected( kck.isDenyLineTableQuery());
denyQuery.setSelected( kck.isDenyExternalEndpointQuery());
denyBroadcast.setSelected( kck.isDenyLineTableBroadcast());
denyBroadcast.setSelected( kck.isDenyExternalEndpointBroadcast());
String stategy= kck.getPerformanceStrategy();
PerformanceStrategy pfs=PerformanceStrategy.fromDescription(stategy);
@@ -73,19 +73,15 @@ public class NodeInformationPanel extends JPanel {
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');
}
String ddesc=null;
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');
ddesc=controller.getConfigItem().getDeviceDescription();
if(ddesc!=null&&ddesc.isEmpty()) {
ddesc=null;
}
}
overviewArea.setText(sb.toString());
overviewArea.setText(buildOverviewText(dname, ddesc));
panel.add(new JScrollPane(overviewArea), BorderLayout.CENTER);
JPanel panel_1 = new JPanel();
@@ -169,16 +165,38 @@ public class NodeInformationPanel extends JPanel {
client=new KLALBRoutingProtocolAPIClient(controller.getIpv6Router().getKlalbRouteProtol());
try {
client.requestOpenLines(new InetSocketAddress( address.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT), (result)->{
client.requestNodeInfoFull(new InetSocketAddress( address.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT), (info)->{
listModel.clear();
for (MultiProtocolSocketAddress multiProtocolSocketAddress : result) {
for (MultiProtocolSocketAddress multiProtocolSocketAddress : info.getOpenLines()) {
listModel.addElement(multiProtocolSocketAddress);
}
// 用对端返回的设备名称/描述更新概览(旧版本节点无该字段时保留原显示)
String dn=info.getDeviceName();
if(dn==null||dn.isEmpty()) {
dn=controller.getIpv6Router().getKlalbRouteProtol().getDeviceName(address);
}
String dd=info.getDeviceDescription();
if(dd!=null&&dd.isEmpty()) {
dd=null;
}
overviewArea.setText(buildOverviewText(dn, dd));
});
} catch (IOException e) {
e.printStackTrace();
}
}
private String buildOverviewText(String dname,String ddesc) {
StringBuilder sb=new StringBuilder();
sb.append(UIEnv.getRsb().getString("ipv6addr")).append(": ").append(address.toCompressedString()).append('\n');
if(dname!=null&&!dname.isEmpty()) {
sb.append('\n').append(UIEnv.getRsb().getString("devicename")).append(": ").append(dname).append('\n');
}
if(ddesc!=null&&!ddesc.isEmpty()) {
sb.append('\n').append(UIEnv.getRsb().getString("devicedescription")).append(":\n").append(ddesc).append('\n');
}
return sb.toString();
}
public Icon getIcon() {
return new ImageIcon(image);
}
@@ -625,8 +625,10 @@ public class KLALBWebServer {
current.setUDPListen(new MultiProtocolSocketAddress(json.get("UDPListen").getAsString()));
}
if (json.has("openConnections") || json.has("OpenConnections") || json.has("lineTable") || json.has("LineTable")) {
JsonArray arr = json.has("openConnections") ? json.getAsJsonArray("openConnections")
if (json.has("externalEndpoints") || json.has("ExternalEndpoints") || json.has("openConnections") || json.has("OpenConnections") || json.has("lineTable") || json.has("LineTable")) {
JsonArray arr = json.has("externalEndpoints") ? json.getAsJsonArray("externalEndpoints")
: json.has("ExternalEndpoints") ? json.getAsJsonArray("ExternalEndpoints")
: json.has("openConnections") ? json.getAsJsonArray("openConnections")
: json.has("OpenConnections") ? json.getAsJsonArray("OpenConnections")
: json.has("lineTable") ? json.getAsJsonArray("lineTable") : json.getAsJsonArray("LineTable");
List<MultiProtocolSocketAddress> list = new ArrayList<>();
@@ -635,7 +637,7 @@ public class KLALBWebServer {
list.add(new MultiProtocolSocketAddress(el.getAsString()));
}
}
current.setOpenConnections(list);
current.setExternalEndpoints(list);
}
if (json.has("autoConnections") || json.has("AutoConnections") || json.has("connectLineTable") || json.has("ConnectLineTable")) {
@@ -731,16 +733,20 @@ public class KLALBWebServer {
current.setLinkNagleDelayTime(json.get("linkNagleDelayTime").getAsLong());
}
if (json.has("denyConnectionQuery")) {
current.setDenyConnectionQuery(json.get("denyConnectionQuery").getAsBoolean());
if (json.has("denyExternalEndpointQuery")) {
current.setDenyExternalEndpointQuery(json.get("denyExternalEndpointQuery").getAsBoolean());
} else if (json.has("denyConnectionQuery")) {
current.setDenyExternalEndpointQuery(json.get("denyConnectionQuery").getAsBoolean());
} else if (json.has("denyLineTableQuery")) {
current.setDenyConnectionQuery(json.get("denyLineTableQuery").getAsBoolean());
current.setDenyExternalEndpointQuery(json.get("denyLineTableQuery").getAsBoolean());
}
if (json.has("denyConnectionBroadcast")) {
current.setDenyConnectionBroadcast(json.get("denyConnectionBroadcast").getAsBoolean());
if (json.has("denyExternalEndpointBroadcast")) {
current.setDenyExternalEndpointBroadcast(json.get("denyExternalEndpointBroadcast").getAsBoolean());
} else if (json.has("denyConnectionBroadcast")) {
current.setDenyExternalEndpointBroadcast(json.get("denyConnectionBroadcast").getAsBoolean());
} else if (json.has("denyLineTableBroadcast")) {
current.setDenyConnectionBroadcast(json.get("denyLineTableBroadcast").getAsBoolean());
current.setDenyExternalEndpointBroadcast(json.get("denyLineTableBroadcast").getAsBoolean());
}
// Trigger GUI save consumer or save directly
@@ -0,0 +1,52 @@
package org.kne.cloud.network.srv6;
import java.util.List;
import org.kne.cloud.network.MultiProtocolSocketAddress;
/**
* 节点信息(开放线路 + 设备名称 + 设备描述),由路由协议 JSON API 查询获得。
*/
public class KLALBNodeInformation {
private List<MultiProtocolSocketAddress> openLines;
private String deviceName;
private String deviceDescription;
public KLALBNodeInformation(List<MultiProtocolSocketAddress> openLines, String deviceName,
String deviceDescription) {
super();
this.openLines = openLines;
this.deviceName = deviceName;
this.deviceDescription = deviceDescription;
}
public List<MultiProtocolSocketAddress> getOpenLines() {
return openLines;
}
public void setOpenLines(List<MultiProtocolSocketAddress> openLines) {
this.openLines = openLines;
}
public String getDeviceName() {
return deviceName;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public String getDeviceDescription() {
return deviceDescription;
}
public void setDeviceDescription(String deviceDescription) {
this.deviceDescription = deviceDescription;
}
@Override
public String toString() {
return "KLALBNodeInformation [openLines=" + openLines + ", deviceName=" + deviceName
+ ", deviceDescription=" + deviceDescription + "]";
}
}
@@ -29,20 +29,27 @@ public class KLALBRoutingProtocolAPIClient {
JsonDataPacket relate = null;
//System.out.println(ruid + " " + window.getSendmap());
switch (dataobj.getType()) {
case KLALBRoutingProtocolJsonData.OPEN_LINES_RESP:
case KLALBRoutingProtocolJsonData.NODE_INFO_FULL_RESP:
case KLALBRoutingProtocolJsonData.NODE_INFO_TINY_RESP:
if ((relate = window.ack(ruid)) != null) {
List<?> connects = (List<?>) dataobj.getData();
List<MultiProtocolSocketAddress> connectsm = new ArrayList<MultiProtocolSocketAddress>(connects.size());
for (Object open : connects) {
if (open instanceof MultiProtocolSocketAddress) {
connectsm.add((MultiProtocolSocketAddress) open);
} else {
connectsm.add(new MultiProtocolSocketAddress((String) open));
List<MultiProtocolSocketAddress> connectsm = new ArrayList<MultiProtocolSocketAddress>(
connects == null ? 0 : connects.size());
if (connects != null) {
for (Object open : connects) {
if (open instanceof MultiProtocolSocketAddress) {
connectsm.add((MultiProtocolSocketAddress) open);
} else {
connectsm.add(new MultiProtocolSocketAddress((String) open));
}
}
}
((Consumer<List<MultiProtocolSocketAddress>>) relate.getUserCallback()).accept(connectsm);
// 组装节点信息(线路 + 设备名称 + 设备描述,精简模式下线路与描述为 null)
KLALBNodeInformation info = new KLALBNodeInformation(connectsm, dataobj.getDeviceName(),
dataobj.getDeviceDescription());
((Consumer<KLALBNodeInformation>) relate.getUserCallback()).accept(info);
}
break;
}
@@ -56,11 +63,26 @@ public class KLALBRoutingProtocolAPIClient {
clr.register(this, releaser);
}
public void requestOpenLines(SocketAddress addr, Consumer<List<MultiProtocolSocketAddress>> callback)
/**
* 精简查询:仅获取对端设备名称(开销最小,适用于未查看节点详情的场景)。
*/
public void requestNodeInfoTiny(SocketAddress addr, Consumer<KLALBNodeInformation> callback)
throws IOException {
sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_INFO_TINY_REQ, addr, callback);
}
/**
* 完整查询:获取对端开放线路 + 设备名称 + 设备描述(查看节点信息时使用)。
*/
public void requestNodeInfoFull(SocketAddress addr, Consumer<KLALBNodeInformation> callback)
throws IOException {
sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_INFO_FULL_REQ, addr, callback);
}
private void sendNodeInfoRequest(String type, SocketAddress addr, Consumer<KLALBNodeInformation> callback)
throws IOException {
UUID suid = UUID.randomUUID();
KLALBRoutingProtocolJsonData json = new KLALBRoutingProtocolJsonData(
KLALBRoutingProtocolJsonData.OPEN_LINES_REQ, suid, null);
KLALBRoutingProtocolJsonData json = new KLALBRoutingProtocolJsonData(type, suid, null);
JsonDataPacket packet = new JsonDataPacket(json);
packet.setUserCallback(callback);
window.put(suid, packet);
@@ -21,11 +21,21 @@ public class KLALBRoutingProtocolAPIServer {
KLALBRoutingProtocolJsonData dataobj= data.getDecodedData();
InetSocketAddress addrs=(InetSocketAddress) addr;
switch(dataobj.getType()){
case KLALBRoutingProtocolJsonData.OPEN_LINES_REQ:
if(controller.getConfigItem()==null||(!controller.getConfigItem().isDenyConnectionQuery())) {
KLALBRoutingProtocolJsonData json=new KLALBRoutingProtocolJsonData(KLALBRoutingProtocolJsonData.OPEN_LINES_RESP,dataobj.getUuid(),controller.getSelflineTable());
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(json),addr);
case KLALBRoutingProtocolJsonData.NODE_INFO_TINY_REQ:
// 精简查询:仅返回设备名称(设备名称随路由信息广播公开,不受 denyExternalEndpointQuery 限制)
KLALBRoutingProtocolJsonData tinyjson=new KLALBRoutingProtocolJsonData(KLALBRoutingProtocolJsonData.NODE_INFO_TINY_RESP,dataobj.getUuid(),null);
tinyjson.setDeviceName(controller.getIpv6Router().getDeviceName());
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(tinyjson),addr);
break;
case KLALBRoutingProtocolJsonData.NODE_INFO_FULL_REQ:
// 完整查询:设备名称与描述总是正常响应;denyExternalEndpointQuery 仅隐藏外部端点列表
boolean denyEndpoints=controller.getConfigItem()!=null&&controller.getConfigItem().isDenyExternalEndpointQuery();
KLALBRoutingProtocolJsonData json=new KLALBRoutingProtocolJsonData(KLALBRoutingProtocolJsonData.NODE_INFO_FULL_RESP,dataobj.getUuid(),denyEndpoints?null:controller.getExternalEndpoints());
json.setDeviceName(controller.getIpv6Router().getDeviceName());// 附带本机设备名称
if(controller.getConfigItem()!=null) {
json.setDeviceDescription(controller.getConfigItem().getDeviceDescription());// 附带本机设备描述
}
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(json),addr);
break;
}
} catch (IOException e) {
@@ -3,11 +3,27 @@ package org.kne.cloud.network.srv6;
import java.util.UUID;
public class KLALBRoutingProtocolJsonData {
public static final String OPEN_LINES_REQ="openlinesreq";
public static final String OPEN_LINES_RESP="openlinesresp";
public static final String NODE_INFO_TINY_REQ="nodeinfotinyreq";// 精简查询:仅设备名称
public static final String NODE_INFO_TINY_RESP="nodeinfotinyresp";
public static final String NODE_INFO_FULL_REQ="nodeinfofullreq";// 完整查询:开放线路+设备名称+设备描述
public static final String NODE_INFO_FULL_RESP="nodeinfofullresp";
private String type;
private UUID uuid;
private Object data;
private String deviceName;// 对端设备名称
private String deviceDescription;// 对端设备描述
public String getDeviceName() {
return deviceName;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public String getDeviceDescription() {
return deviceDescription;
}
public void setDeviceDescription(String deviceDescription) {
this.deviceDescription = deviceDescription;
}
public String getType() {
return type;
}
@@ -24,6 +40,8 @@ public class KLALBRoutingProtocolJsonData {
result = prime * result + ((data == null) ? 0 : data.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
result = prime * result + ((deviceName == null) ? 0 : deviceName.hashCode());
result = prime * result + ((deviceDescription == null) ? 0 : deviceDescription.hashCode());
return result;
}
@Override
@@ -50,6 +68,16 @@ public class KLALBRoutingProtocolJsonData {
return false;
} else if (!uuid.equals(other.uuid))
return false;
if (deviceName == null) {
if (other.deviceName != null)
return false;
} else if (!deviceName.equals(other.deviceName))
return false;
if (deviceDescription == null) {
if (other.deviceDescription != null)
return false;
} else if (!deviceDescription.equals(other.deviceDescription))
return false;
return true;
}
public KLALBRoutingProtocolJsonData(String type, UUID uuid, Object data) {
@@ -60,7 +88,8 @@ public class KLALBRoutingProtocolJsonData {
}
@Override
public String toString() {
return "KLALBRoutingProtocolJsonData [type=" + type + ", uuid=" + uuid + ", data=" + data + "]";
return "KLALBRoutingProtocolJsonData [type=" + type + ", uuid=" + uuid + ", data=" + data + ", deviceName="
+ deviceName + ", deviceDescription=" + deviceDescription + "]";
}
}