Static mode for supernode,dump packet, update readme, add code of concept

This commit is contained in:
KusakabeSi 2021-09-30 21:15:23 +00:00
parent b6cb1db5d8
commit 06b7ea1edb
36 changed files with 1157 additions and 136 deletions

2
.vscode/launch.json vendored
View File

@ -12,7 +12,7 @@
"program": "${workspaceFolder}",
"buildFlags": "-tags=vpp",
"env": {"CGO_CFLAGS":"-I/usr/include/memif"},
"args":["-config","tttttttttt.yaml","-mode","edge"/*,"-example"*/],
"args":["-config","example_config/super_mode/s1.yaml","-mode","super"/*,"-example"*/],
}
]
}

View File

@ -4,13 +4,17 @@
A Full Mesh Layer2 VPN based on wireguard-go
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](code_of_conduct.md)
OSPF can find best route based on it's cost.
But sometimes the lentancy are different in the packet goes and back.
I'am thinking, is it possible to find the best route based on the **single-way latency**?
For example, I have two routes A and B at node N1, both of them can reach my node N2. A goes fast, but B backs fast.
My VPN can automatically send packet through route A at node N1, and the packet backsfrom route B.
My VPN can automatically send packet through route A at node N1, and the packet backs from route B.
Here is the solution. This VPN `Etherguard` can collect all the single-way lentancy from all nodes, and calculate the best route using [FloydWarshall algorithm](https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm).
Here is the solution. This VPN `Etherguard` can collect all the single-way lentancy from all nodes, and calculate the best route using [FloydWarshall algorithm](https://en.wikipedia.org/wiki/FloydWarshall_algorithm).
Wirried about the clock not match so that the measure result are not correct? It doesn't matter, here is the proof (Mandarin): [https://www.kskb.eu.org/2021/08/rootless-routerpart-3-etherguard.html](https://www.kskb.eu.org/2021/08/rootless-routerpart-3-etherguard.html)
## Usage
@ -18,7 +22,7 @@ Here is the solution. This VPN `Etherguard` can collect all the single-way lenta
Usage of ./etherguard-go:
-bind string
UDP socket bind mode. [linux|std]
You may need this if tou want to run Etherguard under WSL. (default "linux")
You may need std mode if tou want to run Etherguard under WSL. (default "linux")
-config string
Config path.
-example
@ -68,7 +72,7 @@ Usage of ./etherguard-go:
2. `kbdbg`: Keyboard debug mode.
Let me construct Layer 2 header by ascii character only.
So that I can track the packet flow with `loglevel` option.
3. `noL2`: Remove all Layer 2 header
3. `noL2`: Remove all Layer 2 header, all boardcast
2. `nodeid`: NodeID. Must be unique in the whole Etherguard network.
3. `nodename`: Node Name.
4. `defaultttl`: Default TTL(etherguard layer. not affect ethernet layer)
@ -85,8 +89,8 @@ Usage of ./etherguard-go:
2. `dupchecktimeout`: Duplication chack timeout.
3. `conntimeout`: Connection timeout.
4. `savenewpeers`: Save peer info to local file.
5. `supernode`: See [Super Mode](example_config/super_mode/README_zh.md)
6. `p2p` See [P2P Mode](example_config/p2p_mode/README_zh.md)
5. `supernode`: See [Super Mode](example_config/super_mode/README.md)
6. `p2p` See [P2P Mode](example_config/p2p_mode/README.md)
7. `ntpconfig`: NTP related settings
1. `usentp`: USE NTP or not.
2. `maxserveruse`: How many NTP servers should we use at once.
@ -94,7 +98,7 @@ Usage of ./etherguard-go:
3. `synctimeinterval`: NTP sync interval.
4. `ntptimeout`: NTP timeout
5. `servers`: NTP server list
8. `nexthoptable`: Nexthop table。Only static mode use it. See [Static Mㄍㄟ](example_config/super_mode/README_zh.md)
8. `nexthoptable`: Nexthop table。Only static mode use it. See [Static Mode](example_config/super_mode/README.md)
9. `resetconninterval`: Reset the endpoint for peers. You may need this if that peer use DDNS.
10. `peers`: Peer info.
1. `nodeid`: Node ID.

View File

@ -3,6 +3,8 @@
[English](README.md)
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](code_of_conduct.md)
一個從wireguard-go改來的Full Mesh Layer2 VPN.
OSPF能夠根據cost自動選路

133
code_of_conduct.md Normal file
View File

@ -0,0 +1,133 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[INSERT CONTACT METHOD].
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available
at [https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations

View File

@ -10,8 +10,8 @@ import (
type Vertex uint16
const (
Boardcast Vertex = math.MaxUint16 - iota // Normal boardcast, boardcast with route table
ControlMessage Vertex = math.MaxUint16 - iota // p2p mode: boardcast to every know keer and prevent dup/ super mode: send to supernode
Broadcast Vertex = math.MaxUint16 - iota // Normal boardcast, boardcast with route table
ControlMessage Vertex = math.MaxUint16 - iota // p2p mode: boardcast to every know peer and prevent dup. super mode: send to supernode
SuperNodeMessage Vertex = math.MaxUint16 - iota
Special_NodeID Vertex = SuperNodeMessage
)
@ -39,7 +39,9 @@ type SuperConfig struct {
RePushConfigInterval float64
Passwords Passwords
GraphRecalculateSetting GraphRecalculateSetting
NextHopTable NextHopTable
EdgeTemplate string
UsePSKForInterEdge bool
Peers []SuperPeerInfo
}
@ -86,7 +88,7 @@ type LoggerInfo struct {
func (v *Vertex) ToString() string {
switch *v {
case Boardcast:
case Broadcast:
return "Boardcast"
case ControlMessage:
return "Control"
@ -135,6 +137,7 @@ type P2Pinfo struct {
}
type GraphRecalculateSetting struct {
StaticMode bool
JitterTolerance float64
JitterToleranceMultiplier float64
NodeReportTimeout float64

View File

@ -16,6 +16,8 @@ import (
"sync/atomic"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"golang.org/x/crypto/chacha20poly1305"
"github.com/KusakabeSi/EtherGuardVPN/config"
@ -474,7 +476,7 @@ func (peer *Peer) RoutineSequentialReceiver() {
}
} else {
switch dst_nodeID {
case config.Boardcast:
case config.Broadcast:
should_receive = true
should_transfer = true
case config.SuperNodeMessage:
@ -492,7 +494,7 @@ func (peer *Peer) RoutineSequentialReceiver() {
}
}
case device.ID:
if packet_type == path.NornalPacket {
if packet_type == path.NormalPacket {
should_receive = true
} else {
should_process = true
@ -511,7 +513,7 @@ func (peer *Peer) RoutineSequentialReceiver() {
device.log.Verbosef("TTL is 0 %v", dst_nodeID)
} else {
EgHeader.SetTTL(l2ttl - 1)
if dst_nodeID == config.Boardcast { //Regular transfer algorithm
if dst_nodeID == config.Broadcast { //Regular transfer algorithm
device.TransitBoardcastPacket(src_nodeID, peer.ID, elem.Type, elem.packet, MessageTransportOffsetContent)
} else if dst_nodeID == config.ControlMessage { // Control Message will try send to every know node regardless the connectivity
skip_list := make(map[config.Vertex]bool)
@ -535,7 +537,7 @@ func (peer *Peer) RoutineSequentialReceiver() {
}
if should_process {
if packet_type != path.NornalPacket {
if packet_type != path.NormalPacket {
if device.LogLevel.LogControl {
if peer.GetEndpointDstStr() != "" {
fmt.Println("Control: Received From:" + peer.GetEndpointDstStr() + " " + device.sprint_received(packet_type, elem.packet[path.EgHeaderLen:]))
@ -549,14 +551,16 @@ func (peer *Peer) RoutineSequentialReceiver() {
}
if should_receive { // Write message to tap device
if packet_type == path.NornalPacket {
if device.LogLevel.LogNormal {
fmt.Println("Normal: Reveived Normal packet From:" + peer.GetEndpointDstStr() + " SrcID:" + src_nodeID.ToString() + " DstID:" + dst_nodeID.ToString() + " Len:" + strconv.Itoa(len(elem.packet)))
}
if packet_type == path.NormalPacket {
if len(elem.packet) <= path.EgHeaderLen+12 {
device.log.Errorf("Invalid normal packet from peer %v", peer.ID.ToString())
goto skip
}
if device.LogLevel.LogNormal {
fmt.Println("Normal: Reveived Normal packet From:" + peer.GetEndpointDstStr() + " SrcID:" + src_nodeID.ToString() + " DstID:" + dst_nodeID.ToString() + " Len:" + strconv.Itoa(len(elem.packet)))
packet := gopacket.NewPacket(elem.packet[path.EgHeaderLen:], layers.LayerTypeEthernet, gopacket.Default)
fmt.Println(packet.Dump())
}
src_macaddr := tap.GetSrcMacAddr(elem.packet[path.EgHeaderLen:])
if !tap.IsNotUnicast(src_macaddr) {
actual, loaded := device.l2fib.LoadOrStore(src_macaddr, src_nodeID)

View File

@ -11,11 +11,14 @@ import (
"net/http"
"net/url"
"strconv"
"strings"
"time"
"github.com/KusakabeSi/EtherGuardVPN/config"
orderedmap "github.com/KusakabeSi/EtherGuardVPN/orderdmap"
"github.com/KusakabeSi/EtherGuardVPN/path"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
func (device *Device) SendPacket(peer *Peer, usage path.Usage, packet []byte, offset int) {
@ -26,13 +29,15 @@ func (device *Device) SendPacket(peer *Peer, usage path.Usage, packet []byte, of
}
if device.LogLevel.LogNormal {
EgHeader, _ := path.NewEgHeader(packet[:path.EgHeaderLen])
if usage == path.NornalPacket {
if usage == path.NormalPacket {
dst_nodeID := EgHeader.GetDst()
fmt.Println("Normal: Send Normal packet To:" + peer.GetEndpointDstStr() + " SrcID:" + device.ID.ToString() + " DstID:" + dst_nodeID.ToString() + " Len:" + strconv.Itoa(len(packet)))
packet := gopacket.NewPacket(packet[path.EgHeaderLen:], layers.LayerTypeEthernet, gopacket.Default)
fmt.Println(packet.Dump())
}
}
if device.LogLevel.LogControl {
if usage != path.NornalPacket {
if usage != path.NormalPacket {
if peer.GetEndpointDstStr() != "" {
fmt.Println("Control: Send To:" + peer.GetEndpointDstStr() + " " + device.sprint_received(usage, packet[path.EgHeaderLen:]))
}
@ -155,7 +160,7 @@ func (device *Device) process_received(msg_type path.Usage, peer *Peer, body []b
if content, err := path.ParseQueryPeerMsg(body); err == nil {
return device.process_RequestPeerMsg(content)
}
case path.BoardcastPeer:
case path.BroadcastPeer:
if content, err := path.ParseBoardcastPeerMsg(body); err == nil {
return device.process_BoardcastPeerMsg(peer, content)
}
@ -203,7 +208,7 @@ func (device *Device) sprint_received(msg_type path.Usage, body []byte) string {
return content.ToString()
}
return "QueryPeerMsg: Parse failed"
case path.BoardcastPeer:
case path.BroadcastPeer:
if content, err := path.ParseBoardcastPeerMsg(body); err == nil {
return content.ToString()
}
@ -213,6 +218,16 @@ func (device *Device) sprint_received(msg_type path.Usage, body []byte) string {
}
}
func compareVersion(v1 string, v2 string) bool {
if strings.Contains(v1, "-") {
v1 = strings.Split(v1, "-")[0]
}
if strings.Contains(v2, "-") {
v2 = strings.Split(v2, "-")[0]
}
return v1 == v2
}
func (device *Device) server_process_RegisterMsg(peer *Peer, content path.RegisterMsg) error {
UpdateErrorMsg := path.UpdateErrorMsg{
Node_id: peer.ID,
@ -228,7 +243,7 @@ func (device *Device) server_process_RegisterMsg(peer *Peer, content path.Regist
ErrorMsg: "Your node ID is not match with our registered nodeID",
}
}
if content.Version != device.Version {
if compareVersion(content.Version, device.Version) == false {
UpdateErrorMsg = path.UpdateErrorMsg{
Node_id: peer.ID,
Action: path.Shutdown,
@ -297,11 +312,11 @@ func (device *Device) process_ping(peer *Peer, content path.PingMsg) error {
return nil
}
func (device *Device) SendPing(peer *Peer, times int, replies int, interval float32) {
func (device *Device) SendPing(peer *Peer, times int, replies int, interval float64) {
for i := 0; i < times; i++ {
packet, usage, _ := device.GeneratePingPacket(device.ID, replies)
device.SendPacket(peer, usage, packet, MessageTransportOffsetContent)
time.Sleep(path.S2TD(float64(interval)))
time.Sleep(path.S2TD(interval))
}
}
@ -657,7 +672,7 @@ func (device *Device) RoutineSpreadAllMyNeighbor() {
}
for {
device.process_RequestPeerMsg(path.QueryPeerMsg{
Request_ID: uint32(config.Boardcast),
Request_ID: uint32(config.Broadcast),
})
time.Sleep(path.S2TD(device.DRoute.P2P.SendPeerInterval))
}
@ -737,7 +752,7 @@ func (device *Device) process_RequestPeerMsg(content path.QueryPeerMsg) error {
header.SetSrc(device.ID)
header.SetPacketLength(uint16(len(body)))
copy(buf[path.EgHeaderLen:], body)
device.SpreadPacket(make(map[config.Vertex]bool), path.BoardcastPeer, buf, MessageTransportOffsetContent)
device.SpreadPacket(make(map[config.Vertex]bool), path.BroadcastPeer, buf, MessageTransportOffsetContent)
}
device.peers.RUnlock()
}

View File

@ -19,6 +19,8 @@ import (
"github.com/KusakabeSi/EtherGuardVPN/config"
"github.com/KusakabeSi/EtherGuardVPN/path"
"github.com/KusakabeSi/EtherGuardVPN/tap"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"golang.org/x/crypto/chacha20poly1305"
)
@ -254,9 +256,9 @@ func (device *Device) RoutineReadFromTUN() {
dstMacAddr := tap.GetDstMacAddr(elem.packet[path.EgHeaderLen:])
// lookup peer
if tap.IsNotUnicast(dstMacAddr) {
dst_nodeID = config.Boardcast
dst_nodeID = config.Broadcast
} else if val, ok := device.l2fib.Load(dstMacAddr); !ok { //Lookup failed
dst_nodeID = config.Boardcast
dst_nodeID = config.Broadcast
} else {
dst_nodeID = val.(config.Vertex)
}
@ -264,9 +266,9 @@ func (device *Device) RoutineReadFromTUN() {
EgBody.SetDst(dst_nodeID)
EgBody.SetPacketLength(uint16(len(elem.packet) - path.EgHeaderLen))
EgBody.SetTTL(device.DefaultTTL)
elem.Type = path.NornalPacket
elem.Type = path.NormalPacket
if dst_nodeID != config.Boardcast {
if dst_nodeID != config.Broadcast {
var peer *Peer
next_id := device.graph.Next(device.ID, dst_nodeID)
if next_id != nil {
@ -278,6 +280,8 @@ func (device *Device) RoutineReadFromTUN() {
}
if device.LogLevel.LogNormal {
fmt.Println("Normal: Send Normal packet To:" + peer.GetEndpointDstStr() + " SrcID:" + device.ID.ToString() + " DstID:" + dst_nodeID.ToString() + " Len:" + strconv.Itoa(len(elem.packet)))
packet := gopacket.NewPacket(elem.packet[path.EgHeaderLen:], layers.LayerTypeEthernet, gopacket.Default)
fmt.Println(packet.Dump())
}
if peer.isRunning.Get() {
peer.StagePacket(elem)

View File

@ -62,6 +62,7 @@ P2P模式也有幾個參數
1. sendpeerinterval: 廣播BoardcastPeer的間格
1. peeralivetimeout: 每次收到封包就重置超過時間沒收到就視為該peer離線
1. graphrecalculatesetting: 一些和[Floyd-Warshall演算法](https://zh.wikipedia.org/zh-tw/Floyd-Warshall算法)相關的參數
1. staticmode: 關閉Floyd-Warshall演算法只使用一開始載入的nexthoptable。P2P單純用來打洞
1. jittertolerance: 抖動容許誤差收到Pong以後一個37ms一個39ms不會觸發重新計算
1. jittertolerancemultiplier: 一樣是抖動容許誤差但是高ping的話允許更多誤差
https://www.desmos.com/calculator/raoti16r5n
@ -74,4 +75,4 @@ P2P模式下PSK是禁用的。因為n個節點有n(n-1)/2的連線每個
也不像super mode有中心伺服器統一分發
每對peer要協商出一個PSK有難度因此我設定禁用PSK了只用wireguard原本的加密系統
**最後P2P模式我還沒有大規模測試過穩定性不知如何。PR is welecome**
**最後P2P模式我還沒有大規模測試過穩定性不知如何。PR is welcome**

View File

@ -38,6 +38,7 @@ dynamicroute:
usep2p: true
sendpeerinterval: 20
graphrecalculatesetting:
staticmode: false
jittertolerance: 20
jittertolerancemultiplier: 1.1
nodereporttimeout: 40

View File

@ -38,6 +38,7 @@ dynamicroute:
usep2p: true
sendpeerinterval: 20
graphrecalculatesetting:
staticmode: false
jittertolerance: 20
jittertolerancemultiplier: 1.1
nodereporttimeout: 40

View File

@ -38,6 +38,7 @@ dynamicroute:
usep2p: true
sendpeerinterval: 20
graphrecalculatesetting:
staticmode: false
jittertolerance: 20
jittertolerancemultiplier: 1.1
nodereporttimeout: 40

View File

@ -38,6 +38,7 @@ dynamicroute:
usep2p: true
sendpeerinterval: 20
graphrecalculatesetting:
staticmode: false
jittertolerance: 20
jittertolerancemultiplier: 1.1
nodereporttimeout: 40

View File

@ -38,6 +38,7 @@ dynamicroute:
usep2p: true
sendpeerinterval: 20
graphrecalculatesetting:
staticmode: false
jittertolerance: 20
jittertolerancemultiplier: 1.1
nodereporttimeout: 40

View File

@ -38,6 +38,7 @@ dynamicroute:
usep2p: true
sendpeerinterval: 20
graphrecalculatesetting:
staticmode: false
jittertolerance: 20
jittertolerancemultiplier: 1.1
nodereporttimeout: 40

View File

@ -1,4 +1,205 @@
# Etherguard
[中文版](README_zh.md)
WIP
This is the documentation of the static_mode of this example_config
## Static mode
No dynamic routing, no handshake server.
Similar to original wireguard , all things must be preconfigured.
But you need to setup an additional `Next hop table`, this table are share among all nodes.
The `nexthoptable` section is for this mode, and only works in this mode.
In this mode, there are no any Control Message, no connectivity check.
Please maintains the predefined topology, otherwise if the relay node offline, part of this network will broken,
The topology of this [example_config](./):
!["Topology"](https://raw.githubusercontent.com/KusakabeSi/EtherGuard-VPN/master/example_config/static_mode/Example_static.png)
Before sending packet, We will set the SrcID to my NodeID. And the DstID will be found from l2fib table. If lookup failed or it's a Broadcast address, It will be set to `Broadcast(65535)`
While received packet, if the DstID==NodeID, or DstID==65535, it will receive the packet, and send to correspond tap device. And meanwhile, add the NodeID->SrcMacAddress to l2fib.
If not, it will lookup from the `Next hop table`, to determine who will be sent of this packet.
Here is an example of the `Next hop table` in this example topology. A yaml formatted nested dictionary. `NhTable[SrcID][DstID]= Next hop ID`
```yaml
nexthoptable:
1:
2: 2
3: 2
4: 2
5: 2
6: 2
2:
1: 1
3: 3
4: 4
5: 3
6: 4
3:
1: 2
2: 2
4: 4
5: 5
6: 4
4:
1: 2
2: 2
3: 3
5: 3
6: 6
5:
1: 3
2: 3
3: 3
4: 3
6: 3
6:
1: 4
2: 4
3: 4
4: 4
5: 4
```
### Broadcast
Broadcast is a special case.
Today I am Node 4, and I received a `Src=1, dst=Broadcast`.
I should send to Node 6 ONLY without sending it to Node 3.
Cuz Node 3 should receive it from Node 2 Instead of me.
So if `dst=Broadcast`, I will check src to all my neighbors whether I am a required route of this packet.
**1 -> 6** : [1 2 4 6] , I am a required route
**1 -> 3** : [1 2 3] , I am not a required route
**1 -> 3** : Skip check, packet is coming from it
So I knows I should send this packet to Node 6 only.
### `Next Hop Table` calculator
This tool can also calculate `Next Hop Table` for you.
Prepare a `path.txt` first, mark all single way latency in it like this:
```
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
```
`Inf` means unreachable.
Then use this command to calculate it.
```
./etherguard-go -config example_config/static_mode/path.txt -mode solve
NextHopTable:
1:
2: 2
3: 2
4: 2
5: 2
6: 2
2:
1: 1
3: 3
4: 4
5: 3
6: 4
3:
1: 2
2: 2
4: 4
5: 5
6: 4
4:
1: 2
2: 2
3: 3
5: 3
6: 6
5:
1: 3
2: 3
3: 3
4: 3
6: 3
6:
1: 4
2: 4
3: 4
4: 4
5: 4
```
There are some additional information of the calculation result.
```
Human readable:
src dist path
1 -> 2 0.500000 [1 2]
1 -> 3 1.000000 [1 2 3]
1 -> 4 1.000000 [1 2 4]
1 -> 5 1.500000 [1 2 3 5]
1 -> 6 1.500000 [1 2 4 6]
2 -> 1 0.500000 [2 1]
2 -> 3 0.500000 [2 3]
2 -> 4 0.500000 [2 4]
2 -> 5 1.000000 [2 3 5]
2 -> 6 1.000000 [2 4 6]
3 -> 1 1.000000 [3 2 1]
3 -> 2 0.500000 [3 2]
3 -> 4 0.500000 [3 4]
3 -> 5 0.500000 [3 5]
3 -> 6 1.000000 [3 4 6]
4 -> 1 1.000000 [4 2 1]
4 -> 2 0.500000 [4 2]
4 -> 3 0.500000 [4 3]
4 -> 5 1.000000 [4 3 5]
4 -> 6 0.500000 [4 6]
5 -> 1 1.500000 [5 3 2 1]
5 -> 2 1.000000 [5 3 2]
5 -> 3 0.500000 [5 3]
5 -> 4 1.000000 [5 3 4]
5 -> 6 1.500000 [5 3 4 6]
6 -> 1 1.500000 [6 4 2 1]
6 -> 2 1.000000 [6 4 2]
6 -> 3 1.000000 [6 4 3]
6 -> 4 0.500000 [6 4]
6 -> 5 1.500000 [6 4 3 5]
```
### Quick start
#### Run example config
Execute following command in **Different Terminal**
```
./etherguard-go -config example_config/super_mode/n1.yaml -mode edge
./etherguard-go -config example_config/super_mode/n2.yaml -mode edge
./etherguard-go -config example_config/super_mode/n3.yaml -mode edge
./etherguard-go -config example_config/super_mode/n4.yaml -mode edge
./etherguard-go -config example_config/super_mode/n5.yaml -mode edge
./etherguard-go -config example_config/super_mode/n6.yaml -mode edge
```
The iface type of this example config is `stdio` (keyboard debug), so it will read data from stdin.
Then input following text in the terminal
```
b1message
```
The `l2headermode` is `kbdbg`, means `Keyboard debug`. So that the first two byte will be convert to `FF:FF:FF:FF:FF:FF` and `AA:BB:CC:DD:EE:01`. And the `message` is the real payload.
With other debug message, you should be able to see the message in other terminal.
#### Use it in real world
Please modify the `itype` to `tap`, and modify the pubkey and privkey, close unnecessary logging , and deploy to all nodes.
## Next: [Super Mode](../super_mode/README.md)

View File

@ -31,7 +31,7 @@ Static Mode的[範例配置檔](./)的說明文件
轉發/發送封包時,直接查詢 `NhTable[起點][終點]=下一跳`
就知道下面一個封包要轉給誰了
```
```yaml
nexthoptable:
1:
2: 2
@ -182,4 +182,32 @@ src dist path
6 -> 5 1.500000 [6 4 3 5]
```
接下來你就能了解一下[Super Mode的運作](../super_mode/README_zh.md)
### Quick start
#### Run example config
在**不同terminal**分別執行以下命令
```
./etherguard-go -config example_config/super_mode/n1.yaml -mode edge
./etherguard-go -config example_config/super_mode/n2.yaml -mode edge
./etherguard-go -config example_config/super_mode/n3.yaml -mode edge
./etherguard-go -config example_config/super_mode/n4.yaml -mode edge
./etherguard-go -config example_config/super_mode/n5.yaml -mode edge
./etherguard-go -config example_config/super_mode/n6.yaml -mode edge
```
因為本範例配置是stdio的kbdbg模式stdin會讀入VPN網路
請在其中一個edge視窗中鍵入
```
b1message
```
因為`l2headermode`是`kbdbg`所以b1會被轉換成 12byte 的layer 2 headerb是廣播地址`FF:FF:FF:FF:FF:FF`1是普通地址`AA:BB:CC:DD:EE:01`message是後面的payload然後再丟入VPN
此時應該要能夠在另一個視窗上看見字串b1message。前12byte被轉換回來了
#### Run your own etherguard
要正式使用請將itype改成`tap`,並且修改各節點的公鑰私鑰和連線地址
再關閉不必要的log增加性能最後部屬到不同節點即可
## 下一篇: [Super Mode的運作](../super_mode/README_zh.md)

View File

@ -38,6 +38,7 @@ dynamicroute:
usep2p: false
sendpeerinterval: 20
graphrecalculatesetting:
staticmode: false
jittertolerance: 20
jittertolerancemultiplier: 1.1
nodereporttimeout: 40

View File

@ -38,6 +38,7 @@ dynamicroute:
usep2p: false
sendpeerinterval: 20
graphrecalculatesetting:
staticmode: false
jittertolerance: 20
jittertolerancemultiplier: 1.1
nodereporttimeout: 40

View File

@ -38,6 +38,7 @@ dynamicroute:
usep2p: false
sendpeerinterval: 20
graphrecalculatesetting:
staticmode: false
jittertolerance: 20
jittertolerancemultiplier: 1.1
nodereporttimeout: 40

View File

@ -38,6 +38,7 @@ dynamicroute:
usep2p: false
sendpeerinterval: 20
graphrecalculatesetting:
staticmode: false
jittertolerance: 20
jittertolerancemultiplier: 1.1
nodereporttimeout: 40

View File

@ -38,6 +38,7 @@ dynamicroute:
usep2p: false
sendpeerinterval: 20
graphrecalculatesetting:
staticmode: false
jittertolerance: 20
jittertolerancemultiplier: 1.1
nodereporttimeout: 40

View File

@ -38,6 +38,7 @@ dynamicroute:
usep2p: false
sendpeerinterval: 20
graphrecalculatesetting:
staticmode: false
jittertolerance: 20
jittertolerancemultiplier: 1.1
nodereporttimeout: 40

View File

@ -1,4 +1,487 @@
# Etherguard
[中文版](README_zh.md)
WIP
This is the documentation of the super_mode of this example_config
Before reading this, I'd like to suggest you read the [static mode](../static_mode/README.md) first.
## Super mode
Super mode are inspired by [n2n](https://github.com/ntop/n2n)
We have two types of node, we called it super node and edge node.
All edge nodes have to connect to super node, exchange data and UDP hole punch each other by super node.
The super node runs the [Floyd-Warshall Algorithm](https://en.wikipedia.org/wiki/FloydWarshall_algorithm) and distribute the result to all edge node.
In the super mode of the edge node, the `nexthoptable` and `peers` section are useless. All infos are download from super node.
Meanwhile, super node will generate pre shared key for inter-edge communication(if `usepskforinteredge` enabled).
```golang
psk = shs256("PubkeyPeerA" + "PubkeyPeerB" + "Chef Special and Featured in the season see salt")[:32]
```
### SuperMsg
There are new type of DstID called `SuperMsg`(65534). All packets sends to and receive from super node are using this packet type.
This packet will not send to any other edge node, just like `DstID == self.NodeID`
## Control Message
In Super mode, Beside `Normal Packet`. We introduce a new packet type called `Control Message`. In Super mode, we will not relay any control message. We just receive or send it to target directly.
We list all the control message we use in the super mode below.
### Register
This control message works like this picture:
![Workflow of Register](https://raw.githubusercontent.com/KusakabeSi/EtherGuard-VPN/master/example_config/super_mode/EGS01.png)
1. edge node send Register to the super node
2. Supernode knows it's external IP and port number
3. Update it to database and distribute `UpdatePeerMsg` to all edges
4. Other edges get the notification, download the updated peer infos from supernode via HTTP API
### Ping/Pong
While edges get the peer infos, edges will start trying to talk each other directly like this picture:
![Workflow of Ping/Pong](https://raw.githubusercontent.com/KusakabeSi/EtherGuard-VPN/master/example_config/super_mode/EGS02.png)
1. Send `Ping` to all other edges with local time with TTL=0
2. Received a `Ping`, Subtract the peer time from local time, we get a single way latency.
3. Send a `Pong` to supernode, let supernode calculate the NextHopTable
4. Wait the supernode push `UpdateNhTable` message and download it.
### UpdateNhTable
While supernode get a `Pong` message, it will run the [Floyd-Warshall Algorithm](https://en.wikipedia.org/wiki/FloydWarshall_algorithm) to calculate the NextHopTable
![image](https://raw.githubusercontent.com/KusakabeSi/EtherGuard-VPN/master/example_config/super_mode/EGS03.png)
If there are any changes of this table, it will distribute `UpdateNhTable` to all edges to till then download the latest NextHopTable via HTTP API as soon as possible.
### UpdateError
Notify edges that an error has occurred, and close the edge
It occurs when the version number is not match with supernode, or the NodeID of the edge is configured incorrectly, or the edge is deleted.
### HTTP API
Why we use HTTP API instead of pack all information in the `UpdateXXX`?
Because UDP is an unreliable protocol, there is an limit on the amount of content that can be carried.
But the peer list contains all the peer information, the length is not fixed, it may exceed
So we use `UpdateXXX` to tell we have a update, please download the latest information from supernode via HTTP API as soon as possible.
And `UpdateXXX` itself is not reliable, maybe it didn't reach the edge node at all.
So the information of `UpdateXXX` carries the `state hash`. Bring it when with HTTP API. When the super node receives the HTTP API and sees the `state hash`, it knows that the edge node has received the `UpdateXXX`.
Otherwise, it will send `UpdateXXX` to the node again after few seconds.
## HTTP Guest API
HTTP also has some APIs for the front-end to help manage the entire network
### peerstate
```bash
curl "http://127.0.0.1:3000/api/peerstate?Password=passwd"
```
It can show some information such as single way latency or last seen time.
We can visualize it by Force-directed graph drawing.
There is an `Infinity` section in the json response. It should be 9999. It means infinity if the number larger than it.
Cuz json can't present infinity so that I use this trick.
While we see the latency larger than this, we doesn't need to draw lines in this two nodes.
Example return value:
```json
{
"PeerInfo": {
"1": {
"Name": "hk",
"LastSeen": "2021-09-29 11:23:22.854700559 +0000 UTC m=+28740.116476977"
},
"1001": {
"Name": "relay_kr",
"LastSeen": "2021-09-29 11:23:21.277417897 +0000 UTC m=+28738.539194315"
},
"121": {
"Name": "za_north",
"LastSeen": "0001-01-01 00:00:00 +0000 UTC"
},
"33": {
"Name": "us_west",
"LastSeen": "2021-09-29 11:23:13.257033252 +0000 UTC m=+28730.518809670"
},
"49": {
"Name": "us_east",
"LastSeen": "2021-09-29 11:23:16.606165241 +0000 UTC m=+28733.867941659"
},
"51": {
"Name": "ca_central",
"LastSeen": "0001-01-01 00:00:00 +0000 UTC"
},
"65": {
"Name": "fr",
"LastSeen": "2021-09-29 11:23:19.4084596 +0000 UTC m=+28736.670236018"
},
"81": {
"Name": "au_central",
"LastSeen": "0001-01-01 00:00:00 +0000 UTC"
},
"89": {
"Name": "uae_north",
"LastSeen": "0001-01-01 00:00:00 +0000 UTC"
},
"9": {
"Name": "jp_east",
"LastSeen": "2021-09-29 11:23:16.669505147 +0000 UTC m=+28733.931281565"
},
"97": {
"Name": "br_south",
"LastSeen": "0001-01-01 00:00:00 +0000 UTC"
}
},
"Infinity": 99999,
"Edges": {
"1": {
"1001": 0.033121187,
"33": 0.075653164,
"49": 0.100471502,
"65": 0.065714769,
"9": 0.022864241
},
"1001": {
"1": 0.018561948,
"33": 0.064077348,
"49": 0.094459818,
"65": 0.079481599,
"9": 0.011163433
},
"33": {
"1": 0.075263428,
"1001": 0.070029457,
"49": 0.032631349,
"65": 0.045575061,
"9": 0.050444255
},
"49": {
"1": 0.100271358,
"1001": 0.100182834,
"33": 0.034563118,
"65": 0.017950046,
"9": 0.07510982
},
"65": {
"1": 0.114219741,
"1001": 0.132759205,
"33": 0.095265063,
"49": 0.067413235,
"9": 0.127562362
},
"9": {
"1": 0.026909699,
"1001": 0.022555855,
"33": 0.056469043,
"49": 0.090400723,
"65": 0.08525314
}
},
"NhTable": {
"1": {
"1001": 1001,
"33": 33,
"49": 49,
"65": 65,
"9": 9
},
"1001": {
"1": 1,
"33": 33,
"49": 49,
"65": 65,
"9": 9
},
"33": {
"1": 1,
"1001": 1001,
"49": 49,
"65": 65,
"9": 9
},
"49": {
"1": 1,
"1001": 9,
"33": 33,
"65": 65,
"9": 9
},
"65": {
"1": 1,
"1001": 1001,
"33": 33,
"49": 49,
"9": 9
},
"9": {
"1": 1,
"1001": 1001,
"33": 33,
"49": 33,
"65": 65
}
},
"Dist": {
"1": {
"1": 0,
"1001": 0.033121187,
"33": 0.075119328,
"49": 0.102236885,
"65": 0.074688856,
"9": 0.022473723
},
"1001": {
"1": 0.018561948,
"1001": 0,
"33": 0.064077348,
"49": 0.094459818,
"65": 0.079481599,
"9": 0.011163433
},
"33": {
"1": 0.075263428,
"1001": 0.070029457,
"33": 0,
"49": 0.032631349,
"65": 0.045575061,
"9": 0.050444255
},
"49": {
"1": 0.100271358,
"1001": 0.097665675,
"33": 0.034563118,
"49": 0,
"65": 0.017950046,
"9": 0.07510982
},
"65": {
"1": 0.114219741,
"1001": 0.132759205,
"33": 0.095265063,
"49": 0.067413235,
"65": 0,
"9": 0.127562362
},
"9": {
"1": 0.026909699,
"1001": 0.022555855,
"33": 0.056469043,
"49": 0.089100392,
"65": 0.08525314,
"9": 0
}
}
}
```
Section meaning:
1. PeerInfo: NodeIDNameLastSeen
2. Edges: The **Single way latency**9999 or missing means unreachable(UDP hole punching failed)
3. NhTable: Calculate result.
4. Dist: The latency of **packet through Etherguard**
### peeradd
We can add new edges with this API without restart the supernode
Exanple:
```
curl -X POST "http://127.0.0.1:3000/api/peer/add?Password=passwd_addpeer" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "nodeid=100&name=Node_100&pubkey=6SuqwPH9pxGigtZDNp3PABZYfSEzDaBSwuThsUUAcyM="
```
Parameter:
1. URL query: Password: Password. Configured in the config file.
1. Post body:
1. nodeid: Node ID
1. pubkey: Public Key
1. pskey: Pre shared Key
Return value:
1. http code != 200: Error reason
2. http code == 200An example edge config.
* generate by contents in `edgetemplate` with custom data (nodeid/name/pubkey)
* Convenient for users to copy and paste
```yaml
interface:
itype: stdio
name: tap1
vppifaceid: 1
vppbridgeid: 4242
macaddrprefix: AA:BB:CC:DD
mtu: 1416
recvaddr: 127.0.0.1:4001
sendaddr: 127.0.0.1:5001
l2headermode: kbdbg
nodeid: 100
nodename: Node_100
defaultttl: 200
privkey: Your_Private_Key
listenport: 3001
loglevel:
loglevel: normal
logtransit: true
logcontrol: true
lognormal: true
logntp: true
dynamicroute:
sendpinginterval: 16
peeralivetimeout: 30
dupchecktimeout: 40
conntimeout: 30
connnexttry: 5
savenewpeers: true
supernode:
usesupernode: true
pskey: ""
connurlv4: 127.0.0.1:3000
pubkeyv4: LJ8KKacUcIoACTGB/9Ed9w0osrJ3WWeelzpL2u4oUic=
connurlv6: ""
pubkeyv6: HCfL6YJtpJEGHTlJ2LgVXIWKB/K95P57LHTJ42ZG8VI=
apiurl: http://127.0.0.1:3000/api
supernodeinfotimeout: 50
p2p:
usep2p: false
sendpeerinterval: 20
graphrecalculatesetting:
jittertolerance: 20
jittertolerancemultiplier: 1.1
nodereporttimeout: 40
recalculatecooldown: 5
ntpconfig:
usentp: true
maxserveruse: 8
synctimeinterval: 3600
ntptimeout: 3
servers:
- time.google.com
- time1.google.com
- time2.google.com
- time3.google.com
- time4.google.com
- time1.facebook.com
- time2.facebook.com
- time3.facebook.com
- time4.facebook.com
- time5.facebook.com
- time.cloudflare.com
- time.apple.com
- time.asia.apple.com
- time.euro.apple.com
- time.windows.com
nexthoptable: {}
resetconninterval: 86400
peers: []
```
### peerdel
Delete peer
There are two deletion modes, namely password deletion and private key deletion.
Designed to be used by administrators, or for people who join the network and want to leave the network.
Use Password to delete any node. Take the newly added node above as an example, use this API to delete the node
```
curl "http://127.0.0.1:3000/api/peer/del?Password=passwd_delpeer&nodeid=100"
```
We can also use privkey to delete, the same as above, but use privkey parameter only.
```
curl "http://127.0.0.1:3000/api/peer/del?privkey=IJtpnkm9ytbuCukx4VBMENJKuLngo9KSsS1D60BqonQ="
```
Parameter:
1. URL query:
1. Password: Password: Password. Configured in the config file.
1. nodeid: Node ID that you want to delete
1. privkey: The private key of the edge
Return value:
1. http code != 200: Error reason
2. http code == 200: Success message
## Config Parameters
### Super mode of edge node
1. `usesupernode`: Whether to enable Super mode
1. `pskey`: Pre shared Key used to establish connection with supernode
1. `connurlv4`: IPv4 connection address of the Super node
1. `pubkeyv4`: IPv4 key of Super node
1. `connurlv6`: IPv6 connection address of the Super node
1. `pubkeyv6`: IPv6 key of Super node
1. `apiurl`: HTTP(S) API connection address of Super node
1. `supernodeinfotimeout`: Supernode Timeout
### Super node it self
1. nodename: node name
1. privkeyv4: private key for ipv4
1. privkeyv6: private key for ipv6
1. listenport: listen udp port number
1. loglevel: Refer to [README.md](../README.md)
1. repushconfiginterval: re-push interval of `UpdateXXX` messages
1. passwords: HTTP API password
1. showstate: node information
1. addpeer: add peer
1. delpeer: delete peer
1. graphrecalculatesetting: Some parameters related to [Floyd-Warshall algorithm](https://zh.wikipedia.org/zh-tw/Floyd-Warshall algorithm)
1. staticmode: Disable the Floyd-Warshall algorithm and only use the nexthoptable loaded at the beginning.
Supernode is only used to assist hole punching
1. recalculatecooldown: Floyd-Warshal is O(n^3) time complexity algorithm, which cannot be calculated too often. Set a cooling time
1. jittertolerance: jitter tolerance, after receiving Pong, one 37ms and one 39ms will not trigger recalculation
1. jittertolerancemultiplier: the same is the jitter tolerance, but high ping allows more errors
https://www.desmos.com/calculator/raoti16r5n
1. nodereporttimeout: The timeout of the received `Pong` packet. Change back to Infinity after timeout.
1. nexthoptable: only works in `staticmode==true`, set nexthoptable manually
1. edgetemplate: for `addpeer` API. Refer to this configuration file and show a sample configuration file of the edge to the user
1. usepskforinteredge: Whether to enable pre-share key communication between edges. If enabled, supernode will generate PSKs for edges automatically
1. peers: Peer list, refer to [README.md](../README.md)
1. nodeid: Peer's node ID
1. name: Peer name (displayed on the front end)
1. pubkey: peer public key
1. pskey: preshared key The PSK that this peer connects to this Supernode
## V4 V6 Two Keys
Why we split IPv4 and IPv6 into two session?
Because of this situation
![OneChannel](https://raw.githubusercontent.com/KusakabeSi/EtherGuard-VPN/master/example_config/super_mode/EGS04.png)
In this case, SuperNode does not know the external ipv4 address of Node02 and cannot help Node1 and Node2 to UDP hole punch.
![TwoChannel](https://raw.githubusercontent.com/KusakabeSi/EtherGuard-VPN/master/example_config/super_mode/EGS05.png)
So like this, both V4 and V6 establish a session, so that both V4 and V6 can be taken care of at the same time.
## UDP hole punch reachability
For different NAT type, the UDP hole punch reachability can refer this table.([Origin](https://dh2i.com/kbs/kbs-2961448-understanding-different-nat-types-and-hole-punching/))
![reachability between NAT types](https://raw.githubusercontent.com/KusakabeSi/EtherGuard-VPN/master/example_config/super_mode/EGS06.png)
And if both sides are using ConeNAT, it's not gerenteed to punch success. It depends on the topology and the devices attributes.
Like the section 3.5 in [this article](https://bford.info/pub/net/p2pnat/#SECTION00035000000000000000), we can't punch success.
## Notice for Relay node
Unlike n2n, our supernode do not relay any packet for edges.
If the edge punch failed and no any route available, it's just unreachable. In this case we need to setup a relay node.
Relay node is a regular edge in public network, but `interface=dummy`.
And we have to note that **do not** use 127.0.0.1 to connect to supernode.
Because supernode well distribute the source IP of the nodes to all other edges. But 127.0.0.1 is not accessible from other edge.
![Setup relay node](https://raw.githubusercontent.com/KusakabeSi/EtherGuard-VPN/master/example_config/super_mode/EGS07.png)
To avoid this issue, please use the external IP of the supernode in the edge config.
## Quick start
Run this example_config (please open three terminals):
```bash
./etherguard-go -config example_config/super_mode/s1.yaml -mode super
./etherguard-go -config example_config/super_mode/n1.yaml -mode edge
./etherguard-go -config example_config/super_mode/n2.yaml -mode edge
```
Because it is in `stdio` mode, stdin will be read into the VPN network
Please type in one of the edge windows
```
b1aaaaaaaaaa
```
b1 will be converted into a 12byte layer 2 header, b is the broadcast address `FF:FF:FF:FF:FF:FF`, 1 is the ordinary MAC address `AA:BB:CC:DD:EE:01`, aaaaaaaaaa is the payload, and then feed it into the VPN
You should be able to see the string b1aaaaaaaaaa on another window. The first 12 bytes are converted back
## Next: [P2P Mode](../p2p_mode/README.md)

View File

@ -13,27 +13,30 @@ Super Mode是受到[n2n](https://github.com/ntop/n2n)的啟發
藉由supernode交換其他節點的資訊以及udp打洞
由supernode執行[Floyd-Warshall演算法](https://zh.wikipedia.org/zh-tw/Floyd-Warshall算法)並把計算結果分發給全部edge node
在super mode模式下,設定檔裡面的`nexthoptable`以及`peers`是無效的。
edge node的super模式下設定檔裡面的`nexthoptable`以及`peers`是無效的。
這些資訊都是從super node上面下載
同時supernode會幫每個連線生成Preshared Key分發給edge使用。
同時supernode會幫每個連線生成Preshared Key分發給edge使用(如果`usepskforinteredge`有啟用的話)
```golang
psk = shs256("PubkeyPeerA" + "PubkeyPeerB" + "主廚特調當季精選海鹽")[:32]
```
### SuperMsg
但是比起Static modeSuper mode引入了一種新的 `終點ID` 叫做 `SuperMsg`
所有送往Super node的封包都會是這種類型。
這種封包不會在edge node之間傳播收到也會不會轉給任何人如同`終點ID == 自己`一般
### Register
## Control Message
從Super mode開始我們有了Static mode不存在的Control Message。他會控制EtherGuard一些行為
在Super mode下我們不會轉發任何控制消息。 我們只會直接接收或發送給目標。
下面列出Super Mode會出現的Control message
### Register
具體運作方式類似這張圖
![EGS01](https://raw.githubusercontent.com/KusakabeSi/EtherGuard-VPN/master/example_config/super_mode/EGS01.png)
首先edge node發送regiater給super node
super node收到以後就知道這個edge的endpoint IP和埠號。
更新進資料庫以後發布`UpdatePeerMsg`。
其他edge node收到以後就用HTTP API去下載完整的peer list。並且把自己沒有的peer通通加到本地
![Register運作流程](https://raw.githubusercontent.com/KusakabeSi/EtherGuard-VPN/master/example_config/super_mode/EGS01.png)
1. edge node發送`Register`給super node
2. super node收到以後就知道這個edge的endpoint IP和埠號。
3. 更新進資料庫以後發布`UpdatePeerMsg`。
4. 其他edge node收到以後就用HTTP API去下載完整的peer list。並且把自己沒有的peer通通加到本地
### Ping/Pong
有了peer list以後接下來的運作方式類似這張圖
@ -43,13 +46,17 @@ Edge node 會嘗試向其他所有peer發送`Ping`,裡面會攜帶節點自己
收到`Ping`,就會產生一個`Pong`,並攜帶時間差。這個時間就是單向延遲
但是他不會把`Pong`送回給原節點而是送給Super node
### 轉發表
### UpdateNhTable
Super node收到節點們傳來的Pong以後就知道他們的單向延遲了。接下來的運作方式類似這張圖
![image](https://raw.githubusercontent.com/KusakabeSi/EtherGuard-VPN/master/example_config/super_mode/EGS03.png)
Super node收到Pong以後就會更新它裡面的`Distance matrix`,並且重新計算轉發表
如果有變動,就發布`UpdateNhTableMsg`
其他edge node收到以後就用HTTP API去下載完整的轉發表
### UpdateError
通知edges有錯誤發生關閉egde端程式
發生在版本號不匹被該edge的NodeID配置錯誤還有該Edge被刪除時觸發
### HTTP API
為什麼要用HTTP額外下載呢?直接`UpdateXXX`夾帶資訊不好嗎?
因為udp是不可靠協議能攜帶的內容量也有上限。
@ -61,15 +68,18 @@ Super node收到Pong以後就會更新它裡面的`Distance matrix`,並且
這樣super node收到HTTP API看到`state hash`就知道這個edge node確實有收到`UpdateXXX`了。
不然每隔一段時間就會重新發送`UpdateXXX`給該節點
## HTTP Guest API
HTTP還有一些個API給前端使用幫助管理整個網路
### peerstate
HTTP還有三個個API首先是這個peerstate
```
http://127.0.0.1:3000/api/peerstate?Password=passwd
首先是這個peerstate
```bash
curl "http://127.0.0.1:3000/api/peerstate?Password=passwd"
```
可以給前端看的,用來顯示現在各節點之間的單向延遲狀況
之後可以用來畫力導向圖。
這個json下載下來有一個叫做`infinity`的欄位值應該永遠是99999
這個json下載下來有一個叫做`infinity`的欄位值應該永遠是9999
因為json沒辦法表達無限大。所以大於這個數值的就是無限大不可達的意思
這個數值是編譯時決定的,一般不會動。但保留變更的彈性
所以有這個欄位,前端顯示時看到數值大於這個,就視為不可達,不用畫線了
@ -386,14 +396,14 @@ curl "http://127.0.0.1:3000/api/peer/del?privkey=IJtpnkm9ytbuCukx4VBMENJKuLngo9K
1. privkey: 該節點的私鑰
返回值:
1. http code != 200: 被刪除的nodeID
2. http code == 200: 空字串,表示成功
1. http code != 200: 錯誤訊息
2. http code == 200: 被刪除的nodeID
## Config Paramaters
## Config Parameters
### Super mode的edge node有幾個參數
1. `usesupernode`: 是否啟用Super mode
1. `pskey`: 和supernode建立連線用的Preshared Key
1. `pskey`: 和supernode建立連線用的Pre shared Key
1. `connurlv4`: Super node的IPv4連線地址
1. `pubkeyv4`: Super node的IPv4工鑰
1. `connurlv6`: Super node的IPv6連線地址
@ -407,40 +417,28 @@ curl "http://127.0.0.1:3000/api/peer/del?privkey=IJtpnkm9ytbuCukx4VBMENJKuLngo9K
1. privkeyv4: ipv4用的私鑰
1. privkeyv6: ipv6用的私鑰
1. listenport: 監聽udp埠號
1. statepassword: Guest API 的密碼
1. loglevel: 參考 [README_zh.md](../README_zh.md)
1. repushconfiginterval: 重新push`UpdateXXX`的間格
1. passwords: HTTP API 密碼
1. showstate: 節點資訊
1. addpeer: 新增peer
1. delpeer: 刪除peer
1. graphrecalculatesetting:
1. jittertolerance: 抖動容許誤差收到Pong以後一個37ms一個39ms不會觸發重新計算
1. jittertolerancemultiplier: 一樣是抖動容許誤差但是高ping的話允許更多誤差
1. graphrecalculatesetting: 一些和[Floyd-Warshall演算法](https://zh.wikipedia.org/zh-tw/Floyd-Warshall算法)相關的參數
1. staticmode: 關閉Floyd-Warshall演算法只使用一開始載入的nexthoptable。Supernode單純用來輔助打洞
1. recalculatecooldown: Floyd-Warshal是O(n^3)時間複雜度,不能太常算。設個冷卻時間
1. jittertolerance: 抖動容許誤差收到Pong以後一個37ms一個39ms不會觸發重新計算
1. jittertolerancemultiplier: 一樣是抖動容許誤差但是高ping的話允許更多誤差
https://www.desmos.com/calculator/raoti16r5n
1. nodereporttimeout: 收到的`Pong`封包的有效期限。太久沒收到就變回Infinity
1. recalculatecooldown: Floyd-Warshal是O(n^3)時間複雜度,不能太頻繁計算。設個冷卻時間
1. nodereporttimeout: 收到的`Pong`封包的有效期限。太久沒收到就變回Infinity
1. nexthoptable: 僅在`staticmode==true` 有效手動設定的nexthoptable
1. edgetemplate: 給`addpeer`API用的。參考這個設定檔顯示一個範例設定檔給edge
1. usepskforinteredge: 是否啟用edge間pre shares key通信。若啟用則幫edge們自動生成PSK
1. peers: Peer列表參考 [README_zh.md](../README_zh.md)
1. nodeid: Peer的節點ID
1. name: Peer名稱(顯示在前端)
1. pubkey: peer 公鑰
1. pskey: preshared key 該peer和本Supernode連線的PSK
##
執行此範例設定檔(請開三個terminal):
```bash
./etherguard-go -config example_config/super_mode/s1.yaml -mode super
./etherguard-go -config example_config/super_mode/n1.yaml -mode edge
./etherguard-go -config example_config/super_mode/n2.yaml -mode edge
```
因為是stdio模式stdin會讀入VPN網路
請在其中一個edge視窗中鍵入
```
b1aaaaaaaaaa
```
b1會被轉換成 12byte 的layer 2 headerb是廣播地址`FF:FF:FF:FF:FF:FF`1是普通地址`AA:BB:CC:DD:EE:01`aaaaaaaaaa是後面的payload然後再丟入VPN
此時應該要能夠在另一個視窗上看見字串b1aaaaaaaaaa。前12byte被轉換回來了
## V4 V6 兩個公鑰
為什麼要分開IPv4和IPv6呢?
@ -457,7 +455,7 @@ b1會被轉換成 12byte 的layer 2 headerb是廣播地址`FF:FF:FF:FF:FF:FF`
## 打洞可行性
對於不同的NAT type打洞的可行性可以參考這張圖([出處](https://dh2i.com/kbs/kbs-2961448-understanding-different-nat-types-and-hole-punching/))
![EGS06](https://raw.githubusercontent.com/KusakabeSi/EtherGuard-VPN/master/example_config/super_mode/EGS06.png)
![reachability between NAT types](https://raw.githubusercontent.com/KusakabeSi/EtherGuard-VPN/master/example_config/super_mode/EGS06.png)
還有就算雙方都是ConeNAT也不保證100%成功。
還得看NAT設備的支援情況詳見[此文](https://bford.info/pub/net/p2pnat/#SECTION00035000000000000000)裡面3.5章節描述的情況,也無法打洞成功
@ -473,6 +471,19 @@ Relay node其實也是一個edge node只不過被設定成為interface=dummy
因為如果用127.0.0.1連接supernodesupernode看到封包的src IP就是127.0.0.1就會把127.0.0.1分發給`Node_1`和`Node_2`
`Node_1`和`Node_2`看到`Node_R`的連線地址是`127.0.0.1`,就連不上了
## Quick start
執行此範例設定檔(請開三個terminal):
```bash
./etherguard-go -config example_config/super_mode/s1.yaml -mode super
./etherguard-go -config example_config/super_mode/n1.yaml -mode edge
./etherguard-go -config example_config/super_mode/n2.yaml -mode edge
```
因為是stdio模式stdin會讀入VPN網路
請在其中一個edge視窗中鍵入
```
b1aaaaaaaaaa
```
b1會被轉換成 12byte 的layer 2 headerb是廣播地址`FF:FF:FF:FF:FF:FF`1是普通地址`AA:BB:CC:DD:EE:01`aaaaaaaaaa是後面的payload然後再丟入VPN
此時應該要能夠在另一個視窗上看見字串b1aaaaaaaaaa。前12byte被轉換回來了
看完本章捷,接下來你就能了解一下[P2P Mode的運作](../p2p_mode/README_zh.md)

View File

@ -39,6 +39,7 @@ dynamicroute:
usep2p: false
sendpeerinterval: 20
graphrecalculatesetting:
staticmode: false
jittertolerance: 20
jittertolerancemultiplier: 1.1
nodereporttimeout: 40

View File

@ -39,6 +39,7 @@ dynamicroute:
usep2p: false
sendpeerinterval: 20
graphrecalculatesetting:
staticmode: false
jittertolerance: 20
jittertolerancemultiplier: 1.1
nodereporttimeout: 40

View File

@ -14,17 +14,23 @@ passwords:
addpeer: passwd_addpeer
delpeer: passwd_delpeer
graphrecalculatesetting:
staticmode: false
jittertolerance: 5
jittertolerancemultiplier: 1.01
nodereporttimeout: 50
recalculatecooldown: 5
nexthoptable:
1:
2: 2
2:
1: 1
edgetemplate: example_config/super_mode/n1.yaml
peers:
- nodeid: 2
name: Node_02
pubkey: dHeWQtlTPQGy87WdbUARS4CtwVaR2y7IQ1qcX4GKSXk=
pskey: juJMQaGAaeSy8aDsXSKNsPZv/nFiPj4h/1G70tGYygs=
- nodeid: 1
name: Node_01
pubkey: ZqzLVSbXzjppERslwbf2QziWruW3V/UIx9oqwU8Fn3I=
pskey: iPM8FXfnHVzwjguZHRW9bLNY+h7+B1O2oTJtktptQkI=
- nodeid: 2
name: Node_02
pubkey: dHeWQtlTPQGy87WdbUARS4CtwVaR2y7IQ1qcX4GKSXk=
pskey: juJMQaGAaeSy8aDsXSKNsPZv/nFiPj4h/1G70tGYygs=

13
go.mod
View File

@ -3,12 +3,13 @@ module github.com/KusakabeSi/EtherGuardVPN
go 1.16
require (
git.fd.io/govpp.git v0.3.6-0.20210810100027-c0da1f2999a6
git.fd.io/govpp.git/extras v0.0.0-20210810100027-c0da1f2999a6
git.fd.io/govpp.git v0.3.6-0.20210927044411-385ccc0d8ba9
git.fd.io/govpp.git/extras v0.0.0-20210927044411-385ccc0d8ba9
github.com/KusakabeSi/go-cache v0.0.0-20210823132304-22b5b1d22b41
github.com/beevik/ntp v0.3.0
github.com/sirupsen/logrus v1.8.1
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57
gopkg.in/yaml.v2 v2.4.0
github.com/google/gopacket v1.1.19
github.com/sirupsen/logrus v1.6.0
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1
gopkg.in/yaml.v2 v2.2.2
)

View File

@ -47,7 +47,7 @@ var (
tconfig = flag.String("config", "", "Config path for the interface.")
mode = flag.String("mode", "", "Running mode. [super|edge|solve]")
printExample = flag.Bool("example", false, "Print example config")
bind = flag.String("bind", "linux", "UDP socket bind mode. [linux|std]\nYou may need this if tou want to run Etherguard under WSL.")
bind = flag.String("bind", "linux", "UDP socket bind mode. [linux|std]\nYou may need std mode if you want to run Etherguard under WSL.")
nouapi = flag.Bool("no-uapi", false, "Disable UAPI\nWith UAPI, you can check etherguard status by \"wg\" command")
version = flag.Bool("version", false, "Show version")
help = flag.Bool("help", false, "Show this help")

View File

@ -25,20 +25,24 @@ import (
)
func printExampleEdgeConf() {
v1 := config.Vertex(1)
v2 := config.Vertex(2)
tconfig := config.EdgeConfig{
Interface: config.InterfaceConf{
Itype: "stdio",
VPPIfaceID: 5,
Name: "tap1",
MacAddrPrefix: "AA:BB:CC:DD:EE:FF",
MTU: 1400,
VPPIfaceID: 5,
VPPBridgeID: 4242,
MacAddrPrefix: "AA:BB:CC:DD",
MTU: 1416,
RecvAddr: "127.0.0.1:4001",
SendAddr: "127.0.0.1:5001",
L2HeaderMode: "nochg",
},
NodeID: 1,
NodeName: "Node01",
PrivKey: "SM8pGjT0r8njy1/7ffN4wMwF7nnJ8UYSjGRWpCqo3ng=",
DefaultTTL: 200,
PrivKey: "6GyDagZKhbm5WNqMiRHhkf43RlbMJ34IieTlIuvfJ1M=",
ListenPort: 3001,
LogLevel: config.LoggerInfo{
LogLevel: "normal",
@ -52,20 +56,23 @@ func printExampleEdgeConf() {
PeerAliveTimeout: 30,
DupCheckTimeout: 40,
ConnTimeOut: 30,
ConnNextTry: 5,
SaveNewPeers: true,
SuperNode: config.SuperInfo{
UseSuperNode: true,
PSKey: "iPM8FXfnHVzwjguZHRW9bLNY+h7+B1O2oTJtktptQkI=",
ConnURLV4: "127.0.0.1:3000",
PubKeyV4: "LJ8KKacUcIoACTGB/9Ed9w0osrJ3WWeelzpL2u4oUic=",
ConnURLV6: "[::1]:3000",
PubKeyV6: "HCfL6YJtpJEGHTlJ2LgVXIWKB/K95P57LHTJ42ZG8VI=",
APIUrl: "http://127.0.0.1:3000/api",
SuperNodeInfoTimeout: 40,
SuperNodeInfoTimeout: 50,
},
P2P: config.P2Pinfo{
UseP2P: true,
SendPeerInterval: 20,
GraphRecalculateSetting: config.GraphRecalculateSetting{
StaticMode: false,
JitterTolerance: 20,
JitterToleranceMultiplier: 1.1,
NodeReportTimeout: 40,
@ -76,7 +83,7 @@ func printExampleEdgeConf() {
UseNTP: true,
MaxServerUse: 5,
SyncTimeInterval: 3600,
NTPTimeout: 10,
NTPTimeout: 3,
Servers: []string{"time.google.com",
"time1.google.com",
"time2.google.com",
@ -94,18 +101,20 @@ func printExampleEdgeConf() {
"time.windows.com"},
},
},
NextHopTable: config.NextHopTable{},
NextHopTable: config.NextHopTable{
config.Vertex(1): {
config.Vertex(2): &v2,
},
config.Vertex(2): {
config.Vertex(1): &v1,
},
},
ResetConnInterval: 86400,
Peers: []config.PeerInfo{
{
NodeID: 2,
PubKey: "ZqzLVSbXzjppERslwbf2QziWruW3V/UIx9oqwU8Fn3I=",
EndPoint: "127.0.0.1:3001",
Static: true,
},
{
NodeID: 2,
PubKey: "dHeWQtlTPQGy87WdbUARS4CtwVaR2y7IQ1qcX4GKSXk=",
PSKey: "juJMQaGAaeSy8aDsXSKNsPZv/nFiPj4h/1G70tGYygs=",
EndPoint: "127.0.0.1:3002",
Static: true,
},

View File

@ -159,21 +159,25 @@ func get_peerinfo(w http.ResponseWriter, r *http.Request) {
http_PeerInfo_2peer := make(config.API_Peers)
for PeerPubKey, peerinfo := range http_PeerInfo {
h := sha256.New()
if NodeID > peerinfo.NodeID {
h.Write([]byte(PubKey))
h.Write([]byte(PeerPubKey))
} else if NodeID < peerinfo.NodeID {
h.Write([]byte(PeerPubKey))
h.Write([]byte(PubKey))
if http_sconfig.UsePSKForInterEdge {
h := sha256.New()
if NodeID > peerinfo.NodeID {
h.Write([]byte(PubKey))
h.Write([]byte(PeerPubKey))
} else if NodeID < peerinfo.NodeID {
h.Write([]byte(PeerPubKey))
h.Write([]byte(PubKey))
} else {
continue
}
h.Write(http_HashSalt)
bs := h.Sum(nil)
var psk device.NoisePresharedKey
copy(psk[:], bs[:])
peerinfo.PSKey = psk.ToString()
} else {
continue
peerinfo.PSKey = ""
}
h.Write(http_HashSalt)
bs := h.Sum(nil)
var psk device.NoisePresharedKey
copy(psk[:], bs[:])
peerinfo.PSKey = psk.ToString()
http_PeerInfo_2peer[PeerPubKey] = peerinfo
}
api_peerinfo_str_byte, _ := json.Marshal(&http_PeerInfo_2peer)
@ -321,21 +325,48 @@ func peeradd(w http.ResponseWriter, r *http.Request) { //Waiting for test
}
for _, peerinfo := range http_sconfig.Peers {
if peerinfo.NodeID == NodeID {
w.WriteHeader(http.StatusNotFound)
w.WriteHeader(http.StatusConflict)
w.Write([]byte("NodeID exists"))
return
}
if peerinfo.Name == Name {
w.WriteHeader(http.StatusNotFound)
w.WriteHeader(http.StatusConflict)
w.Write([]byte("Node name exists"))
return
}
if peerinfo.PubKey == PubKey {
w.WriteHeader(http.StatusNotFound)
w.WriteHeader(http.StatusConflict)
w.Write([]byte("PubKey exists"))
return
}
}
if http_sconfig.GraphRecalculateSetting.StaticMode == false {
NhTableStr := r.Form.Get("nexthoptable")
if NhTableStr == "" {
w.WriteHeader(http.StatusExpectationFailed)
w.Write([]byte("Your NextHopTable is in static mode.\nPlease provide your new NextHopTable in \"nexthoptable\" parmater in json format"))
return
}
var NewNhTable config.NextHopTable
err := json.Unmarshal([]byte(NhTableStr), &NewNhTable)
if err != nil {
w.WriteHeader(http.StatusExpectationFailed)
w.Write([]byte(fmt.Sprintf("%v", err)))
return
}
err = checkNhTable(NewNhTable, append(http_sconfig.Peers, config.SuperPeerInfo{
NodeID: NodeID,
Name: Name,
PubKey: PubKey,
PSKey: PSKey,
}))
if err != nil {
w.WriteHeader(http.StatusExpectationFailed)
w.Write([]byte(fmt.Sprintf("%v", err)))
return
}
http_graph.SetNHTable(NewNhTable, [32]byte{})
}
super_peeradd(config.SuperPeerInfo{
NodeID: NodeID,
Name: Name,
@ -362,7 +393,7 @@ func peeradd(w http.ResponseWriter, r *http.Request) { //Waiting for test
func peerdel(w http.ResponseWriter, r *http.Request) { //Waiting for test
params := r.URL.Query()
toDelete := config.Boardcast
toDelete := config.Broadcast
PasswordA, has := params["Password"]
PubKey := ""
@ -402,9 +433,8 @@ func peerdel(w http.ResponseWriter, r *http.Request) { //Waiting for test
toDelete = peerinfo.NodeID
}
}
}
if toDelete == config.Boardcast {
if toDelete == config.Broadcast {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("Wrong password"))
return
@ -418,6 +448,7 @@ func peerdel(w http.ResponseWriter, r *http.Request) { //Waiting for test
peers_new = append(peers_new, peerinfo)
}
}
http_sconfig.Peers = peers_new
configbytes, _ := yaml.Marshal(http_sconfig)
ioutil.WriteFile(http_sconfig_path, configbytes, 0644)

View File

@ -30,7 +30,40 @@ import (
yaml "gopkg.in/yaml.v2"
)
func checkNhTable(NhTable config.NextHopTable, peers []config.SuperPeerInfo) error {
allpeer := make(map[config.Vertex]bool, len(peers))
for _, peer1 := range peers {
allpeer[peer1.NodeID] = true
}
for _, peer1 := range peers {
for _, peer2 := range peers {
if peer1.NodeID == peer2.NodeID {
continue
}
id1 := peer1.NodeID
id2 := peer2.NodeID
if dst, has := NhTable[id1]; has {
if next, has2 := dst[id2]; has2 {
if _, hasa := allpeer[*next]; hasa {
} else {
return errors.New(fmt.Sprintf("NextHopTable[%v][%v]=%v which is not in the peer list", id1, id2, next))
}
} else {
return errors.New(fmt.Sprintf("NextHopTable[%v][%v] not found", id1, id2))
}
} else {
return errors.New(fmt.Sprintf("NextHopTable[%v] not found", id1))
}
}
}
return nil
}
func printExampleSuperConf() {
v1 := config.Vertex(1)
v2 := config.Vertex(2)
sconfig := config.SuperConfig{
NodeName: "NodeSuper",
PrivKeyV4: "mL5IW0GuqbjgDeOJuPHBU2iJzBPNKhaNEXbIGwwYWWk=",
@ -42,24 +75,46 @@ func printExampleSuperConf() {
LogControl: true,
},
RePushConfigInterval: 30,
Peers: []config.SuperPeerInfo{
{
NodeID: 2,
Name: "Node02",
PubKey: "NuYJ/3Ght+C4HovFq5Te/BrIazo6zwDJ8Bdu4rQCz0o=",
PSKey: "NuYJ/3Ght+C4HovFq5Te/BrIazo6zwDJ8Bdu4rQCz0o=",
},
Passwords: config.Passwords{
ShowState: "passwd",
AddPeer: "passwd_addpeer",
DelPeer: "passwd_delpeer",
},
GraphRecalculateSetting: config.GraphRecalculateSetting{
StaticMode: false,
JitterTolerance: 5,
JitterToleranceMultiplier: 1.01,
NodeReportTimeout: 40,
RecalculateCoolDown: 5,
},
NextHopTable: config.NextHopTable{
config.Vertex(1): {
config.Vertex(2): &v2,
},
config.Vertex(2): {
config.Vertex(1): &v1,
},
},
EdgeTemplate: "example_config/super_mode/n1.yaml",
UsePSKForInterEdge: true,
Peers: []config.SuperPeerInfo{
{
NodeID: 1,
Name: "Node_01",
PubKey: "ZqzLVSbXzjppERslwbf2QziWruW3V/UIx9oqwU8Fn3I=",
PSKey: "iPM8FXfnHVzwjguZHRW9bLNY+h7+B1O2oTJtktptQkI=",
},
{
NodeID: 2,
Name: "Node_02",
PubKey: "dHeWQtlTPQGy87WdbUARS4CtwVaR2y7IQ1qcX4GKSXk=",
PSKey: "juJMQaGAaeSy8aDsXSKNsPZv/nFiPj4h/1G70tGYygs=",
},
},
}
soprint, _ := yaml.Marshal(sconfig)
fmt.Print(string(soprint))
scprint, _ := yaml.Marshal(sconfig)
fmt.Print(string(scprint))
return
}
@ -120,7 +175,13 @@ func Super(configPath string, useUAPI bool, printExample bool, bindmode string)
Event_server_NhTable_changed: make(chan struct{}, 1<<4),
}
http_graph = path.NewGraph(3, true, sconfig.GraphRecalculateSetting, config.NTPinfo{}, sconfig.LogLevel.LogNTP)
http_graph.SetNHTable(http_sconfig.NextHopTable, [32]byte{})
if sconfig.GraphRecalculateSetting.StaticMode {
err = checkNhTable(http_sconfig.NextHopTable, sconfig.Peers)
if err != nil {
return err
}
}
thetap4, _ := tap.CreateDummyTAP()
http_device4 = device.NewDevice(thetap4, config.SuperNodeMessage, conn.NewDefaultBind(true, false, bindmode), logger4, http_graph, true, configPath, nil, &sconfig, &super_chains, Version)
defer http_device4.Close()
@ -140,7 +201,6 @@ func Super(configPath string, useUAPI bool, printExample bool, bindmode string)
}
if sconfig.PrivKeyV6 != "" {
pk6, err := device.Str2PriKey(sconfig.PrivKeyV6)
if err != nil {
fmt.Println("Error decode base64 ", err)
@ -316,6 +376,7 @@ func Event_server_event_hendler(graph *path.IG, events path.SUPER_Events) {
new_hash_str := hex.EncodeToString(md5_hash_raw[:])
new_hash_str_byte := []byte(new_hash_str)
copy(http_NhTable_Hash[:], new_hash_str_byte)
copy(graph.NhTableHash[:], new_hash_str_byte)
http_NhTableStr = NhTablestr
PushNhTable()
}

View File

@ -21,7 +21,7 @@ const (
MessageCookieReplyType
MessageTransportType
NornalPacket
NormalPacket
Register //Register to server
UpdatePeer //Comes from server
@ -31,7 +31,7 @@ const (
PingPacket //Comes from other peer
PongPacket //Send to everyone, include server
QueryPeer
BoardcastPeer
BroadcastPeer
)
func NewEgHeader(pac []byte) (e EgHeader, err error) {

View File

@ -1,6 +1,7 @@
package path
import (
"bytes"
"fmt"
"io/ioutil"
"math"
@ -43,6 +44,7 @@ type IG struct {
Vert map[config.Vertex]bool
edges map[config.Vertex]map[config.Vertex]Latency
edgelock *sync.RWMutex
StaticMode bool
JitterTolerance float64
JitterToleranceMultiplier float64
NodeReportTimeout time.Duration
@ -69,6 +71,7 @@ func S2TD(secs float64) time.Duration {
func NewGraph(num_node int, IsSuperMode bool, theconfig config.GraphRecalculateSetting, ntpinfo config.NTPinfo, logntp bool) *IG {
g := IG{
edgelock: &sync.RWMutex{},
StaticMode: theconfig.StaticMode,
JitterTolerance: theconfig.JitterTolerance,
JitterToleranceMultiplier: theconfig.JitterToleranceMultiplier,
NodeReportTimeout: S2TD(theconfig.NodeReportTimeout),
@ -112,6 +115,12 @@ func (g *IG) ShouldUpdate(u config.Vertex, v config.Vertex, newval float64) bool
}
func (g *IG) RecalculateNhTable(checkchange bool) (changed bool) {
if g.StaticMode {
if bytes.Equal(g.NhTableHash[:], make([]byte, 32)) {
changed = checkchange
}
return
}
if g.recalculateTime.Add(g.RecalculateCoolDown).Before(time.Now()) {
dist, next := FloydWarshall(g)
changed = false
@ -147,6 +156,7 @@ func (g *IG) RemoveVirt(v config.Vertex, recalculate bool, checkchange bool) (ch
}
}
g.edgelock.Unlock()
g.NhTableHash = [32]byte{}
if recalculate {
changed = g.RecalculateNhTable(checkchange)
}

View File

@ -39,7 +39,7 @@ func Charform2mac(b byte) MacAddress {
if b == 'b' {
return MacAddress{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
}
return MacAddress{0x00, 0x11, 0xff, 0xff, 0xff, b - 48}
return MacAddress{0xaa, 0xbb, 0xcc, 0xdd, 0xee, b - 48}
}
func Mac2charForm(m []byte) byte {
var M MacAddress
@ -51,13 +51,13 @@ func Mac2charForm(m []byte) byte {
}
// New creates and returns a new TUN interface for the application.
func CreateStdIOTAP(iconfig config.InterfaceConf,NodeID config.Vertex) (tapdev Device, err error) {
func CreateStdIOTAP(iconfig config.InterfaceConf, NodeID config.Vertex) (tapdev Device, err error) {
// Setup TUN Config
if err != nil {
fmt.Println(err.Error())
}
macaddr, err := GetMacAddr(iconfig.MacAddrPrefix,uint32(NodeID))
macaddr, err := GetMacAddr(iconfig.MacAddrPrefix, uint32(NodeID))
if err != nil {
fmt.Println("ERROR: Failed parse mac address:", iconfig.MacAddrPrefix)
return nil, err