forked from KNEMC/KLALB
Compare commits
18
Commits
cc811eddfe
...
patch-4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dde54d0929
|
||
|
|
3dae7dd876
|
||
|
|
b6acc6d509
|
||
|
|
be82f55277
|
||
|
|
7896ee6638
|
||
|
|
ed94423855
|
||
|
|
c3172a04b1
|
||
|
|
cc803f47a7 | ||
|
|
3bb95988fc | ||
|
|
e87e3bb953 | ||
|
|
41db17f1ec | ||
|
|
61868fe93e
|
||
|
|
9cea5ed493
|
||
|
|
e0296e0ccd
|
||
|
|
9cea74943c
|
||
|
|
35c430114e
|
||
|
|
6c7017bc75
|
||
|
|
041cc31c5f
|
+13
@@ -3,3 +3,16 @@
|
||||
/klalbs4.json
|
||||
/klalbs.json
|
||||
/klalbs2.json
|
||||
|
||||
# Dashboard / Frontend
|
||||
dashboard/node_modules/
|
||||
dashboard/dist/
|
||||
dashboard/.pnpm-store/
|
||||
.pnpm-debug.log*
|
||||
dashboard/.env.local
|
||||
dashboard/.env.*.local
|
||||
|
||||
/klalb-config.json
|
||||
/klalbconfig-old.json
|
||||
|
||||
/.slim/
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "dashboard"]
|
||||
path = dashboard
|
||||
url = https://git.code.cq.cn/SerinaNya/KLALB-dashboard.git
|
||||
@@ -1,55 +1,39 @@
|
||||
# AGENTS.md
|
||||
# KLALB Repository Guide
|
||||
|
||||
KLALB ("KLALB Decentralized SRv6 Network") — Java load-balancing/tunnel system that merges multiple WAN links into one virtual IPv6/SRv6 network. Version constant lives in `src/org/kne/cloud/network/klalb/CONST.java`. Protocol specs and manuals are the Chinese `.docx` files in the repo root.
|
||||
KLALB is a Java SRv6/load-balancing system. Start at `org.kne.cloud.network.klalb.KLALBMain`; runtime configuration is root-relative `klalb-config.json`.
|
||||
|
||||
## Build & run
|
||||
## Build And Run
|
||||
|
||||
No Maven/Gradle. Plain Eclipse/IntelliJ project: dependencies are vendored jars in `lib/`, output goes to `bin/` (gitignored). When adding a jar, update **both** `.classpath` and `KLALB.iml`.
|
||||
|
||||
Compile (verified; `-encoding UTF-8` is mandatory — sources contain Chinese text):
|
||||
- This is an Eclipse Java project, not Maven or Gradle: `src/` contains sources, `lib/` vendored dependencies, and `bin/` compiled output. `.classpath` targets Java 25.
|
||||
- Source files contain Chinese text, so compile with UTF-8 from the repository root:
|
||||
|
||||
```powershell
|
||||
& javac -encoding UTF-8 -cp "lib/*" -d bin (Get-ChildItem -Recurse src -Filter *.java | ForEach-Object FullName)
|
||||
& "C:\Program Files\Zulu\zulu-25\bin\javac.exe" -encoding UTF-8 -cp "lib/*" -d bin (Get-ChildItem -Recurse src -Filter *.java | ForEach-Object FullName)
|
||||
```
|
||||
|
||||
Run from the repo root — CWD matters:
|
||||
- reads `klalb-config.json` from CWD
|
||||
- loads native libs from CWD: `tuntap4j.dll/.so/.dylib`, `wintun.dll`, `fastcopy.dll` (TUN device support)
|
||||
- classpath must include `src` as well as `bin`: i18n bundles (`/klalb_*.properties`) and images (`/assets/*`) are classpath resources that Eclipse copies to `bin` but manual `javac` does not
|
||||
- Run from the repository root. `src` must stay on the classpath because manual compilation does not copy resource bundles:
|
||||
|
||||
```powershell
|
||||
java --enable-native-access=ALL-UNNAMED "--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED" -cp "bin;src;lib/*" org.kne.cloud.network.klalb.KLALBMain
|
||||
& "C:\Program Files\Zulu\zulu-25\bin\java.exe" --enable-native-access=ALL-UNNAMED "--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED" -cp "bin;src;lib/*" org.kne.cloud.network.klalb.KLALBMain
|
||||
```
|
||||
|
||||
IDE metadata targets JDK 26 (`jdk-26.0.1`); the tree also compiles cleanly on JDK 25. `.classpath` now references the standard container `JavaSE-25` — an execution-environment spec that any JDK ≥25 satisfies, so it works unchanged on JDK 26 machines too (the original named `jdk-26.0.1` VM broke VS Code import on machines without it). `.vscode/settings.json` maps `JavaSE-25` to the locally installed Adoptium JDK; register every installed JDK there when adding another one. Keep compiler compliance ≤25 (`.settings` pins 19) so both JDKs stay usable.
|
||||
- Native libraries and `klalb-config.json` are resolved from the current directory; restart the JVM after recompiling. TUN creation requires elevation, so use `"enableTUN": false` for non-admin checks.
|
||||
|
||||
Runtime gotchas (all verified):
|
||||
- On JDK 25, `KNEOptimize.jar`'s `FastLib` reflects into `jdk.internal.misc.Unsafe`; without the two JVM flags above it throws `InaccessibleObjectException` at startup (app still runs).
|
||||
- Creating the SRv6 TUN adapter (`WintunCreateAdapter`) requires an elevated shell; without admin rights it logs "创建虚拟网卡失败" and continues with only the `inLoopBack` interface — links/bridges still work.
|
||||
- To disable TUN creation completely (e.g. for non-admin UI/routing testing), set `"TUNName": null` in `klalb-config.json` or leave the TUN Name empty in GUI settings. If omitted, it defaults to `"KLALB_SRv6"`.
|
||||
- Routing broadcast (`RouterInfo`) transmits `deviceName` across the network, which topology and node overview panels display. `deviceDescription` and `ExtraRoutes` remain local controller configs.
|
||||
## Dashboard
|
||||
|
||||
- `dashboard/` is a Git submodule. Commit dashboard changes in that repository, then update the parent submodule pointer.
|
||||
- Run frontend commands from `dashboard/`: `pnpm install --frozen-lockfile`, `pnpm lint`, `pnpm typecheck`, and `pnpm build`. The build is `tsc -b && vite build` and produces `dashboard/dist`, which the Java server hosts.
|
||||
- Vite proxies `/api` to `http://127.0.0.1:4665`; use `pnpm dev` only with the Java API running there.
|
||||
|
||||
## Verification
|
||||
|
||||
No test suite, no CI. Classes named `*Test*` (`nathole/`, `ntp/`) are manual `main()` harnesses requiring real network peers. Practical check = compile succeeds + app launches.
|
||||
- No repository test runner or CI workflow is configured. For Java changes, compile and launch the app; for dashboard changes, run `pnpm typecheck` and `pnpm build`.
|
||||
|
||||
## Architecture
|
||||
## Important Boundaries
|
||||
|
||||
- Entrypoint `org.kne.cloud.network.klalb.KLALBMain`: load config → build `KLALBProxySystem` → open Swing GUI (`KLALBStateGUI3`) unless `"nogui": true` → interactive console (`help`, `links-state`, `route`, `kperf`, ...).
|
||||
- `org.kne.cloud.network` — generic socket framework: `VirtualSocket*` hierarchy, `SocketBridge` port-forwarding proxies, `ProtocolDetector` (multi-protocol mux on one port), `MultiProtocolSocketAddress` = URI-style addresses (`tcp://`, `udp://`, `kltp://`, `ntp://`) dispatched through the `SocketType` registry.
|
||||
- `...network.klalb` — app core: `KLALBController` (the virtual SRv6 network), `KLALBRemoteLink` (WAN lines), `*Packet` wire-format classes, virtual socket implementations.
|
||||
- `...network.congestion` — pluggable congestion control (BBR, Vegas2, DCTCP...), chosen via `"congestionAlgorithm"` in config.
|
||||
- `...network.kltp` — custom reliable transport protocol (packets/streams).
|
||||
- `...network.ipv6`, `...network.srv6` — packet codecs, route table, Dijkstra path computation.
|
||||
- `...network.frpc` — frp client integration.
|
||||
- `...klalb.ui` — all Swing UI code.
|
||||
|
||||
## Config
|
||||
|
||||
`klalb-config.json` is an array of items discriminated by their `"Type"` field. Adding a new item type requires a `KLALBConfigItem` subclass **plus** new cases in both `KLALBConfigItem.getDefaultJsonDeserializer()` and `getDefaultJsonSerializer()`; unknown types are preserved as `UnknownKLALBConfigItem`. Any Gson instance handling config must register these adapters via `registerToGsonBuilder` (see `KLALBProxySystem`).
|
||||
|
||||
## Conventions
|
||||
|
||||
- Sources are UTF-8; comments, log/UI strings, and commit messages are largely Chinese.
|
||||
- UI strings go through `UIEnv.getRsb().getString(...)`; add keys to **both** `src/klalb_zh_CN.properties` and `src/klalb_en_US.properties`.
|
||||
- `client.cfg`, `server.cfg`, `linetable.txt` at the root are example line-table/port-rule files loaded via the GUI file picker — not hardwired paths.
|
||||
- `KLALBConfigItem` is a polymorphic JSON array keyed by case-sensitive `Type`. New types need serializer and deserializer support; preserve unknown items' raw JSON.
|
||||
- `/api/config` is field-by-field parsing, not whole-object Gson mapping. Keep legacy key aliases in sync with new fields.
|
||||
- Vendored Gson is `2.1`: responses that are `JsonElement` instances must be serialized with `JsonElement.toString()`, not reflective `gson.toJson(Object)`.
|
||||
- UI strings use `UIEnv.getRsb()`; add keys to both `src/klalb_zh_CN.properties` and `src/klalb_en_US.properties`.
|
||||
- `KLALBController.PublishedNodeInfo` is the thread-safe node-info source. Publish profile, effective external endpoints, and extra routes through the controller so snapshots and update flags remain coherent. Peer queries are separate profile, endpoint, and extra-route requests; use `NodeInfoQueryCoordinator` when a consumer needs an aggregate detail result.
|
||||
- `RouterInfo` retains an empty legacy UTF slot. `RouterInfoPacket` appends optional update flags behind a marker; treat binary codec changes as mesh-compatibility work and preserve old-reader behavior.
|
||||
|
||||
Submodule
+1
Submodule dashboard added at 8c13af680a
@@ -1,107 +0,0 @@
|
||||
[
|
||||
{
|
||||
"language": "ZH_CN",
|
||||
"nogui": false,
|
||||
"VirtualAddress": "2486:1:0:0:0:0:0:8889",
|
||||
"VirtualASN": 2142606939373348329,
|
||||
"DNS": [
|
||||
"2486:1:0:0:0:0:0:8888"
|
||||
],
|
||||
"TCPListen": "tcp://0.0.0.0:4565",
|
||||
"UDPListen": "udp://0.0.0.0:4572",
|
||||
"VirtualSocketName": "kltp",
|
||||
"LineTable": [
|
||||
"tcp://kne03.yoyo250.fun:4565",
|
||||
"tcp://kne04.yoyo250.fun:4565"
|
||||
],
|
||||
"ConnectLineTable": [
|
||||
"tcp://07f4acdef99b.ofalias.net:4565",
|
||||
"tcp://kne01.yoyo250.fun:4565",
|
||||
"tcp://kne02.yoyo250.fun:4565"
|
||||
],
|
||||
"ntpServerTable": [
|
||||
"ntp://ntp1.aliyun.com",
|
||||
"ntp://ntp2.aliyun.com",
|
||||
"ntp://ntp3.aliyun.com",
|
||||
"ntp://ntp4.aliyun.com",
|
||||
"ntp://ntp5.aliyun.com",
|
||||
"ntp://ntp6.aliyun.com",
|
||||
"ntp://ntp7.aliyun.com",
|
||||
"ntp://ntp1.tencent.com",
|
||||
"ntp://ntp2.tencent.com",
|
||||
"ntp://ntp4.tencent.com",
|
||||
"ntp://ntp5.tencent.com",
|
||||
"ntp://time.google.com",
|
||||
"ntp://time.apple.com",
|
||||
"ntp://pool.ntp.org",
|
||||
"ntp://ntp.ntsc.ac.cn",
|
||||
"ntp://us.ntp.org.cn"
|
||||
],
|
||||
"ExtraRoutes": [],
|
||||
"denyLineTableQuery": false,
|
||||
"denyLineTableBroadcast": false,
|
||||
"congestionAlgorithm": "BBR",
|
||||
"burstLimit": 2.0,
|
||||
"delayUpperBound": 1.2,
|
||||
"delayLowerBound": 1.1,
|
||||
"nagleDelayTime": 1000000,
|
||||
"linkNagleDelayTime": 0,
|
||||
"linkConnectionsCount": 1,
|
||||
"TUNName": null,
|
||||
"performanceStrategy": "multiscatter",
|
||||
"DeviceName": "SerinaNya PC",
|
||||
"DeviceDescription": "desc",
|
||||
"NetworkInterfaceExcepts": [],
|
||||
"Type": "KLALBController"
|
||||
},
|
||||
{
|
||||
"Listen": "kltp://[::0]:5201",
|
||||
"Bridge": {
|
||||
"DEFAULT": "SocketBridge"
|
||||
},
|
||||
"Connect": {
|
||||
"DEFAULT": "tcp://127.0.0.1:5201"
|
||||
},
|
||||
"Type": "SocketBridge"
|
||||
},
|
||||
{
|
||||
"Listen": "tcp://[::1]:5202",
|
||||
"Bridge": {
|
||||
"DEFAULT": "SocketBridge"
|
||||
},
|
||||
"Connect": {
|
||||
"DEFAULT": "kltp://[2486:5acd:e339:4837:a6d9:3aed:4de2:30fd]:5201"
|
||||
},
|
||||
"Type": "SocketBridge"
|
||||
},
|
||||
{
|
||||
"Listen": "tcp://127.0.0.1:35000",
|
||||
"Bridge": {
|
||||
"DEFAULT": "SocketBridge"
|
||||
},
|
||||
"Connect": {
|
||||
"DEFAULT": "kltp://[2486:1::8888]:23333"
|
||||
},
|
||||
"Type": "SocketBridge"
|
||||
},
|
||||
{
|
||||
"Listen": "kltp://[::0]:25565",
|
||||
"Bridge": {
|
||||
"DEFAULT": "SocketBridge"
|
||||
},
|
||||
"Connect": {
|
||||
"DEFAULT": "tcp://127.0.0.1:25566"
|
||||
},
|
||||
"Type": "SocketBridge"
|
||||
},
|
||||
{
|
||||
"Listen": "tcp://127.0.0.1:35565",
|
||||
"Bridge": {
|
||||
"DEFAULT": "SocketBridge"
|
||||
},
|
||||
"Connect": {
|
||||
"DEFAULT": "kltp://[2486:1::8888]:25565"
|
||||
},
|
||||
"Type": "SocketBridge"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"version": 1,
|
||||
"skills": {
|
||||
"shadcn": {
|
||||
"source": "shadcn/ui",
|
||||
"sourceType": "github",
|
||||
"skillPath": "skills/shadcn/SKILL.md",
|
||||
"computedHash": "c1a68ee06a668aced9ab2b5fbdea5f989864123794eb2e056b339a072dbb7f10"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ devicename=Device name
|
||||
devicedescription=Device description
|
||||
dnsserver=DNS server
|
||||
extraroutes=Extra routes
|
||||
noextraroutes=No extra routes
|
||||
asnumber=AS number
|
||||
tcplistening=TCP listening
|
||||
udplistening=UDP listening
|
||||
@@ -112,3 +113,8 @@ performancestrategy=Performance strategy
|
||||
singlecore=Single-Core - Cache Affinity First (Best energy/performance ratio, for low-power & cloud)
|
||||
multifill=Multi-Core - Fill Cores Sequentially (Recommended for general-purpose physical servers)
|
||||
multiscatter=Multi-Core - Spread Load Evenly (Optimized for multi-socket NUMA architectures)
|
||||
webapisettings=Web API settings
|
||||
enablewebapi=Enable Web API
|
||||
weblistenaddr=Web API listen address
|
||||
invaildweblistenaddr=Invalid Web API listen address
|
||||
enabletun=Enable TUN adapter
|
||||
|
||||
@@ -7,6 +7,7 @@ devicename=设备名称
|
||||
devicedescription=设备描述
|
||||
dnsserver=DNS服务器
|
||||
extraroutes=额外路由
|
||||
noextraroutes=暂无额外路由
|
||||
asnumber=AS号码
|
||||
tcplistening=TCP监听端口
|
||||
udplistening=UDP监听端口
|
||||
@@ -112,3 +113,8 @@ performancestrategy=性能策略
|
||||
singlecore=单核-缓存命中率优先(高能耗比,适合低功耗设备、云机)
|
||||
multifill=多核-负载按顺序填充(适合大多数物理服务器、电脑)
|
||||
multiscatter=多核-负载均匀打散分配(适合特殊的多路NUMA服务器)
|
||||
webapisettings=Web API 设置
|
||||
enablewebapi=启用 Web API
|
||||
weblistenaddr=Web API 监听地址:端口
|
||||
invaildweblistenaddr=无效的 Web API 监听地址:端口
|
||||
enabletun=启用TUN虚拟网卡
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
@@ -61,6 +62,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 +72,39 @@ 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");
|
||||
if (!jobj.has("webListen") && jobj.has("webPort") && !jobj.get("webPort").isJsonNull()) {
|
||||
int port = jobj.get("webPort").getAsInt();
|
||||
jobj.add("webListen", new JsonPrimitive("http://0.0.0.0:" + port));
|
||||
}
|
||||
jobj.remove("webPort");
|
||||
}
|
||||
|
||||
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(){
|
||||
|
||||
@@ -11,11 +11,13 @@ import java.net.SocketException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
@@ -54,6 +56,7 @@ import org.kne.cloud.network.ntp.NTPv4Protocol.NTPPeer;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocol;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIServer;
|
||||
import org.kne.cloud.network.srv6.NodeInfoQueryStatus;
|
||||
import org.kne.cloud.network.srv6.SRv6Router;
|
||||
import org.kne.cloud.network.srv6.SRv6RouterListener;
|
||||
import org.kne.cloud.network.tcp.UDPPacket;
|
||||
@@ -76,7 +79,50 @@ 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> configuredExternalEndpoints = new ArrayList<>();
|
||||
private List<MultiProtocolSocketAddress> discoveredExternalEndpoints = new ArrayList<>();
|
||||
|
||||
public static final class PublishedNodeInfo {
|
||||
private final String deviceName;
|
||||
private final String deviceDescription;
|
||||
private final List<MultiProtocolSocketAddress> externalEndpoints;
|
||||
private final List<String> extraRoutes;
|
||||
private final long fullRevision;
|
||||
|
||||
private PublishedNodeInfo(String deviceName, String deviceDescription,
|
||||
List<MultiProtocolSocketAddress> externalEndpoints, List<String> extraRoutes, long fullRevision) {
|
||||
this.deviceName = deviceName;
|
||||
this.deviceDescription = deviceDescription;
|
||||
this.externalEndpoints = Collections.unmodifiableList(
|
||||
new ArrayList<MultiProtocolSocketAddress>(externalEndpoints));
|
||||
this.extraRoutes = Collections.unmodifiableList(new ArrayList<String>(extraRoutes));
|
||||
this.fullRevision = fullRevision;
|
||||
}
|
||||
|
||||
public String getDeviceName() {
|
||||
return deviceName;
|
||||
}
|
||||
|
||||
public String getDeviceDescription() {
|
||||
return deviceDescription;
|
||||
}
|
||||
|
||||
public List<MultiProtocolSocketAddress> getExternalEndpoints() {
|
||||
return externalEndpoints;
|
||||
}
|
||||
|
||||
public List<String> getExtraRoutes() {
|
||||
return extraRoutes;
|
||||
}
|
||||
|
||||
public long getFullRevision() {
|
||||
return fullRevision;
|
||||
}
|
||||
}
|
||||
|
||||
private volatile PublishedNodeInfo publishedNodeInfo = new PublishedNodeInfo(null, null,
|
||||
Collections.<MultiProtocolSocketAddress>emptyList(), Collections.<String>emptyList(), 0L);
|
||||
|
||||
private List<MultiProtocolSocketAddress> listensSocketAddress = new CopyOnWriteArrayList<>();
|
||||
|
||||
@@ -126,6 +172,7 @@ public class KLALBController {
|
||||
public void run() {
|
||||
try {
|
||||
List<InetAddress> localaddress = networkInterfaceManager.getAllNetworkInterfaceAddress();
|
||||
List<MultiProtocolSocketAddress> currentDiscoveredExternalEndpoints = new ArrayList<>();
|
||||
|
||||
for (InetAddress inetAddress : localaddress) {
|
||||
for (Iterator<MultiProtocolSocketAddress> iterator = listensSocketAddress.iterator(); iterator.hasNext();) {
|
||||
@@ -137,10 +184,8 @@ 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);
|
||||
}
|
||||
if (!currentDiscoveredExternalEndpoints.contains(bind)) {
|
||||
currentDiscoveredExternalEndpoints.add(bind);
|
||||
}
|
||||
}
|
||||
} catch (UnknownHostException e) {
|
||||
@@ -150,6 +195,12 @@ public class KLALBController {
|
||||
|
||||
}
|
||||
}
|
||||
synchronized (externalEndpoints) {
|
||||
if (!discoveredExternalEndpoints.equals(currentDiscoveredExternalEndpoints)) {
|
||||
discoveredExternalEndpoints = currentDiscoveredExternalEndpoints;
|
||||
publishDiscoveredExternalEndpointLocked();
|
||||
}
|
||||
}
|
||||
|
||||
lineslock.writeLock().lock();
|
||||
try {
|
||||
@@ -210,7 +261,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.isDenyLineTableBroadcast())) {
|
||||
|| (configItem != null && configItem.isDenyExternalEndpointBroadcast())) {
|
||||
iterator.remove();
|
||||
try {
|
||||
ipMulticastDiscovery.close();
|
||||
@@ -222,7 +273,7 @@ public class KLALBController {
|
||||
}
|
||||
}
|
||||
|
||||
if (configItem != null && configItem.isDenyLineTableBroadcast()) {
|
||||
if (configItem != null && configItem.isDenyExternalEndpointBroadcast()) {
|
||||
|
||||
} else {
|
||||
List<NetworkInterface> interfaceList = networkInterfaceManager.getAllAvaliableNetworkInterface();
|
||||
@@ -299,7 +350,7 @@ public class KLALBController {
|
||||
private boolean checkIsSelf(MultiProtocolSocketAddress inetAddress) throws UnknownHostException {
|
||||
|
||||
return inetAddress.getInetAddress().isAnyLocalAddress() || inetAddress.getInetAddress().isLoopbackAddress()
|
||||
|| selflineTable.contains(inetAddress);
|
||||
|| publishedNodeInfo.getExternalEndpoints().contains(inetAddress);
|
||||
}
|
||||
|
||||
private boolean checkIsSelfLocator(InetAddress inetAddress) {
|
||||
@@ -339,9 +390,11 @@ public class KLALBController {
|
||||
try {
|
||||
InetSocketAddress iaddr = new InetSocketAddress(neighbor.getAddress().toInet6Address(),
|
||||
KLALBRoutingProtocol.DEFAULT_PORT);
|
||||
apiClient.requestOpenLines(iaddr, (v) -> {
|
||||
apiClient.requestExternalEndpoints(iaddr, (v) -> {
|
||||
if (v == null || v.getStatus() != NodeInfoQueryStatus.OK) return;
|
||||
ThreadTool.makeVDaemonThreadIfSupport("线路添加任务", () -> {
|
||||
for (MultiProtocolSocketAddress msa : v) {
|
||||
if (v.getExternalEndpoints() == null) return;
|
||||
for (MultiProtocolSocketAddress msa : v.getExternalEndpoints()) {
|
||||
// System.out.print(msa);
|
||||
addRemoteLines(msa);
|
||||
}
|
||||
@@ -401,8 +454,103 @@ public class KLALBController {
|
||||
}
|
||||
|
||||
|
||||
public List<MultiProtocolSocketAddress> getSelflineTable() {
|
||||
return selflineTable;
|
||||
public List<MultiProtocolSocketAddress> getExternalEndpoints() {
|
||||
return publishedNodeInfo.getExternalEndpoints();
|
||||
}
|
||||
|
||||
public List<MultiProtocolSocketAddress> getExternalEndpointsSnapshot() {
|
||||
return new ArrayList<MultiProtocolSocketAddress>(publishedNodeInfo.getExternalEndpoints());
|
||||
}
|
||||
|
||||
public PublishedNodeInfo getPublishedNodeInfo() {
|
||||
return publishedNodeInfo;
|
||||
}
|
||||
|
||||
private List<MultiProtocolSocketAddress> createEffectiveExternalEndpointsLocked() {
|
||||
List<MultiProtocolSocketAddress> effective = new ArrayList<MultiProtocolSocketAddress>();
|
||||
for (MultiProtocolSocketAddress endpoint : configuredExternalEndpoints) {
|
||||
if (!effective.contains(endpoint)) {
|
||||
effective.add(endpoint);
|
||||
}
|
||||
}
|
||||
for (MultiProtocolSocketAddress endpoint : discoveredExternalEndpoints) {
|
||||
if (!effective.contains(endpoint)) {
|
||||
effective.add(endpoint);
|
||||
}
|
||||
}
|
||||
return effective;
|
||||
}
|
||||
|
||||
private void publishNodeInfoLocked(String deviceName, String deviceDescription,
|
||||
List<MultiProtocolSocketAddress> configEndpoints, List<String> extraRoutes) {
|
||||
configuredExternalEndpoints = configEndpoints == null
|
||||
? new ArrayList<MultiProtocolSocketAddress>()
|
||||
: new ArrayList<MultiProtocolSocketAddress>(configEndpoints);
|
||||
List<String> publishedExtraRoutes = extraRoutes == null
|
||||
? new ArrayList<String>() : new ArrayList<String>(extraRoutes);
|
||||
List<MultiProtocolSocketAddress> effective = createEffectiveExternalEndpointsLocked();
|
||||
PublishedNodeInfo previous = publishedNodeInfo;
|
||||
boolean nameChanged = !Objects.equals(previous.getDeviceName(), deviceName);
|
||||
boolean fullChanged = !Objects.equals(previous.getDeviceDescription(), deviceDescription)
|
||||
|| !previous.getExternalEndpoints().equals(effective)
|
||||
|| !previous.getExtraRoutes().equals(publishedExtraRoutes);
|
||||
|
||||
externalEndpoints.clear();
|
||||
externalEndpoints.addAll(effective);
|
||||
if (srv6Router != null) {
|
||||
srv6Router.setDeviceName(deviceName);
|
||||
}
|
||||
long fullRevision = previous.getFullRevision() + (fullChanged ? 1L : 0L);
|
||||
publishedNodeInfo = new PublishedNodeInfo(deviceName, deviceDescription, effective, publishedExtraRoutes,
|
||||
fullRevision);
|
||||
if (routingProtocol != null) {
|
||||
if (nameChanged) {
|
||||
routingProtocol.announceNodeInfoUpdate(
|
||||
org.kne.cloud.network.srv6.RouterInfoPacket.NODE_INFO_TINY_UPDATE_REQUIRED);
|
||||
}
|
||||
if (fullChanged) {
|
||||
routingProtocol.announceNodeInfoUpdate(
|
||||
org.kne.cloud.network.srv6.RouterInfoPacket.NODE_INFO_FULL_UPDATE_REQUIRED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void publishNodeInfo(String deviceName, String deviceDescription,
|
||||
List<MultiProtocolSocketAddress> configEndpoints, List<String> extraRoutes) {
|
||||
synchronized (externalEndpoints) {
|
||||
if (configItem != null) {
|
||||
configItem.setDeviceName(deviceName);
|
||||
configItem.setDeviceDescription(deviceDescription);
|
||||
configItem.setExternalEndpoints(new ArrayList<MultiProtocolSocketAddress>(configEndpoints == null
|
||||
? Collections.<MultiProtocolSocketAddress>emptyList() : configEndpoints));
|
||||
configItem.setExtraRoutes(new ArrayList<String>(extraRoutes == null
|
||||
? Collections.<String>emptyList() : extraRoutes));
|
||||
}
|
||||
publishNodeInfoLocked(deviceName, deviceDescription, configEndpoints, extraRoutes);
|
||||
}
|
||||
}
|
||||
|
||||
private void publishDiscoveredExternalEndpointLocked() {
|
||||
List<MultiProtocolSocketAddress> effective = createEffectiveExternalEndpointsLocked();
|
||||
if (new HashSet<MultiProtocolSocketAddress>(effective)
|
||||
.equals(new HashSet<MultiProtocolSocketAddress>(externalEndpoints))) {
|
||||
return;
|
||||
}
|
||||
externalEndpoints.clear();
|
||||
externalEndpoints.addAll(effective);
|
||||
PublishedNodeInfo previous = publishedNodeInfo;
|
||||
publishedNodeInfo = new PublishedNodeInfo(previous.getDeviceName(), previous.getDeviceDescription(), effective,
|
||||
previous.getExtraRoutes(), previous.getFullRevision() + 1L);
|
||||
if (routingProtocol != null) {
|
||||
routingProtocol.announceNodeInfoUpdate(
|
||||
org.kne.cloud.network.srv6.RouterInfoPacket.NODE_INFO_FULL_UPDATE_REQUIRED);
|
||||
}
|
||||
}
|
||||
|
||||
public void replaceExternalEndpoints(List<MultiProtocolSocketAddress> endpoints) {
|
||||
PublishedNodeInfo published = publishedNodeInfo;
|
||||
publishNodeInfo(published.getDeviceName(), published.getDeviceDescription(), endpoints,
|
||||
published.getExtraRoutes());
|
||||
}
|
||||
|
||||
|
||||
@@ -593,19 +741,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();) {
|
||||
MultiProtocolSocketAddress klalbRemoteLine = (MultiProtocolSocketAddress) iterator.next();
|
||||
for (MultiProtocolSocketAddress klalbRemoteLine : publishedNodeInfo.getExternalEndpoints()) {
|
||||
sbd.append(klalbRemoteLine.toString());
|
||||
sbd.append('\n');
|
||||
}
|
||||
@@ -647,8 +794,9 @@ public class KLALBController {
|
||||
rawPortBinder= new PortBinder(this.getSelf().getAddress());
|
||||
System.out.println(" Loaded: SRv6 Stack");
|
||||
|
||||
String name=(configItem!=null)?configItem.getTUNName():CONST.KLALB_S_RV6;
|
||||
boolean enableTUN = enableVirtualAdapter && (name != null) && !name.trim().isEmpty() && !name.trim().equalsIgnoreCase("null");
|
||||
String name = (configItem != null && configItem.getTUNName() != null) ? configItem.getTUNName() : CONST.KLALB_S_RV6;
|
||||
boolean isEnabled = configItem == null || configItem.isEnableTUN();
|
||||
boolean enableTUN = enableVirtualAdapter && isEnabled && (name != null) && !name.trim().isEmpty() && !name.trim().equalsIgnoreCase("null");
|
||||
if (enableTUN) {
|
||||
Thread t=new Thread(()->{
|
||||
try {
|
||||
@@ -764,6 +912,9 @@ public class KLALBController {
|
||||
}
|
||||
}).start();
|
||||
loadSRv6ProtocolStack(selfg, enableVirtualAdapter);
|
||||
if (configItem == null) {
|
||||
publishNodeInfo(srv6Router.getDeviceName(), null, null, null);
|
||||
}
|
||||
loadController();
|
||||
}
|
||||
|
||||
@@ -800,18 +951,17 @@ public class KLALBController {
|
||||
getIpv6Router().setASN(vasn);
|
||||
}
|
||||
|
||||
List<MultiProtocolSocketAddress> linele = configItem.getLineTable();
|
||||
if (linele != null) {
|
||||
getSelflineTable().addAll(linele);
|
||||
}
|
||||
List<MultiProtocolSocketAddress> linetoc = configItem.getConnectLineTable();
|
||||
List<MultiProtocolSocketAddress> linele = configItem.getExternalEndpoints();
|
||||
publishNodeInfo(configItem.getDeviceName(), configItem.getDeviceDescription(), linele,
|
||||
configItem.getExtraRoutes());
|
||||
List<MultiProtocolSocketAddress> linetoc = configItem.getAutoConnections();
|
||||
if (linetoc != null) {
|
||||
linetoc.forEach((aline) -> {
|
||||
addRemoteLines(aline);
|
||||
});
|
||||
}
|
||||
|
||||
List<MultiProtocolSocketAddress> ntps = configItem.getNtpServerTable();
|
||||
List<MultiProtocolSocketAddress> ntps = configItem.getNtpServers();
|
||||
if (ntps != null) {
|
||||
getNTPTable().addAll(ntps);
|
||||
}
|
||||
|
||||
@@ -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>LineTable=new ArrayList<>();
|
||||
private List<MultiProtocolSocketAddress>ConnectLineTable=new ArrayList<>();
|
||||
private List<MultiProtocolSocketAddress>ntpServerTable=new ArrayList<>();
|
||||
private List<String>ExtraRoutes=new ArrayList<>();
|
||||
private boolean denyLineTableQuery=false;
|
||||
private boolean denyLineTableBroadcast=false;
|
||||
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 denyExternalEndpointQuery = false;
|
||||
private boolean denyExternalEndpointBroadcast = false;
|
||||
private String congestionAlgorithm="BBR";
|
||||
private double burstLimit=1.50;
|
||||
private double delayUpperBound=1.20;
|
||||
@@ -29,10 +29,37 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
|
||||
private long nagleDelayTime=1000000L;
|
||||
private long linkNagleDelayTime=1000000L;
|
||||
private int linkConnectionsCount=1;
|
||||
private boolean enableTUN = true;
|
||||
private String TUNName=CONST.KLALB_S_RV6;
|
||||
private String performanceStrategy="multifill";
|
||||
private String DeviceName;
|
||||
private String DeviceDescription;
|
||||
private boolean webUI = false;
|
||||
private MultiProtocolSocketAddress webListen = new MultiProtocolSocketAddress("http", "0.0.0.0", 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 MultiProtocolSocketAddress getWebListen() {
|
||||
return webListen;
|
||||
}
|
||||
|
||||
public void setWebListen(MultiProtocolSocketAddress webListen) {
|
||||
this.webListen = webListen;
|
||||
}
|
||||
|
||||
public String getPerformanceStrategy() {
|
||||
return performanceStrategy;
|
||||
@@ -177,39 +204,44 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
|
||||
|
||||
|
||||
|
||||
public List<MultiProtocolSocketAddress> getLineTable() {
|
||||
return LineTable;
|
||||
public List<MultiProtocolSocketAddress> getExternalEndpoints() {
|
||||
return externalEndpoints;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setLineTable(List<MultiProtocolSocketAddress> lineTable) {
|
||||
LineTable = lineTable;
|
||||
public void setExternalEndpoints(List<MultiProtocolSocketAddress> externalEndpoints) {
|
||||
this.externalEndpoints = externalEndpoints;
|
||||
}
|
||||
|
||||
public List<MultiProtocolSocketAddress> getAutoConnections() {
|
||||
return autoConnections;
|
||||
}
|
||||
|
||||
public void setAutoConnections(List<MultiProtocolSocketAddress> autoConnections) {
|
||||
this.autoConnections = autoConnections;
|
||||
}
|
||||
|
||||
public List<MultiProtocolSocketAddress> getConnectLineTable() {
|
||||
return ConnectLineTable;
|
||||
return autoConnections;
|
||||
}
|
||||
|
||||
|
||||
|
||||
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() {
|
||||
return ntpServerTable;
|
||||
return ntpServers;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void setNtpServerTable(List<MultiProtocolSocketAddress> ntpServerTable) {
|
||||
this.ntpServerTable = ntpServerTable;
|
||||
this.ntpServers = ntpServerTable;
|
||||
}
|
||||
|
||||
public List<String> getExtraRoutes() {
|
||||
@@ -280,23 +312,20 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
|
||||
|
||||
|
||||
|
||||
public boolean isDenyLineTableQuery() {
|
||||
return denyLineTableQuery;
|
||||
public boolean isDenyExternalEndpointQuery() {
|
||||
return denyExternalEndpointQuery;
|
||||
}
|
||||
|
||||
|
||||
public void setDenyLineTableQuery(boolean denyLineTableQuery) {
|
||||
this.denyLineTableQuery = denyLineTableQuery;
|
||||
public void setDenyExternalEndpointQuery(boolean denyExternalEndpointQuery) {
|
||||
this.denyExternalEndpointQuery = denyExternalEndpointQuery;
|
||||
}
|
||||
|
||||
|
||||
public boolean isDenyLineTableBroadcast() {
|
||||
return denyLineTableBroadcast;
|
||||
public boolean isDenyExternalEndpointBroadcast() {
|
||||
return denyExternalEndpointBroadcast;
|
||||
}
|
||||
|
||||
|
||||
public void setDenyLineTableBroadcast(boolean denyLineTableBroadcast) {
|
||||
this.denyLineTableBroadcast = denyLineTableBroadcast;
|
||||
public void setDenyExternalEndpointBroadcast(boolean denyExternalEndpointBroadcast) {
|
||||
this.denyExternalEndpointBroadcast = denyExternalEndpointBroadcast;
|
||||
}
|
||||
|
||||
|
||||
@@ -352,12 +381,12 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
|
||||
", TCPListen=" + TCPListen +
|
||||
", UDPListen=" + UDPListen +
|
||||
", VirtualSocketName='" + VirtualSocketName + '\'' +
|
||||
", LineTable=" + LineTable +
|
||||
", ConnectLineTable=" + ConnectLineTable +
|
||||
", ntpServerTable=" + ntpServerTable +
|
||||
", externalEndpoints=" + externalEndpoints +
|
||||
", autoConnections=" + autoConnections +
|
||||
", ntpServers=" + ntpServers +
|
||||
", ExtraRoutes=" + ExtraRoutes +
|
||||
", denyLineTableQuery=" + denyLineTableQuery +
|
||||
", denyLineTableBroadcast=" + denyLineTableBroadcast +
|
||||
", denyExternalEndpointQuery=" + denyExternalEndpointQuery +
|
||||
", denyExternalEndpointBroadcast=" + denyExternalEndpointBroadcast +
|
||||
", congestionAlgorithm='" + congestionAlgorithm + '\'' +
|
||||
", burstLimit=" + burstLimit +
|
||||
", delayUpperBound=" + delayUpperBound +
|
||||
@@ -365,11 +394,14 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
|
||||
", nagleDelayTime=" + nagleDelayTime +
|
||||
", linkNagleDelayTime=" + linkNagleDelayTime +
|
||||
", linkConnectionsCount=" + linkConnectionsCount +
|
||||
", enableTUN=" + enableTUN +
|
||||
", TUNName='" + TUNName + '\'' +
|
||||
", performanceStrategy='" + performanceStrategy + '\'' +
|
||||
", DeviceName='" + DeviceName + '\'' +
|
||||
", DeviceDescription='" + DeviceDescription + '\'' +
|
||||
", NetworkInterfaceExcepts=" + NetworkInterfaceExcepts +
|
||||
", webUI=" + webUI +
|
||||
", webListen=" + webListen +
|
||||
'}';
|
||||
}
|
||||
|
||||
@@ -378,11 +410,11 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
if (!super.equals(o)) return false;
|
||||
KLALBControllerConfigItem that = (KLALBControllerConfigItem) o;
|
||||
return nogui == that.nogui && denyLineTableQuery == that.denyLineTableQuery && denyLineTableBroadcast == that.denyLineTableBroadcast && Double.compare(burstLimit, that.burstLimit) == 0 && Double.compare(delayUpperBound, that.delayUpperBound) == 0 && Double.compare(delayLowerBound, that.delayLowerBound) == 0 && nagleDelayTime == that.nagleDelayTime && linkNagleDelayTime == that.linkNagleDelayTime && linkConnectionsCount == that.linkConnectionsCount && Objects.equals(language, that.language) && Objects.equals(VirtualAddress, that.VirtualAddress) && Objects.equals(VirtualASN, that.VirtualASN) && Objects.equals(DNS, that.DNS) && Objects.equals(TCPListen, that.TCPListen) && Objects.equals(UDPListen, that.UDPListen) && Objects.equals(VirtualSocketName, that.VirtualSocketName) && Objects.equals(LineTable, that.LineTable) && Objects.equals(ConnectLineTable, that.ConnectLineTable) && Objects.equals(ntpServerTable, that.ntpServerTable) && Objects.equals(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 && webUI == that.webUI && 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(webListen, that.webListen) && 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, 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, externalEndpoints, autoConnections, ntpServers, ExtraRoutes, denyExternalEndpointQuery, denyExternalEndpointBroadcast, congestionAlgorithm, burstLimit, delayUpperBound, delayLowerBound, nagleDelayTime, linkNagleDelayTime, linkConnectionsCount, enableTUN, TUNName, performanceStrategy, DeviceName, DeviceDescription, NetworkInterfaceExcepts, webUI, webListen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,13 +52,15 @@ public class KLALBMain {
|
||||
}catch(Throwable e) {
|
||||
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.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);
|
||||
@@ -84,6 +86,7 @@ public class KLALBMain {
|
||||
case "help":
|
||||
System.out.println(" help / ?: see help");
|
||||
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-add <addr:port>: add link");
|
||||
System.out.println(" links-remove <addr:port>: remove link");
|
||||
@@ -102,6 +105,42 @@ public class KLALBMain {
|
||||
e.printStackTrace();
|
||||
}
|
||||
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().getWebListen() != null) {
|
||||
p = kpcje.getControllerConfig().getWebListen().getPort();
|
||||
}
|
||||
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":
|
||||
System.out.println("links state:");
|
||||
//System.out.println("状态\t上传流量\t下载流量\t上传速度\t下载速度\t上传延迟\t下载延迟\t上传抖动\t下载抖动");
|
||||
|
||||
@@ -5,6 +5,8 @@ import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.HashSet;
|
||||
|
||||
@@ -12,18 +14,25 @@ import org.kne.cloud.network.*;
|
||||
import org.kne.cloud.network.klalb.ui.KLALBStateGUI3;
|
||||
import org.kne.cloud.network.klalb.ui.Language;
|
||||
import org.kne.cloud.network.klalb.ui.UIEnv;
|
||||
import org.kne.cloud.network.klalb.web.KLALBWebServer;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
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;
|
||||
|
||||
public class KLALBProxySystem {
|
||||
private Set<Proxy> proxys=new HashSet<>();
|
||||
private KLALBController klalbController;
|
||||
private KLALBRemoteManagement krm;
|
||||
private KLALBWebServer webServer;
|
||||
private KLALBConfig config;
|
||||
private Gson gson;
|
||||
private File jsonFile;
|
||||
@@ -31,8 +40,28 @@ public class KLALBProxySystem {
|
||||
GsonBuilder gb=new GsonBuilder().setPrettyPrinting();
|
||||
MultiProtocolSocketAddress.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();
|
||||
}
|
||||
public Gson getGson() {
|
||||
return gson;
|
||||
}
|
||||
|
||||
public Set<Proxy> getProxys() {
|
||||
return proxys;
|
||||
}
|
||||
@@ -55,30 +84,38 @@ 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 enableWebServer() throws IOException {
|
||||
KLALBControllerConfigItem cci = getControllerConfig();
|
||||
MultiProtocolSocketAddress listen = cci == null || cci.getWebListen() == null
|
||||
? new MultiProtocolSocketAddress("http", "0.0.0.0", 4665) : cci.getWebListen();
|
||||
enableWebServer(listen);
|
||||
}
|
||||
|
||||
public void enableWebServer(int port) throws IOException {
|
||||
enableWebServer(new MultiProtocolSocketAddress("http", "0.0.0.0", port));
|
||||
}
|
||||
|
||||
public void enableWebServer(MultiProtocolSocketAddress listen) throws IOException {
|
||||
if (webServer == null) {
|
||||
webServer = new KLALBWebServer(this, listen);
|
||||
webServer.start();
|
||||
} else {
|
||||
throw new IllegalStateException("Web server 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 isWebServerEnabled() {
|
||||
return webServer != null && webServer.isRunning();
|
||||
}
|
||||
|
||||
public boolean isRemoteManagementEnabled() {
|
||||
return krm!=null;
|
||||
public KLALBWebServer getWebServer() {
|
||||
return webServer;
|
||||
}
|
||||
|
||||
public void disableRemoteManagement() {
|
||||
if(krm!=null) {
|
||||
krm.close();
|
||||
krm=null;
|
||||
public void disableWebServer() {
|
||||
if (webServer != null) {
|
||||
webServer.stop();
|
||||
webServer = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,30 +232,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;
|
||||
public KLALBStateGUI3 getKLALBGUI() {
|
||||
if(kgui==null) {
|
||||
kgui=new KLALBStateGUI3(klalbController);
|
||||
kgui.loadConfig(config);
|
||||
kgui.setSaveComsumer((cfg)->{
|
||||
String json=gson.toJson(cfg);
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
saveConfigToFile();
|
||||
});
|
||||
}
|
||||
return kgui;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
package org.kne.cloud.network.klalb;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
||||
import org.kne.cloud.network.srv6.ExternalEndpointsResult;
|
||||
import org.kne.cloud.network.srv6.ExtraRoutesResult;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
|
||||
import org.kne.cloud.network.srv6.NodeInfoQueryStatus;
|
||||
import org.kne.cloud.network.srv6.NodeProfile;
|
||||
|
||||
/** Coordinates the three independent sections of a remote node-info query. */
|
||||
public final class NodeInfoQueryCoordinator {
|
||||
private static final long DEADLINE_MILLIS = 3000L;
|
||||
private static final ExecutorService REQUEST_EXECUTOR = Executors.newVirtualThreadPerTaskExecutor();
|
||||
private static final ScheduledExecutorService DEADLINE_EXECUTOR = Executors
|
||||
.newSingleThreadScheduledExecutor(r -> {
|
||||
Thread thread = new Thread(r, "KLALB-node-info-deadline");
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
|
||||
private final KLALBRoutingProtocolAPIClient client;
|
||||
|
||||
public NodeInfoQueryCoordinator(KLALBRoutingProtocolAPIClient client) {
|
||||
this.client = Objects.requireNonNull(client, "client");
|
||||
}
|
||||
|
||||
/** Starts all section requests and completes at the first all-sections response or the shared deadline. */
|
||||
public CompletableFuture<Result> query(SocketAddress address) {
|
||||
Objects.requireNonNull(address, "address");
|
||||
Pending pending = new Pending();
|
||||
pending.result.whenComplete((ignored, error) -> {
|
||||
if (pending.result.isCancelled()) pending.cancel();
|
||||
});
|
||||
pending.deadline = DEADLINE_EXECUTOR.schedule(pending::timeout, DEADLINE_MILLIS, TimeUnit.MILLISECONDS);
|
||||
|
||||
issue(pending, () -> client.requestNodeProfile(address, pending::profile), pending::profileFailed);
|
||||
issue(pending, () -> client.requestExternalEndpoints(address, pending::externalEndpoints),
|
||||
pending::externalEndpointsFailed);
|
||||
issue(pending, () -> client.requestExtraRoutes(address, pending::extraRoutes), pending::extraRoutesFailed);
|
||||
return pending.result;
|
||||
}
|
||||
|
||||
private void issue(Pending pending, ThrowingRequest request, Runnable failure) {
|
||||
REQUEST_EXECUTOR.execute(() -> pending.issue(request, failure));
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface ThrowingRequest {
|
||||
void run() throws Exception;
|
||||
}
|
||||
|
||||
public static final class Section<T> {
|
||||
private final boolean received;
|
||||
private final Optional<T> value;
|
||||
private final Optional<NodeInfoQueryStatus> status;
|
||||
|
||||
private Section(boolean received, T value, NodeInfoQueryStatus status) {
|
||||
this.received = received;
|
||||
this.value = Optional.ofNullable(value);
|
||||
this.status = Optional.ofNullable(status);
|
||||
}
|
||||
|
||||
public boolean isReceived() {
|
||||
return received;
|
||||
}
|
||||
|
||||
public Optional<T> getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public Optional<NodeInfoQueryStatus> getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
private static <T> Section<T> missing() {
|
||||
return new Section<T>(false, null, null);
|
||||
}
|
||||
|
||||
private static <T> Section<T> received(T value, NodeInfoQueryStatus status) {
|
||||
return new Section<T>(true, value, status);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Result {
|
||||
private final Section<NodeProfile> profile;
|
||||
private final Section<List<MultiProtocolSocketAddress>> externalEndpoints;
|
||||
private final Section<List<String>> extraRoutes;
|
||||
|
||||
private Result(Section<NodeProfile> profile,
|
||||
Section<List<MultiProtocolSocketAddress>> externalEndpoints,
|
||||
Section<List<String>> extraRoutes) {
|
||||
this.profile = profile;
|
||||
this.externalEndpoints = externalEndpoints;
|
||||
this.extraRoutes = extraRoutes;
|
||||
}
|
||||
|
||||
public Section<NodeProfile> getProfile() {
|
||||
return profile;
|
||||
}
|
||||
|
||||
public Section<List<MultiProtocolSocketAddress>> getExternalEndpoints() {
|
||||
return externalEndpoints;
|
||||
}
|
||||
|
||||
public Section<List<String>> getExtraRoutes() {
|
||||
return extraRoutes;
|
||||
}
|
||||
}
|
||||
|
||||
private final class Pending {
|
||||
private final CompletableFuture<Result> result = new CompletableFuture<>();
|
||||
private final AtomicBoolean finished = new AtomicBoolean();
|
||||
private int remaining = 3;
|
||||
private Section<NodeProfile> profile = Section.missing();
|
||||
private Section<List<MultiProtocolSocketAddress>> externalEndpoints = Section.missing();
|
||||
private Section<List<String>> extraRoutes = Section.missing();
|
||||
private java.util.concurrent.ScheduledFuture<?> deadline;
|
||||
|
||||
private synchronized void profile(NodeProfile value) {
|
||||
if (profile.isReceived()) return;
|
||||
profile = Section.received(value, NodeInfoQueryStatus.OK);
|
||||
completeSection();
|
||||
}
|
||||
|
||||
private synchronized void profileFailed() {
|
||||
if (profile.isReceived()) return;
|
||||
profile = Section.missing();
|
||||
completeSection();
|
||||
}
|
||||
|
||||
private synchronized void externalEndpoints(ExternalEndpointsResult value) {
|
||||
if (externalEndpoints.isReceived()) return;
|
||||
if (value == null) {
|
||||
externalEndpoints = Section.missing();
|
||||
} else {
|
||||
List<MultiProtocolSocketAddress> endpoints = value.getExternalEndpoints();
|
||||
externalEndpoints = Section.received(endpoints == null
|
||||
? Collections.<MultiProtocolSocketAddress>emptyList()
|
||||
: Collections.unmodifiableList(new ArrayList<MultiProtocolSocketAddress>(endpoints)),
|
||||
value.getStatus());
|
||||
}
|
||||
completeSection();
|
||||
}
|
||||
|
||||
private synchronized void externalEndpointsFailed() {
|
||||
if (externalEndpoints.isReceived()) return;
|
||||
externalEndpoints = Section.missing();
|
||||
completeSection();
|
||||
}
|
||||
|
||||
private synchronized void extraRoutes(ExtraRoutesResult value) {
|
||||
if (extraRoutes.isReceived()) return;
|
||||
if (value == null) {
|
||||
extraRoutes = Section.missing();
|
||||
} else {
|
||||
List<String> routes = value.getExtraRoutes();
|
||||
extraRoutes = Section.received(routes == null
|
||||
? Collections.<String>emptyList()
|
||||
: Collections.unmodifiableList(new ArrayList<String>(routes)), value.getStatus());
|
||||
}
|
||||
completeSection();
|
||||
}
|
||||
|
||||
private synchronized void extraRoutesFailed() {
|
||||
if (extraRoutes.isReceived()) return;
|
||||
extraRoutes = Section.missing();
|
||||
completeSection();
|
||||
}
|
||||
|
||||
private synchronized void issue(ThrowingRequest request, Runnable failure) {
|
||||
if (finished.get() || result.isDone() || result.isCancelled()) return;
|
||||
try {
|
||||
request.run();
|
||||
} catch (Exception e) {
|
||||
failure.run();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void cancel() {
|
||||
finish();
|
||||
}
|
||||
|
||||
private void completeSection() {
|
||||
remaining--;
|
||||
if (remaining == 0) finish();
|
||||
}
|
||||
|
||||
private synchronized void timeout() {
|
||||
finish();
|
||||
}
|
||||
|
||||
private void finish() {
|
||||
if (!finished.compareAndSet(false, true)) return;
|
||||
if (deadline != null) deadline.cancel(false);
|
||||
result.complete(new Result(profile, externalEndpoints, extraRoutes));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.function.Consumer;
|
||||
@@ -75,7 +76,9 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
private JTextField addressField; // 地址显示框
|
||||
private JTextField addressFieldSet; // 地址设置框
|
||||
private JTextField asnFieldSet; // ASN设置框
|
||||
private JCheckBox enableTUN; // 虚拟网卡启用禁用
|
||||
private JTextField tunDeviceName; // 虚拟网卡名称设置框
|
||||
private JTextField webListenSet; // Web API 监听地址设置框
|
||||
private NetworkGraphPanel graph; // 网络图面板
|
||||
private JTextField textFieldLocate; // 定位地址输入框
|
||||
private JTextField asnField2; // ASN显示框
|
||||
@@ -96,6 +99,7 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
private JTextArea ntpServerSet; // NTP服务器设置区域
|
||||
private JCheckBox denyBroadcast;
|
||||
private JCheckBox denyQuery;
|
||||
private JCheckBox webApiEnabled; // 启用 Web API 开关
|
||||
private ClosableTabbedPane tabbedPane; // 可关闭的标签页面板
|
||||
private JCheckBox nogui;
|
||||
|
||||
@@ -820,6 +824,12 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
asnFieldSet = asn.getTextField();
|
||||
settings.getView().add(asn);
|
||||
|
||||
//虚拟网卡
|
||||
CheckBoxSettingItem enableTUNs = new CheckBoxSettingItem(UIEnv.getRsb().getString("enabletun"),
|
||||
CONST.itemwidth, CONST.settingheight);
|
||||
enableTUN=enableTUNs.getCheckBox();
|
||||
settings.getView().add(enableTUNs);
|
||||
|
||||
TextSettingItem tunName = new TextSettingItem(UIEnv.getRsb().getString("tunname"),
|
||||
CONST.itemwidth, CONST.settingheight);
|
||||
tunDeviceName = tunName.getTextField();
|
||||
@@ -905,6 +915,21 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
denyBroadcast=denyBroadcastc.getCheckBox();
|
||||
settings.getView().add(denyBroadcastc);
|
||||
|
||||
// Web服务设置标题
|
||||
SettingItem wsi = new SettingItem(UIEnv.getRsb().getString("webapisettings"),
|
||||
UIEnv.getFont().deriveFont(20.0f).deriveFont(Font.BOLD), CONST.itemwidth, CONST.settingheight);
|
||||
settings.getView().add(wsi);
|
||||
|
||||
CheckBoxSettingItem webApic = new CheckBoxSettingItem(UIEnv.getRsb().getString("enablewebapi"),
|
||||
CONST.itemwidth, CONST.settingheight);
|
||||
webApiEnabled=webApic.getCheckBox();
|
||||
settings.getView().add(webApic);
|
||||
|
||||
TextSettingItem webListen = new TextSettingItem(UIEnv.getRsb().getString("weblistenaddr"),
|
||||
CONST.itemwidth, CONST.settingheight);
|
||||
webListenSet = webListen.getTextField();
|
||||
settings.getView().add(webListen);
|
||||
|
||||
//性能设置标题
|
||||
|
||||
SettingItem pshi = new SettingItem(UIEnv.getRsb().getString("performancesettings"),
|
||||
@@ -1039,6 +1064,17 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
for (KLALBConfigItem item : config) {
|
||||
if (item instanceof KLALBControllerConfigItem) {
|
||||
KLALBControllerConfigItem kck = (KLALBControllerConfigItem) item;
|
||||
String oldDeviceName = kck.getDeviceName();
|
||||
String oldDeviceDescription = kck.getDeviceDescription();
|
||||
List<MultiProtocolSocketAddress> oldExternalEndpoints = kck.getExternalEndpoints() == null
|
||||
? null
|
||||
: new ArrayList<MultiProtocolSocketAddress>(kck.getExternalEndpoints());
|
||||
List<String> oldExtraRoutes = kck.getExtraRoutes() == null
|
||||
? null : new ArrayList<String>(kck.getExtraRoutes());
|
||||
String newDeviceName;
|
||||
String newDeviceDescription;
|
||||
List<String> newExtraRoutes;
|
||||
List<MultiProtocolSocketAddress> newExternalEndpoints;
|
||||
|
||||
// 保存语言设置
|
||||
kck.setLanguage(((Language) comboLang.getSelectedItem()).name());
|
||||
@@ -1048,20 +1084,16 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
// 保存设备名称
|
||||
String dnametext = deviceNameSet.getText().trim();
|
||||
if (dnametext.equals("")) {
|
||||
kck.setDeviceName(null);
|
||||
newDeviceName = null;
|
||||
} else {
|
||||
kck.setDeviceName(dnametext);
|
||||
newDeviceName = dnametext;
|
||||
}
|
||||
if (kcontroller != null && kcontroller.getIpv6Router() != null) {
|
||||
kcontroller.getIpv6Router().setDeviceName(kck.getDeviceName());
|
||||
}
|
||||
|
||||
// 保存设备描述
|
||||
String ddesctext = deviceDescriptionSet.getText().trim();
|
||||
if (ddesctext.equals("")) {
|
||||
kck.setDeviceDescription(null);
|
||||
newDeviceDescription = null;
|
||||
} else {
|
||||
kck.setDeviceDescription(ddesctext);
|
||||
newDeviceDescription = ddesctext;
|
||||
}
|
||||
|
||||
|
||||
@@ -1117,7 +1149,7 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
}
|
||||
}
|
||||
}
|
||||
kck.setExtraRoutes(eroutes);
|
||||
newExtraRoutes = eroutes;
|
||||
|
||||
// 保存ASN
|
||||
String asntext = asnFieldSet.getText();
|
||||
@@ -1134,6 +1166,8 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
}
|
||||
}
|
||||
|
||||
kck.setEnableTUN( enableTUN.isSelected());
|
||||
|
||||
String tunNameText=tunDeviceName.getText().trim();
|
||||
if(tunNameText.equals("") || tunNameText.equalsIgnoreCase("null")) {
|
||||
kck.setTUNName(null);
|
||||
@@ -1141,6 +1175,22 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
kck.setTUNName(tunNameText);
|
||||
}
|
||||
|
||||
// 保存Web API设置
|
||||
kck.setWebUI(webApiEnabled.isSelected());
|
||||
String webListenText = webListenSet.getText().trim();
|
||||
if (webListenText.equals("")) {
|
||||
kck.setWebListen(new MultiProtocolSocketAddress("http", "0.0.0.0", 4665));
|
||||
} else {
|
||||
try {
|
||||
kck.setWebListen(new MultiProtocolSocketAddress(webListenText));
|
||||
} catch (RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
JOptionPane.showMessageDialog(this, UIEnv.getRsb().getString("invaildweblistenaddr"),
|
||||
UIEnv.getRsb().getString("warning"), JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 保存TCP监听设置
|
||||
String tcptext = tcpListeningSet.getText();
|
||||
if (tcptext.equals("")) {
|
||||
@@ -1190,7 +1240,7 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
}
|
||||
}
|
||||
}
|
||||
kck.setLineTable(iaddr1);
|
||||
newExternalEndpoints = iaddr1;
|
||||
|
||||
// 保存自动连接线路表
|
||||
String[] splt11 = connectLineTabelSet.getText().split("\n");
|
||||
@@ -1243,9 +1293,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) {
|
||||
@@ -1263,6 +1313,22 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
kck.setNagleDelayTime(nagleDelayTime.getSlider().getValue()*100000L);
|
||||
|
||||
kck.setLinkNagleDelayTime(linkNagleDelayTime.getSlider().getValue()*100000L);
|
||||
|
||||
boolean deviceNameChanged = !Objects.equals(oldDeviceName, newDeviceName);
|
||||
boolean deviceDescriptionChanged = !Objects.equals(oldDeviceDescription, newDeviceDescription);
|
||||
boolean externalEndpointsChanged = !Objects.equals(oldExternalEndpoints, newExternalEndpoints);
|
||||
boolean extraRoutesChanged = !Objects.equals(oldExtraRoutes, newExtraRoutes);
|
||||
if (deviceNameChanged || deviceDescriptionChanged || externalEndpointsChanged || extraRoutesChanged) {
|
||||
if (kcontroller != null) {
|
||||
kcontroller.publishNodeInfo(newDeviceName, newDeviceDescription, newExternalEndpoints,
|
||||
newExtraRoutes);
|
||||
} else {
|
||||
kck.setDeviceName(newDeviceName);
|
||||
kck.setDeviceDescription(newDeviceDescription);
|
||||
kck.setExternalEndpoints(newExternalEndpoints);
|
||||
kck.setExtraRoutes(newExtraRoutes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1364,17 +1430,19 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
tsk5 = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isVisible()) {
|
||||
graph.loadNodes();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
graph.runPhy();
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (isVisible()) {
|
||||
graph.loadNodes();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
graph.runPhy();
|
||||
}
|
||||
graph.repaint();
|
||||
graph.revalidate();
|
||||
}
|
||||
graph.repaint();
|
||||
graph.revalidate();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
t.scheduleAtFixedRate(tsk5, 1000, 1000);
|
||||
t.scheduleAtFixedRate(tsk5, 0, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1538,9 +1606,19 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
Long vasn = kck.getVirtualASN();
|
||||
asnFieldSet.setText(vasn != null ? vasn.toString() : "");
|
||||
|
||||
enableTUN.setSelected(kck.isEnableTUN());
|
||||
|
||||
|
||||
String tunname=kck.getTUNName();
|
||||
tunDeviceName.setText(tunname!=null?tunname:"");
|
||||
|
||||
|
||||
|
||||
// 加载Web API设置
|
||||
webApiEnabled.setSelected(kck.isWebUI());
|
||||
webListenSet.setText(kck.getWebListen() != null ? kck.getWebListen().toString()
|
||||
: "http://0.0.0.0:4665");
|
||||
|
||||
// 加载TCP监听
|
||||
MultiProtocolSocketAddress mpat = kck.getTCPListen();
|
||||
tcpListeningSet.setText(mpat != null ? mpat.toString() : "");
|
||||
@@ -1550,7 +1628,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 +1657,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);
|
||||
@@ -1710,6 +1788,15 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
* 关闭窗口并清理资源
|
||||
*/
|
||||
public void close() {
|
||||
for (int i = 0; i < tabbedPane.getTabCount(); i++) {
|
||||
Component component = tabbedPane.getComponentAt(i);
|
||||
if (component instanceof NodeInformationPanel) {
|
||||
((NodeInformationPanel) component).close();
|
||||
}
|
||||
}
|
||||
if (graph != null) {
|
||||
graph.close();
|
||||
}
|
||||
setVisible(false);
|
||||
if (tsk != null) {
|
||||
tsk.cancel();
|
||||
|
||||
@@ -8,28 +8,58 @@ import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
||||
import org.kne.cloud.network.ipv6.IPv6Address;
|
||||
import org.kne.cloud.network.klalb.KLALBController;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocol;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocol.LinkDirection;
|
||||
import org.kne.cloud.network.srv6.NodeProfile;
|
||||
import org.kne.cloud.network.srv6.RouterInfoPacket;
|
||||
|
||||
public class NetworkGraphPanel extends GraphPanel {
|
||||
private KLALBController controller;
|
||||
private KLALBStateGUI3 klbgui;
|
||||
private final KLALBRoutingProtocolAPIClient nodeInfoClient;
|
||||
private final KLALBRoutingProtocol nodeInfoRoutingProtocol;
|
||||
private final BiConsumer<IPv6Address, Integer> nodeInfoUpdateListener;
|
||||
private final Object nodeInfoLock = new Object();
|
||||
private final Map<IPv6Address, String> nodeNameCache = new HashMap<IPv6Address, String>();
|
||||
private final Map<IPv6Address, Long> nodeNameCacheTimes = new HashMap<IPv6Address, Long>();
|
||||
private final Map<IPv6Address, Long> nodeInfoRequestTimes = new HashMap<IPv6Address, Long>();
|
||||
private final Map<IPv6Address, Long> nodeInfoRequestGenerations = new HashMap<IPv6Address, Long>();
|
||||
private static final long NODE_INFO_REQUEST_TIMEOUT = 3000L;
|
||||
private static final long NODE_INFO_CACHE_TTL = 60000L;
|
||||
private long nextNodeInfoRequestGeneration;
|
||||
private volatile boolean nodeInfoClosed;
|
||||
|
||||
public NetworkGraphPanel(KLALBController controller,KLALBStateGUI3 klbgui) {
|
||||
super();
|
||||
this.controller = controller;
|
||||
this.klbgui=klbgui;
|
||||
this.nodeInfoRoutingProtocol = controller.getIpv6Router().getKlalbRouteProtol();
|
||||
this.nodeInfoClient = new KLALBRoutingProtocolAPIClient(nodeInfoRoutingProtocol);
|
||||
this.nodeInfoUpdateListener = (address, flags) -> {
|
||||
if ((flags & RouterInfoPacket.NODE_INFO_TINY_UPDATE_REQUIRED) != 0) {
|
||||
SwingUtilities.invokeLater(() -> invalidateTinyNodeInfo(address));
|
||||
}
|
||||
};
|
||||
nodeInfoRoutingProtocol.addNodeInfoUpdateListener(nodeInfoUpdateListener);
|
||||
cacheLocalNodeName();
|
||||
}
|
||||
|
||||
public NetworkGraphPanel(KLALBController kc) {
|
||||
@@ -153,18 +183,146 @@ public class NetworkGraphPanel extends GraphPanel {
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点标签:显示广播得知的设备名称(若有)+IP地址
|
||||
* 节点标签:显示 Tiny API 查询的设备名称(若有)+IP地址
|
||||
*/
|
||||
private String getNodeText(IPv6Address address) {
|
||||
StringBuilder sb=new StringBuilder();
|
||||
String dname=controller.getIpv6Router().getKlalbRouteProtol().getDeviceName(address);
|
||||
if(dname!=null)
|
||||
String dname;
|
||||
synchronized (nodeInfoLock) {
|
||||
dname = nodeNameCache.get(address);
|
||||
}
|
||||
if(dname!=null&&!dname.isEmpty())
|
||||
sb.append(dname).append('\n');
|
||||
sb.append(InetGraphNode.getText(address));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private double nsPerPixel=1000L;
|
||||
private void cacheLocalNodeName() {
|
||||
IPv6Address localAddress = controller.getIpv6Router().getLocator().getAddress();
|
||||
synchronized (nodeInfoLock) {
|
||||
nodeNameCache.put(localAddress, controller.getIpv6Router().getDeviceName());
|
||||
nodeNameCacheTimes.put(localAddress, System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
|
||||
private void requestNodeProfile(final IPv6Address address) {
|
||||
final long requestTime = System.currentTimeMillis();
|
||||
final long requestGeneration;
|
||||
synchronized (nodeInfoLock) {
|
||||
if (nodeInfoClosed)
|
||||
return;
|
||||
Long cachedAt = nodeNameCacheTimes.get(address);
|
||||
if (nodeNameCache.containsKey(address) && cachedAt != null
|
||||
&& requestTime - cachedAt < NODE_INFO_CACHE_TTL)
|
||||
return;
|
||||
nodeNameCache.remove(address);
|
||||
nodeNameCacheTimes.remove(address);
|
||||
Long previousRequestTime = nodeInfoRequestTimes.get(address);
|
||||
if (previousRequestTime != null && requestTime - previousRequestTime < NODE_INFO_REQUEST_TIMEOUT)
|
||||
return;
|
||||
nodeInfoRequestTimes.put(address, requestTime);
|
||||
requestGeneration = ++nextNodeInfoRequestGeneration;
|
||||
nodeInfoRequestGenerations.put(address, requestGeneration);
|
||||
}
|
||||
try {
|
||||
nodeInfoClient.requestNodeProfile(
|
||||
new InetSocketAddress(address.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT),
|
||||
(profile) -> SwingUtilities.invokeLater(() -> handleNodeProfile(address, requestGeneration, profile)));
|
||||
} catch (IOException e) {
|
||||
synchronized (nodeInfoLock) {
|
||||
if (nodeInfoRequestGenerations.get(address) != null
|
||||
&& nodeInfoRequestGenerations.get(address).longValue() == requestGeneration) {
|
||||
nodeInfoRequestTimes.remove(address);
|
||||
nodeInfoRequestGenerations.remove(address);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleNodeProfile(IPv6Address address, long requestGeneration, NodeProfile profile) {
|
||||
if (nodeInfoClosed)
|
||||
return;
|
||||
boolean currentRequest;
|
||||
boolean nodeExists = getNodes().containsKey(address);
|
||||
String deviceName = profile == null ? "" : profile.getDeviceName();
|
||||
if (deviceName == null || deviceName.isEmpty())
|
||||
deviceName = "";
|
||||
synchronized (nodeInfoLock) {
|
||||
if (nodeInfoClosed)
|
||||
return;
|
||||
Long activeRequestGeneration = nodeInfoRequestGenerations.get(address);
|
||||
currentRequest = activeRequestGeneration != null
|
||||
&& activeRequestGeneration.longValue() == requestGeneration;
|
||||
if (currentRequest) {
|
||||
nodeInfoRequestTimes.remove(address);
|
||||
nodeInfoRequestGenerations.remove(address);
|
||||
if (nodeExists)
|
||||
nodeNameCache.put(address, deviceName);
|
||||
else
|
||||
nodeNameCache.remove(address);
|
||||
if (nodeExists)
|
||||
nodeNameCacheTimes.put(address, System.currentTimeMillis());
|
||||
else
|
||||
nodeNameCacheTimes.remove(address);
|
||||
}
|
||||
}
|
||||
if (currentRequest && nodeExists) {
|
||||
((InetGraphNode) getNodes().get(address)).updateLabel();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private void removeNodeInfoState(IPv6Address address) {
|
||||
synchronized (nodeInfoLock) {
|
||||
nodeNameCache.remove(address);
|
||||
nodeNameCacheTimes.remove(address);
|
||||
nodeInfoRequestTimes.remove(address);
|
||||
nodeInfoRequestGenerations.remove(address);
|
||||
}
|
||||
}
|
||||
|
||||
private void invalidateTinyNodeInfo(IPv6Address address) {
|
||||
synchronized (nodeInfoLock) {
|
||||
if (nodeInfoClosed)
|
||||
return;
|
||||
nodeNameCache.remove(address);
|
||||
nodeNameCacheTimes.remove(address);
|
||||
nodeInfoRequestTimes.remove(address);
|
||||
nodeInfoRequestGenerations.remove(address);
|
||||
}
|
||||
InetGraphNode node = (InetGraphNode) getNodes().get(address);
|
||||
if (node != null) {
|
||||
node.updateLabel();
|
||||
}
|
||||
repaint();
|
||||
requestNodeProfile(address);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
synchronized (nodeInfoLock) {
|
||||
if (nodeInfoClosed)
|
||||
return;
|
||||
nodeInfoClosed = true;
|
||||
nodeNameCache.clear();
|
||||
nodeNameCacheTimes.clear();
|
||||
nodeInfoRequestTimes.clear();
|
||||
nodeInfoRequestGenerations.clear();
|
||||
}
|
||||
nodeInfoRoutingProtocol.removeNodeInfoUpdateListener(nodeInfoUpdateListener);
|
||||
nodeInfoClient.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNotify() {
|
||||
super.removeNotify();
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (!isDisplayable() && getParent() == null) {
|
||||
close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private double nsPerPixel=5000L;
|
||||
private class InetGraphEdgeGroup extends GraphEdgeGroup{
|
||||
public InetGraphEdgeGroup(GraphNode nodeA, GraphNode nodeB) {
|
||||
super(nodeA, nodeB);
|
||||
@@ -181,6 +339,8 @@ public class NetworkGraphPanel extends GraphPanel {
|
||||
}
|
||||
protected void loadNodes() {
|
||||
Map<IPv6Address, Long> addr= controller.getIpv6Router().getKlalbRouteProtol().getAddresses();
|
||||
IPv6Address localAddress = controller.getIpv6Router().getLocator().getAddress();
|
||||
cacheLocalNodeName();
|
||||
Set<IPv6Address> ks=addr.keySet();
|
||||
for (Iterator<IPv6Address> iterator = ks.iterator(); iterator.hasNext();) {
|
||||
IPv6Address inet6Address = (IPv6Address) iterator.next();
|
||||
@@ -188,12 +348,15 @@ public class NetworkGraphPanel extends GraphPanel {
|
||||
Vector2 v2pos=super.getRandomPos();
|
||||
getNodes().put(inet6Address,new InetGraphNode(inet6Address,Color.BLACK,v2pos.x,v2pos.y,inet6Address.equals(controller.getIpv6Router().getLocator().getAddress())));
|
||||
}
|
||||
if(!inet6Address.equals(localAddress))
|
||||
requestNodeProfile(inet6Address);
|
||||
}
|
||||
Set<IPv6Address> kns=getNodes().keySet();
|
||||
for (Iterator<IPv6Address> iterator = kns.iterator(); iterator.hasNext();) {
|
||||
IPv6Address inet6Address = (IPv6Address) iterator.next();
|
||||
if(!addr.containsKey(inet6Address)) {
|
||||
iterator.remove();
|
||||
removeNodeInfoState(inet6Address);
|
||||
}else {
|
||||
((InetGraphNode)getNodes().get(inet6Address)).updateLabel();
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ import java.awt.BorderLayout;
|
||||
import java.awt.Image;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.Icon;
|
||||
@@ -24,8 +24,11 @@ import org.kne.cloud.klalb.uitool.XDefaultListModel;
|
||||
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
||||
import org.kne.cloud.network.ipv6.IPv6Address;
|
||||
import org.kne.cloud.network.klalb.KLALBController;
|
||||
import org.kne.cloud.network.klalb.NodeInfoQueryCoordinator;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocol;
|
||||
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
|
||||
import org.kne.cloud.network.srv6.NodeInfoQueryStatus;
|
||||
import org.kne.cloud.network.srv6.NodeProfile;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JMenuItem;
|
||||
@@ -34,14 +37,20 @@ import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class NodeInformationPanel extends JPanel {
|
||||
private IPv6Address address;
|
||||
private KLALBRoutingProtocolAPIClient client;
|
||||
private NodeInfoQueryCoordinator queryCoordinator;
|
||||
private KLALBController controller;
|
||||
private Image image;
|
||||
|
||||
private XDefaultListModel<MultiProtocolSocketAddress> listModel=new XDefaultListModel<>();
|
||||
private XDefaultListModel<String> extraRoutesModel=new XDefaultListModel<>();
|
||||
private volatile boolean active=true;
|
||||
private final AtomicLong fullInfoGeneration=new AtomicLong();
|
||||
private final java.util.function.BiConsumer<IPv6Address,Integer> nodeInfoUpdateListener;
|
||||
public KLALBController getController() {
|
||||
return controller;
|
||||
}
|
||||
@@ -73,21 +82,21 @@ 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');
|
||||
}
|
||||
if(address.equals(controller.getIpv6Router().getLocator().getAddress())&&controller.getConfigItem()!=null) {
|
||||
String ddesc=controller.getConfigItem().getDeviceDescription();
|
||||
if(ddesc!=null&&!ddesc.isEmpty()) {
|
||||
sb.append('\n').append(UIEnv.getRsb().getString("devicedescription")).append(":\n").append(ddesc).append('\n');
|
||||
}
|
||||
}
|
||||
overviewArea.setText(sb.toString());
|
||||
overviewArea.setText(buildOverviewText(null, null));
|
||||
panel.add(new JScrollPane(overviewArea), BorderLayout.CENTER);
|
||||
|
||||
JPanel extraRoutesPanel = new JPanel(new BorderLayout());
|
||||
JList<String> extraRoutesList = new JList<String>(extraRoutesModel);
|
||||
extraRoutesList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
extraRoutesList.setFont(UIEnv.getFont().deriveFont(14.0f));
|
||||
extraRoutesPanel.add(new JScrollPane(extraRoutesList), BorderLayout.CENTER);
|
||||
javax.swing.JLabel extraRoutesEmptyLabel=new javax.swing.JLabel(UIEnv.getRsb().getString("noextraroutes"));
|
||||
extraRoutesEmptyLabel.setText("...");
|
||||
extraRoutesEmptyLabel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
||||
extraRoutesEmptyLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
|
||||
extraRoutesPanel.add(extraRoutesEmptyLabel, BorderLayout.SOUTH);
|
||||
tabbedPane.addTab(UIEnv.getRsb().getString("extraroutes"), null, extraRoutesPanel, null);
|
||||
|
||||
JPanel panel_1 = new JPanel();
|
||||
panel_1.setLayout(new BorderLayout(0, 0));
|
||||
panel_1.add(scrollPane);
|
||||
@@ -167,17 +176,121 @@ public class NodeInformationPanel extends JPanel {
|
||||
|
||||
panel_1.add(btnNewButton, BorderLayout.SOUTH);
|
||||
|
||||
client=new KLALBRoutingProtocolAPIClient(controller.getIpv6Router().getKlalbRouteProtol());
|
||||
try {
|
||||
client.requestOpenLines(new InetSocketAddress( address.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT), (result)->{
|
||||
listModel.clear();
|
||||
for (MultiProtocolSocketAddress multiProtocolSocketAddress : result) {
|
||||
listModel.addElement(multiProtocolSocketAddress);
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
KLALBRoutingProtocol routingProtocol=controller.getIpv6Router().getKlalbRouteProtol();
|
||||
client=new KLALBRoutingProtocolAPIClient(routingProtocol);
|
||||
queryCoordinator=new NodeInfoQueryCoordinator(client);
|
||||
nodeInfoUpdateListener=(updatedAddress, flags) -> {
|
||||
if(active && address.equals(updatedAddress)
|
||||
&& (flags & org.kne.cloud.network.srv6.RouterInfoPacket.NODE_INFO_FULL_UPDATE_REQUIRED) != 0) {
|
||||
requestFullInfo(overviewArea, extraRoutesEmptyLabel);
|
||||
}
|
||||
};
|
||||
routingProtocol.addNodeInfoUpdateListener(nodeInfoUpdateListener);
|
||||
requestFullInfo(overviewArea, extraRoutesEmptyLabel);
|
||||
}
|
||||
|
||||
private void requestFullInfo(JTextArea overviewArea, javax.swing.JLabel extraRoutesEmptyLabel) {
|
||||
final long generation=fullInfoGeneration.incrementAndGet();
|
||||
if (controller.getIpv6Router().getLocator().getAddress().equals(address)) {
|
||||
KLALBController.PublishedNodeInfo published=controller.getPublishedNodeInfo();
|
||||
applyNodeInfo(overviewArea, extraRoutesEmptyLabel, published.getDeviceName(),
|
||||
published.getDeviceDescription(), published.getExternalEndpoints(), published.getExtraRoutes());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
queryCoordinator.query(new InetSocketAddress(address.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT)).whenComplete((info, error)->{
|
||||
if(!active || generation!=fullInfoGeneration.get()) return;
|
||||
javax.swing.SwingUtilities.invokeLater(() -> {
|
||||
if(!active || generation!=fullInfoGeneration.get()) return;
|
||||
if (error != null || info == null) {
|
||||
return;
|
||||
}
|
||||
applyRemoteNodeInfo(overviewArea, extraRoutesEmptyLabel, info);
|
||||
});
|
||||
});
|
||||
} catch (RuntimeException e) {
|
||||
if(active) e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void applyNodeInfo(JTextArea overviewArea, javax.swing.JLabel extraRoutesEmptyLabel,
|
||||
String deviceName, String deviceDescription, List<MultiProtocolSocketAddress> endpoints,
|
||||
List<String> routes) {
|
||||
applyProfile(overviewArea, deviceName, deviceDescription);
|
||||
applyEndpoints(endpoints == null ? Collections.<MultiProtocolSocketAddress>emptyList() : endpoints);
|
||||
applyRoutes(extraRoutesEmptyLabel, routes == null ? Collections.<String>emptyList() : routes);
|
||||
}
|
||||
|
||||
private void applyRemoteNodeInfo(JTextArea overviewArea, javax.swing.JLabel extraRoutesEmptyLabel,
|
||||
NodeInfoQueryCoordinator.Result info) {
|
||||
if (info.getProfile().isReceived()) {
|
||||
NodeProfile profile=info.getProfile().getValue().orElse(null);
|
||||
applyProfile(overviewArea, profile == null ? null : profile.getDeviceName(),
|
||||
profile == null ? null : profile.getDeviceDescription());
|
||||
}
|
||||
|
||||
if (info.getExternalEndpoints().isReceived()) {
|
||||
List<MultiProtocolSocketAddress> endpoints=info.getExternalEndpoints().getStatus()
|
||||
.filter(NodeInfoQueryStatus.OK::equals).isPresent()
|
||||
? info.getExternalEndpoints().getValue().orElse(Collections.<MultiProtocolSocketAddress>emptyList())
|
||||
: Collections.<MultiProtocolSocketAddress>emptyList();
|
||||
applyEndpoints(endpoints);
|
||||
}
|
||||
|
||||
if (info.getExtraRoutes().isReceived()) {
|
||||
List<String> routes=info.getExtraRoutes().getStatus().filter(NodeInfoQueryStatus.OK::equals).isPresent()
|
||||
? info.getExtraRoutes().getValue().orElse(Collections.<String>emptyList())
|
||||
: Collections.<String>emptyList();
|
||||
applyRoutes(extraRoutesEmptyLabel, routes);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyProfile(JTextArea overviewArea, String deviceName, String deviceDescription) {
|
||||
overviewArea.setText(buildOverviewText(deviceName,
|
||||
deviceDescription == null || deviceDescription.isEmpty() ? null : deviceDescription));
|
||||
}
|
||||
|
||||
private void applyEndpoints(List<MultiProtocolSocketAddress> endpoints) {
|
||||
listModel.clear();
|
||||
if (endpoints != null) for (MultiProtocolSocketAddress item : endpoints) listModel.addElement(item);
|
||||
}
|
||||
|
||||
private void applyRoutes(javax.swing.JLabel extraRoutesEmptyLabel, List<String> routes) {
|
||||
extraRoutesModel.clear();
|
||||
if (routes != null) for (String route : routes) extraRoutesModel.addElement(route);
|
||||
extraRoutesEmptyLabel.setText(UIEnv.getRsb().getString("noextraroutes"));
|
||||
extraRoutesEmptyLabel.setVisible(extraRoutesModel.isEmpty());
|
||||
}
|
||||
|
||||
public synchronized void close() {
|
||||
if (!active)
|
||||
return;
|
||||
active=false;
|
||||
fullInfoGeneration.incrementAndGet();
|
||||
KLALBRoutingProtocol routingProtocol=controller.getIpv6Router().getKlalbRouteProtol();
|
||||
routingProtocol.removeNodeInfoUpdateListener(nodeInfoUpdateListener);
|
||||
if(client!=null) {
|
||||
client.close();
|
||||
client=null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNotify() {
|
||||
close();
|
||||
super.removeNotify();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,49 @@
|
||||
package org.kne.cloud.network.srv6;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
||||
|
||||
public final class ExternalEndpointsResult {
|
||||
private final List<MultiProtocolSocketAddress> externalEndpoints;
|
||||
private final NodeInfoQueryStatus status;
|
||||
|
||||
public ExternalEndpointsResult(List<MultiProtocolSocketAddress> externalEndpoints, NodeInfoQueryStatus status) {
|
||||
this.externalEndpoints = Collections.unmodifiableList(new ArrayList<MultiProtocolSocketAddress>(
|
||||
externalEndpoints == null ? Collections.<MultiProtocolSocketAddress>emptyList() : externalEndpoints));
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public List<MultiProtocolSocketAddress> getExternalEndpoints() {
|
||||
return externalEndpoints;
|
||||
}
|
||||
|
||||
public NodeInfoQueryStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(externalEndpoints, status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof ExternalEndpointsResult)) {
|
||||
return false;
|
||||
}
|
||||
ExternalEndpointsResult other = (ExternalEndpointsResult) obj;
|
||||
return Objects.equals(externalEndpoints, other.externalEndpoints) && status == other.status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ExternalEndpointsResult [externalEndpoints=" + externalEndpoints + ", status=" + status + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.kne.cloud.network.srv6;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class ExtraRoutesResult {
|
||||
private final List<String> extraRoutes;
|
||||
private final NodeInfoQueryStatus status;
|
||||
|
||||
public ExtraRoutesResult(List<String> extraRoutes, NodeInfoQueryStatus status) {
|
||||
this.extraRoutes = Collections.unmodifiableList(new ArrayList<String>(
|
||||
extraRoutes == null ? Collections.<String>emptyList() : extraRoutes));
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public List<String> getExtraRoutes() {
|
||||
return extraRoutes;
|
||||
}
|
||||
|
||||
public NodeInfoQueryStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(extraRoutes, status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof ExtraRoutesResult)) {
|
||||
return false;
|
||||
}
|
||||
ExtraRoutesResult other = (ExtraRoutesResult) obj;
|
||||
return Objects.equals(extraRoutes, other.extraRoutes) && status == other.status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ExtraRoutesResult [extraRoutes=" + extraRoutes + ", status=" + status + "]";
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ public class JsonDataPacket extends KLALBRoutingProtocolPacket implements Serial
|
||||
private String data;
|
||||
|
||||
private static final int HEADER_LENGTH=3;
|
||||
private static final int MAX_JSON_PAYLOAD_LENGTH=65535-HEADER_LENGTH;
|
||||
|
||||
private static Gson gson;
|
||||
static{
|
||||
@@ -75,6 +76,9 @@ public class JsonDataPacket extends KLALBRoutingProtocolPacket implements Serial
|
||||
@Override
|
||||
public void writeToChannel(WritableByteChannel dto) throws IOException {
|
||||
byte[]bta=data.getBytes(Charset.forName("UTF-8"));
|
||||
if(bta.length>MAX_JSON_PAYLOAD_LENGTH) {
|
||||
throw new IOException("JSON payload exceeds " + MAX_JSON_PAYLOAD_LENGTH + " bytes");
|
||||
}
|
||||
getHeader().putChar(1,(char) bta.length);
|
||||
super.writeToChannel(dto);
|
||||
dto.write(ByteBuffer.wrap(bta));
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@@ -21,6 +22,8 @@ import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
@@ -43,6 +46,14 @@ public class KLALBRoutingProtocol extends Thread{
|
||||
|
||||
private RouterInfo selfRouterInfo;
|
||||
private Map<IPv6Address, RouterInfo> netmap=new ConcurrentHashMap<>();
|
||||
private final AtomicLong selfCreateTime = new AtomicLong();
|
||||
private final AtomicInteger pendingNodeInfoUpdateFlags = new AtomicInteger();
|
||||
private static final int NODE_INFO_UPDATE_REPLAY_WINDOW_SIZE = 64;
|
||||
private final Map<IPv6Address, LinkedHashSet<Long>> seenNodeInfoUpdates = new HashMap<>();
|
||||
|
||||
public Map<IPv6Address, RouterInfo> getNetmap() {
|
||||
return netmap;
|
||||
}
|
||||
|
||||
|
||||
private volatile Map<IPv6Address,Long>addresses;
|
||||
@@ -81,6 +92,7 @@ public class KLALBRoutingProtocol extends Thread{
|
||||
private ReentrantLock sendLock=new ReentrantLock();
|
||||
|
||||
private Set<BiConsumer<SocketAddress,JsonDataPacket>>receivers=new CopyOnWriteArraySet<BiConsumer<SocketAddress,JsonDataPacket>>();
|
||||
private Set<BiConsumer<IPv6Address,Integer>> nodeInfoUpdateListeners = new CopyOnWriteArraySet<BiConsumer<IPv6Address,Integer>>();
|
||||
|
||||
public void addReceiver(BiConsumer<SocketAddress,JsonDataPacket> rec) {
|
||||
receivers.add(rec);
|
||||
@@ -90,6 +102,57 @@ public class KLALBRoutingProtocol extends Thread{
|
||||
receivers.remove(rec);
|
||||
}
|
||||
|
||||
public void addNodeInfoUpdateListener(BiConsumer<IPv6Address,Integer> listener) {
|
||||
nodeInfoUpdateListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeNodeInfoUpdateListener(BiConsumer<IPv6Address,Integer> listener) {
|
||||
nodeInfoUpdateListeners.remove(listener);
|
||||
}
|
||||
|
||||
public void announceNodeInfoUpdate(int flags) {
|
||||
int validFlags = flags & (RouterInfoPacket.NODE_INFO_TINY_UPDATE_REQUIRED
|
||||
| RouterInfoPacket.NODE_INFO_FULL_UPDATE_REQUIRED);
|
||||
if(validFlags != 0) {
|
||||
pendingNodeInfoUpdateFlags.getAndUpdate(old -> old | validFlags);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean markNodeInfoUpdateSeen(IPv6Address origin, long createTime) {
|
||||
synchronized(seenNodeInfoUpdates) {
|
||||
LinkedHashSet<Long> createTimes = seenNodeInfoUpdates.get(origin);
|
||||
if(createTimes == null) {
|
||||
createTimes = new LinkedHashSet<>();
|
||||
seenNodeInfoUpdates.put(origin, createTimes);
|
||||
}
|
||||
if(!createTimes.add(createTime)) {
|
||||
return false;
|
||||
}
|
||||
while(createTimes.size() > NODE_INFO_UPDATE_REPLAY_WINDOW_SIZE) {
|
||||
createTimes.remove(createTimes.iterator().next());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void removeNodeInfoUpdateWindow(IPv6Address origin) {
|
||||
synchronized(seenNodeInfoUpdates) {
|
||||
seenNodeInfoUpdates.remove(origin);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyNodeInfoUpdate(IPv6Address origin, int flags) {
|
||||
for(BiConsumer<IPv6Address,Integer> listener : nodeInfoUpdateListeners) {
|
||||
try {
|
||||
listener.accept(origin, flags);
|
||||
} catch(Throwable e) {
|
||||
if(debug) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Thread.currentThread().setName("KLALB路由协议接收线程");
|
||||
@@ -104,6 +167,17 @@ public class KLALBRoutingProtocol extends Thread{
|
||||
selfRouterInfo = getSelfRouterInfo();
|
||||
RouterInfo oslf=netmap.put(selfRouterInfo.getLocator().getAddress(), selfRouterInfo);
|
||||
noticeUpdate();
|
||||
int nodeInfoUpdateFlags = pendingNodeInfoUpdateFlags.getAndSet(0);
|
||||
if(nodeInfoUpdateFlags != 0) {
|
||||
RouterInfoPacket updatePacket = new RouterInfoPacket(getSelfRouterInfo(), true, -1);
|
||||
updatePacket.setNodeInfoUpdateFlags(nodeInfoUpdateFlags);
|
||||
try {
|
||||
floodPacket(null, updatePacket);
|
||||
} catch(IOException e) {
|
||||
pendingNodeInfoUpdateFlags.getAndUpdate(old -> old | nodeInfoUpdateFlags);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
/*if(oslf==null||(!selfRouterInfo.equals(oslf))) {
|
||||
long cur=System.nanoTime();
|
||||
if(cur-floodTimer2>5000000000L) {
|
||||
@@ -144,8 +218,10 @@ public class KLALBRoutingProtocol extends Thread{
|
||||
timeout=2000000000L;
|
||||
}
|
||||
if(val.checkTimeOut(timeout)) {
|
||||
iterator.remove();
|
||||
noticeUpdate();
|
||||
if(netmap.remove(type.getKey(), val)) {
|
||||
removeNodeInfoUpdateWindow(type.getKey());
|
||||
noticeUpdate();
|
||||
}
|
||||
}
|
||||
/*List<NeighborInfo> ads=val.getNeighborAddresses();
|
||||
for (Iterator<NeighborInfo> iterator2 = ads.iterator(); iterator2.hasNext();) {
|
||||
@@ -212,7 +288,7 @@ public class KLALBRoutingProtocol extends Thread{
|
||||
ds.receive(dgp);
|
||||
//System.out.println(Arrays.toString( Arrays.copyOf( dgp.getData(),dgp.getLength())));
|
||||
ByteArrayInputStream bi=new ByteArrayInputStream(dgp.getData(),0,dgp.getLength());
|
||||
KLALBRoutingProtocolPacket kp=KLALBRoutingProtocolPacket.readKLALBPacketFromChannel(Channels.newChannel(bi));
|
||||
KLALBRoutingProtocolPacket kp=KLALBRoutingProtocolPacket.readKLALBPacketFromChannel(Channels.newChannel(bi), dgp.getLength());
|
||||
|
||||
switch(kp.getType()) {
|
||||
case KLALBRoutingProtocolPacket.RINFO_REQ:
|
||||
@@ -233,24 +309,30 @@ public class KLALBRoutingProtocol extends Thread{
|
||||
RouterInfoPacket rifp=(RouterInfoPacket) kp;
|
||||
RouterInfo rif=rifp.getRinfo();
|
||||
RouterInfo oldrif=netmap.get(rif.getLocator().getAddress());
|
||||
IPv6Address origin = rif.getLocator().getAddress();
|
||||
int nodeInfoUpdateFlags = rifp.getNodeInfoUpdateFlags();
|
||||
boolean newNodeInfoUpdate = nodeInfoUpdateFlags != 0
|
||||
&& markNodeInfoUpdateSeen(origin, rif.getCreateTime());
|
||||
|
||||
if(debug)
|
||||
System.out.println(rif);
|
||||
if(oldrif==null||oldrif.getCreateTime()<rif.getCreateTime()) {
|
||||
boolean topologyUpdated = oldrif==null||oldrif.getCreateTime()<rif.getCreateTime();
|
||||
if(topologyUpdated) {
|
||||
if(debug)
|
||||
System.out.println("update RouterInfo");
|
||||
netmap.put(rif.getLocator().getAddress(), rif);
|
||||
noticeUpdate();
|
||||
if(rifp.isFlood()) {
|
||||
floodPacket( dgp.getSocketAddress(), rifp);
|
||||
|
||||
}
|
||||
|
||||
}else {
|
||||
|
||||
if(debug)
|
||||
System.out.println("dispose RouterInfo");
|
||||
}
|
||||
if(newNodeInfoUpdate) {
|
||||
notifyNodeInfoUpdate(origin, nodeInfoUpdateFlags);
|
||||
}
|
||||
if(rifp.isFlood() && (topologyUpdated || newNodeInfoUpdate)) {
|
||||
floodPacket(dgp.getSocketAddress(), rifp);
|
||||
}
|
||||
computeShortestPathIfUpdated();
|
||||
break;
|
||||
case KLALBRoutingProtocolPacket.JSON_DATA:
|
||||
@@ -313,6 +395,7 @@ public class KLALBRoutingProtocol extends Thread{
|
||||
}catch(BindException e) {
|
||||
e.printStackTrace();
|
||||
System.err.println("Cannot assign:"+dest);
|
||||
throw e;
|
||||
}finally {
|
||||
sendLock.unlock();
|
||||
}
|
||||
@@ -544,8 +627,9 @@ public class KLALBRoutingProtocol extends Thread{
|
||||
private RouterInfo getSelfRouterInfo() {
|
||||
List<IPv6NetworkLink>links=router.getLinkTabel();
|
||||
Object[] nls=links.toArray();
|
||||
RouterInfo ri=new RouterInfo(System.currentTimeMillis(),router.getLocator(),router.getASN());
|
||||
ri.setDeviceName(router.getDeviceName());
|
||||
long currentTime = System.currentTimeMillis();
|
||||
long createTime = selfCreateTime.updateAndGet(previous -> Math.max(currentTime, previous + 1));
|
||||
RouterInfo ri=new RouterInfo(createTime,router.getLocator(),router.getASN());
|
||||
for(int i=0;i<nls.length;i++) {
|
||||
IPv6NetworkLink nl=(IPv6NetworkLink) nls[i];
|
||||
if((!nl.isLoopBack())&&nl.isUp()) {
|
||||
@@ -601,12 +685,6 @@ public class KLALBRoutingProtocol extends Thread{
|
||||
* 查询某地址广播的设备名称(未知返回null)
|
||||
*/
|
||||
public String getDeviceName(IPv6Address address) {
|
||||
RouterInfo ri=netmap.get(address);
|
||||
if(ri!=null) {
|
||||
String dn=ri.getDeviceName();
|
||||
if(dn!=null&&!dn.isEmpty())
|
||||
return dn;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public Map<IPv6Address, List<LinkDirection>> getPaths() {
|
||||
|
||||
@@ -3,16 +3,19 @@ package org.kne.cloud.network.srv6;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.Cleaner;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.InetAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
||||
import org.kne.cloud.network.congestion.NOCongestionAlgorithm;
|
||||
import org.kne.cloud.network.congestion.SendPacketSlidingWindow;
|
||||
import org.kne.cloud.network.klalb.SendItem;
|
||||
import org.kne.opencl64.Releaser;
|
||||
|
||||
public class KLALBRoutingProtocolAPIClient {
|
||||
@@ -24,31 +27,106 @@ public class KLALBRoutingProtocolAPIClient {
|
||||
|
||||
private BiConsumer<SocketAddress, JsonDataPacket> rec = (addr, data) -> {
|
||||
KLALBRoutingProtocolJsonData dataobj = data.getDecodedData();
|
||||
InetSocketAddress addrs = (InetSocketAddress) addr;
|
||||
UUID ruid = dataobj.getUuid();
|
||||
JsonDataPacket relate = null;
|
||||
//System.out.println(ruid + " " + window.getSendmap());
|
||||
switch (dataobj.getType()) {
|
||||
case KLALBRoutingProtocolJsonData.OPEN_LINES_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));
|
||||
|
||||
}
|
||||
}
|
||||
((Consumer<List<MultiProtocolSocketAddress>>) relate.getUserCallback()).accept(connectsm);
|
||||
case KLALBRoutingProtocolJsonData.NODE_PROFILE_RESP:
|
||||
PendingRequest profileRequest = acknowledgeResponse(ruid,
|
||||
KLALBRoutingProtocolJsonData.NODE_PROFILE_REQ, addr);
|
||||
if (profileRequest != null) {
|
||||
((Consumer<NodeProfile>) profileRequest.callback)
|
||||
.accept(new NodeProfile(dataobj.getDeviceName(), dataobj.getDeviceDescription()));
|
||||
}
|
||||
break;
|
||||
case KLALBRoutingProtocolJsonData.NODE_EXTERNAL_ENDPOINTS_RESP:
|
||||
PendingRequest endpointsRequest = acknowledgeResponse(ruid,
|
||||
KLALBRoutingProtocolJsonData.NODE_EXTERNAL_ENDPOINTS_REQ, addr);
|
||||
if (endpointsRequest != null) {
|
||||
((Consumer<ExternalEndpointsResult>) endpointsRequest.callback).accept(
|
||||
new ExternalEndpointsResult(decodeExternalEndpoints(dataobj.getData()), dataobj.getStatus()));
|
||||
}
|
||||
break;
|
||||
case KLALBRoutingProtocolJsonData.NODE_EXTRA_ROUTES_RESP:
|
||||
PendingRequest routesRequest = acknowledgeResponse(ruid,
|
||||
KLALBRoutingProtocolJsonData.NODE_EXTRA_ROUTES_REQ, addr);
|
||||
if (routesRequest != null) {
|
||||
((Consumer<ExtraRoutesResult>) routesRequest.callback).accept(
|
||||
new ExtraRoutesResult(decodeExtraRoutes(dataobj.getData()), dataobj.getStatus()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private PendingRequest acknowledgeResponse(UUID uuid, String expectedRequestType, SocketAddress sourceAddress) {
|
||||
SendItem<JsonDataPacket> pending = window.getSendmap().get(uuid);
|
||||
if (pending == null) {
|
||||
return null;
|
||||
}
|
||||
KLALBRoutingProtocolJsonData request = pending.getPacket().getDecodedData();
|
||||
if (request == null || !expectedRequestType.equals(request.getType())) {
|
||||
return null;
|
||||
}
|
||||
Object callback = pending.getPacket().getUserCallback();
|
||||
if (!(callback instanceof PendingRequest)) {
|
||||
return null;
|
||||
}
|
||||
PendingRequest pendingRequest = (PendingRequest) callback;
|
||||
if (!expectedRequestType.equals(pendingRequest.expectedRequestType)
|
||||
|| !sameDestination(pendingRequest.expectedDestination, sourceAddress)) {
|
||||
return null;
|
||||
}
|
||||
return window.ack(uuid) == null ? null : pendingRequest;
|
||||
}
|
||||
|
||||
private static boolean sameDestination(SocketAddress expected, SocketAddress actual) {
|
||||
if (expected instanceof InetSocketAddress && actual instanceof InetSocketAddress) {
|
||||
InetSocketAddress expectedInet = (InetSocketAddress) expected;
|
||||
InetSocketAddress actualInet = (InetSocketAddress) actual;
|
||||
if (expectedInet.getPort() != actualInet.getPort()) return false;
|
||||
InetAddress expectedAddress = expectedInet.getAddress();
|
||||
InetAddress actualAddress = actualInet.getAddress();
|
||||
if (expectedAddress != null && actualAddress != null) return expectedAddress.equals(actualAddress);
|
||||
if (expectedAddress == null && actualAddress == null) {
|
||||
return expectedInet.getHostString().equalsIgnoreCase(actualInet.getHostString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return expected != null && expected.equals(actual);
|
||||
}
|
||||
|
||||
private List<MultiProtocolSocketAddress> decodeExternalEndpoints(Object data) {
|
||||
if (!(data instanceof List<?>)) {
|
||||
return new ArrayList<MultiProtocolSocketAddress>();
|
||||
}
|
||||
List<?> encoded = (List<?>) data;
|
||||
List<MultiProtocolSocketAddress> endpoints = new ArrayList<MultiProtocolSocketAddress>(encoded.size());
|
||||
for (Object value : encoded) {
|
||||
if (value instanceof MultiProtocolSocketAddress) {
|
||||
endpoints.add((MultiProtocolSocketAddress) value);
|
||||
} else if (value instanceof String) {
|
||||
try {
|
||||
endpoints.add(new MultiProtocolSocketAddress((String) value));
|
||||
} catch (RuntimeException ignored) {
|
||||
// Ignore malformed entries while preserving the rest of the response.
|
||||
}
|
||||
}
|
||||
}
|
||||
return endpoints;
|
||||
}
|
||||
|
||||
private List<String> decodeExtraRoutes(Object data) {
|
||||
if (!(data instanceof List<?>)) {
|
||||
return new ArrayList<String>();
|
||||
}
|
||||
List<?> encoded = (List<?>) data;
|
||||
List<String> routes = new ArrayList<String>(encoded.size());
|
||||
for (Object value : encoded) {
|
||||
if (value instanceof String) {
|
||||
routes.add((String) value);
|
||||
}
|
||||
}
|
||||
return routes;
|
||||
}
|
||||
|
||||
public KLALBRoutingProtocolAPIClient(KLALBRoutingProtocol routingProtocol) {
|
||||
this.routingProtocol = routingProtocol;
|
||||
routingProtocol.addReceiver(rec);
|
||||
@@ -56,16 +134,29 @@ public class KLALBRoutingProtocolAPIClient {
|
||||
clr.register(this, releaser);
|
||||
}
|
||||
|
||||
public void requestOpenLines(SocketAddress addr, Consumer<List<MultiProtocolSocketAddress>> callback)
|
||||
throws IOException {
|
||||
UUID suid = UUID.randomUUID();
|
||||
KLALBRoutingProtocolJsonData json = new KLALBRoutingProtocolJsonData(
|
||||
KLALBRoutingProtocolJsonData.OPEN_LINES_REQ, suid, null);
|
||||
JsonDataPacket packet = new JsonDataPacket(json);
|
||||
packet.setUserCallback(callback);
|
||||
window.put(suid, packet);
|
||||
routingProtocol.sendJsonPacketToAddress(packet, addr);
|
||||
public void requestNodeProfile(SocketAddress addr, Consumer<NodeProfile> callback) throws IOException {
|
||||
sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_PROFILE_REQ, addr, callback);
|
||||
}
|
||||
|
||||
public void requestExternalEndpoints(SocketAddress addr, Consumer<ExternalEndpointsResult> callback)
|
||||
throws IOException {
|
||||
sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_EXTERNAL_ENDPOINTS_REQ, addr, callback);
|
||||
}
|
||||
|
||||
public void requestExtraRoutes(SocketAddress addr, Consumer<ExtraRoutesResult> callback) throws IOException {
|
||||
sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_EXTRA_ROUTES_REQ, addr, callback);
|
||||
}
|
||||
|
||||
private void sendNodeInfoRequest(String type, SocketAddress addr, Object callback) throws IOException {
|
||||
UUID suid = UUID.randomUUID();
|
||||
KLALBRoutingProtocolJsonData json = new KLALBRoutingProtocolJsonData(type, suid, null);
|
||||
JsonDataPacket packet = new JsonDataPacket(json);
|
||||
packet.setUserCallback(new PendingRequest(addr, type, callback));
|
||||
synchronized (this) {
|
||||
if (closed.get() || releaser.isReleased()) throw new IOException("Node info client is closed");
|
||||
window.put(suid, packet);
|
||||
routingProtocol.sendJsonPacketToAddress(packet, addr);
|
||||
}
|
||||
}
|
||||
|
||||
public KLALBRoutingProtocol getRoutingProtocol() {
|
||||
@@ -74,13 +165,27 @@ public class KLALBRoutingProtocolAPIClient {
|
||||
|
||||
private KLALBRoutingProtocolAPIClientReleaser releaser;
|
||||
|
||||
public void close() {
|
||||
releaser.run();
|
||||
public synchronized void close() {
|
||||
if (closed.compareAndSet(false, true)) releaser.run();
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return releaser.isReleased();
|
||||
return closed.get() || releaser.isReleased();
|
||||
}
|
||||
|
||||
private static final class PendingRequest {
|
||||
private final SocketAddress expectedDestination;
|
||||
private final String expectedRequestType;
|
||||
private final Object callback;
|
||||
|
||||
private PendingRequest(SocketAddress expectedDestination, String expectedRequestType, Object callback) {
|
||||
this.expectedDestination = expectedDestination;
|
||||
this.expectedRequestType = expectedRequestType;
|
||||
this.callback = callback;
|
||||
}
|
||||
}
|
||||
|
||||
private final AtomicBoolean closed = new AtomicBoolean();
|
||||
}
|
||||
|
||||
class KLALBRoutingProtocolAPIClientReleaser extends Releaser<BiConsumer<SocketAddress, JsonDataPacket>> {
|
||||
|
||||
@@ -2,8 +2,8 @@ package org.kne.cloud.network.srv6;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.Cleaner;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.kne.cloud.network.klalb.KLALBController;
|
||||
@@ -13,31 +13,52 @@ public class KLALBRoutingProtocolAPIServer {
|
||||
private KLALBController controller;
|
||||
private KLALBRoutingProtocol routingProtocol;
|
||||
|
||||
private static final Cleaner clr = Cleaner.create();
|
||||
|
||||
private static final Cleaner clr=Cleaner.create();
|
||||
|
||||
private BiConsumer<SocketAddress, JsonDataPacket> rec=(addr,data)->{
|
||||
private BiConsumer<SocketAddress, JsonDataPacket> rec = (addr, data) -> {
|
||||
try {
|
||||
KLALBRoutingProtocolJsonData dataobj= data.getDecodedData();
|
||||
InetSocketAddress addrs=(InetSocketAddress) addr;
|
||||
switch(dataobj.getType()){
|
||||
case KLALBRoutingProtocolJsonData.OPEN_LINES_REQ:
|
||||
if(controller.getConfigItem()==null||(!controller.getConfigItem().isDenyLineTableQuery())) {
|
||||
KLALBRoutingProtocolJsonData json=new KLALBRoutingProtocolJsonData(KLALBRoutingProtocolJsonData.OPEN_LINES_RESP,dataobj.getUuid(),controller.getSelflineTable());
|
||||
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(json),addr);
|
||||
KLALBRoutingProtocolJsonData dataobj = data.getDecodedData();
|
||||
switch (dataobj.getType()) {
|
||||
case KLALBRoutingProtocolJsonData.NODE_PROFILE_REQ:
|
||||
KLALBController.PublishedNodeInfo profilePublished = controller.getPublishedNodeInfo();
|
||||
KLALBRoutingProtocolJsonData profile = new KLALBRoutingProtocolJsonData(
|
||||
KLALBRoutingProtocolJsonData.NODE_PROFILE_RESP, dataobj.getUuid(), null);
|
||||
profile.setDeviceName(profilePublished.getDeviceName());
|
||||
profile.setDeviceDescription(profilePublished.getDeviceDescription());
|
||||
profile.setStatus(NodeInfoQueryStatus.OK);
|
||||
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(profile), addr);
|
||||
break;
|
||||
case KLALBRoutingProtocolJsonData.NODE_EXTERNAL_ENDPOINTS_REQ:
|
||||
boolean denyEndpoints = controller.getConfigItem() != null
|
||||
&& controller.getConfigItem().isDenyExternalEndpointQuery();
|
||||
KLALBController.PublishedNodeInfo endpointsPublished = controller.getPublishedNodeInfo();
|
||||
KLALBRoutingProtocolJsonData endpoints = new KLALBRoutingProtocolJsonData(
|
||||
KLALBRoutingProtocolJsonData.NODE_EXTERNAL_ENDPOINTS_RESP, dataobj.getUuid(),
|
||||
denyEndpoints ? new ArrayList<org.kne.cloud.network.MultiProtocolSocketAddress>()
|
||||
: new ArrayList<org.kne.cloud.network.MultiProtocolSocketAddress>(
|
||||
endpointsPublished.getExternalEndpoints()));
|
||||
endpoints.setStatus(denyEndpoints ? NodeInfoQueryStatus.DENIED : NodeInfoQueryStatus.OK);
|
||||
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(endpoints), addr);
|
||||
break;
|
||||
case KLALBRoutingProtocolJsonData.NODE_EXTRA_ROUTES_REQ:
|
||||
KLALBController.PublishedNodeInfo routesPublished = controller.getPublishedNodeInfo();
|
||||
KLALBRoutingProtocolJsonData routes = new KLALBRoutingProtocolJsonData(
|
||||
KLALBRoutingProtocolJsonData.NODE_EXTRA_ROUTES_RESP, dataobj.getUuid(),
|
||||
new ArrayList<String>(routesPublished.getExtraRoutes()));
|
||||
routes.setStatus(NodeInfoQueryStatus.OK);
|
||||
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(routes), addr);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
};
|
||||
|
||||
public KLALBRoutingProtocolAPIServer(KLALBRoutingProtocol routingProtocol,KLALBController controller) {
|
||||
this.controller=controller;
|
||||
this.routingProtocol=routingProtocol;
|
||||
public KLALBRoutingProtocolAPIServer(KLALBRoutingProtocol routingProtocol, KLALBController controller) {
|
||||
this.controller = controller;
|
||||
this.routingProtocol = routingProtocol;
|
||||
routingProtocol.addReceiver(rec);
|
||||
this.releaser=new KLALBRoutingProtocolAPIServerReleaser(this.routingProtocol,rec);
|
||||
this.releaser = new KLALBRoutingProtocolAPIServerReleaser(this.routingProtocol, rec);
|
||||
clr.register(this, releaser);
|
||||
}
|
||||
|
||||
@@ -49,26 +70,25 @@ public class KLALBRoutingProtocolAPIServer {
|
||||
return routingProtocol;
|
||||
}
|
||||
|
||||
|
||||
private KLALBRoutingProtocolAPIServerReleaser releaser;
|
||||
private KLALBRoutingProtocolAPIServerReleaser releaser;
|
||||
|
||||
public void close() {
|
||||
releaser.run();
|
||||
}
|
||||
|
||||
|
||||
public boolean isClosed() {
|
||||
return releaser.isReleased();
|
||||
}
|
||||
|
||||
}
|
||||
class KLALBRoutingProtocolAPIServerReleaser extends Releaser<BiConsumer<SocketAddress, JsonDataPacket>>{
|
||||
|
||||
class KLALBRoutingProtocolAPIServerReleaser extends Releaser<BiConsumer<SocketAddress, JsonDataPacket>> {
|
||||
|
||||
private KLALBRoutingProtocol routingProtocol;
|
||||
|
||||
public KLALBRoutingProtocolAPIServerReleaser(KLALBRoutingProtocol routingProtocol,BiConsumer<SocketAddress, JsonDataPacket> resource) {
|
||||
public KLALBRoutingProtocolAPIServerReleaser(KLALBRoutingProtocol routingProtocol,
|
||||
BiConsumer<SocketAddress, JsonDataPacket> resource) {
|
||||
super(resource);
|
||||
this.routingProtocol=routingProtocol;
|
||||
this.routingProtocol = routingProtocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,66 +1,89 @@
|
||||
package org.kne.cloud.network.srv6;
|
||||
|
||||
import java.util.Objects;
|
||||
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_PROFILE_REQ = "nodeprofilereq";
|
||||
public static final String NODE_PROFILE_RESP = "nodeprofileresp";
|
||||
public static final String NODE_EXTERNAL_ENDPOINTS_REQ = "nodeexternalendpointsreq";
|
||||
public static final String NODE_EXTERNAL_ENDPOINTS_RESP = "nodeexternalendpointsresp";
|
||||
public static final String NODE_EXTRA_ROUTES_REQ = "nodeextraroutesreq";
|
||||
public static final String NODE_EXTRA_ROUTES_RESP = "nodeextraroutesresp";
|
||||
|
||||
private String type;
|
||||
private UUID uuid;
|
||||
private Object data;
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public Object getData() {
|
||||
return data;
|
||||
}
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((data == null) ? 0 : data.hashCode());
|
||||
result = prime * result + ((type == null) ? 0 : type.hashCode());
|
||||
result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
KLALBRoutingProtocolJsonData other = (KLALBRoutingProtocolJsonData) obj;
|
||||
if (data == null) {
|
||||
if (other.data != null)
|
||||
return false;
|
||||
} else if (!data.equals(other.data))
|
||||
return false;
|
||||
if (type == null) {
|
||||
if (other.type != null)
|
||||
return false;
|
||||
} else if (!type.equals(other.type))
|
||||
return false;
|
||||
if (uuid == null) {
|
||||
if (other.uuid != null)
|
||||
return false;
|
||||
} else if (!uuid.equals(other.uuid))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
private String deviceName;
|
||||
private String deviceDescription;
|
||||
private NodeInfoQueryStatus status;
|
||||
|
||||
public KLALBRoutingProtocolJsonData(String type, UUID uuid, Object data) {
|
||||
super();
|
||||
this.type = type;
|
||||
this.uuid = uuid;
|
||||
this.data = data;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "KLALBRoutingProtocolJsonData [type=" + type + ", uuid=" + uuid + ", data=" + data + "]";
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public Object getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
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 NodeInfoQueryStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(NodeInfoQueryStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(type, uuid, data, deviceName, deviceDescription, status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof KLALBRoutingProtocolJsonData)) {
|
||||
return false;
|
||||
}
|
||||
KLALBRoutingProtocolJsonData other = (KLALBRoutingProtocolJsonData) obj;
|
||||
return Objects.equals(type, other.type) && Objects.equals(uuid, other.uuid)
|
||||
&& Objects.equals(data, other.data) && Objects.equals(deviceName, other.deviceName)
|
||||
&& Objects.equals(deviceDescription, other.deviceDescription)
|
||||
&& Objects.equals(status, other.status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "KLALBRoutingProtocolJsonData [type=" + type + ", uuid=" + uuid + ", data=" + data
|
||||
+ ", deviceName=" + deviceName + ", deviceDescription=" + deviceDescription + ", status=" + status
|
||||
+ "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,10 @@ public abstract class KLALBRoutingProtocolPacket extends NetworkPacket {
|
||||
}
|
||||
|
||||
public static KLALBRoutingProtocolPacket readKLALBPacketFromChannel(ReadableByteChannel in) throws IOException {
|
||||
return readKLALBPacketFromChannel(in, -1);
|
||||
}
|
||||
|
||||
public static KLALBRoutingProtocolPacket readKLALBPacketFromChannel(ReadableByteChannel in, long packetLength) throws IOException {
|
||||
while(true) {
|
||||
ByteBuffer bb=NetworkPacket.bufferAllocator.allocate(40);
|
||||
bb.limit(1);
|
||||
@@ -111,11 +115,13 @@ public abstract class KLALBRoutingProtocolPacket extends NetworkPacket {
|
||||
switch(type) {
|
||||
case RINFO_REQ:
|
||||
klp=new RouterInfoRequestPacket(bb);
|
||||
klp.readFromChannel(in);
|
||||
if(packetLength < 0) klp.readFromChannel(in);
|
||||
else klp.readFromChannel(in, packetLength);
|
||||
return klp;
|
||||
case RINFO:
|
||||
klp=new RouterInfoPacket(bb);
|
||||
klp.readFromChannel(in);
|
||||
if(packetLength < 0) klp.readFromChannel(in);
|
||||
else klp.readFromChannel(in, packetLength);
|
||||
return klp;
|
||||
case JSON_DATA:
|
||||
klp=new JsonDataPacket(bb);
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.kne.cloud.network.srv6;
|
||||
|
||||
public enum NodeInfoQueryStatus {
|
||||
OK,
|
||||
DENIED
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.kne.cloud.network.srv6;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public final class NodeProfile {
|
||||
private final String deviceName;
|
||||
private final String deviceDescription;
|
||||
|
||||
public NodeProfile(String deviceName, String deviceDescription) {
|
||||
this.deviceName = deviceName;
|
||||
this.deviceDescription = deviceDescription;
|
||||
}
|
||||
|
||||
public String getDeviceName() {
|
||||
return deviceName;
|
||||
}
|
||||
|
||||
public String getDeviceDescription() {
|
||||
return deviceDescription;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(deviceName, deviceDescription);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof NodeProfile)) {
|
||||
return false;
|
||||
}
|
||||
NodeProfile other = (NodeProfile) obj;
|
||||
return Objects.equals(deviceName, other.deviceName)
|
||||
&& Objects.equals(deviceDescription, other.deviceDescription);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NodeProfile [deviceName=" + deviceName + ", deviceDescription=" + deviceDescription + "]";
|
||||
}
|
||||
}
|
||||
@@ -60,7 +60,6 @@ public class RouterInfo implements Serializable{
|
||||
return Objects.equals(locator, other.locator) && Objects.equals(neighborAddresses, other.neighborAddresses);
|
||||
}
|
||||
private List<NeighborInfo> neighborAddresses=new ArrayList<>();
|
||||
private String deviceName=""; // 设备名称(广播时携带,描述不广播)
|
||||
private long createTime;
|
||||
|
||||
|
||||
@@ -69,6 +68,7 @@ public class RouterInfo implements Serializable{
|
||||
}
|
||||
private static final long INFO_UPDATETIME=10000000000L;
|
||||
private static final long INFO_TIMEOUT=60000000000L;
|
||||
private static final int MAX_NEIGHBOR_COUNT = 512;
|
||||
private long putTime=System.nanoTime();
|
||||
public boolean checkUpdateTime() {
|
||||
return System.nanoTime()-putTime>INFO_UPDATETIME;
|
||||
@@ -91,14 +91,6 @@ public class RouterInfo implements Serializable{
|
||||
return neighborAddresses;
|
||||
}
|
||||
|
||||
public String getDeviceName() {
|
||||
return deviceName;
|
||||
}
|
||||
|
||||
public void setDeviceName(String deviceName) {
|
||||
this.deviceName = deviceName;
|
||||
}
|
||||
|
||||
public RouterInfo( long createTime,IPv6AddressGroup locator,long asn) {
|
||||
this.createTime = createTime;
|
||||
this.locator=locator;
|
||||
@@ -108,14 +100,19 @@ public class RouterInfo implements Serializable{
|
||||
public RouterInfo() {
|
||||
}
|
||||
public void writeToStream(DataOutputStream out) throws IOException {
|
||||
int neighborCount = neighborAddresses == null ? -1 : neighborAddresses.size();
|
||||
if(neighborCount<0 || neighborCount>MAX_NEIGHBOR_COUNT) {
|
||||
throw new IOException("Invalid neighbor count: " + neighborCount);
|
||||
}
|
||||
locator.writeToStream(out);
|
||||
out.writeLong(asn);
|
||||
out.writeLong(createTime);
|
||||
out.writeInt(neighborAddresses.size());
|
||||
out.writeInt(neighborCount);
|
||||
for( NeighborInfo neighborInfo :neighborAddresses) {
|
||||
neighborInfo.writeToStream(out);
|
||||
}
|
||||
out.writeUTF(deviceName==null?"":deviceName);
|
||||
// Keep the legacy UTF slot so older RINFO decoders stay aligned.
|
||||
out.writeUTF("");
|
||||
}
|
||||
|
||||
protected void writeToChannel(WritableByteChannel dto) throws IOException {
|
||||
@@ -129,6 +126,9 @@ public class RouterInfo implements Serializable{
|
||||
asn=in.readLong();
|
||||
createTime=in.readLong();
|
||||
int size=in.readInt();
|
||||
if(size<0 || size>MAX_NEIGHBOR_COUNT) {
|
||||
throw new IOException("Invalid neighbor count: " + size);
|
||||
}
|
||||
neighborAddresses=new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
NeighborInfo nif=new NeighborInfo();
|
||||
@@ -136,7 +136,8 @@ public class RouterInfo implements Serializable{
|
||||
neighborAddresses.add(nif);
|
||||
|
||||
}
|
||||
deviceName=in.readUTF();
|
||||
// Consume the legacy device-name slot; names are no longer broadcast here.
|
||||
in.readUTF();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.kne.cloud.network.srv6;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.Externalizable;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInput;
|
||||
@@ -23,6 +24,11 @@ import org.kne.cloud.network.NetworkPacket;
|
||||
import org.kne.cloud.network.ipv6.IPv6AddressGroup;
|
||||
|
||||
public class RouterInfoPacket extends KLALBRoutingProtocolPacket implements Serializable{
|
||||
public static final int NODE_INFO_TINY_UPDATE_REQUIRED = 1;
|
||||
public static final int NODE_INFO_FULL_UPDATE_REQUIRED = 2;
|
||||
private static final int NODE_INFO_UPDATE_FLAGS_MASK = NODE_INFO_TINY_UPDATE_REQUIRED
|
||||
| NODE_INFO_FULL_UPDATE_REQUIRED;
|
||||
private static final int NODE_INFO_UPDATE_MAGIC = 0xA5;
|
||||
|
||||
public RouterInfoPacket(RouterInfo routerInfo, boolean flood,long asn) {
|
||||
super(RINFO);
|
||||
@@ -35,6 +41,7 @@ public class RouterInfoPacket extends KLALBRoutingProtocolPacket implements Seri
|
||||
}
|
||||
|
||||
private RouterInfo rinfo;
|
||||
private transient int nodeInfoUpdateFlags;
|
||||
|
||||
public boolean isFlood() {
|
||||
return (getHeader().get(1)&1)==1;
|
||||
@@ -47,6 +54,14 @@ public class RouterInfoPacket extends KLALBRoutingProtocolPacket implements Seri
|
||||
public RouterInfo getRinfo() {
|
||||
return rinfo;
|
||||
}
|
||||
|
||||
public int getNodeInfoUpdateFlags() {
|
||||
return nodeInfoUpdateFlags;
|
||||
}
|
||||
|
||||
public void setNodeInfoUpdateFlags(int flags) {
|
||||
nodeInfoUpdateFlags = flags & NODE_INFO_UPDATE_FLAGS_MASK;
|
||||
}
|
||||
@Override
|
||||
protected long getHeaderSize() {
|
||||
return super.getHeaderSize()+9;
|
||||
@@ -68,12 +83,43 @@ public class RouterInfoPacket extends KLALBRoutingProtocolPacket implements Seri
|
||||
public void writeToChannel(WritableByteChannel dto) throws IOException {
|
||||
super.writeToChannel(dto);
|
||||
rinfo.writeToChannel(dto);
|
||||
int flags = nodeInfoUpdateFlags & NODE_INFO_UPDATE_FLAGS_MASK;
|
||||
if(flags != 0) {
|
||||
ByteBuffer extension = ByteBuffer.wrap(new byte[] {(byte) NODE_INFO_UPDATE_MAGIC, (byte) flags});
|
||||
while(extension.hasRemaining()) {
|
||||
dto.write(extension);
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void readFromChannel(ReadableByteChannel din, long length) throws IOException {
|
||||
super.readFromChannel(din, length);
|
||||
rinfo=new RouterInfo();
|
||||
rinfo.readFromChannel(din, length);
|
||||
if(length < 0) {
|
||||
rinfo.readFromChannel(din, length);
|
||||
nodeInfoUpdateFlags = 0;
|
||||
return;
|
||||
}
|
||||
long payloadLength = length - getHeaderSize();
|
||||
if(payloadLength < 0 || payloadLength > Integer.MAX_VALUE) {
|
||||
throw new IOException("Invalid RINFO packet length: " + length);
|
||||
}
|
||||
byte[] payload = new byte[(int) payloadLength];
|
||||
ByteBuffer payloadBuffer = ByteBuffer.wrap(payload);
|
||||
while(payloadBuffer.hasRemaining()) {
|
||||
if(din.read(payloadBuffer) == -1) {
|
||||
throw new java.io.EOFException();
|
||||
}
|
||||
}
|
||||
ByteArrayInputStream payloadInput = new ByteArrayInputStream(payload);
|
||||
rinfo.readFromStream(new DataInputStream(payloadInput));
|
||||
nodeInfoUpdateFlags = 0;
|
||||
int marker = payloadInput.read();
|
||||
int flags = payloadInput.read();
|
||||
int trailing = payloadInput.read();
|
||||
if(marker == NODE_INFO_UPDATE_MAGIC && flags >= 0 && trailing == -1) {
|
||||
nodeInfoUpdateFlags = flags & NODE_INFO_UPDATE_FLAGS_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user