EtherGuard-VPN/path/path.go

380 lines
9.1 KiB
Go
Raw Normal View History

2021-08-16 20:58:15 +02:00
package path
import (
"fmt"
2021-08-25 15:21:26 +02:00
"io/ioutil"
2021-08-16 20:58:15 +02:00
"math"
2021-08-25 15:21:26 +02:00
"strconv"
"strings"
2021-08-21 16:54:24 +02:00
"sync"
2021-08-16 20:58:15 +02:00
"time"
2021-08-20 19:32:50 +02:00
"github.com/KusakabeSi/EtherGuardVPN/config"
2021-08-25 13:54:13 +02:00
orderedmap "github.com/KusakabeSi/EtherGuardVPN/orderdmap"
2021-08-16 20:58:15 +02:00
yaml "gopkg.in/yaml.v2"
)
2021-08-20 19:32:50 +02:00
const Infinity = float64(99999)
func (g *IG) GetCurrentTime() time.Time {
2021-08-25 13:54:13 +02:00
return time.Now().Add(g.ntp_offset).Round(0)
2021-08-20 19:32:50 +02:00
}
2021-08-16 20:58:15 +02:00
// A Graph is the interface implemented by graphs that
// this algorithm can run on.
type Graph interface {
2021-08-20 19:32:50 +02:00
Vertices() map[config.Vertex]bool
Neighbors(v config.Vertex) []config.Vertex
Weight(u, v config.Vertex) float64
2021-08-16 20:58:15 +02:00
}
type Latency struct {
ping float64
time time.Time
}
type Fullroute struct {
2021-08-25 15:21:26 +02:00
Next config.NextHopTable `yaml:"NextHopTable"`
Dist config.DistTable `yaml:"DistanceTable"`
2021-08-16 20:58:15 +02:00
}
// IG is a graph of integers that satisfies the Graph interface.
type IG struct {
2021-08-20 19:32:50 +02:00
Vert map[config.Vertex]bool
2021-08-21 16:54:24 +02:00
edges map[config.Vertex]map[config.Vertex]Latency
2021-08-23 18:39:04 +02:00
edgelock *sync.RWMutex
2021-08-20 19:32:50 +02:00
JitterTolerance float64
JitterToleranceMultiplier float64
NodeReportTimeout time.Duration
SuperNodeInfoTimeout time.Duration
RecalculateCoolDown time.Duration
RecalculateTime time.Time
dlTable config.DistTable
nhTable config.NextHopTable
2021-08-20 19:32:50 +02:00
NhTableHash [32]byte
2021-08-24 20:16:21 +02:00
NhTableExpire time.Time
2021-08-20 19:32:50 +02:00
IsSuperMode bool
2021-08-25 13:54:13 +02:00
ntp_wg sync.WaitGroup
ntp_log bool
ntp_info config.NTPinfo
ntp_offset time.Duration
ntp_servers orderedmap.OrderedMap // serverurl:lentancy
2021-08-20 19:32:50 +02:00
}
func S2TD(secs float64) time.Duration {
return time.Duration(secs * float64(time.Second))
2021-08-16 20:58:15 +02:00
}
2021-08-25 13:54:13 +02:00
func NewGraph(num_node int, IsSuperMode bool, theconfig config.GraphRecalculateSetting, ntpinfo config.NTPinfo, logntp bool) *IG {
2021-08-20 19:32:50 +02:00
g := IG{
2021-08-23 18:39:04 +02:00
edgelock: &sync.RWMutex{},
2021-08-20 19:32:50 +02:00
JitterTolerance: theconfig.JitterTolerance,
JitterToleranceMultiplier: theconfig.JitterToleranceMultiplier,
NodeReportTimeout: S2TD(theconfig.NodeReportTimeout),
RecalculateCoolDown: S2TD(theconfig.RecalculateCoolDown),
2021-08-25 13:54:13 +02:00
ntp_info: ntpinfo,
2021-08-20 19:32:50 +02:00
}
g.Vert = make(map[config.Vertex]bool, num_node)
2021-08-21 16:54:24 +02:00
g.edges = make(map[config.Vertex]map[config.Vertex]Latency, num_node)
2021-08-20 19:32:50 +02:00
g.IsSuperMode = IsSuperMode
2021-08-25 13:54:13 +02:00
g.ntp_log = logntp
g.InitNTP()
2021-08-21 16:54:24 +02:00
return &g
2021-08-16 20:58:15 +02:00
}
2021-08-24 10:43:55 +02:00
func (g *IG) GetWeightType(x float64) (y float64) {
2021-08-20 19:32:50 +02:00
x = math.Abs(x)
2021-08-24 10:43:55 +02:00
y = x
if g.JitterTolerance > 0.001 && g.JitterToleranceMultiplier > 1 {
t := g.JitterTolerance
r := g.JitterToleranceMultiplier
y = math.Pow(math.Ceil(math.Pow(x/t, 1/r)), r) * t
2021-08-20 19:32:50 +02:00
}
return y
}
func (g *IG) ShouldUpdate(u config.Vertex, v config.Vertex, newval float64) bool {
2021-08-24 10:43:55 +02:00
oldval := math.Abs(g.Weight(u, v) * 1000)
newval = math.Abs(newval * 1000)
2021-08-20 19:32:50 +02:00
if g.IsSuperMode {
2021-08-24 10:43:55 +02:00
if g.JitterTolerance > 0.001 && g.JitterToleranceMultiplier >= 1 {
diff := math.Abs(newval - oldval)
x := math.Max(oldval, newval)
t := g.JitterTolerance
r := g.JitterToleranceMultiplier
return diff > t+x*(r-1) // https://www.desmos.com/calculator/raoti16r5n
}
return oldval == newval
2021-08-20 19:32:50 +02:00
} else {
2021-08-25 15:21:26 +02:00
return g.GetWeightType(oldval) != g.GetWeightType(newval)
2021-08-20 19:32:50 +02:00
}
}
func (g *IG) RecalculateNhTable(checkchange bool) (changed bool) {
if g.RecalculateTime.Add(g.RecalculateCoolDown).Before(time.Now()) {
dist, next := FloydWarshall(g)
2021-08-21 16:54:24 +02:00
changed = false
2021-08-20 19:32:50 +02:00
if checkchange {
CheckLoop:
for src, dsts := range next {
2021-08-21 16:54:24 +02:00
for dst, old_next := range dsts {
2021-08-20 19:32:50 +02:00
nexthop := g.Next(src, dst)
2021-08-21 16:54:24 +02:00
if old_next != nexthop {
changed = true
break CheckLoop
2021-08-20 19:32:50 +02:00
}
}
}
}
g.dlTable, g.nhTable = dist, next
2021-08-24 20:16:21 +02:00
g.NhTableExpire = time.Now().Add(g.NodeReportTimeout)
2021-08-20 19:32:50 +02:00
g.RecalculateTime = time.Now()
}
return
}
2021-08-25 15:21:26 +02:00
func (g *IG) UpdateLentancy(u, v config.Vertex, dt time.Duration, recalculate bool, checkchange bool) (changed bool) {
2021-08-21 16:54:24 +02:00
g.edgelock.Lock()
2021-08-16 20:58:15 +02:00
g.Vert[u] = true
g.Vert[v] = true
2021-08-20 19:32:50 +02:00
w := float64(dt) / float64(time.Second)
2021-08-21 16:54:24 +02:00
if _, ok := g.edges[u]; !ok {
g.edges[u] = make(map[config.Vertex]Latency)
2021-08-20 19:32:50 +02:00
}
2021-08-21 16:54:24 +02:00
g.edges[u][v] = Latency{
2021-08-16 20:58:15 +02:00
ping: w,
time: time.Now(),
}
2021-08-21 16:54:24 +02:00
g.edgelock.Unlock()
2021-08-25 15:21:26 +02:00
if g.ShouldUpdate(u, v, w) && recalculate {
changed = g.RecalculateNhTable(checkchange)
}
2021-08-20 19:32:50 +02:00
return
}
func (g IG) Vertices() map[config.Vertex]bool {
2021-08-23 18:39:04 +02:00
vr := make(map[config.Vertex]bool)
2021-08-23 19:45:09 +02:00
g.edgelock.RLock()
defer g.edgelock.RUnlock()
2021-08-23 18:39:04 +02:00
for k, v := range g.Vert { //copy a new list
vr[k] = v
}
return vr
2021-08-16 20:58:15 +02:00
}
2021-08-20 19:32:50 +02:00
func (g IG) Neighbors(v config.Vertex) (vs []config.Vertex) {
2021-08-23 18:39:04 +02:00
g.edgelock.RLock()
defer g.edgelock.RUnlock()
for k := range g.edges[v] { //copy a new list
2021-08-16 20:58:15 +02:00
vs = append(vs, k)
}
return vs
}
2021-08-20 19:32:50 +02:00
func (g IG) Next(u, v config.Vertex) *config.Vertex {
if _, ok := g.nhTable[u]; !ok {
2021-08-20 19:32:50 +02:00
return nil
}
if _, ok := g.nhTable[u][v]; !ok {
2021-08-20 19:32:50 +02:00
return nil
}
return g.nhTable[u][v]
2021-08-20 19:32:50 +02:00
}
func (g IG) Weight(u, v config.Vertex) float64 {
2021-08-23 18:39:04 +02:00
g.edgelock.RLock()
defer g.edgelock.RUnlock()
2021-08-21 16:54:24 +02:00
if _, ok := g.edges[u]; !ok {
2021-08-23 18:39:04 +02:00
g.edgelock.RUnlock()
2021-08-21 16:54:24 +02:00
g.edgelock.Lock()
g.edges[u] = make(map[config.Vertex]Latency)
g.edgelock.Unlock()
2021-08-23 18:39:04 +02:00
g.edgelock.RLock()
2021-08-20 19:32:50 +02:00
return Infinity
}
2021-08-21 16:54:24 +02:00
if _, ok := g.edges[u][v]; !ok {
2021-08-20 19:32:50 +02:00
return Infinity
}
2021-08-21 16:54:24 +02:00
if time.Now().After(g.edges[u][v].time.Add(g.NodeReportTimeout)) {
2021-08-20 19:32:50 +02:00
return Infinity
2021-08-16 20:58:15 +02:00
}
2021-08-21 16:54:24 +02:00
return g.edges[u][v].ping
2021-08-16 20:58:15 +02:00
}
2021-08-20 19:32:50 +02:00
func FloydWarshall(g Graph) (dist config.DistTable, next config.NextHopTable) {
2021-08-16 20:58:15 +02:00
vert := g.Vertices()
2021-08-20 19:32:50 +02:00
dist = make(config.DistTable)
next = make(config.NextHopTable)
2021-08-16 20:58:15 +02:00
for u, _ := range vert {
2021-08-20 19:32:50 +02:00
dist[u] = make(map[config.Vertex]float64)
next[u] = make(map[config.Vertex]*config.Vertex)
2021-08-16 20:58:15 +02:00
for v, _ := range vert {
dist[u][v] = Infinity
}
dist[u][u] = 0
for _, v := range g.Neighbors(u) {
w := g.Weight(u, v)
if w < Infinity {
v := v
dist[u][v] = w
next[u][v] = &v
}
}
}
for k, _ := range vert {
for i, _ := range vert {
for j, _ := range vert {
if dist[i][k] < Infinity && dist[k][j] < Infinity {
if dist[i][j] > dist[i][k]+dist[k][j] {
dist[i][j] = dist[i][k] + dist[k][j]
next[i][j] = next[i][k]
}
}
}
}
}
return dist, next
}
2021-08-20 19:32:50 +02:00
func Path(u, v config.Vertex, next config.NextHopTable) (path []config.Vertex) {
2021-08-16 20:58:15 +02:00
if next[u][v] == nil {
2021-08-20 19:32:50 +02:00
return []config.Vertex{}
2021-08-16 20:58:15 +02:00
}
2021-08-20 19:32:50 +02:00
path = []config.Vertex{u}
2021-08-16 20:58:15 +02:00
for u != v {
u = *next[u][v]
path = append(path, u)
}
return path
}
2021-08-20 19:32:50 +02:00
func (g *IG) SetNHTable(nh config.NextHopTable, table_hash [32]byte) { // set nhTable from supernode
g.nhTable = nh
2021-08-20 19:32:50 +02:00
g.NhTableHash = table_hash
2021-08-24 20:16:21 +02:00
g.NhTableExpire = time.Now().Add(g.SuperNodeInfoTimeout)
2021-08-20 19:32:50 +02:00
}
func (g *IG) GetNHTable(checkChange bool) config.NextHopTable {
2021-08-24 20:16:21 +02:00
if time.Now().After(g.NhTableExpire) {
2021-08-20 19:32:50 +02:00
g.RecalculateNhTable(checkChange)
}
return g.nhTable
2021-08-20 19:32:50 +02:00
}
2021-08-24 20:16:21 +02:00
func (g *IG) GetDtst() config.DistTable {
return g.dlTable
}
func (g *IG) GetEdges() (edges map[config.Vertex]map[config.Vertex]float64) {
vert := g.Vertices()
edges = make(map[config.Vertex]map[config.Vertex]float64, len(vert))
for src, _ := range vert {
edges[src] = make(map[config.Vertex]float64, len(vert))
for dst, _ := range vert {
2021-08-25 10:13:53 +02:00
if src != dst {
edges[src][dst] = g.Weight(src, dst)
}
2021-08-24 20:16:21 +02:00
}
}
return
}
2021-08-20 19:32:50 +02:00
func (g *IG) GetBoardcastList(id config.Vertex) (tosend map[config.Vertex]bool) {
tosend = make(map[config.Vertex]bool)
for _, element := range g.nhTable[id] {
2021-08-16 20:58:15 +02:00
tosend[*element] = true
}
return
}
2021-08-20 19:32:50 +02:00
func (g *IG) GetBoardcastThroughList(self_id config.Vertex, in_id config.Vertex, src_id config.Vertex) (tosend map[config.Vertex]bool) {
tosend = make(map[config.Vertex]bool)
for check_id, _ := range g.GetBoardcastList(self_id) {
for _, path_node := range Path(src_id, check_id, g.nhTable) {
2021-08-20 19:32:50 +02:00
if path_node == self_id && check_id != in_id {
2021-08-16 20:58:15 +02:00
tosend[check_id] = true
continue
}
}
}
return
}
2021-08-25 15:21:26 +02:00
func printExample() {
fmt.Println(`X 1 2 3 4 5 6
1 0 0.5 Inf Inf Inf Inf
2 0.5 0 0.5 0.5 Inf Inf
3 Inf 0.5 0 0.5 0.5 Inf
4 Inf 0.5 0.5 0 Inf 0.5
5 Inf Inf 0.5 Inf 0 Inf
6 Inf Inf Inf 0.5 Inf 0`)
}
func a2n(s string) (ret float64) {
if s == "Inf" {
return Infinity
}
ret, err := strconv.ParseFloat(s, 64)
if err != nil {
panic(err)
}
return
}
func a2v(s string) config.Vertex {
ret, err := strconv.Atoi(s)
if err != nil {
panic(err)
}
return config.Vertex(ret)
}
func Solve(filePath string, pe bool) error {
if pe {
printExample()
return nil
}
g := NewGraph(3, false, config.GraphRecalculateSetting{
NodeReportTimeout: 9999,
}, config.NTPinfo{}, false)
inputb, err := ioutil.ReadFile(filePath)
if err != nil {
return err
}
input := string(inputb)
lines := strings.Split(input, "\n")
verts := strings.Fields(lines[0])
for _, line := range lines[1:] {
element := strings.Fields(line)
src := a2v(element[0])
for index, sval := range element[1:] {
val := a2n(sval)
dst := a2v(verts[index+1])
if src != dst && val != Infinity {
g.UpdateLentancy(src, dst, S2TD(val), false, false)
2021-08-16 20:58:15 +02:00
}
}
}
2021-08-25 15:21:26 +02:00
dist, next := FloydWarshall(g)
2021-08-16 20:58:15 +02:00
rr, _ := yaml.Marshal(Fullroute{
Dist: dist,
Next: next,
})
fmt.Print(string(rr))
2021-08-25 15:21:26 +02:00
fmt.Println("\nHuman readable:")
fmt.Println("src\tdist\t\tpath")
for _, U := range verts[1:] {
u := a2v(U)
for _, V := range verts[1:] {
v := a2v(V)
if u != v {
fmt.Printf("%d -> %d\t%3f\t%s\n", u, v, dist[u][v], fmt.Sprint(Path(u, v, next)))
}
}
}
return nil
2021-08-16 20:58:15 +02:00
}