Files
KLALB/src/org/kne/cloud/network/srv6/KLALBRoutingProtocol.java
T
2026-10-29 13:40:01 +08:00

754 lines
23 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.ByteBuffer;
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.io.KNEChannels;
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<>();
public Map<IPv6Address, RouterInfo> getNetmap() {
return netmap;
}
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 {
KLALBRoutingProtocolPacket kp=receivePacket();
switch(kp.getType()) {
case KLALBRoutingProtocolPacket.RINFO_REQ:
RouterInfo rifr=netmap.get(router.getLocator().getAddress());
if(debug)
System.out.println("get Request from:"+kp.getFrom());
if(rifr!=null) {
writePacket( new RouterInfoPacket( rifr,false,-1), kp.getFrom());
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( kp.getFrom(), rifp);
}
}else {
if(debug)
System.out.println("dispose RouterInfo");
}
computeShortestPathIfUpdated();
break;
case KLALBRoutingProtocolPacket.JSON_DATA:
JsonDataPacket data=(JsonDataPacket) kp;
receivers.forEach((v)->{v.accept(kp.getFrom(),data);});
break;
default:
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (SocketException e2) {
e2.printStackTrace();
}finally {
if(ds!=null) {
ds.close();
}
}
}
private static final ThreadLocal<ByteBuffer> LSDB_RX_BUFFER =
ThreadLocal.withInitial(() -> ByteBuffer.allocate(65535));
private KLALBRoutingProtocolPacket receivePacket() throws IOException {
// 2. 获取并重置缓冲区
ByteBuffer bbf = LSDB_RX_BUFFER.get();
bbf.clear();
// 3. 构造 DatagramPacket(注意这里要传底层 array 和 capacity)
DatagramPacket dgp = new DatagramPacket(bbf.array(), bbf.capacity());
// 4. 阻塞接收
ds.receive(dgp);
// 5. 限制缓冲区范围到实际收到的长度
bbf.limit(dgp.getLength());
bbf.position(0);
// 6. 直接用 KNEChannels 包装 ByteBuffer,避免 ByteArrayInputStream
KLALBRoutingProtocolPacket kp = KLALBRoutingProtocolPacket.readKLALBPacketFromChannel(
KNEChannels.newReadableChannel(bbf)
);
if(kp!=null)
kp.setFrom(dgp.getSocketAddress());
return kp;
}
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);
}
// 1. 在类成员变量处定义一个 64KB 的 ThreadLocal 缓冲区
// 注意:这里依然使用 65535,因为你说 LSDB 同步可能需要这么大
private static final ThreadLocal<ByteBuffer> LSDB_TX_BUFFER =
ThreadLocal.withInitial(() -> ByteBuffer.allocate(65535));
private void writePacket(KLALBRoutingProtocolPacket pkt, SocketAddress dest) throws IOException {
// 2. 从 ThreadLocal 获取,并清空状态
ByteBuffer bbf = LSDB_TX_BUFFER.get();
bbf.clear();
// 3. 使用 KNEChannels 绑定到 ByteBuffer,直接写入
// 这避免了 ByteArrayOutputStream 内部的动态扩容和拷贝
KLALBRoutingProtocolPacket.writeKLALBPacketToChannel(KNEChannels.newWritableChannel(bbf), pkt);
// 4. 翻转,准备读取数据
bbf.flip();
// 5. 直接拿底层的 byte[] 和真实的 limit 发送
DatagramPacket dgpx = new DatagramPacket(bbf.array(), bbf.limit());
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);
}
}