diff --git a/README.md b/README.md index e69de29..42ae0c0 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,43 @@ +# MLSR +## 介绍 +MLSR(MIN Link State Routing Protocol,多标识网络链路状态路由协议)是MIN(Mutil-Identifier Network,多标识网络)中的基本路由协议。 + +MLSR支持推式传输和拉式传输下的路由寻址。 + +## 功能模块简述 + +### LSA模块 + +### LSDB模块 + +### 路由表模块 + +### NPT模块 + +## 开发规范 +### 注释规范 +* 源代码文件抬头注明开发者、时间、版本、组织等信息,请参考较早提交的代码文件。 +* 结构体及函数需注明其作用,请参考较早提交的代码文件。 + +### 代码规范 +* 函数之间留有确定行数的空行。 +* 复杂函数内部可根据逻辑关系,用空行分割为若干块,并加入注释予以解释。 + + +## 开发环境及依赖 +### 开发环境 + +| 环境项 | 信息 | +| :----: | :----: | +| Golang | go 1.18 | +| IDE推荐 | GoLand | + +### 软件依赖 +minlib +注:请将minlib放置在与mlsr同目录下,注意,不是mlsr目录之下,是平行关系。 + +## 联系我们 +MIN-Group +国家重大科技基础设施——未来网络北大实验室 +深圳市信息论与未来网络重点实验室 +开发者邮箱:wefree@stu.pku.edu.cn \ No newline at end of file diff --git a/common/MlsrConfig.go b/common/MlsrConfig.go index af3e9b0..1d97932 100644 --- a/common/MlsrConfig.go +++ b/common/MlsrConfig.go @@ -11,6 +11,8 @@ package common import ( "encoding/json" "gopkg.in/ini.v1" + common2 "minlib/common" + "strconv" ) const DefaultConfFileName = "mlsrConf.ini" @@ -20,58 +22,154 @@ const DefaultConfFileName = "mlsrConf.ini" // @Description: MLSR 配置文件的配置。与mlsrConf.ini一一对应 // type MlsrConfig struct { - GeneralConfig `ini:"General"` - NeighborsConfig `ini:"Neighbors"` - HyperbolicConfig `ini:"Hyperbolic"` - FibConfig `ini:"Fib"` - AdvertisingConfig `ini:"Advertising"` - SecurityConfig `ini:"Security"` + GeneralConfig `ini:"General"` + LogConfig `ini:"Log"` + NeighborsConfig `ini:"Neighbors"` + HyperbolicConfig `ini:"Hyperbolic"` + FibConfig `ini:"Fib"` + AdvertisingConfig `ini:"Advertising"` + SecurityConfig `ini:"Security"` - mlsrConfigPath string // 配置文件所在路径 - neighbors []NeighborConfig // NeighborsConfig中的信息 + // 获取值的接口 + MlsrConfigParameters + + mlsrConfigPath string // 配置文件所在路径 + neighbors []NeighborConfig // NeighborsConfig中的信息 +} + +// +// ToString +// @Description: +// @receiver c +// +func (c *MlsrConfig) ToString() string { + var str string + str += "----------------------- MlsrConfig -------------------------\n" + str += "Config File Path: " + c.mlsrConfigPath + "\n" + str += "----------------- 1. GeneralConfig -------------------------\n" + bytes, err := json.Marshal(c.GeneralConfig) + if err == nil { + str += string(bytes) + "\n" + } + str += "----------------- 2. LogConfig -----------------------------\n" + bytes, err = json.Marshal(c.LogConfig) + if err == nil { + str += string(bytes) + "\n" + } + str += "----------------- 3. NeighborsConfig -----------------------\n" + bytes, err = json.Marshal(c.NeighborsConfig) + if err == nil { + str += string(bytes) + "\n" + } + str += "----------------- 4. HyperbolicConfig ----------------------\n" + bytes, err = json.Marshal(c.HyperbolicConfig) + if err == nil { + str += string(bytes) + "\n" + } + str += "----------------- 5. FibConfig -----------------------------\n" + bytes, err = json.Marshal(c.FibConfig) + if err == nil { + str += string(bytes) + "\n" + } + str += "----------------- 6. AdvertisingConfig ---------------------\n" + bytes, err = json.Marshal(c.AdvertisingConfig) + if err == nil { + str += string(bytes) + "\n" + } + str += "----------------- 7. SecurityConfig ------------------------\n" + bytes, err = json.Marshal(c.SecurityConfig) + if err == nil { + str += string(bytes) + "\n" + } + str += "\n" + return str +} + +func (c *MlsrConfig) ParametersToString() string { + var str string + str += "----------------------- MlsrConfigParameters -------------------------\n" + str += "----------------------- 1. 标识符 -------------------------------------\n" + str += "路由器定位符:" + c.MlsrConfigParameters.m_routerName.ToUri() + "\n" + str += "网域定位符:" + c.MlsrConfigParameters.m_siteName.ToUri() + "\n" + str += "网络定位符:" + c.MlsrConfigParameters.m_network.ToUri() + "\n" + str += "路由器前缀:" + c.MlsrConfigParameters.m_routerPrefix.ToUri() + "\n" + str += "同步用户前缀:" + c.MlsrConfigParameters.m_syncUserPrefix.ToUri() + "\n" + str += "同步前缀:" + c.MlsrConfigParameters.m_syncPrefix.ToUri() + "\n" + str += "LSA前缀:" + c.MlsrConfigParameters.m_lsaPrefix.ToUri() + "\n" + str += "----------------------- 2. 时间 ---------------------------------------\n" + str += "LSA刷新时间:" + strconv.Itoa(int(c.MlsrConfigParameters.m_lsaRefreshTime.Seconds())) + " " + + "秒 = " + strconv.Itoa(int(c.MlsrConfigParameters.m_lsaRefreshTime.Seconds())/60) + "分钟\n" + str += "LSA兴趣包生命周期:" + strconv.Itoa(int(c.MlsrConfigParameters.m_lsaInterestLifetime.Seconds())) + " 秒" + "\n" + str += "路由器死亡间隔:" + strconv.Itoa(int(c.MlsrConfigParameters.m_routerDeadInterval.Seconds())) + " " + + "秒 = " + strconv.Itoa(int(c.MlsrConfigParameters.m_routerDeadInterval.Seconds())/60) + "分钟\n" + str += "同步兴趣包生命周期:" + strconv.Itoa(int(c.MlsrConfigParameters.m_syncInterestLifetime.Seconds())) + " 秒" + "\n" + + str += "hello超时时间:" + strconv.Itoa(int(c.MlsrConfigParameters.m_helloTimeout.Seconds())) + " 秒" + "\n" + str += "hello发送间隔:" + strconv.Itoa(int(c.MlsrConfigParameters.m_helloInterval.Seconds())) + " 秒" + "\n" + str += "邻接LSA构建间隔:" + strconv.Itoa(int(c.MlsrConfigParameters.m_adjLsaBuildInterval.Seconds())) + " 秒" + "\n" + str += "LogicFace数据集请求间隔:" + strconv.Itoa(int(c.MlsrConfigParameters.m_faceDatasetFetchInterval.Seconds())) + " " + + "秒 = " + strconv.Itoa(int(c.MlsrConfigParameters.m_faceDatasetFetchInterval.Seconds())/60) + "分钟\n" + str += "路由计算间隔:" + strconv.Itoa(int(c.MlsrConfigParameters.m_RoutingCalcInterval.Seconds())) + " 秒" + "\n" + str += "\n" + return str } // // GeneralConfig -// @Description: 最基础配置信息:LSA、同步协议 +// @Description: 一、最基础配置信息:LSA、同步协议 // type GeneralConfig struct { - Network string `ini:"Network"` // 所属网络 - Site string `ini:"Site"` // 所属站点 - Router string `ini:"Router"` // 所属路由器 + Network string `ini:"Network"` // 所属网络 + Site string `ini:"Site"` // 所属站点 + Router string `ini:"Router"` // 所属路由器 - LsaRefreshTime uint32 `ini:"LsaRefreshTime"` // LSA刷新时间 + LsaRefreshTime uint32 `ini:"LsaRefreshTime"` // LSA刷新时间【单位:毫秒】 - RouterDeadInterval uint32 `ini:"RouterDeadInterval"` // 路由器判断死亡的时间间隔(必须大于LSA刷新时间) + // 路由器死亡后,本机NameLsa和AdjLsa将超期失效,从而被LSDB清除 + // 路由器死亡时间是本机LSA构建时的超期时间,但它在本机进行刷新时,是不按照死亡时间进行刷新的,而是按照LSA刷新时间做刷新的触发 + // 路由器死亡时间是给其他路由器的LSA超期时间,例如路由器B在死亡时间间隔后会将路由器A的LSA删除,但路由器A其实在LSA刷新时间就已经 + // 刷新该LSA,并通知给路由器B,因而保证了路由器B一直有路由器A的LSA。 + RouterDeadInterval uint32 `ini:"RouterDeadInterval"` // 路由器判断死亡的时间间隔(必须大于LSA刷新时间)【单位:毫秒】 - LsaInterestLifetime int `ini:"LsaInterestLifetime"` // LSA兴趣包生存周期。当使用推式包时,此值无效 + LsaInterestLifetime int `ini:"LsaInterestLifetime"` // LSA兴趣包生存周期。当使用推式包时,此值无效【单位:毫秒】 - SyncProtocol string `ini:"SyncProtocol"` // 同步协议 - SyncInterestLifetime uint32 `ini:"SyncInterestLifetime"` // 同步协议兴趣包生命周期 + SyncProtocol string `ini:"SyncProtocol"` // 同步协议 + SyncInterestLifetime uint32 `ini:"SyncInterestLifetime"` // 同步协议兴趣包生命周期【单位:毫秒】 +} + +// +// LogConfig +// @Description: 二、日志模块 +// +type LogConfig struct { + LogLevel string `ini:"LogLevel"` // 日志等级 + ReportCaller bool `ini:"ReportCaller"` // 日志输出时是否添加文件名和函数名 + LogFormat string `ini:"LogFormat"` // 输出日志的格式 "json" | "text" + LogFilePath string `ini:"LogFilePath"` // 日志输出文件路径,为空则输出至控制台 } // // NeighborsConfig -// @Description: 邻居路由器相关配置 +// @Description: 三、邻居路由器相关配置 // type NeighborsConfig struct { - HelloRetries int `ini:"HelloRetries"` // hello协议探测最大次数 - HelloTimeout uint32 `ini:"HelloTimeout"` // hello协议中的超时时间 - HelloInterval uint32 `ini:"HelloInterval"` // 两个hello包之间的间隔时间 + HelloRetries int `ini:"HelloRetries"` // hello协议探测最大次数【单位:毫秒】 + HelloTimeout uint32 `ini:"HelloTimeout"` // hello协议中的超时时间【单位:毫秒】 + HelloInterval uint32 `ini:"HelloInterval"` // 两个hello包之间的间隔时间【单位:毫秒】 - AdjLsaBuildInterval uint32 `ini:"AdjLsaBuildInterval"` // 邻接LSA构建的时间间隔 - LogicFaceDatasetFetchRetries uint32 `ini:"LogicFaceDatasetFetchRetries"` // 获取FaceStatus数据集的重试次数 - LogicFaceDatasetFetchInterval uint32 `ini:"LogicFaceDatasetFetchInterval"` // FaceStatus数据集获取尝试之间的间隔 - - NeighborsInfo NeighborsJsonString `ini:"NeighborsInfo"` // 邻居路由器链路信息 + AdjLsaBuildInterval uint32 `ini:"AdjLsaBuildInterval"` // 邻接LSA构建的时间间隔【单位:毫秒】 + LogicFaceDatasetFetchRetries uint32 `ini:"LogicFaceDatasetFetchRetries"` // 获取FaceStatus数据集的重试次数 + LogicFaceDatasetFetchInterval uint32 `ini:"LogicFaceDatasetFetchInterval"` // FaceStatus数据集获取尝试之间的间隔【单位:毫秒】 + + NeighborsInfo NeighborsJsonString `ini:"NeighborsInfo"` // 邻居路由器链路信息 } type NeighborsJsonString string type NeighborConfig struct { - NeighborName string `ini:"NeighborName"` // 邻居路由器的名称前缀 - LogicFaceUri string `ini:"LogicFaceUri"` // 连接该邻居路由器的LogicFace的URI - LinkCost uint64 `ini:"LinkCost"` // 链路开销(用uint64还是double待决) + NeighborName string `ini:"NeighborName"` // 邻居路由器的名称前缀 + LogicFaceUri string `ini:"LogicFaceUri"` // 连接该邻居路由器的LogicFace的URI + LinkCost uint64 `ini:"LinkCost"` // 链路开销(用uint64还是double待决) } // @@ -81,10 +179,10 @@ type NeighborConfig struct { // @return NeighborsJsonString // @return error // -func parseNeighborConfigToJsonString(neighbors []NeighborConfig) (NeighborsJsonString,error) { - bytes,err := json.Marshal(neighbors) - if err!= nil{ - return "",err +func parseNeighborConfigToJsonString(neighbors []NeighborConfig) (NeighborsJsonString, error) { + bytes, err := json.Marshal(neighbors) + if err != nil { + return "", err } return NeighborsJsonString(bytes), nil } @@ -97,35 +195,43 @@ func parseNeighborConfigToJsonString(neighbors []NeighborConfig) (NeighborsJsonS // @return []NeighborConfig // @return error // -func parseJsonStringToNeighbors(jsonString NeighborsJsonString) ([]NeighborConfig,error) { +func parseJsonStringToNeighbors(jsonString NeighborsJsonString) ([]NeighborConfig, error) { var neighbors []NeighborConfig err := json.Unmarshal([]byte(jsonString), &neighbors) - if err!= nil{ - return nil,err + if err != nil { + return nil, err } return neighbors, nil } // // HyperbolicConfig -// @Description: 预留双曲坐标的接口 +// @Description: 四、预留双曲坐标的配置 // type HyperbolicConfig struct { } // // FibConfig -// @Description: 路由计算相关配置 +// @Description: 五、路由计算与FIB更新相关配置 // type FibConfig struct { - MaxLogicFacesPerPrefix int `ini:"MaxLogicFacesPerPrefix"` // 每个前缀对应的下一跳face列表的最大数目 - RoutingCalcInterval uint32 `ini:"RoutingCalcInterval"` // 两次路由计算的时间间隔 + MaxLogicFacesPerPrefix int `ini:"MaxLogicFacesPerPrefix"` // 每个前缀对应的下一跳face列表的最大数目 + RoutingCalcInterval uint32 `ini:"RoutingCalcInterval"` // 两次路由计算的时间间隔【单位:毫秒】 } +// +// AdvertisingConfig +// @Description: 六、需要通告给其它路由器的前缀 +// type AdvertisingConfig struct { - Prefixs []string `ini:"Prefixs"` // 需要广告给其他路由器的本机前缀 + Prefixs []string `ini:"Prefixs"` // 需要广告给其他路由器的本机前缀 } +// +// SecurityConfig +// @Description: 七、预留安全相关的配置 +// type SecurityConfig struct { } @@ -134,24 +240,32 @@ type SecurityConfig struct { // @Description: 初始化配置,给所有的配置设置默认值 // @receiver c // -func (c *MlsrConfig) Init() { - // 通用配置参数 +func (c *MlsrConfig) Init() { + // 1. 通用配置参数 c.GeneralConfig.Network = "/min" c.GeneralConfig.Site = "/pkusz" c.GeneralConfig.Router = "/routerA" - c.GeneralConfig.LsaRefreshTime = 1800 // 240-7200 - c.GeneralConfig.RouterDeadInterval = 2*c.GeneralConfig.LsaRefreshTime - c.GeneralConfig.LsaInterestLifetime = 4 // 4-60 + c.GeneralConfig.LsaRefreshTime = 30 * 60 * 1000 // 60*4-60*120,范围是4分钟-2小时。默认半小时 + c.GeneralConfig.RouterDeadInterval = 2 * c.GeneralConfig.LsaRefreshTime // 默认一小时 + c.GeneralConfig.LsaInterestLifetime = 4 * 1000 // 4-60,范围是4秒钟到1分钟。默认4秒钟 c.GeneralConfig.SyncProtocol = "min-sync" - c.GeneralConfig.SyncInterestLifetime = 60000 // 1000-120000 + c.GeneralConfig.SyncInterestLifetime = 60 * 1000 // 1000-120*1000,范围是1秒到2分钟。默认1分钟 - // 邻居路由器配置参数 - c.NeighborsConfig.HelloRetries = 3 // 1-15 - c.NeighborsConfig.HelloTimeout = 1 // 1-15 - c.NeighborsConfig.HelloInterval = 60 // 30-90 - c.NeighborsConfig.AdjLsaBuildInterval = 10 // 5-30 - c.NeighborsConfig.LogicFaceDatasetFetchRetries = 3 // 1-10 - c.NeighborsConfig.LogicFaceDatasetFetchInterval = 3600 // 1800-5400 + // 2. Log + c.LogConfig.LogLevel = "ALL" + c.LogConfig.ReportCaller = true + c.LogConfig.LogFormat = "text" + c.LogConfig.LogFilePath = "" + + // 3. 邻居路由器配置参数 + c.NeighborsConfig.HelloRetries = 3 // 1-15,次数 + c.NeighborsConfig.HelloTimeout = 1 * 1000 // 1-15,超时时间,范围1秒-15秒。默认1秒钟 + c.NeighborsConfig.HelloInterval = 60 * 1000 // 30-90,间隔时间,范围30秒到1分钟。默认1分钟 + c.NeighborsConfig.AdjLsaBuildInterval = 10 * 1000 // 5-30,构建间隔,范围5秒到30秒。默认10秒钟 + c.NeighborsConfig.LogicFaceDatasetFetchRetries = 3 // 1-10,次数 + c.NeighborsConfig.LogicFaceDatasetFetchInterval = 60 * 60 * 1000 // 30*60-90*60,半小时到一个半小时。默认1小时 + // 邻接链路信息 + c.neighbors = []NeighborConfig{} // 将邻居链路信息存储到配置文件中 todo:用于测试 neighborConfig1 := NeighborConfig{ NeighborName: "/routerB", @@ -166,14 +280,16 @@ func (c *MlsrConfig) Init() { //neighborsJsonString, _ := parseNeighborConfigToJsonString( // []NeighborConfig{neighborConfig1,neighborConfig2}) //c.NeighborsConfig.NeighborsInfo=neighborsJsonString - c.neighbors=[]NeighborConfig{neighborConfig1,neighborConfig2} + c.neighbors = append(c.neighbors, neighborConfig1) + c.neighbors = append(c.neighbors, neighborConfig2) - // 路由计算配置参数 - c.FibConfig.MaxLogicFacesPerPrefix = 0 // 0-60 todo:为啥默认是0呢 - c.FibConfig.RoutingCalcInterval = 15 // 0-15 + // 5. FIB更新配置参数 + c.FibConfig.MaxLogicFacesPerPrefix = 0 // 0-60,个数,最多有多少个下一跳。置0表示保留所有计算出的下一跳 + c.FibConfig.RoutingCalcInterval = 15 * 1000 // 0-15,路由计算间隔。默认15秒计算一次 - // 广播本地前缀配置参数 - c.AdvertisingConfig.Prefixs=[]string{"/videos","/files","voices"} // todo:用于测试 + // 6. 广播本地前缀配置参数 + // c.AdvertisingConfig.Prefixs = []string{} // 非测试状态 + c.AdvertisingConfig.Prefixs = []string{"/videos", "/files", "/voices"} // todo:用于测试 } // @@ -184,10 +300,10 @@ func (c *MlsrConfig) Init() { // func (c *MlsrConfig) Save() error { cfg := ini.Empty() - if len(c.neighbors)>0 { + if len(c.neighbors) > 0 { c.NeighborsInfo, _ = parseNeighborConfigToJsonString(c.neighbors) } - if err:= ini.ReflectFrom(cfg,c); err!= nil{ + if err := ini.ReflectFrom(cfg, c); err != nil { return err } return cfg.SaveTo(c.mlsrConfigPath) @@ -204,18 +320,24 @@ func (c *MlsrConfig) Save() error { func ParseConfig(configPath string) (*MlsrConfig, error) { cfg, err := ini.Load(configPath) if err != nil { + common2.LogFatal("ParseConfig error, load error, path is ", configPath, err.Error()) return nil, err } mlsrConfig := new(MlsrConfig) mlsrConfig.mlsrConfigPath = configPath // 初始化配置,给所有的配置项设置默认值 mlsrConfig.Init() - // 加载配置文件中的配置 + // 加载配置文件中的配置:如果有,则理应覆盖Init中设置的默认配置 if err = cfg.MapTo(&mlsrConfig); err != nil { + common2.LogFatal("ParseConfig error, mapTo error, path is ", configPath, err.Error()) return nil, err } - if mlsrConfig.NeighborsInfo!="" { + if mlsrConfig.NeighborsInfo != "" { mlsrConfig.neighbors, _ = parseJsonStringToNeighbors(mlsrConfig.NeighborsInfo) } + // 将配置中的参数进行初始化,以支持直接获取结构体形式的参数 + mlsrConfig.MlsrConfigParameters.Init(mlsrConfig) + // 按照配置文件中的参数,对日志模块进行初始化。不做这一步,则日志模块的配置失效 + InitLogger(mlsrConfig) return mlsrConfig, nil } diff --git a/common/MlsrConfigParameters.go b/common/MlsrConfigParameters.go new file mode 100644 index 0000000..acd5d78 --- /dev/null +++ b/common/MlsrConfigParameters.go @@ -0,0 +1,460 @@ +// Package common +// @Author: Wang Feng +// @Description: MlsrConfig的辅助类。MlsrConf中主要实现读写配置文件的操作, +// 本类则负责将配置参数转化为实际的结构体。 +// @Version: 0.1.0 +// @Date: 2022/5/12 15:29 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package common + +import ( + common2 "minlib/common" + "minlib/component" + "mlsr/lsa" + "strconv" + "time" +) + +// 同步协议版本号 +const SYNC_VERSION = 1 + +type MlsrConfigParameters struct { + // 元数据从配置文件中来 + mlsrconf *MlsrConfig + + // 名称前缀/名称标识 + m_routerName *component.Identifier + m_siteName *component.Identifier + m_network *component.Identifier + m_routerPrefix *component.Identifier + m_syncUserPrefix *component.Identifier + m_syncPrefix *component.Identifier + m_lsaPrefix *component.Identifier + + // 时间:通用配置 + m_lsaRefreshTime time.Duration + m_lsaInterestLifetime time.Duration + m_routerDeadInterval time.Duration // 路由器死亡时间(经过了该时间,NameLsa将触发超时) + m_syncInterestLifetime time.Duration + + // 时间:邻居路由器 + m_helloTimeout time.Duration + m_helloInterval time.Duration + m_adjLsaBuildInterval time.Duration + m_faceDatasetFetchInterval time.Duration + + // 时间:FIB更新 + m_RoutingCalcInterval time.Duration + + // 邻接信息列表 + m_adjl *lsa.AdjLsaAdjacenctList + // 命名前缀列表 + m_npl *lsa.NameLsaNamePrefixList +} + +// MlsrConfig初始化 +func (mcp *MlsrConfigParameters) Init(config *MlsrConfig) { + mcp.setMetaData(config) + // 依次构建各个名称前缀/名称标识 + _ = mcp.build3Prefix() + _ = mcp.buildSyncAndLsaPrefix() + _ = mcp.buildRouterAndSyncUserPrefix() + // 构建时间间隔 + mcp.genDurations() + // 构建邻接表和名称前缀表 + mcp.InitAdjacencyList() + mcp.InitNamePrefixList() +} + +// +// setMetaData +// @Description: 设置mlsrconfig +// @receiver mcp +// @param config +// +func (mcp *MlsrConfigParameters) setMetaData(config *MlsrConfig) { + mcp.mlsrconf = config +} + +// +// genDurations +// @Description: 生成时间相关配置信息 +// @receiver mcp +// +func (mcp *MlsrConfigParameters) genDurations() { + mcp.m_lsaRefreshTime = + time.Duration(mcp.mlsrconf.LsaRefreshTime) * time.Millisecond + mcp.m_lsaInterestLifetime = + time.Duration(mcp.mlsrconf.LsaInterestLifetime) * time.Millisecond + mcp.m_routerDeadInterval = + time.Duration(mcp.mlsrconf.RouterDeadInterval) * time.Millisecond + mcp.m_syncInterestLifetime = + time.Duration(mcp.mlsrconf.SyncInterestLifetime) * time.Millisecond + + mcp.m_helloTimeout = + time.Duration(mcp.mlsrconf.HelloTimeout) * time.Millisecond + mcp.m_helloInterval = + time.Duration(mcp.mlsrconf.HelloInterval) * time.Millisecond + mcp.m_adjLsaBuildInterval = + time.Duration(mcp.mlsrconf.AdjLsaBuildInterval) * time.Millisecond + mcp.m_faceDatasetFetchInterval = + time.Duration(mcp.mlsrconf.LogicFaceDatasetFetchInterval) * time.Millisecond + + mcp.m_RoutingCalcInterval = + time.Duration(mcp.mlsrconf.RoutingCalcInterval) * time.Millisecond +} + +// +// build3Prefix +// @Description: 构建了三个必要的名称组件,网络、网域/网站、路由器 +// @receiver mcp +// @return error +// +func (mcp *MlsrConfigParameters) build3Prefix() error { + var err error + mcp.m_network, err = component.CreateIdentifierByString(mcp.mlsrconf.Network) + if err != nil { + return err + } + + mcp.m_siteName, err = component.CreateIdentifierByString(mcp.mlsrconf.Site) + if err != nil { + return err + } + + mcp.m_routerName, err = component.CreateIdentifierByString(mcp.mlsrconf.Router) + if err != nil { + return err + } + return nil +} + +func (mcp *MlsrConfigParameters) buildSyncAndLsaPrefix() error { + // 同步前缀 = "/localhop" + 网络字段 + "/mlsr/sync" + SYNC版本号 + syncPrefix := "/localhop" + mcp.mlsrconf.Network + "/mlsr/sync" + "/" + strconv.Itoa(SYNC_VERSION) + var err error + mcp.m_syncPrefix, err = component.CreateIdentifierByString(syncPrefix) + if err != nil { + return err + } + + // Lsa前缀 = "/localhop" + 网络字段 + "/sync/LSA" + lsaPrefix := "/localhop" + mcp.mlsrconf.Network + "/sync/LSA" + mcp.m_lsaPrefix, err = component.CreateIdentifierByString(lsaPrefix) + if err != nil { + return err + } + return nil +} + +func (mcp *MlsrConfigParameters) buildRouterAndSyncUserPrefix() error { + // 路由器前缀 = 网络字段 + 网域字段 + 路由器字段 + prefix := mcp.mlsrconf.Network + mcp.mlsrconf.Site + mcp.mlsrconf.Router + var err error + mcp.m_routerPrefix, err = component.CreateIdentifierByString(prefix) + if err != nil { + return err + } + + // 同步用户前缀 = Lsa前缀 + 网域字段 + 路由器字段 + syncUserPrefix := mcp.m_lsaPrefix.ToUri() + mcp.mlsrconf.Site + mcp.mlsrconf.Router + mcp.m_syncUserPrefix, err = component.CreateIdentifierByString(syncUserPrefix) + if err != nil { + return err + } + return nil +} + +// +// GetNetwork +// @Description: 网络定位符 +// @receiver mcp +// @return *component.Identifier +// +func (mcp *MlsrConfigParameters) GetNetwork() *component.Identifier { + return mcp.m_network +} + +// +// GetSiteName +// @Description:网域定位符 +// @receiver mcp +// @return *component.Identifier +// +func (mcp *MlsrConfigParameters) GetSiteName() *component.Identifier { + return mcp.m_siteName +} + +// +// GetRouterName +// @Description: 路由器定位符 +// @receiver mcp +// @return *component.Identifier +// +func (mcp *MlsrConfigParameters) GetRouterName() *component.Identifier { + return mcp.m_routerName +} + +// +// GetRouterPrefix +// @Description: 路由器前缀 +// @receiver mcp +// @return *component.Identifier +// +func (mcp *MlsrConfigParameters) GetRouterPrefix() *component.Identifier { + return mcp.m_routerPrefix +} + +// +// GetSyncUserPrefix +// @Description: 同步用户前缀 +// @receiver mcp +// @return *component.Identifier +// +func (mcp *MlsrConfigParameters) GetSyncUserPrefix() *component.Identifier { + return mcp.m_syncUserPrefix +} + +// +// GetSyncPrefix +// @Description: 同步前缀 +// @receiver mcp +// @return *component.Identifier +// +func (mcp *MlsrConfigParameters) GetSyncPrefix() *component.Identifier { + return mcp.m_syncPrefix +} + +// +// GetLsaPrefix +// @Description: LSA前缀 +// @receiver mcp +// @return *component.Identifier +// +func (mcp *MlsrConfigParameters) GetLsaPrefix() *component.Identifier { + return mcp.m_lsaPrefix +} + +func (mcp *MlsrConfigParameters) GetSyncProtocol() { + // todo +} + +// +// GetLsaRefreshTime +// @Description: LSA刷新时间 +// @receiver mcp +// @return time.Duration +// +func (mcp *MlsrConfigParameters) GetLsaRefreshTime() time.Duration { + return mcp.m_lsaRefreshTime +} + +// +// GetLsaInterestLifetime +// @Description: LSA兴趣包生命周期 +// @receiver mcp +// @return time.Duration +// +func (mcp *MlsrConfigParameters) GetLsaInterestLifetime() time.Duration { + return mcp.m_lsaInterestLifetime +} + +// +// GetRouterDeadInterval +// @Description: 路由器死亡间隔 +// @receiver mcp +// @return time.Duration +// +func (mcp *MlsrConfigParameters) GetRouterDeadInterval() time.Duration { + return mcp.m_routerDeadInterval +} + +// +// GetSyncInterestLifetime +// @Description: 同步兴趣包生命周期 +// @receiver mcp +// @return time.Duration +// +func (mcp *MlsrConfigParameters) GetSyncInterestLifetime() time.Duration { + return mcp.m_syncInterestLifetime +} + +// +// GetAdjLsaBuildInterval +// @Description: 邻接LSA构建间隔 +// @receiver mcp +// @return time.Duration +// +func (mcp *MlsrConfigParameters) GetAdjLsaBuildInterval() time.Duration { + return mcp.m_adjLsaBuildInterval +} + +// +// GetFaceDatasetFetchInterval +// @Description: LogicFace数据集请求间隔 +// @receiver mcp +// @return time.Duration +// +func (mcp *MlsrConfigParameters) GetFaceDatasetFetchInterval() time.Duration { + return mcp.m_faceDatasetFetchInterval +} + +// +// GetRoutingCalcInterval +// @Description: 路由计算间隔 +// @receiver mcp +// @return time.Duration +// +func (mcp *MlsrConfigParameters) GetRoutingCalcInterval() time.Duration { + return mcp.m_RoutingCalcInterval +} + +// +// GetFaceDatasetFetchTries +// @Description: LogicFace数据集请求次数 +// @receiver mcp +// @return int +// +func (mcp *MlsrConfigParameters) GetFaceDatasetFetchTries() uint32 { + return mcp.mlsrconf.LogicFaceDatasetFetchRetries +} + +// +// GetHelloRetries +// @Description: hello兴趣包重传次数 +// @receiver mcp +// @return int +// +func (mcp MlsrConfigParameters) GetHelloRetries() int { + return mcp.mlsrconf.HelloRetries +} + +// +// GetHelloTimeout +// @Description: Hello超时时间 +// @receiver mcp +// @return time.Duration +// +func (mcp *MlsrConfigParameters) GetHelloTimeout() time.Duration { + return mcp.m_helloTimeout +} + +// +// GetHelloInterval +// @Description: Hello重传间隔 +// @receiver mcp +// @return time.Duration +// +func (mcp *MlsrConfigParameters) GetHelloInterval() time.Duration { + return mcp.m_helloInterval +} + +func (mcp *MlsrConfigParameters) GetInterestRetryNumber() { + // todo +} + +func (mcp *MlsrConfigParameters) GetInterestResendTime() { + // todo +} + +func (mcp *MlsrConfigParameters) GetInfoInterestInterval() { + // todo +} + +func (mcp *MlsrConfigParameters) GetMaxFacesPerPrefix() int { + return mcp.mlsrconf.MaxLogicFacesPerPrefix +} + +// +// InitAdjacencyList +// @Description: 从配置文件中初始化邻接信息列表 +// @receiver mcp +// +func (mcp *MlsrConfigParameters) InitAdjacencyList() { + mcp.m_adjl = new(lsa.AdjLsaAdjacenctList) + neighborConfigs := mcp.mlsrconf.neighbors + for i := 0; i < len(neighborConfigs); i++ { + // 根据配置文件构造每条邻接链路信息 + // 链路开销、URI及定位符均按照配置进行构造, + // FaceId缺省为0,兴趣包超时次数缺省为0,状态缺省为未激活 + linkCost := lsa.AdjLsaLinkCost{} + linkCost.SetLinkCost(neighborConfigs[i].LinkCost) + faceUri := lsa.AdjLsaLogicFaceUri{} + faceUri.SetLogicFaceUri(neighborConfigs[i].LogicFaceUri) + iden, err := component.CreateIdentifierByString(neighborConfigs[i].NeighborName) + if err != nil { + common2.LogError("MlsrCOnfig GetAdjacencyList error, beacuse ", err.Error()) + continue + } + faceId := lsa.AdjLsaLogicFaceId{} + faceId.SetLogicFaceId(0) + adjInfo := lsa.NewAdjLsaAdjacencyInfo(linkCost, faceUri, faceId, iden, 0, lsa.STATUS_INACTIVE) + // 加入信息列表 + mcp.m_adjl.Insert(adjInfo) + } +} + +func (mcp *MlsrConfigParameters) InitNamePrefixList() { + mcp.m_npl = new(lsa.NameLsaNamePrefixList) + // 从配置文件读取广播前缀 + adverPrefixs := mcp.mlsrconf.AdvertisingConfig.Prefixs + for i := 0; i < len(adverPrefixs); i++ { + // 为每个广播前缀构建一条名称前缀信息,其source为缺省的"" + namePrefixInfo := new(lsa.NameLsaNamePrefixInfo) + iden, err := component.CreateIdentifierByString(adverPrefixs[i]) + if err != nil { + common2.LogError("MlsrCOnfig GetNamePrefixList error, beacuse ", err.Error()) + continue + } + namePrefixInfo.NamePrefix = iden + namePrefixInfo.InsertSource("") + // 将该名称前缀信息加入列表 + mcp.m_npl.InsertNamePrefixInfo(namePrefixInfo) + } +} + +// +// GetAdjacencyList +// @Description: 从配置文件中获取邻接信息列表 +// @receiver mcp +// @return []*lsa.AdjLsaAdjacencyInfo +// +func (mcp *MlsrConfigParameters) GetAdjacencyList() []*lsa.AdjLsaAdjacencyInfo { + return mcp.m_adjl.GetAdjList() +} + +// +// GetAdjacencys +// @Description: 获取整体的邻接信息 +// @receiver mcp +// @return *lsa.AdjLsaAdjacenctList +// +func (mcp *MlsrConfigParameters) GetAdjacencys() *lsa.AdjLsaAdjacenctList { + return mcp.m_adjl +} + +// +// GetNamePrefixList +// @Description: 从配置文件中获取名称前缀信息列表 +// @receiver mcp +// @return []*lsa.NameLsaNamePrefixInfo +// +func (mcp *MlsrConfigParameters) GetNamePrefixList() []*lsa.NameLsaNamePrefixInfo { + return mcp.m_npl.GetNamePrefixList() +} + +// +// GetNamePrefixs +// @Description: 获取整体的名称前缀信息 +// @receiver mcp +// @return *lsa.NameLsaNamePrefixList +// +func (mcp *MlsrConfigParameters) GetNamePrefixs() *lsa.NameLsaNamePrefixList { + return mcp.m_npl +} + +func (mcp *MlsrConfigParameters) GetKeyChain() { + // todo +} diff --git a/common/MlsrConfig_test.go b/common/MlsrConfig_test.go index 0a4a849..cb878b3 100644 --- a/common/MlsrConfig_test.go +++ b/common/MlsrConfig_test.go @@ -25,6 +25,55 @@ func getCurrentDirectory() string { return dir } +const testConfigPath = "D:\\" + DefaultConfFileName + +// +// TestCreatConfigFile +// @Description: 测试生成mlsrConfig文件 +// @param t +// +func TestCreatConfigFile(t *testing.T) { + testPath := "D:" + testPath += "\\" + testPath += DefaultConfFileName + + mlsrConfig := new(MlsrConfig) + mlsrConfig.mlsrConfigPath = testPath + mlsrConfig.Init() + mlsrConfig.MlsrConfigParameters.Init(mlsrConfig) + _ = mlsrConfig.Save() +} + +// +// TestMlsrConfigParameters +// @Description: 测试配置项所有参数 +// @param t +// +func TestMlsrConfigParameters(t *testing.T) { + // 1、从指定目录解析配置文件 + mlsrConfig, err := ParseConfig(testConfigPath) + if err != nil { + fmt.Println("配置文件解析错误") + } + // 2、打印配置中的普通字符串信息 + fmt.Println(mlsrConfig.ToString()) + // 3、打印配置中的特殊参数信息 + fmt.Println(mlsrConfig.ParametersToString()) + fmt.Println("!!!", mlsrConfig.MlsrConfigParameters.GetSyncUserPrefix().ToUri()+"00000") + fmt.Println("end") + // 打印邻接列表 + adjs := mlsrConfig.GetAdjacencyList() + for i := 0; i < len(adjs); i++ { + fmt.Println(adjs[i].ToString()) + } + fmt.Println("#########################################################") + // 打印名称前缀表 + prefixs := mlsrConfig.GetNamePrefixList() + for i := 0; i < len(prefixs); i++ { + fmt.Println(prefixs[i].ToString()) + } +} + // // TestMlsrConfig_ParseConfigAndSave // @Description: 测试配置文件解析和存储 @@ -34,15 +83,18 @@ func TestMlsrConfig_ParseConfigAndSave(t *testing.T) { testPath := "D:" testPath += "\\" testPath += DefaultConfFileName - fmt.Println("test mlsr conf path: "+testPath) - mlsrConfig,_ := ParseConfig(testPath) + fmt.Println("test mlsr conf path: " + testPath) + mlsrConfig, err := ParseConfig(testPath) + if err != nil { + fmt.Println("配置文件加载错误") + } // 存储 fmt.Println(mlsrConfig) _ = mlsrConfig.Save() // 取出 - mlsrConfig2,_ := ParseConfig(testPath) + mlsrConfig2, _ := ParseConfig(testPath) fmt.Println(mlsrConfig2) // 解析neighbors @@ -50,11 +102,11 @@ func TestMlsrConfig_ParseConfigAndSave(t *testing.T) { fmt.Println(neighbors) // 修改再存 - ss:=NeighborConfig{ + ss := NeighborConfig{ NeighborName: "nei", LogicFaceUri: "uri", LinkCost: 0, } - mlsrConfig2.neighbors = append(mlsrConfig2.neighbors,ss) + mlsrConfig2.neighbors = append(mlsrConfig2.neighbors, ss) _ = mlsrConfig2.Save() } diff --git a/common/MlsrLogger.go b/common/MlsrLogger.go new file mode 100644 index 0000000..06ed3d6 --- /dev/null +++ b/common/MlsrLogger.go @@ -0,0 +1,25 @@ +// Package common +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/5/16 17:03 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package common + +import "minlib/common" + +// InitLogger 日志模块初始化 +// +// @Description: +// @param config 配置文件 +// +func InitLogger(config *MlsrConfig) { + common.InitLogger(&common.LoggerParameters{ + ReportCaller: config.LogConfig.ReportCaller, + LogLevel: config.LogConfig.LogLevel, + LogFormat: config.LogConfig.LogFormat, + LogFilePath: config.LogConfig.LogFilePath, + }) +} diff --git a/extensions/TlvMlsrExtention.go b/extensions/TlvMlsrExtention.go index 62a2749..601a0a5 100644 --- a/extensions/TlvMlsrExtention.go +++ b/extensions/TlvMlsrExtention.go @@ -12,25 +12,32 @@ package extensions const ( // Lsa for MLSR - TlvMlsrLsaAdjacency = 401 // 表示MIR之间邻接关系信息的LSA - TlvMlsrLsaNamePrefix = 402 // 表示名称前缀(内容标识、身份标识等以前缀形式命名的标识)信息的LSA - TlvMlsrLsaCoordinate = 403 // 表示坐标信息的LSA + TlvMlsrLsaAdjacency = 401 // 表示MIR之间邻接关系信息的LSA + TlvMlsrLsaName = 402 // 表示名称前缀(内容标识、身份标识等以前缀形式命名的标识)信息的LSA + TlvMlsrLsaCoordinate = 403 // 表示坐标信息的LSA // 所有类型Lsa的必需部分 - TlvMlsrLsaBase = 410 // 表示LSA最基本信息:源MIR、序列号、过期时间 - TlvMlsrLsaExpirationTime = 420 // LSA过期时间 - TlvMlsrLsaSequenceNumber = 421 // LSA序列号 + TlvMlsrLsaBase = 410 // 表示LSA最基本信息:源MIR、序列号、过期时间 + TlvMlsrLsaExpirationTime = 420 // LSA过期时间 + TlvMlsrLsaSequenceNumber = 421 // LSA序列号 // AdjLsa的组件 - TlvMlsrAdjLsaLogicFaceId = 422 // logicface Id - TlvMlsrAdjLsaLinkCost = 423 // 链路开销 - TlvMlsrAdjLsaLogicFaceUri = 424 // LogicFace前缀 - TlvMlsrAdjLsaAdjacencyInfo = 425 // 单条邻接信息 - TlvMlsrAdjLsaAdjacencyList = 426 // 一个邻接LSA包含的所有邻接信息 + TlvMlsrAdjLsaLogicFaceId = 422 // logicface Id + TlvMlsrAdjLsaLinkCost = 423 // 链路开销 + TlvMlsrAdjLsaLogicFaceUri = 424 // LogicFace前缀 + TlvMlsrAdjLsaAdjacencyInfo = 425 // 单条邻接信息 + TlvMlsrAdjLsaAdjacencyList = 426 // 一个邻接LSA包含的所有邻接信息 // NameLsa的组件 - TlvMlsrNameLsaNamePrefixSource = 427 // 名称前缀的来源 - TlvMlsrNameLsaNamePrefixInfo = 428 // 单条名称前缀信息 - TlvMlsrNameLsaNamePrefixList = 429 // 一个名称LSA包含的所有名称前缀信息 -) + TlvMlsrNameLsaNamePrefixSource = 427 // 名称前缀的来源 + TlvMlsrNameLsaNamePrefixInfo = 428 // 单条名称前缀信息 + TlvMlsrNameLsaNamePrefixList = 429 // 一个名称LSA包含的所有名称前缀信息 + // route模块涉及到的组件 + TlvMlsrConnectingLogicFaceUri = 441 + TlvMlsrRouteCost = 442 + TlvMlsrNextHop = 443 + TlvMlsrNextHopList = 444 + TlvMlsrRoutingTableEntry = 445 + TlvMlsrRoutingTable = 446 +) diff --git a/go.mod b/go.mod index 45800ee..b7fd07b 100644 --- a/go.mod +++ b/go.mod @@ -5,12 +5,12 @@ go 1.16 require ( //github.com/rakanalh/scheduler v0.1 github.com/go-co-op/gocron v1.13.0 + github.com/liyue201/gostl v1.0.1 github.com/smartystreets/goconvey v1.7.2 // indirect github.com/tidwall/btree v1.2.1 // indirect github.com/tidwall/buntdb v1.2.9 github.com/tidwall/gjson v1.14.0 // indirect gopkg.in/ini.v1 v1.62.0 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect minlib v0.0.0 ) diff --git a/go.sum b/go.sum index d83584c..4583ce7 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,8 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/liyue201/gostl v1.0.1 h1:VQdvogZ90WpCb5WdG9UxS6r5ulnYEp8VfEMEZXVtpIs= +github.com/liyue201/gostl v1.0.1/go.mod h1:rgK+T1a0sQ1+CsAonfuD1J8C4iuGfOU9VAt9mmR/m10= github.com/mutecomm/go-sqlcipher/v4 v4.4.2 h1:eM10bFtI4UvibIsKr10/QT7Yfz+NADfjZYh0GKrXUNc= github.com/mutecomm/go-sqlcipher/v4 v4.4.2/go.mod h1:mF2UmIpBnzFeBdu/ypTDb/LdbS0nk0dfSN1WUsWTjMA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -30,6 +32,7 @@ github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI= @@ -71,6 +74,7 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= diff --git a/lsa/AdjLsa.go b/lsa/AdjLsa.go index 57c2547..bd38ef1 100644 --- a/lsa/AdjLsa.go +++ b/lsa/AdjLsa.go @@ -9,30 +9,152 @@ package lsa import ( + "encoding/json" + "minlib/component" "minlib/encoding" "mlsr/extensions" ) +// +// AdjLsa +// @Description: 邻接LSA +// type AdjLsa struct { encoding.SelfEncodingBase LsaBase AdjLsaAdjacenctList } +// +// innerAdjLsa +// @Description: 内置结构体之:邻接LSA +// +type innerAdjLsa struct { + LsaTypeInt int + LsaOriginRouter string + LsaSequenceNumber uint64 + LsaExpirationTime uint64 + AdjacencyInfoList []innerAdjacencyInfo +} + +// +// innerAdjacencyInfo +// @Description: 内置结构体之:邻接信息 +// +type innerAdjacencyInfo struct { + LinkCost uint64 + LogicFaceUri string + LogicFaceId uint64 + NeighborRouter string + InterestTimedOutNo uint32 + Status int +} + +// +// parseAdjLsaToInner +// @Description: 邻接LSA转内置结构体 +// @param aLsa +// @return innerAdjLsa +// @return error +// +func parseAdjLsaToInner(aLsa *AdjLsa) (innerAdjLsa, error) { + innerALSA := innerAdjLsa{} + innerALSA.LsaTypeInt = int(aLsa.GetType()) + innerALSA.LsaOriginRouter = aLsa.LsaOriginRouterIdentifier.ToUri() + innerALSA.LsaSequenceNumber = aLsa.GetSeqNo() + innerALSA.LsaExpirationTime = aLsa.GetExpirationTime() + for i := 0; i < len(aLsa.lsaAdjacencys); i++ { + info := innerAdjacencyInfo{} + info.LinkCost = aLsa.lsaAdjacencys[i].LinkCost() + info.LogicFaceUri = aLsa.lsaAdjacencys[i].LogicFaceUri() + info.LogicFaceId = aLsa.lsaAdjacencys[i].LogicFaceId() + info.NeighborRouter = aLsa.lsaAdjacencys[i].lsaNeighborRouterIdentifier.ToUri() + info.InterestTimedOutNo = aLsa.lsaAdjacencys[i].InterestTimedOutNo + info.Status = aLsa.lsaAdjacencys[i].Status + innerALSA.AdjacencyInfoList = append(innerALSA.AdjacencyInfoList, info) + } + return innerALSA, nil +} + +// +// parseInnerToAdjLsa +// @Description: 内置结构体转邻接LSA +// @param inner +// @param alsa +// @return error +// +func parseInnerToAdjLsa(inner *innerAdjLsa, alsa *AdjLsa) error { + routerIdent, err := component.CreateIdentifierByString(inner.LsaOriginRouter) + if err != nil { + return err + } + alsa.SetOriginRouter(routerIdent) + alsa.SetSeqNo(inner.LsaSequenceNumber) + alsa.SetExpirationTime(inner.LsaExpirationTime) + for i := 0; i < len(inner.AdjacencyInfoList); i++ { + realInfo := new(AdjLsaAdjacencyInfo) + realInfo.SetLinkCost(inner.AdjacencyInfoList[i].LinkCost) + realInfo.SetLogicFaceUri(inner.AdjacencyInfoList[i].LogicFaceUri) + realInfo.SetLogicFaceId(inner.AdjacencyInfoList[i].LogicFaceId) + iden, err2 := component.CreateIdentifierByString( + inner.AdjacencyInfoList[i].NeighborRouter) + if err2 != nil { + return err2 + } + realInfo.SetNeighborRouterIdentifier(iden) + realInfo.InterestTimedOutNo = inner.AdjacencyInfoList[i].InterestTimedOutNo + realInfo.Status = inner.AdjacencyInfoList[i].Status + alsa.lsaAdjacencys = append(alsa.lsaAdjacencys, realInfo) + } + return nil +} + +// +// LoadFromJsonString +// @Description: 从json字符串中加载AdjLsa +// @receiver a +// @param jsonBytes +// @return error +// +func (a *AdjLsa) LoadFromJsonString(jsonBytes []byte) error { + // 转中间结构体 + inner := innerAdjLsa{} + err := json.Unmarshal(jsonBytes, &inner) + if err != nil { + return err + } + // 转实际结构体 + err = parseInnerToAdjLsa(&inner, a) + if err != nil { + return err + } + + return nil +} + +// +// GetJsonString +// @Description: AdjLsa转json +// @receiver a +// @return []byte +// @return error +// +func (a *AdjLsa) GetJsonString() ([]byte, error) { + innerAdjLsa, err := parseAdjLsaToInner(a) + if err != nil { + return nil, err + } + b, err := json.Marshal(innerAdjLsa) + if err != nil { + return nil, err + } + return b, nil +} + func (a *AdjLsa) GetType() LsaType { return LsaADJACENCYType } -func (a *AdjLsa) GetJsonString() ([]byte,error) { - // todo - return nil,nil -} - -func (a *AdjLsa) LoadFromJsonString(jsonBytes []byte) error{ - //todo - return nil -} - // // GetAdjList // @Description: 获取邻接信息表 @@ -72,8 +194,13 @@ func (a *AdjLsa) IsEqualContent(aLsa *AdjLsa) bool { // @return string // func (a *AdjLsa) ToString() string { - // todo - return "" + var res = "" + res += "{ " + res += a.LsaBase.ToString() + res += ", " + res += a.AdjLsaAdjacenctList.ToString() + res += " }" + return res } // @@ -84,22 +211,22 @@ func (a *AdjLsa) ToString() string { // @return int // @return error // -func (a *AdjLsa) WireEncode(encoder *encoding.Encoder) (int,error) { +func (a *AdjLsa) WireEncode(encoder *encoding.Encoder) (int, error) { totalLength := 0 // TLV-Value - tmpLen,err := a.AdjLsaAdjacenctList.WireEncode(encoder) + tmpLen, err := a.AdjLsaAdjacenctList.WireEncode(encoder) if err != nil { return 0, err } - totalLength+=tmpLen - tmpLen,err = a.LsaBase.WireEncode(encoder) + totalLength += tmpLen + tmpLen, err = a.LsaBase.WireEncode(encoder) if err != nil { return 0, err } - totalLength+=tmpLen + totalLength += tmpLen // TLV-Length - tmpLen,err = encoder.PrependVarNumber(encoding.VlInt(totalLength)) + tmpLen, err = encoder.PrependVarNumber(encoding.VlInt(totalLength)) if err != nil { return 0, err } @@ -112,7 +239,7 @@ func (a *AdjLsa) WireEncode(encoder *encoding.Encoder) (int,error) { } totalLength += tmpLen - return totalLength,nil + return totalLength, nil } // @@ -134,17 +261,17 @@ func (a *AdjLsa) WireDecode(block *encoding.Block) error { } // 提取参数 - for _, parameter := range block.GetSubElements(){ + for _, parameter := range block.GetSubElements() { switch parameter.GetType() { case extensions.TlvMlsrLsaBase: - if err:= a.LsaBase.WireDecode(parameter); err != nil { + if err := a.LsaBase.WireDecode(parameter); err != nil { return err } case extensions.TlvMlsrAdjLsaAdjacencyList: - if err:= a.AdjLsaAdjacenctList.WireDecode(parameter); err != nil { + if err := a.AdjLsaAdjacenctList.WireDecode(parameter); err != nil { return err } } } return nil -} \ No newline at end of file +} diff --git a/lsa/AdjLsaAdjacencyInfo.go b/lsa/AdjLsaAdjacencyInfo.go index 68842b4..4a3eef5 100644 --- a/lsa/AdjLsaAdjacencyInfo.go +++ b/lsa/AdjLsaAdjacencyInfo.go @@ -12,22 +12,23 @@ import ( "minlib/component" "minlib/encoding" "mlsr/extensions" + "strconv" ) const ( - STATUS_UNKNOWN = -1 + STATUS_UNKNOWN = -1 STATUS_INACTIVE = 0 - STATUS_ACTIVE = 1 + STATUS_ACTIVE = 1 ) type AdjLsaAdjacencyInfo struct { - AdjLsaLinkCost // 两个路由器之间的链路成本 - AdjLsaLogicFaceUri // MIR层级对LogicFace的字符串描述,如udp://1.0.0.2,或ether:aa.aa.aa.aa... - AdjLsaLogicFaceId // MIR给LogicFace分配的uint64类型的id,如182828,nlsr可通过该id定位到哪个logicface负责传该链路数据 + AdjLsaLinkCost // 两个路由器之间的链路成本 + AdjLsaLogicFaceUri // MIR层级对LogicFace的字符串描述,如udp://1.0.0.2,或ether:aa.aa.aa.aa... + AdjLsaLogicFaceId // MIR给LogicFace分配的uint64类型的id,如182828,nlsr可通过该id定位到哪个logicface负责传该链路数据 lsaNeighborRouterIdentifier *component.Identifier // NLSR已配置的邻居路由器标识符,如/min/pkusz/group1/router1 - InterestTimedOutNo uint32 // 自从收到最后一个回复之后,记录已经失败的hello兴趣包的数目 - Status int // 表示邻居路由器的状态:未知-1、未激活0、激活1 + InterestTimedOutNo uint32 // 自从收到最后一个回复之后,记录已经失败的hello兴趣包的数目 + Status int // 表示邻居路由器的状态:未知-1、未激活0、激活1 } // @@ -41,13 +42,37 @@ type AdjLsaAdjacencyInfo struct { // @return *AdjLsaAdjacencyInfo // func NewAdjLsaAdjacencyInfo(adjLsaLinkCost AdjLsaLinkCost, adjLsaLogicFaceUri AdjLsaLogicFaceUri, - m_lsaNeighborRouterIdentifier *component.Identifier, m_interestTimedOutNo uint32, - m_status int) *AdjLsaAdjacencyInfo { + faceId AdjLsaLogicFaceId, m_lsaNeighborRouterIdentifier *component.Identifier, + m_interestTimedOutNo uint32, m_status int) *AdjLsaAdjacencyInfo { return &AdjLsaAdjacencyInfo{AdjLsaLinkCost: adjLsaLinkCost, AdjLsaLogicFaceUri: adjLsaLogicFaceUri, - lsaNeighborRouterIdentifier: m_lsaNeighborRouterIdentifier, + AdjLsaLogicFaceId: faceId, lsaNeighborRouterIdentifier: m_lsaNeighborRouterIdentifier, InterestTimedOutNo: m_interestTimedOutNo, Status: m_status} } +// +// NewAdjLsaAdjacencyInfoByInstance +// @Description: 根据已有的结构体实例,创建邻接信息结构体 +// @param adjLsaLinkCost +// @param adjLsaLogicFaceUri +// @param faceId +// @param m_lsaNeighborRouterIdentifier +// @param m_interestTimedOutNo +// @param m_status +// @return *AdjLsaAdjacencyInfo +// +func NewAdjLsaAdjacencyInfoByInstance(adjLsaLinkCost *AdjLsaLinkCost, adjLsaLogicFaceUri *AdjLsaLogicFaceUri, + faceId *AdjLsaLogicFaceId, m_lsaNeighborRouterIdentifier *component.Identifier, + m_interestTimedOutNo uint32, m_status int) *AdjLsaAdjacencyInfo { + adjRes := AdjLsaAdjacencyInfo{} + adjRes.SetLinkCost(adjLsaLinkCost.LinkCost()) + adjRes.SetLogicFaceUri(adjLsaLogicFaceUri.LogicFaceUri()) + adjRes.SetLogicFaceId(faceId.LogicFaceId()) + adjRes.SetNeighborRouterIdentifier(m_lsaNeighborRouterIdentifier) + adjRes.InterestTimedOutNo = m_interestTimedOutNo + adjRes.Status = m_status + return &adjRes +} + // // GetNeighborRouterIdentidier // @Description: 获取相邻路由器的标识 @@ -66,7 +91,7 @@ func (l *AdjLsaAdjacencyInfo) GetNeighborRouterIdentifier() *component.Identifie // func (l *AdjLsaAdjacencyInfo) SetNeighborRouterIdentifier(routerIdentifier *component.Identifier) { // todo: 验证identifier,如类型、字段是否为空等 - l.lsaNeighborRouterIdentifier= routerIdentifier + l.lsaNeighborRouterIdentifier = routerIdentifier } // @@ -78,7 +103,7 @@ func (l *AdjLsaAdjacencyInfo) SetNeighborRouterIdentifier(routerIdentifier *comp // func (l *AdjLsaAdjacencyInfo) Compare(routerIdentifier *component.Identifier) bool { // todo: 验证identifier,如类型、字段是否为空等 - if routerIdentifier.ToUri()==l.lsaNeighborRouterIdentifier.ToUri() { + if routerIdentifier.ToUri() == l.lsaNeighborRouterIdentifier.ToUri() { return true } return false @@ -92,7 +117,7 @@ func (l *AdjLsaAdjacencyInfo) Compare(routerIdentifier *component.Identifier) bo // @return bool // func (l *AdjLsaAdjacencyInfo) CompareLogicFaceUri(faceUri *AdjLsaLogicFaceUri) bool { - if faceUri.LogicFaceUri()==l.LogicFaceUri(){ + if faceUri.LogicFaceUri() == l.LogicFaceUri() { return true } return false @@ -106,7 +131,7 @@ func (l *AdjLsaAdjacencyInfo) CompareLogicFaceUri(faceUri *AdjLsaLogicFaceUri) b // @return bool // func (l *AdjLsaAdjacencyInfo) CompareLogicFaceUriString(faceUriString string) bool { - if faceUriString==l.LogicFaceUri(){ + if faceUriString == l.LogicFaceUri() { return true } return false @@ -120,12 +145,35 @@ func (l *AdjLsaAdjacencyInfo) CompareLogicFaceUriString(faceUriString string) bo // @return bool // func (l *AdjLsaAdjacencyInfo) CompareLogicFaceId(faceId uint64) bool { - if faceId==l.LogicFaceId(){ + if faceId == l.LogicFaceId() { return true } return false } +// +// ToString +// @Description: 转string +// @receiver l +// +func (l *AdjLsaAdjacencyInfo) ToString() string { + var resString string + resString += "{ LinkCost: " + resString += strconv.FormatUint(l.LinkCost(), 10) + resString += ", LogicFaceUri: " + resString += l.LogicFaceUri() + resString += ", LogicFaceId: " + resString += strconv.FormatUint(l.LogicFaceId(), 10) + resString += ", NeighborRouterIdentifier: " + resString += l.lsaNeighborRouterIdentifier.ToUri() + resString += ", InterestTimedOutNo: " + resString += strconv.Itoa(int(l.InterestTimedOutNo)) + resString += ", Status: " + resString += strconv.Itoa(l.Status) + resString += " }" + return resString +} + // // WireEncode // @Description: 线速编码 @@ -134,41 +182,41 @@ func (l *AdjLsaAdjacencyInfo) CompareLogicFaceId(faceId uint64) bool { // @return int // @return error // -func (l *AdjLsaAdjacencyInfo) WireEncode(encoder *encoding.Encoder) (int,error) { +func (l *AdjLsaAdjacencyInfo) WireEncode(encoder *encoding.Encoder) (int, error) { totalLength := 0 // TLV-Value - if tmpLen,err := l.AdjLsaLinkCost.WireEncode(encoder); err!=nil{ + if tmpLen, err := l.AdjLsaLinkCost.WireEncode(encoder); err != nil { return 0, err - }else{ + } else { totalLength += tmpLen } - if tmpLen,err := l.AdjLsaLogicFaceUri.WireEncode(encoder); err!=nil{ + if tmpLen, err := l.AdjLsaLogicFaceUri.WireEncode(encoder); err != nil { return 0, err - }else{ + } else { totalLength += tmpLen } - if tmpLen,err := l.lsaNeighborRouterIdentifier.WireEncode(encoder); err!=nil{ + if tmpLen, err := l.lsaNeighborRouterIdentifier.WireEncode(encoder); err != nil { return 0, err - }else{ + } else { totalLength += tmpLen } // TLV-Length - if tmpLen,err := encoder.PrependVarNumber(encoding.VlInt(totalLength)); err!=nil{ + if tmpLen, err := encoder.PrependVarNumber(encoding.VlInt(totalLength)); err != nil { return 0, err - }else{ + } else { totalLength += tmpLen } // TLV-Type - if tmpLen,err := encoder.PrependVarNumber(extensions.TlvMlsrAdjLsaAdjacencyInfo); err!=nil{ + if tmpLen, err := encoder.PrependVarNumber(extensions.TlvMlsrAdjLsaAdjacencyInfo); err != nil { return 0, err - }else{ + } else { totalLength += tmpLen } - return totalLength,nil + return totalLength, nil } // @@ -190,21 +238,23 @@ func (l *AdjLsaAdjacencyInfo) WireDecode(block *encoding.Block) error { } // 提取参数 - for _, parameter := range block.GetSubElements(){ + for _, parameter := range block.GetSubElements() { switch parameter.GetType() { case encoding.TlvIdentifier: - if err:= l.lsaNeighborRouterIdentifier.WireDecode(parameter); err != nil { + iden := component.Identifier{} + if err := iden.WireDecode(parameter); err != nil { return err } + l.lsaNeighborRouterIdentifier = &iden case extensions.TlvMlsrAdjLsaLogicFaceUri: - if err:= l.AdjLsaLogicFaceUri.WireDecode(parameter); err != nil { + if err := l.AdjLsaLogicFaceUri.WireDecode(parameter); err != nil { return err } case extensions.TlvMlsrAdjLsaLinkCost: - if err:= l.AdjLsaLinkCost.WireDecode(parameter); err != nil { + if err := l.AdjLsaLinkCost.WireDecode(parameter); err != nil { return err } } } return nil -} \ No newline at end of file +} diff --git a/lsa/AdjLsaAdjacencyInfo_test.go b/lsa/AdjLsaAdjacencyInfo_test.go new file mode 100644 index 0000000..8e8b77d --- /dev/null +++ b/lsa/AdjLsaAdjacencyInfo_test.go @@ -0,0 +1,130 @@ +// Package lsa +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/4/22 11:01 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package lsa + +import ( + "fmt" + "minlib/component" + "minlib/encoding" + "testing" +) + +// +// TestNewAdjLsaAdjacencyInfo +// @Description: 测试构造一条邻接信息 +// @param t +// +func TestNewAdjLsaAdjacencyInfo(t *testing.T) { + linkCost := AdjLsaLinkCost{} + linkCost.SetLinkCost(100) + faceUri := AdjLsaLogicFaceUri{} + faceUri.SetLogicFaceUri("uri") + faceId := AdjLsaLogicFaceId{} + faceId.SetLogicFaceId(77) + iden, _ := component.CreateIdentifierByString("/pku/router") + adjInfo := NewAdjLsaAdjacencyInfo(linkCost, faceUri, faceId, iden, 123, 1) + fmt.Println("cost: ", adjInfo.LinkCost()) + fmt.Println("uri: ", adjInfo.LogicFaceUri()) + fmt.Println("id: ", adjInfo.LogicFaceId()) + fmt.Println("identifier: ", iden.ToUri()) + fmt.Println(adjInfo.ToString()) +} + +// +// TestAdjLsaAdjacencyInfo_Compare +// @Description: 测试一些比较函数 +// @param t +// +func TestAdjLsaAdjacencyInfo_Compare(t *testing.T) { + // 先构造 + linkCost := AdjLsaLinkCost{} + linkCost.SetLinkCost(100) + faceUri := AdjLsaLogicFaceUri{} + faceUri.SetLogicFaceUri("uri") + faceId := AdjLsaLogicFaceId{} + faceId.SetLogicFaceId(77) + iden, _ := component.CreateIdentifierByString("/pku/router") + adjInfo := NewAdjLsaAdjacencyInfo(linkCost, faceUri, faceId, iden, 123, 1) + // 再测试:全部为true则pass + iden2, _ := component.CreateIdentifierByString("/pku/router") + fmt.Println(adjInfo.Compare(iden2)) + fmt.Println(adjInfo.CompareLogicFaceId(77)) + fmt.Println(adjInfo.CompareLogicFaceUriString("uri")) + fmt.Println(adjInfo.CompareLogicFaceUri(&faceUri)) +} + +// +// TestAdjLsaAdjacencyInfo_WireEncode +// @Description: 测试编码 +// @param t +// +func TestAdjLsaAdjacencyInfo_WireEncode(t *testing.T) { + // 先构造 + linkCost := AdjLsaLinkCost{} + linkCost.SetLinkCost(100) + faceUri := AdjLsaLogicFaceUri{} + faceUri.SetLogicFaceUri("uri") + faceId := AdjLsaLogicFaceId{} + faceId.SetLogicFaceId(77) + iden, _ := component.CreateIdentifierByString("/pku/router") + adjInfo := NewAdjLsaAdjacencyInfo(linkCost, faceUri, faceId, iden, 123, 1) + + // 构造编码器 + encoder := encoding.Encoder{} + err := encoder.EncoderReset(encoding.MaxPacketSize, 0) + if err != nil { + fmt.Println("encoder reset失败 ", err.Error()) + return + } + + // 编码并打印 + cnt, err := adjInfo.WireEncode(&encoder) + if err != nil { + fmt.Println("AdjInfo 线速编码失败 ", err.Error()) + return + } + res, err := encoder.GetBuffer() + if err != nil { + fmt.Println("获取编码结果失败 ", err.Error()) + return + } + fmt.Println(cnt) + for i := 0; i < len(res); i++ { + fmt.Print(res[i], ", ") + } + fmt.Println() + // 241, 1, 169, 33, 101, 19, 100, 2, 0, 47, 100, 4, 0, 112, 107, 117, 100, 7, 0, 114, 111, 117, 116, 101, 114, 241, 1, 168, 3, 117, 114, 105, 241, 1, 167, 1, 100, +} + +// +// TestAdjLsaAdjacencyInfo_WireDecode +// @Description: 测试线速解码 +// 注意,编码解码目前只作用于三个信息项目:链路开销、faceuri、邻接路预期标识符三个信息项 +// @param t +// +func TestAdjLsaAdjacencyInfo_WireDecode(t *testing.T) { + // 由数组构建block + var buf = [37]byte{241, 1, 169, 33, 101, 19, 100, 2, 0, 47, 100, 4, 0, 112, 107, + 117, 100, 7, 0, 114, 111, 117, 116, + 101, 114, 241, 1, 168, 3, 117, 114, 105, 241, 1, 167, 1, 100} + block, err := encoding.CreateBlockByBuffer(buf[:], true) + if err != nil { + fmt.Println("创建block失败 ", err.Error()) + return + } + + // 由Block构建结构体,并打印变量 + adjInfo := new(AdjLsaAdjacencyInfo) + err = adjInfo.WireDecode(block) + if err != nil { + fmt.Println("线性解码失败 ", err.Error()) + return + } + fmt.Println(adjInfo.ToString()) +} diff --git a/lsa/AdjLsaAdjacencyList.go b/lsa/AdjLsaAdjacencyList.go index d7b4c85..8d9010e 100644 --- a/lsa/AdjLsaAdjacencyList.go +++ b/lsa/AdjLsaAdjacencyList.go @@ -37,6 +37,18 @@ func (l *AdjLsaAdjacenctList) Insert(info *AdjLsaAdjacencyInfo) { l.lsaAdjacencys = append(l.lsaAdjacencys, info) } +// +// InsertAdjacenctList +// @Description: 插入邻接列表 +// @receiver l +// @param list +// +func (l *AdjLsaAdjacenctList) InsertAdjacenctList(list []*AdjLsaAdjacencyInfo) { + for i := 0; i < len(list); i++ { + l.Insert(list[i]) + } +} + // // getAdjList // @Description: 获取包含所有邻接信息的邻接表 @@ -47,6 +59,25 @@ func (l *AdjLsaAdjacenctList) GetAdjList() []*AdjLsaAdjacencyInfo { return l.lsaAdjacencys } +// +// ToString +// @Description: 转string +// @receiver l +// @return string +// +func (l *AdjLsaAdjacenctList) ToString() string { + var resString string + resString += "[ " + for i := 0; i < l.Size(); i++ { + resString += l.GetAdjList()[i].ToString() + if i < (l.Size() - 1) { + resString += ", " + } + } + resString += " ]" + return resString +} + // // isNeighbor // @Description: 判断某个MIR标识符是否是邻居MIR @@ -55,8 +86,8 @@ func (l *AdjLsaAdjacenctList) GetAdjList() []*AdjLsaAdjacencyInfo { // @return bool // func (l *AdjLsaAdjacenctList) IsNeighbor(routerIdentifier *component.Identifier) bool { - for i:=0; i= interestRetryNo { + // 如果邻居全部都没有激活,且兴趣包尝试次数超出最大值,就删掉本机的邻接LSA + nTimedOutNeighbors++ } } - return true + if nTimedOutNeighbors == len(l.lsaAdjacencys) { + return true + } else { + // 既没有激活邻居,又还在尝试查看邻居情况,则继续安排构建邻接LSA + return false + } } // @@ -195,11 +230,11 @@ func (l *AdjLsaAdjacenctList) IsAdjLsaBuildable(interestRetryNo uint32) bool { // @return *AdjLsaAdjacencyInfo // @return error // -func (l *AdjLsaAdjacenctList) GetAdjacency(routerIdentifier *component.Identifier) (*AdjLsaAdjacencyInfo,error){ - if index,err:=l.findIndexByRouterIdentifier(routerIdentifier);err!=nil{ - return nil,err - }else{ - return l.lsaAdjacencys[index],nil +func (l *AdjLsaAdjacenctList) GetAdjacency(routerIdentifier *component.Identifier) (*AdjLsaAdjacencyInfo, error) { + if index, err := l.findIndexByRouterIdentifier(routerIdentifier); err != nil { + return nil, err + } else { + return l.lsaAdjacencys[index], nil } } @@ -210,9 +245,9 @@ func (l *AdjLsaAdjacenctList) GetAdjacency(routerIdentifier *component.Identifie // @return int // func (l *AdjLsaAdjacenctList) GetNumOfActiveNeighbor() int { - numbers:=0 - for i:=0; i=0; i-- { - if l.lsaAdjacencys[i]==nil { + for i := len(l.lsaAdjacencys) - 1; i >= 0; i-- { + if l.lsaAdjacencys[i] == nil { continue } - tmpLen,err := l.lsaAdjacencys[i].WireEncode(encoder) + tmpLen, err := l.lsaAdjacencys[i].WireEncode(encoder) if err != nil { return 0, err } @@ -377,7 +421,7 @@ func (l *AdjLsaAdjacenctList) WireEncode(encoder *encoding.Encoder) (int,error) } totalLength += tmpLen - return totalLength,nil + return totalLength, nil } // @@ -408,4 +452,4 @@ func (l *AdjLsaAdjacenctList) WireDecode(block *encoding.Block) error { } l.lsaAdjacencys = adjacencyContainer return nil -} \ No newline at end of file +} diff --git a/lsa/AdjLsaAdjacencyList_test.go b/lsa/AdjLsaAdjacencyList_test.go new file mode 100644 index 0000000..debe648 --- /dev/null +++ b/lsa/AdjLsaAdjacencyList_test.go @@ -0,0 +1,236 @@ +// Package lsa +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/4/22 0:15 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package lsa + +import ( + "fmt" + "minlib/component" + "minlib/encoding" + "testing" +) + +// +// TestAdjLsaAdjacenctList_GetAdjList +// @Description: 测试构造、打印 +// @param t +// +func TestAdjLsaAdjacenctList_GetAdjList(t *testing.T) { + // 先构造一个info + linkCost := AdjLsaLinkCost{} + linkCost.SetLinkCost(100) + faceUri := AdjLsaLogicFaceUri{} + faceUri.SetLogicFaceUri("uri") + faceId := AdjLsaLogicFaceId{} + faceId.SetLogicFaceId(77) + iden, _ := component.CreateIdentifierByString("/pku/router1") + adjInfo := NewAdjLsaAdjacencyInfo(linkCost, faceUri, faceId, iden, 123, 1) + + // 构造Info列表 + adjInfoList := new(AdjLsaAdjacenctList) + adjInfoList.Insert(adjInfo) + + // 再加第二个info + iden2, _ := component.CreateIdentifierByString("/pku/router2") + adjInfo.SetNeighborRouterIdentifier(iden2) + adjInfoList.Insert(adjInfo) + + // 测试输出 + fmt.Println(adjInfoList.ToString()) +} + +// +// TestAdjLsaAdjacenctList_IsNeighbor +// @Description: 测试是否是邻居路由器 +// @param t +// +func TestAdjLsaAdjacenctList_IsNeighbor(t *testing.T) { + // 先构造一个info + linkCost := AdjLsaLinkCost{} + linkCost.SetLinkCost(100) + faceUri := AdjLsaLogicFaceUri{} + faceUri.SetLogicFaceUri("uri") + faceId := AdjLsaLogicFaceId{} + faceId.SetLogicFaceId(77) + iden, _ := component.CreateIdentifierByString("/pku/router1") + adjInfo := NewAdjLsaAdjacencyInfo(linkCost, faceUri, faceId, iden, 123, 1) + adjInfoList := new(AdjLsaAdjacenctList) + adjInfoList.Insert(adjInfo) + + // 测试某路由器是否是邻居节点 + fmt.Println(adjInfoList.IsNeighbor(iden)) + iden2, _ := component.CreateIdentifierByString("/pku/router2") + fmt.Println(adjInfoList.IsNeighbor(iden2)) +} + +// +// TestAdjLsaAdjacenctList_IncrementTimedOutInterestCount +// @Description: 测试增加、设置、获取已经失败的hello兴趣包的数目 +// @param t +// +func TestAdjLsaAdjacenctList_IncrementTimedOutInterestCount(t *testing.T) { + // 先构造一个info + linkCost := AdjLsaLinkCost{} + linkCost.SetLinkCost(100) + faceUri := AdjLsaLogicFaceUri{} + faceUri.SetLogicFaceUri("uri") + faceId := AdjLsaLogicFaceId{} + faceId.SetLogicFaceId(77) + iden, _ := component.CreateIdentifierByString("/pku/router1") + adjInfo := NewAdjLsaAdjacencyInfo(linkCost, faceUri, faceId, iden, 123, 1) + adjInfoList := new(AdjLsaAdjacenctList) + adjInfoList.Insert(adjInfo) + + // 测试增加已经失败的hello兴趣包的数目 + _ = adjInfoList.IncrementTimedOutInterestCount(iden) + fmt.Println(adjInfoList.ToString()) + + // 测试设置 + _ = adjInfoList.SetTimedOutInterestCount(iden, 10) + fmt.Println(adjInfoList.ToString()) + + // 测试获取 + fmt.Println(adjInfoList.GetTimedOutInterestCount(iden)) +} + +// +// TestAdjLsaAdjacenctList_GetStatusOfNeighbor +// @Description: 测试操作邻居路由器的状态 +// @param t +// +func TestAdjLsaAdjacenctList_GetStatusOfNeighbor(t *testing.T) { + // 先构造一个info + linkCost := AdjLsaLinkCost{} + linkCost.SetLinkCost(100) + faceUri := AdjLsaLogicFaceUri{} + faceUri.SetLogicFaceUri("uri") + faceId := AdjLsaLogicFaceId{} + faceId.SetLogicFaceId(77) + iden, _ := component.CreateIdentifierByString("/pku/router1") + adjInfo := NewAdjLsaAdjacencyInfo(linkCost, faceUri, faceId, iden, 123, 1) + adjInfoList := new(AdjLsaAdjacenctList) + adjInfoList.Insert(adjInfo) + + // 测试获取状态 + fmt.Println(adjInfoList.GetStatusOfNeighbor(iden)) + + // 设置再获取 + adjInfoList.SetStatusOfNeighbor(iden, 2) + fmt.Println(adjInfoList.GetStatusOfNeighbor(iden)) +} + +// +// TestAdjLsaAdjacenctList_FindAdjacencyByRouterIdentifier +// @Description: 测试获取邻接信息 +// @param t +// +func TestAdjLsaAdjacenctList_FindAdjacencyByRouterIdentifier(t *testing.T) { + // 先构造一个info + linkCost := AdjLsaLinkCost{} + linkCost.SetLinkCost(100) + faceUri := AdjLsaLogicFaceUri{} + faceUri.SetLogicFaceUri("uri") + faceId := AdjLsaLogicFaceId{} + faceId.SetLogicFaceId(77) + iden, _ := component.CreateIdentifierByString("/pku/router1") + adjInfo := NewAdjLsaAdjacencyInfo(linkCost, faceUri, faceId, iden, 123, 1) + adjInfoList := new(AdjLsaAdjacenctList) + adjInfoList.Insert(adjInfo) + + // 获取 + fmt.Println(adjInfoList.FindAdjacencysByRouterIdentifier(iden)[0].ToString()) + //fmt.Println(adjInfoList.FindAdjacencyByRouterIdentifier(iden)[1].ToString()) + fmt.Println(adjInfoList.FindAdjacencyByLogicFaceId(&faceId)[0].ToString()) + fmt.Println(adjInfoList.FindAdjacencyByLogicFaceUri(&faceUri)[0].ToString()) +} + +// +// TestAdjLsaAdjacenctList_WireEncode +// @Description: 测试编码 +// @param t +// +func TestAdjLsaAdjacenctList_WireEncode(t *testing.T) { + // 先构造一个info + linkCost := AdjLsaLinkCost{} + linkCost.SetLinkCost(100) + faceUri := AdjLsaLogicFaceUri{} + faceUri.SetLogicFaceUri("uri") + faceId := AdjLsaLogicFaceId{} + faceId.SetLogicFaceId(77) + iden, _ := component.CreateIdentifierByString("/pku/router1") + adjInfo := NewAdjLsaAdjacencyInfo(linkCost, faceUri, faceId, iden, 123, 1) + // 构造Info列表 + adjInfoList := new(AdjLsaAdjacenctList) + adjInfoList.Insert(adjInfo) + // 再加第二个info + iden2, _ := component.CreateIdentifierByString("/pku/router2") + adjInfo2 := AdjLsaAdjacencyInfo{ + AdjLsaLinkCost: adjInfo.AdjLsaLinkCost, + AdjLsaLogicFaceUri: adjInfo.AdjLsaLogicFaceUri, + AdjLsaLogicFaceId: adjInfo.AdjLsaLogicFaceId, + lsaNeighborRouterIdentifier: iden2, + InterestTimedOutNo: 111, + Status: 0, + } + adjInfoList.Insert(&adjInfo2) + fmt.Println(adjInfoList.ToString()) + + // 构造编码器 + encoder := encoding.Encoder{} + err := encoder.EncoderReset(encoding.MaxPacketSize, 0) + if err != nil { + fmt.Println("encoder reset失败 ", err.Error()) + return + } + + // 编码并打印 + cnt, err := adjInfoList.WireEncode(&encoder) + if err != nil { + fmt.Println("LsaBase 线速编码失败 ", err.Error()) + return + } + res, err := encoder.GetBuffer() + if err != nil { + fmt.Println("获取编码结果失败 ", err.Error()) + return + } + fmt.Println(cnt) + for i := 0; i < len(res); i++ { + fmt.Print(res[i], ", ") + } + fmt.Println() +} + +// +// TestAdjLsaAdjacenctList_WireDecode +// @Description: 测试解码 +// @param t +// +func TestAdjLsaAdjacenctList_WireDecode(t *testing.T) { + // 由数组构建block + var buf = [80]byte{241, 1, 170, 76, 241, 1, 169, 34, 101, 20, 100, 2, + 0, 47, 100, 4, 0, 112, 107, 117, 100, 8, 0, 114, 111, 117, 116, + 101, 114, 49, 241, 1, 168, 3, 117, 114, 105, 241, 1, 167, 1, 100, + 241, 1, 169, 34, 101, 20, 100, 2, 0, 47, 100, 4, 0, 112, 107, 117, + 100, 8, 0, 114, 111, 117, 116, 101, 114, 50, + 241, 1, 168, 3, 117, 114, 105, 241, 1, 167, 1, 100} + block, err := encoding.CreateBlockByBuffer(buf[:], true) + if err != nil { + fmt.Println("创建block失败 ", err.Error()) + return + } + + // 由Block构建结构体,并打印变量 + base := new(AdjLsaAdjacenctList) + err = base.WireDecode(block) + if err != nil { + fmt.Println("线性解码失败 ", err.Error()) + return + } + fmt.Println(base.ToString()) +} diff --git a/lsa/AdjLsaLogicFaceUri.go b/lsa/AdjLsaLogicFaceUri.go index 546247f..928fc97 100644 --- a/lsa/AdjLsaLogicFaceUri.go +++ b/lsa/AdjLsaLogicFaceUri.go @@ -33,12 +33,12 @@ func (l *AdjLsaLogicFaceUri) LogicFaceUri() string { } // -// setLogicFaceUri +// SetLogicFaceUri // @Description: 设置LogicFaceUri // @receiver l // @param logicFaceUri // -func (l *AdjLsaLogicFaceUri) setLogicFaceUri(logicFaceUri string) { +func (l *AdjLsaLogicFaceUri) SetLogicFaceUri(logicFaceUri string) { l.CommonString.SetValue(logicFaceUri) } @@ -50,7 +50,7 @@ func (l *AdjLsaLogicFaceUri) setLogicFaceUri(logicFaceUri string) { // @return int // @return error // -func (l *AdjLsaLogicFaceUri) WireEncode(encoder *encoding.Encoder) (int,error) { +func (l *AdjLsaLogicFaceUri) WireEncode(encoder *encoding.Encoder) (int, error) { l.CommonString.SetTlvType(extensions.TlvMlsrAdjLsaLogicFaceUri) return l.CommonString.WireEncode(encoder) } @@ -64,7 +64,7 @@ func (l *AdjLsaLogicFaceUri) WireEncode(encoder *encoding.Encoder) (int,error) { // func (l *AdjLsaLogicFaceUri) WireDecode(block *encoding.Block) error { // 检查type是否正确 - if err := encoding.ExpectType(block.GetType(),extensions.TlvMlsrAdjLsaLogicFaceUri); err!=nil { + if err := encoding.ExpectType(block.GetType(), extensions.TlvMlsrAdjLsaLogicFaceUri); err != nil { return err } return l.CommonString.WireDecode(block) diff --git a/lsa/AdjLsaLogicFaceUri_test.go b/lsa/AdjLsaLogicFaceUri_test.go index 1f7762a..24d347f 100644 --- a/lsa/AdjLsaLogicFaceUri_test.go +++ b/lsa/AdjLsaLogicFaceUri_test.go @@ -21,7 +21,7 @@ import ( // func TestAdjLsaLogicFaceUri_LogicFaceUri(t *testing.T) { testCPU := AdjLsaLogicFaceUri{} - testCPU.setLogicFaceUri("url") + testCPU.SetLogicFaceUri("url") fmt.Println(testCPU.LogicFaceUri()) } @@ -33,7 +33,7 @@ func TestAdjLsaLogicFaceUri_LogicFaceUri(t *testing.T) { func TestAdjLsaLogicFaceUri_WireEncode(t *testing.T) { testCPU := new(AdjLsaLogicFaceUri) fmt.Println(testCPU.LogicFaceUri()) - testCPU.setLogicFaceUri("url") + testCPU.SetLogicFaceUri("url") fmt.Println(testCPU.LogicFaceUri()) //testCPC.SetTlvType(5) fmt.Println(testCPU.Value()) @@ -72,7 +72,7 @@ func TestAdjLsaLogicFaceUri_WireDecode(t *testing.T) { fmt.Println("interest", testCPU.Value()) //testCPC.SetTlvType(5) fmt.Println(testCPU.TlvType()) - testCPU.setLogicFaceUri("url") + testCPU.SetLogicFaceUri("url") fmt.Println("interest", testCPU.Value()) fmt.Println("interest", testCPU.IsInitial()) var encoder encoding.Encoder diff --git a/lsa/AdjLsa_test.go b/lsa/AdjLsa_test.go new file mode 100644 index 0000000..ee00f49 --- /dev/null +++ b/lsa/AdjLsa_test.go @@ -0,0 +1,198 @@ +// Package lsa +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/4/22 11:01 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package lsa + +import ( + "encoding/json" + "fmt" + common2 "minlib/common" + "minlib/component" + "minlib/encoding" + "testing" +) + +func TestAdjLsa_JsonConv(t *testing.T) { + // 构造基本部分 + base := new(AdjLsa) + base.SetLsaExpirationTime(2000) + base.SetLsaSequenceNumber(1234) + base.LsaOriginRouterIdentifier, _ = component.CreateIdentifierByString("/min/pku") + // 构造邻接信息部分 + // 先构造一个info + linkCost := AdjLsaLinkCost{} + linkCost.SetLinkCost(100) + faceUri := AdjLsaLogicFaceUri{} + faceUri.SetLogicFaceUri("uri") + faceId := AdjLsaLogicFaceId{} + faceId.SetLogicFaceId(77) + iden, _ := component.CreateIdentifierByString("/pku/router1") + adjInfo := NewAdjLsaAdjacencyInfo(linkCost, faceUri, faceId, iden, 123, 1) + base.Insert(adjInfo) + // 再加第二个info + iden2, _ := component.CreateIdentifierByString("/pku/router2") + adjInfo2 := AdjLsaAdjacencyInfo{ + AdjLsaLinkCost: adjInfo.AdjLsaLinkCost, + AdjLsaLogicFaceUri: adjInfo.AdjLsaLogicFaceUri, + AdjLsaLogicFaceId: adjInfo.AdjLsaLogicFaceId, + lsaNeighborRouterIdentifier: iden2, + InterestTimedOutNo: 111, + Status: 0, + } + base.Insert(&adjInfo2) + // 打印该adjlsa + common2.LogInfo(base.ToString()) + + // 转byte[]类型的json + jsonBytes, err := json.Marshal(&base) + if err != nil { + common2.LogInfo("邻接LSA json 转换失败") + } + common2.LogInfo("直接用Marshal将邻接LSA为Json: " + string(jsonBytes)) + + jsonBytes, err = base.GetJsonString() + common2.LogInfo("通过中间结构体将邻接LSA转为json: " + string(jsonBytes)) + + // 从Json转回来 + newAdjLsa := new(AdjLsa) + err = newAdjLsa.LoadFromJsonString(jsonBytes) + if err != nil { + common2.LogInfo("邻接LSA json 转换失败") + } + common2.LogInfo("通过中间结构体将json转为邻接LSA: " + newAdjLsa.ToString()) +} + +// +// TestAdjLsa_New +// @Description: 构造 +// @param t +// +func TestAdjLsa_New(t *testing.T) { + // 构造基本部分 + base := new(AdjLsa) + base.SetLsaExpirationTime(2000) + base.SetLsaSequenceNumber(1234) + base.LsaOriginRouterIdentifier, _ = component.CreateIdentifierByString("/min/pku") + // 构造邻接信息部分 + // 先构造一个info + linkCost := AdjLsaLinkCost{} + linkCost.SetLinkCost(100) + faceUri := AdjLsaLogicFaceUri{} + faceUri.SetLogicFaceUri("uri") + faceId := AdjLsaLogicFaceId{} + faceId.SetLogicFaceId(77) + iden, _ := component.CreateIdentifierByString("/pku/router1") + adjInfo := NewAdjLsaAdjacencyInfo(linkCost, faceUri, faceId, iden, 123, 1) + base.Insert(adjInfo) + // 再加第二个info + iden2, _ := component.CreateIdentifierByString("/pku/router2") + adjInfo2 := AdjLsaAdjacencyInfo{ + AdjLsaLinkCost: adjInfo.AdjLsaLinkCost, + AdjLsaLogicFaceUri: adjInfo.AdjLsaLogicFaceUri, + AdjLsaLogicFaceId: adjInfo.AdjLsaLogicFaceId, + lsaNeighborRouterIdentifier: iden2, + InterestTimedOutNo: 111, + Status: 0, + } + base.Insert(&adjInfo2) + + // 打印该adjlsa + fmt.Println(base.ToString()) +} + +// +// TestAdjLsa_WireEncode +// @Description: 测试线速编码 +// @param t +// +func TestAdjLsa_WireEncode(t *testing.T) { + // 构造LSA + base := new(AdjLsa) + base.SetLsaExpirationTime(2000) + base.SetLsaSequenceNumber(1234) + base.LsaOriginRouterIdentifier, _ = component.CreateIdentifierByString("/min/pku") + // 构造邻接信息部分 + // 先构造一个info + linkCost := AdjLsaLinkCost{} + linkCost.SetLinkCost(100) + faceUri := AdjLsaLogicFaceUri{} + faceUri.SetLogicFaceUri("uri") + faceId := AdjLsaLogicFaceId{} + faceId.SetLogicFaceId(77) + iden, _ := component.CreateIdentifierByString("/pku/router1") + adjInfo := NewAdjLsaAdjacencyInfo(linkCost, faceUri, faceId, iden, 123, 1) + base.Insert(adjInfo) + // 再加第二个info + iden2, _ := component.CreateIdentifierByString("/pku/router2") + adjInfo2 := AdjLsaAdjacencyInfo{ + AdjLsaLinkCost: adjInfo.AdjLsaLinkCost, + AdjLsaLogicFaceUri: adjInfo.AdjLsaLogicFaceUri, + AdjLsaLogicFaceId: adjInfo.AdjLsaLogicFaceId, + lsaNeighborRouterIdentifier: iden2, + InterestTimedOutNo: 111, + Status: 0, + } + base.Insert(&adjInfo2) + + // 构造编码器 + encoder := encoding.Encoder{} + err := encoder.EncoderReset(encoding.MaxPacketSize, 0) + if err != nil { + fmt.Println("encoder reset失败 ", err.Error()) + return + } + + // 编码并打印 + cnt, err := base.WireEncode(&encoder) + if err != nil { + fmt.Println("LsaBase 线速编码失败 ", err.Error()) + return + } + res, err := encoder.GetBuffer() + if err != nil { + fmt.Println("获取编码结果失败 ", err.Error()) + return + } + fmt.Println(cnt) + for i := 0; i < len(res); i++ { + fmt.Print(res[i], ", ") + } + fmt.Println() +} + +// +// TestAdjLsa_WireDecode +// @Description: 测试线速解码 +// @param t +// +func TestAdjLsa_WireDecode(t *testing.T) { + // 由数组构建block + var buf = [118]byte{241, 1, 145, 114, 241, 1, 154, 30, 101, 16, 100, + 2, 0, 47, 100, 4, 0, 109, 105, 110, 100, 4, 0, 112, 107, 117, + 241, 1, 165, 2, 4, 210, 241, 1, 164, 2, 7, 208, 241, 1, 170, + 76, 241, 1, 169, 34, 101, 20, 100, 2, 0, 47, 100, 4, 0, 112, + 107, 117, 100, 8, 0, 114, 111, 117, 116, 101, 114, 49, 241, + 1, 168, 3, 117, 114, 105, 241, 1, 167, 1, 100, 241, 1, 169, + 34, 101, 20, 100, 2, 0, 47, 100, 4, 0, 112, 107, 117, 100, + 8, 0, 114, 111, 117, 116, 101, 114, 50, + 241, 1, 168, 3, 117, 114, 105, 241, 1, 167, 1, 100} + block, err := encoding.CreateBlockByBuffer(buf[:], true) + if err != nil { + fmt.Println("创建block失败 ", err.Error()) + return + } + + // 由Block构建结构体,并打印变量 + base := new(AdjLsa) + err = base.WireDecode(block) + if err != nil { + fmt.Println("线性解码失败 ", err.Error()) + return + } + fmt.Println(base.ToString()) +} diff --git a/lsa/NameLsa.go b/lsa/NameLsa.go index ca6a6db..7146f2e 100644 --- a/lsa/NameLsa.go +++ b/lsa/NameLsa.go @@ -15,6 +15,10 @@ import ( "mlsr/extensions" ) +// +// NameLsa +// @Description: 名称LSA +// type NameLsa struct { encoding.SelfEncodingBase LsaBase @@ -23,19 +27,23 @@ type NameLsa struct { // // innerNameLsa -// @Description: 转json所用的中间结构体 +// @Description: 内置结构体之:名称LSA // type innerNameLsa struct { - LsaTypeInt int - LsaOriginRouter string + LsaTypeInt int + LsaOriginRouter string LsaSequenceNumber uint64 LsaExpirationTime uint64 - NamePrefixList []innerNamePrefixInfo + NamePrefixList []innerNamePrefixInfo } +// +// innerNamePrefixInfo +// @Description: 内置结构体之:名称前缀信息 +// type innerNamePrefixInfo struct { NamePrefix string - Sources []string + Sources []string } // @@ -45,19 +53,19 @@ type innerNamePrefixInfo struct { // @return innerNameLsa // @return error // -func parseNameLsaToInner(nLsa *NameLsa) (innerNameLsa,error) { +func parseNameLsaToInner(nLsa *NameLsa) (innerNameLsa, error) { innerNLSA := innerNameLsa{} innerNLSA.LsaTypeInt = int(nLsa.GetType()) innerNLSA.LsaOriginRouter = nLsa.LsaOriginRouterIdentifier.ToUri() innerNLSA.LsaSequenceNumber = nLsa.GetSeqNo() innerNLSA.LsaExpirationTime = nLsa.GetExpirationTime() - for i:=0;i/nlsr/LSA//// @@ -102,10 +194,14 @@ func (l *Lsdb) ScheduleAdjLsaBuild() { // @receiver l // @param interest // -func (l *Lsdb) ProcessInterest(interest *packet.Interest) { +func (l *Lsdb) ProcessInterest(interest *packet.Interest) { } -func (l *Lsdb) ProcessGPPkt(pkt *packet.GPPkt) { +func (l *Lsdb) processInterestForLsa(interest *packet.Interest, + originRouterIdentifier *component.Identifier, lsaType lsa.LsaType, seqNo uint64) { +} + +func (l *Lsdb) ProcessGPPkt(pkt *packet.GPPkt) { } // @@ -138,12 +234,15 @@ func (l *Lsdb) GetSync() *communication.SyncLogicHandler { // func (l *Lsdb) FindLsa(routerIdentifier *component.Identifier, lsaType lsa.LsaType) lsa.ILsa { - // todo - return &lsa.NameLsa{} + lsaGet, err := l.GetLSAByNameAndType(routerIdentifier, lsaType) + if err != nil { + lsaGet = nil + } + return lsaGet } // -// IsLsaNew +// isLsaNew // @Description: 返回来自某个路由器的seq是否表示新的LSA。 // @receiver l // @param routerIdentifier @@ -151,23 +250,110 @@ func (l *Lsdb) FindLsa(routerIdentifier *component.Identifier, // @param seqNo // @return bool // -func (l *Lsdb) IsLsaNew(routerIdentifier *component.Identifier, - lsaType lsa.LsaType,seqNo uint64) bool { - lsa := l.FindLsa(routerIdentifier,lsaType) - if lsa.GetSeqNo() >= seqNo{ - return false +func (l *Lsdb) isLsaNew(routerIdentifier *component.Identifier, + lsaType lsa.LsaType, seqNo uint64) bool { + iLsa := l.FindLsa(routerIdentifier, lsaType) + // 没找到,说明是最新的 + if iLsa == nil { + return true } - return true + // 找到了,且LSDB中的该lsa序列号小于要查的lsa,说明是最新的 + if iLsa.GetSeqNo() < seqNo { + return true + } + return false } -func (l *Lsdb) InstallLsa(lsa lsa.ILsa) { +// +// IsLsaNew +// @Description: 返回来自某个路由器的seq是否表示新的LSA。 +// @receiver l +// @param fLsa +// @return bool +// +func (l *Lsdb) IsLsaNew(fLsa lsa.ILsa) bool { + return l.isLsaNew(fLsa.GetOriginRouter(), fLsa.GetType(), fLsa.GetSeqNo()) } -func (l* Lsdb) RemoveLsa(routerIdentifier *component.Identifier, +// +// InstallLsa +// @Description: 将LSA安装到lsdb中 +// @receiver l +// @param lsa +// +func (l *Lsdb) InstallLsa(lsa lsa.ILsa) { + timeToExpire := l.lsaRefreshTime + + // 判断不是本机LSA时,调整超期时间 + if lsa.GetOriginRouter().ToUri() != l.thisRouterPrefix.ToUri() { + // 如果该其他路由器发来的LSA还没超时,则将超时时间间隔设置为该LSA剩余存活时间 + if l.uintToTime(lsa.GetExpirationTime()).After(time.Now()) { + timeToExpire = l.uintToTime(lsa.GetExpirationTime()).Sub(time.Now()) + } + } + + // 查看数据库中是否已有该LSA源路由器所发出的LSA + lsaInDb := l.FindLsa(lsa.GetOriginRouter(), lsa.GetType()) + if lsaInDb == nil { + // 如果没有: + common2.LogDebug("Adding Lsa ", lsa.ToString()) + // 1. 就插入该LSA + err := l.Emplace(lsa) + if err != nil { + common2.LogError("Add Lsa error", lsa.ToString()) + return + } + // 2. 触发LSDB安装LSA的动作信号 + // todo: onLsdbModified(lsa, LsdbUpdate::INSTALLED, {}, {}); + // 3. 设置LSA超期时间事件 + l.ScheduleLsaExpiration(lsa, timeToExpire) + } else if lsaInDb.GetSeqNo() < lsa.GetSeqNo() { + // 如果查到了,而且传入的这个LSA比数据库中的更新,我们将更新数据库 + common2.LogDebug("Updating Lsa ", lsaInDb.ToString()) + // 1. 就插入该LSA + err := l.Emplace(lsa) + if err != nil { + common2.LogError("Add Lsa error", lsa.ToString()) + return + } + // 2. 触发LSDB更新LSA的动作信号 + // todo: onLsdbModified(lsa, LsdbUpdate::UPDATED, namesToAdd, namesToRemove); + // 3. 设置LSA超期时间事件 + l.ScheduleLsaExpiration(lsa, timeToExpire) + common2.LogDebug("Updated Lsa ", lsa.ToString()) + } +} + +// +// RemoveLsa +// @Description: 从LSDB中删除LSA +// @receiver l +// @param lsa +// +func (l *Lsdb) RemoveLsa(lsa lsa.ILsa) { + err := l.LsaContainer.EraseLsa(lsa) + if err != nil { + common2.LogError("") + return + } + common2.LogDebug("Removed LSA: ", lsa.ToString()) +} + +// +// RemoveLsaByNameAndType +// @Description: 删除lsa +// @receiver l +// @param routerIdentifier +// @param lsaType +// +func (l *Lsdb) RemoveLsaByNameAndType(routerIdentifier *component.Identifier, lsaType lsa.LsaType) { -} - -func (l* Lsdb) RemoveLsaByContainerIndex(index int) { + err := l.LsaContainer.EraseLsaByNameAndType(routerIdentifier, lsaType) + if err != nil { + common2.LogError("") + return + } + common2.LogDebug("Removed LSA: ", routerIdentifier.ToUri()) } // @@ -176,7 +362,43 @@ func (l* Lsdb) RemoveLsaByContainerIndex(index int) { // 只有当路由器的所有邻居状态是已知的时,才可以构建它的邻接LSA。 // @receiver l // -func (l *Lsdb) BuildAdjLsa() { +func (l *Lsdb) BuildAdjLsa() { + common2.LogDebug("buildAdjLsa called") + + // 构建邻接lsa的任务启动,那么安排构建就变为false + l.isBuildAdjLsaScheduled = false + + // 根据配置文件中邻接表的状态,执行不同的动作 + if l.mlsrConfig.GetAdjacencys().IsAdjLsaBuildable(uint32(l.mlsrConfig.GetHelloRetries())) { + // 只有在有计划的情况下才执行adjLsa构建 + adjBuildCountCache := l.adjBuildCount + if adjBuildCountCache > 0 { + // 只有有安排时,构建邻接lsa + if l.mlsrConfig.GetAdjacencys().GetNumOfActiveNeighbor() > 0 { + // 有激活状态的邻居,则安装邻接LSA + common2.LogDebug("Building and installing own Adj LSA") + l.BuildAndInstallOwnAdjLsa() + } else { + // 我们没有活跃的邻居,这意味着没有人可以穿过我们。 + // 因此,请删除LSDB中的条目。这会阻止此路由器刷新LSA,最终导致其他路由器也将其删除。 + common2.LogDebug("Removing own Adj LSA; no ACTIVE neighbors") + l.RemoveLsaByNameAndType(l.thisRouterPrefix, lsa.LsaADJACENCYType) + } + // 如果在构建adj LSA的过程中,FIB必须等待兴趣响应, + // 那么计划的adj LSA构建的数量可能会发生变化,因此我们不应该只将其设置为0。 + l.adjBuildCount = l.adjBuildCount - adjBuildCountCache + } + } else { + // 我们仍在等待了解某个邻居的邻接状态,因此请安排稍后的构建(当有望完成时) + l.isBuildAdjLsaScheduled = true + scheTime := l.mlsrConfig.GetHelloTimeout() * time.Duration(l.mlsrConfig.GetHelloRetries()) + job, err := l.scheduler.ScheduleTaskAfterDuration(scheTime, 1).Do(l.BuildAdjLsa) + job.LimitRunsTo(1) + l.scheduler.StartAsync() + if err != nil { + common2.LogError("Scheduling BuildAdjLsa error, ", err.Error()) + } + } } // @@ -184,7 +406,21 @@ func (l *Lsdb) BuildAdjLsa() { // @Description: 为当前路由器构建并安装一个邻接LSA // @receiver l // -func (l *Lsdb) BuildAndInstallOwnAdjLsa() { +func (l *Lsdb) BuildAndInstallOwnAdjLsa() { + // 根据配置文件,构造本机的名称LSA + adjLsa := new(lsa.AdjLsa) + adjLsa.SetOriginRouter(l.thisRouterPrefix) + adjLsa.SetSeqNo(l.sequencingManager.GetAdjLsaSeq() + 1) // 序列号 + adjLsa.SetLsaExpirationTime(l.getLsaExpirationTimePoint()) // 超期时间 + adjLsa.AdjLsaAdjacenctList.InsertAdjacenctList(l.mlsrConfig.GetAdjacencyList()) // 名称前缀列表 + // 刷新序列号管理器中的名称LSA序列号 + l.sequencingManager.IncreaseAdjLsaSeq() + + // todo: 使用sync通知路由更新 + // m_sync.publishRoutingUpdate(Lsa::Type::ADJACENCY, m_sequencingManager.getAdjLsaSeq()); + + // 安装lsa到LSDB + l.InstallLsa(adjLsa) } // @@ -192,19 +428,69 @@ func (l *Lsdb) BuildAndInstallOwnAdjLsa() { // @Description: 在调度器中安排一个刷新或过期事件 // @receiver l // -func (l *Lsdb) ScheduleLsaExpiration(lsa lsa.ILsa,duration *time.Duration) { - +func (l *Lsdb) ScheduleLsaExpiration(lsa lsa.ILsa, duration time.Duration) { + // 在指定间隔的几秒后触发刷新或过期事件 + duration = duration + GRACE_PERIOD + // 安排LSA在指定时间间隔后进行删除 + job, err := l.scheduler.ScheduleTaskAfterDuration(duration, 1).Do(l.ExpireOrRefreshLsa, lsa) + job.LimitRunsTo(1) + l.scheduler.StartAsync() + if err != nil { + common2.LogError("Scheduling Lsa Expiration error, ", err.Error()) + } } -func (l* Lsdb) ExpireOrRefreshLsa(lsa lsa.ILsa) { -} +// +// ExpireOrRefreshLsa +// @Description: LSA过期时间触发后,执行该函数 +// @receiver l +// @param lsa +// +func (l *Lsdb) ExpireOrRefreshLsa(lsa lsa.ILsa) { + common2.LogDebug("ExpireOrRefreshLsa called for ", lsa.ToString()) -func (l *Lsdb) ProcessInterestForLsa(interest *packet.Interest, - originRouterIdentifier *component.Identifier,lsaType lsa.LsaType,seqNo uint64) { + lsaInDb := l.FindLsa(lsa.GetOriginRouter(), lsa.GetType()) + if lsaInDb != nil { + common2.LogDebug("LSA Exists with seq no: ", lsaInDb.GetSeqNo()) + // 只有DB中LSA序列号依然是传入调度器的LSA序列号时,才执行删除或更新动作。否则,该LSA已是新的,则不应修改它。 + if lsaInDb.GetSeqNo() == lsa.GetSeqNo() { + if lsaInDb.GetOriginRouter().ToUri() == l.thisRouterPrefix.ToUri() { + // 如果是本机LSA,由于它不能被删除,因此我们只继续更新它 + // 1. 更新 + common2.LogDebug("Own lsa refreshing, ", lsaInDb.ToString()) + lsaInDb.SetSeqNo(lsaInDb.GetSeqNo() + 1) + l.sequencingManager.SetLsaSeq(lsaInDb.GetSeqNo()+1, lsaInDb.GetType()) + lsaInDb.SetExpirationTime(l.getLsaExpirationTimePoint()) + err := l.Emplace(lsaInDb) + if err != nil { + common2.LogError("Own lsa refresh error, ", err.Error()) + return + } + common2.LogDebug("Own lsa refreshed, ", lsaInDb.ToString()) + // 2. 安排新的超期事件 + l.ScheduleLsaExpiration(lsaInDb, l.lsaRefreshTime) + // 3. todo: 将lsa更新通知给sync + // m_sync.publishRoutingUpdate(lsaPtr->getType(), m_sequencingManager.getLsaSeq(lsaPtr->getType())); + } else { + // 如果是其他路由器的LSA,我们将删除它。 + // 更新其他路由器的LSA是通过min-sync实现的,而不是超期触发。 + common2.LogDebug("Other's LSA, so removing from LSDB, lsa is ", lsa.ToString()) + l.RemoveLsa(lsaInDb) + } + } + } } +// +// ExpressInterest +// @Description: 发送请求其它路由器LSA的兴趣包 +// @receiver l +// @param interestName +// @param timeOut +// @param deadline +// func (l *Lsdb) ExpressInterest(interestName *component.Identifier, - timeOut uint32,deadline *time.Time) { + timeOut uint32, deadline *time.Time) { } // @@ -219,9 +505,9 @@ func (l *Lsdb) ExpressInterest(interestName *component.Identifier, // @param lsaName // @param seqNo // -func (l *Lsdb) OnFetchLsaError(errorCode uint32,msg string, - interestName *component.Identifier,retransmitNo uint32, - deadline *time.Time,lsaName *component.Identifier,seqNo uint64) { +func (l *Lsdb) onFetchLsaError(errorCode uint32, msg string, + interestName *component.Identifier, retransmitNo uint32, + deadline *time.Time, lsaName *component.Identifier, seqNo uint64) { } // @@ -233,11 +519,16 @@ func (l *Lsdb) OnFetchLsaError(errorCode uint32,msg string, // @param buffer // @param interestName // -func (l *Lsdb) AfterFetchLsa(buffer bytes.Buffer,interestName *component.Identifier) { +func (l *Lsdb) afterFetchLsa(buffer bytes.Buffer, interestName *component.Identifier) { } -func (l *Lsdb) getLsaExpirationTimePoint() time.Time { - // todo - return time.Time{} +func (l *Lsdb) getLsaExpirationTimePoint() uint64 { + timeExpire := time.Now() + timeExpire = timeExpire.Add(l.mlsrConfig.GetRouterDeadInterval()) + + return uint64(timeExpire.UnixMilli()) } +func (l *Lsdb) uintToTime(uintTime uint64) time.Time { + return time.UnixMilli(int64(uintTime)) +} diff --git a/lsdb/Lsdb_test.go b/lsdb/Lsdb_test.go new file mode 100644 index 0000000..f1a3855 --- /dev/null +++ b/lsdb/Lsdb_test.go @@ -0,0 +1,254 @@ +// Package lsdb +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/5/25 10:15 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package lsdb + +import ( + "fmt" + common2 "minlib/common" + "minlib/component" + "minlib/encoding" + "minlib/logicface" + "mlsr/common" + "mlsr/lsa" + "strconv" + "testing" + "time" +) + +const testConfigPath = "D:\\" + common.DefaultConfFileName + +// +// TestLsdb_Init +// @Description: 测试LSDB初始化 +// @param t +// +func TestLsdb_Init(t *testing.T) { + // 配置文件初始化 + mlsrConfig, err := common.ParseConfig(testConfigPath) + if err != nil { + fmt.Println("配置文件解析错误") + } + // 调度器初始化 + sche := new(MlsrScheduler) + sche.Init() + // LogicFace初始化(假设初始化,暂时用不到) + face := new(logicface.LogicFace) + // LSDB初始化 + lsdb := new(Lsdb) + lsdb.Init(mlsrConfig, sche, face) +} + +// +// TestUnixTime +// @Description: 测试时间与时间戳的转换。将用于LSA中过期时间的保存,非常重要。 +// @param t +// +func TestUnixTime(t *testing.T) { + // 配置文件初始化 + mlsrConfig, err := common.ParseConfig(testConfigPath) + if err != nil { + fmt.Println("配置文件解析错误") + } + // 调度器初始化 + sche := new(MlsrScheduler) + sche.Init() + // LogicFace初始化(假设初始化,暂时用不到) + face := new(logicface.LogicFace) + // LSDB初始化 + lsdb := new(Lsdb) + lsdb.Init(mlsrConfig, sche, face) + + // 获取过期时间的时间戳 + timeExpire := lsdb.getLsaExpirationTimePoint() + // 时间戳转回过期时间 + timeTemplate := "2006-01-02 15:04:05" // 时间戳常规类型 + fmt.Println(lsdb.uintToTime(timeExpire).Format(timeTemplate)) +} + +// +// TestLsdb_ScheduleLsaExpiration +// @Description: 测试安排LSA过期事件 +// @param t +// +func TestLsdb_ScheduleLsaExpiration(t *testing.T) { + // 1. 初始化LSDB + // 配置文件初始化 + mlsrConfig, err := common.ParseConfig(testConfigPath) + if err != nil { + common2.LogInfo("配置文件解析错误") + } + // 调度器初始化 + sche := new(MlsrScheduler) + sche.Init() + // LogicFace初始化(假设初始化,暂时用不到) + face := new(logicface.LogicFace) + // LSDB初始化 + lsdb := new(Lsdb) + lsdb.Init(mlsrConfig, sche, face) + + // 2. 生成一个邻接Lsa + adjLsa := new(lsa.AdjLsa) + adjLsa.SetLsaExpirationTime(2000) + adjLsa.SetLsaSequenceNumber(1234) + adjLsa.LsaOriginRouterIdentifier, _ = component.CreateIdentifierByString("/min/pku") + // 构造邻接信息部分 + // 先构造一个info + linkCost := lsa.AdjLsaLinkCost{} + linkCost.SetLinkCost(100) + faceUri := lsa.AdjLsaLogicFaceUri{} + faceUri.SetLogicFaceUri("uri") + faceId := lsa.AdjLsaLogicFaceId{} + faceId.SetLogicFaceId(77) + iden, _ := component.CreateIdentifierByString("/pku/router1") + adjInfo := lsa.NewAdjLsaAdjacencyInfo(linkCost, faceUri, faceId, iden, 123, 1) + adjLsa.Insert(adjInfo) + + // 3. 插入Lsdb + err = lsdb.Emplace(adjLsa) + if err != nil { + common2.LogInfo("插入错误") + } + common2.LogInfo("插入成功") + + // 4. 设置自动删除任务【3s+10s=13s】 + timeDur := 3 * time.Second + lsdb.ScheduleLsaExpiration(adjLsa, timeDur) + common2.LogInfo("???") + + // 5. 不断循环查询lsdb中是否存在该lsa + i := 0 + for true { + b := lsdb.DoesLsaExist(adjLsa) + common2.LogInfo("是否存在:" + strconv.FormatBool(b)) + lsaCache := lsdb.FindLsa(adjLsa.GetOriginRouter(), adjLsa.GetType()) + if lsaCache != nil { + common2.LogInfo(lsaCache.ToString()) + } + time.Sleep(time.Second) + i++ + } +} + +// +// TestLsab_InstallLsa +// @Description: 测试安装lsa。安装lsa时会根据过期时间来设置多久后删除或更新该lsa +// @param t +// +func TestLsab_InstallLsa(t *testing.T) { + // 1. 初始化LSDB + // 配置文件初始化 + mlsrConfig, err := common.ParseConfig(testConfigPath) + if err != nil { + common2.LogInfo("配置文件解析错误") + } + // 调度器初始化 + sche := new(MlsrScheduler) + sche.Init() + // LogicFace初始化(假设初始化,暂时用不到) + face := new(logicface.LogicFace) + // LSDB初始化 + lsdb := new(Lsdb) + lsdb.Init(mlsrConfig, sche, face) + + // 2. 构建三秒后过期的时间戳,并将之设置到namelsa中 + timeExpire := uint64(time.Now().Add(3 * time.Second).UnixMilli()) + // 构造info + namePrefixInfo := new(lsa.NameLsaNamePrefixInfo) + namePrefixInfo.NamePrefix, _ = component.CreateIdentifierByString("/min/pku") + namePrefixInfo.InsertSource("/wechat") + namePrefixInfo.InsertSource("/qq") + // 构造nameLSA + nameLsa := new(lsa.NameLsa) + nameLsa.LsaBase = lsa.LsaBase{ + SelfEncodingBase: encoding.SelfEncodingBase{}, + LsaExpirationTime: lsa.LsaExpirationTime{}, + LsaSequenceNumber: lsa.LsaSequenceNumber{}, + LsaOriginRouterIdentifier: nil, + } + nameLsa.SetLsaSequenceNumber(111) + nameLsa.SetLsaExpirationTime(timeExpire) + routerIdent, _ := component.CreateIdentifierByString("/pku/router") + nameLsa.SetOriginRouter(routerIdent) + nameLsa.InsertNamePrefixInfo(namePrefixInfo) + + // 3. 安装到lsdb + lsdb.InstallLsa(nameLsa) + + // 4. 不断循环查询lsdb中是否存在该lsa + i := 0 + for true { + b := lsdb.DoesLsaExist(nameLsa) + common2.LogInfo("是否存在:" + strconv.FormatBool(b)) + lsaCache := lsdb.FindLsa(nameLsa.GetOriginRouter(), nameLsa.GetType()) + if lsaCache != nil { + common2.LogInfo(lsaCache.ToString()) + } + time.Sleep(time.Second) + i++ + } +} + +// +// TestName_BuildAndInstallAdjLsa +// @Description: 测试构建本机的adjlsa +// @param t +// +func TestName_BuildAndInstallAdjLsa(t *testing.T) { + // 1. 初始化LSDB + // 配置文件初始化 + mlsrConfig, err := common.ParseConfig(testConfigPath) + if err != nil { + fmt.Println("配置文件解析错误") + } + // 调度器初始化 + sche := new(MlsrScheduler) + sche.Init() + // LogicFace初始化(假设初始化,暂时用不到) + face := new(logicface.LogicFace) + // LSDB初始化 + lsdb := new(Lsdb) + lsdb.Init(mlsrConfig, sche, face) + + // 2. 安装本机adjlsa + lsdb.BuildAndInstallOwnAdjLsa() + + // 3. 打印lsdb中所有本机lsa + lsas, _ := lsdb.LsaContainer.GetLSAsByType(lsa.LsaADJACENCYType) + for i := 0; i < len(lsas); i++ { + common2.LogInfo(lsas[i].ToString()) + } + lsas, _ = lsdb.LsaContainer.GetLSAsByType(lsa.LsaNAMEType) + for i := 0; i < len(lsas); i++ { + common2.LogInfo(lsas[i].ToString()) + } +} + +// +// TestLsdb_InstallLsa +// @Description: 测试安装lsa +// @param t +// +func TestLsdb_InstallLsa_ScheduleAdjLsaBuild(t *testing.T) { + // 1. 初始化LSDB + // 配置文件初始化 + mlsrConfig, err := common.ParseConfig(testConfigPath) + if err != nil { + fmt.Println("配置文件解析错误") + } + // 调度器初始化 + sche := new(MlsrScheduler) + sche.Init() + // LogicFace初始化(假设初始化,暂时用不到) + face := new(logicface.LogicFace) + // LSDB初始化 + lsdb := new(Lsdb) + lsdb.Init(mlsrConfig, sche, face) + + // 2. todo 暂时不测,等linux机器上做实机测试 +} diff --git a/lsdb/Scheduler.go b/lsdb/Scheduler.go index 9773a2e..ecfe410 100644 --- a/lsdb/Scheduler.go +++ b/lsdb/Scheduler.go @@ -8,10 +8,76 @@ package lsdb +import ( + "github.com/go-co-op/gocron" + "time" +) + // // Scheduler -// @Description: 调度器 +// @Description: 封装了goCron的调度器 // -type Scheduler struct { +type MlsrScheduler struct { + sche *gocron.Scheduler +} +// +// Init +// @Description: 调度器初始化 +// @receiver s +// +func (s *MlsrScheduler) Init() { + // UTC表示unix标准时间,与北京时区不同,但不影响任务执行 + s.sche = gocron.NewScheduler(time.UTC) +} + +// +// ScheduleTaskAfterDuration +// @Description: 安排任务在一定时间后触发,触发一次 +// @receiver s +// @param dur 时间间隔 +// +func (s *MlsrScheduler) ScheduleTaskAfterDuration(dur time.Duration, intervalSeconds int) *gocron.Scheduler { + timeRun := time.Now() + timeRun = timeRun.Add(dur) + return s.scheduleRecurringTaskAtTime(timeRun, intervalSeconds) +} + +// +// ScheduleTaskAtTime +// @Description: 安排任务在某个时间执行一次 +// @receiver s +// @param timeRun 执行时间 +// +func (s *MlsrScheduler) ScheduleTaskAtTime(timeRun time.Time, intervalSeconds int) *gocron.Scheduler { + return s.scheduleRecurringTaskAtTime(timeRun, intervalSeconds) +} + +// +// scheduleRecurringTaskAtTime +// @Description: 安排执行某次循环性任务 +// @receiver s +// @param timeRun 首次执行时间 +// @param intervalSeconds 执行间隔(单位:秒) +// +func (s *MlsrScheduler) scheduleRecurringTaskAtTime(timeRun time.Time, intervalSeconds int) *gocron.Scheduler { + // 判断调度器是否还在正常运行 + if (s.sche == nil) || (!s.sche.IsRunning()) { + s.Init() + } + // 安排任务执行 + return s.sche.Every(intervalSeconds).Seconds().StartAt(timeRun) +} + +func (s *MlsrScheduler) StartAsync() { + s.sche.StartAsync() +} + +// +// Close +// @Description: 关闭调度器 +// @receiver s +// +func (s *MlsrScheduler) Close() { + s.sche.Clear() } diff --git a/lsdb/Scheduler_test.go b/lsdb/Scheduler_test.go new file mode 100644 index 0000000..409471b --- /dev/null +++ b/lsdb/Scheduler_test.go @@ -0,0 +1,115 @@ +// Package lsdb +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/5/30 11:19 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package lsdb + +import ( + "fmt" + "github.com/go-co-op/gocron" + common2 "minlib/common" + "testing" + "time" +) + +func printHelloWithS(s string) { + fmt.Println("hello, ", s) +} + +func printHello() { + fmt.Println("hello! ") +} + +func testLuoTest() error { + timeRun := time.Now() + timeRun.Add(time.Second) + + sche := gocron.NewScheduler(time.UTC) + job, err := sche.Every(3).Seconds().StartAt(timeRun).Do(printHelloWithS, "fuck") + if err != nil { + fmt.Println(err) + } + job.LimitRunsTo(3) + sche.StartAsync() + return nil + //for true { + // time.Sleep(time.Second) + //} +} + +func testLuoTest2() error { + timeRun := time.Now() + timeRun.Add(time.Second) + + sche := gocron.NewScheduler(time.UTC) + job, err := sche.Every(3).Seconds().StartAt(timeRun).Do(printHelloWithS, "fuck2") + if err != nil { + fmt.Println(err) + } + job.LimitRunsTo(3) + sche.StartAsync() + return nil + //for true { + // time.Sleep(time.Second) + //} +} + +func TestMlsrScheduler_ScheduleTaskAfterDuration(t *testing.T) { + //_ = testLuoTest() + //_ = testLuoTest2() + //printHelloWithS("ddd") + //for true { + // time.Sleep(time.Second) + //} + + // 构造 + s := new(MlsrScheduler) + s.Init() + // 定时 + timeNow := time.Now() + timeNow = timeNow.Add(3 * time.Second) + //调度任务 + job, err := s.ScheduleTaskAfterDuration(time.Second, 3).Do(printHelloWithS, "sss") + job.LimitRunsTo(3) + s.StartAsync() + if err != nil { + fmt.Println("调度器调度任务测试失败, " + err.Error()) + return + } + for true { + time.Sleep(time.Second) + } + //关闭调度器 + s.Close() +} + +// +// TestMlsrScheduler_ScheduleTaskAtTime +// @Description: 测试调度器在指定时间执行任务 +// @param t +// +func TestMlsrScheduler_ScheduleTaskAtTime(t *testing.T) { + // 构造 + s := new(MlsrScheduler) + s.Init() + // 定时 + timeNow := time.Now() + timeNow = timeNow.Add(3 * time.Second) + // 调度任务 + job, err := s.ScheduleTaskAtTime(timeNow, 1).Do(printHello) + job.LimitRunsTo(3) + s.StartAsync() + if err != nil { + common2.LogFatal("调度器调度任务测试失败, " + err.Error()) + return + } + for true { + time.Sleep(time.Second) + } + // 关闭调度器 + s.Close() +} diff --git a/lsdb/SequencingManager.go b/lsdb/SequencingManager.go index 35319d3..bc25a79 100644 --- a/lsdb/SequencingManager.go +++ b/lsdb/SequencingManager.go @@ -10,21 +10,31 @@ package lsdb import ( "errors" - "minlib/common" + common2 "minlib/common" "mlsr/lsa" + "mlsr/utils" + "path" "strconv" + "strings" "sync" ) -var once sync.Once // 用以保证SequencingManager的使用是单例模式 -var instance *SequencingManager // 单例模式的序列号管理器 +var once sync.Once // 用以保证SequencingManager的使用是单例模式 +var instance *SequencingManager // 单例模式的序列号管理器 -var seqFileName string = "mlsrSeqNo.txt" // 保存序列号的文件名 +var seqFileName string = ".mlsrSeqNo.txt" // 保存序列号的文件名 +// +// SequencingManager +// @Description: 序列号管理器 +// type SequencingManager struct { - adjLsaSeq uint64 + // 三种序列号 + adjLsaSeq uint64 nameLsaSeq uint64 - corLsaSeq uint64 + corLsaSeq uint64 + + // 序列号保存到的文件路径 seqFileNameWithPath string } @@ -34,15 +44,19 @@ type SequencingManager struct { // @param filePath 序列号同步到本地文件的文件位置 // @return *SequencingManager // -func GetInstance(filePath string) *SequencingManager { +func GetInstance() *SequencingManager { once.Do(func() { - homePath, err := common.Home() + homePath, err := common2.Home() if err != nil { - common.LogFatal("Get current user home path failed!") + common2.LogError("Get current user home path failed!") } - instance = &SequencingManager{0,0,0, - homePath+"/"+seqFileName} - _ = instance.SetSeqFileDirectory(filePath) + // 生成序列号管理器文件路径,并日志记录下来 + filePath := path.Join(homePath, seqFileName) + common2.LogInfo("SequencingManager file path is " + filePath) + // 从文件中初始化该序列号管理器 + instance = &SequencingManager{0, 0, 0, + filePath} + _ = instance.setSeqFileDirectory(filePath) instance.InitiateSeqNoFromFile() }) return instance @@ -55,25 +69,65 @@ func GetInstance(filePath string) *SequencingManager { // @receiver s // @param filePath // -func (s *SequencingManager) SetSeqFileDirectory(filePath string) error { - //todo +func (s *SequencingManager) setSeqFileDirectory(filePath string) error { + s.seqFileNameWithPath = filePath return nil } // // InitiateSeqNoFromFile -// @Description: todo:从文件中初始化各种类型lsa的seq +// @Description: 从文件中初始化各种类型lsa的seq // @receiver s // -func (s *SequencingManager) InitiateSeqNoFromFile() { +func (s *SequencingManager) InitiateSeqNoFromFile() { + // 先判断文件是否存在,不存在,则初始化文件 + _, b := utils.IsExists(s.seqFileNameWithPath) + if !b { + common2.LogInfo("sequencingManager file not exists, fileinfo is " + s.seqFileNameWithPath) + homePath, err := common2.Home() + if err != nil { + common2.LogError("Get current user home path failed!") + } + // 生成序列号管理器文件,并将序列号写入文件 + filePath := path.Join(homePath, seqFileName) + _ = s.setSeqFileDirectory(filePath) + utils.ClearAndWriteFile(filePath, "0 0 0") // 依次表示:邻接LSA序列号、名称LSA序列号、双曲LSA序列号 + } + // 并读取数据 + seqs, err := utils.ReadFromFile(s.seqFileNameWithPath) + common2.LogInfo("读取数据成功,序列号为: " + seqs) + if err != nil { + common2.LogFatal("read seqs from " + s.seqFileNameWithPath + " failed.") + return + } + // 读取邻接 + seqsArray := strings.Fields(seqs) + s.adjLsaSeq, err = strconv.ParseUint(seqsArray[0], 10, 64) + if err != nil { + common2.LogFatal("read seqs from " + s.seqFileNameWithPath + " failed, reason is " + err.Error()) + } + // 读取名称 + s.nameLsaSeq, err = strconv.ParseUint(seqsArray[1], 10, 64) + if err != nil { + common2.LogFatal("read seqs from " + s.seqFileNameWithPath + " failed, reason is " + err.Error()) + } + // 读取双曲 + s.corLsaSeq, err = strconv.ParseUint(seqsArray[2], 10, 64) + if err != nil { + common2.LogFatal("read seqs from " + s.seqFileNameWithPath + " failed, reason is " + err.Error()) + } } // // WriteSeqNoToFile -// @Description: todo:将当前seq写入文件 +// @Description: 将当前seq写入文件 // @receiver s // -func (s SequencingManager) WriteSeqNoToFile() { +func (s *SequencingManager) writeSeqNoToFile() { + // 将序列号写入文件。依次表示:邻接LSA序列号、名称LSA序列号、双曲LSA序列号 + utils.ClearAndWriteFile(s.seqFileNameWithPath, strconv.FormatUint(s.adjLsaSeq, 10)+" "+ + strconv.FormatUint(s.nameLsaSeq, 10)+" "+ + strconv.FormatUint(s.corLsaSeq, 10)) } // @@ -83,15 +137,17 @@ func (s SequencingManager) WriteSeqNoToFile() { // @param seqNo // @param lsaType // -func (s *SequencingManager) SetLsaSeq(seqNo uint64,lsaType lsa.LsaType) { +func (s *SequencingManager) SetLsaSeq(seqNo uint64, lsaType lsa.LsaType) { switch lsaType { case lsa.LsaADJACENCYType: - s.adjLsaSeq=seqNo + s.adjLsaSeq = seqNo case lsa.LsaNAMEType: - s.nameLsaSeq=seqNo + s.nameLsaSeq = seqNo case lsa.LsaCOORDINATEType: - s.corLsaSeq=seqNo + s.corLsaSeq = seqNo } + // 更新到文件 + s.writeSeqNoToFile() } // @@ -102,16 +158,16 @@ func (s *SequencingManager) SetLsaSeq(seqNo uint64,lsaType lsa.LsaType) { // @return uint64 // @return error // -func (s *SequencingManager) GetLsaSeq(lsaType lsa.LsaType) (uint64,error) { +func (s *SequencingManager) GetLsaSeq(lsaType lsa.LsaType) (uint64, error) { switch lsaType { case lsa.LsaADJACENCYType: - return s.adjLsaSeq,nil + return s.adjLsaSeq, nil case lsa.LsaNAMEType: - return s.nameLsaSeq,nil + return s.nameLsaSeq, nil case lsa.LsaCOORDINATEType: - return s.corLsaSeq,nil + return s.corLsaSeq, nil } - return 0,errors.New("no found the lsaType: "+ strconv.Itoa(int(lsaType))) + return 0, errors.New("no found the lsaType: " + strconv.Itoa(int(lsaType))) } // @@ -131,7 +187,9 @@ func (s *SequencingManager) GetAdjLsaSeq() uint64 { // @param seq // func (s *SequencingManager) SetAdjLsaSeq(seq uint64) { - s.adjLsaSeq=seq + s.adjLsaSeq = seq + // 更新到文件 + s.writeSeqNoToFile() } // @@ -151,7 +209,9 @@ func (s *SequencingManager) GetNameLsaSeq() uint64 { // @param seq // func (s *SequencingManager) SetNameLsaSeq(seq uint64) { - s.nameLsaSeq=seq + s.nameLsaSeq = seq + // 更新到文件 + s.writeSeqNoToFile() } // @@ -171,7 +231,9 @@ func (s *SequencingManager) GetCorLsaSeq() uint64 { // @param seq // func (s *SequencingManager) SetCorLsaSeq(seq uint64) { - s.corLsaSeq=seq + s.corLsaSeq = seq + // 更新到文件 + s.writeSeqNoToFile() } // @@ -179,8 +241,10 @@ func (s *SequencingManager) SetCorLsaSeq(seq uint64) { // @Description: 邻接LSA序列号加一 // @receiver s // -func (s *SequencingManager) IncreaseAdjLsaSeq() { +func (s *SequencingManager) IncreaseAdjLsaSeq() { s.adjLsaSeq++ + // 更新到文件 + s.writeSeqNoToFile() } // @@ -188,8 +252,10 @@ func (s *SequencingManager) IncreaseAdjLsaSeq() { // @Description: 名称LSA序列号加一 // @receiver s // -func (s *SequencingManager) IncreaseNameLsaSeq() { +func (s *SequencingManager) IncreaseNameLsaSeq() { s.nameLsaSeq++ + // 更新到文件 + s.writeSeqNoToFile() } // @@ -197,6 +263,8 @@ func (s *SequencingManager) IncreaseNameLsaSeq() { // @Description: 坐标LSA序列号加一 // @receiver s // -func (s *SequencingManager) IncreaseCorLsaSeq() { +func (s *SequencingManager) IncreaseCorLsaSeq() { s.corLsaSeq++ -} \ No newline at end of file + // 更新到文件 + s.writeSeqNoToFile() +} diff --git a/lsdb/SequencingManager_test.go b/lsdb/SequencingManager_test.go new file mode 100644 index 0000000..a94cf26 --- /dev/null +++ b/lsdb/SequencingManager_test.go @@ -0,0 +1,32 @@ +// Package lsdb +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/5/19 16:47 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package lsdb + +import ( + "fmt" + "mlsr/lsa" + "testing" +) + +// 测试从文件读写序列号管理器 +func TestSequencingManager_InitiateSeqNoFromFile(t *testing.T) { + // 构建序列号管理器 + seqManager := GetInstance() + fmt.Println("path: ", seqManager.seqFileNameWithPath) + + // 增加邻接序列号 + seqManager.IncreaseAdjLsaSeq() + + // 设置,获取指定lsa序列号 + seqManager.SetLsaSeq(9999, lsa.LsaNAMEType) + seq1 := seqManager.GetNameLsaSeq() + seq2 := seqManager.GetAdjLsaSeq() + seq3, _ := seqManager.GetLsaSeq(lsa.LsaCOORDINATEType) + fmt.Println(seq1, seq2, seq3) +} diff --git a/main.go b/main.go deleted file mode 100644 index ba43540..0000000 --- a/main.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import ( - "fmt" - "github.com/tidwall/buntdb" - "minlib/logicface" - "mlsr/lsa" -) - -func main() { - fmt.Println("abc") - s:=logicface.LogicFaceTypeEther - fmt.Println("def",s) - - ss:=lsa.AdjLsaAdjacencyInfo{} - ss.Status=1 - - TestBuntDb() -} - -func TestBuntDb() { - // https://github.com/tidwall/buntdb - db,err := buntdb.Open(":memory:") - if err!= nil{ - fmt.Println("bunt db open err") - } - _ = db.Close() - fmt.Println("bunt db closed") -} - - diff --git a/mlsrConf.ini b/mlsrConf.ini new file mode 100644 index 0000000..3af5564 --- /dev/null +++ b/mlsrConf.ini @@ -0,0 +1,38 @@ +[General] +Network = /min +Site = /pkusz +Router = /routerA +LsaRefreshTime = 1800 +RouterDeadInterval = 3600 +LsaInterestLifetime = 4 +SyncProtocol = min-sync +SyncInterestLifetime = 60000 + +[Log] +LogLevel = ALL +ReportCaller = true +LogFormat = text +LogFilePath = + +[Neighbors] +HelloRetries = 3 +HelloTimeout = 1 +HelloInterval = 60 +AdjLsaBuildInterval = 10 +LogicFaceDatasetFetchRetries = 3 +LogicFaceDatasetFetchInterval = 3600 +NeighborsInfo = [{"NeighborName":"/routerB","LogicFaceUri":"tcp://1.1.1.1","LinkCost":10},{"NeighborName":"/routerC","LogicFaceUri":"tcp://1.1.1.0","LinkCost":10},{"NeighborName":"nei","LogicFaceUri":"uri","LinkCost":0}] + +[Hyperbolic] + +[Fib] +MaxLogicFacesPerPrefix = 0 +RoutingCalcInterval = 15 + +[Advertising] +Prefixs = /videos,/files,voices + +[Security] + +[MlsrConfigParameters] + diff --git a/mlsrcmd/mlsr/main.go b/mlsrcmd/mlsr/main.go new file mode 100644 index 0000000..3a8b92c --- /dev/null +++ b/mlsrcmd/mlsr/main.go @@ -0,0 +1,26 @@ +// Package mlsr +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/5/16 17:32 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package mlsr + +import ( + "fmt" + "mlsr/common" +) + +func main() { + // 初始化配置文件 + testPath := "D:" + testPath += "\\" + testPath += common.DefaultConfFileName + fmt.Println("test mlsr conf path: " + testPath) + mlsrConfig, _ := common.ParseConfig(testPath) + + // 初始化日志模块 + common.InitLogger(mlsrConfig) +} diff --git a/route/ConnectingLogicFaceUri.go b/route/ConnectingLogicFaceUri.go new file mode 100644 index 0000000..3952ba9 --- /dev/null +++ b/route/ConnectingLogicFaceUri.go @@ -0,0 +1,71 @@ +// Package lsa +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/5/5 10:38 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package route + +import ( + "minlib/component" + "minlib/encoding" + "mlsr/extensions" +) + +// +// ConnectingLogicFaceUri +// @Description: MIR层级对LogicFace的描述。例子:udp://1.0.0.2 +// +type ConnectingLogicFaceUri struct { + component.CommonString +} + +// +// LogicFaceUri +// @Description: 获取LogicFaceUri +// @receiver l +// @return string +// +func (l *ConnectingLogicFaceUri) LogicFaceUri() string { + return l.CommonString.Value() +} + +// +// setLogicFaceUri +// @Description: 设置LogicFaceUri +// @receiver l +// @param logicFaceUri +// +func (l *ConnectingLogicFaceUri) SetLogicFaceUri(logicFaceUri string) { + l.CommonString.SetValue(logicFaceUri) +} + +// +// WireEncode +// @Description: 线速编码 +// @receiver l +// @param encoder +// @return int +// @return error +// +func (l *ConnectingLogicFaceUri) WireEncode(encoder *encoding.Encoder) (int,error) { + l.CommonString.SetTlvType(extensions.TlvMlsrConnectingLogicFaceUri) + return l.CommonString.WireEncode(encoder) +} + +// +// WireDecode +// @Description: 线速解码 +// @receiver l +// @param block +// @return error +// +func (l *ConnectingLogicFaceUri) WireDecode(block *encoding.Block) error { + // 检查type是否正确 + if err := encoding.ExpectType(block.GetType(),extensions.TlvMlsrConnectingLogicFaceUri); err!=nil { + return err + } + return l.CommonString.WireDecode(block) +} diff --git a/route/ConnectingLogicFaceUri_test.go b/route/ConnectingLogicFaceUri_test.go new file mode 100644 index 0000000..48447df --- /dev/null +++ b/route/ConnectingLogicFaceUri_test.go @@ -0,0 +1,109 @@ +// Package route +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/5/26 10:39 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package route + +import ( + "fmt" + "minlib/encoding" + "testing" +) + +// +// TestConnectingLogicFaceUri_LogicFaceUri +// @Description: 设置参数 +// @param t +// +func TestConnectingLogicFaceUri_LogicFaceUri(t *testing.T) { + testCPU := ConnectingLogicFaceUri{} + testCPU.SetLogicFaceUri("url") + fmt.Println(testCPU.LogicFaceUri()) +} + +// +// TestConnectingLogicFaceUri_WireEncode +// @Description: 测试编码 +// @param t +// +func TestConnectingLogicFaceUri_WireEncode(t *testing.T) { + testCPU := new(ConnectingLogicFaceUri) + fmt.Println(testCPU.LogicFaceUri()) + testCPU.SetLogicFaceUri("url") + fmt.Println(testCPU.LogicFaceUri()) + //testCPC.SetTlvType(5) + fmt.Println(testCPU.Value()) + var encoder encoding.Encoder + err := encoder.EncoderReset(encoding.MaxPacketSize, 0) + if err != nil { + fmt.Println("encoder.EncoderReset() failed", err) + return + } + a, err := testCPU.WireEncode(&encoder) + if err != nil { + fmt.Println("ConnectingLogicFaceUri.WireEncode() failed", err) + return + } + fmt.Println(a) + buf, err := encoder.GetBuffer() + if err != nil { + fmt.Println("encoder.GetBuffer() failed", err) + return + } + fmt.Println("interest buf", buf) + block, err := encoding.CreateBlockByBuffer(buf[:], false) + if err != nil { + fmt.Println("encoding.CreateBlockByBuffer() failed", err) + } + fmt.Println("interest block", block) +} + +// +// TestConnectingLogicFaceUri_WireDecode +// @Description: 测试解码 +// @param t +// +func TestConnectingLogicFaceUri_WireDecode(t *testing.T) { + testCPU := new(ConnectingLogicFaceUri) + fmt.Println("interest", testCPU.Value()) + //testCPC.SetTlvType(5) + fmt.Println(testCPU.TlvType()) + testCPU.SetLogicFaceUri("url") + fmt.Println("interest", testCPU.Value()) + fmt.Println("interest", testCPU.IsInitial()) + var encoder encoding.Encoder + err := encoder.EncoderReset(encoding.MaxPacketSize, 0) + if err != nil { + fmt.Println("encoder.EncoderReset() failed", err) + return + } + a, err := testCPU.WireEncode(&encoder) + if err != nil { + fmt.Println("ConnectingLogicFaceUri.WireEncode() failed", err) + } + fmt.Println(a) + buf, err := encoder.GetBuffer() + if err != nil { + fmt.Println("encoder.GetBuffer() failed:", err) + return + } + fmt.Println("interest buf", buf) + block, err := encoding.CreateBlockByBuffer(buf[:], false) + if err != nil { + fmt.Println("encoding.CreateBlockByBuffer() failed:", err) + return + } + fmt.Println("interest block", block) + interest1 := new(ConnectingLogicFaceUri) + err = interest1.WireDecode(block) + if err != nil { + fmt.Println("interest1.WireDecode() failed", err) + return + } + fmt.Println("interest1.value: ", interest1.Value()) + fmt.Println("interest1", interest1.IsInitial()) +} diff --git a/route/LinkStateRoutingTableCalculator.go b/route/LinkStateRoutingTableCalculator.go new file mode 100644 index 0000000..bc9767e --- /dev/null +++ b/route/LinkStateRoutingTableCalculator.go @@ -0,0 +1,346 @@ +// 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{} +} diff --git a/route/LinkStateRoutingTableCalculator_test.go b/route/LinkStateRoutingTableCalculator_test.go new file mode 100644 index 0000000..c43c644 --- /dev/null +++ b/route/LinkStateRoutingTableCalculator_test.go @@ -0,0 +1,293 @@ +// Package route +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/5/30 10:57 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package route + +import ( + "fmt" + common2 "minlib/common" + "minlib/component" + "minlib/logicface" + "mlsr/common" + "mlsr/lsa" + "mlsr/lsdb" + "strconv" + "testing" + "time" +) + +// +// testCreateAdjLsa +// @Description: 构造邻接lsa +// @param uri +// @return *lsa.AdjLsa +// +func testCreateAdjLsa(uri string) *lsa.AdjLsa { + base := new(lsa.AdjLsa) + timeExpire := time.Now() + timeExpire = timeExpire.Add(30 * time.Minute) + base.SetLsaExpirationTime(uint64(timeExpire.UnixMilli())) + base.SetLsaSequenceNumber(1234) + base.LsaOriginRouterIdentifier, _ = component.CreateIdentifierByString(uri) + return base +} + +// +// testCreateAdjInfo +// @Description: 构造邻接信息 +// @param cost +// @param uri +// @return *lsa.AdjLsaAdjacencyInfo +// +func testCreateAdjInfo(cost uint64, uri string) *lsa.AdjLsaAdjacencyInfo { + linkCost := lsa.AdjLsaLinkCost{} + linkCost.SetLinkCost(cost) + faceUri := lsa.AdjLsaLogicFaceUri{} + faceUri.SetLogicFaceUri("uri") + faceId := lsa.AdjLsaLogicFaceId{} + faceId.SetLogicFaceId(77) + iden, _ := component.CreateIdentifierByString(uri) + adjInfo := lsa.NewAdjLsaAdjacencyInfo(linkCost, faceUri, faceId, iden, 123, 1) + return adjInfo +} + +// +// TestLinkStateRoutingTableCalculator_CalculateOnePath +// @Description: 测试普通的最短路径计算。测试已通过。 +// @param t +// +func TestLinkStateRoutingTableCalculator_CalculateOnePath(t *testing.T) { + // 1. 构造邻接状态路由表计算器 + rtc := new(LinkStateRoutingTableCalculator) + + // 1.1 构造LSDB + // 配置文件初始化 + mlsrConfig, err := common.ParseConfig(testConfigPath) + if err != nil { + fmt.Println("配置文件解析错误") + } + // 调度器初始化 + sche := new(lsdb.MlsrScheduler) + sche.Init() + // LogicFace初始化(假设初始化,暂时用不到) + face := new(logicface.LogicFace) + // LSDB初始化 + mlsdb := new(lsdb.Lsdb) + mlsdb.Init(mlsrConfig, sche, face) + // 安装本机adjlsa + mlsdb.BuildAndInstallOwnAdjLsa() + // 传入一些其它虚拟lsa,这些lsa与本机lsa共同形成一个小型路由拓扑 + // 路由器B + adjLsaB := testCreateAdjLsa("/routerB") + adjInfoBA := testCreateAdjInfo(10, "/min/pkusz/routerA") + adjInfoBE := testCreateAdjInfo(50, "/routerE") + adjLsaB.Insert(adjInfoBA) + adjLsaB.Insert(adjInfoBE) + // 路由器C + adjLsaC := testCreateAdjLsa("/routerC") + adjInfoCA := testCreateAdjInfo(100, "/min/pkusz/routerA") + adjInfoCE := testCreateAdjInfo(10, "/routerE") + adjInfoCD := testCreateAdjInfo(60, "/routerD") + adjLsaC.Insert(adjInfoCA) + adjLsaC.Insert(adjInfoCD) + adjLsaC.Insert(adjInfoCE) + // 路由器E + adjLsaE := testCreateAdjLsa("/routerE") + adjInfoEB := testCreateAdjInfo(50, "/routerB") + adjInfoEC := testCreateAdjInfo(10, "/routerC") + adjInfoED := testCreateAdjInfo(20, "/routerD") + adjLsaE.Insert(adjInfoEB) + adjLsaE.Insert(adjInfoEC) + adjLsaE.Insert(adjInfoED) + // 路由器D + adjLsaD := testCreateAdjLsa("/routerD") + adjInfoDA := testCreateAdjInfo(30, "/min/pkusz/routerA") + adjInfoDC := testCreateAdjInfo(60, "/routerC") + adjInfoDE := testCreateAdjInfo(20, "/routerE") + adjLsaD.Insert(adjInfoDA) + adjLsaD.Insert(adjInfoDC) + adjLsaD.Insert(adjInfoDE) + mlsdb.InstallLsa(adjLsaB) + mlsdb.InstallLsa(adjLsaC) + mlsdb.InstallLsa(adjLsaD) + mlsdb.InstallLsa(adjLsaE) + // 1.2 构造RouterMap + lsas, err := mlsdb.GetLSAsByType(lsa.LsaADJACENCYType) + if err != nil { + common2.LogError("Get Lsa in calculateLsRoutingTable error, ", err) + } + common2.LogInfo("邻接lsa数目: ", len(lsas)) + rMap := new(RouterMap) + rMap.Init() + err = rMap.CreateFromAdjLsdb(lsas) + // 打印rm + common2.LogInfo(rMap.ToString()) + + // 1.3 根据RouterMap,和lsdb中的adjlsa,构造邻接矩阵 + // 设置路由器数目 + rtc.SetRouterNum(rMap.GetMapSize()) + // 分配矩阵空间 + rtc.allocateAdjMatrix() + // 初始化矩阵值 + rtc.initMatrix() + // 构造矩阵 + err = rtc.makeAdjMatrix(mlsdb, rMap) + if err != nil { + common2.LogError("邻接矩阵构造失败") + } + + // 1.4 打印输出路由表计算器 + fmt.Println(rMap.ToString()) + fmt.Println(rtc.ToString()) + fmt.Println("源路由器的邻接数目:", rtc.getNumOfLinkfromAdjMatrix(0)) + fmt.Println("另一个路由器的邻接数目:", rtc.getNumOfLinkfromAdjMatrix(1)) + + // 2. 执行单源最短路径算法 + rtc.allocateParent() + rtc.allocateDistance() + rtc.doDijkstraPathCalculation(0) + // 查看执行结果 + // 计算器中的上一跳路径,以及开销 + fmt.Println(rtc.ToString()) + // 计算器获取的针对每个目的路由器的下一跳,及开销。 + names, nexthops := rtc.addAllLsNextHopsToRoutingTable(mlsrConfig.GetAdjacencys(), rMap, 0) + for i := 0; i < len(names); i++ { + fmt.Println(names[i].ToUri() + " ") + } + fmt.Println("") + for i := 0; i < len(nexthops); i++ { + fmt.Println(nexthops[i].LogicFaceUri() + "-" + + strconv.FormatUint(nexthops[i].RouteCost.RouteCost(), 10) + " ") + } +} + +// +// TestLinkStateRoutingTableCalculator_CalculateMutilPath +// @Description: 测试多路径计算 +// @param t +// +func TestLinkStateRoutingTableCalculator_CalculateMutilPath(t *testing.T) { + // 1. 构造邻接状态路由表计算器 + rtc := new(LinkStateRoutingTableCalculator) + + // 1.1 构造LSDB + // 配置文件初始化 + mlsrConfig, err := common.ParseConfig(testConfigPath) + if err != nil { + fmt.Println("配置文件解析错误") + } + // 调度器初始化 + sche := new(lsdb.MlsrScheduler) + sche.Init() + // LogicFace初始化(假设初始化,暂时用不到) + face := new(logicface.LogicFace) + // LSDB初始化 + mlsdb := new(lsdb.Lsdb) + mlsdb.Init(mlsrConfig, sche, face) + // 安装本机adjlsa + mlsdb.BuildAndInstallOwnAdjLsa() + // 传入一些其它虚拟lsa,这些lsa与本机lsa共同形成一个小型路由拓扑 + // 路由器B + adjLsaB := testCreateAdjLsa("/routerB") + adjInfoBA := testCreateAdjInfo(10, "/min/pkusz/routerA") + adjInfoBE := testCreateAdjInfo(50, "/routerE") + adjLsaB.Insert(adjInfoBA) + adjLsaB.Insert(adjInfoBE) + // 路由器C + adjLsaC := testCreateAdjLsa("/routerC") + adjInfoCA := testCreateAdjInfo(100, "/min/pkusz/routerA") + adjInfoCE := testCreateAdjInfo(10, "/routerE") + adjInfoCD := testCreateAdjInfo(60, "/routerD") + adjLsaC.Insert(adjInfoCA) + adjLsaC.Insert(adjInfoCD) + adjLsaC.Insert(adjInfoCE) + // 路由器E + adjLsaE := testCreateAdjLsa("/routerE") + adjInfoEB := testCreateAdjInfo(50, "/routerB") + adjInfoEC := testCreateAdjInfo(10, "/routerC") + adjInfoED := testCreateAdjInfo(20, "/routerD") + adjLsaE.Insert(adjInfoEB) + adjLsaE.Insert(adjInfoEC) + adjLsaE.Insert(adjInfoED) + // 路由器D + adjLsaD := testCreateAdjLsa("/routerD") + adjInfoDA := testCreateAdjInfo(30, "/min/pkusz/routerA") + adjInfoDC := testCreateAdjInfo(60, "/routerC") + adjInfoDE := testCreateAdjInfo(20, "/routerE") + adjLsaD.Insert(adjInfoDA) + adjLsaD.Insert(adjInfoDC) + adjLsaD.Insert(adjInfoDE) + mlsdb.InstallLsa(adjLsaB) + mlsdb.InstallLsa(adjLsaC) + mlsdb.InstallLsa(adjLsaD) + mlsdb.InstallLsa(adjLsaE) + // 1.2 构造RouterMap + lsas, err := mlsdb.GetLSAsByType(lsa.LsaADJACENCYType) + if err != nil { + common2.LogError("Get Lsa in calculateLsRoutingTable error, ", err) + } + common2.LogInfo("邻接lsa数目: ", len(lsas)) + rMap := new(RouterMap) + rMap.Init() + err = rMap.CreateFromAdjLsdb(lsas) + // 打印rm + common2.LogInfo(rMap.ToString()) + + // 1.3 根据RouterMap,和lsdb中的adjlsa,构造邻接矩阵 + // 设置路由器数目 + rtc.SetRouterNum(rMap.GetMapSize()) + // 分配矩阵空间 + rtc.allocateAdjMatrix() + // 初始化矩阵值 + rtc.initMatrix() + // 构造矩阵 + err = rtc.makeAdjMatrix(mlsdb, rMap) + if err != nil { + common2.LogError("邻接矩阵构造失败") + } + + // 1.4 打印输出路由表计算器 + fmt.Println(rMap.ToString()) + fmt.Println(rtc.ToString()) + fmt.Println("源路由器的邻接数目:", rtc.getNumOfLinkfromAdjMatrix(0)) + fmt.Println("另一个路由器的邻接数目:", rtc.getNumOfLinkfromAdjMatrix(1)) + + // 2. 执行多路径算法 + rtc.allocateParent() + rtc.allocateDistance() + // 2.1. 先确定源路由器的邻居路由器数目 + rtc.setNoLink(rtc.getNumOfLinkfromAdjMatrix(0)) + rtc.allocateLinks() + rtc.allocateLinkCosts() + // 2.2. 获取用于路径计算的稀疏邻接列表。具体操作是填充:c.links,c.linkCosts + rtc.getLinksFromAdjMatrix(0) + // + routers := []*component.Identifier{} // 路由器标识 + nexthops := []*NextHop{} // 下一跳 + for i := 0; i < rtc.vNoLink; i++ { + // 模拟只有当前邻居i可以访问的情形。即:砍掉除当前邻居路由器外的其他邻接链路 + rtc.adjustAdMatrix(0, rtc.links[i], rtc.linkCosts[i]) + // 以当前邻居为起点,执行Dijkstra算法。 + rtc.doDijkstraPathCalculation(0) + // 用计算结果更新路由表。 + rs, ns := rtc.addAllLsNextHopsToRoutingTable(mlsrConfig.GetAdjacencys(), rMap, 0) + routers = append(routers, rs...) + nexthops = append(nexthops, ns...) + } + // 查看执行结果 + // 计算器中的上一跳路径,以及开销 + fmt.Println(rtc.ToString()) + for i := 0; i < len(routers); i++ { + fmt.Println(routers[i].ToUri() + " ") + } + fmt.Println("") + for i := 0; i < len(nexthops); i++ { + fmt.Println(nexthops[i].LogicFaceUri() + "-" + + strconv.FormatUint(nexthops[i].RouteCost.RouteCost(), 10) + " ") + } +} diff --git a/route/NamePrefixTable.go b/route/NamePrefixTable.go new file mode 100644 index 0000000..09fd953 --- /dev/null +++ b/route/NamePrefixTable.go @@ -0,0 +1,373 @@ +// Package route +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/6/10 11:16 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package route + +import ( + common2 "minlib/common" + "minlib/component" + "mlsr/lsa" + "mlsr/lsdb" +) + +// +// NamePrefixTable +// @Description: 名称前缀表 +// +type NamePrefixTable struct { + // 路由表池:存储“目的路由器标识URI->RTPE”的映射 + // 相当于存储了所有拥有前缀的路由表项,并包含每个路由表项对应的前缀列表信息,以及该前缀列表长度useCount + m_rtpool map[string]*RoutingTablePoolEntry + // 名称前缀表项列表 + nptEntryList []*NamePrefixTableEntry + // 该名称前缀表所属的本机路由器标识 + m_ownRouterName *component.Identifier + // 路由表。包含路由计算器计算出来的每个路由表标识及其对应的下一跳列表 + m_routingTable *RoutingTable + // todo fib +} + +// +// UpdateFromLsdb +// @Description: 根据lsdb的更新,对名称进行增加、更新或移除 +// @receiver t +// @param lsa +// @param updateType +// @param namesToAdd +// @param namesToRemove +// +func (t *NamePrefixTable) UpdateFromLsdb(ilsa lsa.ILsa, + updateType lsdb.LsdbUpdate, namesToAdd []*component.Identifier, + namesToRemove []*component.Identifier) { + // 如果是本地Lsa的增删更操作,则不做响应 + if ilsa.GetOriginRouter().ToUri() == t.m_ownRouterName.ToUri() { + return + } + + common2.LogInfo("Got update from Lsdb for router: ", ilsa.GetOriginRouter().ToUri()) + + // lsa安装操作 + if updateType == lsdb.INSTALLED { + // 先增加对该路由器标识的条目 + t.AddEntry(ilsa.GetOriginRouter(), ilsa.GetOriginRouter()) + + // 只针对名称LSA进行更新 + if ilsa.GetType() == lsa.LsaNAMEType { + // 获取该lsa中的名称前缀列表 + nlsa := ilsa.(*lsa.NameLsa) + names := nlsa.GetNamePrefixes() + // 遍历所有名称前缀,将其添加到名称前缀表中 + for i := 0; i < len(names); i++ { + if names[i].ToUri() != t.m_ownRouterName.ToUri() { + t.AddEntry(names[i], ilsa.GetOriginRouter()) + } + } + } + } else if updateType == lsdb.UPDATED { + // lsa更新操作(增加、删除) + if ilsa.GetType() == lsa.LsaNAMEType { + // 增加 + for i := 0; i < len(namesToAdd); i++ { + if namesToAdd[i].ToUri() != t.m_ownRouterName.ToUri() { + t.AddEntry(namesToAdd[i], ilsa.GetOriginRouter()) + } + } + // 删除 + for i := 0; i < len(namesToRemove); i++ { + if namesToRemove[i].ToUri() != t.m_ownRouterName.ToUri() { + t.RemoveEntry(namesToRemove[i], ilsa.GetOriginRouter()) + } + } + } + } else { + // 既不是安装,又不是更新(增加或删除lsa的某些部分) => 那就是删除整个LSA + t.RemoveEntry(ilsa.GetOriginRouter(), ilsa.GetOriginRouter()) + if ilsa.GetType() == lsa.LsaNAMEType { + // 获取该lsa中的名称前缀列表 + nlsa := ilsa.(*lsa.NameLsa) + names := nlsa.GetNamePrefixes() + // 遍历所有名称前缀,将其添加到名称前缀表中 + for i := 0; i < len(names); i++ { + if names[i].ToUri() != t.m_ownRouterName.ToUri() { + t.RemoveEntry(names[i], ilsa.GetOriginRouter()) + } + } + } + } +} + +// +// findNamePrefix +// @Description: 查找名称前缀 +// @receiver t +// @param name +// @return int 返回-1时表示没有查找到 +// +func (t *NamePrefixTable) findNamePrefix(name *component.Identifier) int { + for i := 0; i < len(t.nptEntryList); i++ { + if t.nptEntryList[i].GetNamePrefix().ToUri() == name.ToUri() { + return i + } + } + return -1 +} + +// +// deleteNamePrefix +// @Description: 根据名称前缀的索引坐标,删除指定名称前缀 +// @receiver t +// @param nameIndex +// +func (t *NamePrefixTable) deleteNamePrefix(nameIndex int) { + var newList []*NamePrefixTableEntry + for i := 0; i < len(t.nptEntryList); i++ { + if i != nameIndex { + newList = append(newList, t.nptEntryList[i]) + } + } + t.nptEntryList = newList +} + +// +// AddEntry +// @Description: 此方法将路由器添加到名称前缀表条目中。如果名称前缀表条目不存在,则会创建它。 +// 该方法将首先查看其缓存项的本地池,以找到destRouter的路由信息。 +// 如果在那里找不到,它将构造一个,并用路由表中适当的RoutingTable条目中的信息填充它。 +// 如果没有匹配项,它将实例化它,但没有下一跳。FIB也将收到NPT条目变更的通知。 +// @receiver t +// @param name +// @param destRouter +// +func (t *NamePrefixTable) AddEntry(name *component.Identifier, destRouter *component.Identifier) { + // 检查播发的名称前缀是否已在表中。 + nameIndex := t.findNamePrefix(name) + + // 查找目的路由器标识是否在路由表项缓存池中 + v, ok := t.m_rtpool[destRouter.ToUri()] + + // 如果池中没有该目的路由器的路由表项,也即找不到去到name的路由路径,就加一个缓存池项 + if !ok { + // 查看路由表,看是否有包含该目的路由器的条目 + rte := t.m_routingTable.FindRoutingTableEntry(destRouter) + + // 如果路由表里也没有,那我们在缓存里加一个新的条目(虽然没路径) + rtpe := new(RoutingTablePoolEntry) + if rte == nil { + rtpe.InitDest(destRouter, 0) + } else { + // 有的话,直接从路由器中取出该条目,放入缓存池 + rtpe.InitRTE(rte, 0) + } + + // 放入缓存池,并将v刷新为该缓存池项 + v = t.AddRtpeToPool(rtpe) + } + + // 更新npt项,并通知给fib + if nameIndex < 0 { + // 如果NPT中没有该name + common2.LogDebug("Adding origin: " + v.GetDestination().ToUri() + + " to a new name prefix: " + name.ToUri()) + // 构造名称前缀表项,并添加之 + npte := new(NamePrefixTableEntry) + npte.m_namePrefix = name + npte.AddRoutingTableEntry(v) // useCount在这个时候会+1 + npte.GenerateNhlfromRteList() + t.nptEntryList = append(t.nptEntryList, npte) + nameIndex = t.findNamePrefix(name) + + // 如果这个条目有下一跳,我们需要通知FIB + if len(npte.GetNextHops()) > 0 { + common2.LogDebug("Updating FIB with next hops for ", npte.GetNamePrefix().ToUri()) + // todo 更新fib m_fib.update(name, npte->getNexthopList()); + } else { + // 如果没有下一跳 + // 路由表可以重新计算并添加一个没有下一跳的路由表条目,以替换现有的路由表条目。 + // 在这种情况下,通过下一个跃点无法再访问名称前缀,应将其从FIB中删除。 + // 但是,前缀应该保留在名称前缀表中,因为未来的路由表计算可能会添加下一跳。 + common2.LogDebug(npte.GetNamePrefix().ToUri() + " has no next hops; removing from FIB") + // todo 更新fib m_fib.remove(name); + } + } else { + // 如果NPT有这个name + common2.LogDebug("Adding origin: " + v.GetDestination().ToUri() + + " to existing prefix: " + name.ToUri()) + // 更新之 + t.nptEntryList[nameIndex].AddRoutingTableEntry(v) // useCount在这个时候会+1 + t.nptEntryList[nameIndex].GenerateNhlfromRteList() + + // FIB通知 + if len(t.nptEntryList[nameIndex].GetNextHops()) > 0 { + common2.LogDebug("Updating FIB with next hops for ", + t.nptEntryList[nameIndex].GetNamePrefix().ToUri()) + // todo 更新fib m_fib.update(name, npte->getNexthopList()); + } else { + common2.LogDebug(t.nptEntryList[nameIndex].GetNamePrefix().ToUri() + + " has no next hops; removing from FIB") + // todo 更新fib m_fib.remove(name); + } + } + + // 将此NPT的引用添加到RTPE中。 + v.NamePrefixTableEntries[t.nptEntryList[nameIndex].GetNamePrefix().ToUri()] = t.nptEntryList[nameIndex] +} + +// +// RemoveEntry +// @Description: 此方法从条目中删除目标。如果传递了无效的“名称-目标”对,也不会失败。 +// 删除后,如果RoutingTablePoolEntry的使用计数为0,则会将其从表中删除。 +// 此外,如果名称前缀没有与之关联的路由表条目,则会将其从NPT中删除。 +// 在任何情况下,FIB都会被告知这些变化。 +// @receiver t +// @param name +// @param destRouter +// +func (t *NamePrefixTable) RemoveEntry(name *component.Identifier, destRouter *component.Identifier) { + common2.LogDebug("Removing origin: " + destRouter.ToUri() + " from " + name.ToUri()) + + // 根据目的路由器标识,获取其在路由条目缓存池中的条目 + rtpe, ok := t.m_rtpool[destRouter.ToUri()] + if !ok { + // 如果没有找到,则不进行后续处理 + common2.LogDebug("No entry for origin: " + destRouter.ToUri() + + " found, so it cannot be removed from prefix: " + name.ToUri()) + return + } + + // 查找名称前缀 + nameIndex := t.findNamePrefix(name) + if nameIndex >= 0 { + common2.LogDebug("Removing origin: " + rtpe.GetDestination().ToUri() + + " from prefix: " + name.ToUri()) + + // 判断该缓存表项在被删除一个与某前缀的关联之后,其使用数是否为零,如果是,则删除该引用。 + useCount := t.nptEntryList[nameIndex].RemoveRoutingTableEntry(rtpe) + if useCount == 0 { + t.DeleteRtpeFromPool(rtpe) + } + + // 1 如果前缀是路由器前缀,并且没有任何其他路由表条目,则已从LSDB中删除与该原始路由器关联的邻接/坐标LSA, + // 因此应从名称前缀表中删除路由器前缀。 + // 2 如果前缀是播发的名称前缀:如果另一个路由器播发此名称前缀, + // 则RteList应具有该路由器的另一个条目;下一个跃点应重新计算并安装在FIB中。 + // 3 如果没有其他路由器播发此名称前缀,则RteList应为空,并且可以从名称前缀表中删除前缀。 + // 一旦新名称LSA播发此前缀,将创建前缀的新条目。 + if t.nptEntryList[nameIndex].GetRteListSize() == 0 { + common2.LogDebug(name.ToUri() + " has no routing table entries;" + + " removing from table and FIB") + t.deleteNamePrefix(nameIndex) + // todo 删除fib中的该name m_fib.remove(name); + } else { + common2.LogDebug(name.ToUri() + " has other routing table entries;" + + " updating FIB with next hops") + t.nptEntryList[nameIndex].GenerateNhlfromRteList() + // todo 更新fib m_fib.update(name, (*nameItr)->getNexthopList()); + } + } else { + common2.LogDebug("Attempted to remove origin: " + rtpe.GetDestination().ToUri() + + " from non-existent prefix: " + name.ToUri()) + } +} + +// +// isNextHopsEqual +// @Description: 遍历查看两个下一跳列表是否完全一致 +// @param hops1 +// @param hops2 +// @return bool +// +func isNextHopsEqual(hops1 []*NextHop, hops2 []*NextHop) bool { + // 长度不等,则false + if len(hops1) != len(hops2) { + return false + } + // 遍历,如果有一个不相等,则false + for i := 0; i < len(hops1); i++ { + if hops1[i].LogicFaceUri() != hops2[i].LogicFaceUri() || + hops1[i].RouteCost.RouteCost() != hops2[i].RouteCost.RouteCost() { + return false + } + } + return true +} + +// +// UpdateWithNewRoute +// @Description: 接收假定为详尽的条目列表,并使用条目中相应条目中包含的下一跳信息更新每个池条目。 +// 如果找不到条目,则假定该池条目的目标不可访问,其下一跳信息将被删除。 +// @receiver t +// @param entries +// +func (t *NamePrefixTable) UpdateWithNewRoute(entries []*RoutingTableEntry) { + common2.LogDebug("Updating table with newly calculated routes") + + // 遍历名称前缀表中的路由表缓存池 + for _, rtpe := range t.m_rtpool { + // 查看当前缓存表项的目的路由器,是否在传入的真路由表项里可以找到对应的 + sameIndex := -1 // 默认没找到 + for i := 0; i < len(entries); i++ { + if entries[i].GetDestination().ToUri() == rtpe.GetDestination().ToUri() { + sameIndex = i + } + } + + // 根据缓存池与原路由表项的差异,将缓存池更新 + if sameIndex >= 0 && !isNextHopsEqual(rtpe.GetNextHops(), entries[sameIndex].GetNextHops()) { + // 如果找到了对应条目,且两个路由表条目的下一跳列表不一致 + common2.LogDebug("Routing entry: " + rtpe.GetDestination().ToUri() + " has changed next-hops.") + // 更新rtpe的下一跳列表 + rtpe.SetNexthopList(entries[sameIndex].NextHopList) + // 更新rtpe的每个名称前缀表项 + for _, nameEntry := range rtpe.NamePrefixTableEntries { + t.AddEntry(nameEntry.GetNamePrefix(), rtpe.GetDestination()) + } + } else if sameIndex < 0 { + // 没有找到对应条目,说明到该路由器的路径已经无了无了无了~ + common2.LogDebug("Routing entry: " + rtpe.GetDestination().ToUri() + " now has no next-hops.") + // 更新rtpe的下一跳列表:清空 + rtpe.Clear() + // 更新rtpe的每个名称前缀表项 + for _, nameEntry := range rtpe.NamePrefixTableEntries { + t.AddEntry(nameEntry.GetNamePrefix(), rtpe.GetDestination()) + } + } else { + // 有找到对应条目,但是两个条目的下一跳列表完全一致 + common2.LogDebug("No change in routing entry:" + rtpe.GetDestination().ToUri() + + ", no action necessary.") + } + } +} + +// +// AddRtpeToPool +// @Description: 将RoutingTablePoolEntry添加到NPT的本地池。 +// 在C++版实现中,返回值将使用共享指针,因为它消除了处理生命周期问题的复杂hacks,并简化了内存管理。 +// @receiver t +// @param rtpe +// @return *RoutingTablePoolEntry +// +func (t *NamePrefixTable) AddRtpeToPool(rtpe *RoutingTablePoolEntry) *RoutingTablePoolEntry { + t.m_rtpool[rtpe.GetDestination().ToUri()] = rtpe + return rtpe +} + +// +// DeleteRtpeFromPool +// @Description: 从池中删除池项。 +// @receiver t +// @param rtpe +// +func (t *NamePrefixTable) DeleteRtpeFromPool(rtpe *RoutingTablePoolEntry) { + // 查看是否有该条目 + _, ok := t.m_rtpool[rtpe.GetDestination().ToUri()] + if !ok { + common2.LogDebug("Attempted to delete non-existent origin: " + + rtpe.GetDestination().ToUri() + " from NPT routing table entry storage pool.") + } + delete(t.m_rtpool, rtpe.GetDestination().ToUri()) +} diff --git a/route/NamePrefixTableEntry.go b/route/NamePrefixTableEntry.go new file mode 100644 index 0000000..020111e --- /dev/null +++ b/route/NamePrefixTableEntry.go @@ -0,0 +1,173 @@ +// Package route +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/6/9 15:07 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package route + +import ( + common2 "minlib/common" + "minlib/component" +) + +// +// NamePrefixTableEntry +// @Description: 名称前缀表项 +// +type NamePrefixTableEntry struct { + // 名称前缀 + m_namePrefix *component.Identifier + + // 保存路由表池项的切片。切片真好用。 + // 这是因为,由于支持网内缓存,一个名称前缀可能对应多个路由器标识符。 + // 名称前缀标项,和路由表项,是多对多的关系。 + m_rteList []*RoutingTablePoolEntry + // 下一跳列表 + NextHopList +} + +// +// GetNamePrefix +// @Description: 获取所表示的名称前缀 +// @receiver e +// @return *component.Identifier +// +func (e *NamePrefixTableEntry) GetNamePrefix() *component.Identifier { + return e.m_namePrefix +} + +// +// GetRteList +// @Description: 获取与该名称前缀所关联的路由表池项 +// @receiver e +// @return []*RoutingTablePoolEntry +// +func (e *NamePrefixTableEntry) GetRteList() []*RoutingTablePoolEntry { + return e.m_rteList +} + +// +// ResetRteListNextHop +// @Description: 重置播发此名称前缀的所有路由表项的下一跳列表。 +// @receiver e +// +func (e *NamePrefixTableEntry) ResetRteListNextHop() { + for i := 0; i < len(e.m_rteList); i++ { + e.m_rteList[i].Clear() + } +} + +// +// GetRteListSize +// @Description: 返回所关联的路由表池项的数目。即当前名称前缀被哪些路由器所拥有。 +// @receiver e +// @return int +// +func (e *NamePrefixTableEntry) GetRteListSize() int { + return len(e.m_rteList) +} + +// +// GetNexthopList +// @Description: 获取本名称前缀所关联的下一跳列表。 +// @receiver e +// @return []*NextHop +// +func (e *NamePrefixTableEntry) GetNexthopList() *NextHopList { + return &e.NextHopList +} + +// +// GenerateNhlfromRteList +// @Description: 收集此条目的路由条目播发的所有下一个跃点。 +// 其逻辑主要是将所有关联的路由表项的下一跳全部加进来。 +// @receiver e +// +func (e *NamePrefixTableEntry) GenerateNhlfromRteList() { + // 清空旧的下一跳列表 + e.NextHopList.Clear() + // 遍历关联的每个路由表条目 + for i := 0; i < len(e.m_rteList); i++ { + // 遍历路由表条目的下一跳列表 + for j := 0; j < e.m_rteList[i].NextHopList.Size(); j++ { + e.NextHopList.AddNextHop(e.m_rteList[i].nextHops[j]) + } + } +} + +// +// findRtpe +// @Description: 查找指定池条目 +// @receiver e +// @param rtpePtr +// @return int 没找到时返回-1 +// +func (e *NamePrefixTableEntry) findRtpe(rtpePtr *RoutingTablePoolEntry) int { + for i := 0; i < len(e.m_rteList); i++ { + if e.m_rteList[i].IsEqual(rtpePtr) { + return i + } + } + return -1 +} + +// +// deleteRtpe +// @Description: 删除指定索引位置的rtpe +// @receiver e +// @param index +// +func (e *NamePrefixTableEntry) deleteRtpe(index int) { + var tmpList []*RoutingTablePoolEntry + for i := 0; i < len(e.m_rteList); i++ { + if i != index { + tmpList = append(tmpList, e.m_rteList[i]) + } + } + e.m_rteList = tmpList +} + +// +// RemoveRoutingTableEntry +// @Description: 从该NPT条目中删除一个路由表条目 +// @receiver e +// @param rtpePtr +// @return uint64 使用刚刚删除的路由条目的NPT数。 +// +func (e *NamePrefixTableEntry) RemoveRoutingTableEntry(rtpePtr *RoutingTablePoolEntry) uint64 { + index := e.findRtpe(rtpePtr) + if index >= 0 { + // 该条目的使用数减一 + e.m_rteList[index].DecrementUseCount() + // 删除该条目与本名称前缀的关联 + // 1. 删掉:条目 -> 前缀 + delete(e.m_rteList[index].NamePrefixTableEntries, e.GetNamePrefix().ToUri()) + // 2. 删掉:前缀 -> 条目 + e.deleteRtpe(index) + } else { + common2.LogError("Routing entry for: " + rtpePtr.GetDestination().ToUri() + + " not found in NPT entry: " + e.GetNamePrefix().ToUri()) + } + return rtpePtr.m_useCount +} + +// +// AddRoutingTableEntry +// @Description:将路由表池项添加到此NPT项的列表中 +// (提醒:每个RTPE都有下一个跃点列表)。它们用于计算此条目的整体下一跳列表。 +// @receiver e +// @param rtpePtr +// +func (e *NamePrefixTableEntry) AddRoutingTableEntry(rtpePtr *RoutingTablePoolEntry) { + index := e.findRtpe(rtpePtr) + // 先确定它是一个新的路由表条目 + if index < 0 { + // 其使用数加一 + rtpePtr.IncrementUseCount() + // 将其加入list + e.m_rteList = append(e.m_rteList, rtpePtr) + } +} diff --git a/route/NextHop.go b/route/NextHop.go new file mode 100644 index 0000000..07854d9 --- /dev/null +++ b/route/NextHop.go @@ -0,0 +1,115 @@ +// Package route +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/5/5 20:45 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package route + +import ( + "minlib/encoding" + "mlsr/extensions" +) + +// +// NextHop +// @Description: 下一跳 +// +type NextHop struct { + encoding.SelfEncodingBase + ConnectingLogicFaceUri // 指示接口 + RouteCost // 路由开销 + IsHyperbolic bool // 是不是双曲路由(v0.1暂时用不到) +} + +// +// NewNextHop +// @Description: 新建一个下一跳结构体 +// @param uri +// @param cost +// @return *NextHop +// @return error +// +func NewNextHop(uri string, cost uint64) (*NextHop, error) { + // todo 检查传入的参数格式是否有效 + nextHop := new(NextHop) + nextHop.IsHyperbolic = false + nextHop.SetLogicFaceUri(uri) + nextHop.SetRouteCost(cost) + return nextHop, nil +} + +// +// WireEncode +// @Description: 线速编码 +// @receiver l +// @param encoder +// @return int +// @return error +// +func (l *NextHop) WireEncode(encoder *encoding.Encoder) (int, error) { + totalLength := 0 + // TLV-Value + tmpLen, err := l.ConnectingLogicFaceUri.WireEncode(encoder) + if err != nil { + return 0, err + } + totalLength += tmpLen + tmpLen, err = l.RouteCost.WireEncode(encoder) + if err != nil { + return 0, err + } + totalLength += tmpLen + + // TLV-Length + tmpLen, err = encoder.PrependVarNumber(encoding.VlInt(totalLength)) + if err != nil { + return 0, err + } + totalLength += tmpLen + + // TLV-Type + tmpLen, err = encoder.PrependVarNumber(extensions.TlvMlsrNextHop) + if err != nil { + return 0, err + } + totalLength += tmpLen + + return totalLength, nil +} + +// +// WireDecode +// @Description: 线速解码 +// @receiver l +// @param block +// @return error +// +func (l *NextHop) WireDecode(block *encoding.Block) error { + // 检查 TLV-TYPE 是否正确 + if err := encoding.ExpectType(block.GetType(), extensions.TlvMlsrNextHop); err != nil { + return err + } + + // 首先解析子Block + if err := block.ParseSubElements(); err != nil { + return err + } + + // 提取参数 + for _, parameter := range block.GetSubElements() { + switch parameter.GetType() { + case extensions.TlvMlsrConnectingLogicFaceUri: + if err := l.ConnectingLogicFaceUri.WireDecode(parameter); err != nil { + return err + } + case extensions.TlvMlsrRouteCost: + if err := l.RouteCost.WireDecode(parameter); err != nil { + return err + } + } + } + return nil +} diff --git a/route/NextHopList.go b/route/NextHopList.go new file mode 100644 index 0000000..e904b4b --- /dev/null +++ b/route/NextHopList.go @@ -0,0 +1,209 @@ +// Package route +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/5/5 20:44 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package route + +import ( + "errors" + "minlib/encoding" + "mlsr/extensions" + "strconv" +) + +type NextHopContainer []*NextHop + +// +// NextHopList +// @Description: 下一跳列表 +// +type NextHopList struct { + encoding.SelfEncodingBase + nextHops NextHopContainer +} + +// +// ToString +// @Description: 转string。用于测试 +// @receiver l +// @return string +// +func (l *NextHopList) ToString() string { + res := "----------------\n" + for i := 0; i < len(l.nextHops); i++ { + res += l.nextHops[i].LogicFaceUri() + " " + res += strconv.FormatUint(l.nextHops[i].RouteCost.RouteCost(), 10) + " " + res += strconv.FormatBool(l.nextHops[i].IsHyperbolic) + " \n" + } + res += "----------------\n" + return res +} + +// +// GetNextHops +// @Description: 获取下一跳列表 +// @receiver l +// @return []*NextHop +// +func (l *NextHopList) GetNextHops() []*NextHop { + return l.nextHops +} + +// +// findSameUriNextHop +// @Description: 找到uri一致的下一跳表项 +// @receiver l +// @param hop +// @return int 找不到时,返回-1 +// +func (l *NextHopList) findSameUriNextHop(hop *NextHop) int { + for i := 0; i < len(l.nextHops); i++ { + if l.nextHops[i].LogicFaceUri() == hop.LogicFaceUri() { + return i + } + } + return -1 +} + +// +// findSameNextHop +// @Description: 找到uri和cost都一致的下一跳表项 +// @receiver l +// @param hop +// @return int 找不到,返回-1 +// +func (l *NextHopList) findSameNextHop(hop *NextHop) int { + for i := 0; i < len(l.nextHops); i++ { + if (l.nextHops[i].LogicFaceUri() == hop.LogicFaceUri()) && + (l.nextHops[i].RouteCost.RouteCost() == hop.RouteCost.RouteCost()) { + return i + } + } + return -1 +} + +// +// AddNextHop +// @Description: 增加一个下一跳表项。1. 如果不存在同uri,则插入;2. 如果存在,但cost不同,则更新它 +// @receiver l +// @param hop +// +func (l *NextHopList) AddNextHop(hop *NextHop) { + index := l.findSameUriNextHop(hop) + if index < 0 { + l.nextHops = append(l.nextHops, hop) + } else if l.nextHops[index].RouteCost.RouteCost() != hop.RouteCost.RouteCost() { + l.nextHops[index].RouteCost.SetRouteCost(hop.RouteCost.RouteCost()) + } +} + +// +// RemoveNextHop +// @Description: 删除一个下一跳表项。 +// @receiver l +// @param hop +// +func (l *NextHopList) RemoveNextHop(hop *NextHop) { + // 查找该条目索引 + index := l.findSameUriNextHop(hop) + if index >= 0 { + l.nextHops = append(l.nextHops[:index], l.nextHops[index+1:]...) + } +} + +// +// Size +// @Description: 返回表项数目 +// @receiver l +// @return int +// +func (l *NextHopList) Size() int { + return len(l.nextHops) +} + +// +// Clear +// @Description: 清空列表 +// @receiver l +// +func (l *NextHopList) Clear() { + l.nextHops = []*NextHop{} +} + +// +// WireEncode +// @Description: 线速编码 +// @receiver l +// @param encoder +// @return int +// @return error +// +func (l *NextHopList) WireEncode(encoder *encoding.Encoder) (int, error) { + // 判断列表长度,为0则抛出错误 + if len(l.nextHops) == 0 { + return 0, errors.New("Invalid NextHopList, expect at least one NextHop!") + } + totalLength := 0 + // 编码 TLV-VALUE + // 反向遍历,可以保证解码的时候正向输出 + for i := len(l.nextHops) - 1; i >= 0; i-- { + if l.nextHops[i] == nil { + continue + } + tmpLen, err := l.nextHops[i].WireEncode(encoder) + if err != nil { + return 0, err + } + totalLength += tmpLen + } + + // 编码 TLV-LENGTH + tmpLen, err := encoder.PrependVarNumber(encoding.VlInt(totalLength)) + if err != nil { + return 0, err + } + totalLength += tmpLen + + // 编码 TLV-TYPE + tmpLen, err = encoder.PrependVarNumber(extensions.TlvMlsrNextHopList) + if err != nil { + return 0, err + } + totalLength += tmpLen + + return totalLength, nil +} + +// +// WireDecode +// @Description: 线速解码 +// @receiver l +// @param block +// @return error +// +func (l *NextHopList) WireDecode(block *encoding.Block) error { + // 检查 TLV-TYPE 是否正确 + if err := encoding.ExpectType(block.GetType(), extensions.TlvMlsrNextHopList); err != nil { + return err + } + + // 解析子 TLV + if err := block.ParseSubElements(); err != nil { + return err + } + hopContainer := make(NextHopContainer, len(block.GetSubElements())) + for index, element := range block.GetSubElements() { + var nextHop NextHop + err := nextHop.WireDecode(element) + if err != nil { + return err + } + hopContainer[index] = &nextHop + } + l.nextHops = hopContainer + return nil +} diff --git a/route/NextHopList_test.go b/route/NextHopList_test.go new file mode 100644 index 0000000..c2a4c1c --- /dev/null +++ b/route/NextHopList_test.go @@ -0,0 +1,190 @@ +// Package route +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/6/16 17:08 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package route + +import ( + "fmt" + "minlib/encoding" + "testing" +) + +// +// TestNextHopList_AddNextHop +// @Description: 测试构造下一跳列表 +// @param t +// +func TestNextHopList_AddNextHop(t *testing.T) { + // 构造三个下一跳 + nthop1, _ := NewNextHop("/hop1", 10) + nthop2, _ := NewNextHop("/hop2", 20) + nthop3, _ := NewNextHop("/hop3", 30) + + // 构造hoplist + hopList := new(NextHopList) + hopList.AddNextHop(nthop1) + hopList.AddNextHop(nthop2) + hopList.AddNextHop(nthop3) + + // 打印长度 + fmt.Println(hopList.Size()) + + // 打印所有下一跳 + fmt.Println(hopList.ToString()) +} + +// +// TestNextHopList_find +// @Description: 测试查找 +// @param t +// +func TestNextHopList_find(t *testing.T) { + // 构造三个下一跳 + nthop1, _ := NewNextHop("/hop1", 10) + nthop2, _ := NewNextHop("/hop2", 20) + nthop3, _ := NewNextHop("/hop3", 30) + + // 构造hoplist + hopList := new(NextHopList) + hopList.AddNextHop(nthop1) + hopList.AddNextHop(nthop2) + hopList.AddNextHop(nthop3) + + // 测试查找相同Uri + ntTest, _ := NewNextHop("/hop2", 111) + i1 := hopList.findSameUriNextHop(ntTest) + fmt.Println("第二个下标:", i1) + ntTest, _ = NewNextHop("/hopTest", 111) + i2 := hopList.findSameUriNextHop(ntTest) + fmt.Println("没找到下标:", i2) + + // 测试查找完全相同下一跳 + ntTest, _ = NewNextHop("/hop2", 20) + i1 = hopList.findSameNextHop(ntTest) + fmt.Println("第二个下标:", i1) + ntTest.SetRouteCost(555) + i2 = hopList.findSameNextHop(ntTest) + fmt.Println("没找到下标:", i2) +} + +// +// TestNextHopList_RemoveNextHop +// @Description: 测试删除 +// @param t +// +func TestNextHopList_RemoveNextHop(t *testing.T) { + // 构造三个下一跳 + nthop1, _ := NewNextHop("/hop1", 10) + nthop2, _ := NewNextHop("/hop2", 20) + nthop3, _ := NewNextHop("/hop3", 30) + + // 构造hoplist + hopList := new(NextHopList) + hopList.AddNextHop(nthop1) + hopList.AddNextHop(nthop2) + hopList.AddNextHop(nthop3) + + // 打印长度 + fmt.Println(hopList.Size()) + // 打印所有下一跳 + fmt.Println(hopList.ToString()) + + // 删除 + npTest, _ := NewNextHop("/hop1", 10) + hopList.RemoveNextHop(npTest) + // 打印长度 + fmt.Println(hopList.Size()) + // 打印所有下一跳 + fmt.Println(hopList.ToString()) + + // 删除一个只有uri相同 + npTest, _ = NewNextHop("/hop3", 100) + hopList.RemoveNextHop(npTest) + // 打印长度 + fmt.Println(hopList.Size()) + // 打印所有下一跳 + fmt.Println(hopList.ToString()) + + // 删除一个uri不同 + npTest, _ = NewNextHop("/hoppp", 100) + hopList.RemoveNextHop(npTest) + // 打印长度 + fmt.Println(hopList.Size()) + // 打印所有下一跳 + fmt.Println(hopList.ToString()) +} + +// +// TestNextHopList_WireEncode +// @Description: 测试编码 +// @param t +// +func TestNextHopList_WireEncode(t *testing.T) { + // 构造三个下一跳 + nthop1, _ := NewNextHop("/hop1", 10) + nthop2, _ := NewNextHop("/hop2", 20) + nthop3, _ := NewNextHop("/hop3", 30) + // 构造hoplist + hopList := new(NextHopList) + hopList.AddNextHop(nthop1) + hopList.AddNextHop(nthop2) + hopList.AddNextHop(nthop3) + + // 编码 + encoder := encoding.Encoder{} + err := encoder.EncoderReset(encoding.MaxPacketSize, 0) + if err != nil { + fmt.Println("NextHopList编码报错,", err.Error()) + return + } + length, err := hopList.WireEncode(&encoder) + if err != nil { + t.Fatal("NextHopList编码失败", err.Error()) + } + + // 获取编码 + buf, err := encoder.GetBuffer() + if err != nil { + t.Fatal("NextHopList获取buffer结果失败", err.Error()) + } + + // 打印编码结果。如果可以正确解码,则说明编码成功 + fmt.Println(length) + for i := 0; i < len(buf); i++ { + fmt.Print(buf[i], ", ") + } + fmt.Println() +} + +// +// TestNextHopList_WireDecode +// @Description: 测试解码 +// @param t +// +func TestNextHopList_WireDecode(t *testing.T) { + // 由数组构建block + var buf = [58]byte{241, 1, 188, 54, 241, 1, 187, 14, 241, 1, 186, + 1, 10, 241, 1, 185, 5, 47, 104, 111, 112, 49, 241, 1, 187, 14, + 241, 1, 186, 1, 20, 241, 1, 185, 5, 47, 104, 111, 112, 50, 241, + 1, 187, 14, + 241, 1, 186, 1, 30, 241, 1, 185, 5, 47, 104, 111, 112, 51} + block, err := encoding.CreateBlockByBuffer(buf[:], true) + if err != nil { + fmt.Println("创建block失败 ", err.Error()) + return + } + + // 由Block构建结构体,并打印变量 + base := new(NextHopList) + err = base.WireDecode(block) + if err != nil { + fmt.Println("线性解码失败 ", err.Error()) + return + } + fmt.Println(base.ToString()) +} diff --git a/route/NextHop_test.go b/route/NextHop_test.go new file mode 100644 index 0000000..e4ad2e2 --- /dev/null +++ b/route/NextHop_test.go @@ -0,0 +1,96 @@ +// Package route +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/6/16 16:52 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package route + +import ( + "fmt" + "minlib/encoding" + "testing" +) + +// +// TestNewNextHop +// @Description: 测试构造 +// @param t +// +func TestNewNextHop(t *testing.T) { + nexthop, err := NewNextHop("/test", 543) + if err == nil { + fmt.Println(nexthop.IsHyperbolic) + fmt.Println(nexthop.RouteCost.RouteCost()) + fmt.Println(nexthop.LogicFaceUri()) + } else { + fmt.Println("测试NewNextHop报错,", err.Error()) + } +} + +// +// TestNextHop_WireEncode +// @Description: 测试编码 +// @param t +// +func TestNextHop_WireEncode(t *testing.T) { + // 构造 + nexthop, err := NewNextHop("/test", 543) + if err != nil { + fmt.Println("NewNextHop报错,", err.Error()) + return + } + + // 编码 + encoder := encoding.Encoder{} + err = encoder.EncoderReset(encoding.MaxPacketSize, 0) + if err != nil { + fmt.Println("NextHop编码报错,", err.Error()) + return + } + length, err := nexthop.WireEncode(&encoder) + if err != nil { + t.Fatal("NextHop编码失败", err.Error()) + } + + // 获取编码 + buf, err := encoder.GetBuffer() + if err != nil { + t.Fatal("NextHop获取buffer结果失败", err.Error()) + } + + // 打印编码结果。如果可以正确解码,则说明编码成功 + fmt.Println(length) + for i := 0; i < len(buf); i++ { + fmt.Print(buf[i], ", ") + } + fmt.Println() +} + +// +// TestNextHop_WireDecode +// @Description: 测试解码 +// @param t +// +func TestNextHop_WireDecode(t *testing.T) { + // 由数组构建block + var buf = [19]byte{241, 1, 187, 15, 241, 1, 186, 2, 2, 31, 241, 1, 185, 5, 47, 116, 101, 115, 116} + block, err := encoding.CreateBlockByBuffer(buf[:], true) + if err != nil { + fmt.Println("创建block失败 ", err.Error()) + return + } + + // 由Block构建结构体,并打印变量 + base := new(NextHop) + err = base.WireDecode(block) + if err != nil { + fmt.Println("线性解码失败 ", err.Error()) + return + } + fmt.Println(base.IsHyperbolic) + fmt.Println(base.RouteCost.RouteCost()) + fmt.Println(base.LogicFaceUri()) +} diff --git a/route/RouteCost.go b/route/RouteCost.go new file mode 100644 index 0000000..e061b28 --- /dev/null +++ b/route/RouteCost.go @@ -0,0 +1,77 @@ +// Package lsa +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/3/10 10:39 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package route + +import ( + "math" + "minlib/component" + "minlib/encoding" + "mlsr/extensions" +) + +const ( + DEFAULT_LINK_COST = 10 + NON_ADJACENT_COST = math.MaxUint64 +) + +// +// RouteCost +// @Description: 链路开销 +// +type RouteCost struct { + component.CommonUint64 +} + +// +// RouteCost +// @Description: 获取开销 +// @receiver l +// @return uint64 +// +func (l *RouteCost) RouteCost() uint64 { + return l.CommonUint64.Value() +} + +// +// SetRouteCost +// @Description: 设置开销 +// @receiver l +// @param linkCost +// +func (l *RouteCost) SetRouteCost(linkCost uint64) { + l.CommonUint64.SetValue(linkCost) +} + +// +// WireEncode +// @Description: 线速编码 +// @receiver l +// @param encoder +// @return int +// @return error +// +func (l *RouteCost) WireEncode(encoder *encoding.Encoder) (int, error) { + l.CommonUint64.SetTlvType(extensions.TlvMlsrRouteCost) + return l.CommonUint64.WireEncode(encoder) +} + +// +// WireDecode +// @Description: 线速解码 +// @receiver l +// @param block +// @return error +// +func (l *RouteCost) WireDecode(block *encoding.Block) error { + // 检查type是否正确 + if err := encoding.ExpectType(block.GetType(),extensions.TlvMlsrRouteCost); err!=nil { + return err + } + return l.CommonUint64.WireDecode(block) +} diff --git a/route/RouteCost_test.go b/route/RouteCost_test.go new file mode 100644 index 0000000..bf4d5b3 --- /dev/null +++ b/route/RouteCost_test.go @@ -0,0 +1,81 @@ +// Package route +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/5/26 12:02 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package route + +import ( + "bytes" + "fmt" + "minlib/encoding" + "testing" +) + +// +// TestRouteCost_SetRouteCost +// @Description: 测试设置属性 +// @param t +// +func TestRouteCost_SetRouteCost(t *testing.T) { + lsaSequenceNumber := RouteCost{} + lsaSequenceNumber.SetRouteCost(77) + if lsaSequenceNumber.RouteCost() != 77 { + t.Fatal("RouteCost设置失败") + } +} + +// +// TestRouteCost_WireEncode +// @Description: 测试编码 +// @param t +// +func TestRouteCost_WireEncode(t *testing.T) { + linkCost := RouteCost{} + linkCost.SetRouteCost(1234) + + encoder := encoding.Encoder{} + encoder.EncoderReset(encoding.MaxPacketSize, 0) + length, err := linkCost.WireEncode(&encoder) + if err != nil { + t.Fatal("RouteCost编码失败", err.Error()) + } + + buf, err := encoder.GetBuffer() + if err != nil { + t.Fatal("RouteCost获取buffer结果失败", err.Error()) + } + + res := []byte{241, 1, 186, 2, 4, 210} + if length != 6 || !bytes.Equal(buf, res) { + fmt.Println("buf: ", buf) + fmt.Println("res: ", res) + t.Fatal("RouteCost编码内容错误", length, buf) + } +} + +// +// TestRouteCost_WireDecode +// @Description: 测试解码 +// @param t +// +func TestRouteCost_WireDecode(t *testing.T) { + var buf [6]byte = [6]byte{241, 1, 186, 2, 4, 210} + block, err := encoding.CreateBlockByBuffer(buf[:], true) + if err != nil { + t.Fatal("RouteCost创建Block失败", err.Error()) + } + + linkCost := RouteCost{} + err = linkCost.WireDecode(block) + if err != nil { + t.Fatal("RouteCost解码失败", err.Error()) + } + + if linkCost.RouteCost() != 1234 { + t.Fatal("RouteCost解码内容错误", linkCost.RouteCost()) + } +} diff --git a/route/RouterMap.go b/route/RouterMap.go new file mode 100644 index 0000000..80834c6 --- /dev/null +++ b/route/RouterMap.go @@ -0,0 +1,182 @@ +// Package route +// @Author: Wang Feng +// @Description: +// 参考博文:https://www.zhaoyanchang.com/detail/31.html +// @Version: 0.1.0 +// @Date: 2022/5/10 15:26 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package route + +import ( + "encoding/json" + "minlib/component" + "mlsr/lsa" + "strconv" + "sync" +) + +// +// RouterMap +// @Description: 给每个路由器标识分配一个索引号 +// +type RouterMap struct { + // 存储:路由器到索引值的映射 + routerToNumberMap map[string]int + // 存储:索引值到路由器的映射 + numberToRouterMap map[int]string + // 存储:路由器URI到其路由器标识的映射 => 相同URI的路由器标识被视为同一个路由器标识 + uriToIdentifierMap map[string]*component.Identifier + + // 初始值为0,每次加一递增 + mappingIndex int + + // 并发加锁 + mapLock *sync.RWMutex +} + +// +// ToString +// @Description: 转string,便于测试查看map的状态 +// @receiver m +// @return string +// +func (m *RouterMap) ToString() string { + s := "" + s += "MappingIndex:" + strconv.Itoa(m.mappingIndex) + " " + // 第一个map + dataType, _ := json.Marshal(m.routerToNumberMap) + dataString := string(dataType) + s += dataString + " " + // 第二个map + dataType, _ = json.Marshal(m.numberToRouterMap) + dataString = string(dataType) + s += dataString + " " + return s +} + +// +// Store +// @Description: 存入一个路由器标识 +// @receiver m +// @param routerName +// +func (m *RouterMap) Store(routerName *component.Identifier) { + m.mapLock.Lock() + uri := routerName.ToUri() + _, ok := m.uriToIdentifierMap[uri] + if !ok { + // 如果不存在,则将其存入三张map + m.uriToIdentifierMap[uri] = routerName + m.routerToNumberMap[uri] = m.mappingIndex + m.numberToRouterMap[m.mappingIndex] = uri + m.mappingIndex++ + } + m.mapLock.Unlock() +} + +// +// CreateFromAdjLsdb +// @Description: 根据从LSDB中取出来的邻接LSA,进行RouterMap的生成 +// @receiver m +// @param lsas +// @return error +// +func (m *RouterMap) CreateFromAdjLsdb(lsas []lsa.ILsa) error { + for i := 0; i < len(lsas); i++ { + // 1. 先将LSA解析为邻接LSA + adjLsa := (lsas[i]).(*lsa.AdjLsa) + // 2. 将每个邻接lsa的源路由,以及所有该源路由的邻接路由器,都加入到map中 + m.Store(adjLsa.GetOriginRouter()) + adjs := adjLsa.GetAdjList() + for j := 0; j < len(adjs); j++ { + m.Store(adjs[j].GetNeighborRouterIdentifier()) + } + } + return nil +} + +// +// Delete +// @Description: 删除一个路由器标识。在实际使用中用不到,故先小写其字母。 +// @receiver m +// @param routerName +// +func (m *RouterMap) delete(routerName *component.Identifier) { + m.mapLock.Lock() + uri := routerName.ToUri() + value, ok := m.routerToNumberMap[uri] + if ok { + // 如果存在,则将其从三张map中删除 + delete(m.uriToIdentifierMap, uri) + delete(m.routerToNumberMap, uri) + delete(m.numberToRouterMap, value) + } + m.mapLock.Unlock() +} + +// +// GetRouterNameByMappingNo +// @Description: 根据索引获取路由器标识 +// @receiver m +// @param index +// @return *component.Identifier +// @return bool +// +func (m *RouterMap) GetRouterNameByMappingNo(index int) (*component.Identifier, bool) { + m.mapLock.Lock() + defer m.mapLock.Unlock() + value, ok := m.numberToRouterMap[index] + if ok { + idenValue := m.uriToIdentifierMap[value] + return idenValue, true + } + return nil, false +} + +// +// GetMappingNoByRouterName +// @Description: 根据标识获取索引 +// @receiver m +// @param routerName +// @return int +// @return bool +// +func (m *RouterMap) GetMappingNoByRouterName(routerName *component.Identifier) (int, bool) { + m.mapLock.Lock() + defer m.mapLock.Unlock() + value, ok := m.routerToNumberMap[routerName.ToUri()] + return value, ok +} + +// +// GetMapSize +// @Description: 获取路由器数目 +// @receiver m +// @return int +// +func (m *RouterMap) GetMapSize() int { + m.Init() + return len(m.routerToNumberMap) +} + +// +// Init +// @Description: 必须先执行该函数,以初始化RouterMap +// @receiver m* +// +func (m *RouterMap) Init() { + if m.uriToIdentifierMap == nil { + m.uriToIdentifierMap = make(map[string]*component.Identifier) + } + if m.routerToNumberMap == nil { + m.routerToNumberMap = make(map[string]int) + } + if m.numberToRouterMap == nil { + m.numberToRouterMap = make(map[int]string) + } + if m.mapLock == nil { + m.mapLock = new(sync.RWMutex) + } +} diff --git a/route/RouterMap_test.go b/route/RouterMap_test.go new file mode 100644 index 0000000..824de3b --- /dev/null +++ b/route/RouterMap_test.go @@ -0,0 +1,105 @@ +// Package Calculator +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/5/10 16:53 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package route + +import ( + "fmt" + common2 "minlib/common" + "minlib/component" + "minlib/logicface" + "mlsr/common" + "mlsr/lsa" + "mlsr/lsdb" + "testing" +) + +const testConfigPath = "D:\\" + common.DefaultConfFileName + +func TestRouterMap_CreateFromAdjLsdb(t *testing.T) { + // 1. 构造LSDB + // 配置文件初始化 + mlsrConfig, err := common.ParseConfig(testConfigPath) + if err != nil { + fmt.Println("配置文件解析错误") + } + // 调度器初始化 + sche := new(lsdb.MlsrScheduler) + sche.Init() + // LogicFace初始化(假设初始化,暂时用不到) + face := new(logicface.LogicFace) + // LSDB初始化 + lsdb := new(lsdb.Lsdb) + lsdb.Init(mlsrConfig, sche, face) + // 强行构建邻接lsa + lsdb.BuildAndInstallOwnAdjLsa() + + // 2. 取出LSDB中的邻接LSA + lsas, err := lsdb.GetLSAsByType(lsa.LsaADJACENCYType) + if err != nil { + common2.LogError("Get Lsa in calculateLsRoutingTable error, ", err) + } + common2.LogInfo("邻接lsa数目: ", len(lsas)) + + // 3. 构造RouterMap + rMap := new(RouterMap) + rMap.Init() + err = rMap.CreateFromAdjLsdb(lsas) + + // 4. 打印RouterMap状态 + common2.LogInfo(rMap) + common2.LogInfo(rMap.ToString()) +} + +// +// TestRouterMap_Store +// @Description: 测试RouterMap +// @param t +// +func TestRouterMap_Store(t *testing.T) { + iden, _ := component.CreateIdentifierByString("/test/weforever") + iden1, _ := component.CreateIdentifierByString("/test/weforever1") + iden2, _ := component.CreateIdentifierByString("/test/weforever2") + // 存入 + rmap := new(RouterMap) + rmap.Init() + rmap.Store(iden) + rmap.Store(iden1) + rmap.Store(iden2) + + // 取出标识 + index, b := rmap.GetMappingNoByRouterName(iden2) + fmt.Println(index, b) + + // 取出一个同URI的标识 + idenNew2, _ := component.CreateIdentifierByString("/test/weforever2") + index, b = rmap.GetMappingNoByRouterName(idenNew2) + fmt.Println(index, b) + + // 取出Iden + idenCat, bb := rmap.GetRouterNameByMappingNo(0) + fmt.Println(idenCat.ToUri(), bb) + idenCat, bb = rmap.GetRouterNameByMappingNo(1) + fmt.Println(idenCat.ToUri(), bb) + idenCat, bb = rmap.GetRouterNameByMappingNo(2) + fmt.Println(idenCat.ToUri(), bb) + idenCat, bb = rmap.GetRouterNameByMappingNo(3) + fmt.Println(bb) + + // 查看map大小 + fmt.Println(rmap.GetMapSize()) + + // 删除后再查看 + rmap.delete(iden) + rmap.delete(idenNew2) + fmt.Println(rmap.GetMapSize()) + idenCat, bb = rmap.GetRouterNameByMappingNo(0) + fmt.Println(bb) + idenCat, bb = rmap.GetRouterNameByMappingNo(2) + fmt.Println(bb) +} diff --git a/route/RoutingTable.go b/route/RoutingTable.go new file mode 100644 index 0000000..1958963 --- /dev/null +++ b/route/RoutingTable.go @@ -0,0 +1,297 @@ +// Package route +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/5/9 16:53 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package route + +import ( + "errors" + common2 "minlib/common" + "minlib/component" + "minlib/encoding" + "mlsr/common" + "mlsr/extensions" + "mlsr/lsa" + "mlsr/lsdb" + "time" +) + +type RoutingTable struct { + // lsa存储库 + lsdb *lsdb.Lsdb + // 配置参数 + mlsrConfig *common.MlsrConfig + // 任务调度器 + scheduler *lsdb.MlsrScheduler + + // 路由条目列表 + rTable []*RoutingTableEntry + // 路由计算间隔时间 + m_routingCalcInterval time.Duration + // 路由表是否正在计算 + m_isRoutingTableCalculating bool + // 是否已经安排了路由计算 + m_isRouteCalculationScheduled bool + // 当前路由器的邻接LSA是否存在 + m_ownAdjLsaExist bool + + // 双曲坐标状态 => 暂时用不到 + m_hyperbolicState uint32 +} + +// +// Init +// @Description: 初始化 +// @receiver t +// @param mc +// @param ms +// @param mlsdb +// +func (t *RoutingTable) Init(mc *common.MlsrConfig, + ms *lsdb.MlsrScheduler, mlsdb *lsdb.Lsdb) { + // 初始化任务调度器 + t.scheduler = ms + // 初始化配置项 + t.mlsrConfig = mc + // 初始化lsdb + t.lsdb = mlsdb + + // 路由计算间隔时间 + t.m_routingCalcInterval = mc.GetRoutingCalcInterval() + // 路由计算状态 + t.m_isRoutingTableCalculating = false + t.m_isRouteCalculationScheduled = false + + // todo 事件监听:如果监听到了传来的删除本地邻接LSA操作 + + // todo 事件监听:如果监听到了传来的安装本地邻接LSA操作 + t.m_ownAdjLsaExist = true + + // todo 事件监听:如果监听到了传来的安装或更新本地邻接LSA操作 + //err := t.ScheduleRoutingTableCalculation() + //if err != nil { + // common2.LogError("RoutingTable.Init ScheduleRoutingTableCalculation error.") + //} +} + +// +// Calculate +// @Description: 计算网络中每个路由器的下一个跃点列表。 +// @receiver t +// +func (t *RoutingTable) Calculate() { + if !t.m_isRoutingTableCalculating { + // 先将状态改为正在做路由计算 + t.m_isRoutingTableCalculating = true + + // 做邻接路由计算 + t.calculateLsRoutingTable() + + // 恢复计算状态 + t.m_isRouteCalculationScheduled = false + t.m_isRoutingTableCalculating = false + } else { + // 安排指定时间之后执行一个路由计算 + err := t.ScheduleRoutingTableCalculation() + if err != nil { + common2.LogError("Calculate error,Schedule RoutingTableCalculation error, ", err) + } + } +} + +// +// AddNextHop +// @Description: 向路由表条目添加下一个跃点。 +// @receiver t +// @param destRouter 要修改其RTE的目标路由器。 +// @param hop 要添加到RTE的下一个跃点。 +// +func (t *RoutingTable) AddNextHop(destRouter *component.Identifier, hop *NextHop) { + common2.LogInfo("Adding " + hop.LogicFaceUri() + " for destination " + destRouter.ToUri()) + + // 查找路由表项 + rte := t.FindRoutingTableEntry(destRouter) + // 如果没有这条表项,则构建并加入 + if rte == nil { + rte = new(RoutingTableEntry) + rte.AddNextHop(hop) + t.rTable = append(t.rTable, rte) + } else { + // 如果有,则直接编辑该条目,增加一个下一跳. + // todo: 这块需要测试,是否在go中可以直接根据引用进行表项的修改 + rte.AddNextHop(hop) + } +} + +// +// FindRoutingTableEntry +// @Description: 根据某个目的路由器,查找对应的路由表项 +// @receiver t +// @param destRouter +// @return *RoutingTableEntry +// +func (t *RoutingTable) FindRoutingTableEntry(destRouter *component.Identifier) *RoutingTableEntry { + uri := destRouter.ToUri() + for i := 0; i < len(t.rTable); i++ { + if t.rTable[i].GetDestination().ToUri() == uri { + return t.rTable[i] + } + } + return nil +} + +// +// ScheduleRoutingTableCalculation +// @Description: 仅当尚未计划计算事件时,才在事件计划程序中计划计算事件。 +// @receiver t +// +func (t *RoutingTable) ScheduleRoutingTableCalculation() error { + if !t.m_isRouteCalculationScheduled { + common2.LogInfo("Scheduling routing table calculation in ", t.m_routingCalcInterval) + job, err := t.scheduler.ScheduleTaskAfterDuration(t.m_routingCalcInterval, 1).Do(t.Calculate) + if err != nil { + return err + } + job.LimitRunsTo(1) + t.scheduler.StartAsync() + t.m_isRouteCalculationScheduled = true + } + return nil +} + +// +// calculateLsRoutingTable +// @Description: 计算链路状态路由表。 +// @receiver t +// +func (t *RoutingTable) calculateLsRoutingTable() { + if t.lsdb.GetIsBuildAdjLsaScheduled() { + common2.LogError("Adjacency build is scheduled, routing table can not be calculated :(") + return + } + + // 我们只在LS中检查这一点,因为我们从不移除自己的坐标LSA。而如果没有任何邻居,则移除自己的邻接LSA + if !t.m_ownAdjLsaExist { + return + } + + t.clearRoutingTable() + + // 构造路由映射表 + lsas, err := t.lsdb.GetLSAsByType(lsa.LsaADJACENCYType) + if err != nil { + common2.LogError("Get Lsa in calculateLsRoutingTable error, ", err) + } + rMap := new(RouterMap) + rMap.Init() + err = rMap.CreateFromAdjLsdb(lsas) + if err != nil { + common2.LogError("create RouterMap from lsdb error in calculateLsRoutingTable error, ", err) + } + + nRouters := rMap.GetMapSize() + + // 构造路由计算器 + calculator := new(LinkStateRoutingTableCalculator) + calculator.SetRouterNum(nRouters) + // 使用计算器去计算路由 + routers, nexthops, err := calculator.CalculatePath(rMap, t.mlsrConfig, t.lsdb) + if err != nil { + common2.LogError("CalculatePath in calculateLsRoutingTable error, ", err) + } + // 将路由计算结果依次加入路由表 + for i := 0; i < len(routers); i++ { + t.AddNextHop(routers[i], nexthops[i]) + } + + // todo: 开始根据新计算的路由去更新NPT表 + common2.LogInfo("Calling Update NPT With new Route") + //todo +} + +// +// clearRoutingTable +// @Description: 清空路由表 +// @receiver t +// +func (t *RoutingTable) clearRoutingTable() { + t.rTable = []*RoutingTableEntry{} +} + +// +// WireEncode +// @Description: 线速编码 +// @receiver l +// @param encoder +// @return int +// @return error +// +func (t *RoutingTable) WireEncode(encoder *encoding.Encoder) (int, error) { + // 判断列表长度,为0则抛出错误 + if len(t.rTable) == 0 { + return 0, errors.New("Invalid RoutingTable, expect at least one RoutingTableEntry!") + } + totalLength := 0 + // 编码 TLV-VALUE + // 反向遍历,可以保证解码的时候正向输出 + for i := len(t.rTable) - 1; i >= 0; i-- { + if t.rTable[i] == nil { + continue + } + tmpLen, err := t.rTable[i].WireEncode(encoder) + if err != nil { + return 0, err + } + totalLength += tmpLen + } + + // 编码 TLV-LENGTH + tmpLen, err := encoder.PrependVarNumber(encoding.VlInt(totalLength)) + if err != nil { + return 0, err + } + totalLength += tmpLen + + // 编码 TLV-TYPE + tmpLen, err = encoder.PrependVarNumber(extensions.TlvMlsrRoutingTable) + if err != nil { + return 0, err + } + totalLength += tmpLen + + return totalLength, nil +} + +// +// WireDecode +// @Description: 线速解码 +// @receiver l +// @param block +// @return error +// +func (t *RoutingTable) WireDecode(block *encoding.Block) error { + // 检查 TLV-TYPE 是否正确 + if err := encoding.ExpectType(block.GetType(), extensions.TlvMlsrRoutingTable); err != nil { + return err + } + + // 解析子 TLV + if err := block.ParseSubElements(); err != nil { + return err + } + tableEntries := make([]*RoutingTableEntry, len(block.GetSubElements())) + for index, element := range block.GetSubElements() { + var tableEntry RoutingTableEntry + err := tableEntry.WireDecode(element) + if err != nil { + return err + } + tableEntries[index] = &tableEntry + } + t.rTable = tableEntries + return nil +} diff --git a/route/RoutingTableCalculator.go b/route/RoutingTableCalculator.go new file mode 100644 index 0000000..8b65e05 --- /dev/null +++ b/route/RoutingTableCalculator.go @@ -0,0 +1,278 @@ +// Package route +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/5/9 17:30 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package route + +import ( + "errors" + "mlsr/lsa" + "mlsr/lsdb" + "strconv" + "sync" +) + +type RoutingTableCalculator struct { + // 邻接矩阵 + adjMatrix [][]uint64 + // 路由器数目 + m_nRouters int + + // 稀疏矩阵的形式存储本机路由器的邻接链路信息 + vNoLink int // 链路数 + links []int // 链路索引号 + linkCosts []uint64 // 链路开销 +} + +// +// ToString +// @Description: 将路由表计算器的成员变量转为string。用于测试。 +// @receiver c +// @return string +// +func (c *RoutingTableCalculator) ToString() string { + res := "\n" + res += "路由器数目:" + strconv.Itoa(c.m_nRouters) + "\n" + res += "邻接矩阵:\n" + for i := 0; i < len(c.adjMatrix); i++ { + for j := 0; j < len(c.adjMatrix[i]); j++ { + res += "[" + res += strconv.Itoa(i) + res += "," + res += strconv.Itoa(j) + res += "," + res += strconv.FormatUint(c.adjMatrix[i][j], 10) + res += "] " + } + res += "\n" + } + res += "本机相邻链路数:" + strconv.Itoa(c.vNoLink) + "\n" + res += "本机相邻链路索引:" + res += "[" + for i := 0; i < len(c.links); i++ { + res += strconv.Itoa(c.links[i]) + if i < (len(c.links) - 1) { + res += "," + } + } + res += "]\n" + res += "本机相邻链路开销:" + res += "[" + for i := 0; i < len(c.linkCosts); i++ { + res += strconv.Itoa(int(c.linkCosts[i])) + if i < (len(c.linkCosts) - 1) { + res += "," + } + } + res += "]\n" + return res +} + +// +// SetRouterNum +// @Description: 设置路由器个数 +// @receiver c +// @param n +// +func (c *RoutingTableCalculator) SetRouterNum(n int) { + c.m_nRouters = n +} + +// +// allocateAdjMatrix +// @Description: 给邻接矩阵分配所需的空间 +// @receiver c +// +func (c *RoutingTableCalculator) allocateAdjMatrix() { + c.adjMatrix = make([][]uint64, c.m_nRouters) + // 根据路由器的数目,确定邻接矩阵的长度 + for i := 0; i < c.m_nRouters; i++ { + c.adjMatrix[i] = make([]uint64, c.m_nRouters) + } +} + +// +// initMatrix +// @Description: 为矩阵的每个单元设置非邻接成本(NON_ADJACENT_COST,如-12345),以确保内存安全。 +// @receiver c +// +func (c *RoutingTableCalculator) initMatrix() { + for i := 0; i < c.m_nRouters; i++ { + for j := 0; j < c.m_nRouters; j++ { + c.adjMatrix[i][j] = NON_ADJACENT_COST + } + } +} + +// +// makeAdjMatrix +// @Description: 构建一个要去计算的邻接矩阵 +// @receiver c +// @param lsdb +// @param p +// +func (c *RoutingTableCalculator) makeAdjMatrix(lsdb *lsdb.Lsdb, + rp *RouterMap) error { + // 1. 在Map中表示每个LSA + // 先从Lsdb中取出邻接LSA + lsas, err := lsdb.GetLSAsByType(lsa.LsaADJACENCYType) + if err != nil { + return errors.New("No Found adj lsa in LSDB. ") + } + // 2. 再对每个lsa进行操作 + for i := 0; i < len(lsas); i++ { + // 2.1 取出源路由器,找其索引值,作为邻接矩阵的行号 + row, b := rp.GetMappingNoByRouterName(lsas[i].GetOriginRouter()) + if !b { + return errors.New("No Found origin router in routerMap. ") + } + // 2.2 依次取出该源路由器对应的所有邻接路由器标识,以及链路开销,来构建邻接矩阵 + adjlsa, ok := (lsas[i]).(*lsa.AdjLsa) + if !ok { + return errors.New("Lsa from LSDB is not adjlsa. ") + } + adjs := adjlsa.GetAdjList() + for j := 0; j < len(adjs); j++ { + // 2.2.1 取列数 + col, b := rp.GetMappingNoByRouterName(adjs[j].GetNeighborRouterIdentifier()) + if !b { + return errors.New("No Found neighbor router in routerMap. ") + } + // 2.2.2 取开销 + linkCost := adjs[j].AdjLsaLinkCost.LinkCost() + // 2.2.3 将开销放入邻接矩阵 + if (row < c.m_nRouters) && (col < c.m_nRouters) { + c.adjMatrix[row][col] = linkCost + } + } + } + + // todo 检查路由器A到路由器B的链路开销,若正向开销与反向开销不同,则两者均取较大值 + + return nil +} + +// +// writeAdjMatrixLog +// @Description: 将格式化的邻接矩阵写入调试日志 +// @receiver c 包含邻接矩阵数据的映射 +// @param p +// +func (c *RoutingTableCalculator) writeAdjMatrixLog(p *sync.Map) { + // todo 输出调试信息,验证功能是否正常运行 +} + +// +// getNumOfLinkfromAdjMatrix +// @Description: 返回矩阵中一个路由器的邻接数(不包含其本身) +// @receiver c +// @param sRouter 计算邻接数的路由器 +// +func (c *RoutingTableCalculator) getNumOfLinkfromAdjMatrix(sRouter int) int { + num := 0 + for i := 0; i < c.m_nRouters; i++ { + if (c.adjMatrix[sRouter][i] != NON_ADJACENT_COST) && (i != sRouter) { + num++ + } + } + return num +} + +// +// freeAdjMatrix +// @Description: 释放内存 +// @receiver c +// +func (c *RoutingTableCalculator) freeAdjMatrix() { + c.adjMatrix = [][]uint64{} +} + +// +// adjustAdMatrix +// @Description: 在邻接矩阵中调整链路成本。除要更改的链路外,该源路由器的其它链路全部设置为不可达 +// @receiver c +// @param source 要调整其邻接的源路由器 +// @param link 要调整的源的邻接 +// @param linkCost 更改为的成本。 +// +func (c *RoutingTableCalculator) adjustAdMatrix(source int, link int, linkCost uint64) { + for i := 0; i < c.m_nRouters; i++ { + if i == link { + c.adjMatrix[source][i] = linkCost + } else { + c.adjMatrix[source][i] = NON_ADJACENT_COST + } + } +} + +// +// getLinksFromAdjMatrix +// @Description: 用一些路由器的链路成本填充临时变量 +// 获取一些路由器的邻接和链路开销的稀疏列表。 +// 由于这是稀疏的,这意味着在生成这些数组时,如果矩阵中i处没有邻接, +// 这些临时变量将不会在i处包含非邻接代价(NON_ADJACENT_COST,-12345),而是包含下一个有效邻接的值。 +// @receiver c +// @param links 链接映射号的一个整数数组 +// @param linkCosts 存储链路开销的一个双指针数组 +// @param source 被调整值的路由器 +// +func (c *RoutingTableCalculator) getLinksFromAdjMatrix(source int) { + index := 0 + for i := 0; i < c.m_nRouters; i++ { + if (c.adjMatrix[source][i] != NON_ADJACENT_COST) && (i != source) { + c.links[index] = i + c.linkCosts[index] = c.adjMatrix[source][i] + index++ + } + } +} + +// +// allocateLinks +// @Description: 分配一个足以容纳多路径计算临时值的数组 +// @receiver c +// +func (c *RoutingTableCalculator) allocateLinks() { + c.links = make([]int, c.vNoLink) +} + +// +// allocateLinkCosts +// @Description: 分配内存 +// @receiver c +// +func (c *RoutingTableCalculator) allocateLinkCosts() { + c.linkCosts = make([]uint64, c.vNoLink) +} + +// +// freeLinks +// @Description: 释放内存 +// @receiver c +// +func (c *RoutingTableCalculator) freeLinks() { + c.links = []int{} +} + +// +// freeLinksCosts +// @Description: 释放内存 +// @receiver c +// +func (c *RoutingTableCalculator) freeLinksCosts() { + c.linkCosts = []uint64{} +} + +// +// setNoLink +// @Description: 设置链路数,即多路径中的“多”所代指的个数 +// @receiver c +// @param nl +// +func (c *RoutingTableCalculator) setNoLink(nl int) { + c.vNoLink = nl +} diff --git a/route/RoutingTableCalculator_test.go b/route/RoutingTableCalculator_test.go new file mode 100644 index 0000000..6f852ac --- /dev/null +++ b/route/RoutingTableCalculator_test.go @@ -0,0 +1,73 @@ +// Package route +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/5/26 12:10 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package route + +import ( + "fmt" + common2 "minlib/common" + "minlib/logicface" + "mlsr/common" + "mlsr/lsa" + "mlsr/lsdb" + "testing" +) + +func TestRoutingTableCalculator_Init(t *testing.T) { + // 构造路由计算器 + rtc := new(RoutingTableCalculator) + + // 1. 根据LSDB构造邻接矩阵 + // 1.1 构造LSDB + // 配置文件初始化 + mlsrConfig, err := common.ParseConfig(testConfigPath) + if err != nil { + fmt.Println("配置文件解析错误") + } + // 调度器初始化 + sche := new(lsdb.MlsrScheduler) + sche.Init() + // LogicFace初始化(假设初始化,暂时用不到) + face := new(logicface.LogicFace) + // LSDB初始化 + mlsdb := new(lsdb.Lsdb) + mlsdb.Init(mlsrConfig, sche, face) + // 安装本机adjlsa + mlsdb.BuildAndInstallOwnAdjLsa() + // todo 传入一些其它虚拟lsa,这些lsa与本机lsa共同形成一个小型路由拓扑 + + // 1.2 构造RouterMap + lsas, err := mlsdb.GetLSAsByType(lsa.LsaADJACENCYType) + if err != nil { + common2.LogError("Get Lsa in calculateLsRoutingTable error, ", err) + } + common2.LogInfo("邻接lsa数目: ", len(lsas)) + rMap := new(RouterMap) + rMap.Init() + err = rMap.CreateFromAdjLsdb(lsas) + // 打印rm + common2.LogInfo(rMap.ToString()) + + // 1.3 根据RouterMap,和lsdb中的adjlsa,构造邻接矩阵 + // 设置路由器数目 + rtc.SetRouterNum(rMap.GetMapSize()) + // 分配矩阵空间 + rtc.allocateAdjMatrix() + // 初始化矩阵值 + rtc.initMatrix() + // 构造矩阵 + err = rtc.makeAdjMatrix(mlsdb, rMap) + if err != nil { + common2.LogError("邻接矩阵构造失败") + } + + // 2. 打印输出路由表计算器 + fmt.Println(rtc.ToString()) + fmt.Println("源路由器的邻接数目:", rtc.getNumOfLinkfromAdjMatrix(0)) + fmt.Println("另一个路由器的邻接数目:", rtc.getNumOfLinkfromAdjMatrix(1)) +} diff --git a/route/RoutingTableEntry.go b/route/RoutingTableEntry.go new file mode 100644 index 0000000..c059b3c --- /dev/null +++ b/route/RoutingTableEntry.go @@ -0,0 +1,146 @@ +// Package route +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/5/5 20:41 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package route + +import ( + "minlib/component" + "minlib/encoding" + "mlsr/extensions" +) + +// +// RoutingTableEntry +// @Description: 路由表条目 +// +type RoutingTableEntry struct { + m_destination *component.Identifier + NextHopList +} + +// +// ToString +// @Description: 转string。用于测试 +// @receiver e +// @return string +// +func (e *RoutingTableEntry) ToString() string { + res := "" + res += "目的路由器: " + e.m_destination.ToUri() + "\n" + res += e.NextHopList.ToString() + return res +} + +// +// NewRoutingTableEntry +// @Description: 新建一个路由表条目 +// @param dest +// @return *RoutingTableEntry +// @return error +// +func NewRoutingTableEntry(dest *component.Identifier) (*RoutingTableEntry, error) { + // todo: 判断dest是否格式有效 + rte := new(RoutingTableEntry) + rte.m_destination = dest + return rte, nil +} + +// +// GetDestination +// @Description: 获取目的路由器 +// @receiver e +// @return *component.Identifier +// +func (e *RoutingTableEntry) GetDestination() *component.Identifier { + return e.m_destination +} + +// +// SetDestination +// @Description: 设置目的路由器 +// @receiver e +// @param dest +// +func (e *RoutingTableEntry) SetDestination(dest *component.Identifier) { + e.m_destination = dest +} + +// +// WireEncode +// @Description: 线速编码 +// @receiver l +// @param encoder +// @return int +// @return error +// +func (e *RoutingTableEntry) WireEncode(encoder *encoding.Encoder) (int, error) { + totalLength := 0 + // TLV-Value + tmpLen, err := e.NextHopList.WireEncode(encoder) + if err != nil { + return 0, err + } + totalLength += tmpLen + tmpLen, err = e.m_destination.WireEncode(encoder) + if err != nil { + return 0, err + } + totalLength += tmpLen + + // TLV-Length + tmpLen, err = encoder.PrependVarNumber(encoding.VlInt(totalLength)) + if err != nil { + return 0, err + } + totalLength += tmpLen + + // TLV-Type + tmpLen, err = encoder.PrependVarNumber(extensions.TlvMlsrRoutingTableEntry) + if err != nil { + return 0, err + } + totalLength += tmpLen + + return totalLength, nil +} + +// +// WireDecode +// @Description: 线速解码 +// @receiver l +// @param block +// @return error +// +func (e *RoutingTableEntry) WireDecode(block *encoding.Block) error { + // 检查 TLV-TYPE 是否正确 + if err := encoding.ExpectType(block.GetType(), extensions.TlvMlsrRoutingTableEntry); err != nil { + return err + } + + // 首先解析子Block + if err := block.ParseSubElements(); err != nil { + return err + } + + // 提取参数 + for _, parameter := range block.GetSubElements() { + switch parameter.GetType() { + case encoding.TlvIdentifier: + iden := component.Identifier{} + if err := iden.WireDecode(parameter); err != nil { + return err + } + e.m_destination = &iden + case extensions.TlvMlsrNextHopList: + if err := e.NextHopList.WireDecode(parameter); err != nil { + return err + } + } + } + return nil +} diff --git a/route/RoutingTableEntry_test.go b/route/RoutingTableEntry_test.go new file mode 100644 index 0000000..69562ea --- /dev/null +++ b/route/RoutingTableEntry_test.go @@ -0,0 +1,105 @@ +// Package route +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/6/17 11:29 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package route + +import ( + "fmt" + "minlib/component" + "minlib/encoding" + "testing" +) + +// +// TestNewRoutingTableEntry +// @Description: 测试构造路由表项 +// @param t +// +func TestNewRoutingTableEntry(t *testing.T) { + // 构造 + iden, _ := component.CreateIdentifierByString("/pku/abc") + rte, err := NewRoutingTableEntry(iden) + if err != nil { + fmt.Println("构造路由表项失败") + } + // 打印 + fmt.Println(rte.GetDestination().ToUri()) +} + +// +// TestRoutingTableEntry_WireEncode +// @Description: 测试编码 +// @param t +// +func TestRoutingTableEntry_WireEncode(t *testing.T) { + // 构造三个下一跳 + nthop1, _ := NewNextHop("/hop1", 10) + nthop2, _ := NewNextHop("/hop2", 20) + nthop3, _ := NewNextHop("/hop3", 30) + // 构造路由表项 + iden, _ := component.CreateIdentifierByString("/pku/run") + hopList, _ := NewRoutingTableEntry(iden) + hopList.AddNextHop(nthop1) + hopList.AddNextHop(nthop2) + hopList.AddNextHop(nthop3) + + // 编码 + encoder := encoding.Encoder{} + err := encoder.EncoderReset(encoding.MaxPacketSize, 0) + if err != nil { + fmt.Println("RoutingTableEntry编码报错,", err.Error()) + return + } + length, err := hopList.WireEncode(&encoder) + if err != nil { + t.Fatal("RoutingTableEntry编码失败", err.Error()) + } + + // 获取编码 + buf, err := encoder.GetBuffer() + if err != nil { + t.Fatal("RoutingTableEntry获取buffer结果失败", err.Error()) + } + + // 打印编码结果。如果可以正确解码,则说明编码成功 + fmt.Println(length) + for i := 0; i < len(buf); i++ { + fmt.Print(buf[i], ", ") + } + fmt.Println() +} + +// +// TestRoutingTableEntry_WireDecode +// @Description: 测试解码 +// @param t +// +func TestRoutingTableEntry_WireDecode(t *testing.T) { + // 由数组构建block + var buf = [80]byte{241, 1, 189, 76, 101, 16, 100, 2, 0, 47, + 100, 4, 0, 112, 107, 117, 100, 4, 0, 114, 117, 110, 241, + 1, 188, 54, 241, 1, 187, 14, 241, 1, 186, 1, 10, 241, 1, + 185, 5, 47, 104, 111, 112, 49, 241, 1, 187, 14, 241, 1, + 186, 1, 20, 241, 1, 185, 5, 47, 104, 111, 112, 50, 241, + 1, 187, 14, + 241, 1, 186, 1, 30, 241, 1, 185, 5, 47, 104, 111, 112, 51} + block, err := encoding.CreateBlockByBuffer(buf[:], true) + if err != nil { + fmt.Println("创建block失败 ", err.Error()) + return + } + + // 由Block构建结构体,并打印变量 + base := new(RoutingTableEntry) + err = base.WireDecode(block) + if err != nil { + fmt.Println("线性解码失败 ", err.Error()) + return + } + fmt.Println(base.ToString()) +} diff --git a/route/RoutingTablePoolEntry.go b/route/RoutingTablePoolEntry.go new file mode 100644 index 0000000..2fbc287 --- /dev/null +++ b/route/RoutingTablePoolEntry.go @@ -0,0 +1,127 @@ +// Package route +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/6/10 11:02 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package route + +import ( + "minlib/component" +) + +// +// RoutingTablePoolEntry +// @Description: 路由表池条目。 +// +type RoutingTablePoolEntry struct { + RoutingTableEntry + // 记录使用本池条目的所有名称前缀 NamePrefix-名称前缀表项 + NamePrefixTableEntries map[string]*NamePrefixTableEntry + m_useCount uint64 // 记录本条目被多少条名称前缀所使用 +} + +// +// Init +// @Description: 初始化目的路由器 +// @receiver pe +// @param dest +// +func (pe *RoutingTablePoolEntry) Init(dest *component.Identifier) { + pe.m_destination = dest + pe.m_useCount = 1 +} + +// +// InitRTE +// @Description: 初始化路由表项和使用数 +// @receiver pe +// @param rte +// @param useCount +// +func (pe *RoutingTablePoolEntry) InitRTE(rte *RoutingTableEntry, useCount uint64) { + pe.m_destination = rte.m_destination + pe.nextHops = rte.GetNextHops() + pe.m_useCount = useCount +} + +// +// InitDest +// @Description: 初始化目的路由器和使用数 +// @receiver pe +// @param dest +// @param useCount +// +func (pe *RoutingTablePoolEntry) InitDest(dest *component.Identifier, useCount uint64) { + pe.m_destination = dest + pe.m_useCount = useCount +} + +// +// IsEqual +// @Description: 判断两个路由表池条目是否相等 +// @receiver pe +// @param npe +// @return bool +// +func (pe *RoutingTablePoolEntry) IsEqual(npe *RoutingTablePoolEntry) bool { + if pe.GetDestination().ToUri() == npe.GetDestination().ToUri() && + pe.NextHopList.Size() == pe.NextHopList.Size() { + b := true + for i := 0; i < pe.NextHopList.Size(); i++ { + if pe.GetNextHops()[i].LogicFaceUri() != npe.GetNextHops()[i].LogicFaceUri() || + pe.GetNextHops()[i].RouteCost.RouteCost() != npe.GetNextHops()[i].RouteCost.RouteCost() { + b = false + } + } + return b + } + return false +} + +// +// GetUseCount +// @Description: 获取使用数 +// @receiver pe +// @return uint64 +// +func (pe *RoutingTablePoolEntry) GetUseCount() uint64 { + return pe.m_useCount +} + +// +// IncrementUseCount +// @Description: 使用数加一,并返回 +// @receiver pe +// @return uint64 +// +func (pe *RoutingTablePoolEntry) IncrementUseCount() uint64 { + pe.m_useCount++ + return pe.m_useCount +} + +// +// DecrementUseCount +// @Description: 使用数减一,并返回 +// @receiver pe +// @return uint64 +// +func (pe *RoutingTablePoolEntry) DecrementUseCount() uint64 { + if pe.m_useCount != 0 { + pe.m_useCount-- + return pe.m_useCount + } + return 0 +} + +// +// SetNexthopList +// @Description: 设置下一跳列表 +// @receiver pe +// @param list +// +func (pe *RoutingTablePoolEntry) SetNexthopList(list NextHopList) { + pe.NextHopList = list +} diff --git a/route/RoutingTable_test.go b/route/RoutingTable_test.go new file mode 100644 index 0000000..74ec4f6 --- /dev/null +++ b/route/RoutingTable_test.go @@ -0,0 +1,69 @@ +// Package route +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/6/17 11:28 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package route + +import ( + common2 "minlib/common" + "minlib/logicface" + "mlsr/common" + "mlsr/lsdb" + "testing" +) + +// +// TestRoutingTable_Init +// @Description: 测试初始化 +// @param t +// +func TestRoutingTable_Init(t *testing.T) { + // 1. 组件初始化 + // 配置文件初始化 + mlsrConfig, err := common.ParseConfig(testConfigPath) + if err != nil { + common2.LogInfo("配置文件解析错误") + } + // 调度器初始化 + sche := new(lsdb.MlsrScheduler) + sche.Init() + // LogicFace初始化(假设初始化,暂时用不到) + face := new(logicface.LogicFace) + // LSDB初始化 + mlsdb := new(lsdb.Lsdb) + mlsdb.Init(mlsrConfig, sche, face) + + // 2. 路由表初始化 + rt := new(RoutingTable) + rt.Init(mlsrConfig, sche, mlsdb) +} + +// +// TestRoutingTable_AddNextHop +// @Description: 测试构造路由表及其拓扑 +// @param t +// +func TestRoutingTable_AddNextHop(t *testing.T) { +} + +// +// TestRoutingTable_Calculate +// @Description: 测试计算 +// @param t +// +func TestRoutingTable_Calculate(t *testing.T) { + +} + +// +// TestRoutingTable_ScheduleRoutingTableCalculation +// @Description: 测试安排路由计算 +// @param t +// +func TestRoutingTable_ScheduleRoutingTableCalculation(t *testing.T) { + +} diff --git a/tests/goCron_test.go b/tests/goCron_test.go index ab44355..10300a6 100644 --- a/tests/goCron_test.go +++ b/tests/goCron_test.go @@ -16,7 +16,11 @@ import ( ) func printHHH() { - println("hello,gocron.",time.Now().String()) + println("hello,gocron.", time.Now().String()) +} + +func printHHHWithPara(s string) { + println("hello,gocron.", time.Now().String(), s) } // @@ -31,7 +35,7 @@ func Test_Loop(t *testing.T) { s := gocron.NewScheduler(time.UTC) // Job:只知道它提供的任务(task, go函数),因此只能执行与该任务相关的操作,例如防止自己超出需要很长时间的先前任务。 //j, _ := s.Every(10).Seconds().Do(func() { time.Sleep(2 * time.Second); printHHH()}) - j, _ := s.Every(1).Seconds().Do(func() { printHHH()}) + j, _ := s.Every(1).Seconds().Do(func() { printHHH() }) // Executor:顾名思义,executor 只负责调用Scheduler(调度程序)发送时,Job(作业)交给它的任务(go 函数)。 fmt.Println(j.IsRunning()) @@ -50,7 +54,7 @@ func Test_Loop(t *testing.T) { func Test_IsRunning(t *testing.T) { s := gocron.NewScheduler(time.UTC) // Job:只知道它提供的任务(task, go函数),因此只能执行与该任务相关的操作,例如防止自己超出需要很长时间的先前任务。 - j, _ := s.Every(10).Seconds().Do(func() { time.Sleep(2 * time.Second); printHHH()}) + j, _ := s.Every(10).Seconds().Do(func() { time.Sleep(2 * time.Second); printHHH() }) // Executor:顾名思义,executor 只负责调用Scheduler(调度程序)发送时,Job(作业)交给它的任务(go 函数)。 fmt.Println(j.IsRunning()) @@ -67,10 +71,41 @@ func Test_IsRunning(t *testing.T) { time.Sleep(1 * time.Second) fmt.Println(j.IsRunning()) + + for true { + time.Sleep(1 * time.Second) + } } var task = func() {} +func Test_Run1Time(t *testing.T) { + // 触发时间设置到五秒后 + timeRun := time.Now() + fmt.Println("run time: ", timeRun) + + dur := 5 * time.Second + timeRun = timeRun.Add(dur) + + fmt.Println("run time: ", timeRun) + + // 设置触发事件 + s := gocron.NewScheduler(time.UTC) + _, _ = s.At(timeRun).Do(printHHHWithPara, "xixixi") + //go func() { + // for { + // fmt.Println("Run count", job.RunCount()) + // time.Sleep(time.Second) + // } + //}() + s.StartBlocking() + + // 保活 + //for true { + // time.Sleep(10 * time.Second) + //} +} + // // Test_RunCount // @Description: 测试job.RunCount() @@ -78,14 +113,14 @@ var task = func() {} // func Test_RunCount(t *testing.T) { s := gocron.NewScheduler(time.UTC) - job, _ := s.Every(1).Second().Do(task) + job, _ := s.Every(1).Second().Do(printHHHWithPara, "xxx") go func() { for { fmt.Println("Run count", job.RunCount()) time.Sleep(time.Second) } }() - s.StartAsync() + s.StartBlocking() // 保活 for true { @@ -123,7 +158,7 @@ func Test_LastRun_NextRun(t *testing.T) { func Test_LimitRunsTo(t *testing.T) { s := gocron.NewScheduler(time.UTC) job, _ := s.Every(1).Second().Do(printHHH) - job.LimitRunsTo(2) + job.LimitRunsTo(1) s.StartAsync() // 保活 @@ -135,24 +170,33 @@ func Test_LimitRunsTo(t *testing.T) { // // Test_ScheduleAtTimes -// @Description: 设置事件在指定时间节点被触发 +// @Description: 设置事件在指定时间节点被触发 => 常见用法 // @param t // func Test_ScheduleAtTimes(t *testing.T) { + // 触发时间设定在五秒后 + timeRun := time.Now() + fmt.Println("预定时间(假性):", timeRun) + dur := 4 * time.Second + timeRun = timeRun.Add(dur) + fmt.Println("预定时间:", timeRun) + // 设置一个触发时间 s := gocron.NewScheduler(time.UTC) - job, _ := s.Every(1).Day().At("20:02").Do(func() {printHHH()}) + //job, _ := s.Every(100).Seconds().StartAt(timeRun).Do(printHHHWithPara, "heiheihei") + job, _ := s.Every(3).Seconds().StartAt(timeRun).Do(printHHH) + job.LimitRunsTo(3) s.StartAsync() - fmt.Println(job.ScheduledAtTime()) + fmt.Println("预定触发时间: ", job.ScheduledAtTime()) // 设置多个触发时间。当取时间时,返回最早的那个 - job1, _ := s.Every(1).Day().At("10:30;08:00").Do(task) - fmt.Println(job1.ScheduledAtTime()) + //job1, _ := s.Every(1).Day().At("10:30;08:00").Do(task) + //fmt.Println(job1.ScheduledAtTime()) // Output: // 10:30 // 8:0 // 当取所有时间时,返回一个时间列表 - fmt.Println(job1.ScheduledAtTimes()) + //fmt.Println(job1.ScheduledAtTimes()) // Output: // [8:0 10:30] @@ -179,4 +223,12 @@ func Test_SingletonMode(t *testing.T) { time.Sleep(3 * time.Second) fmt.Println("Last run:", job.LastRun()) } -} \ No newline at end of file +} + +func Test_Running(t *testing.T) { + var s *gocron.Scheduler + if (s == nil) || (!s.IsRunning()) { + fmt.Println("hahhaha") + } + fmt.Println(s == nil) +} diff --git a/tests/main_test.go b/tests/main_test.go new file mode 100644 index 0000000..784f64a --- /dev/null +++ b/tests/main_test.go @@ -0,0 +1,30 @@ +package tests + +import ( + "fmt" + "github.com/tidwall/buntdb" + "minlib/logicface" + "mlsr/lsa" + "testing" +) + +func TestName(t *testing.T) { + fmt.Println("abc") + s := logicface.LogicFaceTypeEther + fmt.Println("def", s) + + ss := lsa.AdjLsaAdjacencyInfo{} + ss.Status = 1 + + TestBuntDb(t) +} + +func TestBuntDb(t *testing.T) { + // https://github.com/tidwall/buntdb + db, err := buntdb.Open(":memory:") + if err != nil { + fmt.Println("bunt db open err") + } + _ = db.Close() + fmt.Println("bunt db closed") +} diff --git a/utils/FileUtils.go b/utils/FileUtils.go new file mode 100644 index 0000000..a23c2de --- /dev/null +++ b/utils/FileUtils.go @@ -0,0 +1,121 @@ +// Package utils +// @Author: Jianming Que (阙建明) +// @Description: +// @Version: 1.0.0 +// @Date: 2021/4/26 11:27 上午 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// +package utils + +import ( + "fmt" + "io/ioutil" + "minlib/common" + "os" + "strings" +) + +func GetRelPath(path string) string { + if strings.HasPrefix(path, "~") { + homePath, err := common.Home() + if err != nil { + common.LogFatal("Get current user home path failed!") + } + return homePath + path[1:] + } else { + return path + } +} + +func ReadFromFile(path string) (string, error) { + file, err := os.Open(path) + if err != nil { + return "", err + } + defer file.Close() + if content, err := ioutil.ReadAll(file); err != nil { + return "", err + } else { + return string(content), nil + } +} + +// WriteFile 写入文件,文件不存在则创建,如在则重写内容 +func ClearAndWriteFile(path string, str string) { + _, b := IsFile(path) + var f *os.File + var err error + if b { + //打开文件, + f, _ = os.OpenFile(path, os.O_RDWR|os.O_TRUNC, 0666) + } else { + //新建文件 + f, err = os.Create(path) + } + + //使用完毕,需要关闭文件 + defer func() { + err = f.Close() + if err != nil { + fmt.Println("err = ", err) + } + }() + + if err != nil { + fmt.Println("err = ", err) + return + } + _, err = f.WriteString(str) + if err != nil { + fmt.Println("err = ", err) + } +} + +// WriteFile 写入文件,文件不存在则创建,如在则追加内容 +func WriteFile(path string, str string) { + _, b := IsFile(path) + var f *os.File + var err error + if b { + //打开文件, + f, _ = os.OpenFile(path, os.O_APPEND, 0666) + } else { + //新建文件 + f, err = os.Create(path) + } + + //使用完毕,需要关闭文件 + defer func() { + err = f.Close() + if err != nil { + fmt.Println("err = ", err) + } + }() + + if err != nil { + fmt.Println("err = ", err) + return + } + _, err = f.WriteString(str) + if err != nil { + fmt.Println("err = ", err) + } +} + +// IsExists 判断路径是否存在 +func IsExists(path string) (os.FileInfo, bool) { + f, err := os.Stat(path) + return f, err == nil || os.IsExist(err) +} + +// IsDir 判断所给路径是否为文件夹 +func IsDir(path string) (os.FileInfo, bool) { + f, flag := IsExists(path) + return f, flag && f.IsDir() +} + +// IsFile 判断所给路径是否为文件 +func IsFile(path string) (os.FileInfo, bool) { + f, flag := IsExists(path) + return f, flag && !f.IsDir() +} diff --git a/utils/Numbers.go b/utils/Numbers.go new file mode 100644 index 0000000..3a7f588 --- /dev/null +++ b/utils/Numbers.go @@ -0,0 +1,13 @@ +// Package utils +// @Author: Wang Feng +// @Description: +// @Version: 0.1.0 +// @Date: 2022/5/10 11:48 +// @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室 +// + +package utils + +const ( +) + diff --git a/utils/Signal.go b/utils/Signal.go new file mode 100644 index 0000000..54dfc49 --- /dev/null +++ b/utils/Signal.go @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2014-2022, Peking University Shenzhen Graduate School + * + * This file is part of MIN-Sync. + * See AUTHORS.md for complete list of MIN-Sync authors and contributors. + * + * MIN-Sync is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * MIN-Sync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * MIN-Sync, e.g., in COPYING.md file. If not, see . + * + + * This file incorporates work covered by the following copyright and + * permission notice: + + * The MIT License (MIT) + + * Copyright (c) 2000 Arash Partow + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package utils + +import ( + "minlib/common" + "reflect" + + "github.com/liyue201/gostl/ds/vector" +) + +type DisconnectFunction = func() + +/** \brief represents a connection to a signal + * \note This type is copyable. Any copy can be used to disconnect. + */ +type Connection struct { + disconnect DisconnectFunction +} + +/** \brief disconnects from the signal + * \note If the connection is already disconnected, or if the Signal has been cleared, + * this operation has no effect. + * \warning During signal emission, attempting to disconnect a connection other than + * the executing handler's own connection results in undefined behavior. + */ +func (c *Connection) Disconnect() { + if c.IsConnected() { + c.disconnect() + } +} + +/** \brief check if connected to the signal + */ +func (c *Connection) IsConnected() bool { + return c.disconnect != nil +} + +/** \brief compare for equality + * + * Two connections are equal if they both point to the same connection that isn't disconnected, + * or they are both disconnected. + */ +func (c *Connection) Equals(other *Connection) bool { + return (!c.IsConnected() && !other.IsConnected()) || reflect.ValueOf(c.disconnect).Pointer() == reflect.ValueOf(other.disconnect).Pointer() +} + +/* Factory Method for Connection */ +func NewConnectionByDisconnectFunc(df DisconnectFunction) *Connection { + c := new(Connection) + c.disconnect = df + return c +} + +/** \brief represents a function that can connect to the signal + */ +type Handler = func(...interface{}) + +/** \brief provides a lightweight signal / event system + * + * To declare a signal: + * factory method; + * To connect to a signal: + * owner.signalName.connect(f); + * Multiple functions can connect to the same signal. + * To emit a signal from owner: + * this->signalName.emit(arg1, arg2); + * + * \sa signal-emit.hpp allows owner's derived classes to emit signals + */ + +type Signal struct { + slots SlotList + /** \brief is a signal handler executing? + */ + isExecuting bool + currentSlot SlotIterator +} + +/** \brief connects a handler to the signal + * \note If invoked from a handler, the new handler won't receive the current emitted signal. + * \warning The handler is permitted to disconnect itself, but it must ensure its validity. + */ +func (s *Signal) Connect(handler Handler) *Connection { + slot := NewSlotByHandler(handler) + slotIterator := s.slots.Insert(s.slots.End(), slot) + + /* disconnect management */ + df := func() { + s.disconnect(slotIterator) + } + slot.Disconnect = df /* strong reference */ + weakPtr := NewConnectionByDisconnectFunc(df) /* weak reference */ + slot.Connection = weakPtr /* slot takes over connection */ + return weakPtr +} + +/** \brief connects a single-shot handler to the signal + * + * After the handler is executed once, it is automatically disconnected. + */ +func (s *Signal) ConnectSingleShot(handler Handler) *Connection { + slot := new(Slot) + slotIterator := s.slots.Insert(s.slots.End(), slot) + + df := func() { + s.disconnect(slotIterator) + } + conn := NewConnectionByDisconnectFunc(df) + + slot.Disconnect = df + slot.Connection = conn + slot.Handler = func(args ...interface{}) { + handler(args) + conn.Disconnect() + } + + return conn +} + +/** \retval true if there is no connection + */ +func (s *Signal) IsEmpty() bool { + return !s.isExecuting && s.slots.Empty() +} + +/** \brief emits a signal + * \param args arguments passed to all handlers + * \warning Emitting the signal from a handler is undefined behavior. + * \note If a handler throws, the exception will be propagated to the caller + * who emits this signal, and some handlers may not be executed. + */ +func (s *Signal) Emit(args ...interface{}) { + if s.IsEmpty() { + return + } + + defer func() { + s.isExecuting = false + }() + s.isExecuting = true + + it := s.slots.Begin() + last := s.slots.Last() + isLast := false + for !isLast { + isLast = it.Equal(last) + s.currentSlot = it + s.currentSlot.Value().(*Slot).Handler(args...) + + // disconnect while executing current slot + if s.currentSlot.Equal(s.slots.End()) { + it = s.slots.Erase(it) + } else { + it = it.Next().(SlotIterator) + } + } +} + +/** \brief disconnects the handler in a slot + */ +func (s *Signal) disconnect(iterator SlotIterator) { + if s.isExecuting { + // during signal emission, only the currently executing handler can be disconnected + if !s.currentSlot.Equal(iterator) { + common.LogWarn("cannot disconnect another handler from a handler") + return + } + + // this serves to indicate that the current slot needs to be erased from the list + // after it finishes executing; we cannot do it here because of bug #2333 + s.currentSlot = s.slots.End() + // destruct + iterator.Value().(*Slot).Destruct() + } else { + iterator.Value().(*Slot).Destruct() + s.slots.Erase(iterator) + } +} + +/* Clear + */ +func (s *Signal) Clear() { + /* \warn should only called when signal is not executing */ + s.currentSlot = nil + + /* clean all slots */ + it := s.slots.Begin() + for !it.Equal(s.slots.End()) { + it.Value().(*Slot).Destruct() + it = it.Next().(*vector.VectorIterator) + } + + /* clean slots container */ + s.slots.Clear() + // s.slots = nil is not needed +} + +/* factory method for Signal */ +func NewSignal() *Signal { + s := new(Signal) + s.isExecuting = false + s.slots = vector.New() + return s +} + +type SlotList = *vector.Vector + +type SlotIterator = *vector.VectorIterator + +type Slot struct { + /** \brief weak reference of disconnect function + connection's lifetime is taken over by slot + */ + *Connection + /** \brief the handler function who will receive emitted signals + */ + Handler Handler + /** \brief the disconnect function which will disconnect this handler + * + * In practice this is the Signal.Disconnect method bound to a ptr + * pointing at this slot. + * + * Connection also has a ptr which references the same function object. + * When the slot is erased or the signal is destructed, this function object is + * destructed, and the related Connections cannot disconnect this slot again. + */ + Disconnect DisconnectFunction +} + +/* Clear +\warn cannot use the slot which has been already Destructed +*/ +func (s *Slot) Destruct() { + s.Handler = nil + s.Disconnect = nil + s.Connection.disconnect = nil + s.Connection = nil +} + +func NewSlotByHandler(handler Handler) *Slot { + s := new(Slot) + s.Handler = handler + return s +} diff --git a/utils/Signal_test.go b/utils/Signal_test.go new file mode 100644 index 0000000..fabb674 --- /dev/null +++ b/utils/Signal_test.go @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2014-2022, Peking University Shenzhen Graduate School + * + * This file is part of MIN-Sync. + * See AUTHORS.md for complete list of MIN-Sync authors and contributors. + * + * MIN-Sync is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * MIN-Sync is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * MIN-Sync, e.g., in COPYING.md file. If not, see . + * + + * This file incorporates work covered by the following copyright and + * permission notice: + + * The MIT License (MIT) + + * Copyright (c) 2000 Arash Partow + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package utils + +import ( + "testing" +) + +func TestZeroSlot(t *testing.T) { + s := NewSignal() + s.Emit() +} + +func TestTwoListener(t *testing.T) { + s := NewSignal() + + hit1 := 0 + hit2 := 0 + + func1 := func(...interface{}) { + hit1 += 1 + } + func2 := func(...interface{}) { + hit2 += 1 + } + + s.Connect(func1) + s.Connect(func2) + + s.Emit() + + if hit1 != 1 || hit2 != 1 { + t.Fatalf("Two listener failed.") + } +} + +func TestOneArgument(t *testing.T) { + s := NewSignal() + + hit1 := 0 + + func1 := func(args ...interface{}) { + hit1 += 1 + num := args[0].(int) + if num != 8106 { + panic("Test one argument failed.") + } + } + + s.Connect(func1) + + s.Emit(8106) + + if hit1 != 1 { + t.Fatalf("Two listener failed.") + } +} + +func TestTwoArguments(t *testing.T) { + s := NewSignal() + + hit1 := 0 + + func1 := func(args ...interface{}) { + hit1 += 1 + num := args[0].(int) + if num != 8106 { + panic("Test two arguments failed.") + } + num1 := args[1].(int) + if num1 != 8107 { + panic("Test two arguments failed.") + } + } + + s.Connect(func1) + + s.Emit(8106, 8107) + + if hit1 != 1 { + t.Fatalf("Two listener failed.") + } +} + +func TestManualDisconnect(t *testing.T) { + s := NewSignal() + + hit1 := 0 + + func1 := func(args ...interface{}) { + hit1 += 1 + } + + conn := s.Connect(func1) + + s.Emit() + if hit1 != 1 { + t.Fatalf("ManualDisconnect failed.") + } + + if !conn.IsConnected() { + t.Fatalf("ManualDisconnect failed.") + } + + conn.Disconnect() + + if conn.IsConnected() { + t.Fatalf("ManualDisconnect failed.") + } + + s.Emit() + if hit1 != 1 { + t.Fatalf("ManualDisconnect failed.") + } + + conn.Disconnect() +} + +func TestManualDisconnectDestructed(t *testing.T) { + s := NewSignal() + + hit1 := 0 + + func1 := func(args ...interface{}) { + hit1 += 1 + } + + conn := s.Connect(func1) + + s.Emit() + if hit1 != 1 { + t.Fatalf("ManualDisconnectDestructed failed.") + } + + s.Clear() + if conn.IsConnected() { + t.Fatalf("ManualDisconnectDestructed failed.") + } + s.Clear() + s.Emit() + s.Clear() + s.Emit() + if hit1 != 1 { + t.Fatalf("ManualDisconnectDestructed failed.") + } +} + +func TestConnectSingleSlot(t *testing.T) { + s := NewSignal() + + hit1 := 0 + + func1 := func(args ...interface{}) { + hit1 += 1 + } + + conn := s.ConnectSingleShot(func1) + + s.Emit() + if hit1 != 1 { + t.Fatalf("ConnectSingleSlot failed.") + } + + if conn.IsConnected() { + t.Fatalf("ConnectSingleSlot failed.") + } +} + +func TestConnectSingleShotDisconnected(t *testing.T) { + s := NewSignal() + + hit1 := 0 + + func1 := func(args ...interface{}) { + hit1 += 1 + } + + conn := s.ConnectSingleShot(func1) + + if !conn.IsConnected() { + t.Fatalf("ConnectSingleShotDisconnected failed.") + } + + conn.Disconnect() + + if conn.IsConnected() { + t.Fatalf("ConnectSingleShotDisconnected failed.") + } + + s.Emit() + if hit1 != 0 { + t.Fatalf("ConnectSingleShotDisconnected failed.") + } +} + +func TestConnectSingleShot1(t *testing.T) { + s := NewSignal() + + hit1 := 0 + + func1 := func(args ...interface{}) { + hit1 += 1 + } + + conn := s.ConnectSingleShot(func1) + + s.Emit() + if hit1 != 1 { + t.Fatalf("ConnectSingleShot1 failed.") + } + + if conn.IsConnected() { + t.Fatalf("ConnectSingleShot1 failed.") + } + + s.Emit() + if hit1 != 1 { + t.Fatalf("ConnectSingleShot1 failed.") + } +} + +func TestConnectInHandler(t *testing.T) { + s := NewSignal() + + hit1 := 0 + hit2 := 0 + hasHandler2 := false + + func1 := func(args ...interface{}) { + hit1 += 1 + if !hasHandler2 { + s.Connect(func(i ...interface{}) { + hit2 += 1 + hasHandler2 = true + }) + } + } + + s.Connect(func1) + + s.Emit() + if hit1 != 1 || hit2 != 0 { + t.Fatalf("ConnectInHandler failed.") + } + + s.Emit() + if hit1 != 2 || hit2 != 1 { + t.Fatalf("ConnectInHandler failed.") + } +} + +func TestDisconnectSelfInHandler(t *testing.T) { + s := NewSignal() + + hit1 := 0 + + var conn *Connection + func1 := func(args ...interface{}) { + hit1 += 1 + if !conn.IsConnected() { + t.Fatalf("DisconnectSelfInHandler failed.") + } + conn.Disconnect() + if conn.IsConnected() { + t.Fatalf("DisconnectSelfInHandler failed.") + } + if s.IsEmpty() { + t.Fatalf("DisconnectSelfInHandler failed.") + } + } + + conn = s.Connect(func1) + s.Emit() + + if hit1 != 1 { + t.Fatalf("DisconnectSelfInHandler failed.") + } + + if conn.IsConnected() { + t.Fatalf("DisconnectSelfInHandler failed.") + } + + if !s.IsEmpty() { + t.Fatalf("DisconnectSelfInHandler failed.") + } + + s.Emit() + + if hit1 != 1 { + t.Fatalf("DisconnectSelfInHandler failed.") + } +} + +func TestThrowInHandler(t *testing.T) { + s := NewSignal() + + hit1 := 0 + func1 := func(args ...interface{}) { + hit1 += 1 + panic("hello") + } + + s.Connect(func1) + defer func() { + if r := recover(); r != nil { + if hit1 != 1 { + t.Fatalf("ThrowInHandler failed.") + } + } + }() + s.Emit() +}