22 Commits
Author SHA1 Message Date
KNEMC 21b975676d 现在当目的地址是直接邻居(不需要转发)时,不插入SRv6扩展头节约带宽 2026-09-01 10:21:49 +08:00
KNEMC 2c111770b3 整理SRv6Router代码,修bug,增加SRHInsert虚链路 2026-08-31 19:07:53 +08:00
SerinaNya 52f44335e4 chore(dashboard): update dashboard submodule 2026-08-31 14:40:46 +08:00
KNEMC a06e1cb5eb 修改路由表名字为ENDSID和ENDXSID 2026-08-31 14:18:34 +08:00
KNEMC 58913254e0 区分了ENDXSID和ENDSID。移除了多余的路由表对象的isLoopback(),因为是否是环回是链路的属性,不是路由表的。 2026-08-31 10:57:08 +08:00
KNEMC 02d7bcd93b update JFreeChart 2026-08-30 21:46:01 +08:00
KNEMC 1e52e82d6d Merge branch 'master' of https://git.code.cq.cn/KNEMC/KLALB
# Conflicts:
#	.classpath
#	src/org/kne/cloud/network/klalb/KLALBControllerConfigItem.java
2026-08-30 21:42:29 +08:00
KNEMC 3023fe5b2a ntp和udp URI的GUI现在可以不写协议 2026-08-30 21:40:27 +08:00
SerinaNya 7896ee6638 🙈 add omo-slim files to .gitignore 2026-08-28 17:00:40 +08:00
SerinaNya ed94423855 🚑 recover fastcopy.dll 2026-08-28 16:53:37 +08:00
SerinaNya c3172a04b1 refactor(web)!: remove obsolete interfaces endpoint 2026-08-27 23:19:53 +08:00
SerinaNya cc803f47a7 feat(web): add /api/node-info endpoint for on-demand node detail queries
- Expose GET /api/node-info?address=<ipv6> endpoint in KLALBWebServer
- Return local deviceName and deviceDescription immediately for self node
- Perform asynchronous full node-info query (NODE_INFO_FULL_REQ) via
  KLALBRoutingProtocolAPIClient for remote nodes with 3s timeout fallback
2026-08-26 21:31:48 +08:00
KNEMC 3bb95988fc 增加enableTUN checkbox 2026-08-26 21:10:41 +08:00
KNEMC e87e3bb953 remove existed klalb-config.json 2026-08-26 20:28:31 +08:00
KNEMC 41db17f1ec remove klalb-config.json 2026-08-26 20:22:23 +08:00
SerinaNya 61868fe93e feat(config): switch webPort to URI-style webListen address
- replace integer webPort with MultiProtocolSocketAddress webListen
  (default: http://0.0.0.0:4665) in controller config
- rename Swing UI item to "Web API 监听地址" and validate as full URI
- update WebServer binding to honor configured host and port
- normalize legacy webPort integers to http://0.0.0.0:<port> on load
  and keep JSON API backward compatibility
- update i18n keys in zh_CN and en_US resource bundles
- update root klalb-config.json and AGENTS.md documentation
2026-08-26 19:42:26 +08:00
SerinaNya 9cea5ed493 feat(gui): add enable-Web-API toggle and web port settings
- new "Web API settings" section after security settings in the Swing
  options panel: checkbox bound to webUI plus a port text field bound
  to webPort (empty -> default 4665; non-numeric or out of 0-65535
  shows an invalid-port warning and aborts the save)
- load handler fills both controls from the current config item
- add i18n keys to klalb_zh_CN / klalb_en_US bundles
- include previously missing webUI/webPort fields in
  KLALBControllerConfigItem equals/hashCode
2026-08-26 00:36:24 +08:00
SerinaNya e0296e0ccd feat(srv6)!: replace openlines query with tiny/full node-info API
- routing-protocol JSON API: nodeinfotinyreq/resp returns device name
  only and is never gated; nodeinfofullreq/resp returns external
  endpoints + device name + description, where denyExternalEndpointQuery
  hides only the endpoint list (name/description always answer);
  legacy openlines* wire types removed - upgrade the whole mesh together
- rename misnamed openConnections -> externalEndpoints and
  denyConnection{Query,Broadcast} -> denyExternalEndpoint{Query,Broadcast};
  legacy config keys normalized on load because gson-2.1 lacks
  @SerializedName(alternate=...)
- remove TCP-based KLALBRemoteManagement, superseded by the HTTP API
- dashboard settings follow the renamed keys; update AGENTS.md
2026-08-26 00:23:21 +08:00
SerinaNya 9cea74943c fix(web): avoid gson-2.1 members-wrapped JSON responses; update AGENTS.md
- sendJsonResponse now serializes JsonElement via toString(), since gson-2.1's
  JSON_ELEMENT factory misses JsonObject/JsonArray subclasses and falls back to
  reflection (leaking the internal "members" field into every REST response)
- Document javac full-path requirement, pre-existing compile warnings,
  frontend page data flow, and gson quirks in AGENTS.md
2026-08-24 23:59:14 +08:00
SerinaNya 35c430114e update AGENTS.md 2026-08-24 00:13:33 +08:00
SerinaNya 6c7017bc75 feat(web): integrate embedded web server, REST/SSE APIs and modernize configuration
- Add embedded KLALBWebServer with REST API, SSE streaming (200ms) and SPA hosting
- Introduce enableTUN configuration flag with fallback and non-admin execution support
- Refactor LineTable and ConnectLineTable to openConnections and autoConnections
- Rename denyLineTableQuery/Broadcast to denyConnectionQuery/Broadcast with backwards compatibility
- Support runtime config hot-reloading and automatic persistence to klalb-config.json
- Update default Web API port to 4665 and update dashboard submodule reference
2026-08-24 00:01:24 +08:00
SerinaNya 041cc31c5f 🍱 add dashboard as submodule 2026-08-23 17:42:49 +08:00
58 changed files with 1958 additions and 884 deletions
+3 -1
View File
@@ -4,7 +4,6 @@
<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/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/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"/>
@@ -29,5 +28,8 @@
<attribute name="module" value="true"/>
</attributes>
</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"/>
</classpath>
+13
View File
@@ -3,3 +3,16 @@
/klalbs4.json
/klalbs.json
/klalbs2.json
# Dashboard / Frontend
dashboard/node_modules/
dashboard/dist/
dashboard/.pnpm-store/
.pnpm-debug.log*
dashboard/.env.local
dashboard/.env.*.local
/klalb-config.json
/klalbconfig-old.json
/.slim/
+3
View File
@@ -0,0 +1,3 @@
[submodule "dashboard"]
path = dashboard
url = https://git.code.cq.cn/SerinaNya/KLALB-dashboard.git
+38
View File
@@ -0,0 +1,38 @@
<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"?>
<project version="4">
<component name="ProjectRootManager" version="2">
<component name="ProjectRootManager" version="2" languageLevel="JDK_26" project-jdk-name="jdk-26.0.1" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/classes" />
</component>
</project>
+51 -5
View File
@@ -6,12 +6,14 @@ KLALB ("KLALB Decentralized SRv6 Network") — Java load-balancing/tunnel system
No Maven/Gradle. Plain Eclipse/IntelliJ project: dependencies are vendored jars in `lib/`, output goes to `bin/` (gitignored). When adding a jar, update **both** `.classpath` and `KLALB.iml`.
Compile (verified; `-encoding UTF-8` is mandatory sources contain Chinese text):
Compile (`javac` is NOT on PATH — use the full JDK path; `-encoding UTF-8` is mandatory because sources contain Chinese text):
```powershell
& javac -encoding UTF-8 -cp "lib/*" -d bin (Get-ChildItem -Recurse src -Filter *.java | ForEach-Object FullName)
& "C:\Program Files\Zulu\zulu-25\bin\javac.exe" -encoding UTF-8 -cp "lib/*" -d bin (Get-ChildItem -Recurse src -Filter *.java | ForEach-Object FullName)
```
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 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)
@@ -26,8 +28,8 @@ IDE metadata targets JDK 26 (`jdk-26.0.1`); the tree also compiles cleanly on JD
Runtime gotchas (all verified):
- On JDK 25, `KNEOptimize.jar`'s `FastLib` reflects into `jdk.internal.misc.Unsafe`; without the two JVM flags above it throws `InaccessibleObjectException` at startup (app still runs).
- Creating the SRv6 TUN adapter (`WintunCreateAdapter`) requires an elevated shell; without admin rights it logs "创建虚拟网卡失败" and continues with only the `inLoopBack` interface — links/bridges still work.
- To disable TUN creation completely (e.g. for non-admin UI/routing testing), set `"TUNName": null` in `klalb-config.json` or leave the TUN Name empty in GUI settings. If omitted, it defaults to `"KLALB_SRv6"`.
- Routing broadcast (`RouterInfo`) transmits `deviceName` across the network, which topology and node overview panels display. `deviceDescription` and `ExtraRoutes` remain local controller configs.
- To disable TUN creation completely (e.g. for non-admin UI/routing testing), set `"enableTUN": false` in `klalb-config.json` or toggle off "启用 TUN 虚拟网卡" in GUI/Web settings.
- Routing broadcast (`RouterInfo`) transmits `deviceName`, which topology and node overview panels display. `deviceDescription` is NOT broadcast — it only leaves the node in full node-info query responses (see srv6 API below); `ExtraRoutes` remain local controller configs.
## Verification
@@ -35,19 +37,63 @@ No test suite, no CI. Classes named `*Test*` (`nathole/`, `ntp/`) are manual `ma
## Architecture
- Entrypoint `org.kne.cloud.network.klalb.KLALBMain`: load config → build `KLALBProxySystem` → open Swing GUI (`KLALBStateGUI3`) unless `"nogui": true` → interactive console (`help`, `links-state`, `route`, `kperf`, ...).
- Entrypoint `org.kne.cloud.network.klalb.KLALBMain`: load config → build `KLALBProxySystem` → open Swing GUI (`KLALBStateGUI3`) unless `"nogui": true` start `KLALBWebServer` (if `"webUI": true` or web server enabled, default port `4665`) → interactive console (`help`, `links-state`, `route`, `kperf`, ...).
- `org.kne.cloud.network.klalb.web.KLALBWebServer` — built-in HTTP/SSE server (JDK `HttpServer`):
- API endpoints: `/api/status`, `/api/events` (SSE stream, 200ms intervals), `/api/links`, `/api/links/action`, `/api/links/reconnect`, `/api/routing-table`, `/api/nodes` (topology graph), `/api/node-info?address=<ipv6>` (on-demand full node info), `/api/config`.
- Static file hosting / SPA fallback: serves `dashboard/dist/` assets directly.
- `org.kne.cloud.network` — generic socket framework: `VirtualSocket*` hierarchy, `SocketBridge` port-forwarding proxies, `ProtocolDetector` (multi-protocol mux on one port), `MultiProtocolSocketAddress` = URI-style addresses (`tcp://`, `udp://`, `kltp://`, `ntp://`) dispatched through the `SocketType` registry.
- `...network.klalb` — app core: `KLALBController` (the virtual SRv6 network), `KLALBRemoteLink` (WAN lines), `*Packet` wire-format classes, virtual socket implementations.
- `...network.congestion` — pluggable congestion control (BBR, Vegas2, DCTCP...), chosen via `"congestionAlgorithm"` in config.
- `...network.kltp` — custom reliable transport protocol (packets/streams).
- `...network.ipv6`, `...network.srv6` — packet codecs, route table, Dijkstra path computation.
- **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.
+1
View File
@@ -249,5 +249,6 @@
</library>
</orderEntry>
<orderEntry type="jdk" jdkName="jdk-26.0.1" jdkType="JavaSDK" />
<orderEntry type="library" name="jfreechart-1.5.3" level="project" />
</component>
</module>
-20
View File
@@ -1,20 +0,0 @@
//加速线路
{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
Submodule
+1
Submodule dashboard added at 885a0c2d89
BIN
View File
Binary file not shown.
-107
View File
@@ -1,107 +0,0 @@
[
{
"language": "ZH_CN",
"nogui": false,
"VirtualAddress": "2486:1:0:0:0:0:0:8889",
"VirtualASN": 2142606939373348329,
"DNS": [
"2486:1:0:0:0:0:0:8888"
],
"TCPListen": "tcp://0.0.0.0:4565",
"UDPListen": "udp://0.0.0.0:4572",
"VirtualSocketName": "kltp",
"LineTable": [
"tcp://kne03.yoyo250.fun:4565",
"tcp://kne04.yoyo250.fun:4565"
],
"ConnectLineTable": [
"tcp://07f4acdef99b.ofalias.net:4565",
"tcp://kne01.yoyo250.fun:4565",
"tcp://kne02.yoyo250.fun:4565"
],
"ntpServerTable": [
"ntp://ntp1.aliyun.com",
"ntp://ntp2.aliyun.com",
"ntp://ntp3.aliyun.com",
"ntp://ntp4.aliyun.com",
"ntp://ntp5.aliyun.com",
"ntp://ntp6.aliyun.com",
"ntp://ntp7.aliyun.com",
"ntp://ntp1.tencent.com",
"ntp://ntp2.tencent.com",
"ntp://ntp4.tencent.com",
"ntp://ntp5.tencent.com",
"ntp://time.google.com",
"ntp://time.apple.com",
"ntp://pool.ntp.org",
"ntp://ntp.ntsc.ac.cn",
"ntp://us.ntp.org.cn"
],
"ExtraRoutes": [],
"denyLineTableQuery": false,
"denyLineTableBroadcast": false,
"congestionAlgorithm": "BBR",
"burstLimit": 2.0,
"delayUpperBound": 1.2,
"delayLowerBound": 1.1,
"nagleDelayTime": 1000000,
"linkNagleDelayTime": 0,
"linkConnectionsCount": 1,
"TUNName": null,
"performanceStrategy": "multiscatter",
"DeviceName": "SerinaNya PC",
"DeviceDescription": "desc",
"NetworkInterfaceExcepts": [],
"Type": "KLALBController"
},
{
"Listen": "kltp://[::0]:5201",
"Bridge": {
"DEFAULT": "SocketBridge"
},
"Connect": {
"DEFAULT": "tcp://127.0.0.1:5201"
},
"Type": "SocketBridge"
},
{
"Listen": "tcp://[::1]:5202",
"Bridge": {
"DEFAULT": "SocketBridge"
},
"Connect": {
"DEFAULT": "kltp://[2486:5acd:e339:4837:a6d9:3aed:4de2:30fd]:5201"
},
"Type": "SocketBridge"
},
{
"Listen": "tcp://127.0.0.1:35000",
"Bridge": {
"DEFAULT": "SocketBridge"
},
"Connect": {
"DEFAULT": "kltp://[2486:1::8888]:23333"
},
"Type": "SocketBridge"
},
{
"Listen": "kltp://[::0]:25565",
"Bridge": {
"DEFAULT": "SocketBridge"
},
"Connect": {
"DEFAULT": "tcp://127.0.0.1:25566"
},
"Type": "SocketBridge"
},
{
"Listen": "tcp://127.0.0.1:35565",
"Bridge": {
"DEFAULT": "SocketBridge"
},
"Connect": {
"DEFAULT": "kltp://[2486:1::8888]:25565"
},
"Type": "SocketBridge"
}
]
-79
View File
@@ -1,79 +0,0 @@
[
{"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
@@ -1,29 +0,0 @@
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
@@ -1,18 +0,0 @@
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
@@ -1,8 +0,0 @@
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服(加速线路)
+11
View File
@@ -0,0 +1,11 @@
{
"version": 1,
"skills": {
"shadcn": {
"source": "shadcn/ui",
"sourceType": "github",
"skillPath": "skills/shadcn/SKILL.md",
"computedHash": "c1a68ee06a668aced9ab2b5fbdea5f989864123794eb2e056b339a072dbb7f10"
}
}
}
+10
View File
@@ -112,3 +112,13 @@ performancestrategy=Performance strategy
singlecore=Single-Core - Cache Affinity First (Best energy/performance ratio, for low-power & cloud)
multifill=Multi-Core - Fill Cores Sequentially (Recommended for general-purpose physical servers)
multiscatter=Multi-Core - Spread Load Evenly (Optimized for multi-socket NUMA architectures)
webapisettings=Web API settings
enablewebapi=Enable Web API
weblistenaddr=Web API listen address
invaildweblistenaddr=Invalid Web API listen address
enabletun=Enable TUN adapter
congestionmonitor=Congestion control monitor
rttbaseline=RTT baseline
rtt=RTT
rttmax=RTT max
rttmin=RTT min
+10
View File
@@ -112,3 +112,13 @@ performancestrategy=性能策略
singlecore=单核-缓存命中率优先(高能耗比,适合低功耗设备、云机)
multifill=多核-负载按顺序填充(适合大多数物理服务器、电脑)
multiscatter=多核-负载均匀打散分配(适合特殊的多路NUMA服务器)
webapisettings=Web API 设置
enablewebapi=启用 Web API
weblistenaddr=Web API 监听地址:端口
invaildweblistenaddr=无效的 Web API 监听地址:端口
enabletun=启用TUN虚拟网卡
congestionmonitor=拥塞控制监视器
rttbaseline=往返延迟基线
rtt=往返延迟
rttmax=往返延迟上限
rttmin=往返延迟下限
@@ -136,4 +136,19 @@ public class BBRCongestionAlgorithm implements CongestionAlgorithm ,DetnetConges
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 {
if(windowUsed*3L>=congressWindowSize) {
congressWindowSize+=4000;
congressWindowSize+=4096;
updateWindowSize();
}
}
@@ -4,4 +4,7 @@ public interface DetnetCongestionAlgorithm extends CongestionAlgorithm {
public void setUpperDelayBound(double upper);
public void setLowerDelayBound(double lower);
public void setBurstLimit(double limit);
public double getUpperDelayBound( );
public double getLowerDelayBound();
public double getBurstLimit();
}
@@ -2,6 +2,8 @@ package org.kne.cloud.network.congestion;
import java.util.function.Consumer;
import org.kne.cloud.network.monitor.DelaySampler;
public class Vegas2CongestionAlgorithm implements CongestionAlgorithm,DetnetCongestionAlgorithm {
// 可配置的时延膨胀比上下限 - Vegas2.0核心参数
@@ -17,6 +19,7 @@ public class Vegas2CongestionAlgorithm implements CongestionAlgorithm,DetnetCong
private Consumer<Long> windowControlConsumer;
private long MIN_RTTVAR = 30000000L;
private volatile long RTTCurr = 1000000000L; // RTT
private volatile long RTTMin = 1000000000L; // BaseRTT
private volatile long RTTVar = 1000000000L;
private volatile long RTTAvg = 1000000000L;
@@ -26,11 +29,6 @@ public class Vegas2CongestionAlgorithm implements CongestionAlgorithm,DetnetCong
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 window2 = MIN_WINDOW;
private volatile long maxwindow = 99999999999L;
@@ -58,106 +56,14 @@ public class Vegas2CongestionAlgorithm implements CongestionAlgorithm,DetnetCong
@Override
public void putAck(long packetSize, long RTTns, long OWDup, 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;
}
// 更新最小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;
}
}
private DelaySampler dsp=new DelaySampler();
@Override
public void putAck(long packetSize, long RTTns, boolean ecn) {
if(RTTns<0) {
throw new IllegalArgumentException("RTT:"+ RTTns+" is negative!");
}
this.RTTCurr=RTTns;
// 处理ECN信号 - 将其视为强烈的拥塞信号
if (ecn) {
// 当收到ECN时,更激进地减少窗口
@@ -167,14 +73,7 @@ public class Vegas2CongestionAlgorithm implements CongestionAlgorithm,DetnetCong
}
}
// 更新最小RTTBaseRTT
if (RTTns <= RTTMin) {
RTTMin = RTTns;
} else {
// 缓慢适应:当网络路径真正变化时,BaseRTT应能缓慢更新
// 这里使用极慢的衰减因子,只有在持续观测到更低RTT时才快速更新
RTTMin = (RTTMin * 999 + RTTns) / 1000;
}
dsp.recordDelay(RTTns);
// 更新平滑RTT估计
@@ -190,6 +89,15 @@ public class Vegas2CongestionAlgorithm implements CongestionAlgorithm,DetnetCong
long curr = System.nanoTime();
// 每个RTT周期调整一次窗口(使用当前估计的RTT)
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;
if (RTTCountx > 0) {
long currentRTT = RTTTotal / RTTCountx;
@@ -206,12 +114,12 @@ public class Vegas2CongestionAlgorithm implements CongestionAlgorithm,DetnetCong
// 2. 基于比值的窗口调整(取代原来的基于差值的调整)
if (delayRatio > delayUpperBound) {
// 时延过高:减小窗口,减少幅度与超标程度成正比
window2 -= 4096;
window2 =window2-4096;
} else if (delayRatio < delayLowerBound) {
// 时延过低:增大窗口,增加幅度与低于目标程度成正比
window2 += 4096;
window2 =window2+4096;
} else {
//window2=window2+4096
}
// ===================================================
@@ -271,10 +179,12 @@ public class Vegas2CongestionAlgorithm implements CongestionAlgorithm,DetnetCong
public long getBaseRTT() {
return RTTMin;
}
public long getCurrentRTT() {
public long getAvgRTT() {
return RTTAvg2;
}
public long getCurrentRTT() {
return RTTCurr;
}
@Override
public void setUpperDelayBound(double upper) {
@@ -295,4 +205,19 @@ public class Vegas2CongestionAlgorithm implements CongestionAlgorithm,DetnetCong
return "Vegas2";
}
@Override
public double getUpperDelayBound() {
return delayUpperBound;
}
@Override
public double getLowerDelayBound() {
return delayLowerBound;
}
@Override
public double getBurstLimit() {
return burstLimit;
}
}
@@ -1,5 +1,8 @@
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.function.BiConsumer;
import java.util.function.Supplier;
@@ -10,8 +13,15 @@ public abstract class AbstractIPv6NetworkLink implements IPv6NetworkLink {
private CopyOnWriteArraySet<IPv6LinkStateListener> listeners = new CopyOnWriteArraySet<>();
private SRv6Router srv6Router;
private final boolean loopback;
public AbstractIPv6NetworkLink(boolean loopback) {
super();
this.loopback = loopback;
}
public SRv6Router getSrv6Router() {
return srv6Router;
}
@@ -53,8 +63,49 @@ public abstract class AbstractIPv6NetworkLink implements IPv6NetworkLink {
}
private BiConsumer<IPv6NetworkLink, Supplier<IPv6Packet>> receiveConsumer;
private Supplier<Long> costSupplier;
@Override
public void setReceiveConsumer(BiConsumer<IPv6NetworkLink,Supplier< IPv6Packet>> 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,6 +61,10 @@ public final class IPv6Address implements Comparable<IPv6Address> {
((long)(bytes[15] & 0xFF));
}
public IPv6Address(String string) throws UnknownHostException {
this((Inet6Address) Inet6Address.getByName(string));
}
/**
* 从两个long创建IPv6地址
*/
@@ -67,7 +67,16 @@ public class IPv6AddressGroup implements Comparable<IPv6AddressGroup>{
prefixLength=in.read();
}
public boolean checkMatch(IPv6Address address2) {
return address2.equals(address.maskWith(IPv6Address.createMask(prefixLength)));
IPv6Address cmsk= 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);
public AtomicInteger getRerouteCounter() {
public AtomicInteger getRouteCounter() {
return rerouteCounter;
}
@@ -41,6 +41,7 @@ public class IPv6TUNLoopbackNetworkLink extends AbstractIPv6NetworkLink
private Lock slok = new SpinLock();
public IPv6TUNLoopbackNetworkLink(String name,IPv6AddressGroup hostAddress, int mtu, List<InetAddress> dns) throws IOException {
super(true);
if (tun != null)
throw new IllegalStateException("already open!");
try {
@@ -204,13 +205,6 @@ public class IPv6TUNLoopbackNetworkLink extends AbstractIPv6NetworkLink
LockSupport.unpark(tr);
}
@Override
public boolean isLoopBack() {
return true;
}
@Override
public List<Neighbor> getNeighborsInfo() {
List<Neighbor> hs = new ArrayList<>();
@@ -219,10 +213,6 @@ public class IPv6TUNLoopbackNetworkLink extends AbstractIPv6NetworkLink
return hs;
}
/*
* @Override public boolean isCongress(IPv6Packet iPv6Packet,double scale) {
* return sendQueue.size() > 1000*scale; }
*/
@Override
public String getName() {
@@ -255,27 +245,4 @@ public class IPv6TUNLoopbackNetworkLink extends AbstractIPv6NetworkLink
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,6 +32,7 @@ public class LoopbackIPv6NetworkLink extends AbstractIPv6NetworkLink implements
//new Inet6AddressGroup(loopbackAddress, 128) new Inet6AddressGroup((Inet6Address) Inet6Address.getByName("::1"), 128)
public LoopbackIPv6NetworkLink(List<IPv6AddressGroup> addressGroupsx,SRv6Router router) {
super(true);
this.addressGroups .addAll( addressGroupsx);
}
@@ -54,10 +55,7 @@ public class LoopbackIPv6NetworkLink extends AbstractIPv6NetworkLink implements
}
}
@Override
public boolean isLoopBack() {
return true;
}
@Override
public List<Neighbor> getNeighborsInfo() {
@@ -93,29 +91,6 @@ public class LoopbackIPv6NetworkLink extends AbstractIPv6NetworkLink implements
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() {
+9 -10
View File
@@ -4,6 +4,11 @@ import java.util.Objects;
import java.util.function.Supplier;
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 IPv6Address nexthop;
private IPv6NetworkLink destlink;
@@ -12,7 +17,6 @@ public class RouteItem implements Comparable<RouteItem>,Cloneable{
private long cost;
private String flag;
private Supplier<Long> costSupplier;
private boolean isLoopback;
public String getFlag() {
return flag;
}
@@ -34,21 +38,20 @@ public class RouteItem implements Comparable<RouteItem>,Cloneable{
&& pre == other.pre && Objects.equals(proto, other.proto);
}
public RouteItem(IPv6AddressGroup destination, IPv6Address nexthop, IPv6NetworkLink destlink, String proto, int pre,
long cost,String flag,boolean isLoopback) {
long cost,String flag) {
super();
this.destination = destination;
this.destination = destination.toNetworkRoute();
this.nexthop = nexthop;
this.destlink = destlink;
this.proto = proto;
this.pre = pre;
this.cost = cost;
this.flag=flag;
this.isLoopback=isLoopback;
}
public RouteItem(IPv6AddressGroup destination, IPv6Address nexthop, IPv6NetworkLink destlink, String proto, int pre,
long cost,Supplier<Long> costSupplier,String flag,boolean isLoopback) {
long cost,Supplier<Long> costSupplier,String flag) {
super();
this.destination = destination;
this.destination = destination.toNetworkRoute();
this.nexthop = nexthop;
this.destlink = destlink;
this.proto = proto;
@@ -56,7 +59,6 @@ public class RouteItem implements Comparable<RouteItem>,Cloneable{
this.cost = cost;
this.costSupplier=costSupplier;
this.flag=flag;
this.isLoopback=isLoopback;
}
public IPv6AddressGroup getDestination() {
return destination;
@@ -73,9 +75,6 @@ public class RouteItem implements Comparable<RouteItem>,Cloneable{
public int getPre() {
return pre;
}
public boolean isLoopback() {
return isLoopback;
}
@Override
public String toString() {
return getDestination()+"\t"+getProto()+"\t"+getPre()+"\t"+getCost()+"\t"+getFlag()+"\t"+getNexthop()+"\t"+getDestlink().getName();
@@ -13,6 +13,10 @@ import org.kne.cloud.network.ipv6.RouteItem;
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 KLTP_VIRTUAL_SOCKET_NAME="kltp";
public static final int itemwidth = 1000;
public static final int linepanelheight = 41;
@@ -9,6 +9,7 @@ import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.reflect.TypeToken;
@@ -61,6 +62,7 @@ public class KLALBConfigItem {
String s=jobj.get("Type").getAsString();
switch(s) {
case "KLALBController":
normalizeLegacyControllerKeys(jobj);
return arg2.deserialize(arg0, new TypeToken<KLALBControllerConfigItem>() {}.getType());
case "SocketBridge":
return arg2.deserialize(arg0, new TypeToken<SocketBridgeConfigItem>() {}.getType());
@@ -70,6 +72,39 @@ public class KLALBConfigItem {
}
throw new JsonParseException("not a object:"+arg0);
}
/**
* 将历史命名错误的配置键归一化为当前键名仅在当前键名不存在时生效
* openConnections/lineTable externalEndpoints
* denyConnectionQuery/denyLineTableQuery denyExternalEndpointQuery
* denyConnectionBroadcast/denyLineTableBroadcast denyExternalEndpointBroadcast
*/
private void normalizeLegacyControllerKeys(JsonObject jobj) {
renameLegacyKey(jobj,"externalEndpoints","openConnections","OpenConnections","lineTable","LineTable");
renameLegacyKey(jobj,"denyExternalEndpointQuery","denyConnectionQuery","denyLineTableQuery");
renameLegacyKey(jobj,"denyExternalEndpointBroadcast","denyConnectionBroadcast","denyLineTableBroadcast");
if (!jobj.has("webListen") && jobj.has("webPort") && !jobj.get("webPort").isJsonNull()) {
int port = jobj.get("webPort").getAsInt();
jobj.add("webListen", new JsonPrimitive("http://0.0.0.0:" + port));
}
jobj.remove("webPort");
}
private void renameLegacyKey(JsonObject jobj, String newKey, String... legacyKeys) {
if (jobj.has(newKey)) {
for (String legacyKey : legacyKeys) {
jobj.remove(legacyKey);
}
return;
}
for (String legacyKey : legacyKeys) {
if (jobj.has(legacyKey)) {
jobj.add(newKey, jobj.get(legacyKey));
jobj.remove(legacyKey);
return;
}
}
}
};
}
public static JsonSerializer<KLALBConfigItem>getDefaultJsonSerializer(){
@@ -76,7 +76,7 @@ public class KLALBController {
new HashMapTimestampMonitor<UUID>(HighAccuracyClock.SYSTEM_CLOCK, "up", 100, TIME_WINDOW),
new HashMapTimestampMonitor<UUID>(HighAccuracyClock.SYSTEM_CLOCK, "down", 100, TIME_WINDOW));
private List<MultiProtocolSocketAddress> selflineTable = new ArrayList<>();
private List<MultiProtocolSocketAddress> externalEndpoints = new ArrayList<>();
private List<MultiProtocolSocketAddress> listensSocketAddress = new CopyOnWriteArrayList<>();
@@ -86,7 +86,7 @@ public class KLALBController {
private static final boolean debug=false;
private static final boolean showpacket = false;
private static final int PREFIX = 128;// 112
private static final int PREFIX = 128;
private static final int DISCOVERY_PORT = 4569;
private static List<IPMulticastDiscovery> ipmd = new ArrayList<>();
@@ -137,9 +137,9 @@ public class KLALBController {
MultiProtocolSocketAddress bind = new MultiProtocolSocketAddress(tcpl.getProtocol(),
inetAddress.getHostAddress(), tcpl.getPort());
// System.out.println(bind);
synchronized (selflineTable) {
if (!selflineTable.contains(bind)) {
selflineTable.add(bind);
synchronized (externalEndpoints) {
if (!externalEndpoints.contains(bind)) {
externalEndpoints.add(bind);
}
}
}
@@ -210,7 +210,7 @@ public class KLALBController {
for (Iterator<IPMulticastDiscovery> iterator = ipmd.iterator(); iterator.hasNext(); ) {
IPMulticastDiscovery ipMulticastDiscovery = (IPMulticastDiscovery) iterator.next();
if (ipMulticastDiscovery.isClosed() || (!ipMulticastDiscovery.getInterface().isUp())
|| (configItem != null && configItem.isDenyLineTableBroadcast())) {
|| (configItem != null && configItem.isDenyExternalEndpointBroadcast())) {
iterator.remove();
try {
ipMulticastDiscovery.close();
@@ -222,7 +222,7 @@ public class KLALBController {
}
}
if (configItem != null && configItem.isDenyLineTableBroadcast()) {
if (configItem != null && configItem.isDenyExternalEndpointBroadcast()) {
} else {
List<NetworkInterface> interfaceList = networkInterfaceManager.getAllAvaliableNetworkInterface();
@@ -299,7 +299,7 @@ public class KLALBController {
private boolean checkIsSelf(MultiProtocolSocketAddress inetAddress) throws UnknownHostException {
return inetAddress.getInetAddress().isAnyLocalAddress() || inetAddress.getInetAddress().isLoopbackAddress()
|| selflineTable.contains(inetAddress);
|| externalEndpoints.contains(inetAddress);
}
private boolean checkIsSelfLocator(InetAddress inetAddress) {
@@ -339,9 +339,9 @@ public class KLALBController {
try {
InetSocketAddress iaddr = new InetSocketAddress(neighbor.getAddress().toInet6Address(),
KLALBRoutingProtocol.DEFAULT_PORT);
apiClient.requestOpenLines(iaddr, (v) -> {
apiClient.requestNodeInfoFull(iaddr, (v) -> {
ThreadTool.makeVDaemonThreadIfSupport("线路添加任务", () -> {
for (MultiProtocolSocketAddress msa : v) {
for (MultiProtocolSocketAddress msa : v.getOpenLines()) {
// System.out.print(msa);
addRemoteLines(msa);
}
@@ -401,8 +401,8 @@ public class KLALBController {
}
public List<MultiProtocolSocketAddress> getSelflineTable() {
return selflineTable;
public List<MultiProtocolSocketAddress> getExternalEndpoints() {
return externalEndpoints;
}
@@ -593,18 +593,18 @@ public class KLALBController {
lineslock.writeLock().lock();
try {
krs.startIO();
String selflineTable = generateSelfLineTable();
if (selflineTable != null && !selflineTable.equals(""))
krs.sendPacket(new ADDLINESPacket(selflineTable));
String externalEndpointsText = generateExternalEndpointsString();
if (externalEndpointsText != null && !externalEndpointsText.equals(""))
krs.sendPacket(new ADDLINESPacket(externalEndpointsText));
srv6Router.getLinkTabel().add(krs);
} finally {
lineslock.writeLock().unlock();
}
}
private String generateSelfLineTable() {
private String generateExternalEndpointsString() {
StringBuilder sbd = new StringBuilder();
for (Iterator<MultiProtocolSocketAddress> iterator = selflineTable.iterator(); iterator.hasNext();) {
for (Iterator<MultiProtocolSocketAddress> iterator = externalEndpoints.iterator(); iterator.hasNext();) {
MultiProtocolSocketAddress klalbRemoteLine = (MultiProtocolSocketAddress) iterator.next();
sbd.append(klalbRemoteLine.toString());
sbd.append('\n');
@@ -634,7 +634,8 @@ public class KLALBController {
private void loadSRv6ProtocolStack(IPv6AddressGroup selfx, boolean enableVirtualAdapter) {
networkInterfaceManager.getInetAddressesExcept().add(selfx.getAddress().toInet6Address());
srv6Router = new SRv6Router(selfx, clock);
IPv6AddressGroup group=new IPv6AddressGroup(selfx.getAddress(),32);
srv6Router = new SRv6Router(selfx,group, clock);
if(configItem!=null) {
srv6Router.setPerformanceStrategy(PerformanceStrategy.fromDescription(configItem.getPerformanceStrategy()));
srv6Router.setDeviceName(configItem.getDeviceName());
@@ -647,13 +648,13 @@ public class KLALBController {
rawPortBinder= new PortBinder(this.getSelf().getAddress());
System.out.println(" Loaded: SRv6 Stack");
String name=(configItem!=null)?configItem.getTUNName():CONST.KLALB_S_RV6;
boolean enableTUN = enableVirtualAdapter && (name != null) && !name.trim().isEmpty() && !name.trim().equalsIgnoreCase("null");
String name = (configItem != null && configItem.getTUNName() != null) ? configItem.getTUNName() : CONST.KLALB_S_RV6;
boolean isEnabled = configItem == null || configItem.isEnableTUN();
boolean enableTUN = enableVirtualAdapter && isEnabled && (name != null) && !name.trim().isEmpty() && !name.trim().equalsIgnoreCase("null");
if (enableTUN) {
Thread t=new Thread(()->{
try {
IPv6TUNLoopbackNetworkLink tunlink = new IPv6TUNLoopbackNetworkLink(name.trim(),
new IPv6AddressGroup(srv6Router.getLocator().getAddress(), 32), SRv6Router.MTU, dnsAddresses);
IPv6TUNLoopbackNetworkLink tunlink = new IPv6TUNLoopbackNetworkLink(name.trim(),group, SRv6Router.MTU, dnsAddresses);
tunlink.setMonitor(datatMonitor);
// srv6Router.getLinkTabel().add(tunlink);
srv6Router.getinLoopback().setFallbackLink(tunlink);
@@ -686,7 +687,7 @@ public class KLALBController {
// System.out.println(context);
try {
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() {
@Override
@@ -697,7 +698,7 @@ public class KLALBController {
for (Neighbor neighbor : nb) {
if (neighbor.getLocator() != null)
sp.add(new NTPPeer(
new MultiProtocolSocketAddress(iproxyname + "_Datagram",
new MultiProtocolSocketAddress(iproxyname + "datagram",
neighbor.getLocator().getAddress().toString(), 123),
NTPv4Packet.NTP_SYMMETRIC_ACTIVE));
@@ -800,18 +801,18 @@ public class KLALBController {
getIpv6Router().setASN(vasn);
}
List<MultiProtocolSocketAddress> linele = configItem.getLineTable();
List<MultiProtocolSocketAddress> linele = configItem.getExternalEndpoints();
if (linele != null) {
getSelflineTable().addAll(linele);
getExternalEndpoints().addAll(linele);
}
List<MultiProtocolSocketAddress> linetoc = configItem.getConnectLineTable();
List<MultiProtocolSocketAddress> linetoc = configItem.getAutoConnections();
if (linetoc != null) {
linetoc.forEach((aline) -> {
addRemoteLines(aline);
});
}
List<MultiProtocolSocketAddress> ntps = configItem.getNtpServerTable();
List<MultiProtocolSocketAddress> ntps = configItem.getNtpServers();
if (ntps != null) {
getNTPTable().addAll(ntps);
}
@@ -977,8 +978,8 @@ public class KLALBController {
public void registerToProxyTypeAs(String proxyname) {
MultiProtocolSocketAddress.getSocketTypeRegister().put(proxyname + "_Stream", streamSocketType);
MultiProtocolSocketAddress.getSocketTypeRegister().put(proxyname + "_Datagram", datagramSocketType);
MultiProtocolSocketAddress.getSocketTypeRegister().put(proxyname + "stream", streamSocketType);
MultiProtocolSocketAddress.getSocketTypeRegister().put(proxyname + "datagram", datagramSocketType);
// ProxyProfileEntry.getRegister().put(proxyname, this);
}
@@ -16,12 +16,12 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
private MultiProtocolSocketAddress TCPListen=new MultiProtocolSocketAddress("0.0.0.0",4565);
private MultiProtocolSocketAddress UDPListen=new MultiProtocolSocketAddress("udp","0.0.0.0",4565);
private String VirtualSocketName;
private List<MultiProtocolSocketAddress>LineTable=new ArrayList<>();
private List<MultiProtocolSocketAddress>ConnectLineTable=new ArrayList<>();
private List<MultiProtocolSocketAddress>ntpServerTable=new ArrayList<>();
private List<String>ExtraRoutes=new ArrayList<>();
private boolean denyLineTableQuery=false;
private boolean denyLineTableBroadcast=false;
private List<MultiProtocolSocketAddress> externalEndpoints = new ArrayList<>();
private List<MultiProtocolSocketAddress> autoConnections = new ArrayList<>();
private List<MultiProtocolSocketAddress> ntpServers = new ArrayList<>();
private List<String> ExtraRoutes = new ArrayList<>();
private boolean denyExternalEndpointQuery = false;
private boolean denyExternalEndpointBroadcast = false;
private String congestionAlgorithm="BBR";
private double burstLimit=1.50;
private double delayUpperBound=1.20;
@@ -29,10 +29,37 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
private long nagleDelayTime=1000000L;
private long linkNagleDelayTime=1000000L;
private int linkConnectionsCount=1;
private boolean enableTUN = true;
private String TUNName=CONST.KLALB_S_RV6;
private String performanceStrategy="multifill";
private String DeviceName;
private String DeviceDescription;
private boolean webUI = false;
private MultiProtocolSocketAddress webListen = new MultiProtocolSocketAddress("http", "0.0.0.0", 4665);
public boolean isEnableTUN() {
return enableTUN;
}
public void setEnableTUN(boolean enableTUN) {
this.enableTUN = enableTUN;
}
public boolean isWebUI() {
return webUI;
}
public void setWebUI(boolean webUI) {
this.webUI = webUI;
}
public MultiProtocolSocketAddress getWebListen() {
return webListen;
}
public void setWebListen(MultiProtocolSocketAddress webListen) {
this.webListen = webListen;
}
public String getPerformanceStrategy() {
return performanceStrategy;
@@ -177,39 +204,44 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
public List<MultiProtocolSocketAddress> getLineTable() {
return LineTable;
public List<MultiProtocolSocketAddress> getExternalEndpoints() {
return externalEndpoints;
}
public void setLineTable(List<MultiProtocolSocketAddress> lineTable) {
LineTable = lineTable;
public void setExternalEndpoints(List<MultiProtocolSocketAddress> externalEndpoints) {
this.externalEndpoints = externalEndpoints;
}
public List<MultiProtocolSocketAddress> getAutoConnections() {
return autoConnections;
}
public void setAutoConnections(List<MultiProtocolSocketAddress> autoConnections) {
this.autoConnections = autoConnections;
}
public List<MultiProtocolSocketAddress> getConnectLineTable() {
return ConnectLineTable;
return autoConnections;
}
public void setConnectLineTable(List<MultiProtocolSocketAddress> connectLineTable) {
ConnectLineTable = connectLineTable;
this.autoConnections = connectLineTable;
}
public List<MultiProtocolSocketAddress> getNtpServers() {
return ntpServers;
}
public void setNtpServers(List<MultiProtocolSocketAddress> ntpServers) {
this.ntpServers = ntpServers;
}
public List<MultiProtocolSocketAddress> getNtpServerTable() {
return ntpServerTable;
return ntpServers;
}
public void setNtpServerTable(List<MultiProtocolSocketAddress> ntpServerTable) {
this.ntpServerTable = ntpServerTable;
this.ntpServers = ntpServerTable;
}
public List<String> getExtraRoutes() {
@@ -280,23 +312,20 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
public boolean isDenyLineTableQuery() {
return denyLineTableQuery;
public boolean isDenyExternalEndpointQuery() {
return denyExternalEndpointQuery;
}
public void setDenyLineTableQuery(boolean denyLineTableQuery) {
this.denyLineTableQuery = denyLineTableQuery;
public void setDenyExternalEndpointQuery(boolean denyExternalEndpointQuery) {
this.denyExternalEndpointQuery = denyExternalEndpointQuery;
}
public boolean isDenyLineTableBroadcast() {
return denyLineTableBroadcast;
public boolean isDenyExternalEndpointBroadcast() {
return denyExternalEndpointBroadcast;
}
public void setDenyLineTableBroadcast(boolean denyLineTableBroadcast) {
this.denyLineTableBroadcast = denyLineTableBroadcast;
public void setDenyExternalEndpointBroadcast(boolean denyExternalEndpointBroadcast) {
this.denyExternalEndpointBroadcast = denyExternalEndpointBroadcast;
}
@@ -352,12 +381,12 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
", TCPListen=" + TCPListen +
", UDPListen=" + UDPListen +
", VirtualSocketName='" + VirtualSocketName + '\'' +
", LineTable=" + LineTable +
", ConnectLineTable=" + ConnectLineTable +
", ntpServerTable=" + ntpServerTable +
", externalEndpoints=" + externalEndpoints +
", autoConnections=" + autoConnections +
", ntpServers=" + ntpServers +
", ExtraRoutes=" + ExtraRoutes +
", denyLineTableQuery=" + denyLineTableQuery +
", denyLineTableBroadcast=" + denyLineTableBroadcast +
", denyExternalEndpointQuery=" + denyExternalEndpointQuery +
", denyExternalEndpointBroadcast=" + denyExternalEndpointBroadcast +
", congestionAlgorithm='" + congestionAlgorithm + '\'' +
", burstLimit=" + burstLimit +
", delayUpperBound=" + delayUpperBound +
@@ -365,11 +394,14 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
", nagleDelayTime=" + nagleDelayTime +
", linkNagleDelayTime=" + linkNagleDelayTime +
", linkConnectionsCount=" + linkConnectionsCount +
", enableTUN=" + enableTUN +
", TUNName='" + TUNName + '\'' +
", performanceStrategy='" + performanceStrategy + '\'' +
", DeviceName='" + DeviceName + '\'' +
", DeviceDescription='" + DeviceDescription + '\'' +
", NetworkInterfaceExcepts=" + NetworkInterfaceExcepts +
", webUI=" + webUI +
", webListen=" + webListen +
'}';
}
@@ -378,11 +410,11 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
KLALBControllerConfigItem that = (KLALBControllerConfigItem) o;
return nogui == that.nogui && denyLineTableQuery == that.denyLineTableQuery && denyLineTableBroadcast == that.denyLineTableBroadcast && Double.compare(burstLimit, that.burstLimit) == 0 && Double.compare(delayUpperBound, that.delayUpperBound) == 0 && Double.compare(delayLowerBound, that.delayLowerBound) == 0 && nagleDelayTime == that.nagleDelayTime && linkNagleDelayTime == that.linkNagleDelayTime && linkConnectionsCount == that.linkConnectionsCount && Objects.equals(language, that.language) && Objects.equals(VirtualAddress, that.VirtualAddress) && Objects.equals(VirtualASN, that.VirtualASN) && Objects.equals(DNS, that.DNS) && Objects.equals(TCPListen, that.TCPListen) && Objects.equals(UDPListen, that.UDPListen) && Objects.equals(VirtualSocketName, that.VirtualSocketName) && Objects.equals(LineTable, that.LineTable) && Objects.equals(ConnectLineTable, that.ConnectLineTable) && Objects.equals(ntpServerTable, that.ntpServerTable) && Objects.equals(ExtraRoutes, that.ExtraRoutes) && Objects.equals(congestionAlgorithm, that.congestionAlgorithm) && Objects.equals(TUNName, that.TUNName) && Objects.equals(performanceStrategy, that.performanceStrategy) && Objects.equals(DeviceName, that.DeviceName) && Objects.equals(DeviceDescription, that.DeviceDescription) && Objects.equals(NetworkInterfaceExcepts, that.NetworkInterfaceExcepts);
return nogui == that.nogui && enableTUN == that.enableTUN && webUI == that.webUI && denyExternalEndpointQuery == that.denyExternalEndpointQuery && denyExternalEndpointBroadcast == that.denyExternalEndpointBroadcast && Double.compare(burstLimit, that.burstLimit) == 0 && Double.compare(delayUpperBound, that.delayUpperBound) == 0 && Double.compare(delayLowerBound, that.delayLowerBound) == 0 && nagleDelayTime == that.nagleDelayTime && linkNagleDelayTime == that.linkNagleDelayTime && linkConnectionsCount == that.linkConnectionsCount && Objects.equals(webListen, that.webListen) && Objects.equals(language, that.language) && Objects.equals(VirtualAddress, that.VirtualAddress) && Objects.equals(VirtualASN, that.VirtualASN) && Objects.equals(DNS, that.DNS) && Objects.equals(TCPListen, that.TCPListen) && Objects.equals(UDPListen, that.UDPListen) && Objects.equals(VirtualSocketName, that.VirtualSocketName) && Objects.equals(externalEndpoints, that.externalEndpoints) && Objects.equals(autoConnections, that.autoConnections) && Objects.equals(ntpServers, that.ntpServers) && Objects.equals(ExtraRoutes, that.ExtraRoutes) && Objects.equals(congestionAlgorithm, that.congestionAlgorithm) && Objects.equals(TUNName, that.TUNName) && Objects.equals(performanceStrategy, that.performanceStrategy) && Objects.equals(DeviceName, that.DeviceName) && Objects.equals(DeviceDescription, that.DeviceDescription) && Objects.equals(NetworkInterfaceExcepts, that.NetworkInterfaceExcepts);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), language, nogui, VirtualAddress, VirtualASN, DNS, TCPListen, UDPListen, VirtualSocketName, LineTable, ConnectLineTable, ntpServerTable, ExtraRoutes, denyLineTableQuery, denyLineTableBroadcast, congestionAlgorithm, burstLimit, delayUpperBound, delayLowerBound, nagleDelayTime, linkNagleDelayTime, linkConnectionsCount, TUNName, performanceStrategy, DeviceName, DeviceDescription, NetworkInterfaceExcepts);
return Objects.hash(super.hashCode(), language, nogui, VirtualAddress, VirtualASN, DNS, TCPListen, UDPListen, VirtualSocketName, externalEndpoints, autoConnections, ntpServers, ExtraRoutes, denyExternalEndpointQuery, denyExternalEndpointBroadcast, congestionAlgorithm, burstLimit, delayUpperBound, delayLowerBound, nagleDelayTime, linkNagleDelayTime, linkConnectionsCount, enableTUN, TUNName, performanceStrategy, DeviceName, DeviceDescription, NetworkInterfaceExcepts, webUI, webListen);
}
}
+45 -11
View File
@@ -22,8 +22,7 @@ import org.kne.cloud.network.klalb.ui.UIEnv;
import org.kne.cloud.network.perf.Kperf;
import org.kne.cloud.network.perf.NodeBenchmark;
import org.kne.debug.TimeDebugger;
import org.kne.membandboost.MembandBenchmark;
import org.kne.membandboost.ObjectPoolBandwidthTest;
import org.kne.membandboost.benchmark.*;;
public class KLALBMain {
public static KLALBStateGUI3 ksg;
@@ -52,13 +51,15 @@ public class KLALBMain {
}catch(Throwable e) {
e.printStackTrace();
}
try {
if(kpcje.getControllerConfig() != null && kpcje.getControllerConfig().isWebUI()) {
kpcje.enableWebServer();
}
} catch(Throwable e) {
System.err.println("Failed to start web server: " + e.getMessage());
}
dtb.putTime("UI");
//dtb.print();
/*MultipurposeSocketAddress mpa=new MultipurposeSocketAddress("127.9.9.9", 49573);
kpcje.enableRemoteManagement(mpa);
System.out.println("远程管理端口已在"+mpa+"端口上开启");*/
/*if(true)
return;*/
ServerSocketChannel kpsvr=KLALBVirtualServerSocketChannel.open(kpcje.getKlalbController());
kpsvr.bind(new InetSocketAddress("::0", 4564));
SocketChannelListener stlr=new SocketChannelListener(kpsvr);
@@ -84,6 +85,7 @@ public class KLALBMain {
case "help":
System.out.println(" help / ?: see help");
System.out.println(" monitor: show monitor GUI");
System.out.println(" web [start|stop|status <port>]: manage web dashboard");
System.out.println(" links-state: query link states");
System.out.println(" links-add <addr:port>: add link");
System.out.println(" links-remove <addr:port>: remove link");
@@ -102,6 +104,42 @@ public class KLALBMain {
e.printStackTrace();
}
break;
case "web":
if(sc.length >= 2) {
String action = sc[1].toLowerCase();
if("start".equals(action)) {
int p = 4665;
if(sc.length >= 3) {
try { p = Integer.parseInt(sc[2]); } catch (NumberFormatException ignored) {}
} else if(kpcje.getControllerConfig() != null && kpcje.getControllerConfig().getWebListen() != null) {
p = kpcje.getControllerConfig().getWebListen().getPort();
}
try {
kpcje.enableWebServer(p);
System.out.println("Web dashboard started on http://localhost:" + p);
} catch(Exception e) {
System.out.println("Failed to start web server: " + e.getMessage());
}
} else if("stop".equals(action)) {
kpcje.disableWebServer();
System.out.println("Web dashboard stopped.");
} else if("status".equals(action)) {
if(kpcje.isWebServerEnabled()) {
System.out.println("Web dashboard is running on port " + kpcje.getWebServer().getPort());
} else {
System.out.println("Web dashboard is stopped.");
}
} else {
System.out.println("Usage: web [start|stop|status <port>]");
}
} else {
if(kpcje.isWebServerEnabled()) {
System.out.println("Web dashboard is running on port " + kpcje.getWebServer().getPort());
} else {
System.out.println("Web dashboard is not running. Use 'web start [port]' to start.");
}
}
break;
case "links-state":
System.out.println("links state");
//System.out.println("状态\t上传流量\t下载流量\t上传速度\t下载速度\t上传延迟\t下载延迟\t上传抖动\t下载抖动");
@@ -200,10 +238,6 @@ public class KLALBMain {
case "membandbenchmark":
MembandBenchmark.main(args);
break;
case "memqueuebenchmark":
ObjectPoolBandwidthTest.main(args);
break;
//case "$$SYSTEM:":
//System.out.println();
//break;
@@ -5,6 +5,8 @@ import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Type;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.HashSet;
@@ -12,18 +14,25 @@ import org.kne.cloud.network.*;
import org.kne.cloud.network.klalb.ui.KLALBStateGUI3;
import org.kne.cloud.network.klalb.ui.Language;
import org.kne.cloud.network.klalb.ui.UIEnv;
import org.kne.cloud.network.klalb.web.KLALBWebServer;
import java.util.Set;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.JsonParser;
public class KLALBProxySystem {
private Set<Proxy> proxys=new HashSet<>();
private KLALBController klalbController;
private KLALBRemoteManagement krm;
private KLALBWebServer webServer;
private KLALBConfig config;
private Gson gson;
private File jsonFile;
@@ -31,8 +40,28 @@ public class KLALBProxySystem {
GsonBuilder gb=new GsonBuilder().setPrettyPrinting();
MultiProtocolSocketAddress.registerToGsonBuilder(gb);
KLALBConfigItem.registerToGsonBuilder(gb);
gb.registerTypeAdapter(InetAddress.class, new JsonSerializer<InetAddress>() {
@Override
public JsonElement serialize(InetAddress src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src.getHostAddress());
}
});
gb.registerTypeAdapter(InetAddress.class, new JsonDeserializer<InetAddress>() {
@Override
public InetAddress deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
try {
return InetAddress.getByName(json.getAsString());
} catch (Exception e) {
throw new JsonParseException(e);
}
}
});
gson=gb.create();
}
public Gson getGson() {
return gson;
}
public Set<Proxy> getProxys() {
return proxys;
}
@@ -55,30 +84,38 @@ public class KLALBProxySystem {
public KLALBProxySystem() {
}
public void enableRemoteManagement() throws IOException {
if(krm==null) {
krm=new KLALBRemoteManagement(this);
}else {
throw new IllegalStateException("Remote Management already enabled!");
public void enableWebServer() throws IOException {
KLALBControllerConfigItem cci = getControllerConfig();
MultiProtocolSocketAddress listen = cci == null || cci.getWebListen() == null
? new MultiProtocolSocketAddress("http", "0.0.0.0", 4665) : cci.getWebListen();
enableWebServer(listen);
}
public void enableWebServer(int port) throws IOException {
enableWebServer(new MultiProtocolSocketAddress("http", "0.0.0.0", port));
}
public void enableWebServer(MultiProtocolSocketAddress listen) throws IOException {
if (webServer == null) {
webServer = new KLALBWebServer(this, listen);
webServer.start();
} else {
throw new IllegalStateException("Web server already enabled!");
}
}
public void enableRemoteManagement(MultiProtocolSocketAddress bind) throws IOException {
if(krm==null) {
krm=new KLALBRemoteManagement(this,bind);
}else {
throw new IllegalStateException("Remote Management already enabled!");
}
public boolean isWebServerEnabled() {
return webServer != null && webServer.isRunning();
}
public boolean isRemoteManagementEnabled() {
return krm!=null;
public KLALBWebServer getWebServer() {
return webServer;
}
public void disableRemoteManagement() {
if(krm!=null) {
krm.close();
krm=null;
public void disableWebServer() {
if (webServer != null) {
webServer.stop();
webServer = null;
}
}
@@ -131,9 +168,11 @@ public class KLALBProxySystem {
String vsne=kcci.getVirtualSocketName();
//if(vsne!=null) {
if(vsne!=null) {
MultiProtocolSocketAddress.getSocketTypeRegister().put(vsne, klalbController.getStreamSocketType());
//}
}else{
MultiProtocolSocketAddress.getSocketTypeRegister().put(CONST.KLTP_VIRTUAL_SOCKET_NAME, klalbController.getStreamSocketType());
}
MultiProtocolSocketAddress tcple=kcci.getTCPListen();
if(tcple!=null) {
@@ -195,30 +234,24 @@ public class KLALBProxySystem {
}
private KLALBStateGUI3 kgui;
public KLALBStateGUI3 getKLALBGUI() {
if(kgui==null) {
kgui=new KLALBStateGUI3(klalbController);
kgui.loadConfig(config);
kgui.setSaveComsumer((cfg)->{
String json=gson.toJson(cfg);
if(jsonFile!=null) {
FileWriter fw = null;
try {
fw=new FileWriter(jsonFile);
public void saveConfigToFile() {
if (jsonFile != null && config != null) {
String json = gson.toJson(config);
try (FileWriter fw = new FileWriter(jsonFile)) {
fw.write(json);
}catch(IOException e) {
e.printStackTrace();
}finally {
if(fw!=null)
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private KLALBStateGUI3 kgui;
public KLALBStateGUI3 getKLALBGUI() {
if(kgui==null) {
kgui=new KLALBStateGUI3(klalbController);
kgui.loadConfig(config);
kgui.setSaveComsumer((cfg)->{
saveConfigToFile();
});
}
return kgui;
@@ -142,15 +142,18 @@ public class KLALBRemoteLink extends AbstractControlledIPv6NetworkLink implement
}
public KLALBRemoteLink(KLALBController controller,KLALBPacketLink kpl, IPv6AddressGroup address) {
super(false);
this.klalbController=controller;
sysclk = klalbController.getClock();
this.kplink = kpl;
this.addressGroup = address;
monitor=new SpeedAndTrafficAndDelayMonitorDataImpl(sysclk);
name=kpl.toString();
setCostSupplier( CostSupplierFactory.owdSupplier(monitor));
}
private KLALBRemoteLink(KLALBController controller, MultiProtocolSocketAddress mpa, MultiProtocolSocketAddress bindaddr, IPv6AddressGroup address) {
super(false);
this.klalbController=controller;
sysclk = klalbController.getClock();
this.socketAddress = mpa;
@@ -162,6 +165,7 @@ public class KLALBRemoteLink extends AbstractControlledIPv6NetworkLink implement
} else {
name=bindaddr.toString() + "" + mpa.toString();
}
setCostSupplier( CostSupplierFactory.owdSupplier( monitor));
}
@@ -854,10 +858,6 @@ public class KLALBRemoteLink extends AbstractControlledIPv6NetworkLink implement
}
}
@Override
public boolean isLoopBack() {
return false;
}
BandwidthDistributer bandwidthDistributer=new BandwidthDistributer<>();
@Override
public List<Neighbor> getNeighborsInfo() {
@@ -946,31 +946,28 @@ public class KLALBRemoteLink extends AbstractControlledIPv6NetworkLink implement
return (!isClosed()) && (status.getState() == LinkStatus.UP) ;
}
@Override
/*@Override
public List<RouteItem> getRouteItems() {
List<RouteItem> rlist = new ArrayList<>();
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(); ) {
Neighbor addresses = (Neighbor) iteratorx.next();
RouteItem ri = new RouteItem(new IPv6AddressGroup(addresses.getAddress().getAddress(), 128),
addresses.getAddress().getAddress(), this, "Direct", 0, 128, CostSupplierFactory.expectedDelaySupplier((DelayMonitorData) addresses.getMonitor(), status, algorithm::getRTO), "D", false);
addresses.getAddress().getAddress(), this, RouteItem.SRv6_ENDXSID, 0, 128, CostSupplierFactory.expectedDelaySupplier((DelayMonitorData) addresses.getMonitor(), status, algorithm::getRTO), "D");
rlist.add(ri);
if (addresses.getLocator() != null) {
RouteItem ris = new RouteItem(addresses.getLocator(), addresses.getLocator().getAddress(), this,
"KLALB SRv6", 13, 128, CostSupplierFactory.expectedDelaySupplier((DelayMonitorData) addresses.getMonitor(), status, algorithm::getRTO), "D", false);
RouteItem.SRv6_ENDSID, 13, 128, CostSupplierFactory.expectedDelaySupplier((DelayMonitorData) addresses.getMonitor(), status, algorithm::getRTO), "D");
rlist.add(ris);
}
}
}
return rlist;
}
}*/
@Override
public void setCongressCondition(Lock lock, Condition condition) {
@@ -1,174 +0,0 @@
package org.kne.cloud.network.klalb;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.List;
import org.kne.cloud.network.MultiProtocolSocketAddress;
import org.kne.cloud.network.SocketListener;
import org.kne.cloud.network.ipv6.IPv6NetworkLink;
import org.kne.cloud.network.monitor.LinkStatus;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
public class KLALBRemoteManagement {
SocketListener slr;
private KLALBProxySystem klalbProxySystem;
public KLALBRemoteManagement(KLALBProxySystem klalbProxySystem) throws IOException {
this(klalbProxySystem,new MultiProtocolSocketAddress("127.9.9.9", 49573));
}
public KLALBRemoteManagement(KLALBProxySystem klalbProxySystem, MultiProtocolSocketAddress listen) throws IOException {
this.klalbProxySystem=klalbProxySystem;
slr=new SocketListener(listen);
slr.setCon((srcv)->{
try {
srcv.setSoTimeout(10000);
byte[]input= srcv.getInputStream().readAllBytes();
srcv.shutdownInput();
String req=new String(input,Charset.forName("UTF-8"));
System.out.println("远程管理请求:"+req);
String rsp=processSignal(req);
System.out.println("远程管理响应:"+rsp);
srcv.getOutputStream().write(rsp.getBytes(Charset.forName("UTF-8")));
srcv.shutdownOutput();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
srcv.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
public KLALBProxySystem getKlalbProxySystem() {
return klalbProxySystem;
}
private String processSignal(String req) {
JsonObject jreq= (JsonObject) new JsonParser().parse(req);
JsonObject jrsp=new JsonObject();
String reqt=jreq.getAsJsonPrimitive("REQ").getAsString();
jrsp.addProperty("RSP", reqt);
switch (reqt) {
case "GETLINES":
JsonArray lines=new JsonArray();
List<IPv6NetworkLink>lineslist= klalbProxySystem.getKlalbController().getLines();
synchronized (lineslist) {
for (Iterator<IPv6NetworkLink> iterator = lineslist.iterator(); iterator.hasNext();) {
IPv6NetworkLink link=iterator.next();
if(!(link instanceof KLALBRemoteLink)) {
continue;
}
KLALBRemoteLink klalbRemoteLine = (KLALBRemoteLink) link;
if(klalbRemoteLine.getSocketAddress()==null)
continue;
JsonObject jklbrl=new JsonObject();
jklbrl.addProperty("ipport", klalbRemoteLine.getSocketAddress().toString());
jklbrl.addProperty("state",LinkStatus.stateToString( klalbRemoteLine.getState()));
jklbrl.addProperty("Vaddr", klalbRemoteLine.getRemoteVaddr().getAddress().toString());
jklbrl.addProperty("uploadspeed", klalbRemoteLine.getMonitor().getOutSpeed());
jklbrl.addProperty("downloadspeed", klalbRemoteLine.getMonitor().getInSpeed());
jklbrl.addProperty("uploadtraffic", klalbRemoteLine.getMonitor().getOutTraffic());
jklbrl.addProperty("downloadtraffic", klalbRemoteLine.getMonitor().getInTraffic());
jklbrl.addProperty("uploaddelay",klalbRemoteLine.getMonitor().getOutDelay() );
jklbrl.addProperty("downloaddelay", klalbRemoteLine.getMonitor().getInDelay());
jklbrl.addProperty("uploaddelaymin",klalbRemoteLine.getMonitor().getOutDelayMin() );
jklbrl.addProperty("downloaddelaymin", klalbRemoteLine.getMonitor().getInDelayMin());
lines.add(jklbrl);
}
}
jrsp.add("table", lines);
break;
case "ADDLINE":
String mip=jreq.getAsJsonPrimitive("ipport").getAsString();
try {
jrsp.addProperty ("Vaddr",klalbProxySystem.getKlalbController().getRemoteVaddrBySocketAddress(new MultiProtocolSocketAddress(mip)).getHostAddress());
} catch (SocketTimeoutException e) {
e.printStackTrace();
}
break;
case "GETSELFLINES":
JsonArray lines2=new JsonArray();
List<MultiProtocolSocketAddress>selflineslist=klalbProxySystem.getKlalbController().getSelflineTable();
synchronized (selflineslist) {
for (Iterator<MultiProtocolSocketAddress> iterator = selflineslist.iterator(); iterator.hasNext();) {
MultiProtocolSocketAddress multiProtocolSocketAddress = (MultiProtocolSocketAddress) iterator.next();
lines2.add(new JsonPrimitive(multiProtocolSocketAddress.toString()));
}
}
jrsp.add("table", lines2);
break;
case "ADDSELFLINE":
String mips=jreq.getAsJsonPrimitive("ipport").getAsString();
List<MultiProtocolSocketAddress>selflineslist2=klalbProxySystem.getKlalbController().getSelflineTable();
synchronized (selflineslist2) {
selflineslist2.add(new MultiProtocolSocketAddress(mips));
}
break;
case "REMOVESELFLINE":
String mipsr=jreq.getAsJsonPrimitive("ipport").getAsString();
List<MultiProtocolSocketAddress>selflineslist21=klalbProxySystem.getKlalbController().getSelflineTable();
synchronized (selflineslist21) {
selflineslist21.add(new MultiProtocolSocketAddress(mipsr));
}
break;
case "GETLINKMONITOR":
jrsp.addProperty("uploadspeed", klalbProxySystem.getKlalbController().getLinkMonitor().getOutSpeed());
jrsp.addProperty("downloadspeed", klalbProxySystem.getKlalbController().getLinkMonitor().getInSpeed());
jrsp.addProperty("uploadtraffic", klalbProxySystem.getKlalbController().getLinkMonitor().getOutTraffic());
jrsp.addProperty("downloadtraffic", klalbProxySystem.getKlalbController().getLinkMonitor().getInTraffic());
break;
case "OPENMONITORUI":
klalbProxySystem.getKLALBGUI().setVisible(true);
break;
/*case "GETSOCKETBRIDGE":
JsonArray bridges=new JsonArray();
Set<Proxy> pxy=klalbProxySystem.getProxys();
synchronized (pxy) {
for (Iterator<Proxy> iterator = pxy.iterator(); iterator.hasNext();) {
Proxy proxy = (Proxy) iterator.next();
bridges.add(klalbProxySystem.createJsonObjectByProxy(proxy));
}
}
jrsp.add("table", bridges);
break;
case "ADDSOCKETBRIDGE":
JsonObject jpxy= jreq.getAsJsonObject("socketbridge");
Set<Proxy> pxy2=klalbProxySystem.getProxys();
synchronized (pxy2) {
try {
pxy2.add(klalbProxySystem.createProxyByJson(jpxy));
} catch (IOException e) {
e.printStackTrace();
}
}
break;*/
default:
System.out.println("未知请求类型:"+reqt);
break;
}
return jrsp.toString();
}
public void close() {
slr.close();
}
}
@@ -75,7 +75,9 @@ public class KLALBStateGUI3 extends XFrame {
private JTextField addressField; // 地址显示框
private JTextField addressFieldSet; // 地址设置框
private JTextField asnFieldSet; // ASN设置框
private JCheckBox enableTUN; // 虚拟网卡启用禁用
private JTextField tunDeviceName; // 虚拟网卡名称设置框
private JTextField webListenSet; // Web API 监听地址设置框
private NetworkGraphPanel graph; // 网络图面板
private JTextField textFieldLocate; // 定位地址输入框
private JTextField asnField2; // ASN显示框
@@ -96,6 +98,7 @@ public class KLALBStateGUI3 extends XFrame {
private JTextArea ntpServerSet; // NTP服务器设置区域
private JCheckBox denyBroadcast;
private JCheckBox denyQuery;
private JCheckBox webApiEnabled; // 启用 Web API 开关
private ClosableTabbedPane tabbedPane; // 可关闭的标签页面板
private JCheckBox nogui;
@@ -820,6 +823,12 @@ public class KLALBStateGUI3 extends XFrame {
asnFieldSet = asn.getTextField();
settings.getView().add(asn);
//虚拟网卡
CheckBoxSettingItem enableTUNs = new CheckBoxSettingItem(UIEnv.getRsb().getString("enabletun"),
CONST.itemwidth, CONST.settingheight);
enableTUN=enableTUNs.getCheckBox();
settings.getView().add(enableTUNs);
TextSettingItem tunName = new TextSettingItem(UIEnv.getRsb().getString("tunname"),
CONST.itemwidth, CONST.settingheight);
tunDeviceName = tunName.getTextField();
@@ -905,6 +914,21 @@ public class KLALBStateGUI3 extends XFrame {
denyBroadcast=denyBroadcastc.getCheckBox();
settings.getView().add(denyBroadcastc);
// Web服务设置标题
SettingItem wsi = new SettingItem(UIEnv.getRsb().getString("webapisettings"),
UIEnv.getFont().deriveFont(20.0f).deriveFont(Font.BOLD), CONST.itemwidth, CONST.settingheight);
settings.getView().add(wsi);
CheckBoxSettingItem webApic = new CheckBoxSettingItem(UIEnv.getRsb().getString("enablewebapi"),
CONST.itemwidth, CONST.settingheight);
webApiEnabled=webApic.getCheckBox();
settings.getView().add(webApic);
TextSettingItem webListen = new TextSettingItem(UIEnv.getRsb().getString("weblistenaddr"),
CONST.itemwidth, CONST.settingheight);
webListenSet = webListen.getTextField();
settings.getView().add(webListen);
//性能设置标题
SettingItem pshi = new SettingItem(UIEnv.getRsb().getString("performancesettings"),
@@ -1134,6 +1158,8 @@ public class KLALBStateGUI3 extends XFrame {
}
}
kck.setEnableTUN( enableTUN.isSelected());
String tunNameText=tunDeviceName.getText().trim();
if(tunNameText.equals("") || tunNameText.equalsIgnoreCase("null")) {
kck.setTUNName(null);
@@ -1141,6 +1167,22 @@ public class KLALBStateGUI3 extends XFrame {
kck.setTUNName(tunNameText);
}
// 保存Web API设置
kck.setWebUI(webApiEnabled.isSelected());
String webListenText = webListenSet.getText().trim();
if (webListenText.equals("")) {
kck.setWebListen(new MultiProtocolSocketAddress("http", "0.0.0.0", 4665));
} else {
try {
kck.setWebListen(new MultiProtocolSocketAddress(webListenText));
} catch (RuntimeException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, UIEnv.getRsb().getString("invaildweblistenaddr"),
UIEnv.getRsb().getString("warning"), JOptionPane.WARNING_MESSAGE);
return;
}
}
// 保存TCP监听设置
String tcptext = tcpListeningSet.getText();
if (tcptext.equals("")) {
@@ -1190,7 +1232,7 @@ public class KLALBStateGUI3 extends XFrame {
}
}
}
kck.setLineTable(iaddr1);
kck.setExternalEndpoints(iaddr1);
// 保存自动连接线路表
String[] splt11 = connectLineTabelSet.getText().split("\n");
@@ -1243,9 +1285,9 @@ public class KLALBStateGUI3 extends XFrame {
kck.setLinkConnectionsCount(linkConnectionsCount.getSlider().getValue());
kck.setDenyLineTableQuery(denyQuery.isSelected());
kck.setDenyExternalEndpointQuery(denyQuery.isSelected());
kck.setDenyLineTableBroadcast(denyBroadcast.isSelected());
kck.setDenyExternalEndpointBroadcast(denyBroadcast.isSelected());
PerformanceStrategyItem psi=((PerformanceStrategyItem)comboPerformance.getSelectedItem());
if(psi!=null) {
@@ -1538,9 +1580,19 @@ public class KLALBStateGUI3 extends XFrame {
Long vasn = kck.getVirtualASN();
asnFieldSet.setText(vasn != null ? vasn.toString() : "");
enableTUN.setSelected(kck.isEnableTUN());
String tunname=kck.getTUNName();
tunDeviceName.setText(tunname!=null?tunname:"");
// 加载Web API设置
webApiEnabled.setSelected(kck.isWebUI());
webListenSet.setText(kck.getWebListen() != null ? kck.getWebListen().toString()
: "http://0.0.0.0:4665");
// 加载TCP监听
MultiProtocolSocketAddress mpat = kck.getTCPListen();
tcpListeningSet.setText(mpat != null ? mpat.toString() : "");
@@ -1550,7 +1602,7 @@ public class KLALBStateGUI3 extends XFrame {
udpListeningSet.setText(mpau != null ? mpau.toString() : "");
// 加载开放线路表
List<MultiProtocolSocketAddress> linet = kck.getLineTable();
List<MultiProtocolSocketAddress> linet = kck.getExternalEndpoints();
openLineTabelSet.setText(listToStr2(linet));
// 加载自动连接线路表
@@ -1579,9 +1631,9 @@ public class KLALBStateGUI3 extends XFrame {
}
linkConnectionsCount.getSlider().setValue(conc);
denyQuery.setSelected( kck.isDenyLineTableQuery());
denyQuery.setSelected( kck.isDenyExternalEndpointQuery());
denyBroadcast.setSelected( kck.isDenyLineTableBroadcast());
denyBroadcast.setSelected( kck.isDenyExternalEndpointBroadcast());
String stategy= kck.getPerformanceStrategy();
PerformanceStrategy pfs=PerformanceStrategy.fromDescription(stategy);
@@ -18,7 +18,10 @@ import org.jfree.chart.JFreeChart;
import org.jfree.data.time.Millisecond;
import org.jfree.data.time.TimeSeries;
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.Vegas2CongestionAlgorithm;
import org.kne.cloud.network.ipv6.IPv6Address;
import org.kne.cloud.network.ipv6.IPv6AddressGroup;
import org.kne.cloud.network.ipv6.IPv6Packet;
@@ -42,12 +45,14 @@ public class LineMonitorGUI extends XFrame {
private Timer timer1;
private Timer timer2;
private Timer timer3;
private Timer timer4;
private TimerTask tsk3;
private TimerTask tsk4, tsk5,tsk6;
private TimerTask tsk4, tsk5,tsk6,tsk7;
private TimerTask d1;
private TimerTask d2;
private TimerTask d3;
private TimerTask d4;
private TimeSeries spdup = new TimeSeries(UIEnv.getRsb().getString("uploadspeed"));
private TimeSeries spddown = new TimeSeries(UIEnv.getRsb().getString("downloadspeed"));
@@ -62,13 +67,21 @@ public class LineMonitorGUI extends XFrame {
private TimeSeries queueused = new TimeSeries(UIEnv.getRsb().getString("queueused"));
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 JFreeChart jfc;
private JFreeChart jfce;
private JFreeChart jbfc;
private JFreeChart jbfcv;
private JLabel spdp;
private JLabel delp;
private JLabel sbpdp;
private JLabel sbpdpv;
private long timeRange = 5000;
private long[] values = new long[] { 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000 };
private JPanel panel_1;
@@ -155,6 +168,22 @@ public class LineMonitorGUI extends XFrame {
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.GREEN);
jbfcv.getXYPlot().getRenderer().setSeriesPaint(3, Color.RED);
changeFont(jbfcv);
sbpdpv = new JLabel();
sbpdpv.setBorder(new LineBorder(Color.DARK_GRAY));
jp.add(sbpdpv);
@@ -262,6 +291,14 @@ public class LineMonitorGUI extends XFrame {
buffermax.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");
}
@@ -328,6 +365,24 @@ 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
public void setVisible(boolean b) {
if (b) {
@@ -336,6 +391,7 @@ public class LineMonitorGUI extends XFrame {
timer1 = new Timer();
timer2 = new Timer();
timer3 = new Timer();
timer4 = new Timer();
tsk3 = new TimerTask() {
@Override
@@ -365,6 +421,13 @@ public class LineMonitorGUI extends XFrame {
recordBufferData();
}
};
tsk7 = new TimerTask() {
@Override
public void run() {
recordCongestionData();
}
};
d1 = new TimerTask() {
@Override
@@ -406,6 +469,20 @@ 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);
timer1.scheduleAtFixedRate(tsk4, 10, 10);
timer1.scheduleAtFixedRate(d1, 25, 25);
@@ -413,6 +490,8 @@ public class LineMonitorGUI extends XFrame {
timer2.scheduleAtFixedRate(d2, 25, 25);
timer3.scheduleAtFixedRate(tsk6, 10, 10);
timer3.scheduleAtFixedRate(d3, 25, 25);
timer4.scheduleAtFixedRate(tsk7, 25, 25);
timer4.scheduleAtFixedRate(d4, 25, 25);
}
} else {
@@ -422,12 +501,18 @@ public class LineMonitorGUI extends XFrame {
tsk4.cancel();
if (tsk5 != null)
tsk5.cancel();
if (tsk6 != null)
tsk6.cancel();
if (tsk7 != null)
tsk7.cancel();
if (d1 != null)
d1.cancel();
if (d2 != null)
d2.cancel();
if (d3 != null)
d3.cancel();
if (d4 != null)
d4.cancel();
if (timer != null)
timer.cancel();
if (timer1 != null)
@@ -438,6 +523,9 @@ public class LineMonitorGUI extends XFrame {
if (timer3 != null)
timer3.cancel();
if (timer4 != null)
timer4.cancel();
timer = null;
}
super.setVisible(b);
@@ -82,8 +82,8 @@ public class NetworkGraphPanel extends GraphPanel {
public void actionPerformed(ActionEvent e) {
KperfGUI kpfg=new KperfGUI();
kpfg.setVisible(true);
MultiProtocolSocketAddress source=new MultiProtocolSocketAddress("KLALB_Stream",controller.getIpv6Router().getLocator().getAddress().toString(),0);
MultiProtocolSocketAddress target=new MultiProtocolSocketAddress("KLALB_Stream", address.toString(), 4564);
MultiProtocolSocketAddress source=new MultiProtocolSocketAddress("kltp",controller.getIpv6Router().getLocator().getAddress().toString(),0);
MultiProtocolSocketAddress target=new MultiProtocolSocketAddress("kltp", address.toString(), 4564);
kpfg.setTarget(target,source);
}
});
@@ -164,7 +164,7 @@ public class NetworkGraphPanel extends GraphPanel {
return sb.toString();
}
private double nsPerPixel=1000L;
private double nsPerPixel=5000L;
private class InetGraphEdgeGroup extends GraphEdgeGroup{
public InetGraphEdgeGroup(GraphNode nodeA, GraphNode nodeB) {
super(nodeA, nodeB);
@@ -73,19 +73,15 @@ public class NodeInformationPanel extends JPanel {
overviewArea.setWrapStyleWord(true);
overviewArea.setFont(UIEnv.getFont().deriveFont(14.0f));
overviewArea.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
StringBuilder sb=new StringBuilder();
sb.append(UIEnv.getRsb().getString("ipv6addr")).append(": ").append(address.toCompressedString()).append('\n');
String dname=controller.getIpv6Router().getKlalbRouteProtol().getDeviceName(address);
if(dname!=null) {
sb.append('\n').append(UIEnv.getRsb().getString("devicename")).append(": ").append(dname).append('\n');
}
String ddesc=null;
if(address.equals(controller.getIpv6Router().getLocator().getAddress())&&controller.getConfigItem()!=null) {
String ddesc=controller.getConfigItem().getDeviceDescription();
if(ddesc!=null&&!ddesc.isEmpty()) {
sb.append('\n').append(UIEnv.getRsb().getString("devicedescription")).append(":\n").append(ddesc).append('\n');
ddesc=controller.getConfigItem().getDeviceDescription();
if(ddesc!=null&&ddesc.isEmpty()) {
ddesc=null;
}
}
overviewArea.setText(sb.toString());
overviewArea.setText(buildOverviewText(dname, ddesc));
panel.add(new JScrollPane(overviewArea), BorderLayout.CENTER);
JPanel panel_1 = new JPanel();
@@ -169,16 +165,38 @@ public class NodeInformationPanel extends JPanel {
client=new KLALBRoutingProtocolAPIClient(controller.getIpv6Router().getKlalbRouteProtol());
try {
client.requestOpenLines(new InetSocketAddress( address.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT), (result)->{
client.requestNodeInfoFull(new InetSocketAddress( address.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT), (info)->{
listModel.clear();
for (MultiProtocolSocketAddress multiProtocolSocketAddress : result) {
for (MultiProtocolSocketAddress multiProtocolSocketAddress : info.getOpenLines()) {
listModel.addElement(multiProtocolSocketAddress);
}
// 用对端返回的设备名称/描述更新概览旧版本节点无该字段时保留原显示
String dn=info.getDeviceName();
if(dn==null||dn.isEmpty()) {
dn=controller.getIpv6Router().getKlalbRouteProtol().getDeviceName(address);
}
String dd=info.getDeviceDescription();
if(dd!=null&&dd.isEmpty()) {
dd=null;
}
overviewArea.setText(buildOverviewText(dn, dd));
});
} catch (IOException e) {
e.printStackTrace();
}
}
private String buildOverviewText(String dname,String ddesc) {
StringBuilder sb=new StringBuilder();
sb.append(UIEnv.getRsb().getString("ipv6addr")).append(": ").append(address.toCompressedString()).append('\n');
if(dname!=null&&!dname.isEmpty()) {
sb.append('\n').append(UIEnv.getRsb().getString("devicename")).append(": ").append(dname).append('\n');
}
if(ddesc!=null&&!ddesc.isEmpty()) {
sb.append('\n').append(UIEnv.getRsb().getString("devicedescription")).append(":\n").append(ddesc).append('\n');
}
return sb.toString();
}
public Icon getIcon() {
return new ImageIcon(image);
}
@@ -0,0 +1,986 @@
package org.kne.cloud.network.klalb.web;
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import com.sun.net.httpserver.*;
import com.google.gson.*;
import org.kne.cloud.clock.HighAccuracyClock;
import org.kne.cloud.network.MultiProtocolSocketAddress;
import org.kne.cloud.network.ipv6.IPv6Address;
import org.kne.cloud.network.ipv6.IPv6AddressGroup;
import org.kne.cloud.network.ipv6.IPv6NetworkLink;
import org.kne.cloud.network.ipv6.RouteItem;
import org.kne.cloud.network.klalb.*;
import org.kne.cloud.network.monitor.LinkStatus;
import org.kne.cloud.network.srv6.KLALBNodeInformation;
import org.kne.cloud.network.srv6.KLALBRoutingProtocol;
import org.kne.cloud.network.srv6.KLALBRoutingProtocol.LinkDirection;
import org.kne.cloud.network.srv6.KLALBRoutingProtocolAPIClient;
import org.kne.cloud.network.srv6.NeighborInfo;
import org.kne.cloud.network.srv6.RouterInfo;
import org.kne.cloud.network.srv6.SRv6Router;
/**
* KLALB Web Server providing REST API, SSE live event stream,
* and static dashboard hosting.
*/
public class KLALBWebServer {
private final KLALBProxySystem proxySystem;
private final MultiProtocolSocketAddress listen;
private HttpServer server;
private final Gson gson;
private final ScheduledExecutorService sseExecutor = Executors.newSingleThreadScheduledExecutor(r -> {
Thread t = new Thread(r, "KLALB-Web-SSE");
t.setDaemon(true);
return t;
});
private final Set<HttpExchange> sseClients = Collections.newSetFromMap(new ConcurrentHashMap<>());
private final AtomicBoolean running = new AtomicBoolean(false);
public KLALBWebServer(KLALBProxySystem proxySystem, int port) {
this(proxySystem, new MultiProtocolSocketAddress("http", "0.0.0.0", port));
}
public KLALBWebServer(KLALBProxySystem proxySystem, MultiProtocolSocketAddress listen) {
this.proxySystem = proxySystem;
this.listen = listen;
this.gson = proxySystem.getGson();
}
public synchronized void start() throws IOException {
if (running.get()) return;
InetSocketAddress socketAddress = "0.0.0.0".equals(listen.getHost())
? new InetSocketAddress(listen.getPort()) : listen.getSocketAddress();
server = HttpServer.create(socketAddress, 0);
server.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
// API Contexts
server.createContext("/api/status", this::handleStatus);
server.createContext("/api/events", this::handleEvents);
server.createContext("/api/links", this::handleLinks);
server.createContext("/api/links/action", this::handleLinkAction);
server.createContext("/api/links/reconnect", this::handleReconnectAll);
server.createContext("/api/routing-table", this::handleRoutingTable);
server.createContext("/api/nodes", this::handleNodes);
server.createContext("/api/node-info", this::handleNodeInfo);
server.createContext("/api/config", this::handleConfig);
// Static Files / SPA Fallback Handler
server.createContext("/", this::handleStatic);
server.start();
running.set(true);
startSseBroadcaster();
System.out.println("KLALB Web Dashboard started at " + listen);
}
public synchronized void stop() {
if (!running.get()) return;
running.set(false);
sseExecutor.shutdownNow();
for (HttpExchange client : sseClients) {
try {
client.close();
} catch (Exception ignored) {}
}
sseClients.clear();
if (server != null) {
server.stop(1);
server = null;
}
System.out.println("KLALB Web Dashboard stopped.");
}
public boolean isRunning() {
return running.get();
}
public int getPort() {
return listen.getPort();
}
private void setCorsHeaders(HttpExchange exchange) {
Headers headers = exchange.getResponseHeaders();
headers.set("Access-Control-Allow-Origin", "*");
headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept");
}
private boolean handleCorsPreflight(HttpExchange exchange) throws IOException {
setCorsHeaders(exchange);
if ("OPTIONS".equalsIgnoreCase(exchange.getRequestMethod())) {
exchange.sendResponseHeaders(204, -1);
exchange.close();
return true;
}
return false;
}
private void sendJsonResponse(HttpExchange exchange, int statusCode, Object data) throws IOException {
setCorsHeaders(exchange);
String json;
if (data instanceof JsonElement) {
// gson-2.1 toJson(Object) 会按运行时类型 JsonObject 反射序列化出内部的 members 字段
// 必须走 JsonElement 重载 toString直接输出 JSON
json = ((JsonElement) data).toString();
} else {
json = gson.toJson(data);
}
byte[] bytes = json.getBytes(StandardCharsets.UTF_8);
exchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8");
exchange.sendResponseHeaders(statusCode, bytes.length);
try (OutputStream os = exchange.getResponseBody()) {
os.write(bytes);
}
}
private void sendError(HttpExchange exchange, int statusCode, String message) throws IOException {
JsonObject error = new JsonObject();
error.addProperty("error", message);
sendJsonResponse(exchange, statusCode, error);
}
private String readRequestBody(HttpExchange exchange) throws IOException {
try (InputStream is = exchange.getRequestBody()) {
return new String(is.readAllBytes(), StandardCharsets.UTF_8);
}
}
// -------------------------------------------------------------
// API Handlers
// -------------------------------------------------------------
private JsonObject getStatusJson() {
JsonObject root = new JsonObject();
KLALBController kc = proxySystem.getKlalbController();
if (kc == null) {
root.addProperty("status", "not_ready");
return root;
}
root.addProperty("version", CONST.klalbver);
root.addProperty("title", CONST.klalb);
IPv6AddressGroup self = kc.getSelf();
root.addProperty("address", self != null ? self.getAddress().toString() : "");
SRv6Router router = kc.getIpv6Router();
if (router != null && router.getKlalbRouteProtol() != null) {
root.addProperty("onlineDevices", router.getKlalbRouteProtol().getDevicesFound());
root.addProperty("deviceName", router.getDeviceName() != null ? router.getDeviceName() : "");
} else {
root.addProperty("onlineDevices", 0);
root.addProperty("deviceName", "");
}
KLALBControllerConfigItem configItem = proxySystem.getControllerConfig();
if (configItem != null) {
root.addProperty("deviceDescription", configItem.getDeviceDescription() != null ? configItem.getDeviceDescription() : "");
root.addProperty("enableTUN", configItem.isEnableTUN());
root.addProperty("tunName", configItem.isEnableTUN() && configItem.getTUNName() != null ? configItem.getTUNName() : "");
root.addProperty("congestionAlgorithm", configItem.getCongestionAlgorithm() != null ? configItem.getCongestionAlgorithm() : "");
root.addProperty("performanceStrategy", configItem.getPerformanceStrategy() != null ? configItem.getPerformanceStrategy() : "");
}
// Clock
HighAccuracyClock hac = kc.getClock();
if (hac != null) {
long delta = hac.getFrequency() - 1000000000;
double ppm = delta / 1000.0;
root.addProperty("timeStr", hac.toString2());
root.addProperty("timePpm", ppm);
}
// Metrics
JsonObject metrics = new JsonObject();
if (kc.getLinkMonitor() != null) {
metrics.addProperty("upSpeed", kc.getLinkMonitor().getOutSpeed());
metrics.addProperty("downSpeed", kc.getLinkMonitor().getInSpeed());
metrics.addProperty("upSpeedMax", kc.getLinkMonitor().getOutSpeedMax());
metrics.addProperty("downSpeedMax", kc.getLinkMonitor().getInSpeedMax());
metrics.addProperty("upPPS", kc.getLinkMonitor().getOutPPS());
metrics.addProperty("downPPS", kc.getLinkMonitor().getInPPS());
metrics.addProperty("upPPSMax", kc.getLinkMonitor().getOutPPSMax());
metrics.addProperty("downPPSMax", kc.getLinkMonitor().getInPPSMax());
metrics.addProperty("upTraffic", kc.getLinkMonitor().getOutTraffic());
metrics.addProperty("downTraffic", kc.getLinkMonitor().getInTraffic());
}
if (kc.getDatatMonitor() != null) {
metrics.addProperty("dataUpSpeed", kc.getDatatMonitor().getOutSpeed());
metrics.addProperty("dataDownSpeed", kc.getDatatMonitor().getInSpeed());
metrics.addProperty("dataUpPPS", kc.getDatatMonitor().getOutPPS());
metrics.addProperty("dataDownPPS", kc.getDatatMonitor().getInPPS());
metrics.addProperty("dataUpTraffic", kc.getDatatMonitor().getOutTraffic());
metrics.addProperty("dataDownTraffic", kc.getDatatMonitor().getInTraffic());
}
if (router != null) {
metrics.addProperty("backplaneDelay", router.getBackplaneTime());
metrics.addProperty("backplanePPS", router.getBackplanePPS());
metrics.addProperty("backplanePPSMax", router.getBackplanePPSMax());
metrics.addProperty("backplaneECNRate", router.getECNRate());
metrics.addProperty("backplaneLossRate", router.getLossRate());
}
root.add("metrics", metrics);
return root;
}
private void handleStatus(HttpExchange exchange) throws IOException {
if (handleCorsPreflight(exchange)) return;
if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) {
sendError(exchange, 405, "Method not allowed");
return;
}
sendJsonResponse(exchange, 200, getStatusJson());
}
private JsonArray getLinksJson() {
JsonArray array = new JsonArray();
KLALBController kc = proxySystem.getKlalbController();
if (kc == null) return array;
List<IPv6NetworkLink> linesList = kc.getLines();
synchronized (linesList) {
for (IPv6NetworkLink link : linesList) {
if (!(link instanceof KLALBRemoteLink)) continue;
KLALBRemoteLink remoteLink = (KLALBRemoteLink) link;
JsonObject obj = new JsonObject();
obj.addProperty("name", remoteLink.getName());
obj.addProperty("state", LinkStatus.stateToString(remoteLink.getState()));
obj.addProperty("stateCode", remoteLink.getState());
MultiProtocolSocketAddress mpsa = remoteLink.getSocketAddress();
obj.addProperty("socketAddress", mpsa != null ? mpsa.toString() : "");
IPv6AddressGroup vaddr = remoteLink.getRemoteVaddr();
obj.addProperty("vaddr", vaddr != null ? vaddr.getAddress().toString() : "");
if (remoteLink.getMonitor() != null) {
obj.addProperty("upSpeed", remoteLink.getMonitor().getOutSpeed());
obj.addProperty("downSpeed", remoteLink.getMonitor().getInSpeed());
obj.addProperty("upPPS", remoteLink.getMonitor().getOutPPS());
obj.addProperty("downPPS", remoteLink.getMonitor().getInPPS());
obj.addProperty("upTraffic", remoteLink.getMonitor().getOutTraffic());
obj.addProperty("downTraffic", remoteLink.getMonitor().getInTraffic());
obj.addProperty("upDelay", remoteLink.getMonitor().getOutDelay());
obj.addProperty("downDelay", remoteLink.getMonitor().getInDelay());
obj.addProperty("upDelayMin", remoteLink.getMonitor().getOutDelayMin());
obj.addProperty("downDelayMin", remoteLink.getMonitor().getInDelayMin());
obj.addProperty("upJitter", remoteLink.getMonitor().getOutJitter());
obj.addProperty("downJitter", remoteLink.getMonitor().getInJitter());
}
array.add(obj);
}
}
return array;
}
private void handleLinks(HttpExchange exchange) throws IOException {
if (handleCorsPreflight(exchange)) return;
String method = exchange.getRequestMethod();
KLALBController kc = proxySystem.getKlalbController();
if ("GET".equalsIgnoreCase(method)) {
sendJsonResponse(exchange, 200, getLinksJson());
} else if ("POST".equalsIgnoreCase(method)) {
String body = readRequestBody(exchange);
try {
JsonObject req = new JsonParser().parse(body).getAsJsonObject();
String address = req.get("address").getAsString().trim();
if (address.isEmpty()) {
sendError(exchange, 400, "Address is required");
return;
}
MultiProtocolSocketAddress mpsa = new MultiProtocolSocketAddress(address);
List<KLALBRemoteLink> added = kc.addRemoteLines(mpsa);
JsonObject resp = new JsonObject();
resp.addProperty("success", !added.isEmpty());
resp.addProperty("count", added.size());
sendJsonResponse(exchange, 200, resp);
} catch (Exception e) {
sendError(exchange, 400, "Failed to add link: " + e.getMessage());
}
} else if ("DELETE".equalsIgnoreCase(method)) {
String query = exchange.getRequestURI().getQuery();
String address = null;
if (query != null && query.startsWith("address=")) {
address = URLDecoder.decode(query.substring(8), StandardCharsets.UTF_8);
} else {
String body = readRequestBody(exchange);
if (!body.isEmpty()) {
try {
JsonObject req = new JsonParser().parse(body).getAsJsonObject();
if (req.has("address")) {
address = req.get("address").getAsString();
}
} catch (Exception ignored) {}
}
}
if (address == null || address.trim().isEmpty()) {
sendError(exchange, 400, "Address parameter required");
return;
}
try {
MultiProtocolSocketAddress mpsa = new MultiProtocolSocketAddress(address.trim());
List<KLALBRemoteLink> removed = kc.removeRemoteLines(mpsa);
JsonObject resp = new JsonObject();
resp.addProperty("success", !removed.isEmpty());
resp.addProperty("count", removed.size());
sendJsonResponse(exchange, 200, resp);
} catch (Exception e) {
sendError(exchange, 400, "Failed to remove link: " + e.getMessage());
}
} else {
sendError(exchange, 405, "Method not allowed");
}
}
private void handleLinkAction(HttpExchange exchange) throws IOException {
if (handleCorsPreflight(exchange)) return;
if (!"POST".equalsIgnoreCase(exchange.getRequestMethod())) {
sendError(exchange, 405, "Method not allowed");
return;
}
String body = readRequestBody(exchange);
try {
JsonObject req = new JsonParser().parse(body).getAsJsonObject();
String action = req.get("action").getAsString();
String address = req.has("address") ? req.get("address").getAsString() : null;
KLALBController kc = proxySystem.getKlalbController();
KLALBRemoteLink target = null;
if (address != null) {
for (IPv6NetworkLink link : kc.getLines()) {
if (link instanceof KLALBRemoteLink rl) {
if (rl.getName().equalsIgnoreCase(address) ||
(rl.getSocketAddress() != null && rl.getSocketAddress().toString().equalsIgnoreCase(address))) {
target = rl;
break;
}
}
}
}
JsonObject resp = new JsonObject();
if (target != null) {
switch (action) {
case "reconnect" -> {
target.reconnectImmediately();
resp.addProperty("success", true);
}
case "disconnect" -> {
target.dislink();
resp.addProperty("success", true);
}
case "remove" -> {
MultiProtocolSocketAddress mtar = target.getSocketAddress();
if (mtar != null) {
kc.removeRemoteLines(mtar);
} else {
target.close();
}
resp.addProperty("success", true);
}
default -> {
sendError(exchange, 400, "Unknown action: " + action);
return;
}
}
} else {
sendError(exchange, 404, "Target link not found: " + address);
return;
}
sendJsonResponse(exchange, 200, resp);
} catch (Exception e) {
sendError(exchange, 400, "Failed to execute link action: " + e.getMessage());
}
}
private void handleReconnectAll(HttpExchange exchange) throws IOException {
if (handleCorsPreflight(exchange)) return;
if (!"POST".equalsIgnoreCase(exchange.getRequestMethod())) {
sendError(exchange, 405, "Method not allowed");
return;
}
KLALBController kc = proxySystem.getKlalbController();
if (kc != null) {
kc.reconnectImmediately();
JsonObject resp = new JsonObject();
resp.addProperty("success", true);
sendJsonResponse(exchange, 200, resp);
} else {
sendError(exchange, 500, "Controller not ready");
}
}
private void handleRoutingTable(HttpExchange exchange) throws IOException {
if (handleCorsPreflight(exchange)) return;
if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) {
sendError(exchange, 405, "Method not allowed");
return;
}
KLALBController kc = proxySystem.getKlalbController();
JsonArray routesArray = new JsonArray();
if (kc != null && kc.getIpv6Router() != null) {
List<RouteItem> list = new ArrayList<>(kc.getIpv6Router().getCurrentRouteTabel());
Collections.sort(list);
for (RouteItem item : list) {
JsonObject obj = new JsonObject();
obj.addProperty("destination", item.getDestination() != null ? item.getDestination().toString() : "");
obj.addProperty("protocol", item.getProto());
obj.addProperty("preference", item.getPre());
obj.addProperty("cost", item.getCost());
obj.addProperty("flag", item.getFlag());
obj.addProperty("nexthop", item.getNexthop() != null ? item.getNexthop().toString() : "");
obj.addProperty("interface", item.getDestlink() != null ? item.getDestlink().getName() : "");
routesArray.add(obj);
}
}
sendJsonResponse(exchange, 200, routesArray);
}
private KLALBRoutingProtocolAPIClient nodeInfoClient;
private synchronized KLALBRoutingProtocolAPIClient getNodeInfoClient(KLALBController kc) {
if (nodeInfoClient == null) {
nodeInfoClient = new KLALBRoutingProtocolAPIClient(kc.getIpv6Router().getKlalbRouteProtol());
}
return nodeInfoClient;
}
private void handleNodeInfo(HttpExchange exchange) throws IOException {
if (handleCorsPreflight(exchange)) return;
if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) {
sendError(exchange, 405, "Method not allowed");
return;
}
Map<String, String> params = parseQuery(exchange.getRequestURI().getRawQuery());
String addressStr = params.get("address");
if (addressStr == null || addressStr.trim().isEmpty()) {
sendError(exchange, 400, "address parameter is required");
return;
}
KLALBController kc = proxySystem.getKlalbController();
if (kc == null || kc.getIpv6Router() == null || kc.getIpv6Router().getKlalbRouteProtol() == null) {
sendError(exchange, 503, "Controller not ready");
return;
}
IPv6Address target;
try {
target = new IPv6Address((Inet6Address) InetAddress.getByName(addressStr.trim()));
} catch (Exception e) {
sendError(exchange, 400, "Invalid IPv6 address: " + addressStr);
return;
}
SRv6Router router = kc.getIpv6Router();
boolean isSelf = router.getLocator().getAddress().equals(target);
JsonObject resp = new JsonObject();
resp.addProperty("address", target.toString());
if (isSelf) {
// 本机描述直接取本地控制器配置
KLALBControllerConfigItem cfg = proxySystem.getControllerConfig();
resp.addProperty("isSelf", true);
String dname = router.getDeviceName() != null ? router.getDeviceName()
: (cfg != null && cfg.getDeviceName() != null ? cfg.getDeviceName() : "");
resp.addProperty("deviceName", dname);
resp.addProperty("deviceDescription",
cfg != null && cfg.getDeviceDescription() != null ? cfg.getDeviceDescription() : "");
JsonArray lines = new JsonArray();
for (MultiProtocolSocketAddress mpsa : kc.getExternalEndpoints()) {
lines.add(new JsonPrimitive(mpsa.toString()));
}
resp.add("openLines", lines);
sendJsonResponse(exchange, 200, resp);
return;
}
// 远端节点 SRv6 虚拟网络发送完整节点信息查询异步回调限时等待
resp.addProperty("isSelf", false);
CompletableFuture<KLALBNodeInformation> future = new CompletableFuture<>();
try {
getNodeInfoClient(kc).requestNodeInfoFull(
new InetSocketAddress(target.toInet6Address(), KLALBRoutingProtocol.DEFAULT_PORT),
info -> future.complete(info));
KLALBNodeInformation info;
try {
info = future.get(3, TimeUnit.SECONDS);
} catch (TimeoutException te) {
info = null;
}
String dname = info != null && info.getDeviceName() != null && !info.getDeviceName().isEmpty()
? info.getDeviceName()
: router.getKlalbRouteProtol().getDeviceName(target);
resp.addProperty("deviceName", dname != null ? dname : "");
resp.addProperty("deviceDescription",
info != null && info.getDeviceDescription() != null ? info.getDeviceDescription() : "");
resp.addProperty("reachable", info != null);
JsonArray lines = new JsonArray();
if (info != null && info.getOpenLines() != null) {
for (MultiProtocolSocketAddress mpsa : info.getOpenLines()) {
lines.add(new JsonPrimitive(mpsa.toString()));
}
}
resp.add("openLines", lines);
} catch (Exception e) {
resp.addProperty("deviceName", "");
resp.addProperty("deviceDescription", "");
resp.addProperty("reachable", false);
resp.add("openLines", new JsonArray());
}
sendJsonResponse(exchange, 200, resp);
}
private Map<String, String> parseQuery(String rawQuery) {
Map<String, String> params = new HashMap<>();
if (rawQuery == null || rawQuery.isEmpty()) return params;
for (String pair : rawQuery.split("&")) {
int idx = pair.indexOf('=');
if (idx <= 0) continue;
try {
params.put(URLDecoder.decode(pair.substring(0, idx), StandardCharsets.UTF_8),
URLDecoder.decode(pair.substring(idx + 1), StandardCharsets.UTF_8));
} catch (Exception ignored) {}
}
return params;
}
private void handleNodes(HttpExchange exchange) throws IOException {
if (handleCorsPreflight(exchange)) return;
if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) {
sendError(exchange, 405, "Method not allowed");
return;
}
KLALBController kc = proxySystem.getKlalbController();
JsonObject result = new JsonObject();
JsonArray nodesArray = new JsonArray();
JsonArray edgesArray = new JsonArray();
if (kc != null && kc.getIpv6Router() != null && kc.getIpv6Router().getKlalbRouteProtol() != null) {
KLALBRoutingProtocol rproto = kc.getIpv6Router().getKlalbRouteProtol();
Map<IPv6Address, Long> addrs = rproto.getAddresses();
IPv6Address selfAddr = kc.getIpv6Router().getLocator().getAddress();
if (addrs != null) {
for (IPv6Address addr : addrs.keySet()) {
JsonObject nodeObj = new JsonObject();
nodeObj.addProperty("id", addr.toString());
nodeObj.addProperty("address", addr.toString());
nodeObj.addProperty("compressedAddress", addr.toCompressedString());
nodeObj.addProperty("isSelf", addr.equals(selfAddr));
String dname = rproto.getDeviceName(addr);
nodeObj.addProperty("deviceName", dname != null ? dname : "");
nodesArray.add(nodeObj);
}
}
Map<IPv6Address, RouterInfo> netmap = rproto.getNetmap();
if (netmap != null) {
Set<String> seenEdges = new HashSet<>();
for (Map.Entry<IPv6Address, RouterInfo> entry : netmap.entrySet()) {
IPv6Address from = entry.getKey();
RouterInfo info = entry.getValue();
if (info != null && info.getNeighborAddresses() != null) {
for (NeighborInfo nb : info.getNeighborAddresses()) {
if (nb != null && nb.getLocator() != null) {
String to = nb.getLocator().getAddress().toString();
String edgeKey = from.toString().compareTo(to) < 0
? from.toString() + "->" + to
: to + "->" + from.toString();
if (!seenEdges.contains(edgeKey)) {
seenEdges.add(edgeKey);
JsonObject edgeObj = new JsonObject();
edgeObj.addProperty("source", from.toString());
edgeObj.addProperty("target", to);
edgeObj.addProperty("delay", nb.getUploadDelay());
edgeObj.addProperty("cost", nb.getUploadDelayMin());
edgesArray.add(edgeObj);
}
}
}
}
}
}
}
result.add("nodes", nodesArray);
result.add("edges", edgesArray);
sendJsonResponse(exchange, 200, result);
}
private void handleConfig(HttpExchange exchange) throws IOException {
if (handleCorsPreflight(exchange)) return;
String method = exchange.getRequestMethod();
if ("GET".equalsIgnoreCase(method)) {
KLALBControllerConfigItem config = proxySystem.getControllerConfig();
if (config != null) {
sendJsonResponse(exchange, 200, config);
} else {
sendError(exchange, 404, "Configuration not found");
}
} else if ("POST".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method)) {
String body = readRequestBody(exchange);
try {
JsonObject json = new JsonParser().parse(body).getAsJsonObject();
KLALBControllerConfigItem current = proxySystem.getControllerConfig();
if (current != null) {
if (json.has("deviceName") && !json.get("deviceName").isJsonNull()) {
current.setDeviceName(json.get("deviceName").getAsString());
} else if (json.has("DeviceName") && !json.get("DeviceName").isJsonNull()) {
current.setDeviceName(json.get("DeviceName").getAsString());
}
if (json.has("deviceDescription") && !json.get("deviceDescription").isJsonNull()) {
current.setDeviceDescription(json.get("deviceDescription").getAsString());
} else if (json.has("DeviceDescription") && !json.get("DeviceDescription").isJsonNull()) {
current.setDeviceDescription(json.get("DeviceDescription").getAsString());
}
if (json.has("language") && !json.get("language").isJsonNull()) {
current.setLanguage(json.get("language").getAsString());
}
if (json.has("virtualAddress") && !json.get("virtualAddress").isJsonNull()) {
current.setVirtualAddress(json.get("virtualAddress").getAsString());
} else if (json.has("VirtualAddress") && !json.get("VirtualAddress").isJsonNull()) {
current.setVirtualAddress(json.get("VirtualAddress").getAsString());
}
if (json.has("virtualASN") && !json.get("virtualASN").isJsonNull()) {
current.setVirtualASN(json.get("virtualASN").getAsLong());
} else if (json.has("VirtualASN") && !json.get("VirtualASN").isJsonNull()) {
current.setVirtualASN(json.get("VirtualASN").getAsLong());
}
if (json.has("virtualSocketName") && !json.get("virtualSocketName").isJsonNull()) {
current.setVirtualSocketName(json.get("virtualSocketName").getAsString());
} else if (json.has("VirtualSocketName") && !json.get("VirtualSocketName").isJsonNull()) {
current.setVirtualSocketName(json.get("VirtualSocketName").getAsString());
}
if (json.has("enableTUN")) {
current.setEnableTUN(json.get("enableTUN").getAsBoolean());
} else if (json.has("EnableTUN")) {
current.setEnableTUN(json.get("EnableTUN").getAsBoolean());
}
if (json.has("tunName") && !json.get("tunName").isJsonNull()) {
current.setTUNName(json.get("tunName").getAsString());
} else if (json.has("TUNName") && !json.get("TUNName").isJsonNull()) {
current.setTUNName(json.get("TUNName").getAsString());
}
if (json.has("webUI")) {
current.setWebUI(json.get("webUI").getAsBoolean());
}
if (json.has("webListen") && !json.get("webListen").isJsonNull()) {
current.setWebListen(new MultiProtocolSocketAddress(json.get("webListen").getAsString()));
} else if (json.has("webPort")) {
current.setWebListen(new MultiProtocolSocketAddress("http", "0.0.0.0", json.get("webPort").getAsInt()));
}
if (json.has("nogui")) {
current.setNogui(json.get("nogui").getAsBoolean());
}
if (json.has("tcpListen") && !json.get("tcpListen").isJsonNull()) {
current.setTCPListen(new MultiProtocolSocketAddress(json.get("tcpListen").getAsString()));
} else if (json.has("TCPListen") && !json.get("TCPListen").isJsonNull()) {
current.setTCPListen(new MultiProtocolSocketAddress(json.get("TCPListen").getAsString()));
}
if (json.has("udpListen") && !json.get("udpListen").isJsonNull()) {
current.setUDPListen(new MultiProtocolSocketAddress(json.get("udpListen").getAsString()));
} else if (json.has("UDPListen") && !json.get("UDPListen").isJsonNull()) {
current.setUDPListen(new MultiProtocolSocketAddress(json.get("UDPListen").getAsString()));
}
if (json.has("externalEndpoints") || json.has("ExternalEndpoints") || json.has("openConnections") || json.has("OpenConnections") || json.has("lineTable") || json.has("LineTable")) {
JsonArray arr = json.has("externalEndpoints") ? json.getAsJsonArray("externalEndpoints")
: json.has("ExternalEndpoints") ? json.getAsJsonArray("ExternalEndpoints")
: json.has("openConnections") ? json.getAsJsonArray("openConnections")
: json.has("OpenConnections") ? json.getAsJsonArray("OpenConnections")
: json.has("lineTable") ? json.getAsJsonArray("lineTable") : json.getAsJsonArray("LineTable");
List<MultiProtocolSocketAddress> list = new ArrayList<>();
for (JsonElement el : arr) {
if (el.isJsonPrimitive()) {
list.add(new MultiProtocolSocketAddress(el.getAsString()));
}
}
current.setExternalEndpoints(list);
}
if (json.has("autoConnections") || json.has("AutoConnections") || json.has("connectLineTable") || json.has("ConnectLineTable")) {
JsonArray arr = json.has("autoConnections") ? json.getAsJsonArray("autoConnections")
: json.has("AutoConnections") ? json.getAsJsonArray("AutoConnections")
: json.has("connectLineTable") ? json.getAsJsonArray("connectLineTable") : json.getAsJsonArray("ConnectLineTable");
List<MultiProtocolSocketAddress> list = new ArrayList<>();
for (JsonElement el : arr) {
if (el.isJsonPrimitive()) {
list.add(new MultiProtocolSocketAddress(el.getAsString()));
}
}
current.setAutoConnections(list);
}
if (json.has("ntpServers") || json.has("NtpServers") || json.has("ntpServerTable")) {
JsonArray arr = json.has("ntpServers") ? json.getAsJsonArray("ntpServers")
: json.has("NtpServers") ? json.getAsJsonArray("NtpServers")
: json.getAsJsonArray("ntpServerTable");
List<MultiProtocolSocketAddress> list = new ArrayList<>();
for (JsonElement el : arr) {
if (el.isJsonPrimitive()) {
list.add(new MultiProtocolSocketAddress(el.getAsString()));
}
}
current.setNtpServers(list);
}
if (json.has("dns") || json.has("DNS")) {
JsonArray arr = json.has("dns") ? json.getAsJsonArray("dns") : json.getAsJsonArray("DNS");
List<InetAddress> list = new ArrayList<>();
for (JsonElement el : arr) {
if (el.isJsonPrimitive()) {
try {
list.add(InetAddress.getByName(el.getAsString()));
} catch (Exception ignored) {}
}
}
current.setDNS(list);
}
if (json.has("extraRoutes") || json.has("ExtraRoutes")) {
JsonArray arr = json.has("extraRoutes") ? json.getAsJsonArray("extraRoutes") : json.getAsJsonArray("ExtraRoutes");
List<String> list = new ArrayList<>();
for (JsonElement el : arr) {
if (el.isJsonPrimitive()) {
list.add(el.getAsString());
}
}
current.setExtraRoutes(list);
}
if (json.has("networkInterfaceExcepts") || json.has("NetworkInterfaceExcepts")) {
JsonArray arr = json.has("networkInterfaceExcepts") ? json.getAsJsonArray("networkInterfaceExcepts") : json.getAsJsonArray("NetworkInterfaceExcepts");
List<String> list = new ArrayList<>();
for (JsonElement el : arr) {
if (el.isJsonPrimitive()) {
list.add(el.getAsString());
}
}
current.setNetworkInterfaceExcepts(list);
}
if (json.has("congestionAlgorithm") && !json.get("congestionAlgorithm").isJsonNull()) {
current.setCongestionAlgorithm(json.get("congestionAlgorithm").getAsString());
}
if (json.has("performanceStrategy") && !json.get("performanceStrategy").isJsonNull()) {
current.setPerformanceStrategy(json.get("performanceStrategy").getAsString());
}
if (json.has("linkConnectionsCount")) {
current.setLinkConnectionsCount(json.get("linkConnectionsCount").getAsInt());
}
if (json.has("burstLimit")) {
current.setBurstLimit(json.get("burstLimit").getAsDouble());
}
if (json.has("delayUpperBound")) {
current.setDelayUpperBound(json.get("delayUpperBound").getAsDouble());
}
if (json.has("delayLowerBound")) {
current.setDelayLowerBound(json.get("delayLowerBound").getAsDouble());
}
if (json.has("nagleDelayTime")) {
current.setNagleDelayTime(json.get("nagleDelayTime").getAsLong());
}
if (json.has("linkNagleDelayTime")) {
current.setLinkNagleDelayTime(json.get("linkNagleDelayTime").getAsLong());
}
if (json.has("denyExternalEndpointQuery")) {
current.setDenyExternalEndpointQuery(json.get("denyExternalEndpointQuery").getAsBoolean());
} else if (json.has("denyConnectionQuery")) {
current.setDenyExternalEndpointQuery(json.get("denyConnectionQuery").getAsBoolean());
} else if (json.has("denyLineTableQuery")) {
current.setDenyExternalEndpointQuery(json.get("denyLineTableQuery").getAsBoolean());
}
if (json.has("denyExternalEndpointBroadcast")) {
current.setDenyExternalEndpointBroadcast(json.get("denyExternalEndpointBroadcast").getAsBoolean());
} else if (json.has("denyConnectionBroadcast")) {
current.setDenyExternalEndpointBroadcast(json.get("denyConnectionBroadcast").getAsBoolean());
} else if (json.has("denyLineTableBroadcast")) {
current.setDenyExternalEndpointBroadcast(json.get("denyLineTableBroadcast").getAsBoolean());
}
// Trigger GUI save consumer or save directly
if (proxySystem.getKLALBGUI() != null && proxySystem.getKLALBGUI().getSaveComsumer() != null) {
proxySystem.getKLALBGUI().getSaveComsumer().accept(proxySystem.getConfig());
} else {
proxySystem.saveConfigToFile();
}
JsonObject resp = new JsonObject();
resp.addProperty("success", true);
resp.addProperty("message", "Configuration updated successfully");
sendJsonResponse(exchange, 200, resp);
} else {
sendError(exchange, 500, "Current configuration is null");
}
} catch (Exception e) {
sendError(exchange, 400, "Failed to update configuration: " + e.getMessage());
}
} else {
sendError(exchange, 405, "Method not allowed");
}
}
// -------------------------------------------------------------
// SSE Handler & Broadcaster
// -------------------------------------------------------------
private void handleEvents(HttpExchange exchange) throws IOException {
if (handleCorsPreflight(exchange)) return;
if (!"GET".equalsIgnoreCase(exchange.getRequestMethod())) {
sendError(exchange, 405, "Method not allowed");
return;
}
Headers headers = exchange.getResponseHeaders();
headers.set("Content-Type", "text/event-stream; charset=utf-8");
headers.set("Cache-Control", "no-cache, no-transform");
headers.set("Connection", "keep-alive");
headers.set("Access-Control-Allow-Origin", "*");
exchange.sendResponseHeaders(200, 0);
sseClients.add(exchange);
}
private void startSseBroadcaster() {
sseExecutor.scheduleAtFixedRate(() -> {
if (sseClients.isEmpty()) return;
JsonObject eventData = new JsonObject();
eventData.add("status", getStatusJson());
eventData.add("links", getLinksJson());
String eventStr = "data: " + eventData.toString() + "\n\n";
byte[] bytes = eventStr.getBytes(StandardCharsets.UTF_8);
Iterator<HttpExchange> it = sseClients.iterator();
while (it.hasNext()) {
HttpExchange ex = it.next();
try {
OutputStream os = ex.getResponseBody();
os.write(bytes);
os.flush();
} catch (Exception e) {
try {
ex.close();
} catch (Exception ignored) {}
it.remove();
}
}
}, 200, 200, TimeUnit.MILLISECONDS);
}
// -------------------------------------------------------------
// Static Resource / SPA Handler
// -------------------------------------------------------------
private void handleStatic(HttpExchange exchange) throws IOException {
if (handleCorsPreflight(exchange)) return;
String path = exchange.getRequestURI().getPath();
if (path.startsWith("/api/")) {
sendError(exchange, 404, "API endpoint not found");
return;
}
// Look for dashboard/dist
File distDir = new File("dashboard/dist");
File targetFile = null;
if (distDir.exists() && distDir.isDirectory()) {
String relPath = path.equals("/") ? "index.html" : path.substring(1);
targetFile = new File(distDir, relPath);
if (!targetFile.exists() || targetFile.isDirectory()) {
targetFile = new File(distDir, "index.html");
}
}
if (targetFile != null && targetFile.exists() && targetFile.isFile()) {
String mime = getMimeType(targetFile.getName());
byte[] content = Files.readAllBytes(targetFile.toPath());
setCorsHeaders(exchange);
exchange.getResponseHeaders().set("Content-Type", mime);
exchange.sendResponseHeaders(200, content.length);
try (OutputStream os = exchange.getResponseBody()) {
os.write(content);
}
} else {
// Fallback welcome message if frontend not built yet
String html = "<!DOCTYPE html><html><head><meta charset='utf-8'><title>KLALB Web Dashboard</title>" +
"<style>body{font-family:system-ui,sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#09090b;color:#fafafa}" +
".card{background:#18181b;padding:2rem;border-radius:0.75rem;border:1px solid #27272a;max-width:480px;text-align:center}" +
"h1{margin:0 0 0.5rem;font-size:1.5rem}p{color:#a1a1aa;margin:0 0 1rem;font-size:0.875rem}code{background:#27272a;padding:0.2rem 0.4rem;border-radius:0.25rem}</style></head>" +
"<body><div class='card'><h1>KLALB Web Dashboard API Active</h1>" +
"<p>The backend API is ready. Start the frontend dev server or build the dashboard: <br><code>cd dashboard && pnpm build</code></p>" +
"<p><a href='/api/status' style='color:#38bdf8'>View /api/status</a></p></div></body></html>";
byte[] bytes = html.getBytes(StandardCharsets.UTF_8);
setCorsHeaders(exchange);
exchange.getResponseHeaders().set("Content-Type", "text/html; charset=utf-8");
exchange.sendResponseHeaders(200, bytes.length);
try (OutputStream os = exchange.getResponseBody()) {
os.write(bytes);
}
}
}
private String getMimeType(String filename) {
String lower = filename.toLowerCase();
if (lower.endsWith(".html")) return "text/html; charset=utf-8";
if (lower.endsWith(".js") || lower.endsWith(".mjs")) return "application/javascript; charset=utf-8";
if (lower.endsWith(".css")) return "text/css; charset=utf-8";
if (lower.endsWith(".json")) return "application/json; charset=utf-8";
if (lower.endsWith(".svg")) return "image/svg+xml";
if (lower.endsWith(".png")) return "image/png";
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
if (lower.endsWith(".ico")) return "image/x-icon";
if (lower.endsWith(".woff2")) return "font/woff2";
if (lower.endsWith(".woff")) return "font/woff";
if (lower.endsWith(".ttf")) return "font/ttf";
return "application/octet-stream";
}
}
@@ -26,7 +26,7 @@ import org.kne.math.Long128;
public class NTPContext implements Closeable, AutoCloseable {
private HighAccuracyClock clock;
private static final boolean debug = false;
private static final int REQUEST_COUNT = 5;
private static final int REQUEST_COUNT = 10;
private Long128 systemFrequencyOffset = NTPTimestamps.nanosToNtp128BitTimeInterval(new Long128(5000));
private Long128 localPrecision = NTPTimestamps.nanosToNtp128BitTimeInterval(new Long128(1000));
@@ -0,0 +1,52 @@
package org.kne.cloud.network.srv6;
import java.util.List;
import org.kne.cloud.network.MultiProtocolSocketAddress;
/**
* 节点信息开放线路 + 设备名称 + 设备描述由路由协议 JSON API 查询获得
*/
public class KLALBNodeInformation {
private List<MultiProtocolSocketAddress> openLines;
private String deviceName;
private String deviceDescription;
public KLALBNodeInformation(List<MultiProtocolSocketAddress> openLines, String deviceName,
String deviceDescription) {
super();
this.openLines = openLines;
this.deviceName = deviceName;
this.deviceDescription = deviceDescription;
}
public List<MultiProtocolSocketAddress> getOpenLines() {
return openLines;
}
public void setOpenLines(List<MultiProtocolSocketAddress> openLines) {
this.openLines = openLines;
}
public String getDeviceName() {
return deviceName;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public String getDeviceDescription() {
return deviceDescription;
}
public void setDeviceDescription(String deviceDescription) {
this.deviceDescription = deviceDescription;
}
@Override
public String toString() {
return "KLALBNodeInformation [openLines=" + openLines + ", deviceName=" + deviceName
+ ", deviceDescription=" + deviceDescription + "]";
}
}
@@ -44,6 +44,10 @@ public class KLALBRoutingProtocol extends Thread{
private RouterInfo selfRouterInfo;
private Map<IPv6Address, RouterInfo> netmap=new ConcurrentHashMap<>();
public Map<IPv6Address, RouterInfo> getNetmap() {
return netmap;
}
private volatile Map<IPv6Address,Long>addresses;
@@ -207,7 +211,7 @@ public class KLALBRoutingProtocol extends Thread{
while(true) {
try {
byte[]ca=MembandBoost.allocateUninitializedArray(65535);
byte[]ca=MembandBoost.allocateUninitializedByteArray(65535);
DatagramPacket dgp=new DatagramPacket(ca, ca.length);
ds.receive(dgp);
//System.out.println(Arrays.toString( Arrays.copyOf( dgp.getData(),dgp.getLength())));
@@ -29,11 +29,14 @@ public class KLALBRoutingProtocolAPIClient {
JsonDataPacket relate = null;
//System.out.println(ruid + " " + window.getSendmap());
switch (dataobj.getType()) {
case KLALBRoutingProtocolJsonData.OPEN_LINES_RESP:
case KLALBRoutingProtocolJsonData.NODE_INFO_FULL_RESP:
case KLALBRoutingProtocolJsonData.NODE_INFO_TINY_RESP:
if ((relate = window.ack(ruid)) != null) {
List<?> connects = (List<?>) dataobj.getData();
List<MultiProtocolSocketAddress> connectsm = new ArrayList<MultiProtocolSocketAddress>(connects.size());
List<MultiProtocolSocketAddress> connectsm = new ArrayList<MultiProtocolSocketAddress>(
connects == null ? 0 : connects.size());
if (connects != null) {
for (Object open : connects) {
if (open instanceof MultiProtocolSocketAddress) {
connectsm.add((MultiProtocolSocketAddress) open);
@@ -42,7 +45,11 @@ public class KLALBRoutingProtocolAPIClient {
}
}
((Consumer<List<MultiProtocolSocketAddress>>) relate.getUserCallback()).accept(connectsm);
}
// 组装节点信息线路 + 设备名称 + 设备描述精简模式下线路与描述为 null
KLALBNodeInformation info = new KLALBNodeInformation(connectsm, dataobj.getDeviceName(),
dataobj.getDeviceDescription());
((Consumer<KLALBNodeInformation>) relate.getUserCallback()).accept(info);
}
break;
}
@@ -56,11 +63,26 @@ public class KLALBRoutingProtocolAPIClient {
clr.register(this, releaser);
}
public void requestOpenLines(SocketAddress addr, Consumer<List<MultiProtocolSocketAddress>> callback)
/**
* 精简查询仅获取对端设备名称开销最小适用于未查看节点详情的场景
*/
public void requestNodeInfoTiny(SocketAddress addr, Consumer<KLALBNodeInformation> callback)
throws IOException {
sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_INFO_TINY_REQ, addr, callback);
}
/**
* 完整查询获取对端开放线路 + 设备名称 + 设备描述查看节点信息时使用
*/
public void requestNodeInfoFull(SocketAddress addr, Consumer<KLALBNodeInformation> callback)
throws IOException {
sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_INFO_FULL_REQ, addr, callback);
}
private void sendNodeInfoRequest(String type, SocketAddress addr, Consumer<KLALBNodeInformation> callback)
throws IOException {
UUID suid = UUID.randomUUID();
KLALBRoutingProtocolJsonData json = new KLALBRoutingProtocolJsonData(
KLALBRoutingProtocolJsonData.OPEN_LINES_REQ, suid, null);
KLALBRoutingProtocolJsonData json = new KLALBRoutingProtocolJsonData(type, suid, null);
JsonDataPacket packet = new JsonDataPacket(json);
packet.setUserCallback(callback);
window.put(suid, packet);
@@ -21,11 +21,21 @@ public class KLALBRoutingProtocolAPIServer {
KLALBRoutingProtocolJsonData dataobj= data.getDecodedData();
InetSocketAddress addrs=(InetSocketAddress) addr;
switch(dataobj.getType()){
case KLALBRoutingProtocolJsonData.OPEN_LINES_REQ:
if(controller.getConfigItem()==null||(!controller.getConfigItem().isDenyLineTableQuery())) {
KLALBRoutingProtocolJsonData json=new KLALBRoutingProtocolJsonData(KLALBRoutingProtocolJsonData.OPEN_LINES_RESP,dataobj.getUuid(),controller.getSelflineTable());
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(json),addr);
case KLALBRoutingProtocolJsonData.NODE_INFO_TINY_REQ:
// 精简查询仅返回设备名称设备名称随路由信息广播公开不受 denyExternalEndpointQuery 限制
KLALBRoutingProtocolJsonData tinyjson=new KLALBRoutingProtocolJsonData(KLALBRoutingProtocolJsonData.NODE_INFO_TINY_RESP,dataobj.getUuid(),null);
tinyjson.setDeviceName(controller.getIpv6Router().getDeviceName());
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(tinyjson),addr);
break;
case KLALBRoutingProtocolJsonData.NODE_INFO_FULL_REQ:
// 完整查询设备名称与描述总是正常响应denyExternalEndpointQuery 仅隐藏外部端点列表
boolean denyEndpoints=controller.getConfigItem()!=null&&controller.getConfigItem().isDenyExternalEndpointQuery();
KLALBRoutingProtocolJsonData json=new KLALBRoutingProtocolJsonData(KLALBRoutingProtocolJsonData.NODE_INFO_FULL_RESP,dataobj.getUuid(),denyEndpoints?null:controller.getExternalEndpoints());
json.setDeviceName(controller.getIpv6Router().getDeviceName());// 附带本机设备名称
if(controller.getConfigItem()!=null) {
json.setDeviceDescription(controller.getConfigItem().getDeviceDescription());// 附带本机设备描述
}
routingProtocol.sendJsonPacketToAddress(new JsonDataPacket(json),addr);
break;
}
} catch (IOException e) {
@@ -3,11 +3,27 @@ package org.kne.cloud.network.srv6;
import java.util.UUID;
public class KLALBRoutingProtocolJsonData {
public static final String OPEN_LINES_REQ="openlinesreq";
public static final String OPEN_LINES_RESP="openlinesresp";
public static final String NODE_INFO_TINY_REQ="nodeinfotinyreq";// 精简查询仅设备名称
public static final String NODE_INFO_TINY_RESP="nodeinfotinyresp";
public static final String NODE_INFO_FULL_REQ="nodeinfofullreq";// 完整查询开放线路+设备名称+设备描述
public static final String NODE_INFO_FULL_RESP="nodeinfofullresp";
private String type;
private UUID uuid;
private Object data;
private String deviceName;// 对端设备名称
private String deviceDescription;// 对端设备描述
public String getDeviceName() {
return deviceName;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public String getDeviceDescription() {
return deviceDescription;
}
public void setDeviceDescription(String deviceDescription) {
this.deviceDescription = deviceDescription;
}
public String getType() {
return type;
}
@@ -24,6 +40,8 @@ public class KLALBRoutingProtocolJsonData {
result = prime * result + ((data == null) ? 0 : data.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
result = prime * result + ((deviceName == null) ? 0 : deviceName.hashCode());
result = prime * result + ((deviceDescription == null) ? 0 : deviceDescription.hashCode());
return result;
}
@Override
@@ -50,6 +68,16 @@ public class KLALBRoutingProtocolJsonData {
return false;
} else if (!uuid.equals(other.uuid))
return false;
if (deviceName == null) {
if (other.deviceName != null)
return false;
} else if (!deviceName.equals(other.deviceName))
return false;
if (deviceDescription == null) {
if (other.deviceDescription != null)
return false;
} else if (!deviceDescription.equals(other.deviceDescription))
return false;
return true;
}
public KLALBRoutingProtocolJsonData(String type, UUID uuid, Object data) {
@@ -60,7 +88,8 @@ public class KLALBRoutingProtocolJsonData {
}
@Override
public String toString() {
return "KLALBRoutingProtocolJsonData [type=" + type + ", uuid=" + uuid + ", data=" + data + "]";
return "KLALBRoutingProtocolJsonData [type=" + type + ", uuid=" + uuid + ", data=" + data + ", deviceName="
+ deviceName + ", deviceDescription=" + deviceDescription + "]";
}
}
@@ -0,0 +1,50 @@
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;
}
}
+42 -63
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 MAX_REROUTE_COUNT = 2;// 1 // 最大重路由次数
public static final int MAX_REROUTE_COUNT = 2;// 最大重路由次数
private static final int IPv6_BITS = 128; // IPv6地址位数
@@ -104,7 +104,7 @@ public class SRv6Router {
// 环回网络链路
private final LoopbackIPv6NetworkLink inLoopBack;
private final SRHInsertNetworkLink srhInserter;
// 获取路由表
public List<RouteItem> getRouteTabel() {
return routeTabel;
@@ -121,7 +121,7 @@ public class SRv6Router {
@Override
public void accept(IPv6Packet t) {
// HighPerformanceExecutor.defaultExecutor.execute(() -> {
routePacket(null, t, true); // 执行重路由
routePacket(null, t); // 执行重路由
// });
}
};
@@ -142,21 +142,11 @@ public class SRv6Router {
if (oamtlv == null)
return true;
boolean bool = unduplicateSet.add(oamtlv.getUUID());
// System.out.println(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>> srhReceive = new BiConsumer<IPv6NetworkLink, Supplier<IPv6Packet>>() {
// 接收处理器
private BiConsumer<IPv6NetworkLink, Supplier<IPv6Packet>> receiveConsumer = new BiConsumer<IPv6NetworkLink, Supplier<IPv6Packet>>() {
@Override
public void accept(IPv6NetworkLink link, Supplier<IPv6Packet> pack) {
@@ -255,7 +245,7 @@ public class SRv6Router {
nlink.setSRv6Router(this);
nlink.addIPv6LinkStateListener(listener); // 添加链路状态监听器
nlink.setReceiveConsumer(srhReceive); // 设置接收处理器
nlink.setReceiveConsumer(receiveConsumer); // 设置接收处理器
if (nlink instanceof ControlledIPv6NetworkLink) {
((ControlledIPv6NetworkLink) nlink).setRerouteConsumer(defaultReroute); // 设置重路由处理器
((ControlledIPv6NetworkLink) nlink).setCongressCondition(congressLock, congressCondition); // 设置拥塞条件
@@ -323,9 +313,9 @@ public class SRv6Router {
}
// 插入SRH并路由数据包
public void insertSRHandRoutePacket(IPv6NetworkLink link, IPv6Packet ipp) {
insertHopByHopHeader(ipp);
insertSRHeader(ipp); // 插入段路由头
public void insertHeaderAndRoutePacket(IPv6NetworkLink link, IPv6Packet ipp) {
insertHopByHopHeader(ipp);//插入逐跳头
//insertSRHeader(ipp); // 插入段路由头
routePacket(link, ipp); // 路由数据包
}
@@ -384,16 +374,13 @@ public class SRv6Router {
}
private void routePacket(IPv6Packet iPv6Packet) {
routePacket(null, iPv6Packet, false);
routePacket(null, iPv6Packet);
}
// 路由数据包默认非重路由
private void routePacket(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet) {
routePacket(linkfrom, iPv6Packet, false);
}
// 路由数据包主方法
private void routePacket(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, boolean reroute) {
private void routePacket(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet) {
StringBuilder dbg = null;
if (debug) {
dbg = new StringBuilder(); // 调试信息
@@ -406,7 +393,7 @@ public class SRv6Router {
List<RouteItem> searchResult = getTabelByAddress(dest);
if (searchResult!=null&&(!searchResult.isEmpty())) {
// 匹配到路由表执行负载均衡路由
routingLoadBalance(linkfrom, iPv6Packet, reroute, dbg, searchResult);
routingLoadBalance(linkfrom, iPv6Packet, dbg, searchResult);
return;
}
@@ -441,7 +428,7 @@ public class SRv6Router {
// 重新搜索路由表
List<RouteItem> searchResult2 = getTabelByAddress(dest);
if (searchResult2!=null&&(!searchResult2.isEmpty())) {
routingLoadBalance(linkfrom, iPv6Packet, reroute, dbg, searchResult2);
routingLoadBalance(linkfrom, iPv6Packet, dbg, searchResult2);
return;
}
@@ -508,7 +495,7 @@ public class SRv6Router {
private Condition congressCondition = congressLock.newCondition(); // 拥塞条件
// 负载均衡路由
private boolean routingLoadBalance(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, boolean reroute,
private boolean routingLoadBalance(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet,
StringBuilder dbg, List<RouteItem> routes) throws IllegalRawDataException, IOException {
if(routes==null||routes.isEmpty()) {
@@ -517,7 +504,7 @@ public class SRv6Router {
}
for (double i = 1; i < 100; i += 0.1) {
if (loadBalance(linkfrom, iPv6Packet, reroute, dbg, routes, i)) {
if (loadBalance(linkfrom, iPv6Packet, dbg, routes, i)) {
return true;
}
@@ -536,7 +523,7 @@ public class SRv6Router {
return false; // 路由失败
}
private boolean loadBalance(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, boolean reroute, StringBuilder dbg,
private boolean loadBalance(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, StringBuilder dbg,
List<RouteItem> routes, double cscale)
throws IllegalRawDataException, IOException {
// 遍历可用路由进行负载均衡
@@ -564,31 +551,39 @@ public class SRv6Router {
dbg.append("matched.\n");
}
// 找到可用链路处理数据包
processPacket(linkfrom, iPv6Packet, tri, reroute);
processPacket(linkfrom, iPv6Packet, tri);
return true;
}
return false;
}
// 处理数据包转发
private void processPacket(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, RouteItem ri, boolean reroute)
private void processPacket(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, RouteItem ri)
throws IllegalRawDataException, IOException {
int hop = iPv6Packet.getHopLimit();
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();
if (srhh != null) {
processSRv6Packet(linkfrom, iPv6Packet, ri, srhh, iPv6Packet.getHopByHopHeader(), reroute); // SRv6特殊处理
processSRv6Packet(linkfrom, iPv6Packet, ri, srhh); // SRv6特殊处理
} else {
sendPacketToRouteItem(iPv6Packet, ri);
}
} else { // 普通链路处理
// 检查重路由计数
if (iPv6Packet.getRerouteCounter().getAndIncrement() >= MAX_REROUTE_COUNT) {
int count=iPv6Packet.getRouteCounter().getAndIncrement();
if (count >= MAX_REROUTE_COUNT) {
return; // 超过最大重路由次数丢弃
}
if (!reroute) {
if (count<=0) {
hop--; // 减少TTL非重路由时
}
@@ -651,34 +646,16 @@ public class SRv6Router {
// 处理SRv6数据包
private void processSRv6Packet(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, RouteItem ri,
IPv6SegmentRoutingHeader srhh, IPv6HopByHopHeader iPv6HopByHopHeader, boolean reroute)
IPv6SegmentRoutingHeader srhh)
throws IllegalRawDataException, IOException {
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);
} else {
if (reroute) {
// 重路由处理
} else {
if (iPv6Packet.getRouteCounter().get()<=0) {
// 重路由处理
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处理移动到下一个段
int newSL = oldSL - 1;
srhh.setSegmentsLeft(newSL); // 更新剩余段数
@@ -687,7 +664,7 @@ public class SRv6Router {
// 记录热点地址用于流量工程
if (iPv6Packet.getPayload().getProtocolNumber() != KLALBRoutingProtocol.DEFAULT_PROTOCOL_NUMBER)
klalbRouteProtol.putHotspotAddress(iPv6Packet.getSourceAddress());
routePacket(linkfrom, iPv6Packet, reroute); // 继续路由
routePacket(linkfrom, iPv6Packet); // 继续路由
}
}
@@ -814,7 +791,7 @@ public class SRv6Router {
if(ecn) {
packet.markCE();
}
insertSRHandRoutePacket(null, packet); // 插入SRH并路由
insertHeaderAndRoutePacket(null, packet); // 插入SRH并路由
long time=System.nanoTime()-start;
backplaneCount(time,ecn);
} catch (Exception e) {
@@ -827,7 +804,7 @@ public class SRv6Router {
public void onReceive(IPv6NetworkLink link,IPv6Packet t){
if (unduplicate(t)) {
insertSRHandRoutePacket(link, t); // 插入SRH并路由数据包
insertHeaderAndRoutePacket(link, t); // 插入SRH并路由数据包
}
}
@@ -916,15 +893,17 @@ public class SRv6Router {
}
// 构造函数
public SRv6Router(IPv6AddressGroup hostAddress, HighAccuracyClock clock) {
public SRv6Router(IPv6AddressGroup endSID,IPv6AddressGroup networkGroup, HighAccuracyClock clock) {
super();
this.locator = hostAddress;
this.locator = endSID;
this.clock = clock;
// initWorkerThreads();
// 创建环回链路
this.inLoopBack = new LoopbackIPv6NetworkLink(
List.of(new IPv6AddressGroup(IPv6Address.LOOPBACK, 128), hostAddress), this);
List.of(new IPv6AddressGroup(IPv6Address.LOOPBACK, 128), endSID), this);
linkTabel.add(inLoopBack); // 添加到链路表
this.srhInserter=new SRHInsertNetworkLink(this,networkGroup);
linkTabel.add(srhInserter);
backplaneTimer.scheduleAtFixedRate(new TimerTask() {
@Override
Binary file not shown.