4 Commits
Author SHA1 Message Date
SerinaNya dde54d0929 Revert "fix(config): sync Swing settings after Web updates"
This reverts commit b6acc6d509.
2026-08-31 13:24:21 +08:00
SerinaNya 3dae7dd876 feat(srv6)!: split node information queries
Replace the combined node-info protocol with independent profile,
external-endpoint, and extra-route requests. Aggregate consumers use
NodeInfoQueryCoordinator and validate response source addresses.

BREAKING CHANGE: nodeinfotinyreq/resp and nodeinfofullreq/resp are
removed. Peers must use the profile, external-endpoint, and extra-route
request pairs.
2026-08-31 01:13:49 +08:00
SerinaNya b6acc6d509 fix(config): sync Swing settings after Web updates
Add revisioned configuration events and detached commits so Web changes
refresh clean Swing forms without overwriting unsaved local edits. Preserve
configuration formatting and document the synchronized update workflow.
2026-08-29 00:24:20 +08:00
SerinaNya be82f55277 feat(srv6)!: add node-info update invalidation
Publish node metadata through Tiny and Full queries, synchronize Swing and
web topology details, and document the updated protocol workflow.

BREAKING CHANGE: RouterInfo no longer carries device names; peers must use
Tiny node-info queries.
2026-08-28 23:14:04 +08:00
63 changed files with 2382 additions and 1184 deletions
+1 -3
View File
@@ -4,6 +4,7 @@
<classpathentry kind="lib" path="lib/gson-2.1.jar"/> <classpathentry kind="lib" path="lib/gson-2.1.jar"/>
<classpathentry kind="lib" path="lib/javassist.jar" sourcepath="C:/Users/ADMINI~1/AppData/Local/Temp/1/.org.sf.feeling.decompiler1686963856070/source/javassist-3.29.2-GA-sources.jar"/> <classpathentry kind="lib" path="lib/javassist.jar" sourcepath="C:/Users/ADMINI~1/AppData/Local/Temp/1/.org.sf.feeling.decompiler1686963856070/source/javassist-3.29.2-GA-sources.jar"/>
<classpathentry kind="lib" path="lib/ini4j.jar"/> <classpathentry kind="lib" path="lib/ini4j.jar"/>
<classpathentry kind="lib" path="lib/JFreeChart1.5.2.jar"/>
<classpathentry kind="lib" path="lib/toml4j-0.7.1.jar"/> <classpathentry kind="lib" path="lib/toml4j-0.7.1.jar"/>
<classpathentry kind="lib" path="lib/pcap4j-core-1.8.2.jar" sourcepath="C:/Users/ADMINI~1/AppData/Local/Temp/1/.org.sf.feeling.decompiler1725520274095/source/pcap4j-core-1.8.2-sources.jar"/> <classpathentry kind="lib" path="lib/pcap4j-core-1.8.2.jar" sourcepath="C:/Users/ADMINI~1/AppData/Local/Temp/1/.org.sf.feeling.decompiler1725520274095/source/pcap4j-core-1.8.2-sources.jar"/>
<classpathentry kind="lib" path="lib/pcap4j-packetfactory-propertiesbased-1.8.2.jar"/> <classpathentry kind="lib" path="lib/pcap4j-packetfactory-propertiesbased-1.8.2.jar"/>
@@ -28,8 +29,5 @@
<attribute name="module" value="true"/> <attribute name="module" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="lib" path="lib/jfreechart-1.5.3.jar"/>
<classpathentry kind="lib" path="lib/jfreechart-1.5.3-javadoc.jar"/>
<classpathentry kind="lib" path="lib/jfreechart-1.5.3-sources.jar"/>
<classpathentry kind="output" path="bin"/> <classpathentry kind="output" path="bin"/>
</classpath> </classpath>
-38
View File
@@ -1,38 +0,0 @@
<component name="libraryTable">
<library name="jfreechart-1.5.3">
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/jfreechart-1.5.3.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/disruptor-4.0.0.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/gson-2.1.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/ini4j.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/javassist.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/JavaTUN0.3.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/jctools-core-4.0.6.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/KNElib1.0.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/KNEOptimize.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/pcap4j-core-1.8.2.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/pcap4j-packetfactory-propertiesbased-1.8.2.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/pcap4j-packetfactory-static-1.8.2.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/pcap4j-packettest-1.8.2-tests.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/pcap4j-sample-1.8.2.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/slf4j-api-2.0.9.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/slf4j-api-2.0.9-tests.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/toml4j-0.7.1.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/uuid-creator-6.1.1.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$PROJECT_DIR$/lib/jctools-core-4.0.6-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$PROJECT_DIR$/lib/jfreechart-1.5.3-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/disruptor-4.0.0-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/ini4j.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/JavaTUN0.3.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/jctools-core-4.0.6-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/KNElib1.0.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/slf4j-api-2.0.9-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/toml4j-0.7.1.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/uuid-creator-6.1.1-sources.jar!/" />
</SOURCES>
</library>
</component>
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_26" project-jdk-name="jdk-26.0.1" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2">
<output url="file://$PROJECT_DIR$/classes" /> <output url="file://$PROJECT_DIR$/classes" />
</component> </component>
</project> </project>
+21 -83
View File
@@ -1,101 +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`. - 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:
Compile (`javac` is NOT on PATH — use the full JDK path; `-encoding UTF-8` is mandatory because sources contain Chinese text):
```powershell ```powershell
& "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) & "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)
``` ```
Warnings about `ThreadTool` varargs / deprecated `finalize` are pre-existing and expected — success = exit code 0. After recompiling, restart the running app (IDE-debugged JVMs keep old classes). - Run from the repository root. `src` must stay on the classpath because manual compilation does not copy resource bundles:
Run from the repo root — CWD matters:
- reads `klalb-config.json` from CWD
- loads native libs from CWD: `tuntap4j.dll/.so/.dylib`, `wintun.dll`, `fastcopy.dll` (TUN device support)
- classpath must include `src` as well as `bin`: i18n bundles (`/klalb_*.properties`) and images (`/assets/*`) are classpath resources that Eclipse copies to `bin` but manual `javac` does not
```powershell ```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): ## Dashboard
- 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. - `dashboard/` is a Git submodule. Commit dashboard changes in that repository, then update the parent submodule pointer.
- To disable TUN creation completely (e.g. for non-admin UI/routing testing), set `"enableTUN": false` in `klalb-config.json` or toggle off "启用 TUN 虚拟网卡" in GUI/Web settings. - 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.
- Routing broadcast (`RouterInfo`) transmits `deviceName`, which topology and node overview panels display. `deviceDescription` is NOT broadcast — it only leaves the node in full node-info query responses (see srv6 API below); `ExtraRoutes` remain local controller configs. - Vite proxies `/api` to `http://127.0.0.1:4665`; use `pnpm dev` only with the Java API running there.
## Verification ## 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` → start `KLALBWebServer` (if `"webUI": true` or web server enabled, default port `4665`) → interactive console (`help`, `links-state`, `route`, `kperf`, ...). - `KLALBConfigItem` is a polymorphic JSON array keyed by case-sensitive `Type`. New types need serializer and deserializer support; preserve unknown items' raw JSON.
- `org.kne.cloud.network.klalb.web.KLALBWebServer` — built-in HTTP/SSE server (JDK `HttpServer`): - `/api/config` is field-by-field parsing, not whole-object Gson mapping. Keep legacy key aliases in sync with new fields.
- API endpoints: `/api/status`, `/api/events` (SSE stream, 200ms intervals), `/api/links`, `/api/links/action`, `/api/links/reconnect`, `/api/routing-table`, `/api/nodes` (topology graph), `/api/node-info?address=<ipv6>` (on-demand full node info), `/api/config`. - Vendored Gson is `2.1`: responses that are `JsonElement` instances must be serialized with `JsonElement.toString()`, not reflective `gson.toJson(Object)`.
- Static file hosting / SPA fallback: serves `dashboard/dist/` assets directly. - UI strings use `UIEnv.getRsb()`; add keys to both `src/klalb_zh_CN.properties` and `src/klalb_en_US.properties`.
- `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. - `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.
- `...network.klalb` — app core: `KLALBController` (the virtual SRv6 network), `KLALBRemoteLink` (WAN lines), `*Packet` wire-format classes, virtual socket implementations. - `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.
- `...network.congestion` — pluggable congestion control (BBR, Vegas2, DCTCP...), chosen via `"congestionAlgorithm"` in config.
- `...network.kltp` — custom reliable transport protocol (packets/streams).
- `...network.ipv6`, `...network.srv6` — packet codecs, route table, Dijkstra path computation.
- **Node-info query API** (`...network.srv6`, JSON datagrams on `KLALBRoutingProtocol.DEFAULT_PORT=1001`): `KLALBRoutingProtocolAPIServer/Client` speak two request types —
- `nodeinfotinyreq/resp` → device name ONLY; never gated by any flag (name is public via broadcast anyway).
- `nodeinfofullreq/resp` → externalEndpoints + deviceName + deviceDescription. `denyExternalEndpointQuery=true` hides ONLY the endpoint list (`data=null`); name/description still answer.
- GUI rule: opening `NodeInformationPanel` = Full query; use `requestNodeInfoTiny` for lightweight/background lookups. Legacy `openlines*` message types were removed — mixed-version meshes get silence, so upgrade the whole network together.
- `JsonDataPacket` stores its UTF-8 payload length in a 2-byte header field: keep every JSON message under 64 KiB.
- `...network.frpc` — frp client integration.
- `...klalb.ui` — all Swing UI code.
## Frontend (Dashboard)
Located in `dashboard/`:
- **Git layout**: `dashboard/` is a separate git repo wired in as a submodule (own origin on `git.code.cq.cn`). Commit frontend changes inside `dashboard/` first, then bump the submodule pointer in the parent repo — parent-repo commits alone do not capture them.
- **Stack**: Vite + React 19 + TypeScript + Tailwind CSS v4 + `@base-ui/react` (style: `base-nova`, icons: `lucide-react`, toasts: `@base-ui/react/toast`).
- **Routing**: Hash-based routing (`#/overview`, `#/connections`, `#/routing-table`, `#/topology`, `#/settings`) for seamless SPA hosting under Java `KLALBWebServer`.
- **Package Manager**: `pnpm` (run all commands from `dashboard/` directory).
- **Component installation**: **Must** use CLI via `pnpm dlx shadcn@latest add <component>` (e.g. `pnpm dlx shadcn@latest add alert card badge toast`). Never create or fake shadcn components manually. Non-shadcn libs (`@xyflow/react`, `d3-force`) are installed via plain `pnpm add`.
- **Commands**:
- `pnpm dev` — Start Vite dev server (proxies `/api` to backend `http://127.0.0.1:4665`).
- `pnpm build` — Typecheck and build SPA to `dashboard/dist` (which Java `KLALBWebServer` serves directly).
- `pnpm lint` / `pnpm typecheck` — Verification.
- **Pages & data flow**:
- Overview / Connections read the SSE stream (`use-klalb-sse.ts`, 200ms pushes of status + links).
- Settings loads/saves `/api/config` (`use-klalb-config.ts`); save payload must keep legacy field aliases alongside new names for compatibility.
- Routing table polls `/api/routing-table` every 1s (`use-routing-table.ts`); `cost` is delay-derived and displayed in milliseconds.
- Topology polls `/api/nodes` every 1s (`use-topology.ts`) — SSE does NOT carry topology.
- Selecting a topology node queries `/api/node-info`; remote node descriptions require a full SRv6 node-info request and can time out after 3s.
- Topology layout: `d3-force` headless simulation (recomputed only when node/edge structure changes) rendered by `@xyflow/react` with custom `device-node` / `link-edge` components in `src/components/topology/`.
- React hooks lint rule forbids `setState` synchronously inside effects — initialize form state via component `key` remount + lazy `useState(() => ...)` initializers (see `SettingsForm` pattern).
## 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`).
Key controller config fields:
- `externalEndpoints` / `autoConnections`: published vs auto-connect endpoint lists (renamed from `openConnections`, which itself replaced legacy `LineTable`; the old name was a developer naming mistake — these addresses are this node's externally published endpoints, not "connections").
- `ntpServers`: time server list (replaces `ntpServerTable`).
- `denyExternalEndpointQuery` / `denyExternalEndpointBroadcast`: safety flags — the query flag hides ONLY the external-endpoint list in full node-info responses (device name/description still answer; Tiny queries are never gated), the broadcast flag disables LAN multicast discovery (renamed from `denyConnectionQuery` / `denyConnectionBroadcast`, which replaced `denyLineTableQuery` / `denyLineTableBroadcast`).
- `enableTUN`: boolean flag for TUN interface creation (`"TUNName"` configures device name).
- `webListen`: Web API listen address, normally `http://0.0.0.0:4665`; legacy `webPort` is accepted on load/API input.
Legacy JSON keys are still accepted on load: `KLALBConfigItem.getDefaultJsonDeserializer()` normalizes old key names (`openConnections`/`LineTable`, `denyConnectionQuery`, `denyLineTable*`, ...) before reflective deserialization (manual rewrite because gson-2.1 has no `@SerializedName(alternate=...)`), and `handleConfig` in the web server accepts them too. New saves always write canonical names.
Gson quirks:
- **gson-2.1 (vendored) is ancient**: its `JSON_ELEMENT` adapter factory only matches exact `JsonElement.class`, NOT subclasses. Calling `gson.toJson(Object)` with a runtime `JsonObject`/`JsonArray` reflectively serializes the internal field as `{"members": {...}}`. `KLALBWebServer.sendJsonResponse` guards against this by using `JsonElement.toString()` for JsonElement instances — keep that guard when adding new response paths. SSE avoids the issue entirely via `JsonObject.toString()`.
- `/api/config` GET/POST is parsed field-by-field in `KLALBWebServer.handleConfig` (NOT whole-object Gson reflection) because polymorphic fields (`List<InetAddress>`, `List<MultiProtocolSocketAddress>`) break reflective mapping. Keep new config fields in sync there, accepting both legacy and new JSON key names.
- `InetAddress`, `MultiProtocolSocketAddress`, and `KLALBConfigItem` custom adapters are registered on the shared Gson in `KLALBProxySystem`; the web server reuses that instance via `proxySystem.getGson()`.
- Saving via web API persists through `KLALBProxySystem.saveConfigToFile()` (GUI save consumer takes precedence when present).
## 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.
-1
View File
@@ -249,6 +249,5 @@
</library> </library>
</orderEntry> </orderEntry>
<orderEntry type="jdk" jdkName="jdk-26.0.1" jdkType="JavaSDK" /> <orderEntry type="jdk" jdkName="jdk-26.0.1" jdkType="JavaSDK" />
<orderEntry type="library" name="jfreechart-1.5.3" level="project" />
</component> </component>
</module> </module>
+20
View File
@@ -0,0 +1,20 @@
//加速线路
{KLALBRemote}->43.248.189.107:35000
{KLALBRemote}->cn-bj-bgp-3.openfrp.top:65529
{KLALBRemote}->180.76.147.250:65529
{KLALBRemote}->cn-ah-dx-1.natfrp.cloud:65529
{KLALBRemote}->cn-nn-dx-1.natfrp.cloud:65529
{KLALBRemote}->cn-wh-dx-1.natfrp.cloud:65529
{KLALBRemote}->cn-zz-bgp-10.natfrp.cloud:23330
{KLALBRemote}->cn-zz-bgp-7.natfrp.cloud:33336
{KLALBRemote}->43.143.109.64:49965
{KLALBRemote}->frp.104300.xyz:49965
{KLALBRemote}->us.afrps.cn:49966
{KLALBRemote}->hk.afrps.cn:49966
{KLALBRemote}->la.afrps.cn:49966
{KLALBRemote}->frp.freefrp.net:49965
{KLALBRemote}->frp1.freefrp.net:49965
{KLALBRemote}->frp2.freefrp.net:49965
{KLALBRemote}->frp4.freefrp.net:49965
0.0.0.0:25565->{KLALBVirtual}[171d:a999:e697:4b23:ae52:9f29:4532:e1ee]:25565
BIN
View File
Binary file not shown.
+79
View File
@@ -0,0 +1,79 @@
[
{"Type":"KLALBController" ,
"TCPListen" : "0.0.0.0:4565",
"UDPListen" : "{UDP}0.0.0.0:4572",
"VirtualSocketName" : "KLALB_Stream",
"LineTable":["43.248.189.107:65529",
"cn-bj-bgp-3.openfrp.top:65529",
"180.76.147.250:65529",
"cn-ah-dx-1.natfrp.cloud:65529",
"cn-nn-dx-1.natfrp.cloud:65529",
"cn-wh-dx-1.natfrp.cloud:65529",
"cn-zz-bgp-10.natfrp.cloud:23330",
"cn-zz-bgp-7.natfrp.cloud:33336",
"43.143.109.64:49965",
"frp.104300.xyz:49965",
"us.afrps.cn:49966",
"hk.afrps.cn:49966",
"la.afrps.cn:49966",
"frp.freefrp.net:49965",
"frp1.freefrp.net:49965",
"frp2.freefrp.net:49965",
"frp4.freefrp.net:49965",
"cn-he-plc-2.openfrp.top:4572",
"{UDP}cn-he-plc-2.openfrp.top:4572",
"frp-mom.top:22226",
"frp-man.top:36555",
"us-sjc-bgp-2.openfrp.top:36555",
"us-sjc-bgp-1.openfrp.top:36555",
"[240e:340:d856:b700:6d02:6a6a:f9fc:865b]:4572"]
},
{"Type":"SocketBridge" ,
"Listen" : "0.0.0.0:4572",
"Bridge":{
"KLALB" : "SocketBridge",
"RDP":"SocketBridge",
"HTTP":"SocketBridge",
"HTTPS":"SocketBridge",
"DEFAULT":"MinecraftSocketBridge23332"
} ,
"Connect" : {
"KLALB" : "127.0.0.1:4565",
"RDP":"127.0.0.1:3389",
"HTTP":"127.0.0.1:5212",
"HTTPS":"192.168.1.235:443",
"DEFAULT":"127.0.0.1:36555"
}
},
{"Type":"SocketBridge" ,
"Listen" : "{KLALB_Stream}[::0]:23332",
"Bridge":"MinecraftSocketBridge23332",
"Connect" : "127.0.0.1:36555"
},
{ "Type":"SocketBridge" ,
"Listen" : "{KLALB_Stream}[::0]:23333",
"Bridge":"SocketBridge",
"Connect" : "127.0.0.1:5212"
},
{"Type":"SocketBridge" ,
"Listen" : "0.0.0.0:25561",
"Bridge":{
"KLALB" : "SocketBridge",
"DEFAULT":"MinecraftSocketBridge23330"
} ,
"Connect" : {
"KLALB" : "127.0.0.1:4565",
"DEFAULT":"127.0.0.1:25562"
}
},
{"Type":"SocketBridge" ,
"Listen" : "{KLALB_Stream}[::0]:23330",
"Bridge":"MinecraftSocketBridge23330",
"Connect" : "127.0.0.1:25562"
}
]
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+29
View File
@@ -0,0 +1,29 @@
0.0.0.0:35000(RDP)->192.168.1.233:3389
0.0.0.0:35000(HTTPS)->192.168.1.233:8444
0.0.0.0:35000(HTTP)->192.168.1.233:5212
0.0.0.0:35000(SSH)->192.168.1.236:22
0.0.0.0:35000(KLALB)->{KLALBRemote}
0.0.0.0:35000->127.0.0.1:35001 //Winds服(普通线路)
{KLALBVirtual}[::0]:25565->127.0.0.1:35001 //Winds服(加速线路)
//加速线路
{KLALBRemote}->43.248.189.107:35000
{KLALBRemote}->cn-bj-bgp-3.openfrp.top:65529
{KLALBRemote}->180.76.147.250:65529
{KLALBRemote}->cn-ah-dx-1.natfrp.cloud:65529
{KLALBRemote}->cn-nn-dx-1.natfrp.cloud:65529
{KLALBRemote}->cn-wh-dx-1.natfrp.cloud:65529
{KLALBRemote}->cn-zz-bgp-10.natfrp.cloud:23330
{KLALBRemote}->cn-zz-bgp-7.natfrp.cloud:33336
{KLALBRemote}->43.143.109.64:49965
{KLALBRemote}->frp.104300.xyz:49965
{KLALBRemote}->us.afrps.cn:49966
{KLALBRemote}->hk.afrps.cn:49966
{KLALBRemote}->la.afrps.cn:49966
{KLALBRemote}->frp.freefrp.net:49965
{KLALBRemote}->frp1.freefrp.net:49965
{KLALBRemote}->frp2.freefrp.net:49965
{KLALBRemote}->frp4.freefrp.net:49965
0.0.0.0:25565->{KLALBVirtual}[171d:a999:e697:4b23:ae52:9f29:4532:e1ee]:25565
+18
View File
@@ -0,0 +1,18 @@
43.248.189.107:65529
cn-bj-bgp-3.openfrp.top:65529
180.76.147.250:65529
cn-ah-dx-1.natfrp.cloud:65529
cn-nn-dx-1.natfrp.cloud:65529
cn-wh-dx-1.natfrp.cloud:65529
cn-zz-bgp-10.natfrp.cloud:23330
cn-zz-bgp-7.natfrp.cloud:33336
43.143.109.64:49965
frp.104300.xyz:49965
us.afrps.cn:49966
hk.afrps.cn:49966
la.afrps.cn:49966
frp.freefrp.net:49965
frp1.freefrp.net:49965
frp2.freefrp.net:49965
frp4.freefrp.net:49965
cn-he-plc-2.openfrp.top:4569
View File
+8
View File
@@ -0,0 +1,8 @@
0.0.0.0:35000(RDP)->192.168.1.233:3389
0.0.0.0:35000(HTTPS)->192.168.1.233:8444
0.0.0.0:35000(HTTP)->192.168.1.233:5212
0.0.0.0:35000(SSH)->192.168.1.236:22
0.0.0.0:35000(KLALB)->{KLALBRemote}
0.0.0.0:35000->127.0.0.1:35001 //Winds服(普通线路)
{KLALBVirtual}[::0]:25565->127.0.0.1:35001 //Winds服(加速线路)
+1 -5
View File
@@ -7,6 +7,7 @@ devicename=Device name
devicedescription=Device description devicedescription=Device description
dnsserver=DNS server dnsserver=DNS server
extraroutes=Extra routes extraroutes=Extra routes
noextraroutes=No extra routes
asnumber=AS number asnumber=AS number
tcplistening=TCP listening tcplistening=TCP listening
udplistening=UDP listening udplistening=UDP listening
@@ -117,8 +118,3 @@ enablewebapi=Enable Web API
weblistenaddr=Web API listen address weblistenaddr=Web API listen address
invaildweblistenaddr=Invalid Web API listen address invaildweblistenaddr=Invalid Web API listen address
enabletun=Enable TUN adapter enabletun=Enable TUN adapter
congestionmonitor=Congestion control monitor
rttbaseline=RTT baseline
rtt=RTT
rttmax=RTT max
rttmin=RTT min
+3 -7
View File
@@ -6,7 +6,8 @@ ipv6addr=IPv6地址
devicename=设备名称 devicename=设备名称
devicedescription=设备描述 devicedescription=设备描述
dnsserver=DNS服务器 dnsserver=DNS服务器
extraroutes=额外路由 extraroutes=额外路由
noextraroutes=暂无额外路由
asnumber=AS号码 asnumber=AS号码
tcplistening=TCP监听端口 tcplistening=TCP监听端口
udplistening=UDP监听端口 udplistening=UDP监听端口
@@ -116,9 +117,4 @@ webapisettings=Web API 设置
enablewebapi=启用 Web API enablewebapi=启用 Web API
weblistenaddr=Web API 监听地址:端口 weblistenaddr=Web API 监听地址:端口
invaildweblistenaddr=无效的 Web API 监听地址:端口 invaildweblistenaddr=无效的 Web API 监听地址:端口
enabletun=启用TUN虚拟网卡 enabletun=启用TUN虚拟网卡
congestionmonitor=拥塞控制监视器
rttbaseline=往返延迟基线
rtt=往返延迟
rttmax=往返延迟上限
rttmin=往返延迟下限
@@ -5,9 +5,6 @@ import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.kne.math.Long128; import org.kne.math.Long128;
@@ -259,27 +256,4 @@ public class NTPTimestamps {
.setBit(63); .setBit(63);
} }
} }
public static Long128 median(List<Long128> values) {
Objects.requireNonNull(values);
if ( values.isEmpty()) {
throw new IllegalArgumentException("列表不能为空");
}
// 1. 创建副本并排序
List<Long128> sorted = new ArrayList<>(values);
sorted.sort(Long128::compareTo);
// 2. 计算中位数
int size = sorted.size();
if (size % 2 == 1) {
// 奇数个:取中间值
return sorted.get(size / 2);
} else {
// 偶数个:取中间两个的平均值
Long128 left = sorted.get(size / 2 - 1);
Long128 right = sorted.get(size / 2);
return left.add(right).divide(Long128.valueOf(2));
}
}
} }
@@ -136,19 +136,4 @@ public class BBRCongestionAlgorithm implements CongestionAlgorithm ,DetnetConges
return "BBR"; return "BBR";
} }
@Override
public double getUpperDelayBound() {
return MAX_GAIN;
}
@Override
public double getLowerDelayBound() {
return MIN_GAIN;
}
@Override
public double getBurstLimit() {
return burstLimit;
}
} }
@@ -88,7 +88,7 @@ public class DCTCPCongestionAlgorithm implements CongestionAlgorithm {
} }
}else { }else {
if(windowUsed*3L>=congressWindowSize) { if(windowUsed*3L>=congressWindowSize) {
congressWindowSize+=4096; congressWindowSize+=4000;
updateWindowSize(); updateWindowSize();
} }
} }
@@ -4,7 +4,4 @@ public interface DetnetCongestionAlgorithm extends CongestionAlgorithm {
public void setUpperDelayBound(double upper); public void setUpperDelayBound(double upper);
public void setLowerDelayBound(double lower); public void setLowerDelayBound(double lower);
public void setBurstLimit(double limit); public void setBurstLimit(double limit);
public double getUpperDelayBound( );
public double getLowerDelayBound();
public double getBurstLimit();
} }
@@ -2,8 +2,6 @@ package org.kne.cloud.network.congestion;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.kne.cloud.network.monitor.DelaySampler;
public class Vegas2CongestionAlgorithm implements CongestionAlgorithm,DetnetCongestionAlgorithm { public class Vegas2CongestionAlgorithm implements CongestionAlgorithm,DetnetCongestionAlgorithm {
// 可配置的时延膨胀比上下限 - Vegas2.0核心参数 // 可配置的时延膨胀比上下限 - Vegas2.0核心参数
@@ -19,7 +17,6 @@ public class Vegas2CongestionAlgorithm implements CongestionAlgorithm,DetnetCong
private Consumer<Long> windowControlConsumer; private Consumer<Long> windowControlConsumer;
private long MIN_RTTVAR = 30000000L; private long MIN_RTTVAR = 30000000L;
private volatile long RTTCurr = 1000000000L; // RTT
private volatile long RTTMin = 1000000000L; // BaseRTT private volatile long RTTMin = 1000000000L; // BaseRTT
private volatile long RTTVar = 1000000000L; private volatile long RTTVar = 1000000000L;
private volatile long RTTAvg = 1000000000L; private volatile long RTTAvg = 1000000000L;
@@ -28,6 +25,11 @@ public class Vegas2CongestionAlgorithm implements CongestionAlgorithm,DetnetCong
private volatile long RTTCount = 0; private volatile long RTTCount = 0;
private volatile long RTO = 1000000000L; private volatile long RTO = 1000000000L;
private volatile long OWDTotal = 0;
private volatile long OWDCount = 0;
private volatile long OWDAvg2 = 1000000000L; // 当前平滑OWD
private volatile long OWDMin = 1000000000L; // BaseRTT
private volatile long window = MIN_WINDOW; private volatile long window = MIN_WINDOW;
private volatile long window2 = MIN_WINDOW; private volatile long window2 = MIN_WINDOW;
@@ -56,14 +58,11 @@ public class Vegas2CongestionAlgorithm implements CongestionAlgorithm,DetnetCong
private DelaySampler dsp=new DelaySampler();
@Override @Override
public void putAck(long packetSize, long RTTns, boolean ecn) { public void putAck(long packetSize, long RTTns, long OWDup, boolean ecn) {
if(RTTns<0) { if(RTTns<0) {
throw new IllegalArgumentException("RTT:"+ RTTns+" is negative!"); throw new IllegalArgumentException("RTT:"+ RTTns+" is negative!");
} }
this.RTTCurr=RTTns;
// 处理ECN信号 - 将其视为强烈的拥塞信号 // 处理ECN信号 - 将其视为强烈的拥塞信号
if (ecn) { if (ecn) {
// 当收到ECN时,更激进地减少窗口 // 当收到ECN时,更激进地减少窗口
@@ -73,7 +72,109 @@ public class Vegas2CongestionAlgorithm implements CongestionAlgorithm,DetnetCong
} }
} }
dsp.recordDelay(RTTns); // 更新最小RTTBaseRTT
if (RTTns <= RTTMin) {
RTTMin = RTTns;
} else {
// 缓慢适应:当网络路径真正变化时,BaseRTT应能缓慢更新
// 这里使用极慢的衰减因子,只有在持续观测到更低RTT时才快速更新
RTTMin = (RTTMin * 999 + RTTns) / 1000;
}
// 更新最小OWDBaseOWD
if (OWDup <= OWDMin) {
OWDMin = OWDup;
} else {
// 缓慢适应:当网络路径真正变化时,BaseRTT应能缓慢更新
// 这里使用极慢的衰减因子,只有在持续观测到更低RTT时才快速更新
OWDMin = (OWDMin * 999 + OWDup) / 1000;
}
// 更新平滑RTT估计
RTTVar = (RTTVar * 3 + Math.abs(RTTAvg - RTTns)) / 4;
RTTAvg = (RTTAvg * 7 + RTTns) / 8;
RTO = RTTAvg + Math.max(MIN_RTTVAR, RTTVar * 4);
RTTTotal += RTTns;
RTTCount++;
OWDTotal += OWDup;
OWDCount++;
long curr = System.nanoTime();
// 每个RTT周期调整一次窗口(使用当前估计的RTT)
if (curr - RTTstartTime > Math.max(BIAS, 2*RTTMin)) {
long RTTCountx=RTTCount;
if (RTTCountx > 0) {
long currentRTT = RTTTotal / RTTCountx;
//RTTAvg2 = (RTTAvg2 * 3 + currentRTT) / 4; // 平滑当前RTT
RTTAvg2=currentRTT;
RTTTotal = 0;
RTTCount = 0;
}
long OWDCountx=OWDCount;
if (OWDCountx > 0) {
long currentOWD = OWDTotal / OWDCountx;
OWDAvg2=currentOWD;
OWDTotal = 0;
OWDCount = 0;
}
// ================== Vegas2.0核心逻辑 ==================
// 1. 计算时延膨胀比
double delayRatio = (double) OWDAvg2 / (double) Math.max(BIAS, OWDMin);
//System.out.println(delayRatio+" "+RTTAvg2/1000000.0+" "+RTTMin/1000000.0);
// 2. 基于比值的窗口调整(取代原来的基于差值的调整)
if (delayRatio > delayUpperBound) {
// 时延过高:减小窗口,减少幅度与超标程度成正比
window2 -= 4096;
} else if (delayRatio < delayLowerBound) {
// 时延过低:增大窗口,增加幅度与低于目标程度成正比
window2 += 4096;
} else {
}
// ===================================================
updateWindowSize();
RTTstartTime = curr;
}
}
@Override
public void putAck(long packetSize, long RTTns, boolean ecn) {
if(RTTns<0) {
throw new IllegalArgumentException("RTT:"+ RTTns+" is negative!");
}
// 处理ECN信号 - 将其视为强烈的拥塞信号
if (ecn) {
// 当收到ECN时,更激进地减少窗口
window2 = Math.max(0, window2 * 3 / 4);
if (windowControlConsumer != null) {
windowControlConsumer.accept(window2);
}
}
// 更新最小RTTBaseRTT
if (RTTns <= RTTMin) {
RTTMin = RTTns;
} else {
// 缓慢适应:当网络路径真正变化时,BaseRTT应能缓慢更新
// 这里使用极慢的衰减因子,只有在持续观测到更低RTT时才快速更新
RTTMin = (RTTMin * 999 + RTTns) / 1000;
}
// 更新平滑RTT估计 // 更新平滑RTT估计
@@ -89,15 +190,6 @@ public class Vegas2CongestionAlgorithm implements CongestionAlgorithm,DetnetCong
long curr = System.nanoTime(); long curr = System.nanoTime();
// 每个RTT周期调整一次窗口(使用当前估计的RTT) // 每个RTT周期调整一次窗口(使用当前估计的RTT)
if (curr - RTTstartTime > Math.max(BIAS, 2*RTTMin)) { if (curr - RTTstartTime > Math.max(BIAS, 2*RTTMin)) {
dsp.update();
long rmin=dsp.getMinDelayNanos();
if(rmin<=RTTMin) {
RTTMin=rmin;
}else {
RTTMin=Math.min(rmin, RTTMin+1000);
}
long RTTCountx=RTTCount; long RTTCountx=RTTCount;
if (RTTCountx > 0) { if (RTTCountx > 0) {
long currentRTT = RTTTotal / RTTCountx; long currentRTT = RTTTotal / RTTCountx;
@@ -114,12 +206,12 @@ public class Vegas2CongestionAlgorithm implements CongestionAlgorithm,DetnetCong
// 2. 基于比值的窗口调整(取代原来的基于差值的调整) // 2. 基于比值的窗口调整(取代原来的基于差值的调整)
if (delayRatio > delayUpperBound) { if (delayRatio > delayUpperBound) {
// 时延过高:减小窗口,减少幅度与超标程度成正比 // 时延过高:减小窗口,减少幅度与超标程度成正比
window2 =window2-4096; window2 -= 4096;
} else if (delayRatio < delayLowerBound) { } else if (delayRatio < delayLowerBound) {
// 时延过低:增大窗口,增加幅度与低于目标程度成正比 // 时延过低:增大窗口,增加幅度与低于目标程度成正比
window2 =window2+4096; window2 += 4096;
} else { } else {
//window2=window2+4096
} }
// =================================================== // ===================================================
@@ -179,11 +271,9 @@ public class Vegas2CongestionAlgorithm implements CongestionAlgorithm,DetnetCong
public long getBaseRTT() { public long getBaseRTT() {
return RTTMin; return RTTMin;
} }
public long getAvgRTT() {
return RTTAvg2;
}
public long getCurrentRTT() { public long getCurrentRTT() {
return RTTCurr; return RTTAvg2;
} }
@Override @Override
@@ -205,19 +295,4 @@ public class Vegas2CongestionAlgorithm implements CongestionAlgorithm,DetnetCong
return "Vegas2"; return "Vegas2";
} }
@Override
public double getUpperDelayBound() {
return delayUpperBound;
}
@Override
public double getLowerDelayBound() {
return delayLowerBound;
}
@Override
public double getBurstLimit() {
return burstLimit;
}
} }
@@ -1,8 +1,5 @@
package org.kne.cloud.network.ipv6; package org.kne.cloud.network.ipv6;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Supplier; import java.util.function.Supplier;
@@ -11,17 +8,10 @@ import org.kne.cloud.network.srv6.SRv6Router;
public abstract class AbstractIPv6NetworkLink implements IPv6NetworkLink { public abstract class AbstractIPv6NetworkLink implements IPv6NetworkLink {
private CopyOnWriteArraySet<IPv6LinkStateListener> listeners = new CopyOnWriteArraySet<>(); private CopyOnWriteArraySet<IPv6LinkStateListener> listeners = new CopyOnWriteArraySet<>();
private SRv6Router srv6Router;
private final boolean loopback;
public AbstractIPv6NetworkLink(boolean loopback) {
super();
this.loopback = loopback;
}
private SRv6Router srv6Router;
public SRv6Router getSrv6Router() { public SRv6Router getSrv6Router() {
return srv6Router; return srv6Router;
} }
@@ -63,49 +53,8 @@ public abstract class AbstractIPv6NetworkLink implements IPv6NetworkLink {
} }
private BiConsumer<IPv6NetworkLink, Supplier<IPv6Packet>> receiveConsumer; private BiConsumer<IPv6NetworkLink, Supplier<IPv6Packet>> receiveConsumer;
private Supplier<Long> costSupplier;
@Override @Override
public void setReceiveConsumer(BiConsumer<IPv6NetworkLink,Supplier< IPv6Packet>> receiveConsumer) { public void setReceiveConsumer(BiConsumer<IPv6NetworkLink,Supplier< IPv6Packet>> receiveConsumer) {
this.receiveConsumer = receiveConsumer; this.receiveConsumer = receiveConsumer;
} }
@Override
public boolean isLoopBack() {
return loopback;
}
public Supplier<Long> getCostSupplier() {
return costSupplier;
}
public void setCostSupplier(Supplier<Long> costSupplier) {
this.costSupplier = costSupplier;
}
@Override
public List<RouteItem> getRouteItems() {
List<RouteItem> rlist = new ArrayList<>();
if(isLoopBack()) {
for(IPv6AddressGroup group:getAddressGroups()) {
rlist.add(new RouteItem(new IPv6AddressGroup(group.getAddress(), 128),
group.getAddress(), this, RouteItem.DIRECT, 0, 0, null, "D"));
}
}
if(isUp()) {
for (Iterator<Neighbor> iteratorx = getNeighborsInfo().iterator(); iteratorx.hasNext();) {
Neighbor addresses = (Neighbor) iteratorx.next();
RouteItem ri = new RouteItem(new IPv6AddressGroup(addresses.getAddress().getAddress(), 128),
addresses.getAddress().getAddress(), this,RouteItem.SRv6_ENDXSID, 0, 128, costSupplier, "D");
rlist.add(ri);
RouteItem ris = new RouteItem(addresses.getLocator(),
addresses.getLocator().getAddress(), this, RouteItem.SRv6_ENDSID, 13, 128,
costSupplier, "D");
rlist.add(ris);
}
}
return rlist;
}
} }
@@ -61,10 +61,6 @@ public final class IPv6Address implements Comparable<IPv6Address> {
((long)(bytes[15] & 0xFF)); ((long)(bytes[15] & 0xFF));
} }
public IPv6Address(String string) throws UnknownHostException {
this((Inet6Address) Inet6Address.getByName(string));
}
/** /**
* 从两个long创建IPv6地址 * 从两个long创建IPv6地址
*/ */
@@ -67,16 +67,7 @@ public class IPv6AddressGroup implements Comparable<IPv6AddressGroup>{
prefixLength=in.read(); prefixLength=in.read();
} }
public boolean checkMatch(IPv6Address address2) { public boolean checkMatch(IPv6Address address2) {
IPv6Address cmsk= IPv6Address.createMask(prefixLength); return address2.equals(address.maskWith(IPv6Address.createMask(prefixLength)));
return address2.maskWith(cmsk).equals(address.maskWith(cmsk));
}
/**
* 返回裁剪后的路由表地址(主机位清零)。
* 例如 2001:db8:1::100/64 → 2001:db8:1::/64
*/
public IPv6AddressGroup toNetworkRoute() {
IPv6Address mask = IPv6Address.createMask(prefixLength);
IPv6Address maskedAddr = address.maskWith(mask);
return new IPv6AddressGroup(maskedAddr, prefixLength);
} }
} }
@@ -1378,7 +1378,7 @@ public class IPv6Packet extends NetworkPacket {
// 重路由计数器 // 重路由计数器
private AtomicInteger rerouteCounter = new AtomicInteger(0); private AtomicInteger rerouteCounter = new AtomicInteger(0);
public AtomicInteger getRouteCounter() { public AtomicInteger getRerouteCounter() {
return rerouteCounter; return rerouteCounter;
} }
@@ -41,7 +41,6 @@ public class IPv6TUNLoopbackNetworkLink extends AbstractIPv6NetworkLink
private Lock slok = new SpinLock(); private Lock slok = new SpinLock();
public IPv6TUNLoopbackNetworkLink(String name,IPv6AddressGroup hostAddress, int mtu, List<InetAddress> dns) throws IOException { public IPv6TUNLoopbackNetworkLink(String name,IPv6AddressGroup hostAddress, int mtu, List<InetAddress> dns) throws IOException {
super(true);
if (tun != null) if (tun != null)
throw new IllegalStateException("already open!"); throw new IllegalStateException("already open!");
try { try {
@@ -205,6 +204,13 @@ public class IPv6TUNLoopbackNetworkLink extends AbstractIPv6NetworkLink
LockSupport.unpark(tr); LockSupport.unpark(tr);
} }
@Override
public boolean isLoopBack() {
return true;
}
@Override @Override
public List<Neighbor> getNeighborsInfo() { public List<Neighbor> getNeighborsInfo() {
List<Neighbor> hs = new ArrayList<>(); List<Neighbor> hs = new ArrayList<>();
@@ -213,6 +219,10 @@ public class IPv6TUNLoopbackNetworkLink extends AbstractIPv6NetworkLink
return hs; return hs;
} }
/*
* @Override public boolean isCongress(IPv6Packet iPv6Packet,double scale) {
* return sendQueue.size() > 1000*scale; }
*/
@Override @Override
public String getName() { public String getName() {
@@ -245,4 +255,27 @@ public class IPv6TUNLoopbackNetworkLink extends AbstractIPv6NetworkLink
return List.of(hostAddress); return List.of(hostAddress);
} }
@Override
public List<RouteItem> getRouteItems() {
List<RouteItem> rlist = new ArrayList<>();
for (IPv6AddressGroup grp : getAddressGroups()) {
rlist.add(new RouteItem(new IPv6AddressGroup(grp.getAddress(), 128), grp.getAddress(), this, "Direct", 0, 1,
null, "D", true));
}
for (Iterator<Neighbor> iteratorx = getNeighborsInfo().iterator(); iteratorx.hasNext();) {
Neighbor addresses = (Neighbor) iteratorx.next();
RouteItem ri = new RouteItem(new IPv6AddressGroup(addresses.getAddress().getAddress(), 128),
addresses.getAddress().getAddress(), this, "Direct", 0, 128, null, "D", false);
rlist.add(ri);
RouteItem ris = new RouteItem(addresses.getLocator(), addresses.getLocator().getAddress(), this,
"KLALB SRv6", 13, 128, null, "D", false);
rlist.add(ris);
}
return rlist;
}
} }
@@ -32,7 +32,6 @@ public class LoopbackIPv6NetworkLink extends AbstractIPv6NetworkLink implements
//new Inet6AddressGroup(loopbackAddress, 128) new Inet6AddressGroup((Inet6Address) Inet6Address.getByName("::1"), 128) //new Inet6AddressGroup(loopbackAddress, 128) new Inet6AddressGroup((Inet6Address) Inet6Address.getByName("::1"), 128)
public LoopbackIPv6NetworkLink(List<IPv6AddressGroup> addressGroupsx,SRv6Router router) { public LoopbackIPv6NetworkLink(List<IPv6AddressGroup> addressGroupsx,SRv6Router router) {
super(true);
this.addressGroups .addAll( addressGroupsx); this.addressGroups .addAll( addressGroupsx);
} }
@@ -55,7 +54,10 @@ public class LoopbackIPv6NetworkLink extends AbstractIPv6NetworkLink implements
} }
} }
@Override
public boolean isLoopBack() {
return true;
}
@Override @Override
public List<Neighbor> getNeighborsInfo() { public List<Neighbor> getNeighborsInfo() {
@@ -91,6 +93,29 @@ public class LoopbackIPv6NetworkLink extends AbstractIPv6NetworkLink implements
return rerouteConsumer; return rerouteConsumer;
} }
@Override
public List<RouteItem> getRouteItems() {
List<RouteItem> rlist = new ArrayList<>();
for(IPv6AddressGroup group:addressGroups) {
rlist.add(new RouteItem(new IPv6AddressGroup(group.getAddress(), 128),
group.getAddress(), this, "Direct", 0, 0, null, "D", true));
}
for (Iterator<Neighbor> iteratorx = getNeighborsInfo().iterator(); iteratorx.hasNext();) {
Neighbor addresses = (Neighbor) iteratorx.next();
RouteItem ri = new RouteItem(new IPv6AddressGroup(addresses.getAddress().getAddress(), 128),
addresses.getAddress().getAddress(), this, "Direct", 0, 128, null, "D",
false);
rlist.add(ri);
RouteItem ris = new RouteItem(addresses.getLocator(),
addresses.getLocator().getAddress(), this, "KLALB SRv6", 13, 128,
null, "D", false);
rlist.add(ris);
}
return rlist;
}
public SRv6Router getRouter() { public SRv6Router getRouter() {
+10 -9
View File
@@ -4,11 +4,6 @@ import java.util.Objects;
import java.util.function.Supplier; import java.util.function.Supplier;
public class RouteItem implements Comparable<RouteItem>,Cloneable{ public class RouteItem implements Comparable<RouteItem>,Cloneable{
public static final String DIRECT="Direct";
public static final String SRv6_ENDSID="SRv6 ENDSID";
public static final String SRv6_ENDXSID="SRv6 ENDXSID";
public static final String KLALB_SRv6 = "KLALB_SRv6";
private IPv6AddressGroup destination; private IPv6AddressGroup destination;
private IPv6Address nexthop; private IPv6Address nexthop;
private IPv6NetworkLink destlink; private IPv6NetworkLink destlink;
@@ -17,6 +12,7 @@ public class RouteItem implements Comparable<RouteItem>,Cloneable{
private long cost; private long cost;
private String flag; private String flag;
private Supplier<Long> costSupplier; private Supplier<Long> costSupplier;
private boolean isLoopback;
public String getFlag() { public String getFlag() {
return flag; return flag;
} }
@@ -38,20 +34,21 @@ public class RouteItem implements Comparable<RouteItem>,Cloneable{
&& pre == other.pre && Objects.equals(proto, other.proto); && pre == other.pre && Objects.equals(proto, other.proto);
} }
public RouteItem(IPv6AddressGroup destination, IPv6Address nexthop, IPv6NetworkLink destlink, String proto, int pre, public RouteItem(IPv6AddressGroup destination, IPv6Address nexthop, IPv6NetworkLink destlink, String proto, int pre,
long cost,String flag) { long cost,String flag,boolean isLoopback) {
super(); super();
this.destination = destination.toNetworkRoute(); this.destination = destination;
this.nexthop = nexthop; this.nexthop = nexthop;
this.destlink = destlink; this.destlink = destlink;
this.proto = proto; this.proto = proto;
this.pre = pre; this.pre = pre;
this.cost = cost; this.cost = cost;
this.flag=flag; this.flag=flag;
this.isLoopback=isLoopback;
} }
public RouteItem(IPv6AddressGroup destination, IPv6Address nexthop, IPv6NetworkLink destlink, String proto, int pre, public RouteItem(IPv6AddressGroup destination, IPv6Address nexthop, IPv6NetworkLink destlink, String proto, int pre,
long cost,Supplier<Long> costSupplier,String flag) { long cost,Supplier<Long> costSupplier,String flag,boolean isLoopback) {
super(); super();
this.destination = destination.toNetworkRoute(); this.destination = destination;
this.nexthop = nexthop; this.nexthop = nexthop;
this.destlink = destlink; this.destlink = destlink;
this.proto = proto; this.proto = proto;
@@ -59,6 +56,7 @@ public class RouteItem implements Comparable<RouteItem>,Cloneable{
this.cost = cost; this.cost = cost;
this.costSupplier=costSupplier; this.costSupplier=costSupplier;
this.flag=flag; this.flag=flag;
this.isLoopback=isLoopback;
} }
public IPv6AddressGroup getDestination() { public IPv6AddressGroup getDestination() {
return destination; return destination;
@@ -75,6 +73,9 @@ public class RouteItem implements Comparable<RouteItem>,Cloneable{
public int getPre() { public int getPre() {
return pre; return pre;
} }
public boolean isLoopback() {
return isLoopback;
}
@Override @Override
public String toString() { public String toString() {
return getDestination()+"\t"+getProto()+"\t"+getPre()+"\t"+getCost()+"\t"+getFlag()+"\t"+getNexthop()+"\t"+getDestlink().getName(); return getDestination()+"\t"+getProto()+"\t"+getPre()+"\t"+getCost()+"\t"+getFlag()+"\t"+getNexthop()+"\t"+getDestlink().getName();
@@ -13,10 +13,6 @@ import org.kne.cloud.network.ipv6.RouteItem;
public abstract class AbstractControlledIPv6NetworkLink extends AbstractIPv6NetworkLink implements ControlledIPv6NetworkLink{ public abstract class AbstractControlledIPv6NetworkLink extends AbstractIPv6NetworkLink implements ControlledIPv6NetworkLink{
public AbstractControlledIPv6NetworkLink(boolean loopback) {
super(loopback);
}
} }
+1 -1
View File
@@ -12,7 +12,7 @@ public class CONST {
public static final String KLALB_S_RV6 = "KLALB_SRv6"; public static final String KLALB_S_RV6 = "KLALB_SRv6";
public static final String KLTP_VIRTUAL_SOCKET_NAME="kltp";
public static final int itemwidth = 1000; public static final int itemwidth = 1000;
public static final int linepanelheight = 41; public static final int linepanelheight = 41;
@@ -10,12 +10,14 @@ import java.net.NetworkInterface;
import java.net.SocketException; import java.net.SocketException;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Enumeration; import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
@@ -51,10 +53,11 @@ import org.kne.cloud.network.ntp.NTPContext;
import org.kne.cloud.network.ntp.NTPv4Packet; import org.kne.cloud.network.ntp.NTPv4Packet;
import org.kne.cloud.network.ntp.NTPv4Protocol; import org.kne.cloud.network.ntp.NTPv4Protocol;
import org.kne.cloud.network.ntp.NTPv4Protocol.NTPPeer; import org.kne.cloud.network.ntp.NTPv4Protocol.NTPPeer;
import org.kne.cloud.network.srv6.KLALBRoutingProtocol; import org.kne.cloud.network.srv6.KLALBRoutingProtocol;
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient; import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIServer; import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIServer;
import org.kne.cloud.network.srv6.SRv6Router; 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.srv6.SRv6RouterListener;
import org.kne.cloud.network.tcp.UDPPacket; import org.kne.cloud.network.tcp.UDPPacket;
import org.kne.cloud.network.tcp.UDPProtocolRegister; import org.kne.cloud.network.tcp.UDPProtocolRegister;
@@ -76,7 +79,50 @@ public class KLALBController {
new HashMapTimestampMonitor<UUID>(HighAccuracyClock.SYSTEM_CLOCK, "up", 100, TIME_WINDOW), new HashMapTimestampMonitor<UUID>(HighAccuracyClock.SYSTEM_CLOCK, "up", 100, TIME_WINDOW),
new HashMapTimestampMonitor<UUID>(HighAccuracyClock.SYSTEM_CLOCK, "down", 100, TIME_WINDOW)); new HashMapTimestampMonitor<UUID>(HighAccuracyClock.SYSTEM_CLOCK, "down", 100, TIME_WINDOW));
private List<MultiProtocolSocketAddress> externalEndpoints = 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<>(); private List<MultiProtocolSocketAddress> listensSocketAddress = new CopyOnWriteArrayList<>();
@@ -86,7 +132,7 @@ public class KLALBController {
private static final boolean debug=false; private static final boolean debug=false;
private static final boolean showpacket = false; private static final boolean showpacket = false;
private static final int PREFIX = 128; private static final int PREFIX = 128;// 112
private static final int DISCOVERY_PORT = 4569; private static final int DISCOVERY_PORT = 4569;
private static List<IPMulticastDiscovery> ipmd = new ArrayList<>(); private static List<IPMulticastDiscovery> ipmd = new ArrayList<>();
@@ -125,7 +171,8 @@ public class KLALBController {
@Override @Override
public void run() { public void run() {
try { try {
List<InetAddress> localaddress = networkInterfaceManager.getAllNetworkInterfaceAddress(); List<InetAddress> localaddress = networkInterfaceManager.getAllNetworkInterfaceAddress();
List<MultiProtocolSocketAddress> currentDiscoveredExternalEndpoints = new ArrayList<>();
for (InetAddress inetAddress : localaddress) { for (InetAddress inetAddress : localaddress) {
for (Iterator<MultiProtocolSocketAddress> iterator = listensSocketAddress.iterator(); iterator.hasNext();) { for (Iterator<MultiProtocolSocketAddress> iterator = listensSocketAddress.iterator(); iterator.hasNext();) {
@@ -137,11 +184,9 @@ public class KLALBController {
MultiProtocolSocketAddress bind = new MultiProtocolSocketAddress(tcpl.getProtocol(), MultiProtocolSocketAddress bind = new MultiProtocolSocketAddress(tcpl.getProtocol(),
inetAddress.getHostAddress(), tcpl.getPort()); inetAddress.getHostAddress(), tcpl.getPort());
// System.out.println(bind); // System.out.println(bind);
synchronized (externalEndpoints) { if (!currentDiscoveredExternalEndpoints.contains(bind)) {
if (!externalEndpoints.contains(bind)) { currentDiscoveredExternalEndpoints.add(bind);
externalEndpoints.add(bind); }
}
}
} }
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
// TODO 自动生成的 catch 块 // TODO 自动生成的 catch 块
@@ -149,9 +194,15 @@ public class KLALBController {
} }
} }
} }
synchronized (externalEndpoints) {
lineslock.writeLock().lock(); if (!discoveredExternalEndpoints.equals(currentDiscoveredExternalEndpoints)) {
discoveredExternalEndpoints = currentDiscoveredExternalEndpoints;
publishDiscoveredExternalEndpointLocked();
}
}
lineslock.writeLock().lock();
try { try {
@@ -298,8 +349,8 @@ public class KLALBController {
private boolean checkIsSelf(MultiProtocolSocketAddress inetAddress) throws UnknownHostException { private boolean checkIsSelf(MultiProtocolSocketAddress inetAddress) throws UnknownHostException {
return inetAddress.getInetAddress().isAnyLocalAddress() || inetAddress.getInetAddress().isLoopbackAddress() return inetAddress.getInetAddress().isAnyLocalAddress() || inetAddress.getInetAddress().isLoopbackAddress()
|| externalEndpoints.contains(inetAddress); || publishedNodeInfo.getExternalEndpoints().contains(inetAddress);
} }
private boolean checkIsSelfLocator(InetAddress inetAddress) { private boolean checkIsSelfLocator(InetAddress inetAddress) {
@@ -339,9 +390,11 @@ public class KLALBController {
try { try {
InetSocketAddress iaddr = new InetSocketAddress(neighbor.getAddress().toInet6Address(), InetSocketAddress iaddr = new InetSocketAddress(neighbor.getAddress().toInet6Address(),
KLALBRoutingProtocol.DEFAULT_PORT); KLALBRoutingProtocol.DEFAULT_PORT);
apiClient.requestNodeInfoFull(iaddr, (v) -> { apiClient.requestExternalEndpoints(iaddr, (v) -> {
ThreadTool.makeVDaemonThreadIfSupport("线路添加任务", () -> { if (v == null || v.getStatus() != NodeInfoQueryStatus.OK) return;
for (MultiProtocolSocketAddress msa : v.getOpenLines()) { ThreadTool.makeVDaemonThreadIfSupport("线路添加任务", () -> {
if (v.getExternalEndpoints() == null) return;
for (MultiProtocolSocketAddress msa : v.getExternalEndpoints()) {
// System.out.print(msa); // System.out.print(msa);
addRemoteLines(msa); addRemoteLines(msa);
} }
@@ -401,9 +454,104 @@ public class KLALBController {
} }
public List<MultiProtocolSocketAddress> getExternalEndpoints() { public List<MultiProtocolSocketAddress> getExternalEndpoints() {
return externalEndpoints; 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());
}
public IPv6AddressGroup getSelf() { public IPv6AddressGroup getSelf() {
@@ -602,10 +750,9 @@ public class KLALBController {
} }
} }
private String generateExternalEndpointsString() { private String generateExternalEndpointsString() {
StringBuilder sbd = new StringBuilder(); StringBuilder sbd = new StringBuilder();
for (Iterator<MultiProtocolSocketAddress> iterator = externalEndpoints.iterator(); iterator.hasNext();) { for (MultiProtocolSocketAddress klalbRemoteLine : publishedNodeInfo.getExternalEndpoints()) {
MultiProtocolSocketAddress klalbRemoteLine = (MultiProtocolSocketAddress) iterator.next();
sbd.append(klalbRemoteLine.toString()); sbd.append(klalbRemoteLine.toString());
sbd.append('\n'); sbd.append('\n');
} }
@@ -634,8 +781,7 @@ public class KLALBController {
private void loadSRv6ProtocolStack(IPv6AddressGroup selfx, boolean enableVirtualAdapter) { private void loadSRv6ProtocolStack(IPv6AddressGroup selfx, boolean enableVirtualAdapter) {
networkInterfaceManager.getInetAddressesExcept().add(selfx.getAddress().toInet6Address()); networkInterfaceManager.getInetAddressesExcept().add(selfx.getAddress().toInet6Address());
IPv6AddressGroup group=new IPv6AddressGroup(selfx.getAddress(),32); srv6Router = new SRv6Router(selfx, clock);
srv6Router = new SRv6Router(selfx,group, clock);
if(configItem!=null) { if(configItem!=null) {
srv6Router.setPerformanceStrategy(PerformanceStrategy.fromDescription(configItem.getPerformanceStrategy())); srv6Router.setPerformanceStrategy(PerformanceStrategy.fromDescription(configItem.getPerformanceStrategy()));
srv6Router.setDeviceName(configItem.getDeviceName()); srv6Router.setDeviceName(configItem.getDeviceName());
@@ -654,7 +800,8 @@ public class KLALBController {
if (enableTUN) { if (enableTUN) {
Thread t=new Thread(()->{ Thread t=new Thread(()->{
try { try {
IPv6TUNLoopbackNetworkLink tunlink = new IPv6TUNLoopbackNetworkLink(name.trim(),group, SRv6Router.MTU, dnsAddresses); IPv6TUNLoopbackNetworkLink tunlink = new IPv6TUNLoopbackNetworkLink(name.trim(),
new IPv6AddressGroup(srv6Router.getLocator().getAddress(), 32), SRv6Router.MTU, dnsAddresses);
tunlink.setMonitor(datatMonitor); tunlink.setMonitor(datatMonitor);
// srv6Router.getLinkTabel().add(tunlink); // srv6Router.getLinkTabel().add(tunlink);
srv6Router.getinLoopback().setFallbackLink(tunlink); srv6Router.getinLoopback().setFallbackLink(tunlink);
@@ -687,7 +834,7 @@ public class KLALBController {
// System.out.println(context); // System.out.println(context);
try { try {
NTPv4Protocol nvc = new NTPv4Protocol(context, NTPv4Protocol nvc = new NTPv4Protocol(context,
new MultiProtocolSocketAddress(iproxyname + "datagram","0.0.0.0",123)); new MultiProtocolSocketAddress(iproxyname + "_Datagram","0.0.0.0",123));
srv6Router.addSRv6RouterListener(new SRv6RouterListener() { srv6Router.addSRv6RouterListener(new SRv6RouterListener() {
@Override @Override
@@ -698,7 +845,7 @@ public class KLALBController {
for (Neighbor neighbor : nb) { for (Neighbor neighbor : nb) {
if (neighbor.getLocator() != null) if (neighbor.getLocator() != null)
sp.add(new NTPPeer( sp.add(new NTPPeer(
new MultiProtocolSocketAddress(iproxyname + "datagram", new MultiProtocolSocketAddress(iproxyname + "_Datagram",
neighbor.getLocator().getAddress().toString(), 123), neighbor.getLocator().getAddress().toString(), 123),
NTPv4Packet.NTP_SYMMETRIC_ACTIVE)); NTPv4Packet.NTP_SYMMETRIC_ACTIVE));
@@ -764,8 +911,11 @@ public class KLALBController {
} }
} }
}).start(); }).start();
loadSRv6ProtocolStack(selfg, enableVirtualAdapter); loadSRv6ProtocolStack(selfg, enableVirtualAdapter);
loadController(); if (configItem == null) {
publishNodeInfo(srv6Router.getDeviceName(), null, null, null);
}
loadController();
} }
public KLALBController(boolean enableVirtualAdapter, List<InetAddress> dnsaddr) { public KLALBController(boolean enableVirtualAdapter, List<InetAddress> dnsaddr) {
@@ -801,10 +951,9 @@ public class KLALBController {
getIpv6Router().setASN(vasn); getIpv6Router().setASN(vasn);
} }
List<MultiProtocolSocketAddress> linele = configItem.getExternalEndpoints(); List<MultiProtocolSocketAddress> linele = configItem.getExternalEndpoints();
if (linele != null) { publishNodeInfo(configItem.getDeviceName(), configItem.getDeviceDescription(), linele,
getExternalEndpoints().addAll(linele); configItem.getExtraRoutes());
}
List<MultiProtocolSocketAddress> linetoc = configItem.getAutoConnections(); List<MultiProtocolSocketAddress> linetoc = configItem.getAutoConnections();
if (linetoc != null) { if (linetoc != null) {
linetoc.forEach((aline) -> { linetoc.forEach((aline) -> {
@@ -978,8 +1127,8 @@ public class KLALBController {
public void registerToProxyTypeAs(String proxyname) { public void registerToProxyTypeAs(String proxyname) {
MultiProtocolSocketAddress.getSocketTypeRegister().put(proxyname + "stream", streamSocketType); MultiProtocolSocketAddress.getSocketTypeRegister().put(proxyname + "_Stream", streamSocketType);
MultiProtocolSocketAddress.getSocketTypeRegister().put(proxyname + "datagram", datagramSocketType); MultiProtocolSocketAddress.getSocketTypeRegister().put(proxyname + "_Datagram", datagramSocketType);
// ProxyProfileEntry.getRegister().put(proxyname, this); // ProxyProfileEntry.getRegister().put(proxyname, this);
} }
@@ -22,7 +22,8 @@ import org.kne.cloud.network.klalb.ui.UIEnv;
import org.kne.cloud.network.perf.Kperf; import org.kne.cloud.network.perf.Kperf;
import org.kne.cloud.network.perf.NodeBenchmark; import org.kne.cloud.network.perf.NodeBenchmark;
import org.kne.debug.TimeDebugger; import org.kne.debug.TimeDebugger;
import org.kne.membandboost.benchmark.*;; import org.kne.membandboost.MembandBenchmark;
import org.kne.membandboost.ObjectPoolBandwidthTest;
public class KLALBMain { public class KLALBMain {
public static KLALBStateGUI3 ksg; public static KLALBStateGUI3 ksg;
@@ -238,6 +239,10 @@ public class KLALBMain {
case "membandbenchmark": case "membandbenchmark":
MembandBenchmark.main(args); MembandBenchmark.main(args);
break; break;
case "memqueuebenchmark":
ObjectPoolBandwidthTest.main(args);
break;
//case "$$SYSTEM:": //case "$$SYSTEM:":
//System.out.println(); //System.out.println();
//break; //break;
@@ -168,11 +168,9 @@ public class KLALBProxySystem {
String vsne=kcci.getVirtualSocketName(); String vsne=kcci.getVirtualSocketName();
if(vsne!=null) { //if(vsne!=null) {
MultiProtocolSocketAddress.getSocketTypeRegister().put(vsne, klalbController.getStreamSocketType()); MultiProtocolSocketAddress.getSocketTypeRegister().put(vsne, klalbController.getStreamSocketType());
}else{ //}
MultiProtocolSocketAddress.getSocketTypeRegister().put(CONST.KLTP_VIRTUAL_SOCKET_NAME, klalbController.getStreamSocketType());
}
MultiProtocolSocketAddress tcple=kcci.getTCPListen(); MultiProtocolSocketAddress tcple=kcci.getTCPListen();
if(tcple!=null) { if(tcple!=null) {
@@ -142,18 +142,15 @@ public class KLALBRemoteLink extends AbstractControlledIPv6NetworkLink implement
} }
public KLALBRemoteLink(KLALBController controller,KLALBPacketLink kpl, IPv6AddressGroup address) { public KLALBRemoteLink(KLALBController controller,KLALBPacketLink kpl, IPv6AddressGroup address) {
super(false);
this.klalbController=controller; this.klalbController=controller;
sysclk = klalbController.getClock(); sysclk = klalbController.getClock();
this.kplink = kpl; this.kplink = kpl;
this.addressGroup = address; this.addressGroup = address;
monitor=new SpeedAndTrafficAndDelayMonitorDataImpl(sysclk); monitor=new SpeedAndTrafficAndDelayMonitorDataImpl(sysclk);
name=kpl.toString(); name=kpl.toString();
setCostSupplier( CostSupplierFactory.owdSupplier(monitor));
} }
private KLALBRemoteLink(KLALBController controller, MultiProtocolSocketAddress mpa, MultiProtocolSocketAddress bindaddr, IPv6AddressGroup address) { private KLALBRemoteLink(KLALBController controller, MultiProtocolSocketAddress mpa, MultiProtocolSocketAddress bindaddr, IPv6AddressGroup address) {
super(false);
this.klalbController=controller; this.klalbController=controller;
sysclk = klalbController.getClock(); sysclk = klalbController.getClock();
this.socketAddress = mpa; this.socketAddress = mpa;
@@ -165,7 +162,6 @@ public class KLALBRemoteLink extends AbstractControlledIPv6NetworkLink implement
} else { } else {
name=bindaddr.toString() + "" + mpa.toString(); name=bindaddr.toString() + "" + mpa.toString();
} }
setCostSupplier( CostSupplierFactory.owdSupplier( monitor));
} }
@@ -858,6 +854,10 @@ public class KLALBRemoteLink extends AbstractControlledIPv6NetworkLink implement
} }
} }
@Override
public boolean isLoopBack() {
return false;
}
BandwidthDistributer bandwidthDistributer=new BandwidthDistributer<>(); BandwidthDistributer bandwidthDistributer=new BandwidthDistributer<>();
@Override @Override
public List<Neighbor> getNeighborsInfo() { public List<Neighbor> getNeighborsInfo() {
@@ -946,28 +946,31 @@ public class KLALBRemoteLink extends AbstractControlledIPv6NetworkLink implement
return (!isClosed()) && (status.getState() == LinkStatus.UP) ; return (!isClosed()) && (status.getState() == LinkStatus.UP) ;
} }
/*@Override @Override
public List<RouteItem> getRouteItems() { public List<RouteItem> getRouteItems() {
List<RouteItem> rlist = new ArrayList<>(); List<RouteItem> rlist = new ArrayList<>();
if(isUp()) { if(isUp()) {
IPv6AddressGroup adg = addressGroup;
/*if (adg != null)
rlist.add(new RouteItem(new IPv6AddressGroup(adg.getAddress(), 128), adg.getAddress(), this, "Direct", 0, 0,
null, "D", true));*/
for (Iterator<Neighbor> iteratorx = getNeighborsInfo().iterator(); iteratorx.hasNext(); ) { for (Iterator<Neighbor> iteratorx = getNeighborsInfo().iterator(); iteratorx.hasNext(); ) {
Neighbor addresses = (Neighbor) iteratorx.next(); Neighbor addresses = (Neighbor) iteratorx.next();
RouteItem ri = new RouteItem(new IPv6AddressGroup(addresses.getAddress().getAddress(), 128), RouteItem ri = new RouteItem(new IPv6AddressGroup(addresses.getAddress().getAddress(), 128),
addresses.getAddress().getAddress(), this, RouteItem.SRv6_ENDXSID, 0, 128, CostSupplierFactory.expectedDelaySupplier((DelayMonitorData) addresses.getMonitor(), status, algorithm::getRTO), "D"); addresses.getAddress().getAddress(), this, "Direct", 0, 128, CostSupplierFactory.expectedDelaySupplier((DelayMonitorData) addresses.getMonitor(), status, algorithm::getRTO), "D", false);
rlist.add(ri); rlist.add(ri);
if (addresses.getLocator() != null) { if (addresses.getLocator() != null) {
RouteItem ris = new RouteItem(addresses.getLocator(), addresses.getLocator().getAddress(), this, RouteItem ris = new RouteItem(addresses.getLocator(), addresses.getLocator().getAddress(), this,
RouteItem.SRv6_ENDSID, 13, 128, CostSupplierFactory.expectedDelaySupplier((DelayMonitorData) addresses.getMonitor(), status, algorithm::getRTO), "D"); "KLALB SRv6", 13, 128, CostSupplierFactory.expectedDelaySupplier((DelayMonitorData) addresses.getMonitor(), status, algorithm::getRTO), "D", false);
rlist.add(ris); rlist.add(ris);
} }
} }
} }
return rlist; return rlist;
}*/ }
@Override @Override
public void setCongressCondition(Lock lock, Condition condition) { public void setCongressCondition(Lock lock, Condition condition) {
@@ -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));
}
}
}
@@ -5,9 +5,10 @@ import java.awt.datatransfer.StringSelection;
import java.awt.event.*; import java.awt.event.*;
import java.io.IOException; import java.io.IOException;
import java.net.*; import java.net.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Timer; import java.util.Objects;
import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import java.util.function.Consumer; import java.util.function.Consumer;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
@@ -26,7 +27,7 @@ import org.kne.cloud.network.ipv6.IPv6NetworkLink;
import org.kne.cloud.network.klalb.*; import org.kne.cloud.network.klalb.*;
import org.kne.cloud.network.klalb.ui.GraphPanel.GraphNode; import org.kne.cloud.network.klalb.ui.GraphPanel.GraphNode;
import org.kne.cloud.network.monitor.LinkStatus; import org.kne.cloud.network.monitor.LinkStatus;
import org.kne.cloud.network.srv6.SRv6Router; import org.kne.cloud.network.srv6.SRv6Router;
import org.kne.ui.XFrame; import org.kne.ui.XFrame;
import org.kne.ui.YScrollPane; import org.kne.ui.YScrollPane;
@@ -1061,8 +1062,19 @@ public class KLALBStateGUI3 extends XFrame {
} }
for (KLALBConfigItem item : config) { for (KLALBConfigItem item : config) {
if (item instanceof KLALBControllerConfigItem) { if (item instanceof KLALBControllerConfigItem) {
KLALBControllerConfigItem kck = (KLALBControllerConfigItem) item; 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()); kck.setLanguage(((Language) comboLang.getSelectedItem()).name());
@@ -1070,23 +1082,19 @@ public class KLALBStateGUI3 extends XFrame {
kck.setNogui(nogui.isSelected()); kck.setNogui(nogui.isSelected());
// 保存设备名称 // 保存设备名称
String dnametext = deviceNameSet.getText().trim(); String dnametext = deviceNameSet.getText().trim();
if (dnametext.equals("")) { if (dnametext.equals("")) {
kck.setDeviceName(null); newDeviceName = null;
} else { } 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("")) {
newDeviceDescription = null;
// 保存设备描述 } else {
String ddesctext = deviceDescriptionSet.getText().trim(); newDeviceDescription = ddesctext;
if (ddesctext.equals("")) { }
kck.setDeviceDescription(null);
} else {
kck.setDeviceDescription(ddesctext);
}
// 保存IPv6地址 // 保存IPv6地址
@@ -1141,7 +1149,7 @@ public class KLALBStateGUI3 extends XFrame {
} }
} }
} }
kck.setExtraRoutes(eroutes); newExtraRoutes = eroutes;
// 保存ASN // 保存ASN
String asntext = asnFieldSet.getText(); String asntext = asnFieldSet.getText();
@@ -1232,7 +1240,7 @@ public class KLALBStateGUI3 extends XFrame {
} }
} }
} }
kck.setExternalEndpoints(iaddr1); newExternalEndpoints = iaddr1;
// 保存自动连接线路表 // 保存自动连接线路表
String[] splt11 = connectLineTabelSet.getText().split("\n"); String[] splt11 = connectLineTabelSet.getText().split("\n");
@@ -1304,7 +1312,23 @@ public class KLALBStateGUI3 extends XFrame {
kck.setNagleDelayTime(nagleDelayTime.getSlider().getValue()*100000L); kck.setNagleDelayTime(nagleDelayTime.getSlider().getValue()*100000L);
kck.setLinkNagleDelayTime(linkNagleDelayTime.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);
}
}
} }
} }
@@ -1403,20 +1427,22 @@ public class KLALBStateGUI3 extends XFrame {
if (tsk5 != null) { if (tsk5 != null) {
tsk5.cancel(); tsk5.cancel();
} }
tsk5 = new TimerTask() { tsk5 = new TimerTask() {
@Override @Override
public void run() { public void run() {
if (isVisible()) { SwingUtilities.invokeLater(() -> {
graph.loadNodes(); if (isVisible()) {
for (int i = 0; i < 100; i++) { graph.loadNodes();
graph.runPhy(); 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);
} }
/** /**
@@ -1761,9 +1787,18 @@ public class KLALBStateGUI3 extends XFrame {
/** /**
* 关闭窗口并清理资源 * 关闭窗口并清理资源
*/ */
public void close() { public void close() {
setVisible(false); for (int i = 0; i < tabbedPane.getTabCount(); i++) {
if (tsk != null) { Component component = tabbedPane.getComponentAt(i);
if (component instanceof NodeInformationPanel) {
((NodeInformationPanel) component).close();
}
}
if (graph != null) {
graph.close();
}
setVisible(false);
if (tsk != null) {
tsk.cancel(); tsk.cancel();
} }
if (st != null) { if (st != null) {
@@ -18,10 +18,7 @@ import org.jfree.chart.JFreeChart;
import org.jfree.data.time.Millisecond; import org.jfree.data.time.Millisecond;
import org.jfree.data.time.TimeSeries; import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection; import org.jfree.data.time.TimeSeriesCollection;
import org.kne.cloud.network.congestion.CongestionAlgorithm;
import org.kne.cloud.network.congestion.DetnetCongestionAlgorithm;
import org.kne.cloud.network.congestion.SendPacketSlidingWindow; import org.kne.cloud.network.congestion.SendPacketSlidingWindow;
import org.kne.cloud.network.congestion.Vegas2CongestionAlgorithm;
import org.kne.cloud.network.ipv6.IPv6Address; import org.kne.cloud.network.ipv6.IPv6Address;
import org.kne.cloud.network.ipv6.IPv6AddressGroup; import org.kne.cloud.network.ipv6.IPv6AddressGroup;
import org.kne.cloud.network.ipv6.IPv6Packet; import org.kne.cloud.network.ipv6.IPv6Packet;
@@ -45,14 +42,12 @@ public class LineMonitorGUI extends XFrame {
private Timer timer1; private Timer timer1;
private Timer timer2; private Timer timer2;
private Timer timer3; private Timer timer3;
private Timer timer4;
private TimerTask tsk3; private TimerTask tsk3;
private TimerTask tsk4, tsk5,tsk6,tsk7; private TimerTask tsk4, tsk5,tsk6;
private TimerTask d1; private TimerTask d1;
private TimerTask d2; private TimerTask d2;
private TimerTask d3; private TimerTask d3;
private TimerTask d4;
private TimeSeries spdup = new TimeSeries(UIEnv.getRsb().getString("uploadspeed")); private TimeSeries spdup = new TimeSeries(UIEnv.getRsb().getString("uploadspeed"));
private TimeSeries spddown = new TimeSeries(UIEnv.getRsb().getString("downloadspeed")); private TimeSeries spddown = new TimeSeries(UIEnv.getRsb().getString("downloadspeed"));
@@ -67,21 +62,13 @@ public class LineMonitorGUI extends XFrame {
private TimeSeries queueused = new TimeSeries(UIEnv.getRsb().getString("queueused")); private TimeSeries queueused = new TimeSeries(UIEnv.getRsb().getString("queueused"));
private TimeSeries buffermax = new TimeSeries(UIEnv.getRsb().getString("maxbuffer")); private TimeSeries buffermax = new TimeSeries(UIEnv.getRsb().getString("maxbuffer"));
private TimeSeries rttbaseline = new TimeSeries(UIEnv.getRsb().getString("rttbaseline"));
private TimeSeries rtt = new TimeSeries(UIEnv.getRsb().getString("rtt"));
private TimeSeries rttmin=new TimeSeries(UIEnv.getRsb().getString("rttmin"));
private TimeSeries rttmax=new TimeSeries(UIEnv.getRsb().getString("rttmax"));
private KLALBRemoteLink tr; private KLALBRemoteLink tr;
private JFreeChart jfc; private JFreeChart jfc;
private JFreeChart jfce; private JFreeChart jfce;
private JFreeChart jbfc; private JFreeChart jbfc;
private JFreeChart jbfcv;
private JLabel spdp; private JLabel spdp;
private JLabel delp; private JLabel delp;
private JLabel sbpdp; private JLabel sbpdp;
private JLabel sbpdpv;
private long timeRange = 5000; private long timeRange = 5000;
private long[] values = new long[] { 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000 }; private long[] values = new long[] { 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000 };
private JPanel panel_1; private JPanel panel_1;
@@ -168,22 +155,6 @@ public class LineMonitorGUI extends XFrame {
jp.add(sbpdp); jp.add(sbpdp);
TimeSeriesCollection tbsc1 = new TimeSeriesCollection();
tbsc1.addSeries(rttbaseline);
tbsc1.addSeries(rtt);
tbsc1.addSeries(rttmax);
tbsc1.addSeries(rttmin);
jbfcv = ChartFactory.createTimeSeriesChart(UIEnv.getRsb().getString("congestionmonitor"),
UIEnv.getRsb().getString("time") + "(s)", UIEnv.getRsb().getString("delay") + "(ms)", tbsc1);
jbfcv.getXYPlot().setBackgroundPaint(Color.BLACK);
jbfcv.getXYPlot().getRenderer().setSeriesPaint(0, Color.CYAN);
jbfcv.getXYPlot().getRenderer().setSeriesPaint(1, Color.YELLOW);
jbfcv.getXYPlot().getRenderer().setSeriesPaint(2, Color.RED);
jbfcv.getXYPlot().getRenderer().setSeriesPaint(3, Color.GREEN);
changeFont(jbfcv);
sbpdpv = new JLabel();
sbpdpv.setBorder(new LineBorder(Color.DARK_GRAY));
jp.add(sbpdpv);
@@ -291,14 +262,6 @@ public class LineMonitorGUI extends XFrame {
buffermax.setMaximumItemCount((int) timeRange/5); buffermax.setMaximumItemCount((int) timeRange/5);
queueused.setMaximumItemCount((int) timeRange/5); queueused.setMaximumItemCount((int) timeRange/5);
} }
synchronized (jbfcv) {
jbfcv.getXYPlot().getDomainAxis().setFixedAutoRange(timeRange);
bufferused.setMaximumItemCount((int) timeRange/5);
buffermax.setMaximumItemCount((int) timeRange/5);
queueused.setMaximumItemCount((int) timeRange/5);
}
windowSelect.setText(timeRange / 1000 + "s"); windowSelect.setText(timeRange / 1000 + "s");
} }
@@ -365,24 +328,6 @@ public class LineMonitorGUI extends XFrame {
} }
} }
} }
private void recordCongestionData() {
if (isVisible()) {
Millisecond ms = new Millisecond(
new Date(tr.getMonitor().getClock().getCurrentTimeMillis()));
synchronized (jbfcv) {
CongestionAlgorithm window=tr.getWindow().getAlgorithm();
if(window !=null&&window instanceof DetnetCongestionAlgorithm) {
Vegas2CongestionAlgorithm deta=(Vegas2CongestionAlgorithm) window;
double baseRTT=deta.getBaseRTT();
rttbaseline.addOrUpdate(ms, baseRTT / 1000000.0);
rtt.addOrUpdate(ms, deta.getCurrentRTT() / 1000000.0);
rttmax.addOrUpdate(ms, baseRTT*deta.getUpperDelayBound() / 1000000.0);
rttmin.addOrUpdate(ms, baseRTT*deta.getLowerDelayBound() / 1000000.0);
}
}
}
}
@Override @Override
public void setVisible(boolean b) { public void setVisible(boolean b) {
if (b) { if (b) {
@@ -391,7 +336,6 @@ public class LineMonitorGUI extends XFrame {
timer1 = new Timer(); timer1 = new Timer();
timer2 = new Timer(); timer2 = new Timer();
timer3 = new Timer(); timer3 = new Timer();
timer4 = new Timer();
tsk3 = new TimerTask() { tsk3 = new TimerTask() {
@Override @Override
@@ -421,13 +365,6 @@ public class LineMonitorGUI extends XFrame {
recordBufferData(); recordBufferData();
} }
}; };
tsk7 = new TimerTask() {
@Override
public void run() {
recordCongestionData();
}
};
d1 = new TimerTask() { d1 = new TimerTask() {
@Override @Override
@@ -469,20 +406,6 @@ public class LineMonitorGUI extends XFrame {
} }
} }
}; };
d4 = new TimerTask() {
@Override
public void run() {
if (isVisible()) {
synchronized (jbfcv) {
ImageIcon i3 = new ImageIcon(
jbfcv.createBufferedImage(sbpdpv.getWidth(), sbpdpv.getHeight()));
sbpdpv.setIcon(i3);
}
}
}
};
timer.scheduleAtFixedRate(tsk3, 25, 25); timer.scheduleAtFixedRate(tsk3, 25, 25);
timer1.scheduleAtFixedRate(tsk4, 10, 10); timer1.scheduleAtFixedRate(tsk4, 10, 10);
timer1.scheduleAtFixedRate(d1, 25, 25); timer1.scheduleAtFixedRate(d1, 25, 25);
@@ -490,8 +413,6 @@ public class LineMonitorGUI extends XFrame {
timer2.scheduleAtFixedRate(d2, 25, 25); timer2.scheduleAtFixedRate(d2, 25, 25);
timer3.scheduleAtFixedRate(tsk6, 10, 10); timer3.scheduleAtFixedRate(tsk6, 10, 10);
timer3.scheduleAtFixedRate(d3, 25, 25); timer3.scheduleAtFixedRate(d3, 25, 25);
timer4.scheduleAtFixedRate(tsk7, 25, 25);
timer4.scheduleAtFixedRate(d4, 25, 25);
} }
} else { } else {
@@ -501,18 +422,12 @@ public class LineMonitorGUI extends XFrame {
tsk4.cancel(); tsk4.cancel();
if (tsk5 != null) if (tsk5 != null)
tsk5.cancel(); tsk5.cancel();
if (tsk6 != null)
tsk6.cancel();
if (tsk7 != null)
tsk7.cancel();
if (d1 != null) if (d1 != null)
d1.cancel(); d1.cancel();
if (d2 != null) if (d2 != null)
d2.cancel(); d2.cancel();
if (d3 != null) if (d3 != null)
d3.cancel(); d3.cancel();
if (d4 != null)
d4.cancel();
if (timer != null) if (timer != null)
timer.cancel(); timer.cancel();
if (timer1 != null) if (timer1 != null)
@@ -523,9 +438,6 @@ public class LineMonitorGUI extends XFrame {
if (timer3 != null) if (timer3 != null)
timer3.cancel(); timer3.cancel();
if (timer4 != null)
timer4.cancel();
timer = null; timer = null;
} }
super.setVisible(b); super.setVisible(b);
@@ -8,28 +8,58 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.awt.event.MouseListener; import java.awt.event.MouseListener;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.function.BiConsumer;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import org.kne.cloud.network.MultiProtocolSocketAddress; import org.kne.cloud.network.MultiProtocolSocketAddress;
import org.kne.cloud.network.ipv6.IPv6Address; import org.kne.cloud.network.ipv6.IPv6Address;
import org.kne.cloud.network.klalb.KLALBController; import org.kne.cloud.network.klalb.KLALBController;
import org.kne.cloud.network.srv6.KLALBRoutingProtocol.LinkDirection; 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 { public class NetworkGraphPanel extends GraphPanel {
private KLALBController controller; private KLALBController controller;
private KLALBStateGUI3 klbgui; 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) { public NetworkGraphPanel(KLALBController controller,KLALBStateGUI3 klbgui) {
super(); super();
this.controller = controller; this.controller = controller;
this.klbgui=klbgui; 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) { public NetworkGraphPanel(KLALBController kc) {
@@ -82,8 +112,8 @@ public class NetworkGraphPanel extends GraphPanel {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
KperfGUI kpfg=new KperfGUI(); KperfGUI kpfg=new KperfGUI();
kpfg.setVisible(true); kpfg.setVisible(true);
MultiProtocolSocketAddress source=new MultiProtocolSocketAddress("kltp",controller.getIpv6Router().getLocator().getAddress().toString(),0); MultiProtocolSocketAddress source=new MultiProtocolSocketAddress("KLALB_Stream",controller.getIpv6Router().getLocator().getAddress().toString(),0);
MultiProtocolSocketAddress target=new MultiProtocolSocketAddress("kltp", address.toString(), 4564); MultiProtocolSocketAddress target=new MultiProtocolSocketAddress("KLALB_Stream", address.toString(), 4564);
kpfg.setTarget(target,source); kpfg.setTarget(target,source);
} }
}); });
@@ -153,17 +183,145 @@ public class NetworkGraphPanel extends GraphPanel {
} }
/** /**
* 节点标签:显示广播得知的设备名称(若有)+IP地址 * 节点标签:显示 Tiny API 查询的设备名称(若有)+IP地址
*/ */
private String getNodeText(IPv6Address address) { private String getNodeText(IPv6Address address) {
StringBuilder sb=new StringBuilder(); StringBuilder sb=new StringBuilder();
String dname=controller.getIpv6Router().getKlalbRouteProtol().getDeviceName(address); String dname;
if(dname!=null) synchronized (nodeInfoLock) {
sb.append(dname).append('\n'); dname = nodeNameCache.get(address);
sb.append(InetGraphNode.getText(address)); }
return sb.toString(); if(dname!=null&&!dname.isEmpty())
} sb.append(dname).append('\n');
sb.append(InetGraphNode.getText(address));
return sb.toString();
}
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 double nsPerPixel=5000L;
private class InetGraphEdgeGroup extends GraphEdgeGroup{ private class InetGraphEdgeGroup extends GraphEdgeGroup{
public InetGraphEdgeGroup(GraphNode nodeA, GraphNode nodeB) { public InetGraphEdgeGroup(GraphNode nodeA, GraphNode nodeB) {
@@ -181,6 +339,8 @@ public class NetworkGraphPanel extends GraphPanel {
} }
protected void loadNodes() { protected void loadNodes() {
Map<IPv6Address, Long> addr= controller.getIpv6Router().getKlalbRouteProtol().getAddresses(); Map<IPv6Address, Long> addr= controller.getIpv6Router().getKlalbRouteProtol().getAddresses();
IPv6Address localAddress = controller.getIpv6Router().getLocator().getAddress();
cacheLocalNodeName();
Set<IPv6Address> ks=addr.keySet(); Set<IPv6Address> ks=addr.keySet();
for (Iterator<IPv6Address> iterator = ks.iterator(); iterator.hasNext();) { for (Iterator<IPv6Address> iterator = ks.iterator(); iterator.hasNext();) {
IPv6Address inet6Address = (IPv6Address) iterator.next(); IPv6Address inet6Address = (IPv6Address) iterator.next();
@@ -188,12 +348,15 @@ public class NetworkGraphPanel extends GraphPanel {
Vector2 v2pos=super.getRandomPos(); Vector2 v2pos=super.getRandomPos();
getNodes().put(inet6Address,new InetGraphNode(inet6Address,Color.BLACK,v2pos.x,v2pos.y,inet6Address.equals(controller.getIpv6Router().getLocator().getAddress()))); 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(); Set<IPv6Address> kns=getNodes().keySet();
for (Iterator<IPv6Address> iterator = kns.iterator(); iterator.hasNext();) { for (Iterator<IPv6Address> iterator = kns.iterator(); iterator.hasNext();) {
IPv6Address inet6Address = (IPv6Address) iterator.next(); IPv6Address inet6Address = (IPv6Address) iterator.next();
if(!addr.containsKey(inet6Address)) { if(!addr.containsKey(inet6Address)) {
iterator.remove(); iterator.remove();
removeNodeInfoState(inet6Address);
}else { }else {
((InetGraphNode)getNodes().get(inet6Address)).updateLabel(); ((InetGraphNode)getNodes().get(inet6Address)).updateLabel();
} }
@@ -4,9 +4,9 @@ import java.awt.BorderLayout;
import java.awt.Image; import java.awt.Image;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.StringSelection;
import java.io.IOException; import java.net.InetSocketAddress;
import java.net.InetSocketAddress; import java.util.Collections;
import java.util.List; import java.util.List;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.ImageIcon; import javax.swing.ImageIcon;
@@ -15,33 +15,42 @@ import javax.swing.JButton;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.JTextArea; import javax.swing.JTextArea;
import javax.swing.ListSelectionModel; import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import org.kne.cloud.klalb.uitool.XDefaultListModel; import org.kne.cloud.klalb.uitool.XDefaultListModel;
import org.kne.cloud.network.MultiProtocolSocketAddress; import org.kne.cloud.network.MultiProtocolSocketAddress;
import org.kne.cloud.network.ipv6.IPv6Address; import org.kne.cloud.network.ipv6.IPv6Address;
import org.kne.cloud.network.klalb.KLALBController; import org.kne.cloud.network.klalb.KLALBController;
import org.kne.cloud.network.srv6.KLALBRoutingProtocol; import org.kne.cloud.network.klalb.NodeInfoQueryCoordinator;
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient; 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.JTabbedPane;
import javax.swing.JList; import javax.swing.JList;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.awt.event.MouseListener; import java.awt.event.MouseListener;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.util.concurrent.atomic.AtomicLong;
public class NodeInformationPanel extends JPanel { public class NodeInformationPanel extends JPanel {
private IPv6Address address; private IPv6Address address;
private KLALBRoutingProtocolAPIClient client; private KLALBRoutingProtocolAPIClient client;
private NodeInfoQueryCoordinator queryCoordinator;
private KLALBController controller; private KLALBController controller;
private Image image; private Image image;
private XDefaultListModel<MultiProtocolSocketAddress> listModel=new XDefaultListModel<>(); 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() { public KLALBController getController() {
return controller; return controller;
} }
@@ -73,16 +82,20 @@ public class NodeInformationPanel extends JPanel {
overviewArea.setWrapStyleWord(true); overviewArea.setWrapStyleWord(true);
overviewArea.setFont(UIEnv.getFont().deriveFont(14.0f)); overviewArea.setFont(UIEnv.getFont().deriveFont(14.0f));
overviewArea.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); overviewArea.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
String dname=controller.getIpv6Router().getKlalbRouteProtol().getDeviceName(address); overviewArea.setText(buildOverviewText(null, null));
String ddesc=null; panel.add(new JScrollPane(overviewArea), BorderLayout.CENTER);
if(address.equals(controller.getIpv6Router().getLocator().getAddress())&&controller.getConfigItem()!=null) {
ddesc=controller.getConfigItem().getDeviceDescription(); JPanel extraRoutesPanel = new JPanel(new BorderLayout());
if(ddesc!=null&&ddesc.isEmpty()) { JList<String> extraRoutesList = new JList<String>(extraRoutesModel);
ddesc=null; extraRoutesList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
} extraRoutesList.setFont(UIEnv.getFont().deriveFont(14.0f));
} extraRoutesPanel.add(new JScrollPane(extraRoutesList), BorderLayout.CENTER);
overviewArea.setText(buildOverviewText(dname, ddesc)); javax.swing.JLabel extraRoutesEmptyLabel=new javax.swing.JLabel(UIEnv.getRsb().getString("noextraroutes"));
panel.add(new JScrollPane(overviewArea), BorderLayout.CENTER); 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(); JPanel panel_1 = new JPanel();
panel_1.setLayout(new BorderLayout(0, 0)); panel_1.setLayout(new BorderLayout(0, 0));
@@ -163,28 +176,110 @@ public class NodeInformationPanel extends JPanel {
panel_1.add(btnNewButton, BorderLayout.SOUTH); panel_1.add(btnNewButton, BorderLayout.SOUTH);
client=new KLALBRoutingProtocolAPIClient(controller.getIpv6Router().getKlalbRouteProtol()); KLALBRoutingProtocol routingProtocol=controller.getIpv6Router().getKlalbRouteProtol();
try { client=new KLALBRoutingProtocolAPIClient(routingProtocol);
client.requestNodeInfoFull(new InetSocketAddress( address.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT), (info)->{ queryCoordinator=new NodeInfoQueryCoordinator(client);
listModel.clear(); nodeInfoUpdateListener=(updatedAddress, flags) -> {
for (MultiProtocolSocketAddress multiProtocolSocketAddress : info.getOpenLines()) { if(active && address.equals(updatedAddress)
listModel.addElement(multiProtocolSocketAddress); && (flags & org.kne.cloud.network.srv6.RouterInfoPacket.NODE_INFO_FULL_UPDATE_REQUIRED) != 0) {
} requestFullInfo(overviewArea, extraRoutesEmptyLabel);
// 用对端返回的设备名称/描述更新概览(旧版本节点无该字段时保留原显示) }
String dn=info.getDeviceName(); };
if(dn==null||dn.isEmpty()) { routingProtocol.addNodeInfoUpdateListener(nodeInfoUpdateListener);
dn=controller.getIpv6Router().getKlalbRouteProtol().getDeviceName(address); requestFullInfo(overviewArea, extraRoutesEmptyLabel);
} }
String dd=info.getDeviceDescription();
if(dd!=null&&dd.isEmpty()) { private void requestFullInfo(JTextArea overviewArea, javax.swing.JLabel extraRoutesEmptyLabel) {
dd=null; final long generation=fullInfoGeneration.incrementAndGet();
} if (controller.getIpv6Router().getLocator().getAddress().equals(address)) {
overviewArea.setText(buildOverviewText(dn, dd)); KLALBController.PublishedNodeInfo published=controller.getPublishedNodeInfo();
}); applyNodeInfo(overviewArea, extraRoutesEmptyLabel, published.getDeviceName(),
} catch (IOException e) { published.getDeviceDescription(), published.getExternalEndpoints(), published.getExtraRoutes());
e.printStackTrace(); 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) { private String buildOverviewText(String dname,String ddesc) {
StringBuilder sb=new StringBuilder(); StringBuilder sb=new StringBuilder();
@@ -7,6 +7,7 @@ import java.nio.file.*;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import com.sun.net.httpserver.*; import com.sun.net.httpserver.*;
import com.google.gson.*; import com.google.gson.*;
@@ -19,12 +20,14 @@ import org.kne.cloud.network.ipv6.IPv6NetworkLink;
import org.kne.cloud.network.ipv6.RouteItem; import org.kne.cloud.network.ipv6.RouteItem;
import org.kne.cloud.network.klalb.*; import org.kne.cloud.network.klalb.*;
import org.kne.cloud.network.monitor.LinkStatus; import org.kne.cloud.network.monitor.LinkStatus;
import org.kne.cloud.network.srv6.KLALBNodeInformation;
import org.kne.cloud.network.srv6.KLALBRoutingProtocol; import org.kne.cloud.network.srv6.KLALBRoutingProtocol;
import org.kne.cloud.network.srv6.KLALBRoutingProtocol.LinkDirection; import org.kne.cloud.network.srv6.KLALBRoutingProtocol.LinkDirection;
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient; import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
import org.kne.cloud.network.srv6.NodeInfoQueryStatus;
import org.kne.cloud.network.srv6.NodeProfile;
import org.kne.cloud.network.srv6.NeighborInfo; import org.kne.cloud.network.srv6.NeighborInfo;
import org.kne.cloud.network.srv6.RouterInfo; import org.kne.cloud.network.srv6.RouterInfo;
import org.kne.cloud.network.srv6.RouterInfoPacket;
import org.kne.cloud.network.srv6.SRv6Router; import org.kne.cloud.network.srv6.SRv6Router;
/** /**
@@ -35,14 +38,12 @@ public class KLALBWebServer {
private final KLALBProxySystem proxySystem; private final KLALBProxySystem proxySystem;
private final MultiProtocolSocketAddress listen; private final MultiProtocolSocketAddress listen;
private HttpServer server; private HttpServer server;
private ExecutorService httpExecutor;
private final Gson gson; private final Gson gson;
private final ScheduledExecutorService sseExecutor = Executors.newSingleThreadScheduledExecutor(r -> { private ScheduledExecutorService sseExecutor = createSseExecutor();
Thread t = new Thread(r, "KLALB-Web-SSE");
t.setDaemon(true);
return t;
});
private final Set<HttpExchange> sseClients = Collections.newSetFromMap(new ConcurrentHashMap<>()); private final Set<HttpExchange> sseClients = Collections.newSetFromMap(new ConcurrentHashMap<>());
private final AtomicBoolean running = new AtomicBoolean(false); private final AtomicBoolean running = new AtomicBoolean(false);
private final Object configUpdateLock = new Object();
public KLALBWebServer(KLALBProxySystem proxySystem, int port) { public KLALBWebServer(KLALBProxySystem proxySystem, int port) {
this(proxySystem, new MultiProtocolSocketAddress("http", "0.0.0.0", port)); this(proxySystem, new MultiProtocolSocketAddress("http", "0.0.0.0", port));
@@ -52,15 +53,36 @@ public class KLALBWebServer {
this.proxySystem = proxySystem; this.proxySystem = proxySystem;
this.listen = listen; this.listen = listen;
this.gson = proxySystem.getGson(); this.gson = proxySystem.getGson();
this.nodeInfoUpdateListener = (address, flags) -> {
if ((flags & RouterInfoPacket.NODE_INFO_FULL_UPDATE_REQUIRED) != 0) {
nodeInfoRevisions.merge(address, 1L, Long::sum);
}
if ((flags & RouterInfoPacket.NODE_INFO_TINY_UPDATE_REQUIRED) != 0) {
long lifecycleGeneration;
synchronized (this) {
lifecycleGeneration = nodeInfoLifecycleGeneration;
}
invalidateTinyDeviceName(address);
KLALBController kc = this.proxySystem.getKlalbController();
if (kc != null && kc.getIpv6Router() != null
&& kc.getIpv6Router().getKlalbRouteProtol() != null) {
refreshTinyDeviceNameAsync(kc, address, lifecycleGeneration);
}
}
};
} }
public synchronized void start() throws IOException { public synchronized void start() throws IOException {
if (running.get()) return; if (running.get()) return;
if (sseExecutor == null || sseExecutor.isShutdown()) {
sseExecutor = createSseExecutor();
}
InetSocketAddress socketAddress = "0.0.0.0".equals(listen.getHost()) InetSocketAddress socketAddress = "0.0.0.0".equals(listen.getHost())
? new InetSocketAddress(listen.getPort()) : listen.getSocketAddress(); ? new InetSocketAddress(listen.getPort()) : listen.getSocketAddress();
server = HttpServer.create(socketAddress, 0); server = HttpServer.create(socketAddress, 0);
server.setExecutor(Executors.newVirtualThreadPerTaskExecutor()); ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
server.setExecutor(executor);
// API Contexts // API Contexts
server.createContext("/api/status", this::handleStatus); server.createContext("/api/status", this::handleStatus);
@@ -76,7 +98,13 @@ public class KLALBWebServer {
// Static Files / SPA Fallback Handler // Static Files / SPA Fallback Handler
server.createContext("/", this::handleStatic); server.createContext("/", this::handleStatic);
server.start(); try {
server.start();
httpExecutor = executor;
} catch (RuntimeException e) {
executor.shutdownNow();
throw e;
}
running.set(true); running.set(true);
startSseBroadcaster(); startSseBroadcaster();
@@ -84,7 +112,11 @@ public class KLALBWebServer {
} }
public synchronized void stop() { public synchronized void stop() {
if (!running.get()) return; if (!running.get()) {
closeHttpExecutor();
closeNodeInfoClient();
return;
}
running.set(false); running.set(false);
sseExecutor.shutdownNow(); sseExecutor.shutdownNow();
for (HttpExchange client : sseClients) { for (HttpExchange client : sseClients) {
@@ -97,9 +129,18 @@ public class KLALBWebServer {
server.stop(1); server.stop(1);
server = null; server = null;
} }
closeHttpExecutor();
closeNodeInfoClient();
System.out.println("KLALB Web Dashboard stopped."); System.out.println("KLALB Web Dashboard stopped.");
} }
private void closeHttpExecutor() {
if (httpExecutor != null) {
httpExecutor.shutdownNow();
httpExecutor = null;
}
}
public boolean isRunning() { public boolean isRunning() {
return running.get(); return running.get();
} }
@@ -456,14 +497,159 @@ public class KLALBWebServer {
} }
private KLALBRoutingProtocolAPIClient nodeInfoClient; private KLALBRoutingProtocolAPIClient nodeInfoClient;
private static final long TINY_NAME_REQUEST_TIMEOUT_MS = 3000L;
private final ConcurrentMap<IPv6Address, String> tinyDeviceNameCache = new ConcurrentHashMap<>();
private final ConcurrentMap<IPv6Address, Long> tinyDeviceNameCacheTimes = new ConcurrentHashMap<>();
private final Set<IPv6Address> tinyNameRequestsInFlight = ConcurrentHashMap.newKeySet();
private final ConcurrentMap<IPv6Address, Long> tinyNameRequestTimes = new ConcurrentHashMap<>();
private final ConcurrentMap<IPv6Address, Long> tinyNameRequestGenerations = new ConcurrentHashMap<>();
private final ConcurrentMap<IPv6Address, Long> nodeInfoRevisions = new ConcurrentHashMap<>();
private final Object tinyNameStateLock = new Object();
private static final long TINY_NAME_CACHE_TTL_MS = 60000L;
private long nextTinyNameRequestGeneration;
private KLALBRoutingProtocol nodeInfoRoutingProtocol;
private final BiConsumer<IPv6Address, Integer> nodeInfoUpdateListener;
private long nodeInfoLifecycleGeneration;
private static ScheduledExecutorService createSseExecutor() {
return Executors.newSingleThreadScheduledExecutor(r -> {
Thread t = new Thread(r, "KLALB-Web-SSE");
t.setDaemon(true);
return t;
});
}
private synchronized KLALBRoutingProtocolAPIClient getNodeInfoClient(KLALBController kc) { private synchronized KLALBRoutingProtocolAPIClient getNodeInfoClient(KLALBController kc) {
return getNodeInfoClient(kc, -1L);
}
private synchronized KLALBRoutingProtocolAPIClient getNodeInfoClient(KLALBController kc,
long expectedLifecycleGeneration) {
if (!running.get() || (expectedLifecycleGeneration >= 0
&& expectedLifecycleGeneration != nodeInfoLifecycleGeneration)
|| kc == null || kc.getIpv6Router() == null
|| kc.getIpv6Router().getKlalbRouteProtol() == null) {
return null;
}
if (nodeInfoClient == null) { if (nodeInfoClient == null) {
nodeInfoClient = new KLALBRoutingProtocolAPIClient(kc.getIpv6Router().getKlalbRouteProtol()); nodeInfoRoutingProtocol = kc.getIpv6Router().getKlalbRouteProtol();
nodeInfoClient = new KLALBRoutingProtocolAPIClient(nodeInfoRoutingProtocol);
nodeInfoRoutingProtocol.addNodeInfoUpdateListener(nodeInfoUpdateListener);
} }
return nodeInfoClient; return nodeInfoClient;
} }
private synchronized void closeNodeInfoClient() {
nodeInfoLifecycleGeneration++;
if (nodeInfoRoutingProtocol != null) {
nodeInfoRoutingProtocol.removeNodeInfoUpdateListener(nodeInfoUpdateListener);
nodeInfoRoutingProtocol = null;
}
if (nodeInfoClient != null) {
nodeInfoClient.close();
nodeInfoClient = null;
}
synchronized (tinyNameStateLock) {
tinyDeviceNameCache.clear();
tinyDeviceNameCacheTimes.clear();
tinyNameRequestsInFlight.clear();
tinyNameRequestTimes.clear();
tinyNameRequestGenerations.clear();
}
}
private String getTinyDeviceName(KLALBController kc, IPv6Address addr) {
return getTinyDeviceName(kc, addr, -1L);
}
private String getTinyDeviceName(KLALBController kc, IPv6Address addr, long lifecycleGeneration) {
String cachedName;
long requestStartedAt = 0L;
long requestGeneration;
synchronized (tinyNameStateLock) {
cachedName = tinyDeviceNameCache.get(addr);
Long cachedAt = tinyDeviceNameCacheTimes.get(addr);
long now = System.currentTimeMillis();
if (cachedName != null && cachedAt != null && now - cachedAt < TINY_NAME_CACHE_TTL_MS) return cachedName;
tinyDeviceNameCache.remove(addr);
tinyDeviceNameCacheTimes.remove(addr);
if (tinyNameRequestsInFlight.contains(addr)) {
Long requestedAt = tinyNameRequestTimes.get(addr);
if (requestedAt != null && now - requestedAt < TINY_NAME_REQUEST_TIMEOUT_MS) {
return "";
}
tinyNameRequestsInFlight.remove(addr);
tinyNameRequestTimes.remove(addr);
}
if (!tinyNameRequestsInFlight.add(addr)) return "";
requestStartedAt = now;
tinyNameRequestTimes.put(addr, requestStartedAt);
requestGeneration = ++nextTinyNameRequestGeneration;
tinyNameRequestGenerations.put(addr, requestGeneration);
}
final long requestGenerationToken = requestGeneration;
try {
KLALBRoutingProtocolAPIClient client = lifecycleGeneration < 0
? getNodeInfoClient(kc) : getNodeInfoClient(kc, lifecycleGeneration);
if (client == null) {
synchronized (tinyNameStateLock) {
if (Long.valueOf(requestGenerationToken).equals(tinyNameRequestGenerations.get(addr))) {
tinyNameRequestsInFlight.remove(addr);
tinyNameRequestTimes.remove(addr);
tinyNameRequestGenerations.remove(addr);
}
}
return "";
}
client.requestNodeProfile(
new InetSocketAddress(addr.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT),
profile -> {
synchronized (tinyNameStateLock) {
Long activeRequestGeneration = tinyNameRequestGenerations.get(addr);
if (!Long.valueOf(requestGenerationToken).equals(activeRequestGeneration)) return;
tinyDeviceNameCache.put(addr,
profile != null && profile.getDeviceName() != null ? profile.getDeviceName() : "");
tinyDeviceNameCacheTimes.put(addr, System.currentTimeMillis());
tinyNameRequestsInFlight.remove(addr);
tinyNameRequestTimes.remove(addr);
tinyNameRequestGenerations.remove(addr);
}
});
} catch (IOException e) {
synchronized (tinyNameStateLock) {
Long activeRequestGeneration = tinyNameRequestGenerations.get(addr);
if (Long.valueOf(requestGenerationToken).equals(activeRequestGeneration)) {
tinyNameRequestsInFlight.remove(addr);
tinyNameRequestTimes.remove(addr);
tinyNameRequestGenerations.remove(addr);
}
}
}
return "";
}
private void refreshTinyDeviceNameAsync(KLALBController kc, IPv6Address address, long lifecycleGeneration) {
CompletableFuture.runAsync(() -> {
synchronized (this) {
if (!running.get() || nodeInfoLifecycleGeneration != lifecycleGeneration) return;
getTinyDeviceName(kc, address, lifecycleGeneration);
}
});
}
private void invalidateTinyDeviceName(IPv6Address address) {
synchronized (tinyNameStateLock) {
tinyDeviceNameCache.remove(address);
tinyDeviceNameCacheTimes.remove(address);
tinyNameRequestsInFlight.remove(address);
tinyNameRequestTimes.remove(address);
tinyNameRequestGenerations.remove(address);
}
}
private void handleNodeInfo(HttpExchange exchange) throws IOException { private void handleNodeInfo(HttpExchange exchange) throws IOException {
if (handleCorsPreflight(exchange)) return; if (handleCorsPreflight(exchange)) return;
if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) { if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) {
@@ -500,56 +686,70 @@ public class KLALBWebServer {
if (isSelf) { if (isSelf) {
// 本机:描述直接取本地控制器配置 // 本机:描述直接取本地控制器配置
KLALBControllerConfigItem cfg = proxySystem.getControllerConfig(); KLALBController.PublishedNodeInfo published = kc.getPublishedNodeInfo();
resp.addProperty("isSelf", true); resp.addProperty("isSelf", true);
String dname = router.getDeviceName() != null ? router.getDeviceName() resp.addProperty("reachable", true);
: (cfg != null && cfg.getDeviceName() != null ? cfg.getDeviceName() : ""); resp.addProperty("deviceName", published.getDeviceName() != null ? published.getDeviceName() : "");
resp.addProperty("deviceName", dname);
resp.addProperty("deviceDescription", resp.addProperty("deviceDescription",
cfg != null && cfg.getDeviceDescription() != null ? cfg.getDeviceDescription() : ""); published.getDeviceDescription() != null ? published.getDeviceDescription() : "");
JsonArray lines = new JsonArray(); JsonArray lines = new JsonArray();
for (MultiProtocolSocketAddress mpsa : kc.getExternalEndpoints()) { for (MultiProtocolSocketAddress mpsa : published.getExternalEndpoints()) {
lines.add(new JsonPrimitive(mpsa.toString())); lines.add(new JsonPrimitive(mpsa.toString()));
} }
resp.add("openLines", lines); resp.add("openLines", lines);
JsonArray extraRoutes = new JsonArray();
if (published.getExtraRoutes() != null) {
for (String route : published.getExtraRoutes()) {
extraRoutes.add(new JsonPrimitive(route != null ? route : ""));
}
}
resp.add("extraRoutes", extraRoutes);
sendJsonResponse(exchange, 200, resp); sendJsonResponse(exchange, 200, resp);
return; return;
} }
// 远端节点:经 SRv6 虚拟网络发送完整节点信息查询(异步回调,限时等待) // 远端节点:经 SRv6 虚拟网络发送完整节点信息查询(异步回调,限时等待)
resp.addProperty("isSelf", false); resp.addProperty("isSelf", false);
CompletableFuture<KLALBNodeInformation> future = new CompletableFuture<>();
try { try {
getNodeInfoClient(kc).requestNodeInfoFull( KLALBRoutingProtocolAPIClient client = getNodeInfoClient(kc);
new InetSocketAddress(target.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT), if (client == null) {
info -> future.complete(info)); sendError(exchange, 503, "Node info service stopped");
return;
KLALBNodeInformation info;
try {
info = future.get(3, TimeUnit.SECONDS);
} catch (TimeoutException te) {
info = null;
} }
NodeInfoQueryCoordinator.Result info = new NodeInfoQueryCoordinator(client)
String dname = info != null && info.getDeviceName() != null && !info.getDeviceName().isEmpty() .query(new InetSocketAddress(target.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT)).get();
? info.getDeviceName() NodeInfoQueryCoordinator.Section<NodeProfile> profile = info.getProfile();
: router.getKlalbRouteProtol().getDeviceName(target); NodeProfile nodeProfile = profile.getValue().orElse(null);
resp.addProperty("deviceName", dname != null ? dname : ""); String dname = nodeProfile != null && nodeProfile.getDeviceName() != null
&& !nodeProfile.getDeviceName().isEmpty() ? nodeProfile.getDeviceName() : "";
resp.addProperty("deviceName", dname);
resp.addProperty("deviceDescription", resp.addProperty("deviceDescription",
info != null && info.getDeviceDescription() != null ? info.getDeviceDescription() : ""); nodeProfile != null && nodeProfile.getDeviceDescription() != null
resp.addProperty("reachable", info != null); ? nodeProfile.getDeviceDescription() : "");
resp.addProperty("reachable", profile.isReceived()
|| info.getExternalEndpoints().isReceived() || info.getExtraRoutes().isReceived());
JsonArray lines = new JsonArray(); JsonArray lines = new JsonArray();
if (info != null && info.getOpenLines() != null) { if (info.getExternalEndpoints().getStatus().filter(NodeInfoQueryStatus.OK::equals).isPresent()
for (MultiProtocolSocketAddress mpsa : info.getOpenLines()) { && info.getExternalEndpoints().getValue().isPresent()) {
for (MultiProtocolSocketAddress mpsa : info.getExternalEndpoints().getValue().get()) {
lines.add(new JsonPrimitive(mpsa.toString())); lines.add(new JsonPrimitive(mpsa.toString()));
} }
} }
resp.add("openLines", lines); resp.add("openLines", lines);
JsonArray extraRoutes = new JsonArray();
if (info.getExtraRoutes().getStatus().filter(NodeInfoQueryStatus.OK::equals).isPresent()
&& info.getExtraRoutes().getValue().isPresent()) {
for (String route : info.getExtraRoutes().getValue().get()) {
extraRoutes.add(new JsonPrimitive(route != null ? route : ""));
}
}
resp.add("extraRoutes", extraRoutes);
} catch (Exception e) { } catch (Exception e) {
resp.addProperty("deviceName", ""); resp.addProperty("deviceName", "");
resp.addProperty("deviceDescription", ""); resp.addProperty("deviceDescription", "");
resp.addProperty("reachable", false); resp.addProperty("reachable", false);
resp.add("openLines", new JsonArray()); resp.add("openLines", new JsonArray());
resp.add("extraRoutes", new JsonArray());
} }
sendJsonResponse(exchange, 200, resp); sendJsonResponse(exchange, 200, resp);
} }
@@ -584,6 +784,17 @@ public class KLALBWebServer {
KLALBRoutingProtocol rproto = kc.getIpv6Router().getKlalbRouteProtol(); KLALBRoutingProtocol rproto = kc.getIpv6Router().getKlalbRouteProtol();
Map<IPv6Address, Long> addrs = rproto.getAddresses(); Map<IPv6Address, Long> addrs = rproto.getAddresses();
IPv6Address selfAddr = kc.getIpv6Router().getLocator().getAddress(); IPv6Address selfAddr = kc.getIpv6Router().getLocator().getAddress();
KLALBController.PublishedNodeInfo published = kc.getPublishedNodeInfo();
Set<IPv6Address> activeAddresses = addrs == null
? Collections.<IPv6Address>emptySet() : new HashSet<IPv6Address>(addrs.keySet());
synchronized (tinyNameStateLock) {
tinyDeviceNameCache.keySet().removeIf(address -> !activeAddresses.contains(address));
tinyDeviceNameCacheTimes.keySet().removeIf(address -> !activeAddresses.contains(address));
tinyNameRequestsInFlight.removeIf(address -> !activeAddresses.contains(address));
tinyNameRequestTimes.keySet().removeIf(address -> !activeAddresses.contains(address));
tinyNameRequestGenerations.keySet().removeIf(address -> !activeAddresses.contains(address));
}
nodeInfoRevisions.keySet().removeIf(address -> !activeAddresses.contains(address));
if (addrs != null) { if (addrs != null) {
for (IPv6Address addr : addrs.keySet()) { for (IPv6Address addr : addrs.keySet()) {
@@ -592,8 +803,12 @@ public class KLALBWebServer {
nodeObj.addProperty("address", addr.toString()); nodeObj.addProperty("address", addr.toString());
nodeObj.addProperty("compressedAddress", addr.toCompressedString()); nodeObj.addProperty("compressedAddress", addr.toCompressedString());
nodeObj.addProperty("isSelf", addr.equals(selfAddr)); nodeObj.addProperty("isSelf", addr.equals(selfAddr));
String dname = rproto.getDeviceName(addr); String dname = addr.equals(selfAddr) ? kc.getIpv6Router().getDeviceName()
: getTinyDeviceName(kc, addr);
nodeObj.addProperty("deviceName", dname != null ? dname : ""); nodeObj.addProperty("deviceName", dname != null ? dname : "");
long nodeInfoRevision = addr.equals(selfAddr) ? published.getFullRevision()
: nodeInfoRevisions.getOrDefault(addr, 0L);
nodeObj.addProperty("nodeInfoRevision", nodeInfoRevision);
nodesArray.add(nodeObj); nodesArray.add(nodeObj);
} }
} }
@@ -644,21 +859,33 @@ public class KLALBWebServer {
sendError(exchange, 404, "Configuration not found"); sendError(exchange, 404, "Configuration not found");
} }
} else if ("POST".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method)) { } else if ("POST".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method)) {
synchronized (configUpdateLock) {
String body = readRequestBody(exchange); String body = readRequestBody(exchange);
try { try {
JsonObject json = new JsonParser().parse(body).getAsJsonObject(); JsonObject json = new JsonParser().parse(body).getAsJsonObject();
KLALBControllerConfigItem current = proxySystem.getControllerConfig(); KLALBControllerConfigItem current = proxySystem.getControllerConfig();
if (current != null) { if (current != null) {
String oldDeviceName = current.getDeviceName();
String oldDeviceDescription = current.getDeviceDescription();
List<MultiProtocolSocketAddress> oldExternalEndpoints = current.getExternalEndpoints() == null
? null
: new ArrayList<MultiProtocolSocketAddress>(current.getExternalEndpoints());
List<String> oldExtraRoutes = current.getExtraRoutes() == null
? null : new ArrayList<String>(current.getExtraRoutes());
String newDeviceName = oldDeviceName;
String newDeviceDescription = oldDeviceDescription;
List<MultiProtocolSocketAddress> newExternalEndpoints = current.getExternalEndpoints();
List<String> newExtraRoutes = current.getExtraRoutes();
if (json.has("deviceName") && !json.get("deviceName").isJsonNull()) { if (json.has("deviceName") && !json.get("deviceName").isJsonNull()) {
current.setDeviceName(json.get("deviceName").getAsString()); newDeviceName = json.get("deviceName").getAsString();
} else if (json.has("DeviceName") && !json.get("DeviceName").isJsonNull()) { } else if (json.has("DeviceName") && !json.get("DeviceName").isJsonNull()) {
current.setDeviceName(json.get("DeviceName").getAsString()); newDeviceName = json.get("DeviceName").getAsString();
} }
if (json.has("deviceDescription") && !json.get("deviceDescription").isJsonNull()) { if (json.has("deviceDescription") && !json.get("deviceDescription").isJsonNull()) {
current.setDeviceDescription(json.get("deviceDescription").getAsString()); newDeviceDescription = json.get("deviceDescription").getAsString();
} else if (json.has("DeviceDescription") && !json.get("DeviceDescription").isJsonNull()) { } else if (json.has("DeviceDescription") && !json.get("DeviceDescription").isJsonNull()) {
current.setDeviceDescription(json.get("DeviceDescription").getAsString()); newDeviceDescription = json.get("DeviceDescription").getAsString();
} }
if (json.has("language") && !json.get("language").isJsonNull()) { if (json.has("language") && !json.get("language").isJsonNull()) {
@@ -733,7 +960,7 @@ public class KLALBWebServer {
list.add(new MultiProtocolSocketAddress(el.getAsString())); list.add(new MultiProtocolSocketAddress(el.getAsString()));
} }
} }
current.setExternalEndpoints(list); newExternalEndpoints = list;
} }
if (json.has("autoConnections") || json.has("AutoConnections") || json.has("connectLineTable") || json.has("ConnectLineTable")) { if (json.has("autoConnections") || json.has("AutoConnections") || json.has("connectLineTable") || json.has("ConnectLineTable")) {
@@ -783,7 +1010,7 @@ public class KLALBWebServer {
list.add(el.getAsString()); list.add(el.getAsString());
} }
} }
current.setExtraRoutes(list); newExtraRoutes = list;
} }
if (json.has("networkInterfaceExcepts") || json.has("NetworkInterfaceExcepts")) { if (json.has("networkInterfaceExcepts") || json.has("NetworkInterfaceExcepts")) {
@@ -845,6 +1072,23 @@ public class KLALBWebServer {
current.setDenyExternalEndpointBroadcast(json.get("denyLineTableBroadcast").getAsBoolean()); current.setDenyExternalEndpointBroadcast(json.get("denyLineTableBroadcast").getAsBoolean());
} }
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) {
KLALBController kc = proxySystem.getKlalbController();
if (kc != null) {
kc.publishNodeInfo(newDeviceName, newDeviceDescription, newExternalEndpoints,
newExtraRoutes);
} else {
current.setDeviceName(newDeviceName);
current.setDeviceDescription(newDeviceDescription);
current.setExternalEndpoints(newExternalEndpoints);
current.setExtraRoutes(newExtraRoutes);
}
}
// Trigger GUI save consumer or save directly // Trigger GUI save consumer or save directly
if (proxySystem.getKLALBGUI() != null && proxySystem.getKLALBGUI().getSaveComsumer() != null) { if (proxySystem.getKLALBGUI() != null && proxySystem.getKLALBGUI().getSaveComsumer() != null) {
proxySystem.getKLALBGUI().getSaveComsumer().accept(proxySystem.getConfig()); proxySystem.getKLALBGUI().getSaveComsumer().accept(proxySystem.getConfig());
@@ -862,6 +1106,7 @@ public class KLALBWebServer {
} catch (Exception e) { } catch (Exception e) {
sendError(exchange, 400, "Failed to update configuration: " + e.getMessage()); sendError(exchange, 400, "Failed to update configuration: " + e.getMessage());
} }
}
} else { } else {
sendError(exchange, 405, "Method not allowed"); sendError(exchange, 405, "Method not allowed");
} }
@@ -20,26 +20,26 @@ public class SpeedAndTrafficAndDelayMonitorDataImpl extends SpeedAndTrafficMonit
downSampler.update(); downSampler.update();
if(upSampler.getSnapshotPacketCount()>0&&upSampler.getSnapshotTotalDelay()>0) { if(upSampler.getSnapshotPacketCount()>0&&upSampler.getSnapshotTotalDelay()>0) {
getUploadBandwidth(). recordPacket(KLALBUtils.createGlobalUUID(),0, 0, upSampler.getAvgDelayNanos()); getUploadBandwidth(). recordPacket(KLALBUtils.createGlobalUUID(),0, 0, upSampler.getAvgDelayNanos());
long odelay=upSampler.getMinDelayNanos();
if(outDelayMin==Long.MAX_VALUE||odelay<=outDelayMin) {
outDelayMin=odelay;
}else{
outDelayMin=Math.min(odelay, outDelayMin+10000);
}
} }
if(downSampler.getSnapshotPacketCount()>0&&downSampler.getSnapshotTotalDelay()>0) { if(downSampler.getSnapshotPacketCount()>0&&downSampler.getSnapshotTotalDelay()>0) {
getDownloadBandwidth(). recordPacket(KLALBUtils.createGlobalUUID(),0, 0, downSampler.getAvgDelayNanos()); getDownloadBandwidth(). recordPacket(KLALBUtils.createGlobalUUID(),0, 0, downSampler.getAvgDelayNanos());
long idelay=downSampler.getMinDelayNanos();
if(inDelayMin==Long.MAX_VALUE||idelay<=inDelayMin) {
inDelayMin=idelay;
}else{
inDelayMin=Math.min(idelay, inDelayMin+10000);
}
} }
long time=System.nanoTime(); long time=System.nanoTime();
if(time-stime>timewindowmax) { if(time-stime>timewindowmax) {
stime=time; stime=time;
long idelay=getInDelay();
if(inDelayMin==Long.MAX_VALUE||idelay<=inDelayMin) {
inDelayMin=idelay;
}else{
inDelayMin=(inDelayMin*99+idelay)/100;
}
long odelay=getOutDelay();
if(outDelayMin==Long.MAX_VALUE||odelay<=outDelayMin) {
outDelayMin=odelay;
}else{
outDelayMin=(outDelayMin*99+odelay)/100;
}
} }
} }
+27 -6
View File
@@ -117,7 +117,7 @@ public class NTPContext implements Closeable, AutoCloseable {
} }
protected void putPacket(NTPv4Packet nv4, MultiProtocolSocketAddress inetSocketAddress) { protected void putPacket(NTPv4Packet nv4, MultiProtocolSocketAddress inetSocketAddress) {
//checkIP(); checkIP();
List<NTPv4Packet> newv = new Vector<NTPv4Packet>(); List<NTPv4Packet> newv = new Vector<NTPv4Packet>();
List<NTPv4Packet> oldv = recvmap.putIfAbsent(inetSocketAddress, newv); List<NTPv4Packet> oldv = recvmap.putIfAbsent(inetSocketAddress, newv);
if (oldv == null) { if (oldv == null) {
@@ -132,6 +132,25 @@ public class NTPContext implements Closeable, AutoCloseable {
} }
} }
private void checkIP() {
Set<Entry<MultiProtocolSocketAddress, List<NTPv4Packet>>> ens = recvmap.entrySet();
for (Iterator<Entry<MultiProtocolSocketAddress, List<NTPv4Packet>>> iterator = ens.iterator(); iterator
.hasNext();) {
Entry<MultiProtocolSocketAddress, List<NTPv4Packet>> entry = (Entry<MultiProtocolSocketAddress, List<NTPv4Packet>>) iterator
.next();
AtomicBoolean ab = new AtomicBoolean(false);
ios.forEach((x) -> {
if (!ab.get())
if (x.findPeer(entry.getKey()) != null) {
ab.set(true);
return;
}
});
if (!ab.get()) {
iterator.remove();
}
}
}
private class PeerInfo implements Comparable<PeerInfo> { private class PeerInfo implements Comparable<PeerInfo> {
private MultiProtocolSocketAddress address; private MultiProtocolSocketAddress address;
@@ -206,12 +225,14 @@ public class NTPContext implements Closeable, AutoCloseable {
PeerInfo pix = peerInfo.get(0); PeerInfo pix = peerInfo.get(0);
currentClock = pix; currentClock = pix;
int i; int i;
List<Long128> adjts = new ArrayList<>(); int m = Math.min(peerInfo.size(), 3);
for (i = 0; i < peerInfo.size(); i++) { Long128 bi = Long128.ZERO;
adjts.add(peerInfo.get(i).getAdj()); for (i = 0; i < m; i++) {
PeerInfo pi = peerInfo.get(i);
Long128 adjt = pi.getAdj();
bi = bi.add(adjt);
} }
Long128 delta = NTPTimestamps. median(adjts); Long128 delta = bi.divide(Long128.valueOf(i));
//System.out.println(adjts+":"+delta);
avgAdj=(avgAdj*7+delta.abs().longValue())/8; avgAdj=(avgAdj*7+delta.abs().longValue())/8;
Long128 deltaabs=delta.abs(); Long128 deltaabs=delta.abs();
if (deltaabs.compareTo(adjustThreshold0) > 0&&firstSync.compareAndSet(true, false)) { if (deltaabs.compareTo(adjustThreshold0) > 0&&firstSync.compareAndSet(true, false)) {
@@ -405,5 +405,18 @@ public class NTPv4Protocol implements Closeable, AutoCloseable {
return dgs.isClosed(); return dgs.isClosed();
} }
public static void main(String[] args) throws IOException, InterruptedException {
HighAccuracyClock hac = new HighAccuracyClock();
NTPContext context = new NTPContext(hac);
System.out.println(context);
NTPv4Protocol nvc = new NTPv4Protocol(context, new MultiProtocolSocketAddress("{UDP}0.0.0.0:123"));// 106.55.184.199
nvc.getPeers().add(new NTPPeer("{UDP}106.55.184.199:123", NTPv4Packet.NTP_CLIENT));
nvc.getPeers().add(new NTPPeer("{UDP}time.windows.com:123", NTPv4Packet.NTP_CLIENT));
nvc.getPeers().add(new NTPPeer("{UDP}127.0.0.1:123", NTPv4Packet.NTP_SYMMETRIC_ACTIVE));
/*
* for (int i = 0; i < 10000; i++) { Thread.sleep(1000);
* System.out.println(context.getCurrentSelfDispersion128());
* System.out.println(context); }
*/
}
} }
@@ -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 + "]";
}
}
@@ -33,7 +33,8 @@ public class JsonDataPacket extends KLALBRoutingProtocolPacket implements Serial
private String data; private String data;
private static final int HEADER_LENGTH=3; private static final int HEADER_LENGTH=3;
private static final int MAX_JSON_PAYLOAD_LENGTH=65535-HEADER_LENGTH;
private static Gson gson; private static Gson gson;
static{ static{
@@ -73,9 +74,12 @@ public class JsonDataPacket extends KLALBRoutingProtocolPacket implements Serial
@Override @Override
public void writeToChannel(WritableByteChannel dto) throws IOException { public void writeToChannel(WritableByteChannel dto) throws IOException {
byte[]bta=data.getBytes(Charset.forName("UTF-8")); byte[]bta=data.getBytes(Charset.forName("UTF-8"));
getHeader().putChar(1,(char) bta.length); 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); super.writeToChannel(dto);
dto.write(ByteBuffer.wrap(bta)); dto.write(ByteBuffer.wrap(bta));
} }
@@ -1,52 +0,0 @@
package org.kne.cloud.network.srv6;
import java.util.List;
import org.kne.cloud.network.MultiProtocolSocketAddress;
/**
* 节点信息(开放线路 + 设备名称 + 设备描述),由路由协议 JSON API 查询获得。
*/
public class KLALBNodeInformation {
private List<MultiProtocolSocketAddress> openLines;
private String deviceName;
private String deviceDescription;
public KLALBNodeInformation(List<MultiProtocolSocketAddress> openLines, String deviceName,
String deviceDescription) {
super();
this.openLines = openLines;
this.deviceName = deviceName;
this.deviceDescription = deviceDescription;
}
public List<MultiProtocolSocketAddress> getOpenLines() {
return openLines;
}
public void setOpenLines(List<MultiProtocolSocketAddress> openLines) {
this.openLines = openLines;
}
public String getDeviceName() {
return deviceName;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public String getDeviceDescription() {
return deviceDescription;
}
public void setDeviceDescription(String deviceDescription) {
this.deviceDescription = deviceDescription;
}
@Override
public String toString() {
return "KLALBNodeInformation [openLines=" + openLines + ", deviceName=" + deviceName
+ ", deviceDescription=" + deviceDescription + "]";
}
}
@@ -13,15 +13,18 @@ import java.nio.channels.Channels;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import org.kne.cloud.network.ipv6.IPv6Address; import org.kne.cloud.network.ipv6.IPv6Address;
@@ -41,8 +44,12 @@ public class KLALBRoutingProtocol extends Thread{
private static final boolean debug = false; private static final boolean debug = false;
private RouterInfo selfRouterInfo; private RouterInfo selfRouterInfo;
private Map<IPv6Address, RouterInfo> netmap=new ConcurrentHashMap<>(); 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() { public Map<IPv6Address, RouterInfo> getNetmap() {
return netmap; return netmap;
@@ -84,15 +91,67 @@ public class KLALBRoutingProtocol extends Thread{
private KLALBVirtualRawSocket ds = null; private KLALBVirtualRawSocket ds = null;
private ReentrantLock sendLock=new ReentrantLock(); private ReentrantLock sendLock=new ReentrantLock();
private Set<BiConsumer<SocketAddress,JsonDataPacket>>receivers=new CopyOnWriteArraySet<BiConsumer<SocketAddress,JsonDataPacket>>(); 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) { public void addReceiver(BiConsumer<SocketAddress,JsonDataPacket> rec) {
receivers.add(rec); receivers.add(rec);
} }
public void removeReceiver(BiConsumer<SocketAddress,JsonDataPacket> rec) { public void removeReceiver(BiConsumer<SocketAddress,JsonDataPacket> rec) {
receivers.remove(rec); 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 @Override
public void run() { public void run() {
@@ -103,11 +162,22 @@ public class KLALBRoutingProtocol extends Thread{
new Thread(()->{ new Thread(()->{
Thread.currentThread().setName("KLALB路由协议发送线程"); Thread.currentThread().setName("KLALB路由协议发送线程");
while(true) { while(true) {
try { try {
selfRouterInfo = getSelfRouterInfo(); selfRouterInfo = getSelfRouterInfo();
RouterInfo oslf=netmap.put(selfRouterInfo.getLocator().getAddress(), selfRouterInfo); RouterInfo oslf=netmap.put(selfRouterInfo.getLocator().getAddress(), selfRouterInfo);
noticeUpdate(); 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))) { /*if(oslf==null||(!selfRouterInfo.equals(oslf))) {
long cur=System.nanoTime(); long cur=System.nanoTime();
if(cur-floodTimer2>5000000000L) { if(cur-floodTimer2>5000000000L) {
@@ -147,10 +217,12 @@ public class KLALBRoutingProtocol extends Thread{
if(val.getAsn()==router.getASN()) { if(val.getAsn()==router.getASN()) {
timeout=2000000000L; timeout=2000000000L;
} }
if(val.checkTimeOut(timeout)) { if(val.checkTimeOut(timeout)) {
iterator.remove(); if(netmap.remove(type.getKey(), val)) {
noticeUpdate(); removeNodeInfoUpdateWindow(type.getKey());
} noticeUpdate();
}
}
/*List<NeighborInfo> ads=val.getNeighborAddresses(); /*List<NeighborInfo> ads=val.getNeighborAddresses();
for (Iterator<NeighborInfo> iterator2 = ads.iterator(); iterator2.hasNext();) { for (Iterator<NeighborInfo> iterator2 = ads.iterator(); iterator2.hasNext();) {
Inet6Address address=iterator2.next().getLocator().getAddress(); Inet6Address address=iterator2.next().getLocator().getAddress();
@@ -211,12 +283,12 @@ public class KLALBRoutingProtocol extends Thread{
while(true) { while(true) {
try { try {
byte[]ca=MembandBoost.allocateUninitializedByteArray(65535); byte[]ca=MembandBoost.allocateUninitializedArray(65535);
DatagramPacket dgp=new DatagramPacket(ca, ca.length); DatagramPacket dgp=new DatagramPacket(ca, ca.length);
ds.receive(dgp); ds.receive(dgp);
//System.out.println(Arrays.toString( Arrays.copyOf( dgp.getData(),dgp.getLength()))); //System.out.println(Arrays.toString( Arrays.copyOf( dgp.getData(),dgp.getLength())));
ByteArrayInputStream bi=new ByteArrayInputStream(dgp.getData(),0,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()) { switch(kp.getType()) {
case KLALBRoutingProtocolPacket.RINFO_REQ: case KLALBRoutingProtocolPacket.RINFO_REQ:
@@ -234,28 +306,34 @@ public class KLALBRoutingProtocol extends Thread{
break; break;
case KLALBRoutingProtocolPacket.RINFO: case KLALBRoutingProtocolPacket.RINFO:
RouterInfoPacket rifp=(RouterInfoPacket) kp; RouterInfoPacket rifp=(RouterInfoPacket) kp;
RouterInfo rif=rifp.getRinfo(); RouterInfo rif=rifp.getRinfo();
RouterInfo oldrif=netmap.get(rif.getLocator().getAddress()); 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) if(debug)
System.out.println(rif); System.out.println(rif);
if(oldrif==null||oldrif.getCreateTime()<rif.getCreateTime()) { boolean topologyUpdated = oldrif==null||oldrif.getCreateTime()<rif.getCreateTime();
if(topologyUpdated) {
if(debug) if(debug)
System.out.println("update RouterInfo"); System.out.println("update RouterInfo");
netmap.put(rif.getLocator().getAddress(), rif); netmap.put(rif.getLocator().getAddress(), rif);
noticeUpdate(); noticeUpdate();
if(rifp.isFlood()) { }else {
floodPacket( dgp.getSocketAddress(), rifp);
}
}else {
if(debug) if(debug)
System.out.println("dispose RouterInfo"); System.out.println("dispose RouterInfo");
} }
computeShortestPathIfUpdated(); if(newNodeInfoUpdate) {
notifyNodeInfoUpdate(origin, nodeInfoUpdateFlags);
}
if(rifp.isFlood() && (topologyUpdated || newNodeInfoUpdate)) {
floodPacket(dgp.getSocketAddress(), rifp);
}
computeShortestPathIfUpdated();
break; break;
case KLALBRoutingProtocolPacket.JSON_DATA: case KLALBRoutingProtocolPacket.JSON_DATA:
JsonDataPacket data=(JsonDataPacket) kp; JsonDataPacket data=(JsonDataPacket) kp;
@@ -314,10 +392,11 @@ public class KLALBRoutingProtocol extends Thread{
sendLock.lock(); sendLock.lock();
try { try {
ds.send(dgpx); ds.send(dgpx);
}catch(BindException e) { }catch(BindException e) {
e.printStackTrace(); e.printStackTrace();
System.err.println("Cannot assign:"+dest); System.err.println("Cannot assign:"+dest);
}finally { throw e;
}finally {
sendLock.unlock(); sendLock.unlock();
} }
} }
@@ -548,8 +627,9 @@ public class KLALBRoutingProtocol extends Thread{
private RouterInfo getSelfRouterInfo() { private RouterInfo getSelfRouterInfo() {
List<IPv6NetworkLink>links=router.getLinkTabel(); List<IPv6NetworkLink>links=router.getLinkTabel();
Object[] nls=links.toArray(); Object[] nls=links.toArray();
RouterInfo ri=new RouterInfo(System.currentTimeMillis(),router.getLocator(),router.getASN()); long currentTime = System.currentTimeMillis();
ri.setDeviceName(router.getDeviceName()); 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++) { for(int i=0;i<nls.length;i++) {
IPv6NetworkLink nl=(IPv6NetworkLink) nls[i]; IPv6NetworkLink nl=(IPv6NetworkLink) nls[i];
if((!nl.isLoopBack())&&nl.isUp()) { if((!nl.isLoopBack())&&nl.isUp()) {
@@ -604,14 +684,8 @@ public class KLALBRoutingProtocol extends Thread{
/** /**
* 查询某地址广播的设备名称(未知返回null) * 查询某地址广播的设备名称(未知返回null)
*/ */
public String getDeviceName(IPv6Address address) { public String getDeviceName(IPv6Address address) {
RouterInfo ri=netmap.get(address); return null;
if(ri!=null) {
String dn=ri.getDeviceName();
if(dn!=null&&!dn.isEmpty())
return dn;
}
return null;
} }
public Map<IPv6Address, List<LinkDirection>> getPaths() { public Map<IPv6Address, List<LinkDirection>> getPaths() {
return paths; return paths;
@@ -1,125 +1,208 @@
package org.kne.cloud.network.srv6; package org.kne.cloud.network.srv6;
import java.io.IOException; import java.io.IOException;
import java.lang.ref.Cleaner; import java.lang.ref.Cleaner;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.InetAddress;
import java.util.ArrayList; import java.net.SocketAddress;
import java.util.List; import java.util.ArrayList;
import java.util.UUID; import java.util.List;
import java.util.function.BiConsumer; import java.util.UUID;
import java.util.function.Consumer; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import org.kne.cloud.network.MultiProtocolSocketAddress; import java.util.function.Consumer;
import org.kne.cloud.network.congestion.NOCongestionAlgorithm;
import org.kne.cloud.network.congestion.SendPacketSlidingWindow; import org.kne.cloud.network.MultiProtocolSocketAddress;
import org.kne.opencl64.Releaser; import org.kne.cloud.network.congestion.NOCongestionAlgorithm;
import org.kne.cloud.network.congestion.SendPacketSlidingWindow;
public class KLALBRoutingProtocolAPIClient { import org.kne.cloud.network.klalb.SendItem;
private KLALBRoutingProtocol routingProtocol; import org.kne.opencl64.Releaser;
private SendPacketSlidingWindow<UUID, JsonDataPacket> window = new SendPacketSlidingWindow<UUID, JsonDataPacket>(
new NOCongestionAlgorithm(3000000000L), 1024 * 1024); public class KLALBRoutingProtocolAPIClient {
private KLALBRoutingProtocol routingProtocol;
private static final Cleaner clr = Cleaner.create(); private SendPacketSlidingWindow<UUID, JsonDataPacket> window = new SendPacketSlidingWindow<UUID, JsonDataPacket>(
new NOCongestionAlgorithm(3000000000L), 1024 * 1024);
private BiConsumer<SocketAddress, JsonDataPacket> rec = (addr, data) -> {
KLALBRoutingProtocolJsonData dataobj = data.getDecodedData(); private static final Cleaner clr = Cleaner.create();
InetSocketAddress addrs = (InetSocketAddress) addr;
UUID ruid = dataobj.getUuid(); private BiConsumer<SocketAddress, JsonDataPacket> rec = (addr, data) -> {
JsonDataPacket relate = null; KLALBRoutingProtocolJsonData dataobj = data.getDecodedData();
//System.out.println(ruid + " " + window.getSendmap()); UUID ruid = dataobj.getUuid();
switch (dataobj.getType()) { switch (dataobj.getType()) {
case KLALBRoutingProtocolJsonData.NODE_INFO_FULL_RESP: case KLALBRoutingProtocolJsonData.NODE_PROFILE_RESP:
case KLALBRoutingProtocolJsonData.NODE_INFO_TINY_RESP: PendingRequest profileRequest = acknowledgeResponse(ruid,
KLALBRoutingProtocolJsonData.NODE_PROFILE_REQ, addr);
if ((relate = window.ack(ruid)) != null) { if (profileRequest != null) {
List<?> connects = (List<?>) dataobj.getData(); ((Consumer<NodeProfile>) profileRequest.callback)
List<MultiProtocolSocketAddress> connectsm = new ArrayList<MultiProtocolSocketAddress>( .accept(new NodeProfile(dataobj.getDeviceName(), dataobj.getDeviceDescription()));
connects == null ? 0 : connects.size()); }
if (connects != null) { break;
for (Object open : connects) { case KLALBRoutingProtocolJsonData.NODE_EXTERNAL_ENDPOINTS_RESP:
if (open instanceof MultiProtocolSocketAddress) { PendingRequest endpointsRequest = acknowledgeResponse(ruid,
connectsm.add((MultiProtocolSocketAddress) open); KLALBRoutingProtocolJsonData.NODE_EXTERNAL_ENDPOINTS_REQ, addr);
} else { if (endpointsRequest != null) {
connectsm.add(new MultiProtocolSocketAddress((String) open)); ((Consumer<ExternalEndpointsResult>) endpointsRequest.callback).accept(
new ExternalEndpointsResult(decodeExternalEndpoints(dataobj.getData()), dataobj.getStatus()));
} }
} break;
} case KLALBRoutingProtocolJsonData.NODE_EXTRA_ROUTES_RESP:
// 组装节点信息线路 + 设备名称 + 设备描述精简模式下线路与描述为 null PendingRequest routesRequest = acknowledgeResponse(ruid,
KLALBNodeInformation info = new KLALBNodeInformation(connectsm, dataobj.getDeviceName(), KLALBRoutingProtocolJsonData.NODE_EXTRA_ROUTES_REQ, addr);
dataobj.getDeviceDescription()); if (routesRequest != null) {
((Consumer<KLALBNodeInformation>) relate.getUserCallback()).accept(info); ((Consumer<ExtraRoutesResult>) routesRequest.callback).accept(
} new ExtraRoutesResult(decodeExtraRoutes(dataobj.getData()), dataobj.getStatus()));
break; }
} break;
}
}; };
public KLALBRoutingProtocolAPIClient(KLALBRoutingProtocol routingProtocol) { private PendingRequest acknowledgeResponse(UUID uuid, String expectedRequestType, SocketAddress sourceAddress) {
this.routingProtocol = routingProtocol; SendItem<JsonDataPacket> pending = window.getSendmap().get(uuid);
routingProtocol.addReceiver(rec); if (pending == null) {
this.releaser = new KLALBRoutingProtocolAPIClientReleaser(this.routingProtocol, rec, window); return null;
clr.register(this, releaser); }
} KLALBRoutingProtocolJsonData request = pending.getPacket().getDecodedData();
if (request == null || !expectedRequestType.equals(request.getType())) {
/** return null;
* 精简查询仅获取对端设备名称开销最小适用于未查看节点详情的场景 }
*/ Object callback = pending.getPacket().getUserCallback();
public void requestNodeInfoTiny(SocketAddress addr, Consumer<KLALBNodeInformation> callback) if (!(callback instanceof PendingRequest)) {
throws IOException { return null;
sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_INFO_TINY_REQ, addr, callback); }
} PendingRequest pendingRequest = (PendingRequest) callback;
if (!expectedRequestType.equals(pendingRequest.expectedRequestType)
/** || !sameDestination(pendingRequest.expectedDestination, sourceAddress)) {
* 完整查询获取对端开放线路 + 设备名称 + 设备描述查看节点信息时使用 return null;
*/ }
public void requestNodeInfoFull(SocketAddress addr, Consumer<KLALBNodeInformation> callback) return window.ack(uuid) == null ? null : pendingRequest;
throws IOException { }
sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_INFO_FULL_REQ, addr, callback);
} private static boolean sameDestination(SocketAddress expected, SocketAddress actual) {
if (expected instanceof InetSocketAddress && actual instanceof InetSocketAddress) {
private void sendNodeInfoRequest(String type, SocketAddress addr, Consumer<KLALBNodeInformation> callback) InetSocketAddress expectedInet = (InetSocketAddress) expected;
throws IOException { InetSocketAddress actualInet = (InetSocketAddress) actual;
UUID suid = UUID.randomUUID(); if (expectedInet.getPort() != actualInet.getPort()) return false;
KLALBRoutingProtocolJsonData json = new KLALBRoutingProtocolJsonData(type, suid, null); InetAddress expectedAddress = expectedInet.getAddress();
JsonDataPacket packet = new JsonDataPacket(json); InetAddress actualAddress = actualInet.getAddress();
packet.setUserCallback(callback); if (expectedAddress != null && actualAddress != null) return expectedAddress.equals(actualAddress);
window.put(suid, packet); if (expectedAddress == null && actualAddress == null) {
routingProtocol.sendJsonPacketToAddress(packet, addr); return expectedInet.getHostString().equalsIgnoreCase(actualInet.getHostString());
}
} return false;
}
public KLALBRoutingProtocol getRoutingProtocol() { return expected != null && expected.equals(actual);
return routingProtocol; }
}
private List<MultiProtocolSocketAddress> decodeExternalEndpoints(Object data) {
private KLALBRoutingProtocolAPIClientReleaser releaser; if (!(data instanceof List<?>)) {
return new ArrayList<MultiProtocolSocketAddress>();
public void close() { }
releaser.run(); List<?> encoded = (List<?>) data;
} List<MultiProtocolSocketAddress> endpoints = new ArrayList<MultiProtocolSocketAddress>(encoded.size());
for (Object value : encoded) {
public boolean isClosed() { if (value instanceof MultiProtocolSocketAddress) {
return releaser.isReleased(); endpoints.add((MultiProtocolSocketAddress) value);
} } else if (value instanceof String) {
} try {
endpoints.add(new MultiProtocolSocketAddress((String) value));
class KLALBRoutingProtocolAPIClientReleaser extends Releaser<BiConsumer<SocketAddress, JsonDataPacket>> { } catch (RuntimeException ignored) {
// Ignore malformed entries while preserving the rest of the response.
private KLALBRoutingProtocol routingProtocol; }
private SendPacketSlidingWindow<UUID, JsonDataPacket> window; }
}
public KLALBRoutingProtocolAPIClientReleaser(KLALBRoutingProtocol routingProtocol, return endpoints;
BiConsumer<SocketAddress, JsonDataPacket> resource, SendPacketSlidingWindow<UUID, JsonDataPacket> window) { }
super(resource);
this.routingProtocol = routingProtocol; private List<String> decodeExtraRoutes(Object data) {
this.window = window; if (!(data instanceof List<?>)) {
} return new ArrayList<String>();
}
@Override List<?> encoded = (List<?>) data;
protected void release(BiConsumer<SocketAddress, JsonDataPacket> resource) { List<String> routes = new ArrayList<String>(encoded.size());
window.close(); for (Object value : encoded) {
routingProtocol.removeReceiver(resource); if (value instanceof String) {
} routes.add((String) value);
} }
}
return routes;
}
public KLALBRoutingProtocolAPIClient(KLALBRoutingProtocol routingProtocol) {
this.routingProtocol = routingProtocol;
routingProtocol.addReceiver(rec);
this.releaser = new KLALBRoutingProtocolAPIClientReleaser(this.routingProtocol, rec, window);
clr.register(this, releaser);
}
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() {
return routingProtocol;
}
private KLALBRoutingProtocolAPIClientReleaser releaser;
public synchronized void close() {
if (closed.compareAndSet(false, true)) releaser.run();
}
public boolean isClosed() {
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>> {
private KLALBRoutingProtocol routingProtocol;
private SendPacketSlidingWindow<UUID, JsonDataPacket> window;
public KLALBRoutingProtocolAPIClientReleaser(KLALBRoutingProtocol routingProtocol,
BiConsumer<SocketAddress, JsonDataPacket> resource, SendPacketSlidingWindow<UUID, JsonDataPacket> window) {
super(resource);
this.routingProtocol = routingProtocol;
this.window = window;
}
@Override
protected void release(BiConsumer<SocketAddress, JsonDataPacket> resource) {
window.close();
routingProtocol.removeReceiver(resource);
}
}
@@ -1,88 +1,98 @@
package org.kne.cloud.network.srv6; package org.kne.cloud.network.srv6;
import java.io.IOException; import java.io.IOException;
import java.lang.ref.Cleaner; import java.lang.ref.Cleaner;
import java.net.InetSocketAddress; import java.net.SocketAddress;
import java.net.SocketAddress; import java.util.ArrayList;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import org.kne.cloud.network.klalb.KLALBController; import org.kne.cloud.network.klalb.KLALBController;
import org.kne.opencl64.Releaser; import org.kne.opencl64.Releaser;
public class KLALBRoutingProtocolAPIServer { public class KLALBRoutingProtocolAPIServer {
private KLALBController controller; private KLALBController controller;
private KLALBRoutingProtocol routingProtocol; 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 {
try { KLALBRoutingProtocolJsonData dataobj = data.getDecodedData();
KLALBRoutingProtocolJsonData dataobj= data.getDecodedData(); switch (dataobj.getType()) {
InetSocketAddress addrs=(InetSocketAddress) addr; case KLALBRoutingProtocolJsonData.NODE_PROFILE_REQ:
switch(dataobj.getType()){ KLALBController.PublishedNodeInfo profilePublished = controller.getPublishedNodeInfo();
case KLALBRoutingProtocolJsonData.NODE_INFO_TINY_REQ: KLALBRoutingProtocolJsonData profile = new KLALBRoutingProtocolJsonData(
// 精简查询仅返回设备名称设备名称随路由信息广播公开不受 denyExternalEndpointQuery 限制 KLALBRoutingProtocolJsonData.NODE_PROFILE_RESP, dataobj.getUuid(), null);
KLALBRoutingProtocolJsonData tinyjson=new KLALBRoutingProtocolJsonData(KLALBRoutingProtocolJsonData.NODE_INFO_TINY_RESP,dataobj.getUuid(),null); profile.setDeviceName(profilePublished.getDeviceName());
tinyjson.setDeviceName(controller.getIpv6Router().getDeviceName()); profile.setDeviceDescription(profilePublished.getDeviceDescription());
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(tinyjson),addr); profile.setStatus(NodeInfoQueryStatus.OK);
break; routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(profile), addr);
case KLALBRoutingProtocolJsonData.NODE_INFO_FULL_REQ: break;
// 完整查询设备名称与描述总是正常响应denyExternalEndpointQuery 仅隐藏外部端点列表 case KLALBRoutingProtocolJsonData.NODE_EXTERNAL_ENDPOINTS_REQ:
boolean denyEndpoints=controller.getConfigItem()!=null&&controller.getConfigItem().isDenyExternalEndpointQuery(); boolean denyEndpoints = controller.getConfigItem() != null
KLALBRoutingProtocolJsonData json=new KLALBRoutingProtocolJsonData(KLALBRoutingProtocolJsonData.NODE_INFO_FULL_RESP,dataobj.getUuid(),denyEndpoints?null:controller.getExternalEndpoints()); && controller.getConfigItem().isDenyExternalEndpointQuery();
json.setDeviceName(controller.getIpv6Router().getDeviceName());// 附带本机设备名称 KLALBController.PublishedNodeInfo endpointsPublished = controller.getPublishedNodeInfo();
if(controller.getConfigItem()!=null) { KLALBRoutingProtocolJsonData endpoints = new KLALBRoutingProtocolJsonData(
json.setDeviceDescription(controller.getConfigItem().getDeviceDescription());// 附带本机设备描述 KLALBRoutingProtocolJsonData.NODE_EXTERNAL_ENDPOINTS_RESP, dataobj.getUuid(),
} denyEndpoints ? new ArrayList<org.kne.cloud.network.MultiProtocolSocketAddress>()
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(json),addr); : new ArrayList<org.kne.cloud.network.MultiProtocolSocketAddress>(
break; endpointsPublished.getExternalEndpoints()));
} endpoints.setStatus(denyEndpoints ? NodeInfoQueryStatus.DENIED : NodeInfoQueryStatus.OK);
} catch (IOException e) { routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(endpoints), addr);
e.printStackTrace(); break;
} case KLALBRoutingProtocolJsonData.NODE_EXTRA_ROUTES_REQ:
}; KLALBController.PublishedNodeInfo routesPublished = controller.getPublishedNodeInfo();
KLALBRoutingProtocolJsonData routes = new KLALBRoutingProtocolJsonData(
public KLALBRoutingProtocolAPIServer(KLALBRoutingProtocol routingProtocol,KLALBController controller) { KLALBRoutingProtocolJsonData.NODE_EXTRA_ROUTES_RESP, dataobj.getUuid(),
this.controller=controller; new ArrayList<String>(routesPublished.getExtraRoutes()));
this.routingProtocol=routingProtocol; routes.setStatus(NodeInfoQueryStatus.OK);
routingProtocol.addReceiver(rec); routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(routes), addr);
this.releaser=new KLALBRoutingProtocolAPIServerReleaser(this.routingProtocol,rec); break;
clr.register(this, releaser); }
} } catch (IOException e) {
e.printStackTrace();
public KLALBController getController() { }
return controller; };
}
public KLALBRoutingProtocolAPIServer(KLALBRoutingProtocol routingProtocol, KLALBController controller) {
public KLALBRoutingProtocol getRoutingProtocol() { this.controller = controller;
return routingProtocol; this.routingProtocol = routingProtocol;
} routingProtocol.addReceiver(rec);
this.releaser = new KLALBRoutingProtocolAPIServerReleaser(this.routingProtocol, rec);
clr.register(this, releaser);
private KLALBRoutingProtocolAPIServerReleaser releaser; }
public void close() { public KLALBController getController() {
releaser.run(); return controller;
} }
public KLALBRoutingProtocol getRoutingProtocol() {
public boolean isClosed() { return routingProtocol;
return releaser.isReleased(); }
}
private KLALBRoutingProtocolAPIServerReleaser releaser;
}
class KLALBRoutingProtocolAPIServerReleaser extends Releaser<BiConsumer<SocketAddress, JsonDataPacket>>{ public void close() {
releaser.run();
private KLALBRoutingProtocol routingProtocol; }
public KLALBRoutingProtocolAPIServerReleaser(KLALBRoutingProtocol routingProtocol,BiConsumer<SocketAddress, JsonDataPacket> resource) { public boolean isClosed() {
super(resource); return releaser.isReleased();
this.routingProtocol=routingProtocol; }
} }
@Override class KLALBRoutingProtocolAPIServerReleaser extends Releaser<BiConsumer<SocketAddress, JsonDataPacket>> {
protected void release(BiConsumer<SocketAddress, JsonDataPacket> resource) {
routingProtocol.removeReceiver(resource); private KLALBRoutingProtocol routingProtocol;
}
} public KLALBRoutingProtocolAPIServerReleaser(KLALBRoutingProtocol routingProtocol,
BiConsumer<SocketAddress, JsonDataPacket> resource) {
super(resource);
this.routingProtocol = routingProtocol;
}
@Override
protected void release(BiConsumer<SocketAddress, JsonDataPacket> resource) {
routingProtocol.removeReceiver(resource);
}
}
@@ -1,95 +1,89 @@
package org.kne.cloud.network.srv6; package org.kne.cloud.network.srv6;
import java.util.UUID; import java.util.Objects;
import java.util.UUID;
public class KLALBRoutingProtocolJsonData {
public static final String NODE_INFO_TINY_REQ="nodeinfotinyreq";// 精简查询仅设备名称 public class KLALBRoutingProtocolJsonData {
public static final String NODE_INFO_TINY_RESP="nodeinfotinyresp"; public static final String NODE_PROFILE_REQ = "nodeprofilereq";
public static final String NODE_INFO_FULL_REQ="nodeinfofullreq";// 完整查询开放线路+设备名称+设备描述 public static final String NODE_PROFILE_RESP = "nodeprofileresp";
public static final String NODE_INFO_FULL_RESP="nodeinfofullresp"; public static final String NODE_EXTERNAL_ENDPOINTS_REQ = "nodeexternalendpointsreq";
private String type; public static final String NODE_EXTERNAL_ENDPOINTS_RESP = "nodeexternalendpointsresp";
private UUID uuid; public static final String NODE_EXTRA_ROUTES_REQ = "nodeextraroutesreq";
private Object data; public static final String NODE_EXTRA_ROUTES_RESP = "nodeextraroutesresp";
private String deviceName;// 对端设备名称
private String deviceDescription;// 对端设备描述 private String type;
public String getDeviceName() { private UUID uuid;
return deviceName; private Object data;
} private String deviceName;
public void setDeviceName(String deviceName) { private String deviceDescription;
this.deviceName = deviceName; private NodeInfoQueryStatus status;
}
public String getDeviceDescription() { public KLALBRoutingProtocolJsonData(String type, UUID uuid, Object data) {
return deviceDescription; this.type = type;
} this.uuid = uuid;
public void setDeviceDescription(String deviceDescription) { this.data = data;
this.deviceDescription = deviceDescription; }
}
public String getType() { public String getType() {
return type; return type;
} }
public Object getData() {
return data; public UUID getUuid() {
} return uuid;
public UUID getUuid() { }
return uuid;
} public Object getData() {
@Override return data;
public int hashCode() { }
final int prime = 31;
int result = 1; public String getDeviceName() {
result = prime * result + ((data == null) ? 0 : data.hashCode()); return deviceName;
result = prime * result + ((type == null) ? 0 : type.hashCode()); }
result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
result = prime * result + ((deviceName == null) ? 0 : deviceName.hashCode()); public void setDeviceName(String deviceName) {
result = prime * result + ((deviceDescription == null) ? 0 : deviceDescription.hashCode()); this.deviceName = deviceName;
return result; }
}
@Override public String getDeviceDescription() {
public boolean equals(Object obj) { return deviceDescription;
if (this == obj) }
return true;
if (obj == null) public void setDeviceDescription(String deviceDescription) {
return false; this.deviceDescription = deviceDescription;
if (getClass() != obj.getClass()) }
return false;
KLALBRoutingProtocolJsonData other = (KLALBRoutingProtocolJsonData) obj; public NodeInfoQueryStatus getStatus() {
if (data == null) { return status;
if (other.data != null) }
return false;
} else if (!data.equals(other.data)) public void setStatus(NodeInfoQueryStatus status) {
return false; this.status = status;
if (type == null) { }
if (other.type != null)
return false; @Override
} else if (!type.equals(other.type)) public int hashCode() {
return false; return Objects.hash(type, uuid, data, deviceName, deviceDescription, status);
if (uuid == null) { }
if (other.uuid != null)
return false; @Override
} else if (!uuid.equals(other.uuid)) public boolean equals(Object obj) {
return false; if (this == obj) {
if (deviceName == null) { return true;
if (other.deviceName != null) }
return false; if (!(obj instanceof KLALBRoutingProtocolJsonData)) {
} else if (!deviceName.equals(other.deviceName)) return false;
return false; }
if (deviceDescription == null) { KLALBRoutingProtocolJsonData other = (KLALBRoutingProtocolJsonData) obj;
if (other.deviceDescription != null) return Objects.equals(type, other.type) && Objects.equals(uuid, other.uuid)
return false; && Objects.equals(data, other.data) && Objects.equals(deviceName, other.deviceName)
} else if (!deviceDescription.equals(other.deviceDescription)) && Objects.equals(deviceDescription, other.deviceDescription)
return false; && Objects.equals(status, other.status);
return true; }
}
public KLALBRoutingProtocolJsonData(String type, UUID uuid, Object data) { @Override
super(); public String toString() {
this.type = type; return "KLALBRoutingProtocolJsonData [type=" + type + ", uuid=" + uuid + ", data=" + data
this.uuid = uuid; + ", deviceName=" + deviceName + ", deviceDescription=" + deviceDescription + ", status=" + status
this.data = data; + "]";
} }
@Override }
public String toString() {
return "KLALBRoutingProtocolJsonData [type=" + type + ", uuid=" + uuid + ", data=" + data + ", deviceName="
+ deviceName + ", deviceDescription=" + deviceDescription + "]";
}
}
@@ -95,8 +95,12 @@ public abstract class KLALBRoutingProtocolPacket extends NetworkPacket {
return readKLALBPacketFromChannel(Channels.newChannel(in)); return readKLALBPacketFromChannel(Channels.newChannel(in));
} }
public static KLALBRoutingProtocolPacket readKLALBPacketFromChannel(ReadableByteChannel in) throws IOException { public static KLALBRoutingProtocolPacket readKLALBPacketFromChannel(ReadableByteChannel in) throws IOException {
while(true) { return readKLALBPacketFromChannel(in, -1);
}
public static KLALBRoutingProtocolPacket readKLALBPacketFromChannel(ReadableByteChannel in, long packetLength) throws IOException {
while(true) {
ByteBuffer bb=NetworkPacket.bufferAllocator.allocate(40); ByteBuffer bb=NetworkPacket.bufferAllocator.allocate(40);
bb.limit(1); bb.limit(1);
while(bb.hasRemaining()){ while(bb.hasRemaining()){
@@ -109,18 +113,20 @@ public abstract class KLALBRoutingProtocolPacket extends NetworkPacket {
KLALBRoutingProtocolPacket klp; KLALBRoutingProtocolPacket klp;
switch(type) { switch(type) {
case RINFO_REQ: case RINFO_REQ:
klp=new RouterInfoRequestPacket(bb); klp=new RouterInfoRequestPacket(bb);
klp.readFromChannel(in); if(packetLength < 0) klp.readFromChannel(in);
return klp; else klp.readFromChannel(in, packetLength);
case RINFO: return klp;
klp=new RouterInfoPacket(bb); case RINFO:
klp.readFromChannel(in); klp=new RouterInfoPacket(bb);
return klp; if(packetLength < 0) klp.readFromChannel(in);
case JSON_DATA: else klp.readFromChannel(in, packetLength);
klp=new JsonDataPacket(bb); return klp;
klp.readFromChannel(in); case JSON_DATA:
return klp; klp=new JsonDataPacket(bb);
klp.readFromChannel(in);
return klp;
} }
//throw new StreamCorruptedException("unknown package type:"+type); //throw new StreamCorruptedException("unknown package type:"+type);
System.err.println("ignore unknown KLALBRoutingProtocolPacket type:"+type); System.err.println("ignore unknown KLALBRoutingProtocolPacket type:"+type);
@@ -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 + "]";
}
}
+26 -25
View File
@@ -60,15 +60,15 @@ public class RouterInfo implements Serializable{
return Objects.equals(locator, other.locator) && Objects.equals(neighborAddresses, other.neighborAddresses); return Objects.equals(locator, other.locator) && Objects.equals(neighborAddresses, other.neighborAddresses);
} }
private List<NeighborInfo> neighborAddresses=new ArrayList<>(); private List<NeighborInfo> neighborAddresses=new ArrayList<>();
private String deviceName=""; // 设备名称广播时携带描述不广播 private long createTime;
private long createTime;
public long getCreateTime() { public long getCreateTime() {
return createTime; return createTime;
} }
private static final long INFO_UPDATETIME=10000000000L; private static final long INFO_UPDATETIME=10000000000L;
private static final long INFO_TIMEOUT=60000000000L; private static final long INFO_TIMEOUT=60000000000L;
private static final int MAX_NEIGHBOR_COUNT = 512;
private long putTime=System.nanoTime(); private long putTime=System.nanoTime();
public boolean checkUpdateTime() { public boolean checkUpdateTime() {
return System.nanoTime()-putTime>INFO_UPDATETIME; return System.nanoTime()-putTime>INFO_UPDATETIME;
@@ -91,31 +91,28 @@ public class RouterInfo implements Serializable{
return neighborAddresses; return neighborAddresses;
} }
public String getDeviceName() { public RouterInfo( long createTime,IPv6AddressGroup locator,long asn) {
return deviceName;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public RouterInfo( long createTime,IPv6AddressGroup locator,long asn) {
this.createTime = createTime; this.createTime = createTime;
this.locator=locator; this.locator=locator;
this.asn=asn; this.asn=asn;
} }
public RouterInfo() { public RouterInfo() {
} }
public void writeToStream(DataOutputStream out) throws IOException { public void writeToStream(DataOutputStream out) throws IOException {
locator.writeToStream(out); int neighborCount = neighborAddresses == null ? -1 : neighborAddresses.size();
out.writeLong(asn); if(neighborCount<0 || neighborCount>MAX_NEIGHBOR_COUNT) {
out.writeLong(createTime); throw new IOException("Invalid neighbor count: " + neighborCount);
out.writeInt(neighborAddresses.size()); }
locator.writeToStream(out);
out.writeLong(asn);
out.writeLong(createTime);
out.writeInt(neighborCount);
for( NeighborInfo neighborInfo :neighborAddresses) { for( NeighborInfo neighborInfo :neighborAddresses) {
neighborInfo.writeToStream(out); 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 { protected void writeToChannel(WritableByteChannel dto) throws IOException {
@@ -127,16 +124,20 @@ public class RouterInfo implements Serializable{
public void readFromStream(DataInputStream in) throws IOException { public void readFromStream(DataInputStream in) throws IOException {
locator=new IPv6AddressGroup(in); locator=new IPv6AddressGroup(in);
asn=in.readLong(); asn=in.readLong();
createTime=in.readLong(); createTime=in.readLong();
int size=in.readInt(); int size=in.readInt();
neighborAddresses=new ArrayList<>(size); 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++) { for (int i = 0; i < size; i++) {
NeighborInfo nif=new NeighborInfo(); NeighborInfo nif=new NeighborInfo();
nif.readFromStream(in); nif.readFromStream(in);
neighborAddresses.add(nif); neighborAddresses.add(nif);
} }
deviceName=in.readUTF(); // Consume the legacy device-name slot; names are no longer broadcast here.
in.readUTF();
} }
} }
@@ -1,7 +1,8 @@
package org.kne.cloud.network.srv6; package org.kne.cloud.network.srv6;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.ByteArrayInputStream;
import java.io.Externalizable; import java.io.Externalizable;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInput; import java.io.ObjectInput;
@@ -22,7 +23,12 @@ import java.util.Set;
import org.kne.cloud.network.NetworkPacket; import org.kne.cloud.network.NetworkPacket;
import org.kne.cloud.network.ipv6.IPv6AddressGroup; import org.kne.cloud.network.ipv6.IPv6AddressGroup;
public class RouterInfoPacket extends KLALBRoutingProtocolPacket implements Serializable{ 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) { public RouterInfoPacket(RouterInfo routerInfo, boolean flood,long asn) {
super(RINFO); super(RINFO);
@@ -34,7 +40,8 @@ public class RouterInfoPacket extends KLALBRoutingProtocolPacket implements Seri
super(bb); super(bb);
} }
private RouterInfo rinfo; private RouterInfo rinfo;
private transient int nodeInfoUpdateFlags;
public boolean isFlood() { public boolean isFlood() {
return (getHeader().get(1)&1)==1; return (getHeader().get(1)&1)==1;
@@ -44,9 +51,17 @@ public class RouterInfoPacket extends KLALBRoutingProtocolPacket implements Seri
return getHeader().getLong(2); return getHeader().getLong(2);
} }
public RouterInfo getRinfo() { public RouterInfo getRinfo() {
return rinfo; return rinfo;
} }
public int getNodeInfoUpdateFlags() {
return nodeInfoUpdateFlags;
}
public void setNodeInfoUpdateFlags(int flags) {
nodeInfoUpdateFlags = flags & NODE_INFO_UPDATE_FLAGS_MASK;
}
@Override @Override
protected long getHeaderSize() { protected long getHeaderSize() {
return super.getHeaderSize()+9; return super.getHeaderSize()+9;
@@ -65,15 +80,46 @@ public class RouterInfoPacket extends KLALBRoutingProtocolPacket implements Seri
@Override @Override
public void writeToChannel(WritableByteChannel dto) throws IOException { public void writeToChannel(WritableByteChannel dto) throws IOException {
super.writeToChannel(dto); super.writeToChannel(dto);
rinfo.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 @Override
public void readFromChannel(ReadableByteChannel din, long length) throws IOException { public void readFromChannel(ReadableByteChannel din, long length) throws IOException {
super.readFromChannel(din, length); super.readFromChannel(din, length);
rinfo=new RouterInfo(); 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;
}
} }
} }
@@ -1,50 +0,0 @@
package org.kne.cloud.network.srv6;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.kne.cloud.network.ipv6.*;
public class SRHInsertNetworkLink extends AbstractIPv6NetworkLink {
private IPv6AddressGroup ipgroup;
public SRHInsertNetworkLink(SRv6Router sRv6Router,IPv6AddressGroup ipgroup) {
super(false);
setSRv6Router(sRv6Router);
this.ipgroup=ipgroup;
}
@Override
public List<IPv6AddressGroup> getAddressGroups() {
return new ArrayList<>();
}
@Override
public List<Neighbor> getNeighborsInfo() {
return new ArrayList<>();
}
@Override
public void sendPacket(IPv6Packet pack, IPv6Address inet6Address) throws IOException {
getSrv6Router().insertSRHeader( pack);
getSrv6Router().insertHeaderAndRoutePacket(null, pack);
}
@Override
public List<RouteItem> getRouteItems() {
return List.of(new RouteItem(ipgroup, null, this, RouteItem.KLALB_SRv6, 30, 0,"G"));
}
@Override
public String getName() {
return "SRHInsertNetworkLink";
}
@Override
public boolean isUp() {
return true;
}
}
+63 -42
View File
@@ -60,7 +60,7 @@ public class SRv6Router {
// public static final int MTU = 16384; // public static final int MTU = 16384;
public static final int MTU = 16384; // 最大传输单元 public static final int MTU = 16384; // 最大传输单元
public static final int MAX_REROUTE_COUNT = 2;// 最大重路由次数 public static final int MAX_REROUTE_COUNT = 2;// 1 // 最大重路由次数
private static final int IPv6_BITS = 128; // IPv6地址位数 private static final int IPv6_BITS = 128; // IPv6地址位数
@@ -104,7 +104,7 @@ public class SRv6Router {
// 环回网络链路 // 环回网络链路
private final LoopbackIPv6NetworkLink inLoopBack; private final LoopbackIPv6NetworkLink inLoopBack;
private final SRHInsertNetworkLink srhInserter;
// 获取路由表 // 获取路由表
public List<RouteItem> getRouteTabel() { public List<RouteItem> getRouteTabel() {
return routeTabel; return routeTabel;
@@ -121,7 +121,7 @@ public class SRv6Router {
@Override @Override
public void accept(IPv6Packet t) { public void accept(IPv6Packet t) {
// HighPerformanceExecutor.defaultExecutor.execute(() -> { // HighPerformanceExecutor.defaultExecutor.execute(() -> {
routePacket(null, t); // 执行重路由 routePacket(null, t, true); // 执行重路由
// }); // });
} }
}; };
@@ -142,11 +142,21 @@ public class SRv6Router {
if (oamtlv == null) if (oamtlv == null)
return true; return true;
boolean bool = unduplicateSet.add(oamtlv.getUUID()); boolean bool = unduplicateSet.add(oamtlv.getUUID());
// System.out.println(bool);
return bool; return bool;
} }
// 默认接收处理器
/*
* private BiConsumer<IPv6NetworkLink,IPv6Packet> defaultReceive = new
* BiConsumer<IPv6NetworkLink,IPv6Packet>() {
*
* @Override public void accept(IPv6NetworkLink link,IPv6Packet t) { //
* HighPerformanceExecutor.defaultExecutor.execute(() -> { if(unduplicate(t)) {
* routePacket(t); // 执行路由 t.putTimePassport("routed"); // 标记路由时间点 } // }); } };
*/
// 接收处理器 // SRH接收处理器
private BiConsumer<IPv6NetworkLink, Supplier<IPv6Packet>> receiveConsumer = new BiConsumer<IPv6NetworkLink, Supplier<IPv6Packet>>() { private BiConsumer<IPv6NetworkLink, Supplier<IPv6Packet>> srhReceive = new BiConsumer<IPv6NetworkLink, Supplier<IPv6Packet>>() {
@Override @Override
public void accept(IPv6NetworkLink link, Supplier<IPv6Packet> pack) { public void accept(IPv6NetworkLink link, Supplier<IPv6Packet> pack) {
@@ -245,7 +255,7 @@ public class SRv6Router {
nlink.setSRv6Router(this); nlink.setSRv6Router(this);
nlink.addIPv6LinkStateListener(listener); // 添加链路状态监听器 nlink.addIPv6LinkStateListener(listener); // 添加链路状态监听器
nlink.setReceiveConsumer(receiveConsumer); // 设置接收处理器 nlink.setReceiveConsumer(srhReceive); // 设置接收处理器
if (nlink instanceof ControlledIPv6NetworkLink) { if (nlink instanceof ControlledIPv6NetworkLink) {
((ControlledIPv6NetworkLink) nlink).setRerouteConsumer(defaultReroute); // 设置重路由处理器 ((ControlledIPv6NetworkLink) nlink).setRerouteConsumer(defaultReroute); // 设置重路由处理器
((ControlledIPv6NetworkLink) nlink).setCongressCondition(congressLock, congressCondition); // 设置拥塞条件 ((ControlledIPv6NetworkLink) nlink).setCongressCondition(congressLock, congressCondition); // 设置拥塞条件
@@ -313,9 +323,9 @@ public class SRv6Router {
} }
// 插入SRH并路由数据包 // 插入SRH并路由数据包
public void insertHeaderAndRoutePacket(IPv6NetworkLink link, IPv6Packet ipp) { public void insertSRHandRoutePacket(IPv6NetworkLink link, IPv6Packet ipp) {
insertHopByHopHeader(ipp);//插入逐跳头 insertHopByHopHeader(ipp);
//insertSRHeader(ipp); // 插入段路由头 insertSRHeader(ipp); // 插入段路由头
routePacket(link, ipp); // 路由数据包 routePacket(link, ipp); // 路由数据包
} }
@@ -374,13 +384,16 @@ public class SRv6Router {
} }
private void routePacket(IPv6Packet iPv6Packet) { private void routePacket(IPv6Packet iPv6Packet) {
routePacket(null, iPv6Packet); routePacket(null, iPv6Packet, false);
} }
// 路由数据包默认非重路由
private void routePacket(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet) {
routePacket(linkfrom, iPv6Packet, false);
}
// 路由数据包主方法 // 路由数据包主方法
private void routePacket(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet) { private void routePacket(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, boolean reroute) {
StringBuilder dbg = null; StringBuilder dbg = null;
if (debug) { if (debug) {
dbg = new StringBuilder(); // 调试信息 dbg = new StringBuilder(); // 调试信息
@@ -393,7 +406,7 @@ public class SRv6Router {
List<RouteItem> searchResult = getTabelByAddress(dest); List<RouteItem> searchResult = getTabelByAddress(dest);
if (searchResult!=null&&(!searchResult.isEmpty())) { if (searchResult!=null&&(!searchResult.isEmpty())) {
// 匹配到路由表执行负载均衡路由 // 匹配到路由表执行负载均衡路由
routingLoadBalance(linkfrom, iPv6Packet, dbg, searchResult); routingLoadBalance(linkfrom, iPv6Packet, reroute, dbg, searchResult);
return; return;
} }
@@ -428,7 +441,7 @@ public class SRv6Router {
// 重新搜索路由表 // 重新搜索路由表
List<RouteItem> searchResult2 = getTabelByAddress(dest); List<RouteItem> searchResult2 = getTabelByAddress(dest);
if (searchResult2!=null&&(!searchResult2.isEmpty())) { if (searchResult2!=null&&(!searchResult2.isEmpty())) {
routingLoadBalance(linkfrom, iPv6Packet, dbg, searchResult2); routingLoadBalance(linkfrom, iPv6Packet, reroute, dbg, searchResult2);
return; return;
} }
@@ -495,7 +508,7 @@ public class SRv6Router {
private Condition congressCondition = congressLock.newCondition(); // 拥塞条件 private Condition congressCondition = congressLock.newCondition(); // 拥塞条件
// 负载均衡路由 // 负载均衡路由
private boolean routingLoadBalance(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, private boolean routingLoadBalance(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, boolean reroute,
StringBuilder dbg, List<RouteItem> routes) throws IllegalRawDataException, IOException { StringBuilder dbg, List<RouteItem> routes) throws IllegalRawDataException, IOException {
if(routes==null||routes.isEmpty()) { if(routes==null||routes.isEmpty()) {
@@ -504,7 +517,7 @@ public class SRv6Router {
} }
for (double i = 1; i < 100; i += 0.1) { for (double i = 1; i < 100; i += 0.1) {
if (loadBalance(linkfrom, iPv6Packet, dbg, routes, i)) { if (loadBalance(linkfrom, iPv6Packet, reroute, dbg, routes, i)) {
return true; return true;
} }
@@ -523,7 +536,7 @@ public class SRv6Router {
return false; // 路由失败 return false; // 路由失败
} }
private boolean loadBalance(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, StringBuilder dbg, private boolean loadBalance(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, boolean reroute, StringBuilder dbg,
List<RouteItem> routes, double cscale) List<RouteItem> routes, double cscale)
throws IllegalRawDataException, IOException { throws IllegalRawDataException, IOException {
// 遍历可用路由进行负载均衡 // 遍历可用路由进行负载均衡
@@ -551,39 +564,31 @@ public class SRv6Router {
dbg.append("matched.\n"); dbg.append("matched.\n");
} }
// 找到可用链路处理数据包 // 找到可用链路处理数据包
processPacket(linkfrom, iPv6Packet, tri); processPacket(linkfrom, iPv6Packet, tri, reroute);
return true; return true;
} }
return false; return false;
} }
// 处理数据包转发 // 处理数据包转发
private void processPacket(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, RouteItem ri) private void processPacket(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, RouteItem ri, boolean reroute)
throws IllegalRawDataException, IOException { throws IllegalRawDataException, IOException {
int hop = iPv6Packet.getHopLimit(); int hop = iPv6Packet.getHopLimit();
if (ri.getDestlink().isLoopBack()) { // 环回链路处理 if (ri.getDestlink().isLoopBack()) { // 环回链路处理
IPv6HopByHopHeader iPv6HopByHopHeader=iPv6Packet.getHopByHopHeader();
if(iPv6Packet.getRouteCounter().get()<=0) {
if (iPv6HopByHopHeader != null&&linkfrom instanceof KLALBRemoteLink) {
processKLALBOAM(iPv6Packet, linkfrom.getAddressGroups().get(0).getAddress(),
((KLALBRemoteLink) linkfrom).getRemoteVaddr().getAddress(), iPv6HopByHopHeader);
}
}
IPv6SegmentRoutingHeader srhh = iPv6Packet.getSegmentRoutingHeader(); IPv6SegmentRoutingHeader srhh = iPv6Packet.getSegmentRoutingHeader();
if (srhh != null) { if (srhh != null) {
processSRv6Packet(linkfrom, iPv6Packet, ri, srhh); // SRv6特殊处理 processSRv6Packet(linkfrom, iPv6Packet, ri, srhh, iPv6Packet.getHopByHopHeader(), reroute); // SRv6特殊处理
} else { } else {
sendPacketToRouteItem(iPv6Packet, ri); sendPacketToRouteItem(iPv6Packet, ri);
} }
} else { // 普通链路处理 } else { // 普通链路处理
// 检查重路由计数 // 检查重路由计数
int count=iPv6Packet.getRouteCounter().getAndIncrement(); if (iPv6Packet.getRerouteCounter().getAndIncrement() >= MAX_REROUTE_COUNT) {
if (count >= MAX_REROUTE_COUNT) {
return; // 超过最大重路由次数丢弃 return; // 超过最大重路由次数丢弃
} }
if (count<=0) { if (!reroute) {
hop--; // 减少TTL非重路由时 hop--; // 减少TTL非重路由时
} }
@@ -646,16 +651,34 @@ public class SRv6Router {
// 处理SRv6数据包 // 处理SRv6数据包
private void processSRv6Packet(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, RouteItem ri, private void processSRv6Packet(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, RouteItem ri,
IPv6SegmentRoutingHeader srhh) IPv6SegmentRoutingHeader srhh, IPv6HopByHopHeader iPv6HopByHopHeader, boolean reroute)
throws IllegalRawDataException, IOException { throws IllegalRawDataException, IOException {
if (srhh.getSegmentsLeft() <= 0) { // 所有段已处理完毕 if (srhh.getSegmentsLeft() <= 0) { // 所有段已处理完毕
if (iPv6HopByHopHeader != null) {
int sl = srhh.getSegmentsLeft();
// IPv6Address prevSID
// =sl+1>=srhh.getAddresses().size()?iPv6Packet.getSourceAddress():srhh.getAddresses().get(sl+1);
if (linkfrom instanceof KLALBRemoteLink) {
processKLALBOAM(iPv6Packet, linkfrom.getAddressGroups().get(0).getAddress(),
((KLALBRemoteLink) linkfrom).getRemoteVaddr().getAddress(), iPv6HopByHopHeader);
}
}
// System.out.println(iPv6HopByHopHeader);
sendPacketToRouteItem(iPv6Packet, ri); sendPacketToRouteItem(iPv6Packet, ri);
} else { } else {
if (iPv6Packet.getRouteCounter().get()<=0) { if (reroute) {
// 重路由处理 // 重路由处理
} else {
int oldSL = srhh.getSegmentsLeft(); int oldSL = srhh.getSegmentsLeft();
if (iPv6HopByHopHeader != null) {
// IPv6Address prevSID
// =oldSL+1>=srhh.getAddresses().size()?iPv6Packet.getSourceAddress():srhh.getAddresses().get(oldSL+1);
if (linkfrom instanceof KLALBRemoteLink) {
processKLALBOAM(iPv6Packet, linkfrom.getAddressGroups().get(0).getAddress(),
((KLALBRemoteLink) linkfrom).getRemoteVaddr().getAddress(), iPv6HopByHopHeader);
}
}
// 正常SRv6处理移动到下一个段 // 正常SRv6处理移动到下一个段
int newSL = oldSL - 1; int newSL = oldSL - 1;
srhh.setSegmentsLeft(newSL); // 更新剩余段数 srhh.setSegmentsLeft(newSL); // 更新剩余段数
@@ -664,7 +687,7 @@ public class SRv6Router {
// 记录热点地址用于流量工程 // 记录热点地址用于流量工程
if (iPv6Packet.getPayload().getProtocolNumber() != KLALBRoutingProtocol.DEFAULT_PROTOCOL_NUMBER) if (iPv6Packet.getPayload().getProtocolNumber() != KLALBRoutingProtocol.DEFAULT_PROTOCOL_NUMBER)
klalbRouteProtol.putHotspotAddress(iPv6Packet.getSourceAddress()); klalbRouteProtol.putHotspotAddress(iPv6Packet.getSourceAddress());
routePacket(linkfrom, iPv6Packet); // 继续路由 routePacket(linkfrom, iPv6Packet, reroute); // 继续路由
} }
} }
@@ -791,7 +814,7 @@ public class SRv6Router {
if(ecn) { if(ecn) {
packet.markCE(); packet.markCE();
} }
insertHeaderAndRoutePacket(null, packet); // 插入SRH并路由 insertSRHandRoutePacket(null, packet); // 插入SRH并路由
long time=System.nanoTime()-start; long time=System.nanoTime()-start;
backplaneCount(time,ecn); backplaneCount(time,ecn);
} catch (Exception e) { } catch (Exception e) {
@@ -804,7 +827,7 @@ public class SRv6Router {
public void onReceive(IPv6NetworkLink link,IPv6Packet t){ public void onReceive(IPv6NetworkLink link,IPv6Packet t){
if (unduplicate(t)) { if (unduplicate(t)) {
insertHeaderAndRoutePacket(link, t); // 插入SRH并路由数据包 insertSRHandRoutePacket(link, t); // 插入SRH并路由数据包
} }
} }
@@ -893,17 +916,15 @@ public class SRv6Router {
} }
// 构造函数 // 构造函数
public SRv6Router(IPv6AddressGroup endSID,IPv6AddressGroup networkGroup, HighAccuracyClock clock) { public SRv6Router(IPv6AddressGroup hostAddress, HighAccuracyClock clock) {
super(); super();
this.locator = endSID; this.locator = hostAddress;
this.clock = clock; this.clock = clock;
// initWorkerThreads(); // initWorkerThreads();
// 创建环回链路 // 创建环回链路
this.inLoopBack = new LoopbackIPv6NetworkLink( this.inLoopBack = new LoopbackIPv6NetworkLink(
List.of(new IPv6AddressGroup(IPv6Address.LOOPBACK, 128), endSID), this); List.of(new IPv6AddressGroup(IPv6Address.LOOPBACK, 128), hostAddress), this);
linkTabel.add(inLoopBack); // 添加到链路表 linkTabel.add(inLoopBack); // 添加到链路表
this.srhInserter=new SRHInsertNetworkLink(this,networkGroup);
linkTabel.add(srhInserter);
backplaneTimer.scheduleAtFixedRate(new TimerTask() { backplaneTimer.scheduleAtFixedRate(new TimerTask() {
@Override @Override
Binary file not shown.