mirror of
https://gitee.com/willfree/mlsr.git
synced 2026-06-07 03:09:27 +08:00
347 lines
11 KiB
Go
347 lines
11 KiB
Go
// Package route
|
|
// @Author: Wang Feng
|
|
// @Description:
|
|
// @Version: 0.1.0
|
|
// @Date: 2022/5/9 17:31
|
|
// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室
|
|
//
|
|
|
|
package route
|
|
|
|
import (
|
|
"errors"
|
|
"math"
|
|
"minlib/component"
|
|
"mlsr/common"
|
|
"mlsr/lsa"
|
|
"mlsr/lsdb"
|
|
"strconv"
|
|
)
|
|
|
|
//
|
|
// LinkStateRoutingTableCalculator
|
|
// @Description: 邻接状态路由表计算器。路由计算的核心部分
|
|
//
|
|
type LinkStateRoutingTableCalculator struct {
|
|
RoutingTableCalculator
|
|
// 私有变量区
|
|
// 表示源路由器想要到达某路由器,其最短路径上经过的上一个路由器。
|
|
m_parent []int
|
|
// 表示源路由器想要到达某路由器,其最短路径的距离
|
|
m_distance []uint64
|
|
}
|
|
|
|
//
|
|
// ToString
|
|
// @Description: 转string
|
|
// @receiver c
|
|
// @return string
|
|
//
|
|
func (c *LinkStateRoutingTableCalculator) ToString() string {
|
|
str := ""
|
|
str += "每个节点最佳路径的上一跳:["
|
|
for i := 0; i < len(c.m_parent); i++ {
|
|
str += "(" + strconv.Itoa(i)
|
|
str += ", "
|
|
str += strconv.Itoa(c.m_parent[i])
|
|
str += ")"
|
|
}
|
|
str += "\n"
|
|
str += "每个节点最佳路径的总开销:["
|
|
for i := 0; i < len(c.m_distance); i++ {
|
|
str += "(" + strconv.Itoa(i)
|
|
str += ", "
|
|
str += strconv.FormatUint(c.m_distance[i], 10)
|
|
str += ")"
|
|
}
|
|
str += "\n"
|
|
return str
|
|
}
|
|
|
|
// 一些在最短路径计算时会用到的常量
|
|
const (
|
|
EMPTY_PARENT = -12345 // 没有父节点 -12345
|
|
INF_DISTANCE = math.MaxUint64 // 无限距离 2147483647
|
|
NO_MAPPING_NUM = -1 // 没有映射的点 -1
|
|
// 公用常量
|
|
NO_NEXT_HOP = -12345 // 没有下一跳 -12345
|
|
)
|
|
|
|
//
|
|
// CalculatePath
|
|
// @Description: 计算可转发路径。路由计算里的唯一Public的接口
|
|
// @receiver c
|
|
// @param routerMap
|
|
// @param rTable
|
|
// @param mConfig
|
|
// @param lsdb
|
|
//
|
|
func (c *LinkStateRoutingTableCalculator) CalculatePath(routerMap *RouterMap,
|
|
mConfig *common.MlsrConfig,
|
|
lsdb *lsdb.Lsdb) ([]*component.Identifier, []*NextHop, error) {
|
|
// 设置路由器数目
|
|
c.SetRouterNum(routerMap.GetMapSize())
|
|
// 分配矩阵空间
|
|
c.allocateAdjMatrix()
|
|
// 初始化矩阵数值
|
|
c.initMatrix()
|
|
// 根据Lsdb邻接信息构造邻接矩阵
|
|
_ = c.makeAdjMatrix(lsdb, routerMap)
|
|
// 为路由计算中记录路径和距离的两个数组分配空间
|
|
c.allocateParent()
|
|
c.allocateDistance()
|
|
// 从配置文件中获取源路由器前缀,并根据routerMap获取其映射号
|
|
sourceRouter, b := routerMap.GetMappingNoByRouterName(mConfig.GetRouterPrefix())
|
|
if !b {
|
|
return nil, nil, errors.New("no found the source route in the router map, " +
|
|
"the source router prefix is: " + mConfig.GetRouterPrefix().ToUri())
|
|
}
|
|
// 初始化切片
|
|
routers := []*component.Identifier{} // 路由器标识
|
|
nexthops := []*NextHop{} // 下一跳
|
|
// 判断是单路径计算,还是多路径计算
|
|
if mConfig.GetMaxFacesPerPrefix() == 1 {
|
|
// 在单路径的情况下,我们可以简单地运行Dijkstra算法。
|
|
c.doDijkstraPathCalculation(sourceRouter)
|
|
// 将新的下一跳通知路由表。// 注意,当前版本中,本机的邻接信息是从配置文件中读取出来的。
|
|
rs, ns := c.addAllLsNextHopsToRoutingTable(mConfig.GetAdjacencys(), routerMap, sourceRouter)
|
|
return rs, ns, nil
|
|
} else {
|
|
// 多路径计算
|
|
// 1. 先确定源路由器的邻居路由器数目
|
|
c.setNoLink(c.getNumOfLinkfromAdjMatrix(sourceRouter))
|
|
c.allocateLinks()
|
|
c.allocateLinkCosts()
|
|
// 2. 获取用于路径计算的稀疏邻接列表。具体操作是填充:c.links,c.linkCosts
|
|
c.getLinksFromAdjMatrix(sourceRouter)
|
|
// 3. 遍历每个外接链路能够达到其它目的路由器的所有下一跳及其开销
|
|
for i := 0; i < c.vNoLink; i++ {
|
|
// 模拟只有当前邻居i可以访问的情形。即:砍掉除当前邻居路由器外的其他邻接链路
|
|
c.adjustAdMatrix(sourceRouter, c.links[i], c.linkCosts[i])
|
|
// 以当前邻居为起点,执行Dijkstra算法。
|
|
c.doDijkstraPathCalculation(sourceRouter)
|
|
// 用计算结果更新路由表。
|
|
rs, ns := c.addAllLsNextHopsToRoutingTable(mConfig.GetAdjacencys(), routerMap, sourceRouter)
|
|
routers = append(routers, rs...)
|
|
nexthops = append(nexthops, ns...)
|
|
}
|
|
c.freeLinks()
|
|
c.freeLinksCosts()
|
|
}
|
|
c.freeParent()
|
|
c.freeDistance()
|
|
c.freeAdjMatrix()
|
|
return routers, nexthops, nil
|
|
}
|
|
|
|
//
|
|
// doDijkstraPathCalculation
|
|
// @Description: 在邻接矩阵基础上,针对源路由器,做迪吉斯特拉计算
|
|
// 在注释中,“左集”表示已访问节点,“右集”表示未访问节点
|
|
// @receiver c
|
|
// @param sourceRouter
|
|
//
|
|
func (c *LinkStateRoutingTableCalculator) doDijkstraPathCalculation(sourceRouter int) {
|
|
// 尚未加入左集的最近元素(Q中右集最小元素)
|
|
var u int
|
|
// 每个单元代表具有该映射号的路由器。
|
|
// Q是queue首字母,它按升序存储所有路由器
|
|
Q := make([]int, c.m_nRouters)
|
|
// 尚未加入左集的最近元素在Q中的索引值
|
|
head := 0
|
|
// 初始化parent
|
|
for i := 0; i < c.m_nRouters; i++ {
|
|
// 每个点的最短路径上一点都初始化为空,即无法到达源路由器
|
|
c.m_parent[i] = EMPTY_PARENT
|
|
// 数组中第i个元素存储的是:源路由器到映射号为i的路由器之间的距离。初始化为无穷大
|
|
c.m_distance[i] = INF_DISTANCE
|
|
// 路由器队列初始化为按映射号排列
|
|
Q[i] = i
|
|
}
|
|
if sourceRouter != NO_MAPPING_NUM {
|
|
// 源路由器到其本身的距离为0
|
|
c.m_distance[sourceRouter] = 0
|
|
// 排序完毕,Q中第一个节点必然为源路由器
|
|
c.sortQueueByDistance(Q, c.m_distance, head, c.m_nRouters)
|
|
// 当我们没有访问完每一个节点时,执行循环
|
|
for head < c.m_nRouters {
|
|
// 将u设置为head当前指向的节点。“u,你就是天选之子!”
|
|
u = Q[head]
|
|
if c.m_distance[u] == INF_DISTANCE {
|
|
// 只有没有可访问的节点时,这里才会被执行。"天选之子是个废物,亡矣!"
|
|
break
|
|
}
|
|
// 对u的相邻节点进行迭代遍历。“朕要以你为衡,重设天下!”
|
|
for v := 0; v < c.m_nRouters; v++ {
|
|
// 如果u->v是可达的,
|
|
if c.adjMatrix[u][v] != NON_ADJACENT_COST {
|
|
// 而且我们还没有访问过v,即v属于右集,
|
|
if c.isNotExplored(Q, v, head+1, c.m_nRouters) {
|
|
// 如果到这个节点的距离+从这个节点到v的距离,
|
|
// 小于我们在构建adj LSA时得到的从源节点到v的距离,
|
|
// 就更新为新距离【算法中的松弛操作】
|
|
if c.m_distance[u]+c.adjMatrix[u][v] < c.m_distance[v] {
|
|
// 设置新距离
|
|
c.m_distance[v] = c.m_distance[u] + c.adjMatrix[u][v]
|
|
// 设置parent,即我们如何到达v的
|
|
c.m_parent[v] = u
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// 右移head位置,从我们所在的位置按距离重新排序列表。"苍天已死,黄天当立!"
|
|
head++
|
|
c.sortQueueByDistance(Q, c.m_distance, head, c.m_nRouters)
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// sortQueueByDistance
|
|
// @Description: 对切片中的元素进行交换排序
|
|
// 注意,切片是引用传递,因此这里实际修改了Q的值。
|
|
// @receiver c
|
|
// @param Q 要排序元素的切片
|
|
// @param dist 包含距离的数组
|
|
// @param start 列表中要排序的第一个元素
|
|
// @param element 列表元素数目
|
|
//
|
|
func (c *LinkStateRoutingTableCalculator) sortQueueByDistance(
|
|
Q []int, dist []uint64, start int, element int) {
|
|
for i := start; i < element; i++ {
|
|
for j := i + 1; j < element; j++ {
|
|
if dist[Q[j]] < dist[Q[i]] {
|
|
temp := Q[j]
|
|
Q[j] = Q[i]
|
|
Q[i] = temp
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// isNotExplored
|
|
// @Description: 返回元素是否被访问
|
|
// @receiver c
|
|
// @param Q 要查看的元素列表
|
|
// @param u 要检查的元素
|
|
// @param start 要查看的列表的开头
|
|
// @param element 列表元素数
|
|
//
|
|
func (c *LinkStateRoutingTableCalculator) isNotExplored(
|
|
Q []int, u int, start int, element int) bool {
|
|
for i := start; i < element; i++ {
|
|
if Q[i] == u {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
//
|
|
// addAllLsNextHopsToRoutingTable
|
|
// @Description: 将计算得到的所有路由器及其下一跳加入路由表
|
|
// @receiver c
|
|
// @param adjs
|
|
// @param rMap
|
|
// @param sourceRouter
|
|
// @return []*component.Identifier
|
|
// @return []*route.NextHop
|
|
//
|
|
func (c *LinkStateRoutingTableCalculator) addAllLsNextHopsToRoutingTable(
|
|
adjs *lsa.AdjLsaAdjacenctList, rMap *RouterMap,
|
|
sourceRouter int) ([]*component.Identifier, []*NextHop) {
|
|
// 初始化切片
|
|
routers := []*component.Identifier{} // 路由器标识
|
|
nexthops := []*NextHop{} // 下一跳
|
|
// 对于我们有的每个路由器
|
|
for i := 0; i < c.m_nRouters; i++ {
|
|
if i != sourceRouter {
|
|
// 获取算法确定的下一跳
|
|
nextHopRouter := c.getLsNextHop(i, sourceRouter)
|
|
// 需要该路由器不至于没有对应的下一跳
|
|
if nextHopRouter != NO_NEXT_HOP {
|
|
// 获取链路开销
|
|
routeCost := c.m_distance[i]
|
|
// 根据map获取该路由器的真实名称
|
|
nextHopRouterName, b := rMap.GetRouterNameByMappingNo(nextHopRouter)
|
|
if b {
|
|
// 根据本地邻接LSA列表,获取该下一跳路由器所对应的faceuri
|
|
nextHopFace := adjs.FindAdjacencyByRouterIdentifier(nextHopRouterName)
|
|
if nextHopFace == nil {
|
|
continue
|
|
}
|
|
// 将下一跳加入路由表
|
|
nextHop := new(NextHop)
|
|
nextHop.SetRouteCost(routeCost)
|
|
nextHop.SetLogicFaceUri(nextHopFace.AdjLsaLogicFaceUri.LogicFaceUri())
|
|
// 下面代码相当于:rt.addNextHop(*(pMap.getRouterNameByMappingNo(i)), nh);
|
|
name, bb := rMap.GetRouterNameByMappingNo(i)
|
|
if !bb {
|
|
continue
|
|
}
|
|
routers = append(routers, name)
|
|
nexthops = append(nexthops, nextHop)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return routers, nexthops
|
|
}
|
|
|
|
//
|
|
// getLsNextHop
|
|
// @Description: 决定到目的路由器的下一跳路由器
|
|
// @receiver c
|
|
// @param dest 想要确定的目的路由器
|
|
// @param source 源路由器
|
|
//
|
|
func (c *LinkStateRoutingTableCalculator) getLsNextHop(dest int, source int) int {
|
|
nextHop := NO_NEXT_HOP
|
|
// source的父节点是空节点,故遍历到source或某个断点,就停止循环
|
|
for c.m_parent[dest] != EMPTY_PARENT {
|
|
nextHop = dest
|
|
dest = c.m_parent[dest]
|
|
}
|
|
if dest != source {
|
|
nextHop = NO_NEXT_HOP
|
|
}
|
|
return nextHop
|
|
}
|
|
|
|
//
|
|
// allocateParent
|
|
// @Description: 分配内存
|
|
// @receiver c
|
|
//
|
|
func (c *LinkStateRoutingTableCalculator) allocateParent() {
|
|
c.m_parent = make([]int, c.m_nRouters)
|
|
}
|
|
|
|
//
|
|
// allocateDistance
|
|
// @Description: 分配内存
|
|
// @receiver c
|
|
//
|
|
func (c *LinkStateRoutingTableCalculator) allocateDistance() {
|
|
c.m_distance = make([]uint64, c.m_nRouters)
|
|
}
|
|
|
|
//
|
|
// freeParent
|
|
// @Description: 释放内存
|
|
// @receiver c
|
|
//
|
|
func (c *LinkStateRoutingTableCalculator) freeParent() {
|
|
c.m_parent = []int{}
|
|
}
|
|
|
|
//
|
|
// freeDistance
|
|
// @Description: 释放内存
|
|
// @receiver c
|
|
//
|
|
func (c *LinkStateRoutingTableCalculator) freeDistance() {
|
|
c.m_distance = []uint64{}
|
|
}
|