forked from KNEMC/KLALB
Merge branch 'master' of https://git.code.cq.cn/KNEMC/KLALB
# Conflicts: # .classpath # src/org/kne/cloud/network/klalb/KLALBControllerConfigItem.java
This commit is contained in:
@@ -3,7 +3,10 @@ networkgraph=Network graph
|
||||
language=Language
|
||||
basicsettings=Basic settings
|
||||
ipv6addr=IPv6 address
|
||||
devicename=Device name
|
||||
devicedescription=Device description
|
||||
dnsserver=DNS server
|
||||
extraroutes=Extra routes
|
||||
asnumber=AS number
|
||||
tcplistening=TCP listening
|
||||
udplistening=UDP listening
|
||||
@@ -34,6 +37,7 @@ saveconfigsuccess=Save config success
|
||||
warning=Warning
|
||||
invaildipv6addr=IPv6 address:Invaild Input
|
||||
invailddnsserver=DNS server:Invaild Input
|
||||
invaildextraroutes=Extra routes:Invaild Input
|
||||
invaildasnumber=AS number:Invaild Input
|
||||
invaildtcplisten=TCP listen:Invaild Input
|
||||
invaildudplisten=UDP listen:Invaild Input
|
||||
@@ -107,4 +111,9 @@ performancesettings=Performance settings
|
||||
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)
|
||||
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
|
||||
|
||||
@@ -3,7 +3,10 @@ networkgraph=网络拓扑图
|
||||
language=语言
|
||||
basicsettings=基本设置
|
||||
ipv6addr=IPv6地址
|
||||
devicename=设备名称
|
||||
devicedescription=设备描述
|
||||
dnsserver=DNS服务器
|
||||
extraroutes=额外路由
|
||||
asnumber=AS号码
|
||||
tcplistening=TCP监听端口
|
||||
udplistening=UDP监听端口
|
||||
@@ -34,6 +37,7 @@ saveconfigsuccess=保存配置成功
|
||||
warning=警告
|
||||
invaildipv6addr=IPv6地址:非法输入
|
||||
invailddnsserver=DNS服务器:非法输入
|
||||
invaildextraroutes=额外路由:非法输入
|
||||
invaildasnumber=AS号码:非法输入
|
||||
invaildtcplisten=TCP监听端口:非法输入
|
||||
invaildudplisten=UDP监听端口:非法输入
|
||||
@@ -107,4 +111,9 @@ performancesettings=性能设置
|
||||
performancestrategy=性能策略
|
||||
singlecore=单核-缓存命中率优先(高能耗比,适合低功耗设备、云机)
|
||||
multifill=多核-负载按顺序填充(适合大多数物理服务器、电脑)
|
||||
multiscatter=多核-负载均匀打散分配(适合特殊的多路NUMA服务器)
|
||||
multiscatter=多核-负载均匀打散分配(适合特殊的多路NUMA服务器)
|
||||
webapisettings=Web API 设置
|
||||
enablewebapi=启用 Web API
|
||||
weblistenaddr=Web API 监听地址:端口
|
||||
invaildweblistenaddr=无效的 Web API 监听地址:端口
|
||||
enabletun=启用TUN虚拟网卡
|
||||
@@ -7,8 +7,9 @@ import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
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<>();
|
||||
|
||||
@@ -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');
|
||||
@@ -637,6 +637,7 @@ public class KLALBController {
|
||||
srv6Router = new SRv6Router(selfx, clock);
|
||||
if(configItem!=null) {
|
||||
srv6Router.setPerformanceStrategy(PerformanceStrategy.fromDescription(configItem.getPerformanceStrategy()));
|
||||
srv6Router.setDeviceName(configItem.getDeviceName());
|
||||
}
|
||||
srv6Router.runKLALBRouteProtocol();
|
||||
routingProtocol = srv6Router.getKlalbRouteProtol();
|
||||
@@ -646,11 +647,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;
|
||||
if (enableVirtualAdapter&&(name!=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,
|
||||
IPv6TUNLoopbackNetworkLink tunlink = new IPv6TUNLoopbackNetworkLink(name.trim(),
|
||||
new IPv6AddressGroup(srv6Router.getLocator().getAddress(), 32), SRv6Router.MTU, dnsAddresses);
|
||||
tunlink.setMonitor(datatMonitor);
|
||||
// srv6Router.getLinkTabel().add(tunlink);
|
||||
@@ -664,7 +667,7 @@ public class KLALBController {
|
||||
});
|
||||
t.start();
|
||||
}else {
|
||||
System.out.println(" Tun Adapter Disbaled");
|
||||
System.out.println(" Tun Adapter Disabled");
|
||||
|
||||
}
|
||||
udpr=new UDPProtocolRegister(this);
|
||||
@@ -798,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);
|
||||
}
|
||||
List<MultiProtocolSocketAddress> linetoc = configItem.getConnectLineTable();
|
||||
getExternalEndpoints().addAll(linele);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -15,12 +15,13 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
|
||||
private List<InetAddress> DNS;
|
||||
private MultiProtocolSocketAddress TCPListen=new MultiProtocolSocketAddress("0.0.0.0",4565);
|
||||
private MultiProtocolSocketAddress UDPListen=new MultiProtocolSocketAddress("udp","0.0.0.0",4565);
|
||||
private String VirtualSocketName=CONST.KLTP_VIRTUAL_SOCKET_NAME;
|
||||
private List<MultiProtocolSocketAddress>LineTable=new ArrayList<>();
|
||||
private List<MultiProtocolSocketAddress>ConnectLineTable=new ArrayList<>();
|
||||
private List<MultiProtocolSocketAddress>ntpServerTable=new ArrayList<>();
|
||||
private boolean denyLineTableQuery=false;
|
||||
private boolean denyLineTableBroadcast=false;
|
||||
private String VirtualSocketName;
|
||||
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;
|
||||
@@ -28,8 +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;
|
||||
@@ -39,6 +69,22 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
|
||||
this.performanceStrategy = performanceStrategy;
|
||||
}
|
||||
|
||||
public String getDeviceName() {
|
||||
return DeviceName;
|
||||
}
|
||||
|
||||
public void setDeviceName(String deviceName) {
|
||||
DeviceName = deviceName;
|
||||
}
|
||||
|
||||
public String getDeviceDescription() {
|
||||
return DeviceDescription;
|
||||
}
|
||||
|
||||
public void setDeviceDescription(String deviceDescription) {
|
||||
DeviceDescription = deviceDescription;
|
||||
}
|
||||
|
||||
public void setNetworkInterfaceExcepts(List<String> networkInterfaceExcepts) {
|
||||
NetworkInterfaceExcepts = networkInterfaceExcepts;
|
||||
}
|
||||
@@ -158,39 +204,52 @@ 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() {
|
||||
return ExtraRoutes;
|
||||
}
|
||||
|
||||
public void setExtraRoutes(List<String> extraRoutes) {
|
||||
ExtraRoutes = extraRoutes;
|
||||
}
|
||||
|
||||
|
||||
@@ -253,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -325,11 +381,12 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
|
||||
", TCPListen=" + TCPListen +
|
||||
", UDPListen=" + UDPListen +
|
||||
", VirtualSocketName='" + VirtualSocketName + '\'' +
|
||||
", LineTable=" + LineTable +
|
||||
", ConnectLineTable=" + ConnectLineTable +
|
||||
", ntpServerTable=" + ntpServerTable +
|
||||
", denyLineTableQuery=" + denyLineTableQuery +
|
||||
", denyLineTableBroadcast=" + denyLineTableBroadcast +
|
||||
", externalEndpoints=" + externalEndpoints +
|
||||
", autoConnections=" + autoConnections +
|
||||
", ntpServers=" + ntpServers +
|
||||
", ExtraRoutes=" + ExtraRoutes +
|
||||
", denyExternalEndpointQuery=" + denyExternalEndpointQuery +
|
||||
", denyExternalEndpointBroadcast=" + denyExternalEndpointBroadcast +
|
||||
", congestionAlgorithm='" + congestionAlgorithm + '\'' +
|
||||
", burstLimit=" + burstLimit +
|
||||
", delayUpperBound=" + delayUpperBound +
|
||||
@@ -337,10 +394,15 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
|
||||
", nagleDelayTime=" + nagleDelayTime +
|
||||
", linkNagleDelayTime=" + linkNagleDelayTime +
|
||||
", linkConnectionsCount=" + linkConnectionsCount +
|
||||
", enableTUN=" + enableTUN +
|
||||
", TUNName='" + TUNName + '\'' +
|
||||
", performanceStrategy='" + performanceStrategy + '\'' +
|
||||
", NetworkInterfaceExcepts=" + NetworkInterfaceExcepts +
|
||||
'}';
|
||||
", DeviceName='" + DeviceName + '\'' +
|
||||
", DeviceDescription='" + DeviceDescription + '\'' +
|
||||
", NetworkInterfaceExcepts=" + NetworkInterfaceExcepts +
|
||||
", webUI=" + webUI +
|
||||
", webListen=" + webListen +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -348,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(congestionAlgorithm, that.congestionAlgorithm) && Objects.equals(TUNName, that.TUNName) && Objects.equals(performanceStrategy, that.performanceStrategy) && 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, denyLineTableQuery, denyLineTableBroadcast, congestionAlgorithm, burstLimit, delayUpperBound, delayLowerBound, nagleDelayTime, linkNagleDelayTime, linkConnectionsCount, TUNName, performanceStrategy, 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,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);
|
||||
@@ -83,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");
|
||||
@@ -101,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下载抖动");
|
||||
|
||||
@@ -5,25 +5,34 @@ import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.HashSet;
|
||||
|
||||
import 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,30 +234,24 @@ public class KLALBProxySystem {
|
||||
}
|
||||
|
||||
|
||||
public void saveConfigToFile() {
|
||||
if (jsonFile != null && config != null) {
|
||||
String json = gson.toJson(config);
|
||||
try (FileWriter fw = new FileWriter(jsonFile)) {
|
||||
fw.write(json);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private KLALBStateGUI3 kgui;
|
||||
public KLALBStateGUI3 getKLALBGUI() {
|
||||
if(kgui==null) {
|
||||
kgui=new KLALBStateGUI3(klalbController);
|
||||
kgui.loadConfig(config);
|
||||
kgui.setSaveComsumer((cfg)->{
|
||||
String json=gson.toJson(cfg);
|
||||
if(jsonFile!=null) {
|
||||
FileWriter fw = null;
|
||||
try {
|
||||
fw=new FileWriter(jsonFile);
|
||||
fw.write(json);
|
||||
}catch(IOException e) {
|
||||
e.printStackTrace();
|
||||
}finally {
|
||||
if(fw!=null)
|
||||
try {
|
||||
fw.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
saveConfigToFile();
|
||||
});
|
||||
}
|
||||
return kgui;
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
package org.kne.cloud.network.klalb;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.kne.cloud.network.MultiProtocolSocketAddress;
|
||||
import org.kne.cloud.network.SocketListener;
|
||||
import org.kne.cloud.network.ipv6.IPv6NetworkLink;
|
||||
import org.kne.cloud.network.monitor.LinkStatus;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
||||
public class KLALBRemoteManagement {
|
||||
SocketListener slr;
|
||||
private KLALBProxySystem klalbProxySystem;
|
||||
public KLALBRemoteManagement(KLALBProxySystem klalbProxySystem) throws IOException {
|
||||
this(klalbProxySystem,new MultiProtocolSocketAddress("127.9.9.9", 49573));
|
||||
}
|
||||
public KLALBRemoteManagement(KLALBProxySystem klalbProxySystem, MultiProtocolSocketAddress listen) throws IOException {
|
||||
this.klalbProxySystem=klalbProxySystem;
|
||||
slr=new SocketListener(listen);
|
||||
slr.setCon((srcv)->{
|
||||
try {
|
||||
srcv.setSoTimeout(10000);
|
||||
byte[]input= srcv.getInputStream().readAllBytes();
|
||||
srcv.shutdownInput();
|
||||
String req=new String(input,Charset.forName("UTF-8"));
|
||||
System.out.println("远程管理请求:"+req);
|
||||
String rsp=processSignal(req);
|
||||
System.out.println("远程管理响应:"+rsp);
|
||||
srcv.getOutputStream().write(rsp.getBytes(Charset.forName("UTF-8")));
|
||||
srcv.shutdownOutput();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}finally {
|
||||
try {
|
||||
srcv.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
public KLALBProxySystem getKlalbProxySystem() {
|
||||
return klalbProxySystem;
|
||||
}
|
||||
private String processSignal(String req) {
|
||||
JsonObject jreq= (JsonObject) new JsonParser().parse(req);
|
||||
JsonObject jrsp=new JsonObject();
|
||||
|
||||
|
||||
String reqt=jreq.getAsJsonPrimitive("REQ").getAsString();
|
||||
jrsp.addProperty("RSP", reqt);
|
||||
switch (reqt) {
|
||||
case "GETLINES":
|
||||
JsonArray lines=new JsonArray();
|
||||
List<IPv6NetworkLink>lineslist= klalbProxySystem.getKlalbController().getLines();
|
||||
synchronized (lineslist) {
|
||||
for (Iterator<IPv6NetworkLink> iterator = lineslist.iterator(); iterator.hasNext();) {
|
||||
IPv6NetworkLink link=iterator.next();
|
||||
if(!(link instanceof KLALBRemoteLink)) {
|
||||
continue;
|
||||
}
|
||||
KLALBRemoteLink klalbRemoteLine = (KLALBRemoteLink) link;
|
||||
if(klalbRemoteLine.getSocketAddress()==null)
|
||||
continue;
|
||||
JsonObject jklbrl=new JsonObject();
|
||||
jklbrl.addProperty("ipport", klalbRemoteLine.getSocketAddress().toString());
|
||||
jklbrl.addProperty("state",LinkStatus.stateToString( klalbRemoteLine.getState()));
|
||||
|
||||
jklbrl.addProperty("Vaddr", klalbRemoteLine.getRemoteVaddr().getAddress().toString());
|
||||
|
||||
jklbrl.addProperty("uploadspeed", klalbRemoteLine.getMonitor().getOutSpeed());
|
||||
jklbrl.addProperty("downloadspeed", klalbRemoteLine.getMonitor().getInSpeed());
|
||||
|
||||
jklbrl.addProperty("uploadtraffic", klalbRemoteLine.getMonitor().getOutTraffic());
|
||||
jklbrl.addProperty("downloadtraffic", klalbRemoteLine.getMonitor().getInTraffic());
|
||||
|
||||
jklbrl.addProperty("uploaddelay",klalbRemoteLine.getMonitor().getOutDelay() );
|
||||
jklbrl.addProperty("downloaddelay", klalbRemoteLine.getMonitor().getInDelay());
|
||||
|
||||
jklbrl.addProperty("uploaddelaymin",klalbRemoteLine.getMonitor().getOutDelayMin() );
|
||||
jklbrl.addProperty("downloaddelaymin", klalbRemoteLine.getMonitor().getInDelayMin());
|
||||
lines.add(jklbrl);
|
||||
}
|
||||
}
|
||||
jrsp.add("table", lines);
|
||||
break;
|
||||
case "ADDLINE":
|
||||
String mip=jreq.getAsJsonPrimitive("ipport").getAsString();
|
||||
try {
|
||||
|
||||
jrsp.addProperty ("Vaddr",klalbProxySystem.getKlalbController().getRemoteVaddrBySocketAddress(new MultiProtocolSocketAddress(mip)).getHostAddress());
|
||||
} catch (SocketTimeoutException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
break;
|
||||
case "GETSELFLINES":
|
||||
JsonArray lines2=new JsonArray();
|
||||
List<MultiProtocolSocketAddress>selflineslist=klalbProxySystem.getKlalbController().getSelflineTable();
|
||||
synchronized (selflineslist) {
|
||||
for (Iterator<MultiProtocolSocketAddress> iterator = selflineslist.iterator(); iterator.hasNext();) {
|
||||
MultiProtocolSocketAddress multiProtocolSocketAddress = (MultiProtocolSocketAddress) iterator.next();
|
||||
lines2.add(new JsonPrimitive(multiProtocolSocketAddress.toString()));
|
||||
}
|
||||
}
|
||||
jrsp.add("table", lines2);
|
||||
|
||||
break;
|
||||
case "ADDSELFLINE":
|
||||
String mips=jreq.getAsJsonPrimitive("ipport").getAsString();
|
||||
List<MultiProtocolSocketAddress>selflineslist2=klalbProxySystem.getKlalbController().getSelflineTable();
|
||||
synchronized (selflineslist2) {
|
||||
selflineslist2.add(new MultiProtocolSocketAddress(mips));
|
||||
}
|
||||
break;
|
||||
case "REMOVESELFLINE":
|
||||
String mipsr=jreq.getAsJsonPrimitive("ipport").getAsString();
|
||||
List<MultiProtocolSocketAddress>selflineslist21=klalbProxySystem.getKlalbController().getSelflineTable();
|
||||
synchronized (selflineslist21) {
|
||||
selflineslist21.add(new MultiProtocolSocketAddress(mipsr));
|
||||
}
|
||||
break;
|
||||
case "GETLINKMONITOR":
|
||||
jrsp.addProperty("uploadspeed", klalbProxySystem.getKlalbController().getLinkMonitor().getOutSpeed());
|
||||
jrsp.addProperty("downloadspeed", klalbProxySystem.getKlalbController().getLinkMonitor().getInSpeed());
|
||||
|
||||
jrsp.addProperty("uploadtraffic", klalbProxySystem.getKlalbController().getLinkMonitor().getOutTraffic());
|
||||
jrsp.addProperty("downloadtraffic", klalbProxySystem.getKlalbController().getLinkMonitor().getInTraffic());
|
||||
|
||||
break;
|
||||
case "OPENMONITORUI":
|
||||
klalbProxySystem.getKLALBGUI().setVisible(true);
|
||||
break;
|
||||
/*case "GETSOCKETBRIDGE":
|
||||
JsonArray bridges=new JsonArray();
|
||||
Set<Proxy> pxy=klalbProxySystem.getProxys();
|
||||
synchronized (pxy) {
|
||||
for (Iterator<Proxy> iterator = pxy.iterator(); iterator.hasNext();) {
|
||||
Proxy proxy = (Proxy) iterator.next();
|
||||
bridges.add(klalbProxySystem.createJsonObjectByProxy(proxy));
|
||||
}
|
||||
}
|
||||
jrsp.add("table", bridges);
|
||||
break;
|
||||
case "ADDSOCKETBRIDGE":
|
||||
JsonObject jpxy= jreq.getAsJsonObject("socketbridge");
|
||||
Set<Proxy> pxy2=klalbProxySystem.getProxys();
|
||||
synchronized (pxy2) {
|
||||
try {
|
||||
pxy2.add(klalbProxySystem.createProxyByJson(jpxy));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;*/
|
||||
default:
|
||||
System.out.println("未知请求类型:"+reqt);
|
||||
break;
|
||||
}
|
||||
|
||||
return jrsp.toString();
|
||||
}
|
||||
public void close() {
|
||||
slr.close();
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,27 @@ public class KLALBUtils {
|
||||
v[3] = 0x01;
|
||||
return IPv6Address.valueOf(v);
|
||||
}
|
||||
|
||||
public static String parseCidr(String cidr) {
|
||||
int idx = cidr.indexOf('/');
|
||||
if (idx <= 0 || idx == cidr.length() - 1)
|
||||
throw new IllegalArgumentException("not a cidr: " + cidr);
|
||||
int prefix;
|
||||
try {
|
||||
prefix = Integer.parseInt(cidr.substring(idx + 1));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("bad prefix: " + cidr);
|
||||
}
|
||||
try {
|
||||
InetAddress addr = InetAddress.getByName(cidr.substring(0, idx));
|
||||
int max = (addr instanceof Inet6Address) ? 128 : 32;
|
||||
if (prefix < 0 || prefix > max)
|
||||
throw new IllegalArgumentException("prefix out of range: " + cidr);
|
||||
return addr.getHostAddress() + "/" + prefix;
|
||||
} catch (UnknownHostException e) {
|
||||
throw new IllegalArgumentException("bad address: " + cidr);
|
||||
}
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
System.out.println(uuidToIP(new UUID(-1,-1)));
|
||||
}
|
||||
|
||||
@@ -346,7 +346,16 @@ public class GraphPanel extends JPanel {
|
||||
g.setFont(UIEnv.getFont().deriveFont(10.0f));
|
||||
|
||||
//g.drawString(text, (int)(x+nodesize/2), (int)(y-nodesize/3));
|
||||
g.drawString(text, (int)(x-image.getWidth(null)/2-10), (int)(y+image.getHeight(null)/2+10));
|
||||
int imgw=(image!=null)?image.getWidth(null):(int)nodesize;
|
||||
int imgh=(image!=null)?image.getHeight(null):(int)nodesize;
|
||||
java.awt.FontMetrics fm=g.getFontMetrics();
|
||||
int lineHeight=fm.getHeight()+2;
|
||||
int textx=(int)(x-imgw/2-10);
|
||||
int texty=(int)(y+imgh/2+10)+fm.getAscent();
|
||||
String[] lines=text.split("\n");
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
g.drawString(lines[i], textx, texty+i*lineHeight);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 getPositionVec2() {
|
||||
|
||||
@@ -75,11 +75,17 @@ 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显示框
|
||||
private JTextArea dnsAreaSet; // DNS服务器设置区域
|
||||
private JTextArea extraRoutesSet; // 额外路由设置区域
|
||||
private JTextField deviceNameSet; // 设备名称设置框
|
||||
private JTextArea deviceDescriptionSet; // 设备描述设置区域
|
||||
private KLALBController kcontroller; // 关联的控制器
|
||||
private JComboBox<PerformanceStrategyItem> comboPerformance; // 性能策略选择框
|
||||
private JComboBox comboCongestion; // 拥塞控制算法选择框
|
||||
private JTextField tcpListeningSet; // TCP监听设置
|
||||
@@ -92,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;
|
||||
|
||||
@@ -138,6 +145,7 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
* 初始化UI界面
|
||||
*/
|
||||
private void initUI(KLALBController kc, String title) {
|
||||
kcontroller = kc;
|
||||
// 设置窗口基本属性
|
||||
setIconImage(UIEnv.getIcon());
|
||||
setTitleColor(UIEnv.getDefaultTitleColor());
|
||||
@@ -785,18 +793,42 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
addressFieldSet = addr6.getTextField();
|
||||
settings.getView().add(addr6);
|
||||
|
||||
// 设备名称设置
|
||||
TextSettingItem deviceName = new TextSettingItem(UIEnv.getRsb().getString("devicename"),
|
||||
CONST.itemwidth, CONST.settingheight);
|
||||
deviceNameSet = deviceName.getTextField();
|
||||
settings.getView().add(deviceName);
|
||||
|
||||
// 设备描述设置
|
||||
TextAreaSettingItem deviceDescription = new TextAreaSettingItem(UIEnv.getRsb().getString("devicedescription"),
|
||||
CONST.itemwidth, CONST.settingheight * 5);
|
||||
deviceDescriptionSet = deviceDescription.getTextArea();
|
||||
settings.getView().add(deviceDescription);
|
||||
|
||||
// DNS服务器设置
|
||||
TextAreaSettingItem dns = new TextAreaSettingItem(UIEnv.getRsb().getString("dnsserver"),
|
||||
CONST.itemwidth, CONST.settingheight * 5);
|
||||
dnsAreaSet = dns.getTextArea();
|
||||
settings.getView().add(dns);
|
||||
|
||||
// 额外路由设置
|
||||
TextAreaSettingItem extraRoutes = new TextAreaSettingItem(UIEnv.getRsb().getString("extraroutes"),
|
||||
CONST.itemwidth, CONST.settingheight * 5);
|
||||
extraRoutesSet = extraRoutes.getTextArea();
|
||||
settings.getView().add(extraRoutes);
|
||||
|
||||
// ASN设置
|
||||
TextSettingItem asn = new TextSettingItem(UIEnv.getRsb().getString("asnumber"),
|
||||
CONST.itemwidth, CONST.settingheight);
|
||||
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();
|
||||
@@ -881,7 +913,22 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
CONST.itemwidth, CONST.settingheight);
|
||||
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"),
|
||||
@@ -1022,6 +1069,26 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
|
||||
kck.setNogui(nogui.isSelected());
|
||||
|
||||
// 保存设备名称
|
||||
String dnametext = deviceNameSet.getText().trim();
|
||||
if (dnametext.equals("")) {
|
||||
kck.setDeviceName(null);
|
||||
} else {
|
||||
kck.setDeviceName(dnametext);
|
||||
}
|
||||
if (kcontroller != null && kcontroller.getIpv6Router() != null) {
|
||||
kcontroller.getIpv6Router().setDeviceName(kck.getDeviceName());
|
||||
}
|
||||
|
||||
// 保存设备描述
|
||||
String ddesctext = deviceDescriptionSet.getText().trim();
|
||||
if (ddesctext.equals("")) {
|
||||
kck.setDeviceDescription(null);
|
||||
} else {
|
||||
kck.setDeviceDescription(ddesctext);
|
||||
}
|
||||
|
||||
|
||||
// 保存IPv6地址
|
||||
String ttext = addressFieldSet.getText().trim();
|
||||
if (ttext.equals("")) {
|
||||
@@ -1058,6 +1125,24 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
}
|
||||
kck.setDNS(iaddr);
|
||||
|
||||
// 保存额外路由列表
|
||||
String[] spltEr = extraRoutesSet.getText().split("\n");
|
||||
List<String> eroutes = new ArrayList<>();
|
||||
for (String str : spltEr) {
|
||||
str = str.trim();
|
||||
if (!str.isEmpty()) {
|
||||
try {
|
||||
eroutes.add(KLALBUtils.parseCidr(str));
|
||||
} catch (RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
JOptionPane.showMessageDialog(this, UIEnv.getRsb().getString("invaildextraroutes"),
|
||||
UIEnv.getRsb().getString("warning"), JOptionPane.WARNING_MESSAGE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
kck.setExtraRoutes(eroutes);
|
||||
|
||||
// 保存ASN
|
||||
String asntext = asnFieldSet.getText();
|
||||
if (asntext.equals("")) {
|
||||
@@ -1073,12 +1158,30 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
}
|
||||
}
|
||||
|
||||
String tunNameText=tunDeviceName.getText();
|
||||
if(tunNameText.equals("")) {
|
||||
kck.setEnableTUN( enableTUN.isSelected());
|
||||
|
||||
String tunNameText=tunDeviceName.getText().trim();
|
||||
if(tunNameText.equals("") || tunNameText.equalsIgnoreCase("null")) {
|
||||
kck.setTUNName(null);
|
||||
}else {
|
||||
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();
|
||||
@@ -1129,7 +1232,7 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
}
|
||||
}
|
||||
}
|
||||
kck.setLineTable(iaddr1);
|
||||
kck.setExternalEndpoints(iaddr1);
|
||||
|
||||
// 保存自动连接线路表
|
||||
String[] splt11 = connectLineTabelSet.getText().split("\n");
|
||||
@@ -1182,9 +1285,9 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
|
||||
kck.setLinkConnectionsCount(linkConnectionsCount.getSlider().getValue());
|
||||
|
||||
kck.setDenyLineTableQuery(denyQuery.isSelected());
|
||||
|
||||
kck.setDenyLineTableBroadcast(denyBroadcast.isSelected());
|
||||
kck.setDenyExternalEndpointQuery(denyQuery.isSelected());
|
||||
|
||||
kck.setDenyExternalEndpointBroadcast(denyBroadcast.isSelected());
|
||||
|
||||
PerformanceStrategyItem psi=((PerformanceStrategyItem)comboPerformance.getSelectedItem());
|
||||
if(psi!=null) {
|
||||
@@ -1453,6 +1556,14 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
|
||||
nogui.setSelected( kck.isNogui());
|
||||
|
||||
// 加载设备名称
|
||||
String dname = kck.getDeviceName();
|
||||
deviceNameSet.setText(dname != null ? dname : "");
|
||||
|
||||
// 加载设备描述
|
||||
String ddesc = kck.getDeviceDescription();
|
||||
deviceDescriptionSet.setText(ddesc != null ? ddesc : "");
|
||||
|
||||
// 加载IPv6地址
|
||||
String vaddr = kck.getVirtualAddress();
|
||||
addressFieldSet.setText(vaddr != null ? vaddr : "");
|
||||
@@ -1461,12 +1572,26 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
List<InetAddress> dns = kck.getDNS();
|
||||
dnsAreaSet.setText(listToStr(dns));
|
||||
|
||||
// 加载额外路由
|
||||
List<String> ers = kck.getExtraRoutes();
|
||||
extraRoutesSet.setText(listToStr2(ers));
|
||||
|
||||
// 加载ASN
|
||||
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();
|
||||
@@ -1477,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));
|
||||
|
||||
// 加载自动连接线路表
|
||||
@@ -1506,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);
|
||||
@@ -1654,4 +1779,4 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
KLALBStateGUI3 kcg = new KLALBStateGUI3(new KLALBController());
|
||||
kcg.setVisible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ public class NetworkGraphPanel extends GraphPanel {
|
||||
}
|
||||
|
||||
public InetGraphNode(IPv6Address address, Color color, double x, double y, boolean ismarked) {
|
||||
super(getText(address), color, x, y, ismarked);
|
||||
super(getNodeText(address), color, x, y, ismarked);
|
||||
this.address=address;
|
||||
initign();
|
||||
}
|
||||
@@ -137,18 +137,34 @@ public class NetworkGraphPanel extends GraphPanel {
|
||||
return address2.toCompressedString();
|
||||
}
|
||||
|
||||
public void updateLabel() {
|
||||
setText(getNodeText(address));
|
||||
}
|
||||
|
||||
public IPv6Address getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(IPv6Address address) {
|
||||
this.address = address;
|
||||
super.setText(getText(address));
|
||||
super.setText(getNodeText(address));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private double nsPerPixel=1000L;
|
||||
/**
|
||||
* 节点标签:显示广播得知的设备名称(若有)+IP地址
|
||||
*/
|
||||
private String getNodeText(IPv6Address address) {
|
||||
StringBuilder sb=new StringBuilder();
|
||||
String dname=controller.getIpv6Router().getKlalbRouteProtol().getDeviceName(address);
|
||||
if(dname!=null)
|
||||
sb.append(dname).append('\n');
|
||||
sb.append(InetGraphNode.getText(address));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private double nsPerPixel=5000L;
|
||||
private class InetGraphEdgeGroup extends GraphEdgeGroup{
|
||||
public InetGraphEdgeGroup(GraphNode nodeA, GraphNode nodeB) {
|
||||
super(nodeA, nodeB);
|
||||
@@ -178,6 +194,8 @@ public class NetworkGraphPanel extends GraphPanel {
|
||||
IPv6Address inet6Address = (IPv6Address) iterator.next();
|
||||
if(!addr.containsKey(inet6Address)) {
|
||||
iterator.remove();
|
||||
}else {
|
||||
((InetGraphNode)getNodes().get(inet6Address)).updateLabel();
|
||||
}
|
||||
}
|
||||
//System.out.println("------------------------------------");
|
||||
|
||||
@@ -10,10 +10,12 @@ import java.util.List;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
@@ -65,6 +67,23 @@ public class NodeInformationPanel extends JPanel {
|
||||
panel.setLayout(new BorderLayout(0, 0));
|
||||
tabbedPane.addTab(UIEnv.getRsb().getString("overview"), null, panel, null);
|
||||
|
||||
JTextArea overviewArea = new JTextArea();
|
||||
overviewArea.setEditable(false);
|
||||
overviewArea.setLineWrap(true);
|
||||
overviewArea.setWrapStyleWord(true);
|
||||
overviewArea.setFont(UIEnv.getFont().deriveFont(14.0f));
|
||||
overviewArea.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
||||
String dname=controller.getIpv6Router().getKlalbRouteProtol().getDeviceName(address);
|
||||
String ddesc=null;
|
||||
if(address.equals(controller.getIpv6Router().getLocator().getAddress())&&controller.getConfigItem()!=null) {
|
||||
ddesc=controller.getConfigItem().getDeviceDescription();
|
||||
if(ddesc!=null&&ddesc.isEmpty()) {
|
||||
ddesc=null;
|
||||
}
|
||||
}
|
||||
overviewArea.setText(buildOverviewText(dname, ddesc));
|
||||
panel.add(new JScrollPane(overviewArea), BorderLayout.CENTER);
|
||||
|
||||
JPanel panel_1 = new JPanel();
|
||||
panel_1.setLayout(new BorderLayout(0, 0));
|
||||
panel_1.add(scrollPane);
|
||||
@@ -146,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";
|
||||
}
|
||||
}
|
||||
@@ -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 + "]";
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,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;
|
||||
@@ -545,6 +549,7 @@ public class KLALBRoutingProtocol extends Thread{
|
||||
List<IPv6NetworkLink>links=router.getLinkTabel();
|
||||
Object[] nls=links.toArray();
|
||||
RouterInfo ri=new RouterInfo(System.currentTimeMillis(),router.getLocator(),router.getASN());
|
||||
ri.setDeviceName(router.getDeviceName());
|
||||
for(int i=0;i<nls.length;i++) {
|
||||
IPv6NetworkLink nl=(IPv6NetworkLink) nls[i];
|
||||
if((!nl.isLoopBack())&&nl.isUp()) {
|
||||
@@ -595,6 +600,19 @@ public class KLALBRoutingProtocol extends Thread{
|
||||
public Map<IPv6Address, Long> getAddresses() {
|
||||
return addresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询某地址广播的设备名称(未知返回null)
|
||||
*/
|
||||
public String getDeviceName(IPv6Address address) {
|
||||
RouterInfo ri=netmap.get(address);
|
||||
if(ri!=null) {
|
||||
String dn=ri.getDeviceName();
|
||||
if(dn!=null&&!dn.isEmpty())
|
||||
return dn;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public Map<IPv6Address, List<LinkDirection>> getPaths() {
|
||||
return paths;
|
||||
}
|
||||
|
||||
@@ -29,20 +29,27 @@ public class KLALBRoutingProtocolAPIClient {
|
||||
JsonDataPacket relate = null;
|
||||
//System.out.println(ruid + " " + window.getSendmap());
|
||||
switch (dataobj.getType()) {
|
||||
case KLALBRoutingProtocolJsonData.OPEN_LINES_RESP:
|
||||
case KLALBRoutingProtocolJsonData.NODE_INFO_FULL_RESP:
|
||||
case KLALBRoutingProtocolJsonData.NODE_INFO_TINY_RESP:
|
||||
|
||||
if ((relate = window.ack(ruid)) != null) {
|
||||
List<?> connects = (List<?>) dataobj.getData();
|
||||
List<MultiProtocolSocketAddress> connectsm = new ArrayList<MultiProtocolSocketAddress>(connects.size());
|
||||
for (Object open : connects) {
|
||||
if (open instanceof MultiProtocolSocketAddress) {
|
||||
connectsm.add((MultiProtocolSocketAddress) open);
|
||||
} else {
|
||||
connectsm.add(new MultiProtocolSocketAddress((String) open));
|
||||
List<MultiProtocolSocketAddress> connectsm = new ArrayList<MultiProtocolSocketAddress>(
|
||||
connects == null ? 0 : connects.size());
|
||||
if (connects != null) {
|
||||
for (Object open : connects) {
|
||||
if (open instanceof MultiProtocolSocketAddress) {
|
||||
connectsm.add((MultiProtocolSocketAddress) open);
|
||||
} else {
|
||||
connectsm.add(new MultiProtocolSocketAddress((String) open));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
((Consumer<List<MultiProtocolSocketAddress>>) relate.getUserCallback()).accept(connectsm);
|
||||
// 组装节点信息(线路 + 设备名称 + 设备描述,精简模式下线路与描述为 null)
|
||||
KLALBNodeInformation info = new KLALBNodeInformation(connectsm, dataobj.getDeviceName(),
|
||||
dataobj.getDeviceDescription());
|
||||
((Consumer<KLALBNodeInformation>) relate.getUserCallback()).accept(info);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -56,11 +63,26 @@ public class KLALBRoutingProtocolAPIClient {
|
||||
clr.register(this, releaser);
|
||||
}
|
||||
|
||||
public void requestOpenLines(SocketAddress addr, Consumer<List<MultiProtocolSocketAddress>> callback)
|
||||
/**
|
||||
* 精简查询:仅获取对端设备名称(开销最小,适用于未查看节点详情的场景)。
|
||||
*/
|
||||
public void requestNodeInfoTiny(SocketAddress addr, Consumer<KLALBNodeInformation> callback)
|
||||
throws IOException {
|
||||
sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_INFO_TINY_REQ, addr, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 完整查询:获取对端开放线路 + 设备名称 + 设备描述(查看节点信息时使用)。
|
||||
*/
|
||||
public void requestNodeInfoFull(SocketAddress addr, Consumer<KLALBNodeInformation> callback)
|
||||
throws IOException {
|
||||
sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_INFO_FULL_REQ, addr, callback);
|
||||
}
|
||||
|
||||
private void sendNodeInfoRequest(String type, SocketAddress addr, Consumer<KLALBNodeInformation> callback)
|
||||
throws IOException {
|
||||
UUID suid = UUID.randomUUID();
|
||||
KLALBRoutingProtocolJsonData json = new KLALBRoutingProtocolJsonData(
|
||||
KLALBRoutingProtocolJsonData.OPEN_LINES_REQ, suid, null);
|
||||
KLALBRoutingProtocolJsonData json = new KLALBRoutingProtocolJsonData(type, suid, null);
|
||||
JsonDataPacket packet = new JsonDataPacket(json);
|
||||
packet.setUserCallback(callback);
|
||||
window.put(suid, packet);
|
||||
|
||||
@@ -21,11 +21,21 @@ public class KLALBRoutingProtocolAPIServer {
|
||||
KLALBRoutingProtocolJsonData dataobj= data.getDecodedData();
|
||||
InetSocketAddress addrs=(InetSocketAddress) addr;
|
||||
switch(dataobj.getType()){
|
||||
case KLALBRoutingProtocolJsonData.OPEN_LINES_REQ:
|
||||
if(controller.getConfigItem()==null||(!controller.getConfigItem().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 + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ public class RouterInfo implements Serializable{
|
||||
return Objects.equals(locator, other.locator) && Objects.equals(neighborAddresses, other.neighborAddresses);
|
||||
}
|
||||
private List<NeighborInfo> neighborAddresses=new ArrayList<>();
|
||||
private String deviceName=""; // 设备名称(广播时携带,描述不广播)
|
||||
private long createTime;
|
||||
|
||||
|
||||
@@ -90,6 +91,14 @@ public class RouterInfo implements Serializable{
|
||||
return neighborAddresses;
|
||||
}
|
||||
|
||||
public String getDeviceName() {
|
||||
return deviceName;
|
||||
}
|
||||
|
||||
public void setDeviceName(String deviceName) {
|
||||
this.deviceName = deviceName;
|
||||
}
|
||||
|
||||
public RouterInfo( long createTime,IPv6AddressGroup locator,long asn) {
|
||||
this.createTime = createTime;
|
||||
this.locator=locator;
|
||||
@@ -106,6 +115,7 @@ public class RouterInfo implements Serializable{
|
||||
for( NeighborInfo neighborInfo :neighborAddresses) {
|
||||
neighborInfo.writeToStream(out);
|
||||
}
|
||||
out.writeUTF(deviceName==null?"":deviceName);
|
||||
}
|
||||
|
||||
protected void writeToChannel(WritableByteChannel dto) throws IOException {
|
||||
@@ -126,6 +136,7 @@ public class RouterInfo implements Serializable{
|
||||
neighborAddresses.add(nif);
|
||||
|
||||
}
|
||||
deviceName=in.readUTF();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -294,6 +294,15 @@ public class SRv6Router {
|
||||
|
||||
private IPv6AddressGroup locator; // SRv6定位器
|
||||
private long asn = new SecureRandom().nextLong(1, Long.MAX_VALUE); // 自治系统号
|
||||
private volatile String deviceName; // 设备名称(随路由信息广播)
|
||||
|
||||
public String getDeviceName() {
|
||||
return deviceName;
|
||||
}
|
||||
|
||||
public void setDeviceName(String deviceName) {
|
||||
this.deviceName = deviceName;
|
||||
}
|
||||
|
||||
public IPv6AddressGroup getLocator() {
|
||||
return locator;
|
||||
|
||||
Reference in New Issue
Block a user