mirror of
https://github.com/KusakabeShi/EtherGuard-VPN.git
synced 2025-08-17 20:27:01 +02:00
Negative cycle detection and removal
This commit is contained in:
@@ -143,6 +143,7 @@ type GraphRecalculateSetting struct {
|
|||||||
JitterTolerance float64
|
JitterTolerance float64
|
||||||
JitterToleranceMultiplier float64
|
JitterToleranceMultiplier float64
|
||||||
NodeReportTimeout float64
|
NodeReportTimeout float64
|
||||||
|
TimeoutCheckInterval float64
|
||||||
RecalculateCoolDown float64
|
RecalculateCoolDown float64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -347,11 +347,9 @@ func NewDevice(tapDevice tap.Device, id config.Vertex, bind conn.Bind, logger *L
|
|||||||
if IsSuperNode {
|
if IsSuperNode {
|
||||||
device.Event_server_pong = superevents.Event_server_pong
|
device.Event_server_pong = superevents.Event_server_pong
|
||||||
device.Event_server_register = superevents.Event_server_register
|
device.Event_server_register = superevents.Event_server_register
|
||||||
device.Event_server_NhTable_changed = superevents.Event_server_NhTable_changed
|
|
||||||
device.LogLevel = sconfig.LogLevel
|
device.LogLevel = sconfig.LogLevel
|
||||||
device.SuperConfig = sconfig
|
device.SuperConfig = sconfig
|
||||||
device.SuperConfigPath = configpath
|
device.SuperConfigPath = configpath
|
||||||
go device.RoutineRecalculateNhTable()
|
|
||||||
} else {
|
} else {
|
||||||
device.EdgeConfigPath = configpath
|
device.EdgeConfigPath = configpath
|
||||||
device.EdgeConfig = econfig
|
device.EdgeConfig = econfig
|
||||||
@@ -367,11 +365,11 @@ func NewDevice(tapDevice tap.Device, id config.Vertex, bind conn.Bind, logger *L
|
|||||||
go device.RoutineSetEndpoint()
|
go device.RoutineSetEndpoint()
|
||||||
go device.RoutineRegister()
|
go device.RoutineRegister()
|
||||||
go device.RoutineSendPing()
|
go device.RoutineSendPing()
|
||||||
go device.RoutineRecalculateNhTable()
|
|
||||||
go device.RoutineSpreadAllMyNeighbor()
|
go device.RoutineSpreadAllMyNeighbor()
|
||||||
go device.RoutineResetConn()
|
go device.RoutineResetConn()
|
||||||
go device.RoutineClearL2FIB()
|
go device.RoutineClearL2FIB()
|
||||||
}
|
}
|
||||||
|
go device.RoutineRecalculateNhTable()
|
||||||
// create queues
|
// create queues
|
||||||
|
|
||||||
device.queue.handshake = newHandshakeQueue()
|
device.queue.handshake = newHandshakeQueue()
|
||||||
|
@@ -327,7 +327,7 @@ func (device *Device) process_ping(peer *Peer, content path.PingMsg) error {
|
|||||||
Timediff: device.graph.GetCurrentTime().Sub(content.Time),
|
Timediff: device.graph.GetCurrentTime().Sub(content.Time),
|
||||||
}
|
}
|
||||||
if device.DRoute.P2P.UseP2P && time.Now().After(device.graph.NhTableExpire) {
|
if device.DRoute.P2P.UseP2P && time.Now().After(device.graph.NhTableExpire) {
|
||||||
device.graph.UpdateLentancy(content.Src_nodeID, device.ID, PongMSG.Timediff, true, false)
|
device.graph.UpdateLatency(content.Src_nodeID, device.ID, PongMSG.Timediff, true, false)
|
||||||
}
|
}
|
||||||
body, err := path.GetByte(&PongMSG)
|
body, err := path.GetByte(&PongMSG)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -354,7 +354,7 @@ func (device *Device) process_ping(peer *Peer, content path.PingMsg) error {
|
|||||||
func (device *Device) process_pong(peer *Peer, content path.PongMsg) error {
|
func (device *Device) process_pong(peer *Peer, content path.PongMsg) error {
|
||||||
if device.DRoute.P2P.UseP2P {
|
if device.DRoute.P2P.UseP2P {
|
||||||
if time.Now().After(device.graph.NhTableExpire) {
|
if time.Now().After(device.graph.NhTableExpire) {
|
||||||
device.graph.UpdateLentancy(content.Src_nodeID, content.Dst_nodeID, content.Timediff, true, false)
|
device.graph.UpdateLatency(content.Src_nodeID, content.Dst_nodeID, content.Timediff, true, false)
|
||||||
}
|
}
|
||||||
if !peer.AskedForNeighbor {
|
if !peer.AskedForNeighbor {
|
||||||
QueryPeerMsg := path.QueryPeerMsg{
|
QueryPeerMsg := path.QueryPeerMsg{
|
||||||
@@ -458,10 +458,10 @@ func (device *Device) process_UpdatePeerMsg(peer *Peer, content path.UpdatePeerM
|
|||||||
fmt.Println("Control: Add new peer to local ID:" + peerinfo.NodeID.ToString() + " PubKey:" + PubKey)
|
fmt.Println("Control: Add new peer to local ID:" + peerinfo.NodeID.ToString() + " PubKey:" + PubKey)
|
||||||
}
|
}
|
||||||
if device.graph.Weight(device.ID, peerinfo.NodeID) == path.Infinity { // add node to graph
|
if device.graph.Weight(device.ID, peerinfo.NodeID) == path.Infinity { // add node to graph
|
||||||
device.graph.UpdateLentancy(device.ID, peerinfo.NodeID, path.S2TD(path.Infinity), true, false)
|
device.graph.UpdateLatency(device.ID, peerinfo.NodeID, path.S2TD(path.Infinity), true, false)
|
||||||
}
|
}
|
||||||
if device.graph.Weight(peerinfo.NodeID, device.ID) == path.Infinity { // add node to graph
|
if device.graph.Weight(peerinfo.NodeID, device.ID) == path.Infinity { // add node to graph
|
||||||
device.graph.UpdateLentancy(peerinfo.NodeID, device.ID, path.S2TD(path.Infinity), true, false)
|
device.graph.UpdateLatency(peerinfo.NodeID, device.ID, path.S2TD(path.Infinity), true, false)
|
||||||
}
|
}
|
||||||
device.NewPeer(sk, peerinfo.NodeID, false)
|
device.NewPeer(sk, peerinfo.NodeID, false)
|
||||||
thepeer = device.LookupPeer(sk)
|
thepeer = device.LookupPeer(sk)
|
||||||
@@ -615,10 +615,10 @@ func (device *Device) process_BoardcastPeerMsg(peer *Peer, content path.Boardcas
|
|||||||
fmt.Println("Control: Add new peer to local ID:" + content.NodeID.ToString() + " PubKey:" + pk.ToString())
|
fmt.Println("Control: Add new peer to local ID:" + content.NodeID.ToString() + " PubKey:" + pk.ToString())
|
||||||
}
|
}
|
||||||
if device.graph.Weight(device.ID, content.NodeID) == path.Infinity { // add node to graph
|
if device.graph.Weight(device.ID, content.NodeID) == path.Infinity { // add node to graph
|
||||||
device.graph.UpdateLentancy(device.ID, content.NodeID, path.S2TD(path.Infinity), true, false)
|
device.graph.UpdateLatency(device.ID, content.NodeID, path.S2TD(path.Infinity), true, false)
|
||||||
}
|
}
|
||||||
if device.graph.Weight(content.NodeID, device.ID) == path.Infinity { // add node to graph
|
if device.graph.Weight(content.NodeID, device.ID) == path.Infinity { // add node to graph
|
||||||
device.graph.UpdateLentancy(content.NodeID, device.ID, path.S2TD(path.Infinity), true, false)
|
device.graph.UpdateLatency(content.NodeID, device.ID, path.S2TD(path.Infinity), true, false)
|
||||||
}
|
}
|
||||||
device.NewPeer(pk, content.NodeID, false)
|
device.NewPeer(pk, content.NodeID, false)
|
||||||
}
|
}
|
||||||
@@ -748,13 +748,16 @@ func (device *Device) RoutineRegister() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (device *Device) RoutineRecalculateNhTable() {
|
func (device *Device) RoutineRecalculateNhTable() {
|
||||||
|
if device.graph.TimeoutCheckInterval == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
if device.IsSuperNode {
|
if device.IsSuperNode {
|
||||||
for {
|
for {
|
||||||
changed := device.graph.RecalculateNhTable(true)
|
changed := device.graph.RecalculateNhTable(true)
|
||||||
if changed {
|
if changed {
|
||||||
device.Event_server_NhTable_changed <- struct{}{}
|
device.Event_server_NhTable_changed <- struct{}{}
|
||||||
}
|
}
|
||||||
time.Sleep(device.graph.NodeReportTimeout)
|
time.Sleep(device.graph.TimeoutCheckInterval)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !device.DRoute.P2P.UseP2P {
|
if !device.DRoute.P2P.UseP2P {
|
||||||
@@ -764,7 +767,7 @@ func (device *Device) RoutineRecalculateNhTable() {
|
|||||||
if time.Now().After(device.graph.NhTableExpire) {
|
if time.Now().After(device.graph.NhTableExpire) {
|
||||||
device.graph.RecalculateNhTable(false)
|
device.graph.RecalculateNhTable(false)
|
||||||
}
|
}
|
||||||
time.Sleep(device.graph.NodeReportTimeout)
|
time.Sleep(device.graph.TimeoutCheckInterval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,8 +17,9 @@ graphrecalculatesetting:
|
|||||||
staticmode: false
|
staticmode: false
|
||||||
jittertolerance: 5
|
jittertolerance: 5
|
||||||
jittertolerancemultiplier: 1.01
|
jittertolerancemultiplier: 1.01
|
||||||
nodereporttimeout: 50
|
nodereporttimeout: 20
|
||||||
recalculatecooldown: 5
|
timeoutcheckinterval: 5
|
||||||
|
recalculatecooldown: 1
|
||||||
nexthoptable:
|
nexthoptable:
|
||||||
1:
|
1:
|
||||||
2: 2
|
2: 2
|
||||||
|
30
main_edge.go
30
main_edge.go
@@ -120,21 +120,21 @@ func printExampleEdgeConf() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
g := path.NewGraph(3, false, tconfig.DynamicRoute.P2P.GraphRecalculateSetting, tconfig.DynamicRoute.NTPconfig, false)
|
g := path.NewGraph(3, false, tconfig.DynamicRoute.P2P.GraphRecalculateSetting, tconfig.DynamicRoute.NTPconfig, tconfig.LogLevel)
|
||||||
|
|
||||||
g.UpdateLentancy(1, 2, path.S2TD(0.5), false, false)
|
g.UpdateLatency(1, 2, path.S2TD(0.5), false, false)
|
||||||
g.UpdateLentancy(2, 1, path.S2TD(0.5), false, false)
|
g.UpdateLatency(2, 1, path.S2TD(0.5), false, false)
|
||||||
g.UpdateLentancy(2, 3, path.S2TD(0.5), false, false)
|
g.UpdateLatency(2, 3, path.S2TD(0.5), false, false)
|
||||||
g.UpdateLentancy(3, 2, path.S2TD(0.5), false, false)
|
g.UpdateLatency(3, 2, path.S2TD(0.5), false, false)
|
||||||
g.UpdateLentancy(2, 4, path.S2TD(0.5), false, false)
|
g.UpdateLatency(2, 4, path.S2TD(0.5), false, false)
|
||||||
g.UpdateLentancy(4, 2, path.S2TD(0.5), false, false)
|
g.UpdateLatency(4, 2, path.S2TD(0.5), false, false)
|
||||||
g.UpdateLentancy(3, 4, path.S2TD(0.5), false, false)
|
g.UpdateLatency(3, 4, path.S2TD(0.5), false, false)
|
||||||
g.UpdateLentancy(4, 3, path.S2TD(0.5), false, false)
|
g.UpdateLatency(4, 3, path.S2TD(0.5), false, false)
|
||||||
g.UpdateLentancy(5, 3, path.S2TD(0.5), false, false)
|
g.UpdateLatency(5, 3, path.S2TD(0.5), false, false)
|
||||||
g.UpdateLentancy(3, 5, path.S2TD(0.5), false, false)
|
g.UpdateLatency(3, 5, path.S2TD(0.5), false, false)
|
||||||
g.UpdateLentancy(6, 4, path.S2TD(0.5), false, false)
|
g.UpdateLatency(6, 4, path.S2TD(0.5), false, false)
|
||||||
g.UpdateLentancy(4, 6, path.S2TD(0.5), false, false)
|
g.UpdateLatency(4, 6, path.S2TD(0.5), false, false)
|
||||||
_, next := path.FloydWarshall(g)
|
_, next, _ := g.FloydWarshall(false)
|
||||||
tconfig.NextHopTable = next
|
tconfig.NextHopTable = next
|
||||||
toprint, _ := yaml.Marshal(tconfig)
|
toprint, _ := yaml.Marshal(tconfig)
|
||||||
fmt.Print(string(toprint))
|
fmt.Print(string(toprint))
|
||||||
@@ -222,7 +222,7 @@ func Edge(configPath string, useUAPI bool, printExample bool, bindmode string) (
|
|||||||
if econfig.DynamicRoute.P2P.UseP2P == false && econfig.DynamicRoute.SuperNode.UseSuperNode == false {
|
if econfig.DynamicRoute.P2P.UseP2P == false && econfig.DynamicRoute.SuperNode.UseSuperNode == false {
|
||||||
econfig.LogLevel.LogNTP = false // NTP in static mode is useless
|
econfig.LogLevel.LogNTP = false // NTP in static mode is useless
|
||||||
}
|
}
|
||||||
graph := path.NewGraph(3, false, econfig.DynamicRoute.P2P.GraphRecalculateSetting, econfig.DynamicRoute.NTPconfig, econfig.LogLevel.LogNTP)
|
graph := path.NewGraph(3, false, econfig.DynamicRoute.P2P.GraphRecalculateSetting, econfig.DynamicRoute.NTPconfig, econfig.LogLevel)
|
||||||
graph.SetNHTable(econfig.NextHopTable, [32]byte{})
|
graph.SetNHTable(econfig.NextHopTable, [32]byte{})
|
||||||
|
|
||||||
the_device := device.NewDevice(thetap, econfig.NodeID, conn.NewDefaultBind(true, true, bindmode), logger, graph, false, configPath, &econfig, nil, nil, Version)
|
the_device := device.NewDevice(thetap, econfig.NodeID, conn.NewDefaultBind(true, true, bindmode), logger, graph, false, configPath, &econfig, nil, nil, Version)
|
||||||
|
@@ -56,6 +56,7 @@ type HttpState struct {
|
|||||||
PeerInfo map[config.Vertex]HttpPeerInfo
|
PeerInfo map[config.Vertex]HttpPeerInfo
|
||||||
Infinity float64
|
Infinity float64
|
||||||
Edges map[config.Vertex]map[config.Vertex]float64
|
Edges map[config.Vertex]map[config.Vertex]float64
|
||||||
|
Edges_Nh map[config.Vertex]map[config.Vertex]float64
|
||||||
NhTable config.NextHopTable
|
NhTable config.NextHopTable
|
||||||
Dist config.DistTable
|
Dist config.DistTable
|
||||||
}
|
}
|
||||||
@@ -263,9 +264,10 @@ func get_info(w http.ResponseWriter, r *http.Request) {
|
|||||||
if time.Now().After(http_StateExpire) {
|
if time.Now().After(http_StateExpire) {
|
||||||
hs := HttpState{
|
hs := HttpState{
|
||||||
PeerInfo: make(map[config.Vertex]HttpPeerInfo),
|
PeerInfo: make(map[config.Vertex]HttpPeerInfo),
|
||||||
NhTable: http_graph.GetNHTable(),
|
NhTable: http_graph.GetNHTable(false),
|
||||||
Infinity: path.Infinity,
|
Infinity: path.Infinity,
|
||||||
Edges: http_graph.GetEdges(),
|
Edges: http_graph.GetEdges(false),
|
||||||
|
Edges_Nh: http_graph.GetEdges(true),
|
||||||
Dist: http_graph.GetDtst(),
|
Dist: http_graph.GetDtst(),
|
||||||
}
|
}
|
||||||
http_maps_lock.RLock()
|
http_maps_lock.RLock()
|
||||||
|
@@ -174,7 +174,7 @@ func Super(configPath string, useUAPI bool, printExample bool, bindmode string)
|
|||||||
Event_server_register: make(chan path.RegisterMsg, 1<<5),
|
Event_server_register: make(chan path.RegisterMsg, 1<<5),
|
||||||
Event_server_NhTable_changed: make(chan struct{}, 1<<4),
|
Event_server_NhTable_changed: make(chan struct{}, 1<<4),
|
||||||
}
|
}
|
||||||
http_graph = path.NewGraph(3, true, sconfig.GraphRecalculateSetting, config.NTPinfo{}, sconfig.LogLevel.LogNTP)
|
http_graph = path.NewGraph(3, true, sconfig.GraphRecalculateSetting, config.NTPinfo{}, sconfig.LogLevel)
|
||||||
http_graph.SetNHTable(http_sconfig.NextHopTable, [32]byte{})
|
http_graph.SetNHTable(http_sconfig.NextHopTable, [32]byte{})
|
||||||
if sconfig.GraphRecalculateSetting.StaticMode {
|
if sconfig.GraphRecalculateSetting.StaticMode {
|
||||||
err = checkNhTable(http_sconfig.NextHopTable, sconfig.Peers)
|
err = checkNhTable(http_sconfig.NextHopTable, sconfig.Peers)
|
||||||
@@ -364,7 +364,7 @@ func Event_server_event_hendler(graph *path.IG, events path.SUPER_Events) {
|
|||||||
PushNhTable(false)
|
PushNhTable(false)
|
||||||
}
|
}
|
||||||
case <-events.Event_server_NhTable_changed:
|
case <-events.Event_server_NhTable_changed:
|
||||||
NhTable := graph.GetNHTable()
|
NhTable := graph.GetNHTable(true)
|
||||||
NhTablestr, _ := json.Marshal(NhTable)
|
NhTablestr, _ := json.Marshal(NhTable)
|
||||||
md5_hash_raw := md5.Sum(http_NhTableStr)
|
md5_hash_raw := md5.Sum(http_NhTableStr)
|
||||||
new_hash_str := hex.EncodeToString(md5_hash_raw[:])
|
new_hash_str := hex.EncodeToString(md5_hash_raw[:])
|
||||||
@@ -373,9 +373,9 @@ func Event_server_event_hendler(graph *path.IG, events path.SUPER_Events) {
|
|||||||
http_NhTableStr = NhTablestr
|
http_NhTableStr = NhTablestr
|
||||||
PushNhTable(false)
|
PushNhTable(false)
|
||||||
case pong_msg := <-events.Event_server_pong:
|
case pong_msg := <-events.Event_server_pong:
|
||||||
changed := graph.UpdateLentancy(pong_msg.Src_nodeID, pong_msg.Dst_nodeID, pong_msg.Timediff, true, true)
|
changed := graph.UpdateLatency(pong_msg.Src_nodeID, pong_msg.Dst_nodeID, pong_msg.Timediff, true, true)
|
||||||
if changed {
|
if changed {
|
||||||
NhTable := graph.GetNHTable()
|
NhTable := graph.GetNHTable(true)
|
||||||
NhTablestr, _ := json.Marshal(NhTable)
|
NhTablestr, _ := json.Marshal(NhTable)
|
||||||
md5_hash_raw := md5.Sum(append(http_NhTableStr, http_HashSalt...))
|
md5_hash_raw := md5.Sum(append(http_NhTableStr, http_HashSalt...))
|
||||||
new_hash_str := hex.EncodeToString(md5_hash_raw[:])
|
new_hash_str := hex.EncodeToString(md5_hash_raw[:])
|
||||||
@@ -423,7 +423,10 @@ func PushNhTable(force bool) {
|
|||||||
http_maps_lock.RLock()
|
http_maps_lock.RLock()
|
||||||
for pkstr, peerstate := range http_PeerState {
|
for pkstr, peerstate := range http_PeerState {
|
||||||
isAlive := peerstate.LastSeen.Add(path.S2TD(http_sconfig.GraphRecalculateSetting.NodeReportTimeout)).After(time.Now())
|
isAlive := peerstate.LastSeen.Add(path.S2TD(http_sconfig.GraphRecalculateSetting.NodeReportTimeout)).After(time.Now())
|
||||||
if (force && isAlive) || peerstate.NhTableState != http_NhTable_Hash {
|
if !isAlive {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if force || peerstate.NhTableState != http_NhTable_Hash {
|
||||||
if peer := http_device4.LookupPeerByStr(pkstr); peer != nil && peer.GetEndpointDstStr() != "" {
|
if peer := http_device4.LookupPeerByStr(pkstr); peer != nil && peer.GetEndpointDstStr() != "" {
|
||||||
http_device4.SendPacket(peer, path.UpdateNhTable, buf, device.MessageTransportOffsetContent)
|
http_device4.SendPacket(peer, path.UpdateNhTable, buf, device.MessageTransportOffsetContent)
|
||||||
}
|
}
|
||||||
@@ -452,6 +455,10 @@ func PushPeerinfo(force bool) {
|
|||||||
copy(buf[path.EgHeaderLen:], body)
|
copy(buf[path.EgHeaderLen:], body)
|
||||||
http_maps_lock.RLock()
|
http_maps_lock.RLock()
|
||||||
for pkstr, peerstate := range http_PeerState {
|
for pkstr, peerstate := range http_PeerState {
|
||||||
|
isAlive := peerstate.LastSeen.Add(path.S2TD(http_sconfig.GraphRecalculateSetting.NodeReportTimeout)).After(time.Now())
|
||||||
|
if !isAlive {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if force || peerstate.PeerInfoState != http_PeerInfo_hash {
|
if force || peerstate.PeerInfoState != http_PeerInfo_hash {
|
||||||
if peer := http_device4.LookupPeerByStr(pkstr); peer != nil {
|
if peer := http_device4.LookupPeerByStr(pkstr); peer != nil {
|
||||||
http_device4.SendPacket(peer, path.UpdatePeer, buf, device.MessageTransportOffsetContent)
|
http_device4.SendPacket(peer, path.UpdatePeer, buf, device.MessageTransportOffsetContent)
|
||||||
|
14
path/ntp.go
14
path/ntp.go
@@ -26,8 +26,8 @@ func (g *IG) InitNTP() {
|
|||||||
g.SyncTimeMultiple(-1)
|
g.SyncTimeMultiple(-1)
|
||||||
go g.RoutineSyncTime()
|
go g.RoutineSyncTime()
|
||||||
} else {
|
} else {
|
||||||
if g.ntp_log {
|
if g.loglevel.LogNTP {
|
||||||
fmt.Println("NTP sync disabled")
|
fmt.Println("NTP: NTP sync disabled")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,7 +87,7 @@ func (g *IG) SyncTimeMultiple(count int) {
|
|||||||
results = append(results, result.ClockOffset)
|
results = append(results, result.ClockOffset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if g.ntp_log {
|
if g.loglevel.LogNTP {
|
||||||
fmt.Println("NTP: All done")
|
fmt.Println("NTP: All done")
|
||||||
}
|
}
|
||||||
sort.Sort(ByDuration(results))
|
sort.Sort(ByDuration(results))
|
||||||
@@ -99,25 +99,25 @@ func (g *IG) SyncTimeMultiple(count int) {
|
|||||||
totaltime += result
|
totaltime += result
|
||||||
}
|
}
|
||||||
avgtime := totaltime / time.Duration(len(results))
|
avgtime := totaltime / time.Duration(len(results))
|
||||||
if g.ntp_log {
|
if g.loglevel.LogNTP {
|
||||||
fmt.Println("NTP: Arvage offset: " + avgtime.String())
|
fmt.Println("NTP: Arvage offset: " + avgtime.String())
|
||||||
}
|
}
|
||||||
g.ntp_offset = avgtime
|
g.ntp_offset = avgtime
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *IG) SyncTime(url string, timeout time.Duration) {
|
func (g *IG) SyncTime(url string, timeout time.Duration) {
|
||||||
if g.ntp_log {
|
if g.loglevel.LogNTP {
|
||||||
fmt.Println("NTP: Starting syncing with NTP server :" + url)
|
fmt.Println("NTP: Starting syncing with NTP server :" + url)
|
||||||
}
|
}
|
||||||
options := ntp.QueryOptions{Timeout: timeout}
|
options := ntp.QueryOptions{Timeout: timeout}
|
||||||
response, err := ntp.QueryWithOptions(url, options)
|
response, err := ntp.QueryWithOptions(url, options)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if g.ntp_log {
|
if g.loglevel.LogNTP {
|
||||||
fmt.Println("NTP: NTP server :" + url + "\tResult:" + response.ClockOffset.String() + " RTT:" + response.RTT.String())
|
fmt.Println("NTP: NTP server :" + url + "\tResult:" + response.ClockOffset.String() + " RTT:" + response.RTT.String())
|
||||||
}
|
}
|
||||||
g.ntp_servers.Set(url, *response)
|
g.ntp_servers.Set(url, *response)
|
||||||
} else {
|
} else {
|
||||||
if g.ntp_log {
|
if g.loglevel.LogNTP {
|
||||||
fmt.Println("NTP: NTP server :" + url + "\tFailed :" + err.Error())
|
fmt.Println("NTP: NTP server :" + url + "\tFailed :" + err.Error())
|
||||||
}
|
}
|
||||||
g.ntp_servers.Set(url, ntp.Response{
|
g.ntp_servers.Set(url, ntp.Response{
|
||||||
|
201
path/path.go
201
path/path.go
@@ -2,6 +2,7 @@ package path
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math"
|
"math"
|
||||||
@@ -21,17 +22,10 @@ func (g *IG) GetCurrentTime() time.Time {
|
|||||||
return time.Now().Add(g.ntp_offset).Round(0)
|
return time.Now().Add(g.ntp_offset).Round(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Graph is the interface implemented by graphs that
|
|
||||||
// this algorithm can run on.
|
|
||||||
type Graph interface {
|
|
||||||
Vertices() map[config.Vertex]bool
|
|
||||||
Neighbors(v config.Vertex) []config.Vertex
|
|
||||||
Weight(u, v config.Vertex) float64
|
|
||||||
}
|
|
||||||
|
|
||||||
type Latency struct {
|
type Latency struct {
|
||||||
ping float64
|
ping float64
|
||||||
time time.Time
|
ping_old float64
|
||||||
|
time time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
type Fullroute struct {
|
type Fullroute struct {
|
||||||
@@ -42,7 +36,7 @@ type Fullroute struct {
|
|||||||
// IG is a graph of integers that satisfies the Graph interface.
|
// IG is a graph of integers that satisfies the Graph interface.
|
||||||
type IG struct {
|
type IG struct {
|
||||||
Vert map[config.Vertex]bool
|
Vert map[config.Vertex]bool
|
||||||
edges map[config.Vertex]map[config.Vertex]Latency
|
edges map[config.Vertex]map[config.Vertex]*Latency
|
||||||
edgelock *sync.RWMutex
|
edgelock *sync.RWMutex
|
||||||
StaticMode bool
|
StaticMode bool
|
||||||
JitterTolerance float64
|
JitterTolerance float64
|
||||||
@@ -50,15 +44,16 @@ type IG struct {
|
|||||||
NodeReportTimeout time.Duration
|
NodeReportTimeout time.Duration
|
||||||
SuperNodeInfoTimeout time.Duration
|
SuperNodeInfoTimeout time.Duration
|
||||||
RecalculateCoolDown time.Duration
|
RecalculateCoolDown time.Duration
|
||||||
|
TimeoutCheckInterval time.Duration
|
||||||
recalculateTime time.Time
|
recalculateTime time.Time
|
||||||
dlTable config.DistTable
|
dlTable config.DistTable
|
||||||
nhTable config.NextHopTable
|
nhTable config.NextHopTable
|
||||||
NhTableHash [32]byte
|
NhTableHash [32]byte
|
||||||
NhTableExpire time.Time
|
NhTableExpire time.Time
|
||||||
IsSuperMode bool
|
IsSuperMode bool
|
||||||
|
loglevel config.LoggerInfo
|
||||||
|
|
||||||
ntp_wg sync.WaitGroup
|
ntp_wg sync.WaitGroup
|
||||||
ntp_log bool
|
|
||||||
ntp_info config.NTPinfo
|
ntp_info config.NTPinfo
|
||||||
ntp_offset time.Duration
|
ntp_offset time.Duration
|
||||||
ntp_servers orderedmap.OrderedMap // serverurl:lentancy
|
ntp_servers orderedmap.OrderedMap // serverurl:lentancy
|
||||||
@@ -68,7 +63,7 @@ func S2TD(secs float64) time.Duration {
|
|||||||
return time.Duration(secs * float64(time.Second))
|
return time.Duration(secs * float64(time.Second))
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGraph(num_node int, IsSuperMode bool, theconfig config.GraphRecalculateSetting, ntpinfo config.NTPinfo, logntp bool) *IG {
|
func NewGraph(num_node int, IsSuperMode bool, theconfig config.GraphRecalculateSetting, ntpinfo config.NTPinfo, loglevel config.LoggerInfo) *IG {
|
||||||
g := IG{
|
g := IG{
|
||||||
edgelock: &sync.RWMutex{},
|
edgelock: &sync.RWMutex{},
|
||||||
StaticMode: theconfig.StaticMode,
|
StaticMode: theconfig.StaticMode,
|
||||||
@@ -76,12 +71,13 @@ func NewGraph(num_node int, IsSuperMode bool, theconfig config.GraphRecalculateS
|
|||||||
JitterToleranceMultiplier: theconfig.JitterToleranceMultiplier,
|
JitterToleranceMultiplier: theconfig.JitterToleranceMultiplier,
|
||||||
NodeReportTimeout: S2TD(theconfig.NodeReportTimeout),
|
NodeReportTimeout: S2TD(theconfig.NodeReportTimeout),
|
||||||
RecalculateCoolDown: S2TD(theconfig.RecalculateCoolDown),
|
RecalculateCoolDown: S2TD(theconfig.RecalculateCoolDown),
|
||||||
|
TimeoutCheckInterval: S2TD(theconfig.TimeoutCheckInterval),
|
||||||
ntp_info: ntpinfo,
|
ntp_info: ntpinfo,
|
||||||
}
|
}
|
||||||
g.Vert = make(map[config.Vertex]bool, num_node)
|
g.Vert = make(map[config.Vertex]bool, num_node)
|
||||||
g.edges = make(map[config.Vertex]map[config.Vertex]Latency, num_node)
|
g.edges = make(map[config.Vertex]map[config.Vertex]*Latency, num_node)
|
||||||
g.IsSuperMode = IsSuperMode
|
g.IsSuperMode = IsSuperMode
|
||||||
g.ntp_log = logntp
|
g.loglevel = loglevel
|
||||||
g.InitNTP()
|
g.InitNTP()
|
||||||
return &g
|
return &g
|
||||||
}
|
}
|
||||||
@@ -98,7 +94,7 @@ func (g *IG) GetWeightType(x float64) (y float64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *IG) ShouldUpdate(u config.Vertex, v config.Vertex, newval float64) bool {
|
func (g *IG) ShouldUpdate(u config.Vertex, v config.Vertex, newval float64) bool {
|
||||||
oldval := math.Abs(g.Weight(u, v) * 1000)
|
oldval := math.Abs(g.OldWeight(u, v) * 1000)
|
||||||
newval = math.Abs(newval * 1000)
|
newval = math.Abs(newval * 1000)
|
||||||
if g.IsSuperMode {
|
if g.IsSuperMode {
|
||||||
if g.JitterTolerance > 0.001 && g.JitterToleranceMultiplier >= 1 {
|
if g.JitterTolerance > 0.001 && g.JitterToleranceMultiplier >= 1 {
|
||||||
@@ -121,8 +117,11 @@ func (g *IG) RecalculateNhTable(checkchange bool) (changed bool) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !g.ShouldCalculate() {
|
||||||
|
return
|
||||||
|
}
|
||||||
if g.recalculateTime.Add(g.RecalculateCoolDown).Before(time.Now()) {
|
if g.recalculateTime.Add(g.RecalculateCoolDown).Before(time.Now()) {
|
||||||
dist, next := FloydWarshall(g)
|
dist, next, _ := g.FloydWarshall(false)
|
||||||
changed = false
|
changed = false
|
||||||
if checkchange {
|
if checkchange {
|
||||||
CheckLoop:
|
CheckLoop:
|
||||||
@@ -144,16 +143,10 @@ func (g *IG) RecalculateNhTable(checkchange bool) (changed bool) {
|
|||||||
|
|
||||||
func (g *IG) RemoveVirt(v config.Vertex, recalculate bool, checkchange bool) (changed bool) { //Waiting for test
|
func (g *IG) RemoveVirt(v config.Vertex, recalculate bool, checkchange bool) (changed bool) { //Waiting for test
|
||||||
g.edgelock.Lock()
|
g.edgelock.Lock()
|
||||||
if _, ok := g.Vert[v]; ok {
|
delete(g.Vert, v)
|
||||||
delete(g.Vert, v)
|
delete(g.edges, v)
|
||||||
}
|
for u, _ := range g.edges {
|
||||||
if _, ok := g.edges[v]; ok {
|
delete(g.edges[u], v)
|
||||||
delete(g.edges, v)
|
|
||||||
}
|
|
||||||
for u, vv := range g.edges {
|
|
||||||
if _, ok := vv[v]; ok {
|
|
||||||
delete(g.edges[u], v)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
g.edgelock.Unlock()
|
g.edgelock.Unlock()
|
||||||
g.NhTableHash = [32]byte{}
|
g.NhTableHash = [32]byte{}
|
||||||
@@ -163,21 +156,27 @@ func (g *IG) RemoveVirt(v config.Vertex, recalculate bool, checkchange bool) (ch
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *IG) UpdateLentancy(u, v config.Vertex, dt time.Duration, recalculate bool, checkchange bool) (changed bool) {
|
func (g *IG) UpdateLatency(u, v config.Vertex, dt time.Duration, recalculate bool, checkchange bool) (changed bool) {
|
||||||
g.edgelock.Lock()
|
g.edgelock.Lock()
|
||||||
g.Vert[u] = true
|
g.Vert[u] = true
|
||||||
g.Vert[v] = true
|
g.Vert[v] = true
|
||||||
w := float64(dt) / float64(time.Second)
|
w := float64(dt) / float64(time.Second)
|
||||||
if _, ok := g.edges[u]; !ok {
|
if _, ok := g.edges[u]; !ok {
|
||||||
g.recalculateTime = time.Time{}
|
g.recalculateTime = time.Time{}
|
||||||
g.edges[u] = make(map[config.Vertex]Latency)
|
g.edges[u] = make(map[config.Vertex]*Latency)
|
||||||
}
|
}
|
||||||
g.edgelock.Unlock()
|
g.edgelock.Unlock()
|
||||||
should_update := g.ShouldUpdate(u, v, w)
|
should_update := g.ShouldUpdate(u, v, w)
|
||||||
g.edgelock.Lock()
|
g.edgelock.Lock()
|
||||||
g.edges[u][v] = Latency{
|
if _, ok := g.edges[u][v]; ok {
|
||||||
ping: w,
|
g.edges[u][v].ping = w
|
||||||
time: time.Now(),
|
g.edges[u][v].time = time.Now()
|
||||||
|
} else {
|
||||||
|
g.edges[u][v] = &Latency{
|
||||||
|
ping: w,
|
||||||
|
ping_old: Infinity,
|
||||||
|
time: time.Now(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
g.edgelock.Unlock()
|
g.edgelock.Unlock()
|
||||||
if should_update && recalculate {
|
if should_update && recalculate {
|
||||||
@@ -185,7 +184,7 @@ func (g *IG) UpdateLentancy(u, v config.Vertex, dt time.Duration, recalculate bo
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
func (g IG) Vertices() map[config.Vertex]bool {
|
func (g *IG) Vertices() map[config.Vertex]bool {
|
||||||
vr := make(map[config.Vertex]bool)
|
vr := make(map[config.Vertex]bool)
|
||||||
g.edgelock.RLock()
|
g.edgelock.RLock()
|
||||||
defer g.edgelock.RUnlock()
|
defer g.edgelock.RUnlock()
|
||||||
@@ -203,7 +202,7 @@ func (g IG) Neighbors(v config.Vertex) (vs []config.Vertex) {
|
|||||||
return vs
|
return vs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g IG) Next(u, v config.Vertex) *config.Vertex {
|
func (g *IG) Next(u, v config.Vertex) *config.Vertex {
|
||||||
if _, ok := g.nhTable[u]; !ok {
|
if _, ok := g.nhTable[u]; !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -213,15 +212,14 @@ func (g IG) Next(u, v config.Vertex) *config.Vertex {
|
|||||||
return g.nhTable[u][v]
|
return g.nhTable[u][v]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g IG) Weight(u, v config.Vertex) float64 {
|
func (g *IG) Weight(u, v config.Vertex) (ret float64) {
|
||||||
g.edgelock.RLock()
|
g.edgelock.RLock()
|
||||||
defer g.edgelock.RUnlock()
|
defer g.edgelock.RUnlock()
|
||||||
|
//defer func() { fmt.Println(u, v, ret) }()
|
||||||
|
if u == v {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
if _, ok := g.edges[u]; !ok {
|
if _, ok := g.edges[u]; !ok {
|
||||||
g.edgelock.RUnlock()
|
|
||||||
g.edgelock.Lock()
|
|
||||||
g.edges[u] = make(map[config.Vertex]Latency)
|
|
||||||
g.edgelock.Unlock()
|
|
||||||
g.edgelock.RLock()
|
|
||||||
return Infinity
|
return Infinity
|
||||||
}
|
}
|
||||||
if _, ok := g.edges[u][v]; !ok {
|
if _, ok := g.edges[u][v]; !ok {
|
||||||
@@ -233,7 +231,83 @@ func (g IG) Weight(u, v config.Vertex) float64 {
|
|||||||
return g.edges[u][v].ping
|
return g.edges[u][v].ping
|
||||||
}
|
}
|
||||||
|
|
||||||
func FloydWarshall(g Graph) (dist config.DistTable, next config.NextHopTable) {
|
func (g *IG) OldWeight(u, v config.Vertex) (ret float64) {
|
||||||
|
g.edgelock.RLock()
|
||||||
|
defer g.edgelock.RUnlock()
|
||||||
|
if u == v {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if _, ok := g.edges[u]; !ok {
|
||||||
|
return Infinity
|
||||||
|
}
|
||||||
|
if _, ok := g.edges[u][v]; !ok {
|
||||||
|
return Infinity
|
||||||
|
}
|
||||||
|
return g.edges[u][v].ping_old
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *IG) ShouldCalculate() bool {
|
||||||
|
vert := g.Vertices()
|
||||||
|
for u, _ := range vert {
|
||||||
|
for v, _ := range vert {
|
||||||
|
if u != v {
|
||||||
|
w := g.Weight(u, v)
|
||||||
|
if g.ShouldUpdate(u, v, w) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *IG) SetWeight(u, v config.Vertex, weight float64) {
|
||||||
|
g.edgelock.Lock()
|
||||||
|
defer g.edgelock.Unlock()
|
||||||
|
if _, ok := g.edges[u]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, ok := g.edges[u][v]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.edges[u][v].ping = weight
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *IG) SetOldWeight(u, v config.Vertex, weight float64) {
|
||||||
|
g.edgelock.Lock()
|
||||||
|
defer g.edgelock.Unlock()
|
||||||
|
if _, ok := g.edges[u]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, ok := g.edges[u][v]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.edges[u][v].ping_old = weight
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *IG) RemoveAllNegativeValue() {
|
||||||
|
vert := g.Vertices()
|
||||||
|
for u, _ := range vert {
|
||||||
|
for v, _ := range vert {
|
||||||
|
if g.Weight(u, v) < 0 {
|
||||||
|
if g.loglevel.LogInternal {
|
||||||
|
fmt.Printf("Internal: Remove negative value : edge[%v][%v] = 0\n", u, v)
|
||||||
|
}
|
||||||
|
g.SetWeight(u, v, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *IG) FloydWarshall(again bool) (dist config.DistTable, next config.NextHopTable, err error) {
|
||||||
|
if g.loglevel.LogInternal {
|
||||||
|
if !again {
|
||||||
|
fmt.Println("Internal: Start Floyd Warshall algorithm")
|
||||||
|
} else {
|
||||||
|
fmt.Println("Internal: Start Floyd Warshall algorithm again")
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
vert := g.Vertices()
|
vert := g.Vertices()
|
||||||
dist = make(config.DistTable)
|
dist = make(config.DistTable)
|
||||||
next = make(config.NextHopTable)
|
next = make(config.NextHopTable)
|
||||||
@@ -251,6 +325,7 @@ func FloydWarshall(g Graph) (dist config.DistTable, next config.NextHopTable) {
|
|||||||
dist[u][v] = w
|
dist[u][v] = w
|
||||||
next[u][v] = &v
|
next[u][v] = &v
|
||||||
}
|
}
|
||||||
|
g.SetOldWeight(u, v, w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for k, _ := range vert {
|
for k, _ := range vert {
|
||||||
@@ -265,7 +340,28 @@ func FloydWarshall(g Graph) (dist config.DistTable, next config.NextHopTable) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dist, next
|
for i := range dist {
|
||||||
|
if dist[i][i] < 0 {
|
||||||
|
if !again {
|
||||||
|
if g.loglevel.LogInternal {
|
||||||
|
fmt.Println("Internal: Error: Negative cycle detected")
|
||||||
|
}
|
||||||
|
g.RemoveAllNegativeValue()
|
||||||
|
err = errors.New("negative cycle detected")
|
||||||
|
dist, next, _ = g.FloydWarshall(true)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
dist = make(config.DistTable)
|
||||||
|
next = make(config.NextHopTable)
|
||||||
|
err = errors.New("negative cycle detected again!")
|
||||||
|
if g.loglevel.LogInternal {
|
||||||
|
fmt.Println("Internal: Error: Negative cycle detected again")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func Path(u, v config.Vertex, next config.NextHopTable) (path []config.Vertex) {
|
func Path(u, v config.Vertex, next config.NextHopTable) (path []config.Vertex) {
|
||||||
@@ -286,8 +382,8 @@ func (g *IG) SetNHTable(nh config.NextHopTable, table_hash [32]byte) { // set nh
|
|||||||
g.NhTableExpire = time.Now().Add(g.SuperNodeInfoTimeout)
|
g.NhTableExpire = time.Now().Add(g.SuperNodeInfoTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *IG) GetNHTable() config.NextHopTable {
|
func (g *IG) GetNHTable(recalculate bool) config.NextHopTable {
|
||||||
if time.Now().After(g.NhTableExpire) {
|
if recalculate && time.Now().After(g.NhTableExpire) {
|
||||||
g.RecalculateNhTable(false)
|
g.RecalculateNhTable(false)
|
||||||
}
|
}
|
||||||
return g.nhTable
|
return g.nhTable
|
||||||
@@ -297,14 +393,18 @@ func (g *IG) GetDtst() config.DistTable {
|
|||||||
return g.dlTable
|
return g.dlTable
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *IG) GetEdges() (edges map[config.Vertex]map[config.Vertex]float64) {
|
func (g *IG) GetEdges(isOld bool) (edges map[config.Vertex]map[config.Vertex]float64) {
|
||||||
vert := g.Vertices()
|
vert := g.Vertices()
|
||||||
edges = make(map[config.Vertex]map[config.Vertex]float64, len(vert))
|
edges = make(map[config.Vertex]map[config.Vertex]float64, len(vert))
|
||||||
for src, _ := range vert {
|
for src, _ := range vert {
|
||||||
edges[src] = make(map[config.Vertex]float64, len(vert))
|
edges[src] = make(map[config.Vertex]float64, len(vert))
|
||||||
for dst, _ := range vert {
|
for dst, _ := range vert {
|
||||||
if src != dst {
|
if src != dst {
|
||||||
edges[src][dst] = g.Weight(src, dst)
|
if isOld {
|
||||||
|
edges[src][dst] = g.OldWeight(src, dst)
|
||||||
|
} else {
|
||||||
|
edges[src][dst] = g.Weight(src, dst)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -369,7 +469,7 @@ func Solve(filePath string, pe bool) error {
|
|||||||
|
|
||||||
g := NewGraph(3, false, config.GraphRecalculateSetting{
|
g := NewGraph(3, false, config.GraphRecalculateSetting{
|
||||||
NodeReportTimeout: 9999,
|
NodeReportTimeout: 9999,
|
||||||
}, config.NTPinfo{}, false)
|
}, config.NTPinfo{}, config.LoggerInfo{LogInternal: true})
|
||||||
inputb, err := ioutil.ReadFile(filePath)
|
inputb, err := ioutil.ReadFile(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -385,11 +485,14 @@ func Solve(filePath string, pe bool) error {
|
|||||||
val := a2n(sval)
|
val := a2n(sval)
|
||||||
dst := a2v(verts[index+1])
|
dst := a2v(verts[index+1])
|
||||||
if src != dst && val != Infinity {
|
if src != dst && val != Infinity {
|
||||||
g.UpdateLentancy(src, dst, S2TD(val), false, false)
|
g.UpdateLatency(src, dst, S2TD(val), false, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dist, next := FloydWarshall(g)
|
dist, next, err := g.FloydWarshall(false)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
rr, _ := yaml.Marshal(Fullroute{
|
rr, _ := yaml.Marshal(Fullroute{
|
||||||
Dist: dist,
|
Dist: dist,
|
||||||
|
Reference in New Issue
Block a user