- 设置页新增设备名称(单行)、设备描述(多行)、额外路由(CIDR列表)设置项 - RouterInfo 广播携带 deviceName,并在拓扑图节点与节点概览中展示;deviceDescription 保持本地概览显示 - 拓扑图节点标签支持多行文本绘制 - 节点概览面板新增 IP 地址及设备详情展示 - 支持将 TUNName 设为 null/"null"/空字符串时完全跳过 Wintun 虚拟网卡创建 - .classpath 容器改为标准 JavaSE-25 执行环境以同时兼容 JDK 25 与 26 - 新增 AGENTS.md 与 VS Code 调试及运行配置 BREAKING CHANGE: RouterInfo 在序列化末尾追加了 deviceName 字段(writeUTF),新旧版本节点混连时路由协议解析异常,需同步升级
1004 lines
33 KiB
Java
1004 lines
33 KiB
Java
package org.kne.cloud.network.srv6;
|
||
|
||
import java.io.IOException;
|
||
import java.net.Inet6Address;
|
||
import java.net.UnknownHostException;
|
||
import java.security.SecureRandom;
|
||
import java.util.ArrayList;
|
||
import java.util.Collections;
|
||
import java.util.HashMap;
|
||
import java.util.HashSet;
|
||
import java.util.Iterator;
|
||
import java.util.List;
|
||
import java.util.Map;
|
||
import java.util.Set;
|
||
import java.util.Timer;
|
||
import java.util.TimerTask;
|
||
import java.util.UUID;
|
||
import java.util.concurrent.CopyOnWriteArrayList;
|
||
import java.util.concurrent.CopyOnWriteArraySet;
|
||
import java.util.concurrent.locks.Condition;
|
||
import java.util.concurrent.locks.ReentrantLock;
|
||
import java.util.function.BiConsumer;
|
||
import java.util.function.Consumer;
|
||
import java.util.function.Supplier;
|
||
|
||
import org.jctools.counters.FixedSizeStripedLongCounter;
|
||
import org.kne.cloud.clock.HighAccuracyClock;
|
||
import org.kne.cloud.network.congestion.MessageBatcher;
|
||
import org.kne.cloud.network.ipv6.ControlledIPv6NetworkLink;
|
||
import org.kne.cloud.network.ipv6.ICMPv6PostcardPacket;
|
||
import org.kne.cloud.network.ipv6.ICMPv6TimeExceededPacket;
|
||
import org.kne.cloud.network.ipv6.IPv6Address;
|
||
import org.kne.cloud.network.ipv6.IPv6HopByHopTLV;
|
||
import org.kne.cloud.network.ipv6.IPv6LinkStateListener;
|
||
import org.kne.cloud.network.ipv6.IPv6NetworkLink;
|
||
import org.kne.cloud.network.ipv6.IPv6Packet;
|
||
import org.kne.cloud.network.ipv6.IPv6Packet.IPv6HopByHopHeader;
|
||
import org.kne.cloud.network.ipv6.IPv6Packet.IPv6SegmentRoutingHeader;
|
||
import org.kne.cloud.network.ipv6.IPv6AddressGroup;
|
||
import org.kne.cloud.network.ipv6.KLALBOAMHopByHopTLV;
|
||
import org.kne.cloud.network.ipv6.KLALBPassportHopByHopTLV;
|
||
import org.kne.cloud.network.ipv6.LoopbackIPv6NetworkLink;
|
||
import org.kne.cloud.network.ipv6.Neighbor;
|
||
import org.kne.cloud.network.ipv6.PadNHopByHopTLV;
|
||
import org.kne.cloud.network.ipv6.PostcardEntry;
|
||
import org.kne.cloud.network.ipv6.RouteItem;
|
||
import org.kne.cloud.network.klalb.KLALBRemoteLink;
|
||
import org.kne.cloud.network.klalb.KLALBUtils;
|
||
import org.kne.cloud.network.klalb.PerformanceStrategy;
|
||
import org.kne.cloud.network.klalb.ui.PerformanceStrategyItem;
|
||
import org.kne.concurrent.HighPerformanceExecutor2;
|
||
import org.kne.concurrent.TimeoutConcurrentHashMap;
|
||
import org.pcap4j.packet.IllegalRawDataException;
|
||
|
||
import com.google.gson.internal.Pair;
|
||
|
||
// SRv6路由器主类,实现IPv6段路由功能
|
||
public class SRv6Router {
|
||
private static final boolean debug = false; // 调试模式开关
|
||
|
||
// public static final int MTU = 16384;
|
||
public static final int MTU = 16384; // 最大传输单元
|
||
public static final int MAX_REROUTE_COUNT = 2;// 1 // 最大重路由次数
|
||
|
||
private static final int IPv6_BITS = 128; // IPv6地址位数
|
||
|
||
private PerformanceStrategy performanceStrategy=PerformanceStrategy.MULTI_FILL;
|
||
|
||
/*private TimeoutConcurrentHashMap<FlowSession, PacketIDGenerator> flowIDmap = new TimeoutConcurrentHashMap<FlowSession, PacketIDGenerator>(
|
||
60000000000L);
|
||
*/
|
||
private Set<UUID> unduplicateSet = Collections
|
||
.newSetFromMap(new TimeoutConcurrentHashMap<UUID, Boolean>(3000000000L, 1024, 0.5f));
|
||
|
||
private HighAccuracyClock clock;
|
||
|
||
// 获取环回地址 ::1
|
||
private static Inet6Address getLoopbackAddress() {
|
||
try {
|
||
return (Inet6Address) Inet6Address.getByName("::1");
|
||
} catch (UnknownHostException e) {
|
||
e.printStackTrace();
|
||
}
|
||
return null;
|
||
}
|
||
|
||
// 获取任意本地地址 ::0
|
||
private static Inet6Address getAnyLocalAddress() {
|
||
try {
|
||
return (Inet6Address) Inet6Address.getByName("::0");
|
||
} catch (UnknownHostException e) {
|
||
e.printStackTrace();
|
||
}
|
||
return null;
|
||
}
|
||
|
||
private List<RouteItem> routeTabel = new ArrayList<>(); // 路由表
|
||
|
||
private List<IPv6NetworkLink> linkTabel = new CopyOnWriteArrayList<>(); // 网络链路表,线程安全
|
||
|
||
public List<IPv6NetworkLink> getLinkTabel() {
|
||
return linkTabel;
|
||
}
|
||
|
||
// 环回网络链路
|
||
private final LoopbackIPv6NetworkLink inLoopBack;
|
||
|
||
// 获取路由表
|
||
public List<RouteItem> getRouteTabel() {
|
||
return routeTabel;
|
||
}
|
||
|
||
// 获取当前路由表
|
||
public List<RouteItem> getCurrentRouteTabel() {
|
||
return routeTable0;
|
||
}
|
||
|
||
// 默认重路由处理器
|
||
private Consumer<IPv6Packet> defaultReroute = new Consumer<IPv6Packet>() {
|
||
|
||
@Override
|
||
public void accept(IPv6Packet t) {
|
||
// HighPerformanceExecutor.defaultExecutor.execute(() -> {
|
||
routePacket(null, t, true); // 执行重路由
|
||
// });
|
||
}
|
||
};
|
||
|
||
private boolean unduplicate(IPv6Packet pack) {
|
||
IPv6HopByHopHeader hbh = pack.getHopByHopHeader();
|
||
KLALBOAMHopByHopTLV oamtlv = null;
|
||
if (hbh != null) {
|
||
List<IPv6HopByHopTLV> tlvs = hbh.getTlvs();
|
||
|
||
for (IPv6HopByHopTLV tlv : tlvs) {
|
||
if (tlv instanceof KLALBOAMHopByHopTLV) {
|
||
oamtlv = (KLALBOAMHopByHopTLV) tlv;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (oamtlv == null)
|
||
return true;
|
||
boolean bool = unduplicateSet.add(oamtlv.getUUID());
|
||
// System.out.println(bool);
|
||
return bool;
|
||
}
|
||
// 默认接收处理器
|
||
/*
|
||
* private BiConsumer<IPv6NetworkLink,IPv6Packet> defaultReceive = new
|
||
* BiConsumer<IPv6NetworkLink,IPv6Packet>() {
|
||
*
|
||
* @Override public void accept(IPv6NetworkLink link,IPv6Packet t) { //
|
||
* HighPerformanceExecutor.defaultExecutor.execute(() -> { if(unduplicate(t)) {
|
||
* routePacket(t); // 执行路由 t.putTimePassport("routed"); // 标记路由时间点 } // }); } };
|
||
*/
|
||
|
||
// SRH接收处理器
|
||
private BiConsumer<IPv6NetworkLink, Supplier<IPv6Packet>> srhReceive = new BiConsumer<IPv6NetworkLink, Supplier<IPv6Packet>>() {
|
||
|
||
@Override
|
||
public void accept(IPv6NetworkLink link, Supplier<IPv6Packet> pack) {
|
||
enqueuePacketReceiveTask(link, pack);
|
||
}
|
||
|
||
};
|
||
|
||
public void setPerformanceStrategy(PerformanceStrategy performanceStrategy) {
|
||
this.performanceStrategy=performanceStrategy;
|
||
}
|
||
|
||
public PerformanceStrategy getPerformanceStrategy(){
|
||
return performanceStrategy;
|
||
}
|
||
|
||
// 链路状态监听器实现
|
||
private class LinkListener implements IPv6LinkStateListener {
|
||
|
||
@Override
|
||
public void onOnlineStateUpdate(IPv6NetworkLink link) {
|
||
srListeners.forEach((v) -> {
|
||
v.onLinkChanged(SRv6Router.this, link);
|
||
}); // 通知所有监听器链路状态变化
|
||
}
|
||
|
||
@Override
|
||
public void onAddressUpdate(IPv6NetworkLink link) {
|
||
srListeners.forEach((v) -> {
|
||
v.onLinkChanged(SRv6Router.this, link);
|
||
}); // 通知所有监听器地址更新
|
||
}
|
||
|
||
@Override
|
||
public void onLocatorUpdate(IPv6NetworkLink link) {
|
||
srListeners.forEach((v) -> {
|
||
v.onLinkChanged(SRv6Router.this, link);
|
||
}); // 通知所有监听器定位器更新
|
||
}
|
||
|
||
}
|
||
|
||
private IPv6LinkStateListener listener = new LinkListener(); // 链路状态监听器实例
|
||
|
||
private Set<SRv6RouterListener> srListeners = new CopyOnWriteArraySet<SRv6RouterListener>(); // SRv6路由器监听器集合
|
||
|
||
// 添加SRv6路由器监听器
|
||
public void addSRv6RouterListener(SRv6RouterListener listener) {
|
||
srListeners.add(listener);
|
||
}
|
||
|
||
// 移除SRv6路由器监听器
|
||
public void removeSRv6RouterListener(SRv6RouterListener listener) {
|
||
srListeners.remove(listener);
|
||
}
|
||
public void sortRouteTabel() {
|
||
|
||
// 构建前缀长度索引的路由表
|
||
List<Map<IPv6Address, List<RouteItem>>> routeTabel1x = new ArrayList<Map<IPv6Address, List<RouteItem>>>(
|
||
IPv6_BITS + 1);
|
||
for (int i = 0; i < IPv6_BITS + 1; i++) {
|
||
routeTabel1x.add(new HashMap<IPv6Address, List<RouteItem>>(1024, 0.5f));
|
||
}
|
||
List<RouteItem> routeTabel0x=routeTable0;
|
||
// 按前缀长度组织路由表
|
||
for (RouteItem item : routeTabel0x) {
|
||
IPv6AddressGroup iag = item.getDestination();
|
||
Map<IPv6Address, List<RouteItem>> map = routeTabel1x.get(iag.getPrefixLength());
|
||
List<RouteItem> newlist = new ArrayList<RouteItem>();
|
||
List<RouteItem> oldlist = map.putIfAbsent(iag.getAddress(), newlist);
|
||
if (oldlist == null) {
|
||
oldlist = newlist;
|
||
}
|
||
oldlist.add(item);
|
||
}
|
||
for(Map<IPv6Address, List<RouteItem>> map:routeTabel1x) {
|
||
for(List<RouteItem>v:map.values()) {
|
||
Collections.sort(v);
|
||
}
|
||
}
|
||
// System.out.println(routeTabel1x);
|
||
routeTabel1 = routeTabel1x; // 更新索引路由表
|
||
|
||
|
||
}
|
||
// 更新路由表
|
||
public void updateRouteTabel() {
|
||
List<RouteItem> routeTabel0x = new ArrayList<>();
|
||
|
||
// 遍历所有网络链路,收集路由信息
|
||
for (Iterator<IPv6NetworkLink> iterator = linkTabel.iterator(); iterator.hasNext();) {
|
||
IPv6NetworkLink nlink = (IPv6NetworkLink) iterator.next();
|
||
|
||
List<RouteItem> routes = nlink.getRouteItems(); // 获取链路的路由项
|
||
routeTabel0x.addAll(routes); // 添加到路由表
|
||
|
||
nlink.setSRv6Router(this);
|
||
nlink.addIPv6LinkStateListener(listener); // 添加链路状态监听器
|
||
nlink.setReceiveConsumer(srhReceive); // 设置接收处理器
|
||
if (nlink instanceof ControlledIPv6NetworkLink) {
|
||
((ControlledIPv6NetworkLink) nlink).setRerouteConsumer(defaultReroute); // 设置重路由处理器
|
||
((ControlledIPv6NetworkLink) nlink).setCongressCondition(congressLock, congressCondition); // 设置拥塞条件
|
||
}
|
||
}
|
||
|
||
// 预处理和排序路由项
|
||
for (Iterator<RouteItem> iterator = routeTabel0x.iterator(); iterator.hasNext();) {
|
||
RouteItem routeItem = (RouteItem) iterator.next();
|
||
routeItem.updateCost(); // 预排序
|
||
}
|
||
Collections.sort(routeTabel0x); // 排序路由表
|
||
|
||
// 构建前缀长度索引的路由表
|
||
ArrayList<Map<IPv6Address, List<RouteItem>>> routeTabel1x = new ArrayList<Map<IPv6Address, List<RouteItem>>>(
|
||
IPv6_BITS + 1);
|
||
for (int i = 0; i < IPv6_BITS + 1; i++) {
|
||
routeTabel1x.add(new HashMap<IPv6Address, List<RouteItem>>(1024, 0.5f));
|
||
}
|
||
|
||
// 按前缀长度组织路由表
|
||
for (RouteItem item : routeTabel0x) {
|
||
IPv6AddressGroup iag = item.getDestination();
|
||
Map<IPv6Address, List<RouteItem>> map = routeTabel1x.get(iag.getPrefixLength());
|
||
List<RouteItem> newlist = new ArrayList<RouteItem>();
|
||
List<RouteItem> oldlist = map.putIfAbsent(iag.getAddress(), newlist);
|
||
if (oldlist == null) {
|
||
oldlist = newlist;
|
||
}
|
||
oldlist.add(item);
|
||
}
|
||
// System.out.println(routeTabel1x);
|
||
routeTable0 = routeTabel0x; // 更新平面路由表
|
||
routeTabel1 = routeTabel1x; // 更新索引路由表
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
public long getASN() {
|
||
return asn;
|
||
}
|
||
|
||
public void setASN(long asn) {
|
||
this.asn = asn;
|
||
}
|
||
|
||
// 设置定位器并更新路由表
|
||
public void setLocator(IPv6AddressGroup locator) {
|
||
this.locator = locator;
|
||
updateRouteTabel();
|
||
}
|
||
|
||
// 插入SRH并路由数据包
|
||
public void insertSRHandRoutePacket(IPv6NetworkLink link, IPv6Packet ipp) {
|
||
insertHopByHopHeader(ipp);
|
||
insertSRHeader(ipp); // 插入段路由头
|
||
routePacket(link, ipp); // 路由数据包
|
||
}
|
||
|
||
|
||
private static final PadNHopByHopTLV pad4 = new PadNHopByHopTLV(4);
|
||
|
||
// 插入逐跳路由头
|
||
public boolean insertHopByHopHeader(IPv6Packet ipp) {
|
||
if (ipp.getHopByHopHeader() != null) {
|
||
return false;
|
||
}
|
||
IPv6HopByHopHeader hbh = new IPv6HopByHopHeader();
|
||
hbh.getTlvs().add(pad4);
|
||
UUID pid = KLALBUtils.createGlobalUUID();
|
||
hbh.getTlvs().add(new KLALBOAMHopByHopTLV(0, ipp.getHopLimit(), true, false, true, pid,
|
||
clock.getCurrentTimeNTP64().longValue()));
|
||
ipp.getHeaders().add(0, hbh); // 添加Hop头
|
||
ipp.setHopByHopHeader(hbh);
|
||
return true;
|
||
}
|
||
|
||
// 插入段路由头
|
||
public boolean insertSRHeader(IPv6Packet ipp) {
|
||
// 如果已有SRH,返回false
|
||
if (ipp.getSegmentRoutingHeader() != null) {
|
||
return false;
|
||
}
|
||
// 如果KLALB路由协议可用,创建段列表
|
||
if (klalbRouteProtol != null) {
|
||
IPv6SegmentRoutingHeader segs = klalbRouteProtol.createSegmentList(ipp.getDestinationAddress());
|
||
// System.out.println(segs);
|
||
if (segs != null && (!segs.getAddresses().isEmpty())) {
|
||
|
||
// 创建SRH头
|
||
|
||
ipp.getHeaders().add(segs); // 添加SRH头
|
||
ipp.setRoutingHeader(segs);
|
||
ipp.setDestinationAddress(segs.getAddresses().get(segs.getAddresses().size() - 1)); // 更新目的地址为最后一个段
|
||
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
private volatile List<RouteItem> routeTable0 = new ArrayList<>(); // 当前路由表
|
||
|
||
// 前缀长度索引的路由表
|
||
private volatile List<Map<IPv6Address, List<RouteItem>>> routeTabel1 = new ArrayList<Map<IPv6Address, List<RouteItem>>>(
|
||
IPv6_BITS + 1);
|
||
{
|
||
// 初始化索引路由表
|
||
for (int i = 0; i < IPv6_BITS + 1; i++) {
|
||
routeTabel1.add(new HashMap<IPv6Address, List<RouteItem>>(1024, 0.5f));
|
||
}
|
||
}
|
||
|
||
private void routePacket(IPv6Packet iPv6Packet) {
|
||
routePacket(null, iPv6Packet, false);
|
||
}
|
||
|
||
// 路由数据包(默认非重路由)
|
||
private void routePacket(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet) {
|
||
routePacket(linkfrom, iPv6Packet, false);
|
||
}
|
||
|
||
// 路由数据包主方法
|
||
private void routePacket(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, boolean reroute) {
|
||
StringBuilder dbg = null;
|
||
if (debug) {
|
||
dbg = new StringBuilder(); // 调试信息
|
||
}
|
||
try {
|
||
int hop = iPv6Packet.getHopLimit(); // 获取跳数限制
|
||
if (hop > 0) { // 检查TTL值,只有大于0才会被转发
|
||
// 搜索匹配的路由表项
|
||
IPv6Address dest=iPv6Packet.getDestinationAddress();
|
||
List<RouteItem> searchResult = getTabelByAddress(dest);
|
||
if (searchResult!=null&&(!searchResult.isEmpty())) {
|
||
// 匹配到路由表,执行负载均衡路由
|
||
routingLoadBalance(linkfrom, iPv6Packet, reroute, dbg, searchResult);
|
||
return;
|
||
}
|
||
|
||
if (debug)
|
||
dbg.append("noroute\n");
|
||
|
||
// 未找到路由,尝试FRR(快速重路由)保护
|
||
IPv6SegmentRoutingHeader srh = iPv6Packet.getSegmentRoutingHeader();
|
||
if (srh != null) {
|
||
System.out.println("原SRH:" + srh);
|
||
int segmentsLeft = srh.getSegmentsLeft();
|
||
if (segmentsLeft > 0) {
|
||
srh.getAddresses().remove(segmentsLeft); // 移除当前故障段
|
||
segmentsLeft--;
|
||
}
|
||
// 创建修复段列表
|
||
IPv6SegmentRoutingHeader repairSegments = klalbRouteProtol
|
||
.createSegmentList(srh.getAddresses().get(segmentsLeft));
|
||
if (repairSegments == null ||repairSegments.getAddresses().isEmpty()) {
|
||
System.out.println("FRR保护失败");
|
||
return;
|
||
}
|
||
repairSegments.getAddresses().remove(0); // 移除第一个段(当前节点)
|
||
srh.getAddresses().addAll(segmentsLeft + 1, repairSegments.getAddresses()); // 添加修复段
|
||
segmentsLeft += repairSegments.getAddresses().size(); // 更新剩余段数
|
||
srh.setSegmentsLeft(segmentsLeft);
|
||
iPv6Packet.setDestinationAddress(srh.getAddresses().get(segmentsLeft)); // 更新目的地址
|
||
} else {
|
||
return;
|
||
}
|
||
System.out.println("FRR修复SRH:" + srh);
|
||
// 重新搜索路由表
|
||
List<RouteItem> searchResult2 = getTabelByAddress(dest);
|
||
if (searchResult2!=null&&(!searchResult2.isEmpty())) {
|
||
routingLoadBalance(linkfrom, iPv6Packet, reroute, dbg, searchResult2);
|
||
return;
|
||
}
|
||
|
||
} else {
|
||
// TTL超时,发送ICMP超时消息
|
||
sendTTLExceedPacket(iPv6Packet);
|
||
}
|
||
} catch (Exception e) {
|
||
e.printStackTrace();
|
||
} finally {
|
||
if (debug) {
|
||
System.out.println(dbg.toString()); // 输出调试信息
|
||
}
|
||
}
|
||
}
|
||
|
||
// 搜索路由表找到匹配的路由项
|
||
/*private List<RouteItem> searchTable(IPv6Packet iPv6Packet, StringBuilder dbg)
|
||
throws IllegalRawDataException, IOException {
|
||
if (debug) {
|
||
dbg.append("----------------------------------------\n");
|
||
dbg.append("packet:" + iPv6Packet.getSourceAddress() + " -> " + iPv6Packet.getDestinationAddress() + "\n");
|
||
}
|
||
|
||
IPv6Address rk6 = iPv6Packet.getDestinationAddress();
|
||
List<RouteItem> table = getTabelByAddress(rk6);
|
||
|
||
RouteItem prevr = null;
|
||
List<RouteItem> routes = new ArrayList<>();
|
||
|
||
if (table == null) {
|
||
return routes; // 无匹配路由
|
||
}
|
||
|
||
// 遍历路由表,筛选可用路由
|
||
for (RouteItem tri : table) {
|
||
|
||
// 检查ECMP(等价多路径)条件
|
||
if (prevr != null) {
|
||
if (!tri.ECMPequals(prevr)) {
|
||
return routes; // 非ECMP路由,返回当前结果
|
||
}
|
||
}
|
||
routes.add(tri); // 添加到可用路由列表
|
||
prevr = tri;
|
||
}
|
||
return routes;
|
||
}*/
|
||
|
||
private List<RouteItem> getTabelByAddress(IPv6Address rk6) {
|
||
List<RouteItem> table = null;
|
||
// 最长前缀匹配搜索
|
||
for (int i = 128; i >= 0; i--) {
|
||
rk6 = rk6.maskWith(IPv6Address.createMask(i)); // 应用掩码
|
||
table = routeTabel1.get(i).get(rk6); // 查找匹配的路由表
|
||
if (table != null) {
|
||
break; // 找到匹配
|
||
}
|
||
}
|
||
return table;
|
||
}
|
||
|
||
private ReentrantLock congressLock = new ReentrantLock(); // 拥塞锁
|
||
private Condition congressCondition = congressLock.newCondition(); // 拥塞条件
|
||
|
||
// 负载均衡路由
|
||
private boolean routingLoadBalance(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, boolean reroute,
|
||
StringBuilder dbg, List<RouteItem> routes) throws IllegalRawDataException, IOException {
|
||
|
||
if(routes==null||routes.isEmpty()) {
|
||
//System.out.println("Route not found:" + iPv6Packet);
|
||
return false;
|
||
}
|
||
|
||
for (double i = 1; i < 100; i += 0.1) {
|
||
if (loadBalance(linkfrom, iPv6Packet, reroute, dbg, routes, i)) {
|
||
return true;
|
||
}
|
||
|
||
iPv6Packet.markCE(); // 标记拥塞
|
||
if (debug) {
|
||
dbg.append("ECN enabled.\n");
|
||
}
|
||
|
||
}
|
||
//System.out.println("Send failed:" + iPv6Packet);
|
||
LosscountAdder.inc(1);
|
||
|
||
if (debug) {
|
||
dbg.append("miss.\n"); // 路由失败
|
||
}
|
||
return false; // 路由失败
|
||
}
|
||
|
||
private boolean loadBalance(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, boolean reroute, StringBuilder dbg,
|
||
List<RouteItem> routes, double cscale)
|
||
throws IllegalRawDataException, IOException {
|
||
// 遍历可用路由进行负载均衡
|
||
for (RouteItem tri : routes) {
|
||
IPv6NetworkLink link = tri.getDestlink();
|
||
if (!link.isUp()) { // 检查链路状态
|
||
if (debug) {
|
||
dbg.append(tri + "\n");
|
||
dbg.append("linkdown.\n");
|
||
}
|
||
continue; // 跳过断开链路
|
||
}
|
||
if (link instanceof ControlledIPv6NetworkLink) {
|
||
if (((ControlledIPv6NetworkLink) link).isCongestion(iPv6Packet, cscale)) { // 检查拥塞
|
||
if (debug) {
|
||
dbg.append(tri + "\n");
|
||
dbg.append("congress.\n");
|
||
}
|
||
continue; // 跳过拥塞链路
|
||
}
|
||
|
||
}
|
||
if (debug) {
|
||
dbg.append(tri + "\n");
|
||
dbg.append("matched.\n");
|
||
}
|
||
// 找到可用链路,处理数据包
|
||
processPacket(linkfrom, iPv6Packet, tri, reroute);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// 处理数据包转发
|
||
private void processPacket(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, RouteItem ri, boolean reroute)
|
||
throws IllegalRawDataException, IOException {
|
||
int hop = iPv6Packet.getHopLimit();
|
||
|
||
if (ri.getDestlink().isLoopBack()) { // 环回链路处理
|
||
IPv6SegmentRoutingHeader srhh = iPv6Packet.getSegmentRoutingHeader();
|
||
if (srhh != null) {
|
||
processSRv6Packet(linkfrom, iPv6Packet, ri, srhh, iPv6Packet.getHopByHopHeader(), reroute); // SRv6特殊处理
|
||
} else {
|
||
sendPacketToRouteItem(iPv6Packet, ri);
|
||
}
|
||
} else { // 普通链路处理
|
||
// 检查重路由计数
|
||
if (iPv6Packet.getRerouteCounter().getAndIncrement() >= MAX_REROUTE_COUNT) {
|
||
return; // 超过最大重路由次数,丢弃
|
||
}
|
||
|
||
if (!reroute) {
|
||
hop--; // 减少TTL(非重路由时)
|
||
}
|
||
|
||
if (hop > 0) { // TTL有效
|
||
iPv6Packet.setHopLimit(hop); // 更新TTL
|
||
sendPacketToRouteItem(iPv6Packet, ri); // 发送数据包
|
||
} else {
|
||
sendTTLExceedPacket(iPv6Packet); // TTL超时
|
||
}
|
||
}
|
||
}
|
||
|
||
private void sendPacketToRouteItem(IPv6Packet iPv6Packet, RouteItem ri) throws IOException {
|
||
if (iPv6Packet.getPayload() instanceof ICMPv6PostcardPacket && ri.getDestlink().isLoopBack()) {
|
||
onPostcardReceive(iPv6Packet);
|
||
} else {
|
||
ri.getDestlink().sendPacket(iPv6Packet, ri.getNexthop());
|
||
}
|
||
}
|
||
|
||
private void onPostcardReceive(IPv6Packet postcards) {
|
||
ICMPv6PostcardPacket postcardsi = (ICMPv6PostcardPacket) postcards.getPayload();
|
||
for (PostcardEntry postcard : postcardsi.getPostcards()) {
|
||
IPv6Address sid = postcard.getSID();
|
||
|
||
List<RouteItem> ris = getTabelByAddress(sid);
|
||
if (ris != null) {
|
||
for (RouteItem ri : ris) {
|
||
if (ri.getNexthop().equals(sid)) {
|
||
if (ri.getDestlink() instanceof KLALBRemoteLink) {
|
||
KLALBRemoteLink rem = (KLALBRemoteLink) ri.getDestlink();
|
||
rem.onPostcardReceive(postcard);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
// 发送ICMP TTL超时消息
|
||
private void sendTTLExceedPacket(IPv6Packet iPv6Packet) throws IOException, IllegalRawDataException {
|
||
enqueuePacketSendTask(() -> {
|
||
// 构建IPv6数据包封装ICMP消息
|
||
IPv6Packet icmpv = new IPv6Packet();
|
||
icmpv.setSourceAddress(locator.getAddress());
|
||
icmpv.setDestinationAddress(iPv6Packet.getSourceAddress());
|
||
icmpv.setTrafficClass(iPv6Packet.getTrafficClass());
|
||
icmpv.setVersion(6);
|
||
icmpv.setFlowLabel(0);
|
||
icmpv.setHopLimit(255); // ICMP消息TTL设为最大值
|
||
ICMPv6TimeExceededPacket ipl = ICMPv6TimeExceededPacket.createHopLimitExceeded(iPv6Packet);
|
||
ipl.setParent(icmpv);
|
||
ipl.updateChecksum();
|
||
icmpv.setPayload(ipl);
|
||
return icmpv;
|
||
});
|
||
|
||
}
|
||
|
||
// 处理SRv6数据包
|
||
private void processSRv6Packet(IPv6NetworkLink linkfrom, IPv6Packet iPv6Packet, RouteItem ri,
|
||
IPv6SegmentRoutingHeader srhh, IPv6HopByHopHeader iPv6HopByHopHeader, boolean reroute)
|
||
throws IllegalRawDataException, IOException {
|
||
|
||
if (srhh.getSegmentsLeft() <= 0) { // 所有段已处理完毕
|
||
if (iPv6HopByHopHeader != null) {
|
||
int sl = srhh.getSegmentsLeft();
|
||
// IPv6Address prevSID
|
||
// =sl+1>=srhh.getAddresses().size()?iPv6Packet.getSourceAddress():srhh.getAddresses().get(sl+1);
|
||
if (linkfrom instanceof KLALBRemoteLink) {
|
||
processKLALBOAM(iPv6Packet, linkfrom.getAddressGroups().get(0).getAddress(),
|
||
((KLALBRemoteLink) linkfrom).getRemoteVaddr().getAddress(), iPv6HopByHopHeader);
|
||
}
|
||
}
|
||
// System.out.println(iPv6HopByHopHeader);
|
||
sendPacketToRouteItem(iPv6Packet, ri);
|
||
} else {
|
||
if (reroute) {
|
||
// 重路由处理
|
||
} else {
|
||
int oldSL = srhh.getSegmentsLeft();
|
||
if (iPv6HopByHopHeader != null) {
|
||
// IPv6Address prevSID
|
||
// =oldSL+1>=srhh.getAddresses().size()?iPv6Packet.getSourceAddress():srhh.getAddresses().get(oldSL+1);
|
||
if (linkfrom instanceof KLALBRemoteLink) {
|
||
processKLALBOAM(iPv6Packet, linkfrom.getAddressGroups().get(0).getAddress(),
|
||
((KLALBRemoteLink) linkfrom).getRemoteVaddr().getAddress(), iPv6HopByHopHeader);
|
||
}
|
||
}
|
||
// 正常SRv6处理:移动到下一个段
|
||
int newSL = oldSL - 1;
|
||
srhh.setSegmentsLeft(newSL); // 更新剩余段数
|
||
iPv6Packet.setDestinationAddress(srhh.getAddresses().get(newSL)); // 更新目的地址
|
||
}
|
||
// 记录热点地址(用于流量工程)
|
||
if (iPv6Packet.getPayload().getProtocolNumber() != KLALBRoutingProtocol.DEFAULT_PROTOCOL_NUMBER)
|
||
klalbRouteProtol.putHotspotAddress(iPv6Packet.getSourceAddress());
|
||
routePacket(linkfrom, iPv6Packet, reroute); // 继续路由
|
||
}
|
||
}
|
||
|
||
private void processKLALBOAM(IPv6Packet iPv6Packet, IPv6Address sourceSID, IPv6Address prevSID,
|
||
IPv6HopByHopHeader iPv6HopByHopHeader) {
|
||
KLALBOAMHopByHopTLV oam = null;
|
||
List<IPv6HopByHopTLV> tlvs = iPv6HopByHopHeader.getTlvs();
|
||
KLALBPassportHopByHopTLV prevEndPassport = null;
|
||
for (IPv6HopByHopTLV tlv : tlvs) {
|
||
if (tlv instanceof KLALBOAMHopByHopTLV) {
|
||
oam = (KLALBOAMHopByHopTLV) tlv;
|
||
} else if (tlv instanceof KLALBPassportHopByHopTLV) {
|
||
prevEndPassport = (KLALBPassportHopByHopTLV) tlv;
|
||
}
|
||
}
|
||
if (oam != null) {
|
||
long timentp = clock.getCurrentTimeNTP64().longValue();
|
||
int packetLength = (int) iPv6Packet.getTotalLength();
|
||
long oamt = oam.getTimestamp();
|
||
long delta = timentp - oamt;
|
||
long prevhop = oamt;
|
||
if (prevEndPassport != null) {
|
||
prevhop += prevEndPassport.getTimestampOffsetLong();
|
||
}
|
||
|
||
if (oam.isRecordPassport()) {
|
||
tlvs.add(new KLALBPassportHopByHopTLV(packetLength, (int) (delta >> 4)));
|
||
}
|
||
if (iPv6Packet.getPayload() instanceof ICMPv6PostcardPacket) {
|
||
} else {
|
||
boolean phop = oam.isPostcardToPreviousHop();
|
||
boolean psrc = oam.isPostcardToSource();
|
||
// System.out.println(iPv6Packet.getPayload());
|
||
if (phop) {
|
||
putPostcard(locator.getAddress(), prevSID, sourceSID, oam.getUUID(), prevhop, timentp - prevhop,
|
||
packetLength);
|
||
}
|
||
if (psrc) {
|
||
putPostcard(locator.getAddress(), iPv6Packet.getSourceAddress(), sourceSID, oam.getUUID(), oamt,
|
||
delta, packetLength);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private TimeoutConcurrentHashMap<Pair<IPv6Address, IPv6Address>, MessageBatcher<PostcardEntry>> postcardBatch = new TimeoutConcurrentHashMap<>(
|
||
1000000000L);
|
||
|
||
private void putPostcard(IPv6Address src, IPv6Address dst, IPv6Address srcSID, UUID uuid, long timeStamp,
|
||
long delta, int packetLength) {
|
||
|
||
PostcardEntry pn = new PostcardEntry(srcSID, uuid, timeStamp, (int) (delta >> 4), packetLength);
|
||
/*Pair<IPv6Address, IPv6Address> addressPair = new Pair<IPv6Address, IPv6Address>(src, dst);
|
||
MessageBatcher<PostcardEntry> batch = postcardBatch.computeIfAbsent(addressPair, (k) -> {
|
||
MessageBatcher<PostcardEntry> mb = new ArrayListMessageBatcher<PostcardEntry>(30, 2000000L);
|
||
mb.setConsumer((list) -> {
|
||
enqueuePacketSendTask(() -> {
|
||
IPv6Packet icmpv = new IPv6Packet();
|
||
icmpv.setSourceAddress(k.first);
|
||
icmpv.setDestinationAddress(k.second);
|
||
icmpv.setTrafficClass(0);
|
||
icmpv.setVersion(6);
|
||
icmpv.setFlowLabel(0);
|
||
icmpv.setHopLimit(255); // ICMP消息TTL设为最大值
|
||
ICMPv6PostcardPacket ipl = new ICMPv6PostcardPacket();
|
||
ipl.getPostcards().addAll(list);
|
||
ipl.setParent(icmpv);
|
||
ipl.updateChecksum();
|
||
icmpv.setPayload(ipl);
|
||
return icmpv;
|
||
});
|
||
});
|
||
return mb;
|
||
});
|
||
postcardBatch.refresh(addressPair);
|
||
batch.putMessage(pn);*/
|
||
enqueuePacketSendTask(() -> {
|
||
IPv6Packet icmpv = new IPv6Packet();
|
||
icmpv.setSourceAddress(src);
|
||
icmpv.setDestinationAddress(dst);
|
||
icmpv.setTrafficClass(0);
|
||
icmpv.setVersion(6);
|
||
icmpv.setFlowLabel(0);
|
||
icmpv.setHopLimit(255); // ICMP消息TTL设为最大值
|
||
ICMPv6PostcardPacket ipl = new ICMPv6PostcardPacket();
|
||
ipl.getPostcards().addAll(List.of(pn));
|
||
ipl.setParent(icmpv);
|
||
ipl.updateChecksum();
|
||
icmpv.setPayload(ipl);
|
||
return icmpv;
|
||
});
|
||
}
|
||
|
||
private static HighPerformanceExecutor2 defaultExecutor = new HighPerformanceExecutor2(
|
||
(int) (Runtime.getRuntime().availableProcessors()), Thread.ofVirtual().name("SRv6 Routing").factory());
|
||
|
||
//private static HighPerformanceExecutor2 defaultExecutor = new HighPerformanceExecutor2(4, Thread.ofVirtual().name("SRv6 Routing").factory());
|
||
|
||
/*public static HighPerformanceExecutor2 getDefaultExecutor() {
|
||
return defaultExecutor;
|
||
}*/
|
||
|
||
// private ArrayBlockingQueue<Supplier<IPv6Packet>> packetQueue=new
|
||
// ArrayBlockingQueue<>(10000);
|
||
public void enqueuePacketSendTask(Supplier<IPv6Packet> sendTask) {
|
||
// packetQueue.add(object);
|
||
switch(performanceStrategy) {
|
||
case SINGLE_CORE:
|
||
runPacketSendTask0(sendTask, false);
|
||
break;
|
||
case MULTI_FILL:
|
||
case MULTI_SCATTER:
|
||
defaultExecutor.executeWithCongestionReport((state) -> {
|
||
runPacketSendTask0(sendTask, state);
|
||
}, 20,performanceStrategy);
|
||
|
||
break;
|
||
}
|
||
}
|
||
public void runPacketSendTask0(Supplier<IPv6Packet> sendTask,boolean ecn) {
|
||
try {
|
||
long start=System.nanoTime();
|
||
IPv6Packet packet=sendTask.get();
|
||
if(ecn) {
|
||
packet.markCE();
|
||
}
|
||
insertSRHandRoutePacket(null, packet); // 插入SRH并路由
|
||
long time=System.nanoTime()-start;
|
||
backplaneCount(time,ecn);
|
||
} catch (Exception e) {
|
||
e.printStackTrace();
|
||
}
|
||
}
|
||
public void runPacketSendTask(Supplier<IPv6Packet> sendTask) {
|
||
runPacketSendTask0(sendTask,false);
|
||
}
|
||
|
||
public void onReceive(IPv6NetworkLink link,IPv6Packet t){
|
||
if (unduplicate(t)) {
|
||
insertSRHandRoutePacket(link, t); // 插入SRH并路由数据包
|
||
}
|
||
}
|
||
|
||
public void enqueuePacketReceiveTask(IPv6NetworkLink link, Supplier<IPv6Packet> pack) {
|
||
defaultExecutor.executeWithCongestionReport((state) -> {
|
||
try {
|
||
long start=System.nanoTime();
|
||
IPv6Packet packet = pack.get();
|
||
if(state) {
|
||
packet.markCE();
|
||
}
|
||
onReceive(link ,packet);
|
||
long time=System.nanoTime()-start;
|
||
backplaneCount(time,state);
|
||
} catch (Exception e) {
|
||
e.printStackTrace();
|
||
}
|
||
},20,performanceStrategy);
|
||
}
|
||
|
||
public void enqueuePacket(IPv6Packet packet) {
|
||
enqueuePacketSendTask(() -> {
|
||
return packet;
|
||
});
|
||
}
|
||
|
||
//private BandwidthSampler sampler=new BandwidthSampler();
|
||
private FixedSizeStripedLongCounter timeAdder=KLALBUtils.createCounter();
|
||
private FixedSizeStripedLongCounter countAdder=KLALBUtils.createCounter();
|
||
private FixedSizeStripedLongCounter ECNcountAdder=KLALBUtils.createCounter();
|
||
private FixedSizeStripedLongCounter LosscountAdder=KLALBUtils.createCounter();
|
||
private void backplaneCount(long time,boolean ECN) {
|
||
timeAdder.inc(time);
|
||
countAdder.inc(1);
|
||
if(ECN) {
|
||
ECNcountAdder.inc(1);
|
||
}
|
||
}
|
||
private volatile long backplaneTime=0;
|
||
private volatile double ECNRate=0;
|
||
private volatile double LossRate=0;
|
||
private volatile long backplanePPS=0;
|
||
private volatile long backplanePPSMax=0;
|
||
private volatile long prevTime=System.nanoTime();
|
||
|
||
private Timer backplaneTimer=new Timer();
|
||
private void updateBackplaneCounter() {
|
||
long curr=System.nanoTime();
|
||
long totalTime=timeAdder.getAndReset();
|
||
long totalCount=countAdder.getAndReset();
|
||
long ECNCount=ECNcountAdder.getAndReset();
|
||
long LossCount=LosscountAdder.getAndReset();
|
||
if(totalCount!=0) {
|
||
backplaneTime=totalTime/totalCount;
|
||
ECNRate=((double)ECNCount)/totalCount;
|
||
LossRate=((double)LossCount)/totalCount;
|
||
}
|
||
backplanePPS=totalCount*1000000000L/(curr-prevTime);
|
||
if(backplanePPSMax<backplanePPS) {
|
||
backplanePPSMax=backplanePPS;
|
||
}else {
|
||
backplanePPSMax=(backplanePPSMax*999+backplanePPS)/1000;
|
||
}
|
||
prevTime=curr;
|
||
}
|
||
|
||
|
||
public long getBackplaneTime() {
|
||
return backplaneTime;
|
||
}
|
||
|
||
public long getBackplanePPS() {
|
||
return backplanePPS;
|
||
}
|
||
|
||
public long getBackplanePPSMax() {
|
||
return backplanePPSMax;
|
||
}
|
||
|
||
public double getECNRate() {
|
||
return ECNRate;
|
||
}
|
||
|
||
public double getLossRate() {
|
||
return LossRate;
|
||
}
|
||
|
||
// 构造函数
|
||
public SRv6Router(IPv6AddressGroup hostAddress, HighAccuracyClock clock) {
|
||
super();
|
||
this.locator = hostAddress;
|
||
this.clock = clock;
|
||
// initWorkerThreads();
|
||
// 创建环回链路
|
||
this.inLoopBack = new LoopbackIPv6NetworkLink(
|
||
List.of(new IPv6AddressGroup(IPv6Address.LOOPBACK, 128), hostAddress), this);
|
||
linkTabel.add(inLoopBack); // 添加到链路表
|
||
backplaneTimer.scheduleAtFixedRate(new TimerTask() {
|
||
|
||
@Override
|
||
public void run() {
|
||
updateBackplaneCounter();
|
||
}
|
||
}, 100, 100);
|
||
}
|
||
|
||
/*
|
||
* private void initWorkerThreads() { int
|
||
* threads=Runtime.getRuntime().availableProcessors()*4; for(int
|
||
* i=0;i<threads;i++) { ThreadTool.makeVThread("SRv6 Router Thread"+i,()->{
|
||
* while(true) { try { Supplier<IPv6Packet>packetSup= packetQueue.take();
|
||
* insertSRHandRoutePacket(packetSup.get()); // 插入SRH并路由 }catch(Exception e) {
|
||
* e.printStackTrace(); } } } ).start(); } }
|
||
*/
|
||
|
||
private KLALBRoutingProtocol klalbRouteProtol = null; // KLALB路由协议实例
|
||
|
||
// 启动KLALB路由协议
|
||
public void runKLALBRouteProtocol() {
|
||
if (klalbRouteProtol != null)
|
||
throw new IllegalStateException("KLALB routing protocol is already running!");
|
||
klalbRouteProtol = new KLALBRoutingProtocol(this);
|
||
klalbRouteProtol.start(); // 启动协议
|
||
}
|
||
|
||
public KLALBRoutingProtocol getKlalbRouteProtol() {
|
||
return klalbRouteProtol;
|
||
}
|
||
|
||
public LoopbackIPv6NetworkLink getinLoopback() {
|
||
return inLoopBack;
|
||
}
|
||
|
||
// 获取邻居信息
|
||
public List<Neighbor> getNeighbors() {
|
||
List<Neighbor> result = new ArrayList<>();
|
||
List<IPv6NetworkLink> links = getLinkTabel();
|
||
Object[] nls = links.toArray();
|
||
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();
|
||
result.add(addresses); // 添加邻居信息
|
||
}
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
// 获取所有SID
|
||
public Set<IPv6AddressGroup> getLocators() {
|
||
Set<IPv6AddressGroup> result = new HashSet<>();
|
||
List<IPv6NetworkLink> links = getLinkTabel();
|
||
Object[] nls = links.toArray();
|
||
for (int i = 0; i < nls.length; i++) {
|
||
IPv6NetworkLink nl = (IPv6NetworkLink) nls[i];
|
||
if (!nl.isLoopBack()) { // 排除环回接口
|
||
List<Neighbor> neis = nl.getNeighborsInfo();
|
||
for (Iterator<Neighbor> iteratorx = neis.iterator(); iteratorx.hasNext();) {
|
||
Neighbor addresses = (Neighbor) iteratorx.next();
|
||
if (addresses.getLocator() != null)
|
||
result.add(addresses.getLocator()); // 添加邻居SID
|
||
}
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
|
||
|
||
|
||
} |