Files
KLALB/src/org/kne/cloud/network/srv6/KLALBRoutingProtocol.java
T
SerinaNya 455139dfe9 feat: 新增设备信息与额外路由设置,支持设备名广播及TUN显式禁用
- 设置页新增设备名称(单行)、设备描述(多行)、额外路由(CIDR列表)设置项
- RouterInfo 广播携带 deviceName,并在拓扑图节点与节点概览中展示;deviceDescription 保持本地概览显示
- 拓扑图节点标签支持多行文本绘制
- 节点概览面板新增 IP 地址及设备详情展示
- 支持将 TUNName 设为 null/"null"/空字符串时完全跳过 Wintun 虚拟网卡创建
- .classpath 容器改为标准 JavaSE-25 执行环境以同时兼容 JDK 25 与 26
- 新增 AGENTS.md 与 VS Code 调试及运行配置

BREAKING CHANGE: RouterInfo 在序列化末尾追加了 deviceName 字段(writeUTF),新旧版本节点混连时路由协议解析异常,需同步升级
2026-08-21 23:34:41 +08:00

714 lines
22 KiB
Java

package org.kne.cloud.network.srv6;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.BindException;
import java.net.DatagramPacket;
import java.net.Inet6Address;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.Channels;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiConsumer;
import org.kne.cloud.network.ipv6.IPv6Address;
import org.kne.cloud.network.ipv6.IPv6NetworkLink;
import org.kne.cloud.network.ipv6.IPv6Packet.IPv6SegmentRoutingHeader;
import org.kne.cloud.network.ipv6.IPv6AddressGroup;
import org.kne.cloud.network.ipv6.Neighbor;
import org.kne.cloud.network.klalb.KLALBVirtualRawSocket;
import org.kne.cloud.network.monitor.DelayMonitorData;
import org.kne.cloud.network.monitor.MonitorData;
import org.kne.cloud.network.monitor.SpeedAndTrafficAndDelayMonitorDataImpl;
import org.kne.cloud.network.monitor.SpeedAndTrafficMonitorData;
import org.kne.cloud.network.monitor.SpeedAndTrafficMonitorDataImpl;
import org.kne.membandboost.MembandBoost;
public class KLALBRoutingProtocol extends Thread{
private static final boolean debug = false;
private RouterInfo selfRouterInfo;
private Map<IPv6Address, RouterInfo> netmap=new ConcurrentHashMap<>();
private volatile Map<IPv6Address,Long>addresses;
private volatile Map<IPv6Address, List<LinkDirection>> paths;
public static final int DEFAULT_PORT=1001;
public static final int DEFAULT_PROTOCOL_NUMBER=252;
public static final long HOTSOPT_TIMEOUT = 1000000000;
public static final int HOTSOPT_REPORT_INTERVAL = 1000000;
private SRv6Router router;
public SRv6Router getRouter() {
return router;
}
public KLALBRoutingProtocol(SRv6Router router) {
this.router=router;
}
//private ReentrantLock sendlock=new ReentrantLock();
private long floodTimer=System.nanoTime();
private long floodTimer2=System.nanoTime();
private long requestTimer=System.nanoTime();
private KLALBVirtualRawSocket ds = null;
private ReentrantLock sendLock=new ReentrantLock();
private Set<BiConsumer<SocketAddress,JsonDataPacket>>receivers=new CopyOnWriteArraySet<BiConsumer<SocketAddress,JsonDataPacket>>();
public void addReceiver(BiConsumer<SocketAddress,JsonDataPacket> rec) {
receivers.add(rec);
}
public void removeReceiver(BiConsumer<SocketAddress,JsonDataPacket> rec) {
receivers.remove(rec);
}
@Override
public void run() {
Thread.currentThread().setName("KLALB路由协议接收线程");
getSelfRouterInfo();
try{
ds=new KLALBVirtualRawSocket(router.getinLoopback(),router.getLocator().getAddress().toInet6Address(), DEFAULT_PROTOCOL_NUMBER);
new Thread(()->{
Thread.currentThread().setName("KLALB路由协议发送线程");
while(true) {
try {
selfRouterInfo = getSelfRouterInfo();
RouterInfo oslf=netmap.put(selfRouterInfo.getLocator().getAddress(), selfRouterInfo);
noticeUpdate();
/*if(oslf==null||(!selfRouterInfo.equals(oslf))) {
long cur=System.nanoTime();
if(cur-floodTimer2>5000000000L) {
//System.out.println("update10");
floodTimer2=cur;
floodPacket(ds2, null, new RouterInfoPacket(selfRouterInfo,true));
}
}else {
long cur=System.nanoTime();
if(cur-floodTimer>60000000000L) {
//System.out.println("update60");
floodTimer=cur;
floodPacket(ds2, null, new RouterInfoPacket(selfRouterInfo,true));
}
}*/
long cur=System.nanoTime();
if(cur-floodTimer>20000000000L) {
if(debug)
System.out.println("update60");
floodTimer=cur;
floodPacket( null, new RouterInfoPacket(selfRouterInfo,true,-1));
}
if(cur-floodTimer2>2000000000L) {
if(debug)
System.out.println("update1");
floodTimer2=cur;
floodPacket(null, new RouterInfoPacket(selfRouterInfo,true,router.getASN()));
}
//Set<Inet6Address> requestSet=new HashSet<>();
for (Iterator<Entry<IPv6Address, RouterInfo>> iterator = netmap.entrySet().iterator(); iterator.hasNext();) {
Entry<IPv6Address, RouterInfo> type = (Entry<IPv6Address, RouterInfo>) iterator.next();
RouterInfo val=type.getValue();
long timeout=30000000000L;
if(val.getAsn()==router.getASN()) {
timeout=2000000000L;
}
if(val.checkTimeOut(timeout)) {
iterator.remove();
noticeUpdate();
}
/*List<NeighborInfo> ads=val.getNeighborAddresses();
for (Iterator<NeighborInfo> iterator2 = ads.iterator(); iterator2.hasNext();) {
Inet6Address address=iterator2.next().getLocator().getAddress();
//System.out.println(address);
if(!netmap.containsKey(address)) {
requestSet.add(address);
}
}*/
}
computeShortestPathIfUpdated();
/*long cur2=System.nanoTime();
if(cur2-requestTimer>1000000000L) {
requestTimer=cur2;
for (Iterator<Inet6Address> iterator = requestSet.iterator(); iterator.hasNext();) {
Inet6Address inet6Address = (Inet6Address) iterator.next();
writePacket(ds2, new RouterInfoRequestPacket( ), new InetSocketAddress(inet6Address, DEFAULT_PORT));
if(debug)
System.out.println("Request:"+inet6Address);
}
}*/
for (Iterator<Entry<IPv6Address, HotspotAddressTimer>> iterator = hotspots.entrySet().iterator(); iterator.hasNext();) {
Entry<IPv6Address, HotspotAddressTimer> type = (Entry<IPv6Address, HotspotAddressTimer>) iterator.next();
//if(type.getValue().checkReportTime()) {
//System.out.println("hotspot address:"+type.getKey());
//writePacket(ds2, new RouterInfoPacket( netmap.get(router.getLocator().getAddress()),false,-1), new InetSocketAddress(type.getKey(), DEFAULT_PORT));
//}
if(type.getValue().checkTimeOut()) {
iterator.remove();
}
}
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}catch (IOException e1) {
// TODO 自动生成的 catch 块
e1.printStackTrace();
}
}
}).start();
while(true) {
try {
byte[]ca=MembandBoost.allocateUninitializedArray(65535);
DatagramPacket dgp=new DatagramPacket(ca, ca.length);
ds.receive(dgp);
//System.out.println(Arrays.toString( Arrays.copyOf( dgp.getData(),dgp.getLength())));
ByteArrayInputStream bi=new ByteArrayInputStream(dgp.getData(),0,dgp.getLength());
KLALBRoutingProtocolPacket kp=KLALBRoutingProtocolPacket.readKLALBPacketFromChannel(Channels.newChannel(bi));
switch(kp.getType()) {
case KLALBRoutingProtocolPacket.RINFO_REQ:
RouterInfo rifr=netmap.get(router.getLocator().getAddress());
if(debug)
System.out.println("get Request from:"+dgp.getSocketAddress());
if(rifr!=null) {
writePacket( new RouterInfoPacket( rifr,false,-1), dgp.getSocketAddress());
if(debug)
System.out.println("response router info:"+rifr);
}else {
if(debug)
System.out.println("router info is null!");
}
break;
case KLALBRoutingProtocolPacket.RINFO:
RouterInfoPacket rifp=(RouterInfoPacket) kp;
RouterInfo rif=rifp.getRinfo();
RouterInfo oldrif=netmap.get(rif.getLocator().getAddress());
if(debug)
System.out.println(rif);
if(oldrif==null||oldrif.getCreateTime()<rif.getCreateTime()) {
if(debug)
System.out.println("update RouterInfo");
netmap.put(rif.getLocator().getAddress(), rif);
noticeUpdate();
if(rifp.isFlood()) {
floodPacket( dgp.getSocketAddress(), rifp);
}
}else {
if(debug)
System.out.println("dispose RouterInfo");
}
computeShortestPathIfUpdated();
break;
case KLALBRoutingProtocolPacket.JSON_DATA:
JsonDataPacket data=(JsonDataPacket) kp;
receivers.forEach((v)->{v.accept(dgp.getSocketAddress(),data);});
break;
default:
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (SocketException e2) {
e2.printStackTrace();
}finally {
if(ds!=null) {
ds.close();
}
}
}
private void floodPacket(SocketAddress except,RouterInfoPacket rifp) throws IOException {
long asn=rifp.getASN();
List<Neighbor>ln= router.getNeighbors();
Set<InetSocketAddress>ist=new HashSet<InetSocketAddress>();
for(Neighbor addresses :ln) {
if(addresses.getLocator()==null)
continue;
InetSocketAddress isa=new InetSocketAddress(addresses.getLocator().getAddress().toInet6Address(),DEFAULT_PORT);
if(!ist.add(isa)) {
continue;
}
if(!isa.equals(except)) {
if(asn==-1) {
writePacket( rifp, isa);
}else{
RouterInfo nif= netmap.get( IPv6Address.valueOf( isa.getAddress()));
if(nif!=null&&nif.getAsn()==asn) {
writePacket( rifp, isa);
}
}
}
}
}
public void sendJsonPacketToAddress(JsonDataPacket pkt,SocketAddress dest) throws IOException {
writePacket(pkt,dest);
}
private void writePacket(KLALBRoutingProtocolPacket pkt,SocketAddress dest) throws IOException {
ByteArrayOutputStream bos=new ByteArrayOutputStream(65535);
KLALBRoutingProtocolPacket.writeKLALBPacketToChannel(Channels.newChannel(bos),pkt);
byte[]to=bos.toByteArray();
DatagramPacket dgpx=new DatagramPacket(to,to.length);
dgpx.setSocketAddress(dest);
sendLock.lock();
try {
ds.send(dgpx);
}catch(BindException e) {
e.printStackTrace();
System.err.println("Cannot assign:"+dest);
}finally {
sendLock.unlock();
}
}
private DijkstraAlgorithm algorithm=new SingleDijkstraAlgorithm();
private ReentrantLock directionLock=new ReentrantLock();
private volatile NetmapDirections directions;
private static class NetmapDirections{
public NetmapDirections(long[] direction, List<IPv6Address> rias,
Map<IPv6Address, Long> direction_airs) {
super();
this.direction = direction;
this.direction_rias = rias;
this.direction_airs = direction_airs;
}
public NetmapDirections(long[] direction, List<Inet6Address> rias, Map<Inet6Address, Long> airs,Object obj) {
this.direction = direction;
this.direction_rias=new ArrayList<IPv6Address>();
rias.forEach((addr)->{
direction_rias.add(new IPv6Address(addr));
});
this.direction_airs=new HashMap<>();
airs.forEach((k,v)->{
direction_airs.put(new IPv6Address(k), v);
});
}
private long[] direction;
private List<IPv6Address> direction_rias;
private Map<IPv6Address, Long> direction_airs;
public long[] getDirection() {
return direction;
}
public List<IPv6Address> getDirection_rias() {
return direction_rias;
}
public Map<IPv6Address, Long> getDirection_airs() {
return direction_airs;
}
}
private volatile boolean updated=false;
public void noticeUpdate() {
updated=true;
}
private void computeShortestPathIfUpdated() {
if(updated) {
computeShortestPath();
updated=false;
}
}
private void computeShortestPath() {
directionLock.lock();
try {
if(netmap.get(router.getLocator().getAddress())==null) {
return;
}
Set<Entry<IPv6Address, RouterInfo>> s=netmap.entrySet();
Map<IPv6Address,Long>airs=new HashMap<>(netmap.size()*2,0.4f);
List<IPv6Address>rias=new ArrayList<>(netmap.size()+1);
Map<IPv6Address,List< LinkDirection>>paths=new HashMap<>();
long number=0;
for(Entry<IPv6Address, RouterInfo> entry:s) {
if(!airs.containsKey(entry.getKey())) {
airs.put(entry.getKey(),number);
rias.add( entry.getKey());
number++;
}
List<NeighborInfo> ni= entry.getValue().getNeighborAddresses();
for(NeighborInfo neighborInfo :ni) {
IPv6Address ias2=neighborInfo.getLocator().getAddress();
if(!airs.containsKey(ias2)) {
airs.put(ias2,number);
rias.add( ias2);
number++;
}
List<LinkDirection>lp1=paths.get(entry.getKey());
if(lp1==null) {
paths.put(entry.getKey(), lp1=new ArrayList<>());
}
lp1.add(new LinkDirection(neighborInfo.getLocal(),neighborInfo.getNeighbor(),entry.getKey(), ias2, neighborInfo.getUploadDelay(),neighborInfo.getUploadDelayMin(),neighborInfo.getUploadSpeed(),neighborInfo.getUploadSpeedMax()));
/*Set<LinkDirection>lp2=paths.get(ias2);
if(lp2==null) {
paths.put(ias2, lp2=new HashSet<>());
}
lp2.add( new LinkDirection(neighborInfo.getNeighbor(),neighborInfo.getLocal(),ias2,entry.getKey() , neighborInfo.getDownloadDelay(),neighborInfo.getDownloadDelayMin(),neighborInfo.getDownloadSpeed(),neighborInfo.getDownloadSpeedMax()));
*/
}
}
long[][]pointers=new long[(int) airs.size()][2];
long heappos=0;
long linknumber=0;
for(Entry<IPv6Address, List<LinkDirection>> entry:paths.entrySet()) {
/*for (Iterator<Entry<Inet6Address, Set<LinkDirection>>> iterator = paths.entrySet().iterator(); iterator.hasNext();) {
Entry<Inet6Address, Set<LinkDirection>> entry = (Entry<Inet6Address, Set<LinkDirection>>) iterator.next();*/
linknumber+=entry.getValue().size();
}
long[][]heap=new long[(int) (linknumber)][2];
for (long i = 0; i < number; i++) {
long heapindex=0;
pointers[(int) i][0]=heappos;
IPv6Address ia=rias.get((int) i);
List<LinkDirection>lks=paths.get(ia);
if(lks!=null)
for(LinkDirection linkPath:lks) {
/*
for (Iterator<LinkDirection> iterator = lks.iterator(); iterator.hasNext();) {
LinkDirection linkPath=iterator.next();*/
heap[(int)(heappos+ heapindex)][0]=airs.get(linkPath.getToLocator());
heap[(int) (heappos+heapindex)][1]=linkPath.getWeight();
//heap[(int) (heappos+heapindex)][2]=linkPath.get
// heap[(int) (heappos+heapindex)][3]=0;
heapindex++;
}
heappos+=heapindex;
pointers[(int) i][1]=heapindex;
}
algorithm.setGraph(pointers,heap,airs.get(router.getLocator().getAddress()));
algorithm.run();
directions=new NetmapDirections(algorithm.getDirection(), rias, airs);
addresses=airs;
this.paths=paths;
}finally {
directionLock.unlock();
}
//System.out.println(Arrays.toString( algorithm.getDirection()));
}
public static class LinkDirection{
private IPv6AddressGroup fromAddress;
private IPv6AddressGroup toAddress;
private IPv6Address fromLocator;
private IPv6Address toLocator;
private long delay;
private long delayMin;
private long speed;
public long getSpeed() {
return speed;
}
private long bandwidth;
public IPv6AddressGroup getFromAddress() {
return fromAddress;
}
public IPv6AddressGroup getToAddress() {
return toAddress;
}
public long getDelay() {
return delay;
}
public long getBandwidth() {
return bandwidth;
}
public long getDelayMin() {
return delayMin;
}
public LinkDirection(IPv6AddressGroup fromAddress, IPv6AddressGroup toAddress, IPv6Address fromLocator,
IPv6Address toLocator, long delay, long delayMin, long speed, long bandwidth) {
super();
this.fromAddress = fromAddress;
this.toAddress = toAddress;
this.fromLocator = fromLocator;
this.toLocator = toLocator;
this.delay = delay;
this.delayMin = delayMin;
this.speed = speed;
this.bandwidth = bandwidth;
}
@Override
public String toString() {
return "LinkDirection [fromAddress=" + fromAddress + ", toAddress=" + toAddress + ", fromLocator="
+ fromLocator + ", toLocator=" + toLocator + ", delay=" + delay + ", delayMin=" + delayMin
+ ", speed=" + speed + ", bandwidth=" + bandwidth + "]";
}
public IPv6Address getFromLocator() {
return fromLocator;
}
@Override
public int hashCode() {
return Objects.hash(fromAddress, toAddress);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
LinkDirection other = (LinkDirection) obj;
return Objects.equals(fromAddress, other.fromAddress) && Objects.equals(toAddress, other.toAddress);
}
public IPv6Address getToLocator() {
return toLocator;
}
public long getWeight() {
return delay;
}
}
private RouterInfo getSelfRouterInfo() {
List<IPv6NetworkLink>links=router.getLinkTabel();
Object[] nls=links.toArray();
RouterInfo ri=new RouterInfo(System.currentTimeMillis(),router.getLocator(),router.getASN());
ri.setDeviceName(router.getDeviceName());
for(int i=0;i<nls.length;i++) {
IPv6NetworkLink nl=(IPv6NetworkLink) nls[i];
if((!nl.isLoopBack())&&nl.isUp()) {
List<Neighbor>neis=nl.getNeighborsInfo();
for (Iterator<Neighbor> iteratorx = neis.iterator(); iteratorx.hasNext();) {
Neighbor addresses = (Neighbor) iteratorx.next();
long updelay=10000000000L;
long downdelay=10000000000L;
long updelayMin=10000000000L;
long downdelayMin=10000000000L;
long uploadspeed=1024L*1024;
long downloadspeed=1024L*1024;
long uploadspeedmax=1024L*1024;
long downloadspeedmax=1024L*1024;
MonitorData md=addresses.getMonitor();
if(md!=null) {
if(md instanceof DelayMonitorData) {
updelay=((DelayMonitorData) md).getOutDelay();
downdelay=((DelayMonitorData) md).getInDelay();
if(md instanceof SpeedAndTrafficAndDelayMonitorDataImpl) {
updelayMin=((SpeedAndTrafficAndDelayMonitorDataImpl) md).getOutDelayMin();
downdelayMin=((SpeedAndTrafficAndDelayMonitorDataImpl) md).getInDelayMin();
}
}
if(md instanceof SpeedAndTrafficMonitorData) {
uploadspeed=((SpeedAndTrafficMonitorData) md).getOutSpeed();
downloadspeed=((SpeedAndTrafficMonitorData) md).getInSpeed();
if(md instanceof SpeedAndTrafficMonitorDataImpl) {
SpeedAndTrafficMonitorDataImpl smd=(SpeedAndTrafficMonitorDataImpl) md;
uploadspeedmax=smd.getOutSpeedMax();
downloadspeedmax=smd.getInSpeedMax();
}
}
}
NeighborInfo ni=new NeighborInfo(nl.getAddressGroups().get(0),addresses.getAddress(),addresses.getLocator(),updelay ,downdelay,updelayMin ,downdelayMin ,uploadspeed ,downloadspeed,uploadspeedmax ,downloadspeedmax);
ni.setContext(addresses);
ri.getNeighborAddresses().add(ni);
}
}
}
//if(debug)
//System.out.println(ri);
return ri;
}
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;
}
public IPv6SegmentRoutingHeader createSegmentList(IPv6Address dest) {
NetmapDirections directionsx=directions;
if(directionsx==null) {
return null;
}
Long dn=directionsx.getDirection_airs().get(dest);
if(dn==null) {
return null;
}
IPv6SegmentRoutingHeader segs=new IPv6SegmentRoutingHeader();
List<IPv6Address>segments=segs.getAddresses();
while(true) {
long idx=directionsx.getDirection()[(int) dn.longValue()];
if(idx==-1) {
return null;
}
if(idx==dn) {
segs. resetSegmentPos();
return segs;
}
segments.add(directionsx.getDirection_rias().get(dn.intValue()));
dn=idx;
}
}
public long getDevicesFound() {
return netmap.size();
}
private static class HotspotAddressTimer{
private long putTime=System.nanoTime();
private long reportTime =System.nanoTime();
public boolean checkTimeOut() {
return System.nanoTime()-putTime>HOTSOPT_TIMEOUT;
}
public boolean checkReportTime() {
long cu = System.nanoTime();
if (cu - reportTime > HOTSOPT_REPORT_INTERVAL) {
reportTime = cu;
return true;
} else {
return false;
}
}
public void refreshTimeout() {
putTime=System.nanoTime();
}
}
private Map<IPv6Address, HotspotAddressTimer> hotspots=new ConcurrentHashMap<>();
public void putHotspotAddress(IPv6Address iPv6Address) {
if(true) {
HotspotAddressTimer hat=hotspots.get(iPv6Address);
if(hat==null) {
hotspots.put(iPv6Address,hat= new HotspotAddressTimer());
}else {
hat.refreshTimeout();
}
if(hat.checkReportTime()) {
//System.out.println("hotspot address:"+sourceAddress);
/* try {
writePacket( new RouterInfoPacket( netmap.get(router.getLocator().getAddress()),false,-1), new InetSocketAddress(iPv6Address.toInet6Address(), DEFAULT_PORT));
} catch (IOException e) {
e.printStackTrace();
}*/
}
}
}
private Map<IPv6Address,Long> toIpBandwidth=new ConcurrentHashMap<IPv6Address,Long>();
public void updateTotalRequestBandwidth(IPv6Address targetaAddress, Long treq) {
Objects.requireNonNull(targetaAddress);
//System.out.println("到"+targetaAddress.getHostAddress()+"请求带宽更新:"+treq);
if(treq<=0) {
if(toIpBandwidth.remove(targetaAddress)!=null) {
noticeUpdate();
}
}else {
Long old=toIpBandwidth.put(targetaAddress, treq);
if(old==null||old.longValue()!=treq.longValue()) {
noticeUpdate();
}
}
}
public IPv6Address getDijkstraPrevNode(IPv6Address text) {
NetmapDirections directionsx=directions;
if(directionsx==null)
return null;
Long l=directionsx.getDirection_airs().get(text);
if(l==null)
return null;
long number= l;
long prev= directionsx.getDirection()[(int)number];
if(prev==-1) {
return null;
}
return directionsx.getDirection_rias().get((int) prev);
}
}