1
0
mirror of https://gitee.com/willfree/mlsr.git synced 2026-06-07 03:09:27 +08:00
Files
mlsr/route/LinkStateRoutingTableCalculator.go
T

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{}
}