TAP and routeing

This commit is contained in:
KusakabeSi 2021-08-16 18:58:15 +00:00
parent 1fa36f77ac
commit 87a62f873b
29 changed files with 1667 additions and 373 deletions

3
.gitignore vendored
View File

@ -1 +1,2 @@
wireguard-go
go.sum
etherguard-go

16
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}",
"args":["-config","example_config/n1.yaml","-mode","edge"],
}
]
}

View File

@ -14,18 +14,18 @@ generate-version-and-build:
[ "$$(cat version.go 2>/dev/null)" != "$$ver" ] && \
echo "$$ver" > version.go && \
git update-index --assume-unchanged version.go || true
@$(MAKE) wireguard-go
@$(MAKE) etherguard-go
wireguard-go: $(wildcard *.go) $(wildcard */*.go)
etherguard-go: $(wildcard *.go) $(wildcard */*.go)
go build -v -o "$@"
install: wireguard-go
@install -v -d "$(DESTDIR)$(BINDIR)" && install -v -m 0755 "$<" "$(DESTDIR)$(BINDIR)/wireguard-go"
install: etherguard-go
@install -v -d "$(DESTDIR)$(BINDIR)" && install -v -m 0755 "$<" "$(DESTDIR)$(BINDIR)/etherguard-go"
test:
go test -v ./...
clean:
rm -f wireguard-go
rm -f etherguard-go
.PHONY: all clean test install generate-version-and-build

View File

@ -60,10 +60,15 @@ type LinuxSocketBind struct {
mu sync.RWMutex
sock4 int
sock6 int
use4 bool
use6 bool
}
func NewLinuxSocketBind() Bind { return &LinuxSocketBind{sock4: -1, sock6: -1} }
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 {
return &LinuxSocketBind{sock4: -1, sock6: -1, use4: use4, use6: use6}
}
var _ Endpoint = (*LinuxSocketEndpoint)(nil)
var _ Bind = (*LinuxSocketBind)(nil)
@ -120,32 +125,35 @@ func (bind *LinuxSocketBind) Open(port uint16) ([]ReceiveFunc, uint16, error) {
again:
port = originalPort
var sock4, sock6 int
// Attempt ipv6 bind, update port if successful.
sock6, newPort, err = create6(port)
if err != nil {
if !errors.Is(err, syscall.EAFNOSUPPORT) {
return nil, 0, err
if bind.use6 {
// Attempt ipv6 bind, update port if successful.
sock6, newPort, err = create6(port)
if err != nil {
if !errors.Is(err, syscall.EAFNOSUPPORT) {
return nil, 0, err
}
} else {
port = newPort
}
} else {
port = newPort
}
// Attempt ipv4 bind, update port if successful.
sock4, newPort, err = create4(port)
if err != nil {
if originalPort == 0 && errors.Is(err, syscall.EADDRINUSE) && tries < 100 {
unix.Close(sock6)
tries++
goto again
if bind.use4 {
// Attempt ipv4 bind, update port if successful.
sock4, newPort, err = create4(port)
if err != nil {
if originalPort == 0 && errors.Is(err, syscall.EADDRINUSE) && tries < 100 {
unix.Close(sock6)
tries++
goto again
}
if !errors.Is(err, syscall.EAFNOSUPPORT) {
unix.Close(sock6)
return nil, 0, err
}
} else {
port = newPort
}
if !errors.Is(err, syscall.EAFNOSUPPORT) {
unix.Close(sock6)
return nil, 0, err
}
} else {
port = newPort
}
var fns []ReceiveFunc
if sock4 != -1 {
bind.sock4 = sock4

View File

@ -12,9 +12,10 @@ import (
"time"
"github.com/KusakabeSi/EtherGuardVPN/conn"
"github.com/KusakabeSi/EtherGuardVPN/path"
"github.com/KusakabeSi/EtherGuardVPN/ratelimiter"
"github.com/KusakabeSi/EtherGuardVPN/rwcancel"
"github.com/KusakabeSi/EtherGuardVPN/tun"
"github.com/KusakabeSi/EtherGuardVPN/tap"
)
type Device struct {
@ -60,12 +61,17 @@ type Device struct {
peers struct {
sync.RWMutex // protects keyMap
keyMap map[NoisePublicKey]*Peer
IDMap map[path.Vertex]*Peer
}
allowedips AllowedIPs
indexTable IndexTable
cookieChecker CookieChecker
ID path.Vertex
NhTable path.NextHopTable
l2fib map[tap.MacAddress]path.Vertex
LogTransit bool
pool struct {
messageBuffers *WaitPool
inboundElements *WaitPool
@ -78,8 +84,8 @@ type Device struct {
handshake *handshakeQueue
}
tun struct {
device tun.Device
tap struct {
device tap.Device
mtu int32
}
@ -126,7 +132,6 @@ func (device *Device) isUp() bool {
// Must hold device.peers.Lock()
func removePeerLocked(device *Device, peer *Peer, key NoisePublicKey) {
// stop routing and processing of packets
device.allowedips.RemoveByPeer(peer)
peer.Stop()
// remove from peer map
@ -274,20 +279,23 @@ func (device *Device) SetPrivateKey(sk NoisePrivateKey) error {
return nil
}
func NewDevice(tunDevice tun.Device, bind conn.Bind, logger *Logger) *Device {
func NewDevice(tapDevice tap.Device, id path.Vertex, bind conn.Bind, logger *Logger) *Device {
device := new(Device)
device.state.state = uint32(deviceStateDown)
device.closed = make(chan struct{})
device.log = logger
device.net.bind = bind
device.tun.device = tunDevice
mtu, err := device.tun.device.MTU()
device.tap.device = tapDevice
mtu, err := device.tap.device.MTU()
if err != nil {
device.log.Errorf("Trouble determining MTU, assuming default: %v", err)
mtu = DefaultMTU
}
device.tun.mtu = int32(mtu)
device.tap.mtu = int32(mtu)
device.peers.keyMap = make(map[NoisePublicKey]*Peer)
device.peers.IDMap = make(map[path.Vertex]*Peer)
device.ID = id
device.l2fib = make(map[tap.MacAddress]path.Vertex)
device.rate.limiter.Init()
device.indexTable.Init()
device.PopulatePools()
@ -344,6 +352,7 @@ func (device *Device) RemoveAllPeers() {
}
device.peers.keyMap = make(map[NoisePublicKey]*Peer)
device.peers.IDMap = make(map[path.Vertex]*Peer)
}
func (device *Device) Close() {
@ -355,7 +364,7 @@ func (device *Device) Close() {
atomic.StoreUint32(&device.state.state, uint32(deviceStateClosed))
device.log.Verbosef("Device closing")
device.tun.device.Close()
device.tap.device.Close()
device.downLocked()
// Remove peers before closing queues,

View File

@ -8,11 +8,13 @@ package device
import (
"container/list"
"errors"
"fmt"
"sync"
"sync/atomic"
"time"
"github.com/KusakabeSi/EtherGuardVPN/conn"
"github.com/KusakabeSi/EtherGuardVPN/path"
)
type Peer struct {
@ -24,6 +26,8 @@ type Peer struct {
endpoint conn.Endpoint
stopping sync.WaitGroup // routines pending stop
ID path.Vertex
// These fields are accessed with atomic operations, which must be
// 64-bit aligned even on 32-bit platforms. Go guarantees that an
// allocated struct will be 64-bit aligned. So we place
@ -63,7 +67,7 @@ type Peer struct {
persistentKeepaliveInterval uint32 // accessed atomically
}
func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
func (device *Device) NewPeer(pk NoisePublicKey, id path.Vertex) (*Peer, error) {
if device.isClosed() {
return nil, errors.New("device closed")
}
@ -94,8 +98,13 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
// map public key
_, ok := device.peers.keyMap[pk]
if ok {
return nil, errors.New("adding existing peer")
return nil, errors.New("adding existing peer pubkey:" + fmt.Sprint(pk))
}
_, ok = device.peers.IDMap[id]
if ok {
return nil, errors.New("adding existing peer id:" + fmt.Sprint(id))
}
peer.ID = id
// pre-compute DH
handshake := &peer.handshake
@ -109,6 +118,7 @@ func (device *Device) NewPeer(pk NoisePublicKey) (*Peer, error) {
// add
device.peers.keyMap[pk] = peer
device.peers.IDMap[id] = peer
// start peer
peer.timersInit()

View File

@ -9,16 +9,17 @@ import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"net"
"sync"
"sync/atomic"
"time"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
"github.com/KusakabeSi/EtherGuardVPN/conn"
"github.com/KusakabeSi/EtherGuardVPN/path"
"github.com/KusakabeSi/EtherGuardVPN/tap"
)
type QueueHandshakeElement struct {
@ -397,6 +398,7 @@ func (device *Device) RoutineHandshake(id int) {
func (peer *Peer) RoutineSequentialReceiver() {
device := peer.device
var peer_out *Peer
defer func() {
device.log.Verbosef("%v - Routine: sequential receiver - stopped", peer)
peer.stopping.Done()
@ -407,7 +409,12 @@ func (peer *Peer) RoutineSequentialReceiver() {
if elem == nil {
return
}
var EgBody path.EgHeader
var err error
var src_nodeID path.Vertex
var dst_nodeID path.Vertex
should_receive := false
should_transfer := false
elem.Lock()
if elem.packet == nil {
// decryption failed
@ -435,55 +442,64 @@ func (peer *Peer) RoutineSequentialReceiver() {
}
peer.timersDataReceived()
switch elem.packet[0] >> 4 {
case ipv4.Version:
if len(elem.packet) < ipv4.HeaderLen {
goto skip
}
field := elem.packet[IPv4offsetTotalLength : IPv4offsetTotalLength+2]
length := binary.BigEndian.Uint16(field)
if int(length) > len(elem.packet) || int(length) < ipv4.HeaderLen {
goto skip
}
elem.packet = elem.packet[:length]
src := elem.packet[IPv4offsetSrc : IPv4offsetSrc+net.IPv4len]
if device.allowedips.Lookup(src) != peer {
device.log.Verbosef("IPv4 packet with disallowed source address from %v", peer)
goto skip
}
EgBody, err = path.NewEgHeader(elem.packet[0:path.EgHeaderLen])
src_nodeID = EgBody.GetSrc()
dst_nodeID = EgBody.GetDst()
//elem.packet = elem.packet[:EgBody.GetPacketLength()]
case ipv6.Version:
if len(elem.packet) < ipv6.HeaderLen {
goto skip
}
field := elem.packet[IPv6offsetPayloadLength : IPv6offsetPayloadLength+2]
length := binary.BigEndian.Uint16(field)
length += ipv6.HeaderLen
if int(length) > len(elem.packet) {
goto skip
}
elem.packet = elem.packet[:length]
src := elem.packet[IPv6offsetSrc : IPv6offsetSrc+net.IPv6len]
if device.allowedips.Lookup(src) != peer {
device.log.Verbosef("IPv6 packet with disallowed source address from %v", peer)
goto skip
}
default:
device.log.Verbosef("Packet with invalid IP version from %v", peer)
if dst_nodeID == device.ID {
should_receive = true
} else if dst_nodeID == path.Boardcast {
should_receive = true
should_transfer = true
} else if device.NhTable[device.ID][dst_nodeID] != nil {
should_transfer = true
} else {
device.log.Verbosef("No route to peer ID %v", dst_nodeID)
goto skip
}
_, err = device.tun.device.Write(elem.buffer[:MessageTransportOffsetContent+len(elem.packet)], MessageTransportOffsetContent)
if err != nil && !device.isClosed() {
device.log.Errorf("Failed to write packet to TUN device: %v", err)
}
if len(peer.queue.inbound.c) == 0 {
err = device.tun.device.Flush()
if err != nil {
peer.device.log.Errorf("Unable to flush packets: %v", err)
if should_transfer { //Send to another peer
l2ttl := EgBody.GetTTL()
if l2ttl == 0 {
device.log.Verbosef("TTL is 0 %v", dst_nodeID)
} else {
EgBody.SetTTL(l2ttl - 1)
if dst_nodeID != path.Boardcast {
next_id := *device.NhTable[device.ID][dst_nodeID]
peer_out = device.peers.IDMap[next_id]
if device.LogTransit {
fmt.Printf("Transfer packet from %d through %d to %d\n", peer.ID, device.ID, peer_out.ID)
}
device.SendPacket(peer_out, elem.packet, MessageTransportOffsetContent)
} else {
node_boardcast_list := path.GetBoardcastThroughList(device.ID, src_nodeID, device.NhTable)
for peer_id := range node_boardcast_list {
peer_out = device.peers.IDMap[peer_id]
if device.LogTransit {
fmt.Printf("Transfer packet from %d through %d to %d\n", peer.ID, device.ID, peer_out.ID)
}
device.SendPacket(peer_out, elem.packet, MessageTransportOffsetContent)
}
}
}
}
if should_receive {
src_macaddr := tap.GetSrcMacAddr(elem.packet[path.EgHeaderLen:])
device.l2fib[src_macaddr] = src_nodeID // Write to l2fib table
_, err = device.tap.device.Write(elem.buffer[:MessageTransportOffsetContent+len(elem.packet)], MessageTransportOffsetContent+path.EgHeaderLen)
if err != nil && !device.isClosed() {
device.log.Errorf("Failed to write packet to TUN device: %v", err)
}
if len(peer.queue.inbound.c) == 0 {
err = device.tap.device.Flush()
if err != nil {
peer.device.log.Errorf("Unable to flush packets: %v", err)
}
}
}
skip:
device.PutMessageBuffer(elem.buffer)
device.PutInboundElement(elem)

View File

@ -9,15 +9,14 @@ import (
"bytes"
"encoding/binary"
"errors"
"net"
"os"
"sync"
"sync/atomic"
"time"
"github.com/KusakabeSi/EtherGuardVPN/path"
"github.com/KusakabeSi/EtherGuardVPN/tap"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)
/* Outbound flow
@ -225,7 +224,7 @@ func (device *Device) RoutineReadFromTUN() {
// read packet
offset := MessageTransportHeaderSize
size, err := device.tun.device.Read(elem.buffer[:], offset)
size, err := device.tap.device.Read(elem.buffer[:], offset+path.EgHeaderLen)
if err != nil {
if !device.isClosed() {
@ -239,42 +238,62 @@ func (device *Device) RoutineReadFromTUN() {
return
}
if size == 0 || size > MaxContentSize {
if size == 0 || (size+path.EgHeaderLen) > MaxContentSize {
continue
}
//add custom header dst_node, src_node, ttl
size += path.EgHeaderLen
elem.packet = elem.buffer[offset : offset+size]
EgBody, err := path.NewEgHeader(elem.packet[0:path.EgHeaderLen])
dst_nodeID := EgBody.GetDst()
dstMacAddr := tap.GetDstMacAddr(elem.packet[path.EgHeaderLen:])
// lookup peer
if tap.IsBoardCast(dstMacAddr) {
dst_nodeID = path.Boardcast
} else if val, ok := device.l2fib[dstMacAddr]; !ok { //Lookup failed
dst_nodeID = path.Boardcast
} else {
dst_nodeID = val
}
EgBody.SetSrc(device.ID)
EgBody.SetDst(dst_nodeID)
//EgBody.SetPacketLength(uint16(len(elem.packet)))
EgBody.SetTTL(200)
var peer *Peer
switch elem.packet[0] >> 4 {
case ipv4.Version:
if len(elem.packet) < ipv4.HeaderLen {
if dst_nodeID != path.Boardcast {
var peer_out *Peer
next_id := *device.NhTable[device.ID][dst_nodeID]
peer_out = device.peers.IDMap[next_id]
if peer_out == nil {
continue
}
dst := elem.packet[IPv4offsetDst : IPv4offsetDst+net.IPv4len]
peer = device.allowedips.Lookup(dst)
case ipv6.Version:
if len(elem.packet) < ipv6.HeaderLen {
continue
if peer_out.isRunning.Get() {
peer_out.StagePacket(elem)
elem = nil
peer_out.SendStagedPackets()
}
} else {
for key, _ := range path.GetBoardcastList(device.ID, device.NhTable) {
device.SendPacket(device.peers.IDMap[key], elem.packet, offset)
}
dst := elem.packet[IPv6offsetDst : IPv6offsetDst+net.IPv6len]
peer = device.allowedips.Lookup(dst)
default:
device.log.Verbosef("Received packet with unknown IP version")
}
if peer == nil {
continue
}
if peer.isRunning.Get() {
peer.StagePacket(elem)
elem = nil
peer.SendStagedPackets()
}
}
}
func (device *Device) SendPacket(peer *Peer, packet []byte, offset int) {
if peer == nil {
return
}
var elem *QueueOutboundElement
elem = device.NewOutboundElement()
copy(elem.buffer[offset:offset+len(packet)], packet)
elem.packet = elem.buffer[offset : offset+len(packet)]
if peer.isRunning.Get() {
peer.StagePacket(elem)
elem = nil
peer.SendStagedPackets()
}
}
@ -386,7 +405,7 @@ func (device *Device) RoutineEncryption(id int) {
binary.LittleEndian.PutUint64(fieldNonce, elem.nonce)
// pad content to multiple of 16
paddingSize := calculatePaddingSize(len(elem.packet), int(atomic.LoadInt32(&device.tun.mtu)))
paddingSize := calculatePaddingSize(len(elem.packet), int(atomic.LoadInt32(&device.tap.mtu)))
elem.packet = append(elem.packet, paddingZeros[:paddingSize]...)
// encrypt content and release to consumer

54
device/tap.go Normal file
View File

@ -0,0 +1,54 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
*/
package device
import (
"fmt"
"sync/atomic"
"github.com/KusakabeSi/EtherGuardVPN/tap"
)
const DefaultMTU = 1420
func (device *Device) RoutineTUNEventReader() {
device.log.Verbosef("Routine: event worker - started")
for event := range device.tap.device.Events() {
if event&tap.EventMTUUpdate != 0 {
mtu, err := device.tap.device.MTU()
if err != nil {
device.log.Errorf("Failed to load updated MTU of device: %v", err)
continue
}
if mtu < 0 {
device.log.Errorf("MTU not updated to negative value: %v", mtu)
continue
}
var tooLarge string
if mtu > MaxContentSize {
tooLarge = fmt.Sprintf(" (too large, capped at %v)", MaxContentSize)
mtu = MaxContentSize
}
old := atomic.SwapInt32(&device.tap.mtu, int32(mtu))
if int(old) != mtu {
device.log.Verbosef("MTU updated: %v%s", mtu, tooLarge)
}
}
if event&tap.EventUp != 0 {
device.log.Verbosef("Interface up requested")
device.Up()
}
if event&tap.EventDown != 0 {
device.log.Verbosef("Interface down requested")
device.Down()
}
}
device.log.Verbosef("Routine: event worker - stopped")
}

View File

@ -10,6 +10,7 @@ import (
"bytes"
"errors"
"fmt"
"hash/crc32"
"io"
"net"
"strconv"
@ -19,6 +20,7 @@ import (
"time"
"github.com/KusakabeSi/EtherGuardVPN/ipc"
"github.com/KusakabeSi/EtherGuardVPN/path"
)
type IPCError struct {
@ -120,11 +122,8 @@ func (device *Device) IpcGetOperation(w io.Writer) error {
sendf("tx_bytes=%d", atomic.LoadUint64(&peer.stats.txBytes))
sendf("rx_bytes=%d", atomic.LoadUint64(&peer.stats.rxBytes))
sendf("persistent_keepalive_interval=%d", atomic.LoadUint32(&peer.persistentKeepaliveInterval))
device.allowedips.EntriesForPeer(peer, func(ip net.IP, cidr uint8) bool {
sendf("allowed_ip=%s/%d", ip.String(), cidr)
return true
})
sendf("allowed_ip=%s/%d", net.IPv4zero.String(), 0)
sendf("allowed_ip=%s/%d", net.IPv6zero.String(), 0)
}
}()
@ -269,6 +268,8 @@ func (device *Device) handlePublicKeyLine(peer *ipcSetPeer, value string) error
if err != nil {
return ipcErrorf(ipc.IpcErrorInvalid, "failed to get peer by public key: %w", err)
}
h := crc32.NewIEEE()
h.Write(publicKey[:])
// Ignore peer with the same public key as this device.
device.staticIdentity.RLock()
@ -283,7 +284,7 @@ func (device *Device) handlePublicKeyLine(peer *ipcSetPeer, value string) error
peer.created = peer.Peer == nil
if peer.created {
peer.Peer, err = device.NewPeer(publicKey)
peer.Peer, err = device.NewPeer(publicKey, path.Vertex(h.Sum32()))
if err != nil {
return ipcErrorf(ipc.IpcErrorInvalid, "failed to create new peer: %w", err)
}
@ -366,7 +367,6 @@ func (device *Device) handlePeerLine(peer *ipcSetPeer, key, value string) error
if peer.dummy {
return nil
}
device.allowedips.RemoveByPeer(peer.Peer)
case "allowed_ip":
device.log.Verbosef("%v - UAPI: Adding allowedip", peer.Peer)
@ -378,8 +378,7 @@ func (device *Device) handlePeerLine(peer *ipcSetPeer, key, value string) error
if peer.dummy {
return nil
}
ones, _ := network.Mask.Size()
device.allowedips.Insert(network.IP, uint8(ones), peer.Peer)
_, _ = network.Mask.Size()
case "protocol_version":
if value != "1" {

65
example_config/n1.yaml Normal file
View File

@ -0,0 +1,65 @@
interface:
itype: 'stdio'
ifaceid: 1
name: 'netcat1'
macaddr: 'BB:AA:CC:DD:EE:01'
mtu: 1500
recvaddr: '127.0.0.1:4001'
sendaddr: '127.0.0.1:5001'
humanfriendly: true
nodeid: 1
nodename: 'Node1'
privkey: 'SM8pGjT0r8njy1/7ffN4wMwF7nnJ8UYSjGRWpCqo3ng='
listenport: 3001
loglevel:
loglevel: "error"
logtransit: true
supernode:
enable: false
pubkeyv4: ""
pubkeyv6: ""
regurlv4: ""
regurlv6: ""
apiurl: ""
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
peers:
- nodeid: 2
pubkey: 'NuYJ/3Ght+C4HovFq5Te/BrIazo6zwDJ8Bdu4rQCz0o='
endpoint: '127.0.0.1:3002'

70
example_config/n2.yaml Normal file
View File

@ -0,0 +1,70 @@
interface:
itype: 'stdio'
ifaceid: 2
name: 'netcat2'
macaddr: 'BB:AA:CC:DD:EE:02'
mtu: 1500
recvaddr: '127.0.0.1:4002'
sendaddr: '127.0.0.1:5002'
humanfriendly: true
nodeid: 2
nodename: 'Node2'
privkey: '4Pb81ZCfhNjIT/fobxsUo4ZZ3fls/g9Py/u6/jpa1Vc='
listenport: 3002
loglevel:
loglevel: "error"
logtransit: true
supernode:
enable: false
pubkeyv4: ''
pubkeyv6: ''
regurlv4: ''
regurlv6: ''
apiurl: ''
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
peers:
- nodeid: 1
pubkey: '51/RzDlzd0vuFUbNMvFeVA/5ZgtUQKb+6HD+C5Ea2jA='
endpoint: '127.0.0.1:3001'
- nodeid: 3
pubkey: '9HsPa7QAgBjvSyW1EBuqGCyZtWdAZHkSIlGraTd4+1E='
endpoint: '127.0.0.1:3003'
- nodeid: 4
pubkey: 'QHX2qo9+qn6hPxQ4/E5J7k07HZaBsD9rRxm90+YqTSA='
endpoint: '127.0.0.1:3004'

70
example_config/n3.yaml Normal file
View File

@ -0,0 +1,70 @@
interface:
itype: 'stdio'
ifaceid: 3
name: 'netcat3'
macaddr: 'BB:AA:CC:DD:EE:03'
mtu: 1500
recvaddr: '127.0.0.1:4003'
sendaddr: '127.0.0.1:5003'
humanfriendly: true
nodeid: 3
nodename: 'Node3'
privkey: '8InEcDezmnCyiLA7HC6/qFMKELb4XHdLd3jIu4Jk7lU='
listenport: 3003
loglevel:
loglevel: "error"
logtransit: true
supernode:
enable: false
pubkeyv4: ''
pubkeyv6: ''
regurlv4: ''
regurlv6: ''
apiurl: ''
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
peers:
- nodeid: 2
pubkey: 'NuYJ/3Ght+C4HovFq5Te/BrIazo6zwDJ8Bdu4rQCz0o='
endpoint: '127.0.0.1:3002'
- nodeid: 4
pubkey: 'QHX2qo9+qn6hPxQ4/E5J7k07HZaBsD9rRxm90+YqTSA='
endpoint: '127.0.0.1:3004'
- nodeid: 5
pubkey: 'q91lHawt/0XcfONJ/gHvGirQlVztTxS0Br3zOpuh60o='
endpoint: '127.0.0.1:3005'

70
example_config/n4.yaml Normal file
View File

@ -0,0 +1,70 @@
interface:
itype: 'stdio'
ifaceid: 4
name: 'netcat4'
macaddr: 'BB:AA:CC:DD:EE:04'
mtu: 1500
recvaddr: '127.0.0.1:4004'
sendaddr: '127.0.0.1:5004'
humanfriendly: true
nodeid: 4
nodename: 'Node4'
privkey: 'qPPlRulMI8NFq+Mp5+dq8j486RdRXCATkmFGlNXawXI='
listenport: 3004
loglevel:
loglevel: "error"
logtransit: true
supernode:
enable: false
pubkeyv4: ''
pubkeyv6: ''
regurlv4: ''
regurlv6: ''
apiurl: ''
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
peers:
- nodeid: 2
pubkey: 'NuYJ/3Ght+C4HovFq5Te/BrIazo6zwDJ8Bdu4rQCz0o='
endpoint: '127.0.0.1:3002'
- nodeid: 3
pubkey: '9HsPa7QAgBjvSyW1EBuqGCyZtWdAZHkSIlGraTd4+1E='
endpoint: '127.0.0.1:3003'
- nodeid: 6
pubkey: 'XwQfrzumgOXkfgkm3n/QR/RdqBGkclGTmtLBgmoboBM='
endpoint: '127.0.0.1:3006'

64
example_config/n5.yaml Normal file
View File

@ -0,0 +1,64 @@
interface:
itype: 'stdio'
ifaceid: 5
name: 'netcat5'
macaddr: 'BB:AA:CC:DD:EE:05'
mtu: 1500
recvaddr: '127.0.0.1:4005'
sendaddr: '127.0.0.1:5005'
humanfriendly: true
nodeid: 5
nodename: 'Node5'
privkey: 'IMW0j2o4cxBqXDPX2sqX8KkXbAHkrLGjklnrM4zh/3s='
listenport: 3005
loglevel:
loglevel: "error"
logtransit: true
supernode:
enable: false
pubkeyv4: ''
pubkeyv6: ''
regurlv4: ''
regurlv6: ''
apiurl: ''
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
peers:
- nodeid: 3
pubkey: '9HsPa7QAgBjvSyW1EBuqGCyZtWdAZHkSIlGraTd4+1E='
endpoint: '127.0.0.1:3003'

64
example_config/n6.yaml Normal file
View File

@ -0,0 +1,64 @@
interface:
itype: 'stdio'
ifaceid: 6
name: 'netcat6'
macaddr: 'BB:AA:CC:DD:EE:06'
mtu: 1500
recvaddr: '127.0.0.1:4006'
sendaddr: '127.0.0.1:5006'
humanfriendly: true
nodeid: 6
nodename: 'Node6'
privkey: 'yFCnbYnsOZoq10+ErLdMwWWBh5PVOUv7G4A5T2AyiUc='
listenport: 3006
loglevel:
loglevel: "error"
logtransit: true
supernode:
enable: false
pubkeyv4: ''
pubkeyv6: ''
regurlv4: ''
regurlv6: ''
apiurl: ''
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
peers:
- nodeid: 4
pubkey: 'QHX2qo9+qn6hPxQ4/E5J7k07HZaBsD9rRxm90+YqTSA='
endpoint: '127.0.0.1:3004'

4
go.mod
View File

@ -3,7 +3,7 @@ module github.com/KusakabeSi/EtherGuardVPN
go 1.16
require (
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
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
)

22
go.sum
View File

@ -1,22 +0,0 @@
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309040221-94ec62e08169 h1:fpeMGRM6A+XFcw4RPCO8s8hH7ppgrGR22pSIjwM7YUI=
golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54 h1:rF3Ohx8DRyl8h2zw9qojyLHLhrJpEMgyPOImREEryf0=
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210402192133-700132347e07 h1:4k6HsQjxj6hVMsI2Vf0yKlzt5lXxZsMW1q0zaq2k8zY=
golang.org/x/sys v0.0.0-20210402192133-700132347e07/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@ -28,6 +28,10 @@ const (
// flag in wireguard-android.
var socketDirectory = "/var/run/wireguard"
func SetsocketDirectory(path string) {
socketDirectory = path
}
func sockPath(iface string) string {
return fmt.Sprintf("%s/%s.sock", socketDirectory, iface)
}

266
main.go
View File

@ -8,17 +8,15 @@
package main
import (
"flag"
"fmt"
"io/ioutil"
"os"
"os/signal"
"runtime"
"strconv"
"syscall"
"github.com/KusakabeSi/EtherGuardVPN/conn"
"github.com/KusakabeSi/EtherGuardVPN/device"
"github.com/KusakabeSi/EtherGuardVPN/ipc"
"github.com/KusakabeSi/EtherGuardVPN/tun"
"github.com/KusakabeSi/EtherGuardVPN/path"
yaml "gopkg.in/yaml.v2"
)
const (
@ -27,243 +25,61 @@ const (
)
const (
ENV_WG_TUN_FD = "WG_TUN_FD"
ENV_WG_UAPI_FD = "WG_UAPI_FD"
ENV_WG_PROCESS_FOREGROUND = "WG_PROCESS_FOREGROUND"
ENV_WP_UAPI_FD = "WP_UAPI_FD"
ENV_WP_UAPI_DIR = "WP_UAPI_DIR"
)
func printUsage() {
fmt.Printf("Usage: %s [-f/--foreground] INTERFACE-NAME\n", os.Args[0])
fmt.Printf("Usage: %s -s/c CONFIG-PATH\n", os.Args[0])
}
func warning() {
switch runtime.GOOS {
case "linux", "freebsd", "openbsd":
if os.Getenv(ENV_WG_PROCESS_FOREGROUND) == "1" {
return
}
default:
func readYaml(filePath string, out interface{}) (err error) {
yamlFile, err := ioutil.ReadFile(filePath)
if err != nil {
return
}
err = yaml.Unmarshal(yamlFile, out)
return
}
fmt.Fprintln(os.Stderr, "┌──────────────────────────────────────────────────────┐")
fmt.Fprintln(os.Stderr, "│ │")
fmt.Fprintln(os.Stderr, "│ Running wireguard-go is not required because this │")
fmt.Fprintln(os.Stderr, "│ kernel has first class support for WireGuard. For │")
fmt.Fprintln(os.Stderr, "│ information on installing the kernel module, │")
fmt.Fprintln(os.Stderr, "│ please visit: │")
fmt.Fprintln(os.Stderr, "│ https://www.wireguard.com/install/ │")
fmt.Fprintln(os.Stderr, "│ │")
fmt.Fprintln(os.Stderr, "└──────────────────────────────────────────────────────┘")
var (
config = flag.String("config", "", "Config path for the interface.")
mode = flag.String("mode", "edge", "Running mode. [super|edge]")
version = flag.Bool("version", false, "Show version")
help = flag.Bool("help", false, "Show this help")
nouapi = flag.Bool("no-uapi", false, "Do not use UAPI")
)
type LoggerInfo struct {
LogLevel string
LogTransit bool
}
func main() {
if len(os.Args) == 2 && os.Args[1] == "--version" {
flag.Parse()
if *version == true {
fmt.Printf("wireguard-go v%s\n\nUserspace WireGuard daemon for %s-%s.\nInformation available at https://www.wireguard.com.\nCopyright (C) Jason A. Donenfeld <Jason@zx2c4.com>.\n", Version, runtime.GOOS, runtime.GOARCH)
return
}
warning()
var foreground bool
var interfaceName string
if len(os.Args) < 2 || len(os.Args) > 3 {
printUsage()
if *help == true {
flag.Usage()
return
}
switch os.Args[1] {
case "-f", "--foreground":
foreground = true
if len(os.Args) != 3 {
printUsage()
return
}
interfaceName = os.Args[2]
uapiDir := os.Getenv(ENV_WP_UAPI_DIR)
if uapiDir != "" {
ipc.SetsocketDirectory(uapiDir)
}
switch *mode {
case "edge":
Edge(*config, !*nouapi)
case "super":
Super(*config, !*nouapi)
case "path":
path.Solve()
default:
foreground = false
if len(os.Args) != 2 {
printUsage()
return
}
interfaceName = os.Args[1]
flag.Usage()
}
if !foreground {
foreground = os.Getenv(ENV_WG_PROCESS_FOREGROUND) == "1"
}
// get log level (default: info)
logLevel := func() int {
switch os.Getenv("LOG_LEVEL") {
case "verbose", "debug":
return device.LogLevelVerbose
case "error":
return device.LogLevelError
case "silent":
return device.LogLevelSilent
}
return device.LogLevelError
}()
// open TUN device (or use supplied fd)
tun, err := func() (tun.Device, error) {
tunFdStr := os.Getenv(ENV_WG_TUN_FD)
if tunFdStr == "" {
return tun.CreateTUN(interfaceName, device.DefaultMTU)
}
// construct tun device from supplied fd
fd, err := strconv.ParseUint(tunFdStr, 10, 32)
if err != nil {
return nil, err
}
err = syscall.SetNonblock(int(fd), true)
if err != nil {
return nil, err
}
file := os.NewFile(uintptr(fd), "")
return tun.CreateTUNFromFile(file, device.DefaultMTU)
}()
if err == nil {
realInterfaceName, err2 := tun.Name()
if err2 == nil {
interfaceName = realInterfaceName
}
}
logger := device.NewLogger(
logLevel,
fmt.Sprintf("(%s) ", interfaceName),
)
logger.Verbosef("Starting wireguard-go version %s", Version)
if err != nil {
logger.Errorf("Failed to create TUN device: %v", err)
os.Exit(ExitSetupFailed)
}
// open UAPI file (or use supplied fd)
fileUAPI, err := func() (*os.File, error) {
uapiFdStr := os.Getenv(ENV_WG_UAPI_FD)
if uapiFdStr == "" {
return ipc.UAPIOpen(interfaceName)
}
// use supplied fd
fd, err := strconv.ParseUint(uapiFdStr, 10, 32)
if err != nil {
return nil, err
}
return os.NewFile(uintptr(fd), ""), nil
}()
if err != nil {
logger.Errorf("UAPI listen error: %v", err)
os.Exit(ExitSetupFailed)
return
}
// daemonize the process
if !foreground {
env := os.Environ()
env = append(env, fmt.Sprintf("%s=3", ENV_WG_TUN_FD))
env = append(env, fmt.Sprintf("%s=4", ENV_WG_UAPI_FD))
env = append(env, fmt.Sprintf("%s=1", ENV_WG_PROCESS_FOREGROUND))
files := [3]*os.File{}
if os.Getenv("LOG_LEVEL") != "" && logLevel != device.LogLevelSilent {
files[0], _ = os.Open(os.DevNull)
files[1] = os.Stdout
files[2] = os.Stderr
} else {
files[0], _ = os.Open(os.DevNull)
files[1], _ = os.Open(os.DevNull)
files[2], _ = os.Open(os.DevNull)
}
attr := &os.ProcAttr{
Files: []*os.File{
files[0], // stdin
files[1], // stdout
files[2], // stderr
tun.File(),
fileUAPI,
},
Dir: ".",
Env: env,
}
path, err := os.Executable()
if err != nil {
logger.Errorf("Failed to determine executable: %v", err)
os.Exit(ExitSetupFailed)
}
process, err := os.StartProcess(
path,
os.Args,
attr,
)
if err != nil {
logger.Errorf("Failed to daemonize: %v", err)
os.Exit(ExitSetupFailed)
}
process.Release()
return
}
device := device.NewDevice(tun, conn.NewDefaultBind(), logger)
logger.Verbosef("Device started")
errs := make(chan error)
term := make(chan os.Signal, 1)
uapi, err := ipc.UAPIListen(interfaceName, fileUAPI)
if err != nil {
logger.Errorf("Failed to listen on uapi socket: %v", err)
os.Exit(ExitSetupFailed)
}
go func() {
for {
conn, err := uapi.Accept()
if err != nil {
errs <- err
return
}
go device.IpcHandle(conn)
}
}()
logger.Verbosef("UAPI listener started")
// wait for program to terminate
signal.Notify(term, syscall.SIGTERM)
signal.Notify(term, os.Interrupt)
select {
case <-term:
case <-errs:
case <-device.Wait():
}
// clean up
uapi.Close()
device.Close()
logger.Verbosef("Shutting down")
return
}

229
main_edge.go Normal file
View File

@ -0,0 +1,229 @@
// +build !windows
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
*/
package main
import (
"encoding/base64"
"fmt"
"net"
"os"
"os/signal"
"strconv"
"syscall"
"github.com/KusakabeSi/EtherGuardVPN/conn"
"github.com/KusakabeSi/EtherGuardVPN/device"
"github.com/KusakabeSi/EtherGuardVPN/ipc"
"github.com/KusakabeSi/EtherGuardVPN/path"
"github.com/KusakabeSi/EtherGuardVPN/tap"
yaml "gopkg.in/yaml.v2"
)
type edgeConfig struct {
Interface InterfaceConf
NodeID path.Vertex
NodeName string
PrivKey string
ListenPort int
LogLevel LoggerInfo
SuperNode SuperInfo
NextHopTable path.NextHopTable
Peers []PeerInfo
}
type InterfaceConf struct {
Itype string
IfaceID int
Name string
MacAddr string
MTU int
RecvAddr string
SendAddr string
HumanFriendly bool
}
type SuperInfo struct {
Enable bool
PubKeyV4 string
PubKeyV6 string
RegURLV4 string
RegURLV6 string
APIUrl string
}
type PeerInfo struct {
NodeID path.Vertex
PubKey string
EndPoint string
}
func printExampleConf() {
var config edgeConfig
config.Peers = make([]PeerInfo, 3)
var g path.IG
g.Init(4)
g.Edge(1, 2, 0.5)
g.Edge(2, 1, 0.5)
g.Edge(2, 3, 0.5)
g.Edge(3, 2, 0.5)
g.Edge(2, 4, 0.5)
g.Edge(4, 2, 0.5)
g.Edge(3, 4, 0.5)
g.Edge(4, 3, 0.5)
g.Edge(5, 3, 0.5)
g.Edge(3, 5, 0.5)
g.Edge(6, 4, 0.5)
g.Edge(4, 6, 0.5)
_, next := path.FloydWarshall(g)
config.NextHopTable = next
test, _ := yaml.Marshal(config)
fmt.Print(string(test))
return
}
func Edge(configPath string, useUAPI bool) (err error) {
var config edgeConfig
//printExampleConf()
//return
err = readYaml(configPath, &config)
if err != nil {
fmt.Printf("Error read config: %s :", configPath)
fmt.Print(err)
return err
}
interfaceName := config.NodeName
var logLevel int
switch config.LogLevel.LogLevel {
case "verbose", "debug":
logLevel = device.LogLevelVerbose
case "error":
logLevel = device.LogLevelError
case "silent":
logLevel = device.LogLevelSilent
}
logger := device.NewLogger(
logLevel,
fmt.Sprintf("(%s) ", interfaceName),
)
logger.Verbosef("Starting wireguard-go version %s", Version)
if err != nil {
logger.Errorf("UAPI listen error: %v", err)
os.Exit(ExitSetupFailed)
return
}
var thetap tap.Device
// open TUN device (or use supplied fd)
switch config.Interface.Itype {
case "dummy":
thetap, err = tap.CreateDummyTAP()
case "stdio":
thetap, err = tap.CreateStdIOTAP(config.Interface.Name, config.Interface.HumanFriendly)
case "udpsock":
{
lis, _ := net.ResolveUDPAddr("udp", config.Interface.RecvAddr)
sen, _ := net.ResolveUDPAddr("udp", config.Interface.SendAddr)
thetap, err = tap.CreateUDPSockTAP(config.Interface.Name, lis, sen, config.Interface.HumanFriendly)
}
}
if err != nil {
logger.Errorf("Failed to create TUN device: %v", err)
os.Exit(ExitSetupFailed)
}
////////////////////////////////////////////////////
// Config
the_device := device.NewDevice(thetap, config.NodeID, conn.NewDefaultBind(), logger)
the_device.LogTransit = config.LogLevel.LogTransit
the_device.NhTable = config.NextHopTable
defer the_device.Close()
var sk [32]byte
sk_slice, _ := base64.StdEncoding.DecodeString(config.PrivKey)
copy(sk[:], sk_slice)
the_device.SetPrivateKey(sk)
the_device.IpcSet("fwmark=0\n")
the_device.IpcSet("listen_port=" + strconv.Itoa(config.ListenPort) + "\n")
the_device.IpcSet("replace_peers=true\n")
for _, peerconf := range config.Peers {
sk_slice, _ = base64.StdEncoding.DecodeString(peerconf.PubKey)
copy(sk[:], sk_slice)
the_device.NewPeer(sk, peerconf.NodeID)
if peerconf.EndPoint != "" {
peer := the_device.LookupPeer(sk)
endpoint, err := the_device.Bind().ParseEndpoint(peerconf.EndPoint)
if err != nil {
logger.Errorf("Failed to set endpoint %v: %w", peerconf.EndPoint, err)
return err
}
peer.SetEndpointFromPacket(endpoint)
}
}
logger.Verbosef("Device started")
errs := make(chan error)
term := make(chan os.Signal, 1)
if useUAPI {
fileUAPI, err := func() (*os.File, error) {
uapiFdStr := os.Getenv(ENV_WP_UAPI_FD)
if uapiFdStr == "" {
return ipc.UAPIOpen(interfaceName)
}
// use supplied fd
fd, err := strconv.ParseUint(uapiFdStr, 10, 32)
if err != nil {
return nil, err
}
return os.NewFile(uintptr(fd), ""), nil
}()
uapi, err := ipc.UAPIListen(interfaceName, fileUAPI)
if err != nil {
logger.Errorf("Failed to listen on uapi socket: %v", err)
os.Exit(ExitSetupFailed)
}
go func() {
for {
conn, err := uapi.Accept()
if err != nil {
errs <- err
return
}
go the_device.IpcHandle(conn)
}
}()
defer uapi.Close()
logger.Verbosef("UAPI listener started")
}
// wait for program to terminate
signal.Notify(term, syscall.SIGTERM)
signal.Notify(term, os.Interrupt)
select {
case <-term:
case <-errs:
case <-the_device.Wait():
}
logger.Verbosef("Shutting down")
return
}

12
main_super.go Normal file
View File

@ -0,0 +1,12 @@
// +build !windows
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
*/
package main
func Super(configPath string, useUAPI bool) {
}

67
path/header.go Normal file
View File

@ -0,0 +1,67 @@
package path
import (
"encoding/binary"
"errors"
)
const EgHeaderLen = 10
type EgHeader struct {
buf []byte
}
type Usage uint8
const (
NornalPacket Usage = iota
PingSingleWay
PingDualWay
)
func NewEgHeader(pac []byte) (e EgHeader, err error) {
if len(pac) != EgHeaderLen {
err = errors.New("Invalid packet size")
return
}
e.buf = pac
return
}
func (e EgHeader) GetDst() Vertex {
return Vertex(binary.BigEndian.Uint32(e.buf[0:4]))
}
func (e EgHeader) SetDst(node_ID Vertex) {
binary.BigEndian.PutUint32(e.buf[0:4], uint32(node_ID))
}
func (e EgHeader) GetSrc() Vertex {
return Vertex(binary.BigEndian.Uint32(e.buf[4:8]))
}
func (e EgHeader) SetSrc(node_ID Vertex) {
binary.BigEndian.PutUint32(e.buf[4:8], uint32(node_ID))
}
func (e EgHeader) GetTTL() uint8 {
return e.buf[8]
}
func (e EgHeader) SetTTL(ttl uint8) {
e.buf[8] = ttl
}
func (e EgHeader) GetUsage() uint8 {
return e.buf[9]
}
func (e EgHeader) SetUsage(usage uint8) {
e.buf[9] = usage
}
/*
func (e EgHeader) GetPacketLength() uint16 {
return binary.BigEndian.Uint16(e.buf[10:12])
}
func (e EgHeader) SetPacketLength(length uint16) {
binary.BigEndian.PutUint16(e.buf[10:12], length)
}
*/

172
path/path.go Normal file
View File

@ -0,0 +1,172 @@
package path
import (
"fmt"
"math"
"time"
yaml "gopkg.in/yaml.v2"
)
var (
timeout = time.Second * 3
)
// A Graph is the interface implemented by graphs that
// this algorithm can run on.
type Graph interface {
Vertices() map[Vertex]bool
Neighbors(v Vertex) []Vertex
Weight(u, v Vertex) float64
}
// Nonnegative integer ID of vertex
type Vertex uint32
const Infinity = 99999
var Boardcast = Vertex(math.MaxUint32)
type Latency struct {
ping float64
time time.Time
}
type DistTable map[Vertex]map[Vertex]float64
type NextHopTable map[Vertex]map[Vertex]*Vertex
type Fullroute struct {
Dist DistTable `json:"total distance"`
Next NextHopTable `json:"next hop"`
}
// IG is a graph of integers that satisfies the Graph interface.
type IG struct {
Vert map[Vertex]bool
Edges map[Vertex]map[Vertex]Latency
}
func (g *IG) Init(num_node int) error {
g.Vert = make(map[Vertex]bool, num_node)
g.Edges = make(map[Vertex]map[Vertex]Latency, num_node)
return nil
}
func (g *IG) Edge(u, v Vertex, w float64) {
g.Vert[u] = true
g.Vert[v] = true
if _, ok := g.Edges[u]; !ok {
g.Edges[u] = make(map[Vertex]Latency)
}
g.Edges[u][v] = Latency{
ping: w,
time: time.Now(),
}
}
func (g IG) Vertices() map[Vertex]bool { return g.Vert }
func (g IG) Neighbors(v Vertex) (vs []Vertex) {
for k := range g.Edges[v] {
vs = append(vs, k)
}
return vs
}
func (g IG) Weight(u, v Vertex) float64 {
if time.Now().Sub(g.Edges[u][v].time) < timeout {
return g.Edges[u][v].ping
}
return Infinity
}
func FloydWarshall(g Graph) (dist DistTable, next NextHopTable) {
vert := g.Vertices()
dist = make(DistTable)
next = make(NextHopTable)
for u, _ := range vert {
dist[u] = make(map[Vertex]float64)
next[u] = make(map[Vertex]*Vertex)
for v, _ := range vert {
dist[u][v] = Infinity
}
dist[u][u] = 0
for _, v := range g.Neighbors(u) {
w := g.Weight(u, v)
if w < Infinity {
v := v
dist[u][v] = w
next[u][v] = &v
}
}
}
for k, _ := range vert {
for i, _ := range vert {
for j, _ := range vert {
if dist[i][k] < Infinity && dist[k][j] < Infinity {
if dist[i][j] > dist[i][k]+dist[k][j] {
dist[i][j] = dist[i][k] + dist[k][j]
next[i][j] = next[i][k]
}
}
}
}
}
return dist, next
}
func Path(u, v Vertex, next NextHopTable) (path []Vertex) {
if next[u][v] == nil {
return []Vertex{}
}
path = []Vertex{u}
for u != v {
u = *next[u][v]
path = append(path, u)
}
return path
}
func GetBoardcastList(id Vertex, nh NextHopTable) (tosend map[Vertex]bool) {
tosend = make(map[Vertex]bool)
for _, element := range nh[id] {
tosend[*element] = true
}
return
}
func GetBoardcastThroughList(id Vertex, src Vertex, nh NextHopTable) (tosend map[Vertex]bool) {
tosend = make(map[Vertex]bool)
for check_id, _ := range GetBoardcastList(id, nh) {
for _, path_node := range Path(src, check_id, nh) {
if path_node == id {
tosend[check_id] = true
continue
}
}
}
return
}
func Solve() {
var g IG
g.Init(4)
g.Edge(1, 2, 0.5)
g.Edge(2, 1, 0.5)
g.Edge(2, 3, 2)
g.Edge(3, 2, 2)
g.Edge(2, 4, 0.7)
g.Edge(4, 2, 2)
dist, next := FloydWarshall(g)
fmt.Println("pair\tdist\tpath")
for u, m := range dist {
for v, d := range m {
if u != v {
fmt.Printf("%d -> %d\t%3f\t%s\n", u, v, d, fmt.Sprint(Path(u, v, next)))
}
}
}
fmt.Print("Finish")
rr, _ := yaml.Marshal(Fullroute{
Dist: dist,
Next: next,
})
fmt.Print(string(rr))
}

190
server.go Normal file
View File

@ -0,0 +1,190 @@
package main
import (
"encoding/json"
"fmt"
"net"
"strconv"
"net/http"
"github.com/KusakabeSi/EtherGuardVPN/path"
)
type client struct {
ConnV4 net.Addr
ConnV6 net.Addr
InterV4 []net.Addr
InterV6 []net.Addr
notify4 string
notify6 string
}
type action struct {
Action string `json:"a"`
Node_ID int `json:"id"`
Name string `json:"n"`
}
type serverConf struct {
UDP_port int `json:"port"`
CONN_url string `json:"url"`
USE_Oneway bool `json:"use_oneway"`
}
type pathLentancy struct {
NodeID_S int `json:"src"`
NodeID_E int `json:"dst"`
Latency float64 `json:"ping"`
Is_Oneway bool `json:"oneway"`
}
var (
clients = []client{}
graph = path.IG{}
node_num = 10
udp_port = 9595
http_port = 9595
)
func (c *client) hasV4() bool {
return c.ConnV4.String() == ""
}
func (c *client) hasV6() bool {
return c.ConnV6.String() == ""
}
func (c *client) online() bool {
return c.hasV4() || c.hasV6()
}
func serv(conn net.Conn, version int) {
buffer := make([]byte, 1024)
_, err := conn.Read(buffer)
if err != nil {
fmt.Println(err)
}
incoming := string(buffer)
fmt.Println("[INCOMING]", conn.RemoteAddr(), incoming)
theaction := action{}
err = json.Unmarshal(buffer, &theaction)
if err != nil {
fmt.Println("[Error]", err)
return
}
if theaction.Action != "register" {
fmt.Println("[Error]", "Unknow action", theaction.Action)
return
}
if version == 4 {
clients[theaction.Node_ID].ConnV4 = conn.RemoteAddr()
} else if version == 6 {
clients[theaction.Node_ID].ConnV6 = conn.RemoteAddr()
}
conn.Write([]byte("OK"))
err = conn.Close()
if err != nil {
fmt.Println("[Error]", err)
fmt.Println(err)
}
}
func accept(listener net.Listener, version int) {
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println(err)
continue
}
serv(conn, version)
}
}
// Server --
func RegisterServer() {
/*
graph.Init(node_num)
clients = make([]client, node_num)
addr4 := &net.UDPAddr{
IP: net.IPv4zero,
Port: udp_port,
}
addr6 := &net.UDPAddr{
IP: net.IPv6zero,
Port: udp_port,
}
// Connect to a DTLS server
listener4, err4 := dtls.Listen("udp4", addr4)
if err4 != nil {
fmt.Println(err)
}
defer listener4.Close()
listener6, err6 := dtls.Listen("udp6", addr6)
if err6 != nil {
fmt.Println(err)
}
defer listener6.Close()
if err4 != nil && err6 != nil {
fmt.Println("udp4 and udp6 both failed!")
return
}
go accept(listener4, 4)
go accept(listener6, 6)*/
}
func get_config(w http.ResponseWriter, r *http.Request) {
rr, _ := json.Marshal(serverConf{
UDP_port: udp_port,
CONN_url: "https://example.com",
})
w.WriteHeader(http.StatusOK)
w.Write(rr)
}
func get_neighbor(w http.ResponseWriter, r *http.Request) {
rr, _ := json.Marshal(clients)
w.WriteHeader(http.StatusOK)
w.Write(rr)
}
func get_route(w http.ResponseWriter, r *http.Request) {
dist, next := path.FloydWarshall(graph)
rr, _ := json.Marshal(path.Fullroute{
Dist: dist,
Next: next,
})
w.WriteHeader(http.StatusOK)
w.Write(rr)
}
func post_latency(w http.ResponseWriter, r *http.Request) {
body := make([]byte, r.ContentLength)
info := pathLentancy{}
err := json.Unmarshal(body, &info)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(fmt.Sprint(err)))
return
}
if info.Is_Oneway {
graph.Edge(path.Vertex(info.NodeID_S), path.Vertex(info.NodeID_E), info.Latency)
} else {
graph.Edge(path.Vertex(info.NodeID_S), path.Vertex(info.NodeID_E), info.Latency/2)
graph.Edge(path.Vertex(info.NodeID_E), path.Vertex(info.NodeID_S), info.Latency/2)
}
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
}
func HttpServer() {
mux := http.NewServeMux()
mux.HandleFunc("/api/neighbor/", get_neighbor)
mux.HandleFunc("/api/route/", get_route)
mux.HandleFunc("/api/latency/", post_latency)
go http.ListenAndServe(":"+strconv.Itoa(http_port), mux)
}

50
tap/tap.go Normal file
View File

@ -0,0 +1,50 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
*/
package tap
import (
"bytes"
"os"
)
type Event int
type MacAddress [6]uint8
func GetDstMacAddr(packet []byte) (dstMacAddr MacAddress) {
copy(dstMacAddr[:], packet[0:6])
return
}
func GetSrcMacAddr(packet []byte) (srcMacAddr MacAddress) {
copy(srcMacAddr[:], packet[6:12])
return
}
func IsBoardCast(mac_in MacAddress) bool {
if bytes.Equal(mac_in[:], []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}) {
return true
} else if bytes.Equal(mac_in[0:2], []byte{0x33, 0x33}) {
return true
}
return false
}
const (
EventUp = 1 << iota
EventDown
EventMTUUpdate
)
type Device interface {
File() *os.File // returns the file descriptor of the device
Read([]byte, int) (int, error) // read a packet from the device (without any additional headers)
Write([]byte, int) (int, error) // writes a packet to the device (without any additional headers)
Flush() error // flush all previous writes to the device
MTU() (int, error) // returns the MTU of the device
Name() (string, error) // fetches and returns the current name
Events() chan Event // returns a constant channel of events related to the device
Close() error // stops the device and closes the event channel
}

53
tap/tap_dummy.go Normal file
View File

@ -0,0 +1,53 @@
package tap
import (
"os"
)
type DummyTap struct {
stopRead chan struct{}
events chan Event
}
// New creates and returns a new TUN interface for the application.
func CreateDummyTAP() (tapdev Device, err error) {
// Setup TUN Config
tapdev = &DummyTap{
stopRead: make(chan struct{}, 1<<5),
events: make(chan Event, 1<<5),
}
tapdev.Events() <- EventUp
return
}
// SetMTU sets the Maximum Tansmission Unit Size for a
// Packet on the interface.
func (tap *DummyTap) File() *os.File {
var tapFile *os.File
return tapFile
} // returns the file descriptor of the device
func (tap *DummyTap) Read([]byte, int) (int, error) {
_ = <-tap.stopRead
return 0, nil
} // read a packet from the device (without any additional headers)
func (tap *DummyTap) Write(packet []byte, size int) (int, error) {
return size, nil
} // writes a packet to the device (without any additional headers)
func (tap *DummyTap) Flush() error {
return nil
} // flush all previous writes to the device
func (tap *DummyTap) MTU() (int, error) {
return 1500, nil
} // returns the MTU of the device
func (tap *DummyTap) Name() (string, error) {
return "DummyDevice", nil
} // fetches and returns the current name
func (tap *DummyTap) Events() chan Event {
return tap.events
} // returns a constant channel of events related to the device
func (tap *DummyTap) Close() error {
tap.events <- EventDown
close(tap.events)
return nil
} // stops the device and closes the event channel

97
tap/tap_stdio.go Normal file
View File

@ -0,0 +1,97 @@
package tap
import (
"fmt"
"os"
)
type StdIOTap struct {
name string
mtu int
HumanFriendly bool
events chan Event
}
func Charform2mac(b byte) MacAddress {
if b == 'b' {
return MacAddress{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
}
return MacAddress{0xff, 0xff, 0xff, 0xff, 0xff, b - 48}
}
func Mac2charForm(m []byte) byte {
var M MacAddress
copy(M[:], m)
if IsBoardCast(M) {
return 'b'
}
return m[5] + 48
}
// New creates and returns a new TUN interface for the application.
func CreateStdIOTAP(interfaceName string, HumanFriendly bool) (tapdev Device, err error) {
// Setup TUN Config
if err != nil {
fmt.Println(err.Error())
}
tapdev = &StdIOTap{
name: interfaceName,
mtu: 1500,
HumanFriendly: HumanFriendly,
events: make(chan Event, 1<<5),
}
tapdev.Events() <- EventUp
return
}
// SetMTU sets the Maximum Tansmission Unit Size for a
// Packet on the interface.
func (tap *StdIOTap) File() *os.File {
var tapFile *os.File
return tapFile
} // returns the file descriptor of the device
func (tap *StdIOTap) Read(buf []byte, offset int) (int, error) {
if tap.HumanFriendly {
size, err := os.Stdin.Read(buf[offset+10:])
packet := buf[offset:]
src := Charform2mac(packet[11])
dst := Charform2mac(packet[10])
copy(packet[0:6], dst[:])
copy(packet[6:12], src[:])
return size - 2 + 12, err
} else {
size, err := os.Stdin.Read(buf[offset:])
return size, err
}
} // read a packet from the device (without any additional headers)
func (tap *StdIOTap) Write(buf []byte, offset int) (size int, err error) {
packet := buf[offset:]
if tap.HumanFriendly {
src := Mac2charForm(packet[6:12])
dst := Mac2charForm(packet[0:6])
packet[10] = dst
packet[11] = src
packet = packet[10:]
}
size, err = os.Stdout.Write(packet)
return
} // writes a packet to the device (without any additional headers)
func (tap *StdIOTap) Flush() error {
return nil
} // flush all previous writes to the device
func (tap *StdIOTap) MTU() (int, error) {
return tap.mtu, nil
} // returns the MTU of the device
func (tap *StdIOTap) Name() (string, error) {
return tap.name, nil
} // fetches and returns the current name
func (tap *StdIOTap) Events() chan Event {
return tap.events
} // returns a constant channel of events related to the device
func (tap *StdIOTap) Close() error {
tap.events <- EventDown
os.Stdin.Close()
close(tap.events)
return nil
} // stops the device and closes the event channel

91
tap/tap_udpsock.go Normal file
View File

@ -0,0 +1,91 @@
package tap
import (
"fmt"
"net"
"os"
)
type UdpSockTap struct {
name string
mtu int
recv *net.UDPConn
send *net.UDPAddr
HumanFriendly bool
events chan Event
}
// New creates and returns a new TUN interface for the application.
func CreateUDPSockTAP(interfaceName string, listenAddr *net.UDPAddr, sendAddr *net.UDPAddr, HumanFriendly bool) (tapdev Device, err error) {
// Setup TUN Config
listener, err := net.ListenUDP("udp", listenAddr)
if err != nil {
fmt.Println(err.Error())
}
tapdev = &UdpSockTap{
name: interfaceName,
mtu: 1500,
recv: listener,
send: sendAddr,
HumanFriendly: HumanFriendly,
events: make(chan Event, 1<<5),
}
tapdev.Events() <- EventUp
return
}
// SetMTU sets the Maximum Tansmission Unit Size for a
// Packet on the interface.
func (tap *UdpSockTap) File() *os.File {
var tapFile *os.File
return tapFile
} // returns the file descriptor of the device
func (tap *UdpSockTap) Read(buf []byte, offset int) (int, error) {
if tap.HumanFriendly {
size, _, err := tap.recv.ReadFromUDP(buf[offset+10:])
packet := buf[offset:]
src := Charform2mac(packet[11])
dst := Charform2mac(packet[10])
copy(packet[0:6], dst[:])
copy(packet[6:12], src[:])
return size - 2 + 12, err
} else {
size, _, err := tap.recv.ReadFromUDP(buf[offset:])
return size, err
}
} // read a packet from the device (without any additional headers)
func (tap *UdpSockTap) Write(buf []byte, offset int) (size int, err error) {
packet := buf[offset:]
if tap.HumanFriendly {
src := Mac2charForm(packet[6:12])
dst := Mac2charForm(packet[0:6])
packet[10] = dst
packet[11] = src
size, err = tap.recv.WriteToUDP(packet[10:], tap.send)
return
}
size, err = tap.recv.WriteToUDP(packet, tap.send)
return
} // writes a packet to the device (without any additional headers)
func (tap *UdpSockTap) Flush() error {
return nil
} // flush all previous writes to the device
func (tap *UdpSockTap) MTU() (int, error) {
return tap.mtu, nil
} // returns the MTU of the device
func (tap *UdpSockTap) Name() (string, error) {
return tap.name, nil
} // fetches and returns the current name
func (tap *UdpSockTap) Events() chan Event {
return tap.events
} // returns a constant channel of events related to the device
func (tap *UdpSockTap) Close() error {
tap.events <- EventDown
tap.recv.Close()
close(tap.events)
return nil
} // stops the device and closes the event channel