forked from KNEMC/KLALB
✨ feat(web): integrate embedded web server, REST/SSE APIs and modernize configuration
- Add embedded KLALBWebServer with REST API, SSE streaming (200ms) and SPA hosting - Introduce enableTUN configuration flag with fallback and non-admin execution support - Refactor LineTable and ConnectLineTable to openConnections and autoConnections - Rename denyLineTableQuery/Broadcast to denyConnectionQuery/Broadcast with backwards compatibility - Support runtime config hot-reloading and automatic persistence to klalb-config.json - Update default Web API port to 4665 and update dashboard submodule reference
This commit is contained in:
@@ -3,3 +3,12 @@
|
|||||||
/klalbs4.json
|
/klalbs4.json
|
||||||
/klalbs.json
|
/klalbs.json
|
||||||
/klalbs2.json
|
/klalbs2.json
|
||||||
|
|
||||||
|
# Dashboard / Frontend
|
||||||
|
dashboard/node_modules/
|
||||||
|
dashboard/dist/
|
||||||
|
dashboard/.pnpm-store/
|
||||||
|
.pnpm-debug.log*
|
||||||
|
dashboard/.env.local
|
||||||
|
dashboard/.env.*.local
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ IDE metadata targets JDK 26 (`jdk-26.0.1`); the tree also compiles cleanly on JD
|
|||||||
Runtime gotchas (all verified):
|
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).
|
- 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.
|
- 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"`.
|
- 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` across the network, which topology and node overview panels display. `deviceDescription` and `ExtraRoutes` remain local controller configs.
|
||||||
|
|
||||||
## Verification
|
## Verification
|
||||||
@@ -35,7 +35,10 @@ No test suite, no CI. Classes named `*Test*` (`nathole/`, `ntp/`) are manual `ma
|
|||||||
|
|
||||||
## Architecture
|
## 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`, ...).
|
- Entrypoint `org.kne.cloud.network.klalb.KLALBMain`: load config → build `KLALBProxySystem` → open Swing GUI (`KLALBStateGUI3`) unless `"nogui": true` → start `KLALBWebServer` (if `"webUI": true` or web server enabled) → interactive console (`help`, `links-state`, `route`, `kperf`, ...).
|
||||||
|
- `org.kne.cloud.network.klalb.web.KLALBWebServer` — built-in HTTP/SSE server (JDK `HttpServer`):
|
||||||
|
- API endpoints: `/api/status`, `/api/events` (SSE stream, 200ms intervals), `/api/links`, `/api/links/action`, `/api/links/reconnect`, `/api/routes`, `/api/nodes` (topology graph), `/api/interfaces`, `/api/config`.
|
||||||
|
- Static file hosting / SPA fallback: serves `dashboard/dist/` assets if built.
|
||||||
- `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.
|
- `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.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.congestion` — pluggable congestion control (BBR, Vegas2, DCTCP...), chosen via `"congestionAlgorithm"` in config.
|
||||||
@@ -44,6 +47,17 @@ No test suite, no CI. Classes named `*Test*` (`nathole/`, `ntp/`) are manual `ma
|
|||||||
- `...network.frpc` — frp client integration.
|
- `...network.frpc` — frp client integration.
|
||||||
- `...klalb.ui` — all Swing UI code.
|
- `...klalb.ui` — all Swing UI code.
|
||||||
|
|
||||||
|
## Frontend (Dashboard)
|
||||||
|
|
||||||
|
Located in `dashboard/`:
|
||||||
|
- **Stack**: Vite + React 19 + TypeScript + Tailwind CSS v4 + `@base-ui/react` (style: `base-nova`, icons: `lucide-react`).
|
||||||
|
- **Package Manager**: `pnpm` (run all commands from `dashboard/` directory).
|
||||||
|
- **Component installation**: **Must** use CLI via `pnpm dlx shadcn@latest add <component>` (e.g. `pnpm dlx shadcn@latest add alert card badge`). Never create or fake shadcn components manually.
|
||||||
|
- **Commands**:
|
||||||
|
- `pnpm dev` — Start Vite dev server (proxies to backend or connects to API on localhost).
|
||||||
|
- `pnpm build` — Typecheck and build SPA to `dashboard/dist` (which Java `KLALBWebServer` serves directly).
|
||||||
|
- `pnpm lint` / `pnpm typecheck` — Verification.
|
||||||
|
|
||||||
## Config
|
## 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`).
|
`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`).
|
||||||
|
|||||||
+1
-1
Submodule dashboard updated: ba65c43183...4531e5366b
+16
-10
@@ -10,16 +10,19 @@
|
|||||||
"TCPListen": "tcp://0.0.0.0:4565",
|
"TCPListen": "tcp://0.0.0.0:4565",
|
||||||
"UDPListen": "udp://0.0.0.0:4572",
|
"UDPListen": "udp://0.0.0.0:4572",
|
||||||
"VirtualSocketName": "kltp",
|
"VirtualSocketName": "kltp",
|
||||||
"LineTable": [
|
"openConnections": [
|
||||||
"tcp://kne03.yoyo250.fun:4565",
|
"tcp://kne03.yoyo250.fun:4565",
|
||||||
"tcp://kne04.yoyo250.fun:4565"
|
"tcp://kne04.yoyo250.fun:4565",
|
||||||
],
|
|
||||||
"ConnectLineTable": [
|
|
||||||
"tcp://07f4acdef99b.ofalias.net:4565",
|
"tcp://07f4acdef99b.ofalias.net:4565",
|
||||||
"tcp://kne01.yoyo250.fun:4565",
|
"tcp://kne01.yoyo250.fun:4565",
|
||||||
"tcp://kne02.yoyo250.fun:4565"
|
"tcp://kne02.yoyo250.fun:4565"
|
||||||
],
|
],
|
||||||
"ntpServerTable": [
|
"autoConnections": [
|
||||||
|
"tcp://07f4acdef99b.ofalias.net:4565",
|
||||||
|
"tcp://kne01.yoyo250.fun:4565",
|
||||||
|
"tcp://kne02.yoyo250.fun:4565"
|
||||||
|
],
|
||||||
|
"ntpServers": [
|
||||||
"ntp://ntp1.aliyun.com",
|
"ntp://ntp1.aliyun.com",
|
||||||
"ntp://ntp2.aliyun.com",
|
"ntp://ntp2.aliyun.com",
|
||||||
"ntp://ntp3.aliyun.com",
|
"ntp://ntp3.aliyun.com",
|
||||||
@@ -38,8 +41,8 @@
|
|||||||
"ntp://us.ntp.org.cn"
|
"ntp://us.ntp.org.cn"
|
||||||
],
|
],
|
||||||
"ExtraRoutes": [],
|
"ExtraRoutes": [],
|
||||||
"denyLineTableQuery": false,
|
"denyConnectionQuery": false,
|
||||||
"denyLineTableBroadcast": false,
|
"denyConnectionBroadcast": false,
|
||||||
"congestionAlgorithm": "BBR",
|
"congestionAlgorithm": "BBR",
|
||||||
"burstLimit": 2.0,
|
"burstLimit": 2.0,
|
||||||
"delayUpperBound": 1.2,
|
"delayUpperBound": 1.2,
|
||||||
@@ -47,10 +50,13 @@
|
|||||||
"nagleDelayTime": 1000000,
|
"nagleDelayTime": 1000000,
|
||||||
"linkNagleDelayTime": 0,
|
"linkNagleDelayTime": 0,
|
||||||
"linkConnectionsCount": 1,
|
"linkConnectionsCount": 1,
|
||||||
"TUNName": null,
|
"enableTUN": false,
|
||||||
|
"TUNName": "KLALB_SRv6",
|
||||||
"performanceStrategy": "multiscatter",
|
"performanceStrategy": "multiscatter",
|
||||||
"DeviceName": "SerinaNya PC",
|
"DeviceName": "Device Name",
|
||||||
"DeviceDescription": "desc",
|
"DeviceDescription": "The Description of Device",
|
||||||
|
"webUI": true,
|
||||||
|
"webPort": 4665,
|
||||||
"NetworkInterfaceExcepts": [],
|
"NetworkInterfaceExcepts": [],
|
||||||
"Type": "KLALBController"
|
"Type": "KLALBController"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"skills": {
|
||||||
|
"shadcn": {
|
||||||
|
"source": "shadcn/ui",
|
||||||
|
"sourceType": "github",
|
||||||
|
"skillPath": "skills/shadcn/SKILL.md",
|
||||||
|
"computedHash": "c1a68ee06a668aced9ab2b5fbdea5f989864123794eb2e056b339a072dbb7f10"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -210,7 +210,7 @@ public class KLALBController {
|
|||||||
for (Iterator<IPMulticastDiscovery> iterator = ipmd.iterator(); iterator.hasNext(); ) {
|
for (Iterator<IPMulticastDiscovery> iterator = ipmd.iterator(); iterator.hasNext(); ) {
|
||||||
IPMulticastDiscovery ipMulticastDiscovery = (IPMulticastDiscovery) iterator.next();
|
IPMulticastDiscovery ipMulticastDiscovery = (IPMulticastDiscovery) iterator.next();
|
||||||
if (ipMulticastDiscovery.isClosed() || (!ipMulticastDiscovery.getInterface().isUp())
|
if (ipMulticastDiscovery.isClosed() || (!ipMulticastDiscovery.getInterface().isUp())
|
||||||
|| (configItem != null && configItem.isDenyLineTableBroadcast())) {
|
|| (configItem != null && configItem.isDenyConnectionBroadcast())) {
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
try {
|
try {
|
||||||
ipMulticastDiscovery.close();
|
ipMulticastDiscovery.close();
|
||||||
@@ -222,7 +222,7 @@ public class KLALBController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (configItem != null && configItem.isDenyLineTableBroadcast()) {
|
if (configItem != null && configItem.isDenyConnectionBroadcast()) {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
List<NetworkInterface> interfaceList = networkInterfaceManager.getAllAvaliableNetworkInterface();
|
List<NetworkInterface> interfaceList = networkInterfaceManager.getAllAvaliableNetworkInterface();
|
||||||
@@ -647,8 +647,9 @@ public class KLALBController {
|
|||||||
rawPortBinder= new PortBinder(this.getSelf().getAddress());
|
rawPortBinder= new PortBinder(this.getSelf().getAddress());
|
||||||
System.out.println(" Loaded: SRv6 Stack");
|
System.out.println(" Loaded: SRv6 Stack");
|
||||||
|
|
||||||
String name=(configItem!=null)?configItem.getTUNName():CONST.KLALB_S_RV6;
|
String name = (configItem != null && configItem.getTUNName() != null) ? configItem.getTUNName() : CONST.KLALB_S_RV6;
|
||||||
boolean enableTUN = enableVirtualAdapter && (name != null) && !name.trim().isEmpty() && !name.trim().equalsIgnoreCase("null");
|
boolean isEnabled = configItem == null || configItem.isEnableTUN();
|
||||||
|
boolean enableTUN = enableVirtualAdapter && isEnabled && (name != null) && !name.trim().isEmpty() && !name.trim().equalsIgnoreCase("null");
|
||||||
if (enableTUN) {
|
if (enableTUN) {
|
||||||
Thread t=new Thread(()->{
|
Thread t=new Thread(()->{
|
||||||
try {
|
try {
|
||||||
@@ -800,18 +801,18 @@ public class KLALBController {
|
|||||||
getIpv6Router().setASN(vasn);
|
getIpv6Router().setASN(vasn);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<MultiProtocolSocketAddress> linele = configItem.getLineTable();
|
List<MultiProtocolSocketAddress> linele = configItem.getOpenConnections();
|
||||||
if (linele != null) {
|
if (linele != null) {
|
||||||
getSelflineTable().addAll(linele);
|
getSelflineTable().addAll(linele);
|
||||||
}
|
}
|
||||||
List<MultiProtocolSocketAddress> linetoc = configItem.getConnectLineTable();
|
List<MultiProtocolSocketAddress> linetoc = configItem.getAutoConnections();
|
||||||
if (linetoc != null) {
|
if (linetoc != null) {
|
||||||
linetoc.forEach((aline) -> {
|
linetoc.forEach((aline) -> {
|
||||||
addRemoteLines(aline);
|
addRemoteLines(aline);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
List<MultiProtocolSocketAddress> ntps = configItem.getNtpServerTable();
|
List<MultiProtocolSocketAddress> ntps = configItem.getNtpServers();
|
||||||
if (ntps != null) {
|
if (ntps != null) {
|
||||||
getNTPTable().addAll(ntps);
|
getNTPTable().addAll(ntps);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
|
|||||||
private MultiProtocolSocketAddress TCPListen=new MultiProtocolSocketAddress("0.0.0.0",4565);
|
private MultiProtocolSocketAddress TCPListen=new MultiProtocolSocketAddress("0.0.0.0",4565);
|
||||||
private MultiProtocolSocketAddress UDPListen=new MultiProtocolSocketAddress("udp","0.0.0.0",4565);
|
private MultiProtocolSocketAddress UDPListen=new MultiProtocolSocketAddress("udp","0.0.0.0",4565);
|
||||||
private String VirtualSocketName;
|
private String VirtualSocketName;
|
||||||
private List<MultiProtocolSocketAddress>LineTable=new ArrayList<>();
|
private List<MultiProtocolSocketAddress> openConnections = new ArrayList<>();
|
||||||
private List<MultiProtocolSocketAddress>ConnectLineTable=new ArrayList<>();
|
private List<MultiProtocolSocketAddress> autoConnections = new ArrayList<>();
|
||||||
private List<MultiProtocolSocketAddress>ntpServerTable=new ArrayList<>();
|
private List<MultiProtocolSocketAddress> ntpServers = new ArrayList<>();
|
||||||
private List<String>ExtraRoutes=new ArrayList<>();
|
private List<String> ExtraRoutes = new ArrayList<>();
|
||||||
private boolean denyLineTableQuery=false;
|
private boolean denyConnectionQuery = false;
|
||||||
private boolean denyLineTableBroadcast=false;
|
private boolean denyConnectionBroadcast = false;
|
||||||
private String congestionAlgorithm="BBR";
|
private String congestionAlgorithm="BBR";
|
||||||
private double burstLimit=1.50;
|
private double burstLimit=1.50;
|
||||||
private double delayUpperBound=1.20;
|
private double delayUpperBound=1.20;
|
||||||
@@ -29,10 +29,37 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
|
|||||||
private long nagleDelayTime=1000000L;
|
private long nagleDelayTime=1000000L;
|
||||||
private long linkNagleDelayTime=1000000L;
|
private long linkNagleDelayTime=1000000L;
|
||||||
private int linkConnectionsCount=1;
|
private int linkConnectionsCount=1;
|
||||||
|
private boolean enableTUN = true;
|
||||||
private String TUNName=CONST.KLALB_S_RV6;
|
private String TUNName=CONST.KLALB_S_RV6;
|
||||||
private String performanceStrategy="multifill";
|
private String performanceStrategy="multifill";
|
||||||
private String DeviceName;
|
private String DeviceName;
|
||||||
private String DeviceDescription;
|
private String DeviceDescription;
|
||||||
|
private boolean webUI = false;
|
||||||
|
private int webPort = 4665;
|
||||||
|
|
||||||
|
public boolean isEnableTUN() {
|
||||||
|
return enableTUN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnableTUN(boolean enableTUN) {
|
||||||
|
this.enableTUN = enableTUN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWebUI() {
|
||||||
|
return webUI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWebUI(boolean webUI) {
|
||||||
|
this.webUI = webUI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWebPort() {
|
||||||
|
return webPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWebPort(int webPort) {
|
||||||
|
this.webPort = webPort;
|
||||||
|
}
|
||||||
|
|
||||||
public String getPerformanceStrategy() {
|
public String getPerformanceStrategy() {
|
||||||
return performanceStrategy;
|
return performanceStrategy;
|
||||||
@@ -177,39 +204,52 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public List<MultiProtocolSocketAddress> getLineTable() {
|
public List<MultiProtocolSocketAddress> getOpenConnections() {
|
||||||
return LineTable;
|
return openConnections;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setOpenConnections(List<MultiProtocolSocketAddress> openConnections) {
|
||||||
|
this.openConnections = openConnections;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<MultiProtocolSocketAddress> getLineTable() {
|
||||||
|
return openConnections;
|
||||||
|
}
|
||||||
|
|
||||||
public void setLineTable(List<MultiProtocolSocketAddress> lineTable) {
|
public void setLineTable(List<MultiProtocolSocketAddress> lineTable) {
|
||||||
LineTable = lineTable;
|
this.openConnections = lineTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<MultiProtocolSocketAddress> getAutoConnections() {
|
||||||
|
return autoConnections;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAutoConnections(List<MultiProtocolSocketAddress> autoConnections) {
|
||||||
|
this.autoConnections = autoConnections;
|
||||||
|
}
|
||||||
|
|
||||||
public List<MultiProtocolSocketAddress> getConnectLineTable() {
|
public List<MultiProtocolSocketAddress> getConnectLineTable() {
|
||||||
return ConnectLineTable;
|
return autoConnections;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void setConnectLineTable(List<MultiProtocolSocketAddress> connectLineTable) {
|
public void setConnectLineTable(List<MultiProtocolSocketAddress> connectLineTable) {
|
||||||
ConnectLineTable = connectLineTable;
|
this.autoConnections = connectLineTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<MultiProtocolSocketAddress> getNtpServers() {
|
||||||
|
return ntpServers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNtpServers(List<MultiProtocolSocketAddress> ntpServers) {
|
||||||
|
this.ntpServers = ntpServers;
|
||||||
|
}
|
||||||
|
|
||||||
public List<MultiProtocolSocketAddress> getNtpServerTable() {
|
public List<MultiProtocolSocketAddress> getNtpServerTable() {
|
||||||
return ntpServerTable;
|
return ntpServers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void setNtpServerTable(List<MultiProtocolSocketAddress> ntpServerTable) {
|
public void setNtpServerTable(List<MultiProtocolSocketAddress> ntpServerTable) {
|
||||||
this.ntpServerTable = ntpServerTable;
|
this.ntpServers = ntpServerTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getExtraRoutes() {
|
public List<String> getExtraRoutes() {
|
||||||
@@ -280,23 +320,36 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public boolean isDenyLineTableQuery() {
|
public boolean isDenyConnectionQuery() {
|
||||||
return denyLineTableQuery;
|
return denyConnectionQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDenyConnectionQuery(boolean denyConnectionQuery) {
|
||||||
|
this.denyConnectionQuery = denyConnectionQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDenyLineTableQuery() {
|
||||||
|
return denyConnectionQuery;
|
||||||
|
}
|
||||||
|
|
||||||
public void setDenyLineTableQuery(boolean denyLineTableQuery) {
|
public void setDenyLineTableQuery(boolean denyLineTableQuery) {
|
||||||
this.denyLineTableQuery = denyLineTableQuery;
|
this.denyConnectionQuery = denyLineTableQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isDenyConnectionBroadcast() {
|
||||||
|
return denyConnectionBroadcast;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDenyConnectionBroadcast(boolean denyConnectionBroadcast) {
|
||||||
|
this.denyConnectionBroadcast = denyConnectionBroadcast;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isDenyLineTableBroadcast() {
|
public boolean isDenyLineTableBroadcast() {
|
||||||
return denyLineTableBroadcast;
|
return denyConnectionBroadcast;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setDenyLineTableBroadcast(boolean denyLineTableBroadcast) {
|
public void setDenyLineTableBroadcast(boolean denyLineTableBroadcast) {
|
||||||
this.denyLineTableBroadcast = denyLineTableBroadcast;
|
this.denyConnectionBroadcast = denyLineTableBroadcast;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -352,12 +405,12 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
|
|||||||
", TCPListen=" + TCPListen +
|
", TCPListen=" + TCPListen +
|
||||||
", UDPListen=" + UDPListen +
|
", UDPListen=" + UDPListen +
|
||||||
", VirtualSocketName='" + VirtualSocketName + '\'' +
|
", VirtualSocketName='" + VirtualSocketName + '\'' +
|
||||||
", LineTable=" + LineTable +
|
", openConnections=" + openConnections +
|
||||||
", ConnectLineTable=" + ConnectLineTable +
|
", autoConnections=" + autoConnections +
|
||||||
", ntpServerTable=" + ntpServerTable +
|
", ntpServers=" + ntpServers +
|
||||||
", ExtraRoutes=" + ExtraRoutes +
|
", ExtraRoutes=" + ExtraRoutes +
|
||||||
", denyLineTableQuery=" + denyLineTableQuery +
|
", denyConnectionQuery=" + denyConnectionQuery +
|
||||||
", denyLineTableBroadcast=" + denyLineTableBroadcast +
|
", denyConnectionBroadcast=" + denyConnectionBroadcast +
|
||||||
", congestionAlgorithm='" + congestionAlgorithm + '\'' +
|
", congestionAlgorithm='" + congestionAlgorithm + '\'' +
|
||||||
", burstLimit=" + burstLimit +
|
", burstLimit=" + burstLimit +
|
||||||
", delayUpperBound=" + delayUpperBound +
|
", delayUpperBound=" + delayUpperBound +
|
||||||
@@ -365,6 +418,7 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
|
|||||||
", nagleDelayTime=" + nagleDelayTime +
|
", nagleDelayTime=" + nagleDelayTime +
|
||||||
", linkNagleDelayTime=" + linkNagleDelayTime +
|
", linkNagleDelayTime=" + linkNagleDelayTime +
|
||||||
", linkConnectionsCount=" + linkConnectionsCount +
|
", linkConnectionsCount=" + linkConnectionsCount +
|
||||||
|
", enableTUN=" + enableTUN +
|
||||||
", TUNName='" + TUNName + '\'' +
|
", TUNName='" + TUNName + '\'' +
|
||||||
", performanceStrategy='" + performanceStrategy + '\'' +
|
", performanceStrategy='" + performanceStrategy + '\'' +
|
||||||
", DeviceName='" + DeviceName + '\'' +
|
", DeviceName='" + DeviceName + '\'' +
|
||||||
@@ -378,11 +432,11 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
|
|||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
if (!super.equals(o)) return false;
|
if (!super.equals(o)) return false;
|
||||||
KLALBControllerConfigItem that = (KLALBControllerConfigItem) o;
|
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(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 && 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
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);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,13 @@ public class KLALBMain {
|
|||||||
}catch(Throwable e) {
|
}catch(Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
if(kpcje.getControllerConfig() != null && kpcje.getControllerConfig().isWebUI()) {
|
||||||
|
kpcje.enableWebServer();
|
||||||
|
}
|
||||||
|
} catch(Throwable e) {
|
||||||
|
System.err.println("Failed to start web server: " + e.getMessage());
|
||||||
|
}
|
||||||
dtb.putTime("UI");
|
dtb.putTime("UI");
|
||||||
//dtb.print();
|
//dtb.print();
|
||||||
/*MultipurposeSocketAddress mpa=new MultipurposeSocketAddress("127.9.9.9", 49573);
|
/*MultipurposeSocketAddress mpa=new MultipurposeSocketAddress("127.9.9.9", 49573);
|
||||||
@@ -84,6 +91,7 @@ public class KLALBMain {
|
|||||||
case "help":
|
case "help":
|
||||||
System.out.println(" help / ?: see help");
|
System.out.println(" help / ?: see help");
|
||||||
System.out.println(" monitor: show monitor GUI");
|
System.out.println(" monitor: show monitor GUI");
|
||||||
|
System.out.println(" web [start|stop|status <port>]: manage web dashboard");
|
||||||
System.out.println(" links-state: query link states");
|
System.out.println(" links-state: query link states");
|
||||||
System.out.println(" links-add <addr:port>: add link");
|
System.out.println(" links-add <addr:port>: add link");
|
||||||
System.out.println(" links-remove <addr:port>: remove link");
|
System.out.println(" links-remove <addr:port>: remove link");
|
||||||
@@ -102,6 +110,42 @@ public class KLALBMain {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "web":
|
||||||
|
if(sc.length >= 2) {
|
||||||
|
String action = sc[1].toLowerCase();
|
||||||
|
if("start".equals(action)) {
|
||||||
|
int p = 4665;
|
||||||
|
if(sc.length >= 3) {
|
||||||
|
try { p = Integer.parseInt(sc[2]); } catch (NumberFormatException ignored) {}
|
||||||
|
} else if(kpcje.getControllerConfig() != null && kpcje.getControllerConfig().getWebPort() > 0) {
|
||||||
|
p = kpcje.getControllerConfig().getWebPort();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
kpcje.enableWebServer(p);
|
||||||
|
System.out.println("Web dashboard started on http://localhost:" + p);
|
||||||
|
} catch(Exception e) {
|
||||||
|
System.out.println("Failed to start web server: " + e.getMessage());
|
||||||
|
}
|
||||||
|
} else if("stop".equals(action)) {
|
||||||
|
kpcje.disableWebServer();
|
||||||
|
System.out.println("Web dashboard stopped.");
|
||||||
|
} else if("status".equals(action)) {
|
||||||
|
if(kpcje.isWebServerEnabled()) {
|
||||||
|
System.out.println("Web dashboard is running on port " + kpcje.getWebServer().getPort());
|
||||||
|
} else {
|
||||||
|
System.out.println("Web dashboard is stopped.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.out.println("Usage: web [start|stop|status <port>]");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(kpcje.isWebServerEnabled()) {
|
||||||
|
System.out.println("Web dashboard is running on port " + kpcje.getWebServer().getPort());
|
||||||
|
} else {
|
||||||
|
System.out.println("Web dashboard is not running. Use 'web start [port]' to start.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "links-state":
|
case "links-state":
|
||||||
System.out.println("links state:");
|
System.out.println("links state:");
|
||||||
//System.out.println("状态\t上传流量\t下载流量\t上传速度\t下载速度\t上传延迟\t下载延迟\t上传抖动\t下载抖动");
|
//System.out.println("状态\t上传流量\t下载流量\t上传速度\t下载速度\t上传延迟\t下载延迟\t上传抖动\t下载抖动");
|
||||||
|
|||||||
@@ -5,25 +5,35 @@ import java.io.FileReader;
|
|||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.net.InetSocketAddress;
|
import java.lang.reflect.Type;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
import org.kne.cloud.network.*;
|
import org.kne.cloud.network.*;
|
||||||
import org.kne.cloud.network.klalb.ui.KLALBStateGUI3;
|
import org.kne.cloud.network.klalb.ui.KLALBStateGUI3;
|
||||||
import org.kne.cloud.network.klalb.ui.Language;
|
import org.kne.cloud.network.klalb.ui.Language;
|
||||||
import org.kne.cloud.network.klalb.ui.UIEnv;
|
import org.kne.cloud.network.klalb.ui.UIEnv;
|
||||||
|
import org.kne.cloud.network.klalb.web.KLALBWebServer;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import com.google.gson.JsonSerializationContext;
|
||||||
|
import com.google.gson.JsonSerializer;
|
||||||
import com.google.gson.JsonParser;
|
import com.google.gson.JsonParser;
|
||||||
|
|
||||||
public class KLALBProxySystem {
|
public class KLALBProxySystem {
|
||||||
private Set<Proxy> proxys=new HashSet<>();
|
private Set<Proxy> proxys=new HashSet<>();
|
||||||
private KLALBController klalbController;
|
private KLALBController klalbController;
|
||||||
private KLALBRemoteManagement krm;
|
private KLALBRemoteManagement krm;
|
||||||
|
private KLALBWebServer webServer;
|
||||||
private KLALBConfig config;
|
private KLALBConfig config;
|
||||||
private Gson gson;
|
private Gson gson;
|
||||||
private File jsonFile;
|
private File jsonFile;
|
||||||
@@ -31,8 +41,28 @@ public class KLALBProxySystem {
|
|||||||
GsonBuilder gb=new GsonBuilder().setPrettyPrinting();
|
GsonBuilder gb=new GsonBuilder().setPrettyPrinting();
|
||||||
MultiProtocolSocketAddress.registerToGsonBuilder(gb);
|
MultiProtocolSocketAddress.registerToGsonBuilder(gb);
|
||||||
KLALBConfigItem.registerToGsonBuilder(gb);
|
KLALBConfigItem.registerToGsonBuilder(gb);
|
||||||
|
gb.registerTypeAdapter(InetAddress.class, new JsonSerializer<InetAddress>() {
|
||||||
|
@Override
|
||||||
|
public JsonElement serialize(InetAddress src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive(src.getHostAddress());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
gb.registerTypeAdapter(InetAddress.class, new JsonDeserializer<InetAddress>() {
|
||||||
|
@Override
|
||||||
|
public InetAddress deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||||
|
try {
|
||||||
|
return InetAddress.getByName(json.getAsString());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new JsonParseException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
gson=gb.create();
|
gson=gb.create();
|
||||||
}
|
}
|
||||||
|
public Gson getGson() {
|
||||||
|
return gson;
|
||||||
|
}
|
||||||
|
|
||||||
public Set<Proxy> getProxys() {
|
public Set<Proxy> getProxys() {
|
||||||
return proxys;
|
return proxys;
|
||||||
}
|
}
|
||||||
@@ -81,6 +111,39 @@ public class KLALBProxySystem {
|
|||||||
krm=null;
|
krm=null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void enableWebServer() throws IOException {
|
||||||
|
int port = 4665;
|
||||||
|
KLALBControllerConfigItem cci = getControllerConfig();
|
||||||
|
if (cci != null && cci.getWebPort() > 0) {
|
||||||
|
port = cci.getWebPort();
|
||||||
|
}
|
||||||
|
enableWebServer(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enableWebServer(int port) throws IOException {
|
||||||
|
if (webServer == null) {
|
||||||
|
webServer = new KLALBWebServer(this, port);
|
||||||
|
webServer.start();
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Web server already enabled!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWebServerEnabled() {
|
||||||
|
return webServer != null && webServer.isRunning();
|
||||||
|
}
|
||||||
|
|
||||||
|
public KLALBWebServer getWebServer() {
|
||||||
|
return webServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disableWebServer() {
|
||||||
|
if (webServer != null) {
|
||||||
|
webServer.stop();
|
||||||
|
webServer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void loadConfigJson(File jsonFile) throws IOException {
|
public void loadConfigJson(File jsonFile) throws IOException {
|
||||||
this.jsonFile=jsonFile;
|
this.jsonFile=jsonFile;
|
||||||
@@ -195,30 +258,24 @@ public class KLALBProxySystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void saveConfigToFile() {
|
||||||
|
if (jsonFile != null && config != null) {
|
||||||
|
String json = gson.toJson(config);
|
||||||
|
try (FileWriter fw = new FileWriter(jsonFile)) {
|
||||||
|
fw.write(json);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private KLALBStateGUI3 kgui;
|
private KLALBStateGUI3 kgui;
|
||||||
public KLALBStateGUI3 getKLALBGUI() {
|
public KLALBStateGUI3 getKLALBGUI() {
|
||||||
if(kgui==null) {
|
if(kgui==null) {
|
||||||
kgui=new KLALBStateGUI3(klalbController);
|
kgui=new KLALBStateGUI3(klalbController);
|
||||||
kgui.loadConfig(config);
|
kgui.loadConfig(config);
|
||||||
kgui.setSaveComsumer((cfg)->{
|
kgui.setSaveComsumer((cfg)->{
|
||||||
String json=gson.toJson(cfg);
|
saveConfigToFile();
|
||||||
if(jsonFile!=null) {
|
|
||||||
FileWriter fw = null;
|
|
||||||
try {
|
|
||||||
fw=new FileWriter(jsonFile);
|
|
||||||
fw.write(json);
|
|
||||||
}catch(IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}finally {
|
|
||||||
if(fw!=null)
|
|
||||||
try {
|
|
||||||
fw.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return kgui;
|
return kgui;
|
||||||
|
|||||||
@@ -0,0 +1,876 @@
|
|||||||
|
package org.kne.cloud.network.klalb.web;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.*;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import com.sun.net.httpserver.*;
|
||||||
|
import com.google.gson.*;
|
||||||
|
|
||||||
|
import org.kne.cloud.clock.HighAccuracyClock;
|
||||||
|
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
||||||
|
import org.kne.cloud.network.ipv6.IPv6Address;
|
||||||
|
import org.kne.cloud.network.ipv6.IPv6AddressGroup;
|
||||||
|
import org.kne.cloud.network.ipv6.IPv6NetworkLink;
|
||||||
|
import org.kne.cloud.network.ipv6.RouteItem;
|
||||||
|
import org.kne.cloud.network.klalb.*;
|
||||||
|
import org.kne.cloud.network.monitor.LinkStatus;
|
||||||
|
import org.kne.cloud.network.srv6.KLALBRoutingProtocol;
|
||||||
|
import org.kne.cloud.network.srv6.KLALBRoutingProtocol.LinkDirection;
|
||||||
|
import org.kne.cloud.network.srv6.NeighborInfo;
|
||||||
|
import org.kne.cloud.network.srv6.RouterInfo;
|
||||||
|
import org.kne.cloud.network.srv6.SRv6Router;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* KLALB Web Server providing REST API, SSE live event stream,
|
||||||
|
* and static dashboard hosting.
|
||||||
|
*/
|
||||||
|
public class KLALBWebServer {
|
||||||
|
private final KLALBProxySystem proxySystem;
|
||||||
|
private final int port;
|
||||||
|
private HttpServer server;
|
||||||
|
private final Gson gson;
|
||||||
|
private final ScheduledExecutorService sseExecutor = Executors.newSingleThreadScheduledExecutor(r -> {
|
||||||
|
Thread t = new Thread(r, "KLALB-Web-SSE");
|
||||||
|
t.setDaemon(true);
|
||||||
|
return t;
|
||||||
|
});
|
||||||
|
private final Set<HttpExchange> sseClients = Collections.newSetFromMap(new ConcurrentHashMap<>());
|
||||||
|
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
public KLALBWebServer(KLALBProxySystem proxySystem, int port) {
|
||||||
|
this.proxySystem = proxySystem;
|
||||||
|
this.port = port;
|
||||||
|
this.gson = proxySystem.getGson();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void start() throws IOException {
|
||||||
|
if (running.get()) return;
|
||||||
|
|
||||||
|
server = HttpServer.create(new InetSocketAddress(port), 0);
|
||||||
|
server.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
|
||||||
|
|
||||||
|
// API Contexts
|
||||||
|
server.createContext("/api/status", this::handleStatus);
|
||||||
|
server.createContext("/api/events", this::handleEvents);
|
||||||
|
server.createContext("/api/links", this::handleLinks);
|
||||||
|
server.createContext("/api/links/action", this::handleLinkAction);
|
||||||
|
server.createContext("/api/links/reconnect", this::handleReconnectAll);
|
||||||
|
server.createContext("/api/routes", this::handleRoutes);
|
||||||
|
server.createContext("/api/nodes", this::handleNodes);
|
||||||
|
server.createContext("/api/interfaces", this::handleInterfaces);
|
||||||
|
server.createContext("/api/config", this::handleConfig);
|
||||||
|
|
||||||
|
// Static Files / SPA Fallback Handler
|
||||||
|
server.createContext("/", this::handleStatic);
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
running.set(true);
|
||||||
|
startSseBroadcaster();
|
||||||
|
|
||||||
|
System.out.println("KLALB Web Dashboard started at http://localhost:" + port);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void stop() {
|
||||||
|
if (!running.get()) return;
|
||||||
|
running.set(false);
|
||||||
|
sseExecutor.shutdownNow();
|
||||||
|
for (HttpExchange client : sseClients) {
|
||||||
|
try {
|
||||||
|
client.close();
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
}
|
||||||
|
sseClients.clear();
|
||||||
|
if (server != null) {
|
||||||
|
server.stop(1);
|
||||||
|
server = null;
|
||||||
|
}
|
||||||
|
System.out.println("KLALB Web Dashboard stopped.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRunning() {
|
||||||
|
return running.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setCorsHeaders(HttpExchange exchange) {
|
||||||
|
Headers headers = exchange.getResponseHeaders();
|
||||||
|
headers.set("Access-Control-Allow-Origin", "*");
|
||||||
|
headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
||||||
|
headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean handleCorsPreflight(HttpExchange exchange) throws IOException {
|
||||||
|
setCorsHeaders(exchange);
|
||||||
|
if ("OPTIONS".equalsIgnoreCase(exchange.getRequestMethod())) {
|
||||||
|
exchange.sendResponseHeaders(204, -1);
|
||||||
|
exchange.close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendJsonResponse(HttpExchange exchange, int statusCode, Object data) throws IOException {
|
||||||
|
setCorsHeaders(exchange);
|
||||||
|
byte[] bytes = gson.toJson(data).getBytes(StandardCharsets.UTF_8);
|
||||||
|
exchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8");
|
||||||
|
exchange.sendResponseHeaders(statusCode, bytes.length);
|
||||||
|
try (OutputStream os = exchange.getResponseBody()) {
|
||||||
|
os.write(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendError(HttpExchange exchange, int statusCode, String message) throws IOException {
|
||||||
|
JsonObject error = new JsonObject();
|
||||||
|
error.addProperty("error", message);
|
||||||
|
sendJsonResponse(exchange, statusCode, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readRequestBody(HttpExchange exchange) throws IOException {
|
||||||
|
try (InputStream is = exchange.getRequestBody()) {
|
||||||
|
return new String(is.readAllBytes(), StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------
|
||||||
|
// API Handlers
|
||||||
|
// -------------------------------------------------------------
|
||||||
|
|
||||||
|
private JsonObject getStatusJson() {
|
||||||
|
JsonObject root = new JsonObject();
|
||||||
|
KLALBController kc = proxySystem.getKlalbController();
|
||||||
|
if (kc == null) {
|
||||||
|
root.addProperty("status", "not_ready");
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
root.addProperty("version", CONST.klalbver);
|
||||||
|
root.addProperty("title", CONST.klalb);
|
||||||
|
|
||||||
|
IPv6AddressGroup self = kc.getSelf();
|
||||||
|
root.addProperty("address", self != null ? self.getAddress().toString() : "");
|
||||||
|
|
||||||
|
SRv6Router router = kc.getIpv6Router();
|
||||||
|
if (router != null && router.getKlalbRouteProtol() != null) {
|
||||||
|
root.addProperty("onlineDevices", router.getKlalbRouteProtol().getDevicesFound());
|
||||||
|
root.addProperty("deviceName", router.getDeviceName() != null ? router.getDeviceName() : "");
|
||||||
|
} else {
|
||||||
|
root.addProperty("onlineDevices", 0);
|
||||||
|
root.addProperty("deviceName", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
KLALBControllerConfigItem configItem = proxySystem.getControllerConfig();
|
||||||
|
if (configItem != null) {
|
||||||
|
root.addProperty("deviceDescription", configItem.getDeviceDescription() != null ? configItem.getDeviceDescription() : "");
|
||||||
|
root.addProperty("enableTUN", configItem.isEnableTUN());
|
||||||
|
root.addProperty("tunName", configItem.isEnableTUN() && configItem.getTUNName() != null ? configItem.getTUNName() : "");
|
||||||
|
root.addProperty("congestionAlgorithm", configItem.getCongestionAlgorithm() != null ? configItem.getCongestionAlgorithm() : "");
|
||||||
|
root.addProperty("performanceStrategy", configItem.getPerformanceStrategy() != null ? configItem.getPerformanceStrategy() : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clock
|
||||||
|
HighAccuracyClock hac = kc.getClock();
|
||||||
|
if (hac != null) {
|
||||||
|
long delta = hac.getFrequency() - 1000000000;
|
||||||
|
double ppm = delta / 1000.0;
|
||||||
|
root.addProperty("timeStr", hac.toString2());
|
||||||
|
root.addProperty("timePpm", ppm);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metrics
|
||||||
|
JsonObject metrics = new JsonObject();
|
||||||
|
if (kc.getLinkMonitor() != null) {
|
||||||
|
metrics.addProperty("upSpeed", kc.getLinkMonitor().getOutSpeed());
|
||||||
|
metrics.addProperty("downSpeed", kc.getLinkMonitor().getInSpeed());
|
||||||
|
metrics.addProperty("upSpeedMax", kc.getLinkMonitor().getOutSpeedMax());
|
||||||
|
metrics.addProperty("downSpeedMax", kc.getLinkMonitor().getInSpeedMax());
|
||||||
|
metrics.addProperty("upPPS", kc.getLinkMonitor().getOutPPS());
|
||||||
|
metrics.addProperty("downPPS", kc.getLinkMonitor().getInPPS());
|
||||||
|
metrics.addProperty("upPPSMax", kc.getLinkMonitor().getOutPPSMax());
|
||||||
|
metrics.addProperty("downPPSMax", kc.getLinkMonitor().getInPPSMax());
|
||||||
|
metrics.addProperty("upTraffic", kc.getLinkMonitor().getOutTraffic());
|
||||||
|
metrics.addProperty("downTraffic", kc.getLinkMonitor().getInTraffic());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kc.getDatatMonitor() != null) {
|
||||||
|
metrics.addProperty("dataUpSpeed", kc.getDatatMonitor().getOutSpeed());
|
||||||
|
metrics.addProperty("dataDownSpeed", kc.getDatatMonitor().getInSpeed());
|
||||||
|
metrics.addProperty("dataUpPPS", kc.getDatatMonitor().getOutPPS());
|
||||||
|
metrics.addProperty("dataDownPPS", kc.getDatatMonitor().getInPPS());
|
||||||
|
metrics.addProperty("dataUpTraffic", kc.getDatatMonitor().getOutTraffic());
|
||||||
|
metrics.addProperty("dataDownTraffic", kc.getDatatMonitor().getInTraffic());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (router != null) {
|
||||||
|
metrics.addProperty("backplaneDelay", router.getBackplaneTime());
|
||||||
|
metrics.addProperty("backplanePPS", router.getBackplanePPS());
|
||||||
|
metrics.addProperty("backplanePPSMax", router.getBackplanePPSMax());
|
||||||
|
metrics.addProperty("backplaneECNRate", router.getECNRate());
|
||||||
|
metrics.addProperty("backplaneLossRate", router.getLossRate());
|
||||||
|
}
|
||||||
|
root.add("metrics", metrics);
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleStatus(HttpExchange exchange) throws IOException {
|
||||||
|
if (handleCorsPreflight(exchange)) return;
|
||||||
|
if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) {
|
||||||
|
sendError(exchange, 405, "Method not allowed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendJsonResponse(exchange, 200, getStatusJson());
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonArray getLinksJson() {
|
||||||
|
JsonArray array = new JsonArray();
|
||||||
|
KLALBController kc = proxySystem.getKlalbController();
|
||||||
|
if (kc == null) return array;
|
||||||
|
|
||||||
|
List<IPv6NetworkLink> linesList = kc.getLines();
|
||||||
|
synchronized (linesList) {
|
||||||
|
for (IPv6NetworkLink link : linesList) {
|
||||||
|
if (!(link instanceof KLALBRemoteLink)) continue;
|
||||||
|
KLALBRemoteLink remoteLink = (KLALBRemoteLink) link;
|
||||||
|
|
||||||
|
JsonObject obj = new JsonObject();
|
||||||
|
obj.addProperty("name", remoteLink.getName());
|
||||||
|
obj.addProperty("state", LinkStatus.stateToString(remoteLink.getState()));
|
||||||
|
obj.addProperty("stateCode", remoteLink.getState());
|
||||||
|
|
||||||
|
MultiProtocolSocketAddress mpsa = remoteLink.getSocketAddress();
|
||||||
|
obj.addProperty("socketAddress", mpsa != null ? mpsa.toString() : "");
|
||||||
|
|
||||||
|
IPv6AddressGroup vaddr = remoteLink.getRemoteVaddr();
|
||||||
|
obj.addProperty("vaddr", vaddr != null ? vaddr.getAddress().toString() : "");
|
||||||
|
|
||||||
|
if (remoteLink.getMonitor() != null) {
|
||||||
|
obj.addProperty("upSpeed", remoteLink.getMonitor().getOutSpeed());
|
||||||
|
obj.addProperty("downSpeed", remoteLink.getMonitor().getInSpeed());
|
||||||
|
obj.addProperty("upPPS", remoteLink.getMonitor().getOutPPS());
|
||||||
|
obj.addProperty("downPPS", remoteLink.getMonitor().getInPPS());
|
||||||
|
obj.addProperty("upTraffic", remoteLink.getMonitor().getOutTraffic());
|
||||||
|
obj.addProperty("downTraffic", remoteLink.getMonitor().getInTraffic());
|
||||||
|
obj.addProperty("upDelay", remoteLink.getMonitor().getOutDelay());
|
||||||
|
obj.addProperty("downDelay", remoteLink.getMonitor().getInDelay());
|
||||||
|
obj.addProperty("upDelayMin", remoteLink.getMonitor().getOutDelayMin());
|
||||||
|
obj.addProperty("downDelayMin", remoteLink.getMonitor().getInDelayMin());
|
||||||
|
obj.addProperty("upJitter", remoteLink.getMonitor().getOutJitter());
|
||||||
|
obj.addProperty("downJitter", remoteLink.getMonitor().getInJitter());
|
||||||
|
}
|
||||||
|
array.add(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleLinks(HttpExchange exchange) throws IOException {
|
||||||
|
if (handleCorsPreflight(exchange)) return;
|
||||||
|
String method = exchange.getRequestMethod();
|
||||||
|
KLALBController kc = proxySystem.getKlalbController();
|
||||||
|
|
||||||
|
if ("GET".equalsIgnoreCase(method)) {
|
||||||
|
sendJsonResponse(exchange, 200, getLinksJson());
|
||||||
|
} else if ("POST".equalsIgnoreCase(method)) {
|
||||||
|
String body = readRequestBody(exchange);
|
||||||
|
try {
|
||||||
|
JsonObject req = new JsonParser().parse(body).getAsJsonObject();
|
||||||
|
String address = req.get("address").getAsString().trim();
|
||||||
|
if (address.isEmpty()) {
|
||||||
|
sendError(exchange, 400, "Address is required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MultiProtocolSocketAddress mpsa = new MultiProtocolSocketAddress(address);
|
||||||
|
List<KLALBRemoteLink> added = kc.addRemoteLines(mpsa);
|
||||||
|
JsonObject resp = new JsonObject();
|
||||||
|
resp.addProperty("success", !added.isEmpty());
|
||||||
|
resp.addProperty("count", added.size());
|
||||||
|
sendJsonResponse(exchange, 200, resp);
|
||||||
|
} catch (Exception e) {
|
||||||
|
sendError(exchange, 400, "Failed to add link: " + e.getMessage());
|
||||||
|
}
|
||||||
|
} else if ("DELETE".equalsIgnoreCase(method)) {
|
||||||
|
String query = exchange.getRequestURI().getQuery();
|
||||||
|
String address = null;
|
||||||
|
if (query != null && query.startsWith("address=")) {
|
||||||
|
address = URLDecoder.decode(query.substring(8), StandardCharsets.UTF_8);
|
||||||
|
} else {
|
||||||
|
String body = readRequestBody(exchange);
|
||||||
|
if (!body.isEmpty()) {
|
||||||
|
try {
|
||||||
|
JsonObject req = new JsonParser().parse(body).getAsJsonObject();
|
||||||
|
if (req.has("address")) {
|
||||||
|
address = req.get("address").getAsString();
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address == null || address.trim().isEmpty()) {
|
||||||
|
sendError(exchange, 400, "Address parameter required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
MultiProtocolSocketAddress mpsa = new MultiProtocolSocketAddress(address.trim());
|
||||||
|
List<KLALBRemoteLink> removed = kc.removeRemoteLines(mpsa);
|
||||||
|
JsonObject resp = new JsonObject();
|
||||||
|
resp.addProperty("success", !removed.isEmpty());
|
||||||
|
resp.addProperty("count", removed.size());
|
||||||
|
sendJsonResponse(exchange, 200, resp);
|
||||||
|
} catch (Exception e) {
|
||||||
|
sendError(exchange, 400, "Failed to remove link: " + e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendError(exchange, 405, "Method not allowed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleLinkAction(HttpExchange exchange) throws IOException {
|
||||||
|
if (handleCorsPreflight(exchange)) return;
|
||||||
|
if (!"POST".equalsIgnoreCase(exchange.getRequestMethod())) {
|
||||||
|
sendError(exchange, 405, "Method not allowed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String body = readRequestBody(exchange);
|
||||||
|
try {
|
||||||
|
JsonObject req = new JsonParser().parse(body).getAsJsonObject();
|
||||||
|
String action = req.get("action").getAsString();
|
||||||
|
String address = req.has("address") ? req.get("address").getAsString() : null;
|
||||||
|
|
||||||
|
KLALBController kc = proxySystem.getKlalbController();
|
||||||
|
KLALBRemoteLink target = null;
|
||||||
|
if (address != null) {
|
||||||
|
for (IPv6NetworkLink link : kc.getLines()) {
|
||||||
|
if (link instanceof KLALBRemoteLink rl) {
|
||||||
|
if (rl.getName().equalsIgnoreCase(address) ||
|
||||||
|
(rl.getSocketAddress() != null && rl.getSocketAddress().toString().equalsIgnoreCase(address))) {
|
||||||
|
target = rl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject resp = new JsonObject();
|
||||||
|
if (target != null) {
|
||||||
|
switch (action) {
|
||||||
|
case "reconnect" -> {
|
||||||
|
target.reconnectImmediately();
|
||||||
|
resp.addProperty("success", true);
|
||||||
|
}
|
||||||
|
case "disconnect" -> {
|
||||||
|
target.dislink();
|
||||||
|
resp.addProperty("success", true);
|
||||||
|
}
|
||||||
|
case "remove" -> {
|
||||||
|
MultiProtocolSocketAddress mtar = target.getSocketAddress();
|
||||||
|
if (mtar != null) {
|
||||||
|
kc.removeRemoteLines(mtar);
|
||||||
|
} else {
|
||||||
|
target.close();
|
||||||
|
}
|
||||||
|
resp.addProperty("success", true);
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
sendError(exchange, 400, "Unknown action: " + action);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendError(exchange, 404, "Target link not found: " + address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendJsonResponse(exchange, 200, resp);
|
||||||
|
} catch (Exception e) {
|
||||||
|
sendError(exchange, 400, "Failed to execute link action: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleReconnectAll(HttpExchange exchange) throws IOException {
|
||||||
|
if (handleCorsPreflight(exchange)) return;
|
||||||
|
if (!"POST".equalsIgnoreCase(exchange.getRequestMethod())) {
|
||||||
|
sendError(exchange, 405, "Method not allowed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
KLALBController kc = proxySystem.getKlalbController();
|
||||||
|
if (kc != null) {
|
||||||
|
kc.reconnectImmediately();
|
||||||
|
JsonObject resp = new JsonObject();
|
||||||
|
resp.addProperty("success", true);
|
||||||
|
sendJsonResponse(exchange, 200, resp);
|
||||||
|
} else {
|
||||||
|
sendError(exchange, 500, "Controller not ready");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleRoutes(HttpExchange exchange) throws IOException {
|
||||||
|
if (handleCorsPreflight(exchange)) return;
|
||||||
|
if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) {
|
||||||
|
sendError(exchange, 405, "Method not allowed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
KLALBController kc = proxySystem.getKlalbController();
|
||||||
|
JsonArray routesArray = new JsonArray();
|
||||||
|
if (kc != null && kc.getIpv6Router() != null) {
|
||||||
|
List<RouteItem> list = new ArrayList<>(kc.getIpv6Router().getCurrentRouteTabel());
|
||||||
|
Collections.sort(list);
|
||||||
|
for (RouteItem item : list) {
|
||||||
|
JsonObject obj = new JsonObject();
|
||||||
|
obj.addProperty("destination", item.getDestination() != null ? item.getDestination().toString() : "");
|
||||||
|
obj.addProperty("protocol", item.getProto());
|
||||||
|
obj.addProperty("preference", item.getPre());
|
||||||
|
obj.addProperty("cost", item.getCost());
|
||||||
|
obj.addProperty("flag", item.getFlag());
|
||||||
|
obj.addProperty("nexthop", item.getNexthop() != null ? item.getNexthop().toString() : "");
|
||||||
|
obj.addProperty("interface", item.getDestlink() != null ? item.getDestlink().getName() : "");
|
||||||
|
routesArray.add(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sendJsonResponse(exchange, 200, routesArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleNodes(HttpExchange exchange) throws IOException {
|
||||||
|
if (handleCorsPreflight(exchange)) return;
|
||||||
|
if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) {
|
||||||
|
sendError(exchange, 405, "Method not allowed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
KLALBController kc = proxySystem.getKlalbController();
|
||||||
|
JsonObject result = new JsonObject();
|
||||||
|
JsonArray nodesArray = new JsonArray();
|
||||||
|
JsonArray edgesArray = new JsonArray();
|
||||||
|
|
||||||
|
if (kc != null && kc.getIpv6Router() != null && kc.getIpv6Router().getKlalbRouteProtol() != null) {
|
||||||
|
KLALBRoutingProtocol rproto = kc.getIpv6Router().getKlalbRouteProtol();
|
||||||
|
Map<IPv6Address, Long> addrs = rproto.getAddresses();
|
||||||
|
IPv6Address selfAddr = kc.getIpv6Router().getLocator().getAddress();
|
||||||
|
|
||||||
|
if (addrs != null) {
|
||||||
|
for (IPv6Address addr : addrs.keySet()) {
|
||||||
|
JsonObject nodeObj = new JsonObject();
|
||||||
|
nodeObj.addProperty("id", addr.toString());
|
||||||
|
nodeObj.addProperty("address", addr.toString());
|
||||||
|
nodeObj.addProperty("compressedAddress", addr.toCompressedString());
|
||||||
|
nodeObj.addProperty("isSelf", addr.equals(selfAddr));
|
||||||
|
String dname = rproto.getDeviceName(addr);
|
||||||
|
nodeObj.addProperty("deviceName", dname != null ? dname : "");
|
||||||
|
nodesArray.add(nodeObj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<IPv6Address, RouterInfo> netmap = rproto.getNetmap();
|
||||||
|
if (netmap != null) {
|
||||||
|
Set<String> seenEdges = new HashSet<>();
|
||||||
|
for (Map.Entry<IPv6Address, RouterInfo> entry : netmap.entrySet()) {
|
||||||
|
IPv6Address from = entry.getKey();
|
||||||
|
RouterInfo info = entry.getValue();
|
||||||
|
if (info != null && info.getNeighborAddresses() != null) {
|
||||||
|
for (NeighborInfo nb : info.getNeighborAddresses()) {
|
||||||
|
if (nb != null && nb.getLocator() != null) {
|
||||||
|
String to = nb.getLocator().getAddress().toString();
|
||||||
|
String edgeKey = from.toString().compareTo(to) < 0
|
||||||
|
? from.toString() + "->" + to
|
||||||
|
: to + "->" + from.toString();
|
||||||
|
if (!seenEdges.contains(edgeKey)) {
|
||||||
|
seenEdges.add(edgeKey);
|
||||||
|
JsonObject edgeObj = new JsonObject();
|
||||||
|
edgeObj.addProperty("source", from.toString());
|
||||||
|
edgeObj.addProperty("target", to);
|
||||||
|
edgeObj.addProperty("delay", nb.getUploadDelay());
|
||||||
|
edgeObj.addProperty("cost", nb.getUploadDelayMin());
|
||||||
|
edgesArray.add(edgeObj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.add("nodes", nodesArray);
|
||||||
|
result.add("edges", edgesArray);
|
||||||
|
sendJsonResponse(exchange, 200, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleInterfaces(HttpExchange exchange) throws IOException {
|
||||||
|
if (handleCorsPreflight(exchange)) return;
|
||||||
|
if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) {
|
||||||
|
sendError(exchange, 405, "Method not allowed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
KLALBController kc = proxySystem.getKlalbController();
|
||||||
|
JsonArray ifacesArray = new JsonArray();
|
||||||
|
if (kc != null && kc.getNetworkInterfaceManager() != null) {
|
||||||
|
List<NetworkInterface> ifaces = kc.getNetworkInterfaceManager().getAllAvaliableNetworkInterface();
|
||||||
|
for (NetworkInterface nif : ifaces) {
|
||||||
|
JsonObject obj = new JsonObject();
|
||||||
|
obj.addProperty("name", nif.getName());
|
||||||
|
obj.addProperty("displayName", nif.getDisplayName());
|
||||||
|
JsonArray ips = new JsonArray();
|
||||||
|
List<InetAddress> addrs = kc.getNetworkInterfaceManager().getNetworkInterfaceAddress(nif);
|
||||||
|
for (InetAddress a : addrs) {
|
||||||
|
ips.add(new JsonPrimitive(a.getHostAddress()));
|
||||||
|
}
|
||||||
|
obj.add("addresses", ips);
|
||||||
|
ifacesArray.add(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sendJsonResponse(exchange, 200, ifacesArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleConfig(HttpExchange exchange) throws IOException {
|
||||||
|
if (handleCorsPreflight(exchange)) return;
|
||||||
|
String method = exchange.getRequestMethod();
|
||||||
|
|
||||||
|
if ("GET".equalsIgnoreCase(method)) {
|
||||||
|
KLALBControllerConfigItem config = proxySystem.getControllerConfig();
|
||||||
|
if (config != null) {
|
||||||
|
sendJsonResponse(exchange, 200, config);
|
||||||
|
} else {
|
||||||
|
sendError(exchange, 404, "Configuration not found");
|
||||||
|
}
|
||||||
|
} else if ("POST".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method)) {
|
||||||
|
String body = readRequestBody(exchange);
|
||||||
|
try {
|
||||||
|
JsonObject json = new JsonParser().parse(body).getAsJsonObject();
|
||||||
|
KLALBControllerConfigItem current = proxySystem.getControllerConfig();
|
||||||
|
if (current != null) {
|
||||||
|
if (json.has("deviceName") && !json.get("deviceName").isJsonNull()) {
|
||||||
|
current.setDeviceName(json.get("deviceName").getAsString());
|
||||||
|
} else if (json.has("DeviceName") && !json.get("DeviceName").isJsonNull()) {
|
||||||
|
current.setDeviceName(json.get("DeviceName").getAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("deviceDescription") && !json.get("deviceDescription").isJsonNull()) {
|
||||||
|
current.setDeviceDescription(json.get("deviceDescription").getAsString());
|
||||||
|
} else if (json.has("DeviceDescription") && !json.get("DeviceDescription").isJsonNull()) {
|
||||||
|
current.setDeviceDescription(json.get("DeviceDescription").getAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("language") && !json.get("language").isJsonNull()) {
|
||||||
|
current.setLanguage(json.get("language").getAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("virtualAddress") && !json.get("virtualAddress").isJsonNull()) {
|
||||||
|
current.setVirtualAddress(json.get("virtualAddress").getAsString());
|
||||||
|
} else if (json.has("VirtualAddress") && !json.get("VirtualAddress").isJsonNull()) {
|
||||||
|
current.setVirtualAddress(json.get("VirtualAddress").getAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("virtualASN") && !json.get("virtualASN").isJsonNull()) {
|
||||||
|
current.setVirtualASN(json.get("virtualASN").getAsLong());
|
||||||
|
} else if (json.has("VirtualASN") && !json.get("VirtualASN").isJsonNull()) {
|
||||||
|
current.setVirtualASN(json.get("VirtualASN").getAsLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("virtualSocketName") && !json.get("virtualSocketName").isJsonNull()) {
|
||||||
|
current.setVirtualSocketName(json.get("virtualSocketName").getAsString());
|
||||||
|
} else if (json.has("VirtualSocketName") && !json.get("VirtualSocketName").isJsonNull()) {
|
||||||
|
current.setVirtualSocketName(json.get("VirtualSocketName").getAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("enableTUN")) {
|
||||||
|
current.setEnableTUN(json.get("enableTUN").getAsBoolean());
|
||||||
|
} else if (json.has("EnableTUN")) {
|
||||||
|
current.setEnableTUN(json.get("EnableTUN").getAsBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("tunName") && !json.get("tunName").isJsonNull()) {
|
||||||
|
current.setTUNName(json.get("tunName").getAsString());
|
||||||
|
} else if (json.has("TUNName") && !json.get("TUNName").isJsonNull()) {
|
||||||
|
current.setTUNName(json.get("TUNName").getAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("webUI")) {
|
||||||
|
current.setWebUI(json.get("webUI").getAsBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("webPort")) {
|
||||||
|
current.setWebPort(json.get("webPort").getAsInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("nogui")) {
|
||||||
|
current.setNogui(json.get("nogui").getAsBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("tcpListen") && !json.get("tcpListen").isJsonNull()) {
|
||||||
|
current.setTCPListen(new MultiProtocolSocketAddress(json.get("tcpListen").getAsString()));
|
||||||
|
} else if (json.has("TCPListen") && !json.get("TCPListen").isJsonNull()) {
|
||||||
|
current.setTCPListen(new MultiProtocolSocketAddress(json.get("TCPListen").getAsString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("udpListen") && !json.get("udpListen").isJsonNull()) {
|
||||||
|
current.setUDPListen(new MultiProtocolSocketAddress(json.get("udpListen").getAsString()));
|
||||||
|
} else if (json.has("UDPListen") && !json.get("UDPListen").isJsonNull()) {
|
||||||
|
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")
|
||||||
|
: json.has("OpenConnections") ? json.getAsJsonArray("OpenConnections")
|
||||||
|
: json.has("lineTable") ? json.getAsJsonArray("lineTable") : json.getAsJsonArray("LineTable");
|
||||||
|
List<MultiProtocolSocketAddress> list = new ArrayList<>();
|
||||||
|
for (JsonElement el : arr) {
|
||||||
|
if (el.isJsonPrimitive()) {
|
||||||
|
list.add(new MultiProtocolSocketAddress(el.getAsString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current.setOpenConnections(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("autoConnections") || json.has("AutoConnections") || json.has("connectLineTable") || json.has("ConnectLineTable")) {
|
||||||
|
JsonArray arr = json.has("autoConnections") ? json.getAsJsonArray("autoConnections")
|
||||||
|
: json.has("AutoConnections") ? json.getAsJsonArray("AutoConnections")
|
||||||
|
: json.has("connectLineTable") ? json.getAsJsonArray("connectLineTable") : json.getAsJsonArray("ConnectLineTable");
|
||||||
|
List<MultiProtocolSocketAddress> list = new ArrayList<>();
|
||||||
|
for (JsonElement el : arr) {
|
||||||
|
if (el.isJsonPrimitive()) {
|
||||||
|
list.add(new MultiProtocolSocketAddress(el.getAsString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current.setAutoConnections(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("ntpServers") || json.has("NtpServers") || json.has("ntpServerTable")) {
|
||||||
|
JsonArray arr = json.has("ntpServers") ? json.getAsJsonArray("ntpServers")
|
||||||
|
: json.has("NtpServers") ? json.getAsJsonArray("NtpServers")
|
||||||
|
: json.getAsJsonArray("ntpServerTable");
|
||||||
|
List<MultiProtocolSocketAddress> list = new ArrayList<>();
|
||||||
|
for (JsonElement el : arr) {
|
||||||
|
if (el.isJsonPrimitive()) {
|
||||||
|
list.add(new MultiProtocolSocketAddress(el.getAsString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current.setNtpServers(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("dns") || json.has("DNS")) {
|
||||||
|
JsonArray arr = json.has("dns") ? json.getAsJsonArray("dns") : json.getAsJsonArray("DNS");
|
||||||
|
List<InetAddress> list = new ArrayList<>();
|
||||||
|
for (JsonElement el : arr) {
|
||||||
|
if (el.isJsonPrimitive()) {
|
||||||
|
try {
|
||||||
|
list.add(InetAddress.getByName(el.getAsString()));
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current.setDNS(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("extraRoutes") || json.has("ExtraRoutes")) {
|
||||||
|
JsonArray arr = json.has("extraRoutes") ? json.getAsJsonArray("extraRoutes") : json.getAsJsonArray("ExtraRoutes");
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
for (JsonElement el : arr) {
|
||||||
|
if (el.isJsonPrimitive()) {
|
||||||
|
list.add(el.getAsString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current.setExtraRoutes(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("networkInterfaceExcepts") || json.has("NetworkInterfaceExcepts")) {
|
||||||
|
JsonArray arr = json.has("networkInterfaceExcepts") ? json.getAsJsonArray("networkInterfaceExcepts") : json.getAsJsonArray("NetworkInterfaceExcepts");
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
for (JsonElement el : arr) {
|
||||||
|
if (el.isJsonPrimitive()) {
|
||||||
|
list.add(el.getAsString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current.setNetworkInterfaceExcepts(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("congestionAlgorithm") && !json.get("congestionAlgorithm").isJsonNull()) {
|
||||||
|
current.setCongestionAlgorithm(json.get("congestionAlgorithm").getAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("performanceStrategy") && !json.get("performanceStrategy").isJsonNull()) {
|
||||||
|
current.setPerformanceStrategy(json.get("performanceStrategy").getAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("linkConnectionsCount")) {
|
||||||
|
current.setLinkConnectionsCount(json.get("linkConnectionsCount").getAsInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("burstLimit")) {
|
||||||
|
current.setBurstLimit(json.get("burstLimit").getAsDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("delayUpperBound")) {
|
||||||
|
current.setDelayUpperBound(json.get("delayUpperBound").getAsDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("delayLowerBound")) {
|
||||||
|
current.setDelayLowerBound(json.get("delayLowerBound").getAsDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("nagleDelayTime")) {
|
||||||
|
current.setNagleDelayTime(json.get("nagleDelayTime").getAsLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("linkNagleDelayTime")) {
|
||||||
|
current.setLinkNagleDelayTime(json.get("linkNagleDelayTime").getAsLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("denyConnectionQuery")) {
|
||||||
|
current.setDenyConnectionQuery(json.get("denyConnectionQuery").getAsBoolean());
|
||||||
|
} else if (json.has("denyLineTableQuery")) {
|
||||||
|
current.setDenyConnectionQuery(json.get("denyLineTableQuery").getAsBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.has("denyConnectionBroadcast")) {
|
||||||
|
current.setDenyConnectionBroadcast(json.get("denyConnectionBroadcast").getAsBoolean());
|
||||||
|
} else if (json.has("denyLineTableBroadcast")) {
|
||||||
|
current.setDenyConnectionBroadcast(json.get("denyLineTableBroadcast").getAsBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger GUI save consumer or save directly
|
||||||
|
if (proxySystem.getKLALBGUI() != null && proxySystem.getKLALBGUI().getSaveComsumer() != null) {
|
||||||
|
proxySystem.getKLALBGUI().getSaveComsumer().accept(proxySystem.getConfig());
|
||||||
|
} else {
|
||||||
|
proxySystem.saveConfigToFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObject resp = new JsonObject();
|
||||||
|
resp.addProperty("success", true);
|
||||||
|
resp.addProperty("message", "Configuration updated successfully");
|
||||||
|
sendJsonResponse(exchange, 200, resp);
|
||||||
|
} else {
|
||||||
|
sendError(exchange, 500, "Current configuration is null");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
sendError(exchange, 400, "Failed to update configuration: " + e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sendError(exchange, 405, "Method not allowed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------
|
||||||
|
// SSE Handler & Broadcaster
|
||||||
|
// -------------------------------------------------------------
|
||||||
|
|
||||||
|
private void handleEvents(HttpExchange exchange) throws IOException {
|
||||||
|
if (handleCorsPreflight(exchange)) return;
|
||||||
|
if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) {
|
||||||
|
sendError(exchange, 405, "Method not allowed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Headers headers = exchange.getResponseHeaders();
|
||||||
|
headers.set("Content-Type", "text/event-stream; charset=utf-8");
|
||||||
|
headers.set("Cache-Control", "no-cache, no-transform");
|
||||||
|
headers.set("Connection", "keep-alive");
|
||||||
|
headers.set("Access-Control-Allow-Origin", "*");
|
||||||
|
|
||||||
|
exchange.sendResponseHeaders(200, 0);
|
||||||
|
sseClients.add(exchange);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startSseBroadcaster() {
|
||||||
|
sseExecutor.scheduleAtFixedRate(() -> {
|
||||||
|
if (sseClients.isEmpty()) return;
|
||||||
|
|
||||||
|
JsonObject eventData = new JsonObject();
|
||||||
|
eventData.add("status", getStatusJson());
|
||||||
|
eventData.add("links", getLinksJson());
|
||||||
|
String eventStr = "data: " + eventData.toString() + "\n\n";
|
||||||
|
byte[] bytes = eventStr.getBytes(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
Iterator<HttpExchange> it = sseClients.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
HttpExchange ex = it.next();
|
||||||
|
try {
|
||||||
|
OutputStream os = ex.getResponseBody();
|
||||||
|
os.write(bytes);
|
||||||
|
os.flush();
|
||||||
|
} catch (Exception e) {
|
||||||
|
try {
|
||||||
|
ex.close();
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 200, 200, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------
|
||||||
|
// Static Resource / SPA Handler
|
||||||
|
// -------------------------------------------------------------
|
||||||
|
|
||||||
|
private void handleStatic(HttpExchange exchange) throws IOException {
|
||||||
|
if (handleCorsPreflight(exchange)) return;
|
||||||
|
|
||||||
|
String path = exchange.getRequestURI().getPath();
|
||||||
|
if (path.startsWith("/api/")) {
|
||||||
|
sendError(exchange, 404, "API endpoint not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for dashboard/dist
|
||||||
|
File distDir = new File("dashboard/dist");
|
||||||
|
File targetFile = null;
|
||||||
|
|
||||||
|
if (distDir.exists() && distDir.isDirectory()) {
|
||||||
|
String relPath = path.equals("/") ? "index.html" : path.substring(1);
|
||||||
|
targetFile = new File(distDir, relPath);
|
||||||
|
if (!targetFile.exists() || targetFile.isDirectory()) {
|
||||||
|
targetFile = new File(distDir, "index.html");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetFile != null && targetFile.exists() && targetFile.isFile()) {
|
||||||
|
String mime = getMimeType(targetFile.getName());
|
||||||
|
byte[] content = Files.readAllBytes(targetFile.toPath());
|
||||||
|
setCorsHeaders(exchange);
|
||||||
|
exchange.getResponseHeaders().set("Content-Type", mime);
|
||||||
|
exchange.sendResponseHeaders(200, content.length);
|
||||||
|
try (OutputStream os = exchange.getResponseBody()) {
|
||||||
|
os.write(content);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback welcome message if frontend not built yet
|
||||||
|
String html = "<!DOCTYPE html><html><head><meta charset='utf-8'><title>KLALB Web Dashboard</title>" +
|
||||||
|
"<style>body{font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#09090b;color:#fafafa}" +
|
||||||
|
".card{background:#18181b;padding:2rem;border-radius:0.75rem;border:1px solid #27272a;max-width:480px;text-align:center}" +
|
||||||
|
"h1{margin:0 0 0.5rem;font-size:1.5rem}p{color:#a1a1aa;margin:0 0 1rem;font-size:0.875rem}code{background:#27272a;padding:0.2rem 0.4rem;border-radius:0.25rem}</style></head>" +
|
||||||
|
"<body><div class='card'><h1>KLALB Web Dashboard API Active</h1>" +
|
||||||
|
"<p>The backend API is ready. Start the frontend dev server or build the dashboard: <br><code>cd dashboard && pnpm build</code></p>" +
|
||||||
|
"<p><a href='/api/status' style='color:#38bdf8'>View /api/status</a></p></div></body></html>";
|
||||||
|
byte[] bytes = html.getBytes(StandardCharsets.UTF_8);
|
||||||
|
setCorsHeaders(exchange);
|
||||||
|
exchange.getResponseHeaders().set("Content-Type", "text/html; charset=utf-8");
|
||||||
|
exchange.sendResponseHeaders(200, bytes.length);
|
||||||
|
try (OutputStream os = exchange.getResponseBody()) {
|
||||||
|
os.write(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getMimeType(String filename) {
|
||||||
|
String lower = filename.toLowerCase();
|
||||||
|
if (lower.endsWith(".html")) return "text/html; charset=utf-8";
|
||||||
|
if (lower.endsWith(".js") || lower.endsWith(".mjs")) return "application/javascript; charset=utf-8";
|
||||||
|
if (lower.endsWith(".css")) return "text/css; charset=utf-8";
|
||||||
|
if (lower.endsWith(".json")) return "application/json; charset=utf-8";
|
||||||
|
if (lower.endsWith(".svg")) return "image/svg+xml";
|
||||||
|
if (lower.endsWith(".png")) return "image/png";
|
||||||
|
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
|
||||||
|
if (lower.endsWith(".ico")) return "image/x-icon";
|
||||||
|
if (lower.endsWith(".woff2")) return "font/woff2";
|
||||||
|
if (lower.endsWith(".woff")) return "font/woff";
|
||||||
|
if (lower.endsWith(".ttf")) return "font/ttf";
|
||||||
|
return "application/octet-stream";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,6 +43,10 @@ public class KLALBRoutingProtocol extends Thread{
|
|||||||
|
|
||||||
private RouterInfo selfRouterInfo;
|
private RouterInfo selfRouterInfo;
|
||||||
private Map<IPv6Address, RouterInfo> netmap=new ConcurrentHashMap<>();
|
private Map<IPv6Address, RouterInfo> netmap=new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public Map<IPv6Address, RouterInfo> getNetmap() {
|
||||||
|
return netmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private volatile Map<IPv6Address,Long>addresses;
|
private volatile Map<IPv6Address,Long>addresses;
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public class KLALBRoutingProtocolAPIServer {
|
|||||||
InetSocketAddress addrs=(InetSocketAddress) addr;
|
InetSocketAddress addrs=(InetSocketAddress) addr;
|
||||||
switch(dataobj.getType()){
|
switch(dataobj.getType()){
|
||||||
case KLALBRoutingProtocolJsonData.OPEN_LINES_REQ:
|
case KLALBRoutingProtocolJsonData.OPEN_LINES_REQ:
|
||||||
if(controller.getConfigItem()==null||(!controller.getConfigItem().isDenyLineTableQuery())) {
|
if(controller.getConfigItem()==null||(!controller.getConfigItem().isDenyConnectionQuery())) {
|
||||||
KLALBRoutingProtocolJsonData json=new KLALBRoutingProtocolJsonData(KLALBRoutingProtocolJsonData.OPEN_LINES_RESP,dataobj.getUuid(),controller.getSelflineTable());
|
KLALBRoutingProtocolJsonData json=new KLALBRoutingProtocolJsonData(KLALBRoutingProtocolJsonData.OPEN_LINES_RESP,dataobj.getUuid(),controller.getSelflineTable());
|
||||||
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(json),addr);
|
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(json),addr);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user