std bind, http api, hole punching, domain endpoint, faster reaction, psk

This commit is contained in:
KusakabeSi 2021-09-23 11:31:01 +00:00
parent 1fcc1bbfa1
commit 2ac7c0547d
34 changed files with 519 additions and 147 deletions

View File

@ -16,6 +16,9 @@ Here is the solution. This VPN `Etherguard` can collect all the single-way lenta
```bash
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")
-config string
Config path.
-example
@ -26,6 +29,7 @@ Usage of ./etherguard-go:
Running mode. [super|edge|solve]
-no-uapi
Do not use UAPI
With UAPI, you can check etherguard status by `wg` command
-version
Show version
```

View File

@ -20,6 +20,9 @@ OSPF能夠根據cost自動選路
```bash
Usage of ./etherguard-go-vpp:
-bind string
UDP socket bind mode. [linux|std]
You may need this if tou want to run Etherguard under WSL. (default "linux")
-config string
設定檔路徑
-example

View File

@ -99,8 +99,10 @@ func (v *Vertex) ToString() string {
type DynamicRouteInfo struct {
SendPingInterval float64
PeerAliveTimeout float64
DupCheckTimeout float64
ConnTimeOut float64
ConnNextTry float64
SaveNewPeers bool
SuperNode SuperInfo
P2P P2Pinfo
@ -117,6 +119,7 @@ type NTPinfo struct {
type SuperInfo struct {
UseSuperNode bool
PSKey string
ConnURLV4 string
PubKeyV4 string
ConnURLV6 string
@ -128,7 +131,6 @@ type SuperInfo struct {
type P2Pinfo struct {
UseP2P bool
SendPeerInterval float64
PeerAliveTimeout float64
GraphRecalculateSetting GraphRecalculateSetting
}
@ -144,9 +146,8 @@ type NextHopTable map[Vertex]map[Vertex]*Vertex
type API_Peerinfo struct {
NodeID Vertex
PubKey string
PSKey string
Connurl map[string]bool
Connurl map[string]int
}
type API_Peers map[string]API_Peerinfo // map[PubKey]API_Peerinfo

View File

@ -65,11 +65,17 @@ type LinuxSocketBind struct {
}
func NewLinuxSocketBind() Bind { return &LinuxSocketBind{sock4: -1, sock6: -1, use4: true, use6: true} }
func NewDefaultBind() Bind { return NewLinuxSocketBind() }
func NewCustomBind(use4 bool, use6 bool) Bind {
func NewLinuxSocketBindAf(use4 bool, use6 bool) Bind {
return &LinuxSocketBind{sock4: -1, sock6: -1, use4: use4, use6: use6}
}
func NewDefaultBind(use4 bool, use6 bool, bindmode string) Bind {
if bindmode == "std" {
return NewStdNetBindAf(use4, use6)
}
return NewLinuxSocketBindAf(use4, use6)
}
var _ Endpoint = (*LinuxSocketEndpoint)(nil)
var _ Bind = (*LinuxSocketBind)(nil)

View File

@ -22,9 +22,14 @@ type StdNetBind struct {
ipv6 *net.UDPConn
blackhole4 bool
blackhole6 bool
use4 bool
use6 bool
}
func NewStdNetBind() Bind { return &StdNetBind{} }
func NewStdNetBind() Bind { return &StdNetBind{use4: true, use6: true} }
func NewStdNetBindAf(use4 bool, use6 bool) Bind {
return &StdNetBind{use4: use4, use6: use6}
}
type StdNetEndpoint net.UDPAddr
@ -100,21 +105,31 @@ again:
port := int(uport)
var ipv4, ipv6 *net.UDPConn
ipv4, port, err = listenNet("udp4", port)
if err != nil && !errors.Is(err, syscall.EAFNOSUPPORT) {
return nil, 0, err
if bind.use4 {
ipv4, port, err = listenNet("udp4", port)
if uport == 0 && errors.Is(err, syscall.EADDRINUSE) && tries < 100 {
ipv6.Close()
tries++
goto again
}
if err != nil && !errors.Is(err, syscall.EAFNOSUPPORT) {
ipv6.Close()
return nil, 0, err
}
}
// Listen on the same port as we're using for ipv4.
ipv6, port, err = listenNet("udp6", port)
if uport == 0 && errors.Is(err, syscall.EADDRINUSE) && tries < 100 {
ipv4.Close()
tries++
goto again
}
if err != nil && !errors.Is(err, syscall.EAFNOSUPPORT) {
ipv4.Close()
return nil, 0, err
if bind.use6 {
// Listen on the same port as we're using for ipv4.
ipv6, port, err = listenNet("udp6", port)
if uport == 0 && errors.Is(err, syscall.EADDRINUSE) && tries < 100 {
ipv4.Close()
tries++
goto again
}
if err != nil && !errors.Is(err, syscall.EAFNOSUPPORT) {
ipv4.Close()
return nil, 0, err
}
}
var fns []ReceiveFunc
if ipv4 != nil {

View File

@ -149,3 +149,28 @@ func parseEndpoint(s string) (*net.UDPAddr, error) {
}
return addr, err
}
func LookupIP(host_port string, af int) (string, error) {
network := "udp"
if af == 4 {
network = "udp4"
} else if af == 6 {
network = "udp6"
}
conn, err := net.Dial(network, host_port)
if err != nil {
return "", err
}
defer conn.Close()
return conn.RemoteAddr().String(), nil
}
func ValidIP(ip net.IP) bool {
for b := range ip {
if b != 0 {
return true
}
}
return false
}

View File

@ -9,6 +9,7 @@ import (
"bytes"
"encoding/base64"
"errors"
"net"
"runtime"
"sync"
"sync/atomic"
@ -69,6 +70,8 @@ type Device struct {
IDMap map[config.Vertex]*Peer
SuperPeer map[NoisePublicKey]*Peer
Peer_state [32]byte
LocalV4 net.IP
LocalV6 net.IP
}
event_tryendpoint chan struct{}
ResetConnInterval float64

View File

@ -11,6 +11,7 @@ import (
"errors"
"fmt"
"io/ioutil"
"net"
"sync"
"sync/atomic"
"time"
@ -36,6 +37,7 @@ type Peer struct {
AskedForNeighbor bool
StaticConn bool //if true, this peer will not write to config file when roaming, and the endpoint will be reset periodically
ConnURL string
ConnAF int //0: both, 4: ipv4 only, 6: ipv6 only
// These fields are accessed with atomic operations, which must be
// 64-bit aligned even on 32-bit platforms. Go guarantees that an
@ -76,20 +78,21 @@ type Peer struct {
persistentKeepaliveInterval uint32 // accessed atomically
}
func (device *Device) NewPeer(pk NoisePublicKey, id config.Vertex, isSpecial bool) (*Peer, error) {
if isSpecial {
if id >= config.Special_NodeID {
//pass check
} else {
return nil, errors.New(fmt.Sprint("ID", uint32(id), "is not a special NodeID"))
}
} else {
func (device *Device) NewPeer(pk NoisePublicKey, id config.Vertex, isSuper bool) (*Peer, error) {
if isSuper == false {
if id < config.Special_NodeID {
//pass check
} else {
return nil, errors.New(fmt.Sprint("ID ", uint32(id), " is a special NodeID"))
}
} else {
if id == config.SuperNodeMessage {
//pass check
} else {
return nil, errors.New(fmt.Sprint("ID", uint32(id), "is not a supernode NodeID"))
}
}
if device.isClosed() {
return nil, errors.New("device closed")
}
@ -311,16 +314,53 @@ func (peer *Peer) Stop() {
}
func (peer *Peer) SetPSK(psk NoisePresharedKey) {
if peer.device.IsSuperNode == false && peer.ID < config.Special_NodeID && peer.device.DRoute.P2P.UseP2P == true {
peer.device.log.Verbosef("Preshared keys disabled in P2P mode.")
return
}
peer.handshake.mutex.Lock()
peer.handshake.presharedKey = psk
peer.handshake.mutex.Unlock()
}
func (peer *Peer) SetEndpointFromConnURL(connurl string, af int, static bool) error {
peer.StaticConn = static
peer.ConnURL = connurl
peer.ConnAF = af
var err error
connurl, err = conn.LookupIP(connurl, af)
if err != nil {
return err
}
endpoint, err := peer.device.net.bind.ParseEndpoint(connurl)
if err != nil {
return err
}
peer.StaticConn = static
peer.ConnURL = connurl
peer.SetEndpointFromPacket(endpoint)
return nil
}
func (peer *Peer) SetEndpointFromPacket(endpoint conn.Endpoint) {
if peer.disableRoaming {
return
}
peer.Lock()
if peer.ID == config.SuperNodeMessage {
conn, err := net.Dial("udp", endpoint.DstToString())
defer conn.Close()
if err == nil {
IP := conn.LocalAddr().(*net.UDPAddr).IP
if ip4 := IP.To4(); ip4 != nil {
peer.device.peers.LocalV4 = ip4
} else {
peer.device.peers.LocalV6 = IP
}
}
}
peer.device.SaveToConfig(peer, endpoint)
peer.endpoint = endpoint
peer.Unlock()

View File

@ -7,12 +7,14 @@ import (
"fmt"
"hash/crc32"
"io/ioutil"
"net"
"net/http"
"net/url"
"strconv"
"time"
"github.com/KusakabeSi/EtherGuardVPN/config"
orderedmap "github.com/KusakabeSi/EtherGuardVPN/orderdmap"
"github.com/KusakabeSi/EtherGuardVPN/path"
)
@ -253,6 +255,9 @@ func (device *Device) server_process_Pong(content path.PongMsg) error {
func (device *Device) process_ping(peer *Peer, content path.PingMsg) error {
peer.LastPingReceived = time.Now()
peer.Lock()
//peer.endpoint_trylist.
peer.Unlock()
PongMSG := path.PongMsg{
Src_nodeID: content.Src_nodeID,
Dst_nodeID: device.ID,
@ -279,9 +284,18 @@ func (device *Device) process_ping(peer *Peer, content path.PingMsg) error {
header.SetDst(config.ControlMessage)
device.SpreadPacket(make(map[config.Vertex]bool), path.PongPacket, buf, MessageTransportOffsetContent)
}
go device.SendPing(peer, content.RequestReply, 0, 3)
return nil
}
func (device *Device) SendPing(peer *Peer, times int, replies int, interval float32) {
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)))
}
}
func (device *Device) process_pong(peer *Peer, content path.PongMsg) error {
if device.DRoute.P2P.UseP2P {
if time.Now().After(device.graph.NhTableExpire) {
@ -324,7 +338,7 @@ func (device *Device) process_UpdatePeerMsg(peer *Peer, content path.UpdatePeerM
return nil
}
downloadurl := device.DRoute.SuperNode.APIUrl + "/peerinfo?PubKey=" + url.QueryEscape(device.staticIdentity.publicKey.ToString()) + "&State=" + url.QueryEscape(string(content.State_hash[:]))
downloadurl := device.DRoute.SuperNode.APIUrl + "/peerinfo?NodeID=" + strconv.Itoa(int(device.ID)) + "&PubKey=" + url.QueryEscape(device.staticIdentity.publicKey.ToString()) + "&State=" + url.QueryEscape(string(content.State_hash[:]))
if device.LogLevel.LogControl {
fmt.Println("Control: Download peerinfo from :" + downloadurl)
}
@ -365,14 +379,13 @@ func (device *Device) process_UpdatePeerMsg(peer *Peer, content path.UpdatePeerM
device.RemovePeer(pk)
continue
}
}
for PubKey, peerinfo := range peer_infos {
if len(peerinfo.Connurl) == 0 {
return nil
}
sk, err := Str2PubKey(peerinfo.PubKey)
sk, err := Str2PubKey(PubKey)
if err != nil {
device.log.Errorf("Error decode base64:", err)
return err
@ -393,17 +406,17 @@ func (device *Device) process_UpdatePeerMsg(peer *Peer, content path.UpdatePeerM
}
device.NewPeer(sk, peerinfo.NodeID, false)
thepeer = device.LookupPeer(sk)
if peerinfo.PSKey != "" {
pk, err := Str2PSKey(peerinfo.PSKey)
if err != nil {
device.log.Errorf("Error decode base64:", err)
return err
}
thepeer.handshake.presharedKey = pk
}
if peerinfo.PSKey != "" {
pk, err := Str2PSKey(peerinfo.PSKey)
if err != nil {
device.log.Errorf("Error decode base64:", err)
return err
}
thepeer.SetPSK(pk)
}
if thepeer.LastPingReceived.Add(path.S2TD(device.DRoute.P2P.PeerAliveTimeout)).Before(time.Now()) {
if thepeer.LastPingReceived.Add(path.S2TD(device.DRoute.PeerAliveTimeout)).Before(time.Now()) {
//Peer died, try to switch to this new endpoint
for url, _ := range peerinfo.Connurl {
thepeer.Lock()
@ -440,7 +453,7 @@ func (device *Device) process_UpdateNhTableMsg(peer *Peer, content path.UpdateNh
if bytes.Equal(device.graph.NhTableHash[:], content.State_hash[:]) {
return nil
}
downloadurl := device.DRoute.SuperNode.APIUrl + "/nhtable?PubKey=" + url.QueryEscape(device.staticIdentity.publicKey.ToString()) + "&State=" + url.QueryEscape(string(content.State_hash[:]))
downloadurl := device.DRoute.SuperNode.APIUrl + "/nhtable?NodeID=" + strconv.Itoa(int(device.ID)) + "&PubKey=" + url.QueryEscape(device.staticIdentity.publicKey.ToString()) + "&State=" + url.QueryEscape(string(content.State_hash[:]))
if device.LogLevel.LogControl {
fmt.Println("Control: Download NhTable from :" + downloadurl)
}
@ -477,7 +490,7 @@ func (device *Device) process_UpdateErrorMsg(peer *Peer, content path.UpdateErro
}
return nil
}
device.log.Errorf(content.ToString())
device.log.Errorf(strconv.Itoa(content.ErrorCode) + ": " + content.ErrorMsg)
if content.Action == path.Shutdown {
device.closed <- struct{}{}
} else if content.Action == path.Panic {
@ -494,7 +507,7 @@ func (device *Device) RoutineSetEndpoint() {
NextRun := false
<-device.event_tryendpoint
for _, thepeer := range device.peers.IDMap {
if thepeer.LastPingReceived.Add(path.S2TD(device.DRoute.P2P.PeerAliveTimeout)).After(time.Now()) {
if thepeer.LastPingReceived.Add(path.S2TD(device.DRoute.PeerAliveTimeout)).After(time.Now()) {
//Peer alives
thepeer.Lock()
for _, key := range thepeer.endpoint_trylist.Keys() { // delete whole endpoint_trylist
@ -503,6 +516,9 @@ func (device *Device) RoutineSetEndpoint() {
thepeer.Unlock()
} else {
thepeer.RLock()
thepeer.endpoint_trylist.Sort(func(a *orderedmap.Pair, b *orderedmap.Pair) bool {
return a.Value().(time.Time).Before(b.Value().(time.Time))
})
trylist := thepeer.endpoint_trylist.Keys()
thepeer.RUnlock()
for _, key := range trylist { // try next endpoint
@ -519,25 +535,23 @@ func (device *Device) RoutineSetEndpoint() {
thepeer.endpoint_trylist.Delete(key)
thepeer.Unlock()
} else {
endpoint, err := device.Bind().ParseEndpoint(connurl) //trying to bind first url in the list and wait device.DRoute.P2P.PeerAliveTimeout seconds
if device.LogLevel.LogControl {
fmt.Println("Control: Set endpoint to " + connurl + " for NodeID:" + thepeer.ID.ToString())
}
err := thepeer.SetEndpointFromConnURL(connurl, thepeer.ConnAF, thepeer.StaticConn) //trying to bind first url in the list and wait device.DRoute.P2P.PeerAliveTimeout seconds
if err != nil {
device.log.Errorf("Can't bind " + connurl)
device.log.Errorf("Bind " + connurl + " failed!")
thepeer.Lock()
thepeer.endpoint_trylist.Delete(connurl)
thepeer.Unlock()
continue
}
if device.LogLevel.LogControl {
fmt.Println("Control: Set endpoint to " + endpoint.DstToString() + " for NodeID:" + thepeer.ID.ToString())
}
thepeer.SetEndpointFromPacket(endpoint)
NextRun = true
thepeer.Lock()
thepeer.endpoint_trylist.Set(key, time.Now())
thepeer.Unlock()
//Send Ping message to it
packet, usage, err := device.GeneratePingPacket(device.ID)
device.SendPacket(thepeer, usage, packet, MessageTransportOffsetContent)
go device.SendPing(thepeer, int(device.DRoute.ConnNextTry+1), 1, 1)
break
}
}
@ -551,7 +565,7 @@ func (device *Device) RoutineSetEndpoint() {
break ClearChanLoop
}
}
time.Sleep(path.S2TD(device.DRoute.P2P.PeerAliveTimeout))
time.Sleep(path.S2TD(device.DRoute.ConnNextTry))
if NextRun {
device.event_tryendpoint <- struct{}{}
}
@ -563,7 +577,7 @@ func (device *Device) RoutineSendPing() {
return
}
for {
packet, usage, _ := device.GeneratePingPacket(device.ID)
packet, usage, _ := device.GeneratePingPacket(device.ID, 0)
device.SpreadPacket(make(map[config.Vertex]bool), usage, packet, MessageTransportOffsetContent)
time.Sleep(path.S2TD(device.DRoute.SendPingInterval))
}
@ -575,11 +589,20 @@ func (device *Device) RoutineRegister() {
}
_ = <-device.Event_Supernode_OK
for {
body, _ := path.GetByte(path.RegisterMsg{
Node_id: device.ID,
PeerStateHash: device.peers.Peer_state,
NhStateHash: device.graph.NhTableHash,
Version: device.Version,
LocalV4: net.UDPAddr{
IP: device.peers.LocalV4,
Port: int(device.net.port),
},
LocalV6: net.UDPAddr{
IP: device.peers.LocalV6,
Port: int(device.net.port),
},
})
buf := make([]byte, path.EgHeaderLen+len(body))
header, _ := path.NewEgHeader(buf[0:path.EgHeaderLen])
@ -633,27 +656,27 @@ func (device *Device) RoutineResetConn() {
}
for {
for _, peer := range device.peers.keyMap {
if peer.StaticConn {
if !peer.StaticConn { //Do not reset connecton for dynamic peer
continue
}
if peer.ConnURL == "" {
continue
}
endpoint, err := device.Bind().ParseEndpoint(peer.ConnURL)
err := peer.SetEndpointFromConnURL(peer.ConnURL, peer.ConnAF, peer.StaticConn)
if err != nil {
device.log.Errorf("Failed to bind "+peer.ConnURL, err)
continue
}
peer.SetEndpointFromPacket(endpoint)
}
time.Sleep(time.Duration(device.ResetConnInterval))
time.Sleep(path.S2TD(device.ResetConnInterval))
}
}
func (device *Device) GeneratePingPacket(src_nodeID config.Vertex) ([]byte, path.Usage, error) {
func (device *Device) GeneratePingPacket(src_nodeID config.Vertex, request_reply int) ([]byte, path.Usage, error) {
body, err := path.GetByte(&path.PingMsg{
Src_nodeID: src_nodeID,
Time: device.graph.GetCurrentTime(),
Src_nodeID: src_nodeID,
Time: device.graph.GetCurrentTime(),
RequestReply: request_reply,
})
if err != nil {
return nil, path.PingPacket, err
@ -686,7 +709,6 @@ func (device *Device) process_RequestPeerMsg(content path.QueryPeerMsg) error {
Request_ID: content.Request_ID,
NodeID: peer.ID,
PubKey: pubkey,
PSKey: peer.handshake.presharedKey,
ConnURL: peer.endpoint.DstToString(),
}
peer.handshake.mutex.RUnlock()
@ -731,12 +753,8 @@ func (device *Device) process_BoardcastPeerMsg(peer *Peer, content path.Boardcas
device.graph.UpdateLentancy(content.NodeID, device.ID, path.S2TD(path.Infinity), true, false)
}
device.NewPeer(pk, content.NodeID, false)
thepeer = device.LookupPeer(pk)
var pk NoisePresharedKey
copy(pk[:], content.PSKey[:])
thepeer.handshake.presharedKey = pk
}
if thepeer.LastPingReceived.Add(path.S2TD(device.DRoute.P2P.PeerAliveTimeout)).Before(time.Now()) {
if thepeer.LastPingReceived.Add(path.S2TD(device.DRoute.PeerAliveTimeout)).Before(time.Now()) {
//Peer died, try to switch to this new endpoint
thepeer.Lock()
thepeer.endpoint_trylist.Set(content.ConnURL, time.Time{}) //another gorouting will process it

View File

@ -68,4 +68,10 @@ P2P模式也有幾個參數
1. nodereporttimeout: 收到的`Pong`封包的有效期限。太久沒收到就變回Infinity
1. recalculatecooldown: Floyd-Warshal是O(n^3)時間複雜度,不能太常算。設個冷卻時間
## Note
P2P模式下PSK是禁用的。因為n個節點有n(n-1)/2的連線每個連線都要使用不同PSK
又不像static mode提前設好peer數固定不再變動
也不像super mode有中心伺服器統一分發
每對peer要協商出一個PSK有難度因此我設定禁用PSK了只用wireguard原本的加密系統
**最後P2P模式我還沒有大規模測試過穩定性不知如何。PR is welecome**

View File

@ -21,8 +21,10 @@ loglevel:
logntp: true
dynamicroute:
sendpinginterval: 20
peeralivetimeout: 30
dupchecktimeout: 40
conntimeout: 30
connnexttry: 5
savenewpeers: false
supernode:
usesupernode: false
@ -35,7 +37,6 @@ dynamicroute:
p2p:
usep2p: true
sendpeerinterval: 20
peeralivetimeout: 30
graphrecalculatesetting:
jittertolerance: 20
jittertolerancemultiplier: 1.1

View File

@ -21,8 +21,10 @@ loglevel:
logntp: true
dynamicroute:
sendpinginterval: 20
peeralivetimeout: 30
dupchecktimeout: 40
conntimeout: 30
connnexttry: 5
savenewpeers: false
supernode:
usesupernode: false
@ -35,7 +37,6 @@ dynamicroute:
p2p:
usep2p: true
sendpeerinterval: 20
peeralivetimeout: 30
graphrecalculatesetting:
jittertolerance: 20
jittertolerancemultiplier: 1.1

View File

@ -21,8 +21,10 @@ loglevel:
logntp: true
dynamicroute:
sendpinginterval: 20
peeralivetimeout: 30
dupchecktimeout: 40
conntimeout: 30
connnexttry: 5
savenewpeers: false
supernode:
usesupernode: false
@ -35,7 +37,6 @@ dynamicroute:
p2p:
usep2p: true
sendpeerinterval: 20
peeralivetimeout: 30
graphrecalculatesetting:
jittertolerance: 20
jittertolerancemultiplier: 1.1

View File

@ -21,8 +21,10 @@ loglevel:
logntp: true
dynamicroute:
sendpinginterval: 20
peeralivetimeout: 30
dupchecktimeout: 40
conntimeout: 30
connnexttry: 5
savenewpeers: false
supernode:
usesupernode: false
@ -35,7 +37,6 @@ dynamicroute:
p2p:
usep2p: true
sendpeerinterval: 20
peeralivetimeout: 30
graphrecalculatesetting:
jittertolerance: 20
jittertolerancemultiplier: 1.1

View File

@ -21,8 +21,10 @@ loglevel:
logntp: true
dynamicroute:
sendpinginterval: 20
peeralivetimeout: 30
dupchecktimeout: 40
conntimeout: 30
connnexttry: 5
savenewpeers: false
supernode:
usesupernode: false
@ -35,7 +37,6 @@ dynamicroute:
p2p:
usep2p: true
sendpeerinterval: 20
peeralivetimeout: 30
graphrecalculatesetting:
jittertolerance: 20
jittertolerancemultiplier: 1.1

View File

@ -21,8 +21,10 @@ loglevel:
logntp: true
dynamicroute:
sendpinginterval: 20
peeralivetimeout: 30
dupchecktimeout: 40
conntimeout: 30
connnexttry: 5
savenewpeers: false
supernode:
usesupernode: false
@ -35,7 +37,6 @@ dynamicroute:
p2p:
usep2p: true
sendpeerinterval: 20
peeralivetimeout: 30
graphrecalculatesetting:
jittertolerance: 20
jittertolerancemultiplier: 1.1

View File

@ -21,8 +21,10 @@ loglevel:
logntp: true
dynamicroute:
sendpinginterval: 20
peeralivetimeout: 30
dupchecktimeout: 40
conntimeout: 30
connnexttry: 5
savenewpeers: false
supernode:
usesupernode: false
@ -35,7 +37,6 @@ dynamicroute:
p2p:
usep2p: false
sendpeerinterval: 20
peeralivetimeout: 30
graphrecalculatesetting:
jittertolerance: 20
jittertolerancemultiplier: 1.1

View File

@ -21,8 +21,10 @@ loglevel:
logntp: true
dynamicroute:
sendpinginterval: 20
peeralivetimeout: 30
dupchecktimeout: 40
conntimeout: 30
connnexttry: 5
savenewpeers: false
supernode:
usesupernode: false
@ -35,7 +37,6 @@ dynamicroute:
p2p:
usep2p: false
sendpeerinterval: 20
peeralivetimeout: 30
graphrecalculatesetting:
jittertolerance: 20
jittertolerancemultiplier: 1.1

View File

@ -21,8 +21,10 @@ loglevel:
logntp: true
dynamicroute:
sendpinginterval: 20
peeralivetimeout: 30
dupchecktimeout: 40
conntimeout: 30
connnexttry: 5
savenewpeers: false
supernode:
usesupernode: false
@ -35,7 +37,6 @@ dynamicroute:
p2p:
usep2p: false
sendpeerinterval: 20
peeralivetimeout: 30
graphrecalculatesetting:
jittertolerance: 20
jittertolerancemultiplier: 1.1

View File

@ -21,8 +21,10 @@ loglevel:
logntp: true
dynamicroute:
sendpinginterval: 20
peeralivetimeout: 30
dupchecktimeout: 40
conntimeout: 30
connnexttry: 5
savenewpeers: false
supernode:
usesupernode: false
@ -35,7 +37,6 @@ dynamicroute:
p2p:
usep2p: false
sendpeerinterval: 20
peeralivetimeout: 30
graphrecalculatesetting:
jittertolerance: 20
jittertolerancemultiplier: 1.1

View File

@ -21,8 +21,10 @@ loglevel:
logntp: true
dynamicroute:
sendpinginterval: 20
peeralivetimeout: 30
dupchecktimeout: 40
conntimeout: 30
connnexttry: 5
savenewpeers: false
supernode:
usesupernode: false
@ -35,7 +37,6 @@ dynamicroute:
p2p:
usep2p: false
sendpeerinterval: 20
peeralivetimeout: 30
graphrecalculatesetting:
jittertolerance: 20
jittertolerancemultiplier: 1.1

View File

@ -21,8 +21,10 @@ loglevel:
logntp: true
dynamicroute:
sendpinginterval: 20
peeralivetimeout: 30
dupchecktimeout: 40
conntimeout: 30
connnexttry: 5
savenewpeers: false
supernode:
usesupernode: false
@ -35,7 +37,6 @@ dynamicroute:
p2p:
usep2p: false
sendpeerinterval: 20
peeralivetimeout: 30
graphrecalculatesetting:
jittertolerance: 20
jittertolerancemultiplier: 1.1

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -14,8 +14,11 @@ Super Mode是受到[n2n](https://github.com/ntop/n2n)的啟發
由supernode執行[Floyd-Warshall演算法](https://zh.wikipedia.org/zh-tw/Floyd-Warshall算法)並把計算結果分發給全部edge node
在super mode模式下設定檔裡面的`nexthoptable`以及`peers`是無效的。
這些資訊都是從super node上面下載
這些資訊都是從super node上面下載
同時supernode會幫每個連線生成Preshared Key分發給edge使用。
```golang
psk = shs256("PubkeyPeerA" + "PubkeyPeerB" + "主廚特調當季精選海鹽")[:32]
```
### SuperMsg
@ -58,9 +61,11 @@ Super node收到Pong以後就會更新它裡面的`Distance matrix`,並且
這樣super node收到HTTP API看到`state hash`就知道這個edge node確實有收到`UpdateXXX`了。
不然每隔一段時間就會重新發送`UpdateXXX`給該節點
### Guest API
### peerstate
HTTP還有一個API
`http://127.0.0.1:3000/api/peerstate?Password=passwd`
```
http://127.0.0.1:3000/api/peerstate?Password=passwd
```
可以給前端看的,用來顯示現在各節點之間的單向延遲狀況
之後可以用來畫力導向圖。
@ -69,12 +74,35 @@ HTTP還有一個API
這個數值是編譯時決定的一般不會動。但說不定你想改code改成999呢?
所以有這個欄位,前端顯示時看到數值大於這個,就視為不可達,不用畫線了
接下來你就能了解一下[P2P Mode的運作](../p2p_mode/README_zh.md)
### peeradd
新增peer
範例:
```
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=1&name=Node_01&pubkey=ZqzLVSbXzjppERslwbf2QziWruW3V/UIx9oqwU8Fn3I=&pskey=iPM8FXfnHVzwjguZHRW9bLNY%2bh7%2bB1O2oTJtktptQkI="
```
參數:
1. URL query: Password: 新增peer用的密碼
1. Post body:
1. nodeid: Node ID
1. pubkey: Public Key
1. pskey: Preshared Key
### peerdel
刪除peer
範例:
```
curl "http://127.0.0.1:3000/api/peer/del?Password=passwd_delpeer&nodeid=1"
```
## Config Paramaters
### Super mode的edge node有幾個參數
1. `usesupernode`: 是否啟用Super mode
1. `pskey`: 和supernode建立連線用的Preshared Key
1. `connurlv4`: Super node的IPv4連線地址
1. `pubkeyv4`: Super node的IPv4工鑰
1. `connurlv6`: Super node的IPv6連線地址
@ -91,17 +119,37 @@ HTTP還有一個API
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. 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. recalculatecooldown: Floyd-Warshal是O(n^3)時間複雜度,不能太頻繁計算。設個冷卻時間
1. edgetemplate: 給`addpeer`API用的。參考這個設定檔顯示一個範例設定檔給edge
1. peers: Peer列表參考 [README_zh.md](../README_zh.md)
1. nodeid: Peer的節點ID
1. name: Peer名稱(顯示在前端)
1. pubkey: peer 公鑰
1. pskey: preshared key
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呢?
@ -113,4 +161,27 @@ HTTP還有一個API
![TwoChannel](https://raw.githubusercontent.com/KusakabeSi/EtherGuard-VPN/master/example_config/super_mode/EGS05.png)
所以要像這樣V4和V6都建立一條通道才能讓V4和V6同時都被處理到
所以要像這樣V4和V6都建立一條通道才能讓V4和V6同時都被處理到
## 打洞可行性
對於不同的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)
還有就算雙方都是ConeNAT也不保證100%成功。
還得看NAT設備的支援情況詳見[此文](https://bford.info/pub/net/p2pnat/)裡面3.5章節描述的情況,也無法打洞成功
## Relay node
因為Etherguard的Supernode單純只負責幫忙打洞+計算[Floyd-Warshall](https://zh.wikipedia.org/zh-tw/Floyd-Warshall算法),並分發運算結果
而他本身並不參與資料轉發。因此如上章節描述打洞失敗且沒有任何可達路徑的話就需要搭建relay node
基本上任意一個節點有公網ip就不用擔心沒有路徑可達了。但是還是說明一下
Relay node其實也是一個edge node只不過被設定成為interface=dummy不串接任何真實接口
![EGS07](https://raw.githubusercontent.com/KusakabeSi/EtherGuard-VPN/master/example_config/super_mode/EGS07.png)
只是在設定時要注意Supernode地只要設定成Supernode的**外網ip**。
因為如果用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`,就連不上了
看完本章捷,接下來你就能了解一下[P2P Mode的運作](../p2p_mode/README_zh.md)

View File

@ -21,21 +21,23 @@ loglevel:
logntp: true
dynamicroute:
sendpinginterval: 16
peeralivetimeout: 30
dupchecktimeout: 40
conntimeout: 30
connnexttry: 5
savenewpeers: true
supernode:
usesupernode: true
pskey: 'iPM8FXfnHVzwjguZHRW9bLNY+h7+B1O2oTJtktptQkI='
connurlv4: 127.0.0.1:3000
pubkeyv4: LJ8KKacUcIoACTGB/9Ed9w0osrJ3WWeelzpL2u4oUic=
connurlv6: '[::1]:3000'
connurlv6: ''
pubkeyv6: HCfL6YJtpJEGHTlJ2LgVXIWKB/K95P57LHTJ42ZG8VI=
apiurl: http://127.0.0.1:3000/api
supernodeinfotimeout: 50
p2p:
usep2p: false
sendpeerinterval: 20
peeralivetimeout: 30
graphrecalculatesetting:
jittertolerance: 20
jittertolerancemultiplier: 1.1
@ -45,7 +47,7 @@ dynamicroute:
usentp: true
maxserveruse: 8
synctimeinterval: 3600
ntptimeout: 10
ntptimeout: 3
servers:
- time.google.com
- time1.google.com

View File

@ -21,21 +21,23 @@ loglevel:
logntp: true
dynamicroute:
sendpinginterval: 16
peeralivetimeout: 30
dupchecktimeout: 40
conntimeout: 30
connnexttry: 5
savenewpeers: true
supernode:
usesupernode: true
pskey: 'juJMQaGAaeSy8aDsXSKNsPZv/nFiPj4h/1G70tGYygs='
connurlv4: 127.0.0.1:3000
pubkeyv4: LJ8KKacUcIoACTGB/9Ed9w0osrJ3WWeelzpL2u4oUic=
connurlv6: '[::1]:3000'
connurlv6: ''
pubkeyv6: HCfL6YJtpJEGHTlJ2LgVXIWKB/K95P57LHTJ42ZG8VI=
apiurl: http://127.0.0.1:3000/api
supernodeinfotimeout: 50
p2p:
usep2p: false
sendpeerinterval: 20
peeralivetimeout: 30
graphrecalculatesetting:
jittertolerance: 20
jittertolerancemultiplier: 1.1
@ -45,7 +47,7 @@ dynamicroute:
usentp: true
maxserveruse: 8
synctimeinterval: 3600
ntptimeout: 10
ntptimeout: 3
servers:
- time.google.com
- time1.google.com

View File

@ -2,28 +2,29 @@ nodename: NodeSuper
privkeyv4: mL5IW0GuqbjgDeOJuPHBU2iJzBPNKhaNEXbIGwwYWWk=
privkeyv6: +EdOKIoBp/EvIusHDsvXhV1RJYbyN3Qr8nxlz35wl3I=
listenport: 3000
passwords:
showstate: passwd
addpeer: passwd_addpeer
delpeer: passwd_delpeer
loglevel:
loglevel: normal
logtransit: true
logcontrol: true
lognormal: true
logntp: false
repushconfiginterval: 30
passwords:
showstate: passwd
addpeer: passwd_addpeer
delpeer: passwd_delpeer
graphrecalculatesetting:
jittertolerance: 5
jittertolerancemultiplier: 1.01
nodereporttimeout: 50
recalculatecooldown: 5
edgetemplate: "example_config/super_mode/s1.yaml"
edgetemplate: example_config/super_mode/n1.yaml
peers:
- nodeid: 1
name: "Node_01"
pubkey: ZqzLVSbXzjppERslwbf2QziWruW3V/UIx9oqwU8Fn3I=
pskey: ""
- nodeid: 2
name: "Node_02"
name: Node_02
pubkey: dHeWQtlTPQGy87WdbUARS4CtwVaR2y7IQ1qcX4GKSXk=
pskey: ""
pskey: juJMQaGAaeSy8aDsXSKNsPZv/nFiPj4h/1G70tGYygs=
- nodeid: 1
name: Node_01
pubkey: ZqzLVSbXzjppERslwbf2QziWruW3V/UIx9oqwU8Fn3I=
pskey: iPM8FXfnHVzwjguZHRW9bLNY+h7+B1O2oTJtktptQkI=

View File

@ -47,7 +47,8 @@ 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")
nouapi = flag.Bool("no-uapi", false, "Do not use UAPI")
bind = flag.String("bind", "linux", "UDP socket bind mode. [linux|std]\nYou may need this if tou 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")
)
@ -55,7 +56,7 @@ var (
func main() {
flag.Parse()
if *version == true {
fmt.Printf("etherguard-go %s\n%s-%s\n%s\n\nA full mesh layer 2 VPN powered by Floyd Warshall algorithm.\nInformation available at https://github.com/KusakabeSi/EtherGuardVPN.\nCopyright (C) Kusakabe Si <si@kskb.eu.org>.\n", Version,runtime.GOOS, runtime.GOARCH, tap.VPP_SUPPORT)
fmt.Printf("etherguard-go %s\n%s-%s\n%s\n\nA full mesh layer 2 VPN powered by Floyd Warshall algorithm.\nInformation available at https://github.com/KusakabeSi/EtherGuardVPN.\nCopyright (C) Kusakabe Si <si@kskb.eu.org>.\n", Version, runtime.GOOS, runtime.GOARCH, tap.VPP_SUPPORT)
return
}
if *help == true {
@ -71,9 +72,9 @@ func main() {
var err error
switch *mode {
case "edge":
err = Edge(*tconfig, !*nouapi, *printExample)
err = Edge(*tconfig, !*nouapi, *printExample, *bind)
case "super":
err = Super(*tconfig, !*nouapi, *printExample)
err = Super(*tconfig, !*nouapi, *printExample, *bind)
case "solve":
err = path.Solve(*tconfig, *printExample)
default:

View File

@ -49,6 +49,7 @@ func printExampleEdgeConf() {
},
DynamicRoute: config.DynamicRouteInfo{
SendPingInterval: 20,
PeerAliveTimeout: 30,
DupCheckTimeout: 40,
ConnTimeOut: 30,
SaveNewPeers: true,
@ -64,7 +65,6 @@ func printExampleEdgeConf() {
P2P: config.P2Pinfo{
UseP2P: true,
SendPeerInterval: 20,
PeerAliveTimeout: 30,
GraphRecalculateSetting: config.GraphRecalculateSetting{
JitterTolerance: 20,
JitterToleranceMultiplier: 1.1,
@ -132,7 +132,7 @@ func printExampleEdgeConf() {
return
}
func Edge(configPath string, useUAPI bool, printExample bool) (err error) {
func Edge(configPath string, useUAPI bool, printExample bool, bindmode string) (err error) {
if printExample {
printExampleEdgeConf()
return nil
@ -208,7 +208,7 @@ func Edge(configPath string, useUAPI bool, printExample bool) (err error) {
graph := path.NewGraph(3, false, econfig.DynamicRoute.P2P.GraphRecalculateSetting, econfig.DynamicRoute.NTPconfig, econfig.LogLevel.LogNTP)
graph.SetNHTable(econfig.NextHopTable, [32]byte{})
the_device := device.NewDevice(thetap, econfig.NodeID, conn.NewDefaultBind(), 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)
defer the_device.Close()
pk, err := device.Str2PriKey(econfig.PrivKey)
if err != nil {
@ -228,14 +228,11 @@ func Edge(configPath string, useUAPI bool, printExample bool) (err error) {
the_device.NewPeer(pk, peerconf.NodeID, false)
if peerconf.EndPoint != "" {
peer := the_device.LookupPeer(pk)
endpoint, err := the_device.Bind().ParseEndpoint(peerconf.EndPoint)
err = peer.SetEndpointFromConnURL(peerconf.EndPoint, 0, peerconf.Static)
if err != nil {
logger.Errorf("Failed to set endpoint %v: %w", peerconf.EndPoint, err)
return err
}
peer.StaticConn = peerconf.Static
peer.ConnURL = peerconf.EndPoint
peer.SetEndpointFromPacket(endpoint)
}
}
@ -246,34 +243,44 @@ func Edge(configPath string, useUAPI bool, printExample bool) (err error) {
fmt.Println("Error decode base64 ", err)
return err
}
endpoint, err := the_device.Bind().ParseEndpoint(econfig.DynamicRoute.SuperNode.ConnURLV4)
psk, err := device.Str2PSKey(econfig.DynamicRoute.SuperNode.PSKey)
if err != nil {
fmt.Println("Error decode base64 ", err)
return err
}
peer, err := the_device.NewPeer(pk, config.SuperNodeMessage, true)
if err != nil {
return err
}
peer.StaticConn = false
peer.ConnURL = econfig.DynamicRoute.SuperNode.ConnURLV4
peer.SetEndpointFromPacket(endpoint)
peer.SetPSK(psk)
err = peer.SetEndpointFromConnURL(econfig.DynamicRoute.SuperNode.ConnURLV4, 4, false)
if err != nil {
logger.Errorf("Failed to set endpoint for supernode v4 %v: %v", econfig.DynamicRoute.SuperNode.ConnURLV4, err)
return err
}
}
if econfig.DynamicRoute.SuperNode.ConnURLV6 != "" {
pk, err := device.Str2PubKey(econfig.DynamicRoute.SuperNode.PubKeyV6)
if err != nil {
fmt.Println("Error decode base64 ", err)
}
endpoint, err := the_device.Bind().ParseEndpoint(econfig.DynamicRoute.SuperNode.ConnURLV6)
psk, err := device.Str2PSKey(econfig.DynamicRoute.SuperNode.PSKey)
if err != nil {
fmt.Println("Error decode base64 ", err)
return err
}
peer, err := the_device.NewPeer(pk, config.SuperNodeMessage, true)
if err != nil {
return err
}
peer.SetPSK(psk)
peer.StaticConn = false
peer.ConnURL = econfig.DynamicRoute.SuperNode.ConnURLV6
peer.SetEndpointFromPacket(endpoint)
err = peer.SetEndpointFromConnURL(econfig.DynamicRoute.SuperNode.ConnURLV6, 6, false)
if err != nil {
logger.Errorf("Failed to set endpoint for supernode v6 %v: %v", econfig.DynamicRoute.SuperNode.ConnURLV6, err)
return err
}
}
the_device.Event_Supernode_OK <- struct{}{}
}

View File

@ -3,6 +3,7 @@ package main
import (
"bytes"
"crypto/md5"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
@ -14,6 +15,7 @@ import (
"net/http"
"github.com/KusakabeSi/EtherGuardVPN/config"
"github.com/KusakabeSi/EtherGuardVPN/conn"
"github.com/KusakabeSi/EtherGuardVPN/device"
"github.com/KusakabeSi/EtherGuardVPN/path"
yaml "gopkg.in/yaml.v2"
@ -27,7 +29,7 @@ var (
http_NhTable_Hash [32]byte
http_PeerInfo_hash [32]byte
http_NhTableStr []byte
http_PeerInfoStr []byte
http_PeerInfo config.API_Peers
http_PeerID2PubKey map[config.Vertex]string
@ -36,12 +38,18 @@ var (
http_StateString_tmp []byte
http_PeerState map[string]*PeerState //the state hash reported by peer
http_PeerIPs map[string]*HttpPeerLocalIP
http_sconfig *config.SuperConfig
http_sconfig_path string
http_econfig_tmp *config.EdgeConfig
)
type HttpPeerLocalIP struct {
IPv4 net.UDPAddr
IPv6 net.UDPAddr
}
type HttpState struct {
PeerInfo map[config.Vertex]HttpPeerInfo
Infinity float64
@ -68,26 +76,38 @@ type client struct {
notify6 string
}
func get_api_peers() (api_peerinfo config.API_Peers, api_peerinfo_str_byte []byte, StateHash [32]byte, changed bool) {
func get_api_peers(old_State_hash [32]byte) (api_peerinfo config.API_Peers, StateHash [32]byte, changed bool) {
api_peerinfo = make(config.API_Peers)
for _, peerinfo := range http_sconfig.Peers {
api_peerinfo[peerinfo.PubKey] = config.API_Peerinfo{
NodeID: peerinfo.NodeID,
PubKey: peerinfo.PubKey,
PSKey: peerinfo.PSKey,
Connurl: make(map[string]bool),
Connurl: make(map[string]int),
}
connV4 := http_device4.GetConnurl(peerinfo.NodeID)
connV6 := http_device6.GetConnurl(peerinfo.NodeID)
api_peerinfo[peerinfo.PubKey].Connurl[connV4] = true
api_peerinfo[peerinfo.PubKey].Connurl[connV6] = true
api_peerinfo[peerinfo.PubKey].Connurl[connV4] = 4
api_peerinfo[peerinfo.PubKey].Connurl[connV6] = 6
L4Addr := http_PeerIPs[peerinfo.PubKey].IPv4
L4IP := L4Addr.IP
L4str := L4Addr.String()
if L4str != connV4 && conn.ValidIP(L4IP) {
api_peerinfo[peerinfo.PubKey].Connurl[L4str] = 14
}
L6Addr := http_PeerIPs[peerinfo.PubKey].IPv6
L6IP := L6Addr.IP
L6str := L6Addr.String()
if L6str != connV6 && conn.ValidIP(L6IP) {
api_peerinfo[peerinfo.PubKey].Connurl[L6str] = 16
}
delete(api_peerinfo[peerinfo.PubKey].Connurl, "")
}
api_peerinfo_str_byte, _ = json.Marshal(&api_peerinfo)
api_peerinfo_str_byte, _ := json.Marshal(&api_peerinfo)
hash_raw := md5.Sum(append(api_peerinfo_str_byte, http_HashSalt...))
hash_str := hex.EncodeToString(hash_raw[:])
copy(StateHash[:], []byte(hash_str))
if bytes.Equal(http_PeerInfo_hash[:], StateHash[:]) == false {
if bytes.Equal(old_State_hash[:], StateHash[:]) == false {
changed = true
}
return
@ -107,14 +127,55 @@ func get_peerinfo(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Not found"))
return
}
NIDA, has := params["NodeID"]
if !has {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("Not found"))
return
}
NID2, err := strconv.ParseUint(NIDA[0], 10, 16)
if err != nil {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(fmt.Sprintf("%v", err)))
return
}
PubKey := PubKeyA[0]
State := StateA[0]
NodeID := config.Vertex(NID2)
if http_PeerID2PubKey[NodeID] != PubKey {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("Not found"))
return
}
if bytes.Equal(http_PeerInfo_hash[:], []byte(State)) {
if state := http_PeerState[PubKey]; state != nil {
copy(http_PeerState[PubKey].PeerInfoState[:], State)
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))
} else {
continue
}
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)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(http_PeerInfoStr))
w.Write(api_peerinfo_str_byte)
return
}
}
@ -136,8 +197,27 @@ func get_nhtable(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Not found"))
return
}
NIDA, has := params["NodeID"]
if !has {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("Not found"))
return
}
NID2, err := strconv.ParseUint(NIDA[0], 10, 16)
if err != nil {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte(fmt.Sprintf("%v", err)))
return
}
PubKey := PubKeyA[0]
State := StateA[0]
NodeID := config.Vertex(NID2)
if http_PeerID2PubKey[NodeID] != PubKey {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("Not found"))
return
}
if bytes.Equal(http_NhTable_Hash[:], []byte(State)) {
if state := http_PeerState[PubKey]; state != nil {
copy(http_PeerState[PubKey].NhTableState[:], State)
@ -252,8 +332,22 @@ func peeradd(w http.ResponseWriter, r *http.Request) { //Waiting for test
PubKey: PubKey,
PSKey: PSKey,
})
http_sconfig.Peers = append(http_sconfig.Peers, config.SuperPeerInfo{
NodeID: NodeID,
Name: Name,
PubKey: PubKey,
PSKey: PSKey,
})
configbytes, _ := yaml.Marshal(http_sconfig)
ioutil.WriteFile(http_sconfig_path, configbytes, 0644)
http_econfig_tmp.NodeID = NodeID
http_econfig_tmp.NodeName = Name
http_econfig_tmp.PrivKey = "Your_Private_Key"
http_econfig_tmp.DynamicRoute.SuperNode.PSKey = PSKey
ret_str_byte, _ := yaml.Marshal(&http_econfig_tmp)
w.WriteHeader(http.StatusOK)
w.Write(ret_str_byte)
return
}
func peerdel(w http.ResponseWriter, r *http.Request) { //Waiting for test
@ -264,7 +358,7 @@ func peerdel(w http.ResponseWriter, r *http.Request) { //Waiting for test
PubKey := ""
if has {
password := PasswordA[0]
if password == http_passwords.AddPeer {
if password == http_passwords.DelPeer {
NodeIDA, has := params["nodeid"]
if !has {
w.WriteHeader(http.StatusBadRequest)

View File

@ -8,6 +8,7 @@
package main
import (
"bytes"
"crypto/md5"
"encoding/hex"
"encoding/json"
@ -62,7 +63,7 @@ func printExampleSuperConf() {
return
}
func Super(configPath string, useUAPI bool, printExample bool) (err error) {
func Super(configPath string, useUAPI bool, printExample bool, bindmode string) (err error) {
if printExample {
printExampleSuperConf()
return nil
@ -108,6 +109,7 @@ func Super(configPath string, useUAPI bool, printExample bool) (err error) {
http_sconfig_path = configPath
http_PeerState = make(map[string]*PeerState)
http_PeerIPs = make(map[string]*HttpPeerLocalIP)
http_PeerID2PubKey = make(map[config.Vertex]string)
http_HashSalt = []byte(config.RandomStr(32, "Salt generate failed"))
http_passwords = sconfig.Passwords
@ -120,10 +122,10 @@ func Super(configPath string, useUAPI bool, printExample bool) (err error) {
http_graph = path.NewGraph(3, true, sconfig.GraphRecalculateSetting, config.NTPinfo{}, sconfig.LogLevel.LogNTP)
thetap4, _ := tap.CreateDummyTAP()
http_device4 = device.NewDevice(thetap4, config.SuperNodeMessage, conn.NewCustomBind(true, false), logger4, http_graph, true, configPath, nil, &sconfig, &super_chains, Version)
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()
thetap6, _ := tap.CreateDummyTAP()
http_device6 = device.NewDevice(thetap6, config.SuperNodeMessage, conn.NewCustomBind(false, true), logger6, http_graph, true, configPath, nil, &sconfig, &super_chains, Version)
http_device6 = device.NewDevice(thetap6, config.SuperNodeMessage, conn.NewDefaultBind(false, true, bindmode), logger6, http_graph, true, configPath, nil, &sconfig, &super_chains, Version)
defer http_device6.Close()
if sconfig.PrivKeyV4 != "" {
pk4, err := device.Str2PriKey(sconfig.PrivKeyV4)
@ -199,7 +201,7 @@ func super_peeradd(peerconf config.SuperPeerInfo) error {
fmt.Printf("Error create peer id %v\n", peerconf.NodeID)
return err
}
peer4.StaticConn = true
peer4.StaticConn = false
if peerconf.PSKey != "" {
psk, err := device.Str2PSKey(peerconf.PSKey)
if err != nil {
@ -215,7 +217,7 @@ func super_peeradd(peerconf config.SuperPeerInfo) error {
fmt.Printf("Error create peer id %v\n", peerconf.NodeID)
return err
}
peer6.StaticConn = true
peer6.StaticConn = false
if peerconf.PSKey != "" {
psk, err := device.Str2PSKey(peerconf.PSKey)
if err != nil {
@ -227,15 +229,41 @@ func super_peeradd(peerconf config.SuperPeerInfo) error {
}
http_PeerID2PubKey[peerconf.NodeID] = peerconf.PubKey
http_PeerState[peerconf.PubKey] = &PeerState{}
http_PeerIPs[peerconf.PubKey] = &HttpPeerLocalIP{}
return nil
}
func super_peerdel(toDelete config.Vertex) {
PubKey := http_PeerID2PubKey[toDelete]
UpdateErrorMsg := path.UpdateErrorMsg{
Node_id: toDelete,
Action: path.Shutdown,
ErrorCode: 410,
ErrorMsg: "You've been removed from supernode.",
}
for i := 0; i < 10; i++ {
body, _ := path.GetByte(&UpdateErrorMsg)
buf := make([]byte, path.EgHeaderLen+len(body))
header, _ := path.NewEgHeader(buf[:path.EgHeaderLen])
header.SetSrc(config.SuperNodeMessage)
header.SetTTL(0)
header.SetPacketLength(uint16(len(body)))
copy(buf[path.EgHeaderLen:], body)
header.SetDst(toDelete)
peer4 := http_device4.LookupPeerByStr(PubKey)
http_device4.SendPacket(peer4, path.UpdateError, buf, device.MessageTransportOffsetContent)
peer6 := http_device6.LookupPeerByStr(PubKey)
http_device6.SendPacket(peer6, path.UpdateError, buf, device.MessageTransportOffsetContent)
time.Sleep(path.S2TD(0.1))
}
http_device4.RemovePeerByID(toDelete)
http_device6.RemovePeerByID(toDelete)
http_graph.RemoveVirt(toDelete, true, false)
PubKey := http_PeerID2PubKey[toDelete]
delete(http_PeerState, PubKey)
delete(http_PeerIPs, PubKey)
delete(http_PeerID2PubKey, toDelete)
}
@ -243,15 +271,30 @@ func Event_server_event_hendler(graph *path.IG, events path.SUPER_Events) {
for {
select {
case reg_msg := <-events.Event_server_register:
var should_push_peer bool
var should_push_nh bool
if reg_msg.Node_id < config.Special_NodeID {
copy(http_PeerState[http_PeerID2PubKey[reg_msg.Node_id]].NhTableState[:], reg_msg.NhStateHash[:])
copy(http_PeerState[http_PeerID2PubKey[reg_msg.Node_id]].PeerInfoState[:], reg_msg.PeerStateHash[:])
PubKey := http_PeerID2PubKey[reg_msg.Node_id]
if bytes.Equal(http_PeerState[PubKey].NhTableState[:], reg_msg.NhStateHash[:]) == false {
copy(http_PeerState[PubKey].NhTableState[:], reg_msg.NhStateHash[:])
should_push_nh = true
}
if bytes.Equal(http_PeerState[PubKey].PeerInfoState[:], reg_msg.PeerStateHash[:]) == false {
copy(http_PeerState[PubKey].PeerInfoState[:], reg_msg.PeerStateHash[:])
should_push_peer = true
}
http_PeerIPs[PubKey].IPv4 = reg_msg.LocalV4
http_PeerIPs[PubKey].IPv6 = reg_msg.LocalV6
}
var changed bool
_, http_PeerInfoStr, http_PeerInfo_hash, changed = get_api_peers()
if changed {
var peer_state_changed bool
http_PeerInfo, http_PeerInfo_hash, peer_state_changed = get_api_peers(http_PeerInfo_hash)
if should_push_peer || peer_state_changed {
PushPeerinfo()
}
if should_push_nh {
PushNhTable()
}
case <-events.Event_server_NhTable_changed:
NhTable := graph.GetNHTable()
NhTablestr, _ := json.Marshal(NhTable)
@ -307,7 +350,6 @@ func PushNhTable() {
if peer := http_device6.LookupPeerByStr(pkstr); peer != nil && peer.GetEndpointDstStr() != "" {
http_device6.SendPacket(peer, path.UpdateNhTable, buf, device.MessageTransportOffsetContent)
}
}
}

View File

@ -76,6 +76,12 @@ func (o *OrderedMap) Delete(key string) {
delete(o.values, key)
}
func (o *OrderedMap) Clear() { // delete whole orderdmap
for _, key := range o.Keys() {
o.Delete(key)
}
}
func (o *OrderedMap) Keys() []string {
ret := make([]string, len(o.keys))
for i, v := range o.keys {

View File

@ -4,6 +4,8 @@ import (
"bytes"
"encoding/base64"
"encoding/gob"
"fmt"
"net"
"strconv"
"time"
@ -22,13 +24,24 @@ func GetByte(structIn interface{}) (bb []byte, err error) {
type RegisterMsg struct {
Node_id config.Vertex
Version string
PeerStateHash [32]byte
NhStateHash [32]byte
Version string
LocalV4 net.UDPAddr
LocalV6 net.UDPAddr
}
func Hash2Str(h []byte) string {
for _, v := range h {
if v != 0 {
return base64.StdEncoding.EncodeToString(h)[:10] + "..."
}
}
return "\"\""
}
func (c *RegisterMsg) ToString() string {
return "RegisterMsg Node_id:" + c.Node_id.ToString() + " Version:" + c.Version + " PeerHash:" + base64.StdEncoding.EncodeToString(c.PeerStateHash[:]) + " NhHash:" + base64.StdEncoding.EncodeToString(c.NhStateHash[:])
return fmt.Sprint("RegisterMsg Node_id:"+c.Node_id.ToString(), " Version:"+c.Version, " PeerHash:"+Hash2Str(c.PeerStateHash[:]), " NhHash:"+Hash2Str(c.NhStateHash[:]), " LocalV4:"+c.LocalV4.String(), " LocalV6:"+c.LocalV6.String())
}
func ParseRegisterMsg(bin []byte) (StructPlace RegisterMsg, err error) {
@ -108,9 +121,10 @@ func ParseUpdateNhTableMsg(bin []byte) (StructPlace UpdateNhTableMsg, err error)
}
type PingMsg struct {
RequestID uint32
Src_nodeID config.Vertex
Time time.Time
RequestID uint32
Src_nodeID config.Vertex
Time time.Time
RequestReply int
}
func (c *PingMsg) ToString() string {
@ -164,7 +178,6 @@ type BoardcastPeerMsg struct {
Request_ID uint32
NodeID config.Vertex
PubKey [32]byte
PSKey [32]byte
ConnURL string
}