LinuxTap, not test yet

This commit is contained in:
KusakabeSi 2021-08-24 08:43:55 +00:00
parent 26ba4dbe94
commit d717d35f64
32 changed files with 988 additions and 208 deletions

3
.vscode/launch.json vendored
View File

@ -10,7 +10,8 @@
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}",
"args":["-config","example_config/p2p_mode/n1.yaml","-mode","edge"/*,"-example"*/],
"env": {"CGO_CFLAGS":"-I/usr/include/memif"},
"args":["-config","example_config/static_mode/n1.yaml","-mode","edge"/*,"-example"*/],
}
]
}

View File

@ -13,6 +13,7 @@ type EdgeConfig struct {
LogLevel LoggerInfo
DynamicRoute DynamicRouteInfo
NextHopTable NextHopTable
ResetConnInterval float64
Peers []PeerInfo
}
@ -32,11 +33,11 @@ type InterfaceConf struct {
Name string
VPPIfaceID uint32
VPPBridgeID uint32
MacAddr string
MacAddrPrefix string
MTU int
RecvAddr string
SendAddr string
DevFriendly bool
L2HeaderMode string
}
type PeerInfo struct {

View File

@ -7,6 +7,7 @@ package device
import (
"encoding/base64"
"errors"
"runtime"
"sync"
"sync/atomic"
@ -69,9 +70,12 @@ type Device struct {
Peer_state [32]byte
}
event_tryendpoint chan struct{}
ResetConnInterval float64
EdgeConfigPath string
EdgeConfig *config.EdgeConfig
SuperConfigPath string
SuperConfig *config.SuperConfig
Event_server_register chan path.RegisterMsg
Event_server_pong chan path.PongMsg
@ -304,7 +308,7 @@ func (device *Device) SetPrivateKey(sk NoisePrivateKey) error {
return nil
}
func NewDevice(tapDevice tap.Device, id config.Vertex, bind conn.Bind, logger *Logger, graph *path.IG, IsSuperNode bool, theconfigpath string, econfig *config.EdgeConfig, sconfig *config.SuperConfig, superevents *path.SUPER_Events) *Device {
func NewDevice(tapDevice tap.Device, id config.Vertex, bind conn.Bind, logger *Logger, graph *path.IG, IsSuperNode bool, configpath string, econfig *config.EdgeConfig, sconfig *config.SuperConfig, superevents *path.SUPER_Events) *Device {
device := new(Device)
device.state.state = uint32(deviceStateDown)
device.closed = make(chan struct{})
@ -333,9 +337,11 @@ func NewDevice(tapDevice tap.Device, id config.Vertex, bind conn.Bind, logger *L
device.Event_server_NhTable_changed = superevents.Event_server_NhTable_changed
device.LogTransit = sconfig.LogLevel.LogTransit
device.LogControl = sconfig.LogLevel.LogControl
device.SuperConfig = sconfig
device.SuperConfigPath = configpath
go device.RoutineRecalculateNhTable()
} else {
device.EdgeConfigPath = theconfigpath
device.EdgeConfigPath = configpath
device.EdgeConfig = econfig
device.DRoute = econfig.DynamicRoute
device.DupData = *fixed_time_cache.NewCache(path.S2TD(econfig.DynamicRoute.DupCheckTimeout), false, path.S2TD(60))
@ -343,11 +349,13 @@ func NewDevice(tapDevice tap.Device, id config.Vertex, bind conn.Bind, logger *L
device.Event_save_config = make(chan struct{}, 1<<5)
device.LogTransit = econfig.LogLevel.LogTransit
device.LogControl = econfig.LogLevel.LogControl
device.ResetConnInterval = device.EdgeConfig.ResetConnInterval
go device.RoutineSetEndpoint()
go device.RoutineRegister()
go device.RoutineSendPing()
go device.RoutineRecalculateNhTable()
go device.RoutineSpreadAllMyNeighbor()
go device.RoutineResetConn()
}
// create queues
@ -374,6 +382,29 @@ func NewDevice(tapDevice tap.Device, id config.Vertex, bind conn.Bind, logger *L
return device
}
func (device *Device) LookupPeerIDAtConfig(pk NoisePublicKey) (ID config.Vertex, err error) {
var peerlist []config.PeerInfo
if device.IsSuperNode {
if device.SuperConfig == nil {
return 0, errors.New("Superconfig is nil")
}
peerlist = device.SuperConfig.Peers
} else {
if device.EdgeConfig == nil {
return 0, errors.New("EdgeConfig is nil")
}
peerlist = device.EdgeConfig.Peers
}
pkstr := PubKey2Str(pk)
for _, peerinfo := range peerlist {
if peerinfo.PubKey == pkstr {
return peerinfo.NodeID, nil
}
}
return 0, errors.New("Peer not found in the config file.")
}
func (device *Device) LookupPeer(pk NoisePublicKey) *Peer {
device.peers.RLock()
defer device.peers.RUnlock()

View File

@ -36,6 +36,8 @@ type Peer struct {
ID config.Vertex
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
// These fields are accessed with atomic operations, which must be
// 64-bit aligned even on 32-bit platforms. Go guarantees that an
@ -330,15 +332,16 @@ func (device *Device) SaveToConfig(peer *Peer, endpoint conn.Endpoint) {
if device.IsSuperNode { //Can't in super mode
return
}
if peer.StaticConn == true { //static conn do not write new endpoint to config
return
}
if !device.DRoute.P2P.UseP2P { //Must in p2p mode
return
}
if peer.endpoint != nil && peer.endpoint.DstIP().Equal(endpoint.DstIP()) { //endpoint changed
return
}
if peer.LastPingReceived.Add(path.S2TD(device.DRoute.P2P.PeerAliveTimeout)).After(time.Now()) { //Peer alives
return
}
url := endpoint.DstToString()
foundInFile := false
pubkeystr := PubKey2Str(peer.handshake.remoteStatic)

View File

@ -548,7 +548,7 @@ func (peer *Peer) RoutineSequentialReceiver() {
goto skip
}
src_macaddr := tap.GetSrcMacAddr(elem.packet[path.EgHeaderLen:])
if !tap.IsBoardCast(src_macaddr) {
if !tap.IsNotUnicast(src_macaddr) {
device.l2fib.Store(src_macaddr, src_nodeID) // Write to l2fib table
}
_, err = device.tap.device.Write(elem.buffer[:MessageTransportOffsetContent+len(elem.packet)], MessageTransportOffsetContent+path.EgHeaderLen)

View File

@ -142,7 +142,7 @@ func (device *Device) process_received(msg_type path.Usage, peer *Peer, body []b
}
case path.BoardcastPeer:
if content, err := path.ParseBoardcastPeerMsg(body); err == nil {
return device.process_BoardcastPeerMsg(content)
return device.process_BoardcastPeerMsg(peer, content)
}
default:
err = errors.New("Not a valid msg_type")
@ -245,7 +245,6 @@ func (device *Device) process_pong(peer *Peer, content path.PongMsg) error {
header.SetPacketLength(uint16(len(body)))
copy(buf[path.EgHeaderLen:], body)
device.SendPacket(peer, buf, MessageTransportOffsetContent)
peer.AskedForNeighbor = true
}
}
return nil
@ -434,12 +433,35 @@ func (device *Device) RoutineSpreadAllMyNeighbor() {
}
for {
device.process_RequestPeerMsg(path.QueryPeerMsg{
Request_ID: 0,
Request_ID: uint32(path.Boardcast),
})
time.Sleep(path.S2TD(device.DRoute.P2P.SendPeerInterval))
}
}
func (device *Device) RoutineResetConn() {
if device.ResetConnInterval <= 0.01 {
return
}
for {
for _, peer := range device.peers.keyMap {
if peer.StaticConn {
continue
}
if peer.ConnURL == "" {
continue
}
endpoint, err := device.Bind().ParseEndpoint(peer.ConnURL)
if err != nil {
device.log.Errorf("Failed to bind "+peer.ConnURL, err)
continue
}
peer.SetEndpointFromPacket(endpoint)
}
time.Sleep(time.Duration(device.ResetConnInterval))
}
}
func (device *Device) GeneratePingPacket(src_nodeID config.Vertex) ([]byte, error) {
body, err := path.GetByte(&path.PingMsg{
Src_nodeID: src_nodeID,
@ -531,9 +553,12 @@ func (device *Device) process_RequestPeerMsg(content path.QueryPeerMsg) error {
return nil
}
func (device *Device) process_BoardcastPeerMsg(content path.BoardcastPeerMsg) error {
func (device *Device) process_BoardcastPeerMsg(peer *Peer, content path.BoardcastPeerMsg) error {
if device.DRoute.P2P.UseP2P {
var sk NoisePublicKey
if content.Request_ID == uint32(device.ID) {
peer.AskedForNeighbor = true
}
if bytes.Equal(content.PubKey[:], device.staticIdentity.publicKey[:]) {
return nil
}

View File

@ -250,7 +250,7 @@ func (device *Device) RoutineReadFromTUN() {
dst_nodeID := EgBody.GetDst()
dstMacAddr := tap.GetDstMacAddr(elem.packet[path.EgHeaderLen:])
// lookup peer
if tap.IsBoardCast(dstMacAddr) {
if tap.IsNotUnicast(dstMacAddr) {
dst_nodeID = path.Boardcast
} else if val, ok := device.l2fib.Load(dstMacAddr); !ok { //Lookup failed
dst_nodeID = path.Boardcast

View File

@ -10,7 +10,6 @@ import (
"bytes"
"errors"
"fmt"
"hash/crc32"
"io"
"net"
"strconv"
@ -19,7 +18,6 @@ import (
"sync/atomic"
"time"
"github.com/KusakabeSi/EtherGuardVPN/config"
"github.com/KusakabeSi/EtherGuardVPN/ipc"
)
@ -268,8 +266,6 @@ 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()
@ -284,7 +280,11 @@ func (device *Device) handlePublicKeyLine(peer *ipcSetPeer, value string) error
peer.created = peer.Peer == nil
if peer.created {
peer.Peer, err = device.NewPeer(publicKey, config.Vertex(h.Sum32()))
id, err := device.LookupPeerIDAtConfig(publicKey)
if err != nil {
return errors.New("Create new peer by UAPI is not implemented")
}
peer.Peer, err = device.NewPeer(publicKey, id)
if err != nil {
return ipcErrorf(ipc.IpcErrorInvalid, "failed to create new peer: %w", err)
}

View File

@ -1,18 +1,19 @@
interface:
itype: stdio
ifaceid: 1
name: tap1
macaddr: AA:BB:CC:DD:EE:01
vppifaceid: 1
vppbridgeid: 0
macaddrprefix: AA:BB:CC:DD:EE
mtu: 1400
recvaddr: 127.0.0.1:4001
sendaddr: 127.0.0.1:5001
devfriendly: true
l2headermode: kbdbg
nodeid: 1
nodename: Node01
privkey: aABzjKhWdkFfQ29ZuijtMp1h1TNJe66SDCwvfmvQznw=
listenport: 3001
loglevel:
loglevel: normal`
loglevel: normal
logtransit: true
logcontrol: true
dynamicroute:
@ -23,9 +24,9 @@ dynamicroute:
supernode:
usesupernode: false
connurlv4: 127.0.0.1:3000
pubkeyv4: j8i4dY1i7CUqd/ftaCSfCWosnURiztM+ExI7QRezU2Y=
pubkeyv4: LJ8KKacUcIoACTGB/9Ed9w0osrJ3WWeelzpL2u4oUic=
connurlv6: '[::1]:3000'
pubkeyv6: cCcPlZw0hVkPSi15G+jpJpKE3TdCVEtO1nSiaedukGw=
pubkeyv6: HCfL6YJtpJEGHTlJ2LgVXIWKB/K95P57LHTJ42ZG8VI=
apiurl: http://127.0.0.1:3000/api
supernodeinfotimeout: 40
p2p:
@ -93,6 +94,7 @@ nexthoptable:
3: 4
4: 4
5: 4
resetconninterval: 86400
peers:
- nodeid: 2
pubkey: csT+hco4Jpa7btMeC9subHk2ZqzxcljcBk/57V0cSEk=

View File

@ -1,12 +1,13 @@
interface:
itype: stdio
ifaceid: 2
name: tap2
macaddr: AA:BB:CC:DD:EE:02
vppifaceid: 2
vppbridgeid: 0
macaddrprefix: AA:BB:CC:DD:EE
mtu: 1400
recvaddr: 127.0.0.1:4002
sendaddr: 127.0.0.1:5002
devfriendly: true
l2headermode: kbdbg
nodeid: 2
nodename: Node02
privkey: UNZMzPX5fG/8yGC8edVj/ksF9N6ARRqdq7fqE/PD7ls=
@ -23,9 +24,9 @@ dynamicroute:
supernode:
usesupernode: false
connurlv4: 127.0.0.1:3000
pubkeyv4: j8i4dY1i7CUqd/ftaCSfCWosnURiztM+ExI7QRezU2Y=
pubkeyv4: LJ8KKacUcIoACTGB/9Ed9w0osrJ3WWeelzpL2u4oUic=
connurlv6: '[::1]:3000'
pubkeyv6: cCcPlZw0hVkPSi15G+jpJpKE3TdCVEtO1nSiaedukGw=
pubkeyv6: HCfL6YJtpJEGHTlJ2LgVXIWKB/K95P57LHTJ42ZG8VI=
apiurl: http://127.0.0.1:3000/api
supernodeinfotimeout: 40
p2p:
@ -93,6 +94,7 @@ nexthoptable:
3: 4
4: 4
5: 4
resetconninterval: 86400
peers:
- nodeid: 1
pubkey: CooSkIP7/wiC7Rh83UYnB2yPkJijkNFmhtorHtyYlzY=

View File

@ -1,12 +1,13 @@
interface:
itype: stdio
ifaceid: 3
name: tap3
macaddr: AA:BB:CC:DD:EE:03
vppifaceid: 3
vppbridgeid: 0
macaddrprefix: AA:BB:CC:DD:EE
mtu: 1400
recvaddr: 127.0.0.1:4003
sendaddr: 127.0.0.1:5003
devfriendly: true
l2headermode: kbdbg
nodeid: 3
nodename: Node03
privkey: gJy35nbsd8FuuxyWHjsefN+U+oM7RkuIB1EanNLSVHg=
@ -23,9 +24,9 @@ dynamicroute:
supernode:
usesupernode: false
connurlv4: 127.0.0.1:3000
pubkeyv4: j8i4dY1i7CUqd/ftaCSfCWosnURiztM+ExI7QRezU2Y=
pubkeyv4: LJ8KKacUcIoACTGB/9Ed9w0osrJ3WWeelzpL2u4oUic=
connurlv6: '[::1]:3000'
pubkeyv6: cCcPlZw0hVkPSi15G+jpJpKE3TdCVEtO1nSiaedukGw=
pubkeyv6: HCfL6YJtpJEGHTlJ2LgVXIWKB/K95P57LHTJ42ZG8VI=
apiurl: http://127.0.0.1:3000/api
supernodeinfotimeout: 40
p2p:
@ -93,6 +94,7 @@ nexthoptable:
3: 4
4: 4
5: 4
resetconninterval: 86400
peers:
- nodeid: 2
pubkey: csT+hco4Jpa7btMeC9subHk2ZqzxcljcBk/57V0cSEk=

View File

@ -1,12 +1,13 @@
interface:
itype: stdio
ifaceid: 4
name: tap4
macaddr: AA:BB:CC:DD:EE:04
vppifaceid: 4
vppbridgeid: 0
macaddrprefix: AA:BB:CC:DD:EE
mtu: 1400
recvaddr: 127.0.0.1:4004
sendaddr: 127.0.0.1:5004
devfriendly: true
l2headermode: kbdbg
nodeid: 4
nodename: Node04
privkey: wAdLgCk0SHiO11/aUf9944focD1BUCH5b6Pe+cRHHXQ=
@ -23,9 +24,9 @@ dynamicroute:
supernode:
usesupernode: false
connurlv4: 127.0.0.1:3000
pubkeyv4: j8i4dY1i7CUqd/ftaCSfCWosnURiztM+ExI7QRezU2Y=
pubkeyv4: LJ8KKacUcIoACTGB/9Ed9w0osrJ3WWeelzpL2u4oUic=
connurlv6: '[::1]:3000'
pubkeyv6: cCcPlZw0hVkPSi15G+jpJpKE3TdCVEtO1nSiaedukGw=
pubkeyv6: HCfL6YJtpJEGHTlJ2LgVXIWKB/K95P57LHTJ42ZG8VI=
apiurl: http://127.0.0.1:3000/api
supernodeinfotimeout: 40
p2p:
@ -93,6 +94,7 @@ nexthoptable:
3: 4
4: 4
5: 4
resetconninterval: 86400
peers:
- nodeid: 2
pubkey: csT+hco4Jpa7btMeC9subHk2ZqzxcljcBk/57V0cSEk=

View File

@ -1,12 +1,13 @@
interface:
itype: stdio
ifaceid: 5
name: tap5
macaddr: AA:BB:CC:DD:EE:05
vppifaceid: 5
vppbridgeid: 0
macaddrprefix: AA:BB:CC:DD:EE
mtu: 1400
recvaddr: 127.0.0.1:4005
sendaddr: 127.0.0.1:5005
devfriendly: true
l2headermode: kbdbg
nodeid: 5
nodename: Node05
privkey: gLmzeCbmN/hjiE+ehNXL9IxuG9hhWIYv2s16/DOW6FE=
@ -23,9 +24,9 @@ dynamicroute:
supernode:
usesupernode: false
connurlv4: 127.0.0.1:3000
pubkeyv4: j8i4dY1i7CUqd/ftaCSfCWosnURiztM+ExI7QRezU2Y=
pubkeyv4: LJ8KKacUcIoACTGB/9Ed9w0osrJ3WWeelzpL2u4oUic=
connurlv6: '[::1]:3000'
pubkeyv6: cCcPlZw0hVkPSi15G+jpJpKE3TdCVEtO1nSiaedukGw=
pubkeyv6: HCfL6YJtpJEGHTlJ2LgVXIWKB/K95P57LHTJ42ZG8VI=
apiurl: http://127.0.0.1:3000/api
supernodeinfotimeout: 40
p2p:
@ -93,6 +94,7 @@ nexthoptable:
3: 4
4: 4
5: 4
resetconninterval: 86400
peers:
- nodeid: 3
pubkey: 0meQ0pQCAkLZdpfyMqggpnk0k3UKG2M8jfIMlQjTRWs=

View File

@ -1,12 +1,13 @@
interface:
itype: stdio
ifaceid: 6
name: tap6
macaddr: AA:BB:CC:DD:EE:03
vppifaceid: 6
vppbridgeid: 0
macaddrprefix: AA:BB:CC:DD:EE
mtu: 1400
recvaddr: 127.0.0.1:4006
sendaddr: 127.0.0.1:5006
devfriendly: true
l2headermode: kbdbg
nodeid: 6
nodename: Node06
privkey: IIX5F6oWZUS2dlhxWFJ7TxdJtDCr5jzeuhxUB6YM7Us=
@ -23,9 +24,9 @@ dynamicroute:
supernode:
usesupernode: false
connurlv4: 127.0.0.1:3000
pubkeyv4: j8i4dY1i7CUqd/ftaCSfCWosnURiztM+ExI7QRezU2Y=
pubkeyv4: LJ8KKacUcIoACTGB/9Ed9w0osrJ3WWeelzpL2u4oUic=
connurlv6: '[::1]:3000'
pubkeyv6: cCcPlZw0hVkPSi15G+jpJpKE3TdCVEtO1nSiaedukGw=
pubkeyv6: HCfL6YJtpJEGHTlJ2LgVXIWKB/K95P57LHTJ42ZG8VI=
apiurl: http://127.0.0.1:3000/api
supernodeinfotimeout: 40
p2p:
@ -93,6 +94,7 @@ nexthoptable:
3: 4
4: 4
5: 4
resetconninterval: 86400
peers:
- nodeid: 4
pubkey: 2EfY85KF1S+3dZ3A55eZcyi0QU+sOzOyuADtJs2U2Ww=

View File

@ -1,31 +1,32 @@
interface:
itype: stdio
ifaceid: 1
name: tap1
macaddr: AA:BB:CC:DD:EE:01
vppifaceid: 1
vppbridgeid: 0
macaddrprefix: AA:BB:CC:DD:EE
mtu: 1400
recvaddr: 127.0.0.1:4001
sendaddr: 127.0.0.1:5001
devfriendly: true
l2headermode: kbdbg
nodeid: 1
nodename: Node01
privkey: aABzjKhWdkFfQ29ZuijtMp1h1TNJe66SDCwvfmvQznw=
listenport: 3001
loglevel:
loglevel: normal`
loglevel: normal
logtransit: true
logcontrol: true
dynamicroute:
sendpinginterval: 20
dupchecktimeout: 40
conntimeout: 30
savenewpeers: true
savenewpeers: false
supernode:
usesupernode: false
connurlv4: 127.0.0.1:3000
pubkeyv4: j8i4dY1i7CUqd/ftaCSfCWosnURiztM+ExI7QRezU2Y=
pubkeyv4: LJ8KKacUcIoACTGB/9Ed9w0osrJ3WWeelzpL2u4oUic=
connurlv6: '[::1]:3000'
pubkeyv6: cCcPlZw0hVkPSi15G+jpJpKE3TdCVEtO1nSiaedukGw=
pubkeyv6: HCfL6YJtpJEGHTlJ2LgVXIWKB/K95P57LHTJ42ZG8VI=
apiurl: http://127.0.0.1:3000/api
supernodeinfotimeout: 40
p2p:
@ -93,6 +94,7 @@ nexthoptable:
3: 4
4: 4
5: 4
resetconninterval: 86400
peers:
- nodeid: 2
pubkey: csT+hco4Jpa7btMeC9subHk2ZqzxcljcBk/57V0cSEk=

View File

@ -1,12 +1,13 @@
interface:
itype: stdio
ifaceid: 2
name: tap2
macaddr: AA:BB:CC:DD:EE:02
vppifaceid: 2
vppbridgeid: 0
macaddrprefix: AA:BB:CC:DD:EE
mtu: 1400
recvaddr: 127.0.0.1:4002
sendaddr: 127.0.0.1:5002
devfriendly: true
l2headermode: kbdbg
nodeid: 2
nodename: Node02
privkey: UNZMzPX5fG/8yGC8edVj/ksF9N6ARRqdq7fqE/PD7ls=
@ -19,13 +20,13 @@ dynamicroute:
sendpinginterval: 20
dupchecktimeout: 40
conntimeout: 30
savenewpeers: true
savenewpeers: false
supernode:
usesupernode: false
connurlv4: 127.0.0.1:3000
pubkeyv4: j8i4dY1i7CUqd/ftaCSfCWosnURiztM+ExI7QRezU2Y=
pubkeyv4: LJ8KKacUcIoACTGB/9Ed9w0osrJ3WWeelzpL2u4oUic=
connurlv6: '[::1]:3000'
pubkeyv6: cCcPlZw0hVkPSi15G+jpJpKE3TdCVEtO1nSiaedukGw=
pubkeyv6: HCfL6YJtpJEGHTlJ2LgVXIWKB/K95P57LHTJ42ZG8VI=
apiurl: http://127.0.0.1:3000/api
supernodeinfotimeout: 40
p2p:
@ -93,6 +94,7 @@ nexthoptable:
3: 4
4: 4
5: 4
resetconninterval: 86400
peers:
- nodeid: 1
pubkey: CooSkIP7/wiC7Rh83UYnB2yPkJijkNFmhtorHtyYlzY=

View File

@ -1,12 +1,13 @@
interface:
itype: stdio
ifaceid: 3
name: tap3
macaddr: AA:BB:CC:DD:EE:03
vppifaceid: 3
vppbridgeid: 0
macaddrprefix: AA:BB:CC:DD:EE
mtu: 1400
recvaddr: 127.0.0.1:4003
sendaddr: 127.0.0.1:5003
devfriendly: true
l2headermode: kbdbg
nodeid: 3
nodename: Node03
privkey: gJy35nbsd8FuuxyWHjsefN+U+oM7RkuIB1EanNLSVHg=
@ -19,13 +20,13 @@ dynamicroute:
sendpinginterval: 20
dupchecktimeout: 40
conntimeout: 30
savenewpeers: true
savenewpeers: false
supernode:
usesupernode: false
connurlv4: 127.0.0.1:3000
pubkeyv4: j8i4dY1i7CUqd/ftaCSfCWosnURiztM+ExI7QRezU2Y=
pubkeyv4: LJ8KKacUcIoACTGB/9Ed9w0osrJ3WWeelzpL2u4oUic=
connurlv6: '[::1]:3000'
pubkeyv6: cCcPlZw0hVkPSi15G+jpJpKE3TdCVEtO1nSiaedukGw=
pubkeyv6: HCfL6YJtpJEGHTlJ2LgVXIWKB/K95P57LHTJ42ZG8VI=
apiurl: http://127.0.0.1:3000/api
supernodeinfotimeout: 40
p2p:
@ -93,6 +94,7 @@ nexthoptable:
3: 4
4: 4
5: 4
resetconninterval: 86400
peers:
- nodeid: 2
pubkey: csT+hco4Jpa7btMeC9subHk2ZqzxcljcBk/57V0cSEk=

View File

@ -1,12 +1,13 @@
interface:
itype: stdio
ifaceid: 4
name: tap4
macaddr: AA:BB:CC:DD:EE:04
vppifaceid: 4
vppbridgeid: 0
macaddrprefix: AA:BB:CC:DD:EE
mtu: 1400
recvaddr: 127.0.0.1:4004
sendaddr: 127.0.0.1:5004
devfriendly: true
l2headermode: kbdbg
nodeid: 4
nodename: Node04
privkey: wAdLgCk0SHiO11/aUf9944focD1BUCH5b6Pe+cRHHXQ=
@ -19,13 +20,13 @@ dynamicroute:
sendpinginterval: 20
dupchecktimeout: 40
conntimeout: 30
savenewpeers: true
savenewpeers: false
supernode:
usesupernode: false
connurlv4: 127.0.0.1:3000
pubkeyv4: j8i4dY1i7CUqd/ftaCSfCWosnURiztM+ExI7QRezU2Y=
pubkeyv4: LJ8KKacUcIoACTGB/9Ed9w0osrJ3WWeelzpL2u4oUic=
connurlv6: '[::1]:3000'
pubkeyv6: cCcPlZw0hVkPSi15G+jpJpKE3TdCVEtO1nSiaedukGw=
pubkeyv6: HCfL6YJtpJEGHTlJ2LgVXIWKB/K95P57LHTJ42ZG8VI=
apiurl: http://127.0.0.1:3000/api
supernodeinfotimeout: 40
p2p:
@ -93,6 +94,7 @@ nexthoptable:
3: 4
4: 4
5: 4
resetconninterval: 86400
peers:
- nodeid: 2
pubkey: csT+hco4Jpa7btMeC9subHk2ZqzxcljcBk/57V0cSEk=

View File

@ -1,12 +1,13 @@
interface:
itype: stdio
ifaceid: 5
name: tap5
macaddr: AA:BB:CC:DD:EE:05
vppifaceid: 5
vppbridgeid: 0
macaddrprefix: AA:BB:CC:DD:EE
mtu: 1400
recvaddr: 127.0.0.1:4005
sendaddr: 127.0.0.1:5005
devfriendly: true
l2headermode: kbdbg
nodeid: 5
nodename: Node05
privkey: gLmzeCbmN/hjiE+ehNXL9IxuG9hhWIYv2s16/DOW6FE=
@ -19,13 +20,13 @@ dynamicroute:
sendpinginterval: 20
dupchecktimeout: 40
conntimeout: 30
savenewpeers: true
savenewpeers: false
supernode:
usesupernode: false
connurlv4: 127.0.0.1:3000
pubkeyv4: j8i4dY1i7CUqd/ftaCSfCWosnURiztM+ExI7QRezU2Y=
pubkeyv4: LJ8KKacUcIoACTGB/9Ed9w0osrJ3WWeelzpL2u4oUic=
connurlv6: '[::1]:3000'
pubkeyv6: cCcPlZw0hVkPSi15G+jpJpKE3TdCVEtO1nSiaedukGw=
pubkeyv6: HCfL6YJtpJEGHTlJ2LgVXIWKB/K95P57LHTJ42ZG8VI=
apiurl: http://127.0.0.1:3000/api
supernodeinfotimeout: 40
p2p:
@ -93,6 +94,7 @@ nexthoptable:
3: 4
4: 4
5: 4
resetconninterval: 86400
peers:
- nodeid: 3
pubkey: 0meQ0pQCAkLZdpfyMqggpnk0k3UKG2M8jfIMlQjTRWs=

View File

@ -1,12 +1,13 @@
interface:
itype: stdio
ifaceid: 6
name: tap6
macaddr: AA:BB:CC:DD:EE:03
vppifaceid: 6
vppbridgeid: 0
macaddrprefix: AA:BB:CC:DD:EE
mtu: 1400
recvaddr: 127.0.0.1:4006
sendaddr: 127.0.0.1:5006
devfriendly: true
l2headermode: kbdbg
nodeid: 6
nodename: Node06
privkey: IIX5F6oWZUS2dlhxWFJ7TxdJtDCr5jzeuhxUB6YM7Us=
@ -19,13 +20,13 @@ dynamicroute:
sendpinginterval: 20
dupchecktimeout: 40
conntimeout: 30
savenewpeers: true
savenewpeers: false
supernode:
usesupernode: false
connurlv4: 127.0.0.1:3000
pubkeyv4: j8i4dY1i7CUqd/ftaCSfCWosnURiztM+ExI7QRezU2Y=
pubkeyv4: LJ8KKacUcIoACTGB/9Ed9w0osrJ3WWeelzpL2u4oUic=
connurlv6: '[::1]:3000'
pubkeyv6: cCcPlZw0hVkPSi15G+jpJpKE3TdCVEtO1nSiaedukGw=
pubkeyv6: HCfL6YJtpJEGHTlJ2LgVXIWKB/K95P57LHTJ42ZG8VI=
apiurl: http://127.0.0.1:3000/api
supernodeinfotimeout: 40
p2p:
@ -93,6 +94,7 @@ nexthoptable:
3: 4
4: 4
5: 4
resetconninterval: 86400
peers:
- nodeid: 4
pubkey: 2EfY85KF1S+3dZ3A55eZcyi0QU+sOzOyuADtJs2U2Ww=

View File

@ -1,12 +1,13 @@
interface:
itype: stdio
ifaceid: 1
name: tap1
macaddr: AA:BB:CC:DD:EE:FF
vppifaceid: 1
vppbridgeid: 0
macaddrprefix: AA:BB:CC:DD:EE
mtu: 1400
recvaddr: 127.0.0.1:4001
sendaddr: 127.0.0.1:5001
devfriendly: true
l2headermode: kbdbg
nodeid: 1
nodename: Node01
privkey: 6GyDagZKhbm5WNqMiRHhkf43RlbMJ34IieTlIuvfJ1M=
@ -24,7 +25,7 @@ dynamicroute:
usesupernode: true
connurlv4: 127.0.0.1:3000
pubkeyv4: LJ8KKacUcIoACTGB/9Ed9w0osrJ3WWeelzpL2u4oUic=
connurlv6: '[::1]:2999'
connurlv6: '[::1]:3000'
pubkeyv6: HCfL6YJtpJEGHTlJ2LgVXIWKB/K95P57LHTJ42ZG8VI=
apiurl: http://127.0.0.1:3000/api
supernodeinfotimeout: 40
@ -57,4 +58,5 @@ dynamicroute:
- time.euro.apple.com
- time.windows.com
nexthoptable: {}
resetconninterval: 86400
peers: []

View File

@ -1,14 +1,15 @@
interface:
itype: stdio
ifaceid: 2
name: tap2
macaddr: AA:BB:CC:DD:EE:FF
vppifaceid: 2
vppbridgeid: 0
macaddrprefix: AA:BB:CC:DD:EE
mtu: 1400
recvaddr: 127.0.0.1:4002
sendaddr: 127.0.0.1:5002
devfriendly: true
nodeid: 2
nodename: Node02
l2headermode: kbdbg
nodeid: 1
nodename: Node01
privkey: OH8BsVUU2Rqzeu9B2J5GPG8PUmxWfX8uVvNFZKhVF3o=
listenport: 3002
loglevel:
@ -24,7 +25,7 @@ dynamicroute:
usesupernode: true
connurlv4: 127.0.0.1:3000
pubkeyv4: LJ8KKacUcIoACTGB/9Ed9w0osrJ3WWeelzpL2u4oUic=
connurlv6: '[::1]:2999'
connurlv6: '[::1]:3000'
pubkeyv6: HCfL6YJtpJEGHTlJ2LgVXIWKB/K95P57LHTJ42ZG8VI=
apiurl: http://127.0.0.1:3000/api
supernodeinfotimeout: 40
@ -57,4 +58,5 @@ dynamicroute:
- time.euro.apple.com
- time.windows.com
nexthoptable: {}
resetconninterval: 86400
peers: []

View File

@ -2,14 +2,14 @@ nodename: NodeSuper
privkeyv4: mL5IW0GuqbjgDeOJuPHBU2iJzBPNKhaNEXbIGwwYWWk=
privkeyv6: +EdOKIoBp/EvIusHDsvXhV1RJYbyN3Qr8nxlz35wl3I=
listenport: 3000
repushconfiginterval: 10
loglevel:
loglevel: normal
logtransit: true
logcontrol: true
repushconfiginterval: 30
graphrecalculatesetting:
jittertolerance: 20
jittertolerancemultiplier: 1.1
jittertolerance: 5
jittertolerancemultiplier: 1.01
nodereporttimeout: 40
recalculatecooldown: 5
peers:

15
main.go
View File

@ -25,8 +25,8 @@ const (
)
const (
ENV_WP_UAPI_FD = "WP_UAPI_FD"
ENV_WP_UAPI_DIR = "WP_UAPI_DIR"
ENV_EG_UAPI_FD = "EG_UAPI_FD"
ENV_EG_UAPI_DIR = "EG_UAPI_DIR"
)
func printUsage() {
@ -62,20 +62,25 @@ func main() {
return
}
uapiDir := os.Getenv(ENV_WP_UAPI_DIR)
uapiDir := os.Getenv(ENV_EG_UAPI_DIR)
if uapiDir != "" {
ipc.SetsocketDirectory(uapiDir)
}
var err error
switch *mode {
case "edge":
Edge(*tconfig, !*nouapi, *printExample)
err = Edge(*tconfig, !*nouapi, *printExample)
case "super":
Super(*tconfig, !*nouapi, *printExample)
err = Super(*tconfig, !*nouapi, *printExample)
case "path":
path.Solve()
default:
flag.Usage()
}
if err != nil {
fmt.Fprintf(os.Stderr, "Error :%v\n", err)
os.Exit(1)
}
return
}

View File

@ -31,11 +31,11 @@ func printExampleEdgeConf() {
Itype: "stdio",
VPPIfaceID: 5,
Name: "tap1",
MacAddr: "AA:BB:CC:DD:EE:FF",
MacAddrPrefix: "AA:BB:CC:DD:EE:FF",
MTU: 1400,
RecvAddr: "127.0.0.1:4001",
SendAddr: "127.0.0.1:5001",
DevFriendly: true,
L2HeaderMode: "nochg",
},
NodeID: 1,
NodeName: "Node01",
@ -44,6 +44,7 @@ func printExampleEdgeConf() {
LogLevel: config.LoggerInfo{
LogLevel: "normal",
LogTransit: true,
LogControl: true,
},
DynamicRoute: config.DynamicRouteInfo{
SendPingInterval: 20,
@ -53,9 +54,9 @@ func printExampleEdgeConf() {
SuperNode: config.SuperInfo{
UseSuperNode: true,
ConnURLV4: "127.0.0.1:3000",
PubKeyV4: "j8i4dY1i7CUqd/ftaCSfCWosnURiztM+ExI7QRezU2Y=",
PubKeyV4: "LJ8KKacUcIoACTGB/9Ed9w0osrJ3WWeelzpL2u4oUic=",
ConnURLV6: "[::1]:3000",
PubKeyV6: "cCcPlZw0hVkPSi15G+jpJpKE3TdCVEtO1nSiaedukGw=",
PubKeyV6: "HCfL6YJtpJEGHTlJ2LgVXIWKB/K95P57LHTJ42ZG8VI=",
APIUrl: "http://127.0.0.1:3000/api",
SuperNodeInfoTimeout: 40,
},
@ -91,6 +92,7 @@ func printExampleEdgeConf() {
},
},
NextHopTable: config.NextHopTable{},
ResetConnInterval: 86400,
Peers: []config.PeerInfo{
{
NodeID: 2,
@ -165,13 +167,17 @@ func Edge(configPath string, useUAPI bool, printExample bool) (err error) {
case "dummy":
thetap, err = tap.CreateDummyTAP()
case "stdio":
thetap, err = tap.CreateStdIOTAP(tconfig.Interface.Name, tconfig.Interface.DevFriendly)
tconfig.Interface.VPPIfaceID = uint32(tconfig.NodeID)
thetap, err = tap.CreateStdIOTAP(tconfig.Interface)
case "udpsock":
{
tconfig.Interface.VPPIfaceID = uint32(tconfig.NodeID)
lis, _ := net.ResolveUDPAddr("udp", tconfig.Interface.RecvAddr)
sen, _ := net.ResolveUDPAddr("udp", tconfig.Interface.SendAddr)
thetap, err = tap.CreateUDPSockTAP(tconfig.Interface.Name, lis, sen, tconfig.Interface.DevFriendly)
}
thetap, err = tap.CreateUDPSockTAP(tconfig.Interface, lis, sen)
case "vpp":
thetap, err = tap.CreateVppTAP(tconfig.Interface, tconfig.LogLevel.LogLevel)
default:
return errors.New("Unknow interface type:" + tconfig.Interface.Itype)
}
if err != nil {
logger.Errorf("Failed to create TAP device: %v", err)
@ -206,6 +212,8 @@ func Edge(configPath string, useUAPI bool, printExample bool) (err error) {
logger.Errorf("Failed to set endpoint %v: %w", peerconf.EndPoint, err)
return err
}
peer.StaticConn = peerconf.Static
peer.ConnURL = peerconf.EndPoint
peer.SetEndpointFromPacket(endpoint)
}
}
@ -222,6 +230,8 @@ func Edge(configPath string, useUAPI bool, printExample bool) (err error) {
if err != nil {
return err
}
peer.StaticConn = false
peer.ConnURL = tconfig.DynamicRoute.SuperNode.ConnURLV4
peer.SetEndpointFromPacket(endpoint)
}
if tconfig.DynamicRoute.SuperNode.ConnURLV6 != "" {
@ -235,6 +245,8 @@ func Edge(configPath string, useUAPI bool, printExample bool) (err error) {
if err != nil {
return err
}
peer.StaticConn = false
peer.ConnURL = tconfig.DynamicRoute.SuperNode.ConnURLV6
peer.SetEndpointFromPacket(endpoint)
}
}

View File

@ -34,13 +34,15 @@ import (
func printExampleSuperConf() {
sconfig := config.SuperConfig{
NodeName: "NodeSuper",
PrivKeyV4: "SM8pGjT0r8njy1/7ffN4wMwF7nnJ8UYSjGRWpCqo3ng=",
PrivKeyV6: "SM8pGjT0r8njy1/7ffN4wMwF7nnJ8UYSjGRWpCqo3ng=",
PrivKeyV4: "mL5IW0GuqbjgDeOJuPHBU2iJzBPNKhaNEXbIGwwYWWk=",
PrivKeyV6: "+EdOKIoBp/EvIusHDsvXhV1RJYbyN3Qr8nxlz35wl3I=",
ListenPort: 3000,
LogLevel: config.LoggerInfo{
LogLevel: "normal",
LogTransit: true,
LogControl: true,
},
RePushConfigInterval: 30,
Peers: []config.PeerInfo{
{
NodeID: 2,
@ -50,8 +52,8 @@ func printExampleSuperConf() {
},
},
GraphRecalculateSetting: config.GraphRecalculateSetting{
JitterTolerance: 20,
JitterToleranceMultiplier: 1.1,
JitterTolerance: 5,
JitterToleranceMultiplier: 1.01,
NodeReportTimeout: 40,
RecalculateCoolDown: 5,
},
@ -70,8 +72,7 @@ func Super(configPath string, useUAPI bool, printExample bool) (err error) {
var sconfig config.SuperConfig
err = readYaml(configPath, &sconfig)
if err != nil {
fmt.Printf("Error read config: %s :", configPath)
fmt.Print(err)
fmt.Printf("Error read config: %v\n", configPath)
return err
}
interfaceName := sconfig.NodeName
@ -107,21 +108,29 @@ func Super(configPath string, useUAPI bool, printExample bool) (err error) {
thetap, _ := tap.CreateDummyTAP()
http_graph = path.NewGraph(3, true, sconfig.GraphRecalculateSetting)
http_device4 = device.NewDevice(thetap, path.SuperNodeMessage, conn.NewCustomBind(true, false), logger4, http_graph, true, "", nil, &sconfig, &super_chains)
http_device6 = device.NewDevice(thetap, path.SuperNodeMessage, conn.NewCustomBind(false, true), logger6, http_graph, true, "", nil, &sconfig, &super_chains)
http_device4 = device.NewDevice(thetap, path.SuperNodeMessage, conn.NewCustomBind(true, false), logger4, http_graph, true, configPath, nil, &sconfig, &super_chains)
http_device6 = device.NewDevice(thetap, path.SuperNodeMessage, conn.NewCustomBind(false, true), logger6, http_graph, true, configPath, nil, &sconfig, &super_chains)
defer http_device4.Close()
defer http_device6.Close()
var sk [32]byte
sk_slice, _ := base64.StdEncoding.DecodeString(sconfig.PrivKeyV4)
sk_slice, err := base64.StdEncoding.DecodeString(sconfig.PrivKeyV4)
if err != nil {
fmt.Printf("Can't decode base64:%v\n", sconfig.PrivKeyV4)
return err
}
copy(sk[:], sk_slice)
http_device4.SetPrivateKey(sk)
sk_slice, _ = base64.StdEncoding.DecodeString(sconfig.PrivKeyV6)
sk_slice, err = base64.StdEncoding.DecodeString(sconfig.PrivKeyV6)
if err != nil {
fmt.Printf("Can't decode base64:%v\n", sconfig.PrivKeyV6)
return err
}
copy(sk[:], sk_slice)
http_device6.SetPrivateKey(sk)
http_device4.IpcSet("fwmark=0\n")
http_device6.IpcSet("fwmark=0\n")
http_device4.IpcSet("listen_port=" + strconv.Itoa(sconfig.ListenPort) + "\n")
http_device6.IpcSet("listen_port=" + strconv.Itoa(sconfig.ListenPort-1) + "\n")
http_device6.IpcSet("listen_port=" + strconv.Itoa(sconfig.ListenPort) + "\n")
http_device4.IpcSet("replace_peers=true\n")
http_device6.IpcSet("replace_peers=true\n")
@ -143,8 +152,20 @@ func Super(configPath string, useUAPI bool, printExample bool) (err error) {
PSKey: peerconf.PSKey,
Connurl: make(map[string]bool),
}
peer4, _ := http_device4.NewPeer(pk, peerconf.NodeID)
peer6, _ := http_device6.NewPeer(pk, peerconf.NodeID)
peer4, err := http_device4.NewPeer(pk, peerconf.NodeID)
if err != nil {
fmt.Printf("Error create peer id %v\n", peerconf.NodeID)
return err
}
peer4.StaticConn = true
peer4.ConnURL = peerconf.EndPoint
peer6, err := http_device6.NewPeer(pk, peerconf.NodeID)
if err != nil {
fmt.Printf("Error create peer id %v\n", peerconf.NodeID)
return err
}
peer6.StaticConn = true
peer6.ConnURL = peerconf.EndPoint
if peerconf.PSKey != "" {
var psk device.NoisePresharedKey
psk_slice, err := base64.StdEncoding.DecodeString(peerconf.PSKey)
@ -306,7 +327,7 @@ func PushUpdate() {
func startUAPI(interfaceName string, logger *device.Logger, the_device *device.Device, errs chan error) (net.Listener, error) {
fileUAPI, err := func() (*os.File, error) {
uapiFdStr := os.Getenv(ENV_WP_UAPI_FD)
uapiFdStr := os.Getenv(ENV_EG_UAPI_FD)
if uapiFdStr == "" {
return ipc.UAPIOpen(interfaceName)
}
@ -317,10 +338,14 @@ func startUAPI(interfaceName string, logger *device.Logger, the_device *device.D
}
return os.NewFile(uintptr(fd), ""), nil
}()
if err != nil {
fmt.Printf("Error create UAPI socket \n")
return nil, err
}
uapi, err := ipc.UAPIListen(interfaceName, fileUAPI)
if err != nil {
logger.Errorf("Failed to listen on uapi socket: %v", err)
os.Exit(ExitSetupFailed)
return nil, err
}
go func() {

View File

@ -79,22 +79,29 @@ func NewGraph(num_node int, IsSuperMode bool, theconfig config.GraphRecalculateS
return &g
}
func (g *IG) GetWeightType(x float64) float64 {
func (g *IG) GetWeightType(x float64) (y float64) {
x = math.Abs(x)
y := x
if g.JitterTolerance > 1 && g.JitterToleranceMultiplier > 0.001 {
r := g.JitterTolerance
m := g.JitterToleranceMultiplier
y = math.Pow(math.Ceil(math.Pow(x/m, 1/r)), r) * m
y = x
if g.JitterTolerance > 0.001 && g.JitterToleranceMultiplier > 1 {
t := g.JitterTolerance
r := g.JitterToleranceMultiplier
y = math.Pow(math.Ceil(math.Pow(x/t, 1/r)), r) * t
}
return y
}
func (g *IG) ShouldUpdate(u config.Vertex, v config.Vertex, newval float64) bool {
oldval := g.Weight(u, v) * 1000
newval *= 1000
oldval := math.Abs(g.Weight(u, v) * 1000)
newval = math.Abs(newval * 1000)
if g.IsSuperMode {
return (oldval-newval)*(oldval*g.JitterToleranceMultiplier) >= g.JitterTolerance
if g.JitterTolerance > 0.001 && g.JitterToleranceMultiplier >= 1 {
diff := math.Abs(newval - oldval)
x := math.Max(oldval, newval)
t := g.JitterTolerance
r := g.JitterToleranceMultiplier
return diff > t+x*(r-1) // https://www.desmos.com/calculator/raoti16r5n
}
return oldval == newval
} else {
return g.GetWeightType(oldval) == g.GetWeightType(newval)
}

View File

@ -6,7 +6,10 @@
package tap
import (
"bytes"
"encoding/binary"
"errors"
"strconv"
"strings"
)
type Event int
@ -22,14 +25,45 @@ func GetSrcMacAddr(packet []byte) (srcMacAddr MacAddress) {
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
func GetMacAddr(prefix string, uid uint32) (mac MacAddress, err error) {
macprefix, _, err := prefixStr2prefix(prefix)
if err != nil {
return
}
idbuf := make([]byte, 4)
binary.BigEndian.PutUint32(idbuf, uid)
copy(mac[2:], idbuf)
copy(mac[:], macprefix)
if IsNotUnicast(mac) {
err = errors.New("ERROR: MAC address can only set to unicast address")
return
}
return
}
func prefixStr2prefix(prefix string) ([]uint8, uint32, error) {
hexStrs := strings.Split(strings.ToLower(prefix), ":")
retprefix := make([]uint8, len(hexStrs))
maxID := uint32(1)<<((6-len(hexStrs))*8) - 1
if len(hexStrs) < 2 || len(hexStrs) > 6 {
return []uint8{}, 0, errors.New("Macaddr prefix length must between 2 and 6, " + prefix + " is " + strconv.Itoa(len(hexStrs)))
}
for index, hexstr := range hexStrs {
value, err := strconv.ParseInt(hexstr, 16, 16)
if err != nil {
return []uint8{}, 0, err
}
retprefix[index] = uint8(value)
}
return retprefix, maxID, nil
}
func IsNotUnicast(mac_in MacAddress) bool {
if mac_in[0]&1 == 0 { // Is unicast
return false
}
return true
}
const (
EventUp = 1 << iota

529
tap/tap_linux.go Normal file
View File

@ -0,0 +1,529 @@
/* SPDX-License-Identifier: MIT
*
* Copyright (C) 2017-2021 WireGuard LLC. All Rights Reserved.
*/
package tap
/* Implementation of the TUN device interface for linux
*/
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"os"
"sync"
"syscall"
"time"
"unsafe"
"golang.org/x/sys/unix"
"github.com/KusakabeSi/EtherGuardVPN/config"
"github.com/KusakabeSi/EtherGuardVPN/rwcancel"
)
const (
cloneDevicePath = "/dev/net/tun"
ifReqSize = unix.IFNAMSIZ + 64
)
type NativeTap struct {
tapFile *os.File
index int32 // if index
errors chan error // async error handling
events chan Event // device related events
nopi bool // the device was passed IFF_NO_PI
netlinkSock int
netlinkCancel *rwcancel.RWCancel
hackListenerClosed sync.Mutex
statusListenersShutdown chan struct{}
closeOnce sync.Once
nameOnce sync.Once // guards calling initNameCache, which sets following fields
nameCache string // name of interface
nameErr error
}
func (tap *NativeTap) File() *os.File {
return tap.tapFile
}
func (tap *NativeTap) routineHackListener() {
defer tap.hackListenerClosed.Unlock()
/* This is needed for the detection to work across network namespaces
* If you are reading this and know a better method, please get in touch.
*/
last := 0
const (
up = 1
down = 2
)
for {
sysconn, err := tap.tapFile.SyscallConn()
if err != nil {
return
}
err2 := sysconn.Control(func(fd uintptr) {
_, err = unix.Write(int(fd), nil)
})
if err2 != nil {
return
}
switch err {
case unix.EINVAL:
if last != up {
// If the tunnel is up, it reports that write() is
// allowed but we provided invalid data.
tap.events <- EventUp
last = up
}
case unix.EIO:
if last != down {
// If the tunnel is down, it reports that no I/O
// is possible, without checking our provided data.
tap.events <- EventDown
last = down
}
default:
return
}
select {
case <-time.After(time.Second):
// nothing
case <-tap.statusListenersShutdown:
return
}
}
}
func createNetlinkSocket() (int, error) {
sock, err := unix.Socket(unix.AF_NETLINK, unix.SOCK_RAW, unix.NETLINK_ROUTE)
if err != nil {
return -1, err
}
saddr := &unix.SockaddrNetlink{
Family: unix.AF_NETLINK,
Groups: unix.RTMGRP_LINK | unix.RTMGRP_IPV4_IFADDR | unix.RTMGRP_IPV6_IFADDR,
}
err = unix.Bind(sock, saddr)
if err != nil {
return -1, err
}
return sock, nil
}
func (tap *NativeTap) routineNetlinkListener() {
defer func() {
unix.Close(tap.netlinkSock)
tap.hackListenerClosed.Lock()
close(tap.events)
tap.netlinkCancel.Close()
}()
for msg := make([]byte, 1<<16); ; {
var err error
var msgn int
for {
msgn, _, _, _, err = unix.Recvmsg(tap.netlinkSock, msg[:], nil, 0)
if err == nil || !rwcancel.RetryAfterError(err) {
break
}
if !tap.netlinkCancel.ReadyRead() {
tap.errors <- fmt.Errorf("netlink socket closed: %w", err)
return
}
}
if err != nil {
tap.errors <- fmt.Errorf("failed to receive netlink message: %w", err)
return
}
select {
case <-tap.statusListenersShutdown:
return
default:
}
wasEverUp := false
for remain := msg[:msgn]; len(remain) >= unix.SizeofNlMsghdr; {
hdr := *(*unix.NlMsghdr)(unsafe.Pointer(&remain[0]))
if int(hdr.Len) > len(remain) {
break
}
switch hdr.Type {
case unix.NLMSG_DONE:
remain = []byte{}
case unix.RTM_NEWLINK:
info := *(*unix.IfInfomsg)(unsafe.Pointer(&remain[unix.SizeofNlMsghdr]))
remain = remain[hdr.Len:]
if info.Index != tap.index {
// not our interface
continue
}
if info.Flags&unix.IFF_RUNNING != 0 {
tap.events <- EventUp
wasEverUp = true
}
if info.Flags&unix.IFF_RUNNING == 0 {
// Don't emit EventDown before we've ever emitted EventUp.
// This avoids a startup race with HackListener, which
// might detect Up before we have finished reporting Down.
if wasEverUp {
tap.events <- EventDown
}
}
tap.events <- EventMTUUpdate
default:
remain = remain[hdr.Len:]
}
}
}
}
func (tap *NativeTap) setMacAddr(mac MacAddress) (err error) {
fd, err := unix.Socket(
unix.AF_INET,
unix.SOCK_DGRAM,
0,
)
if err != nil {
return err
}
defer unix.Close(fd)
var ifr [ifReqSize]byte
name, err := tap.Name()
if err != nil {
return err
}
/*
ifreq = struct.pack('16sH6B8x', self.name, AF_UNIX, *macbytes)
fcntl.ioctl(sockfd, SIOCSIFHWADDR, ifreq)
*/
copy(ifr[:16], name)
binary.BigEndian.PutUint16(ifr[16:18], unix.AF_UNIX)
copy(ifr[18:24], mac[:])
copy(ifr[24:32], make([]byte, 8))
_, _, err = unix.Syscall(
unix.SYS_IOCTL,
uintptr(fd),
uintptr(unix.SIOCSIFHWADDR),
uintptr(unsafe.Pointer(&ifr[0])),
)
return
}
func getIFIndex(name string) (int32, error) {
fd, err := unix.Socket(
unix.AF_INET,
unix.SOCK_DGRAM,
0,
)
if err != nil {
return 0, err
}
defer unix.Close(fd)
var ifr [ifReqSize]byte
copy(ifr[:], name)
_, _, errno := unix.Syscall(
unix.SYS_IOCTL,
uintptr(fd),
uintptr(unix.SIOCGIFINDEX),
uintptr(unsafe.Pointer(&ifr[0])),
)
if errno != 0 {
return 0, errno
}
return *(*int32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])), nil
}
func (tap *NativeTap) setMTU(n int) error {
name, err := tap.Name()
if err != nil {
return err
}
// open datagram socket
fd, err := unix.Socket(
unix.AF_INET,
unix.SOCK_DGRAM,
0,
)
if err != nil {
return err
}
defer unix.Close(fd)
// do ioctl call
var ifr [ifReqSize]byte
copy(ifr[:], name)
*(*uint32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = uint32(n)
_, _, errno := unix.Syscall(
unix.SYS_IOCTL,
uintptr(fd),
uintptr(unix.SIOCSIFMTU),
uintptr(unsafe.Pointer(&ifr[0])),
)
if errno != 0 {
return fmt.Errorf("failed to set MTU of TUN device: %w", errno)
}
return nil
}
func (tap *NativeTap) MTU() (int, error) {
name, err := tap.Name()
if err != nil {
return 0, err
}
// open datagram socket
fd, err := unix.Socket(
unix.AF_INET,
unix.SOCK_DGRAM,
0,
)
if err != nil {
return 0, err
}
defer unix.Close(fd)
// do ioctl call
var ifr [ifReqSize]byte
copy(ifr[:], name)
_, _, errno := unix.Syscall(
unix.SYS_IOCTL,
uintptr(fd),
uintptr(unix.SIOCGIFMTU),
uintptr(unsafe.Pointer(&ifr[0])),
)
if errno != 0 {
return 0, fmt.Errorf("failed to get MTU of TUN device: %w", errno)
}
return int(*(*int32)(unsafe.Pointer(&ifr[unix.IFNAMSIZ]))), nil
}
func (tap *NativeTap) Name() (string, error) {
tap.nameOnce.Do(tap.initNameCache)
return tap.nameCache, tap.nameErr
}
func (tap *NativeTap) initNameCache() {
tap.nameCache, tap.nameErr = tap.nameSlow()
}
func (tap *NativeTap) nameSlow() (string, error) {
sysconn, err := tap.tapFile.SyscallConn()
if err != nil {
return "", err
}
var ifr [ifReqSize]byte
var errno syscall.Errno
err = sysconn.Control(func(fd uintptr) {
_, _, errno = unix.Syscall(
unix.SYS_IOCTL,
fd,
uintptr(unix.TUNGETIFF),
uintptr(unsafe.Pointer(&ifr[0])),
)
})
if err != nil {
return "", fmt.Errorf("failed to get name of TUN device: %w", err)
}
if errno != 0 {
return "", fmt.Errorf("failed to get name of TUN device: %w", errno)
}
name := ifr[:]
if i := bytes.IndexByte(name, 0); i != -1 {
name = name[:i]
}
return string(name), nil
}
func (tap *NativeTap) Write(buf []byte, offset int) (int, error) {
buf = buf[offset:]
n, err := tap.tapFile.Write(buf)
if errors.Is(err, syscall.EBADFD) {
err = os.ErrClosed
}
return n, err
}
func (tap *NativeTap) Flush() error {
// TODO: can flushing be implemented by buffering and using sendmmsg?
return nil
}
func (tap *NativeTap) Read(buf []byte, offset int) (n int, err error) {
select {
case err = <-tap.errors:
default:
n, err = tap.tapFile.Read(buf[offset:])
if errors.Is(err, syscall.EBADFD) {
err = os.ErrClosed
}
}
return
}
func (tap *NativeTap) Events() chan Event {
return tap.events
}
func (tap *NativeTap) Close() error {
var err1, err2 error
tap.closeOnce.Do(func() {
if tap.statusListenersShutdown != nil {
close(tap.statusListenersShutdown)
if tap.netlinkCancel != nil {
err1 = tap.netlinkCancel.Cancel()
}
} else if tap.events != nil {
close(tap.events)
}
err2 = tap.tapFile.Close()
})
if err1 != nil {
return err1
}
return err2
}
func CreateTAP(iconfig config.InterfaceConf) (Device, error) {
nfd, err := unix.Open(cloneDevicePath, os.O_RDWR, 0)
if err != nil {
if os.IsNotExist(err) {
return nil, fmt.Errorf("CreateTAP(%q) failed; %s does not exist", iconfig.Name, cloneDevicePath)
}
return nil, err
}
var ifr [ifReqSize]byte
var flags uint16 = unix.IFF_TAP | unix.IFF_NO_PI // (disabled for TUN status hack)
if err != nil {
fmt.Println("ERROR: Failed parse mac address:", iconfig.MacAddrPrefix)
return nil, err
}
nameBytes := []byte(iconfig.Name)
if len(nameBytes) >= unix.IFNAMSIZ {
return nil, fmt.Errorf("interface name too long: %w", unix.ENAMETOOLONG)
}
copy(ifr[:], nameBytes)
*(*uint16)(unsafe.Pointer(&ifr[unix.IFNAMSIZ])) = flags
_, _, errno := unix.Syscall(
unix.SYS_IOCTL,
uintptr(nfd),
uintptr(unix.TUNSETIFF),
uintptr(unsafe.Pointer(&ifr[0])),
)
if errno != 0 {
return nil, errno
}
err = unix.SetNonblock(nfd, true)
// Note that the above -- open,ioctl,nonblock -- must happen prior to handing it to netpoll as below this line.
fd := os.NewFile(uintptr(nfd), cloneDevicePath)
if err != nil {
return nil, err
}
return CreateTAPFromFile(fd, iconfig)
}
func CreateTAPFromFile(file *os.File, iconfig config.InterfaceConf) (Device, error) {
tap := &NativeTap{
tapFile: file,
events: make(chan Event, 5),
errors: make(chan error, 5),
statusListenersShutdown: make(chan struct{}),
nopi: false,
}
name, err := tap.Name()
if err != nil {
return nil, err
}
// start event listener
tap.index, err = getIFIndex(name)
if err != nil {
return nil, err
}
tap.netlinkSock, err = createNetlinkSocket()
if err != nil {
return nil, err
}
tap.netlinkCancel, err = rwcancel.NewRWCancel(tap.netlinkSock)
if err != nil {
unix.Close(tap.netlinkSock)
return nil, err
}
tap.hackListenerClosed.Lock()
go tap.routineNetlinkListener()
go tap.routineHackListener() // cross namespace
err = tap.setMTU(iconfig.MTU)
if err != nil {
unix.Close(tap.netlinkSock)
return nil, err
}
IfMacAddr, err := GetMacAddr(iconfig.MacAddrPrefix, iconfig.VPPIfaceID)
if err != nil {
fmt.Println("ERROR: Failed parse mac address:", iconfig.MacAddrPrefix)
return nil, err
}
err = tap.setMacAddr(IfMacAddr)
if err != nil {
unix.Close(tap.netlinkSock)
return nil, err
}
return tap, nil
}
func CreateUnmonitoredTUNFromFD(fd int) (Device, string, error) {
err := unix.SetNonblock(fd, true)
if err != nil {
return nil, "", err
}
file := os.NewFile(uintptr(fd), "/dev/tap")
tap := &NativeTap{
tapFile: file,
events: make(chan Event, 5),
errors: make(chan error, 5),
nopi: true,
}
name, err := tap.Name()
if err != nil {
return nil, "", err
}
return tap, name, nil
}

View File

@ -3,12 +3,35 @@ package tap
import (
"fmt"
"os"
"github.com/KusakabeSi/EtherGuardVPN/config"
)
type L2MODE uint8
const (
NoChange L2MODE = iota
KeyboardDebug //Register to server
BoardcastAndNodeID
)
func GetL2Mode(mode string) L2MODE {
switch mode {
case "nochg":
return NoChange
case "kbdbg":
return KeyboardDebug
case "noL2":
return BoardcastAndNodeID
}
return NoChange
}
type StdIOTap struct {
name string
mtu int
HumanFriendly bool
macaddr MacAddress
L2mode L2MODE
events chan Event
}
@ -16,28 +39,34 @@ 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}
return MacAddress{0x00, 0x11, 0xff, 0xff, 0xff, b - 48}
}
func Mac2charForm(m []byte) byte {
var M MacAddress
copy(M[:], m)
if IsBoardCast(M) {
if IsNotUnicast(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) {
func CreateStdIOTAP(iconfig config.InterfaceConf) (tapdev Device, err error) {
// Setup TUN Config
if err != nil {
fmt.Println(err.Error())
}
macaddr, err := GetMacAddr(iconfig.MacAddrPrefix, iconfig.VPPIfaceID)
if err != nil {
fmt.Println("ERROR: Failed parse mac address:", iconfig.MacAddrPrefix)
return nil, err
}
tapdev = &StdIOTap{
name: interfaceName,
name: iconfig.Name,
mtu: 1500,
HumanFriendly: HumanFriendly,
macaddr: macaddr,
L2mode: GetL2Mode(iconfig.L2HeaderMode),
events: make(chan Event, 1<<5),
}
tapdev.Events() <- EventUp
@ -48,7 +77,8 @@ func CreateStdIOTAP(interfaceName string, HumanFriendly bool) (tapdev Device, er
// Packet on the interface.
func (tap *StdIOTap) Read(buf []byte, offset int) (int, error) {
if tap.HumanFriendly {
switch tap.L2mode {
case KeyboardDebug:
size, err := os.Stdin.Read(buf[offset+10:])
packet := buf[offset:]
src := Charform2mac(packet[11])
@ -56,22 +86,36 @@ func (tap *StdIOTap) Read(buf []byte, offset int) (int, error) {
copy(packet[0:6], dst[:])
copy(packet[6:12], src[:])
return size - 2 + 12, err
} else {
case BoardcastAndNodeID:
size, err := os.Stdin.Read(buf[offset+12:])
packet := buf[offset:]
src := tap.macaddr
dst := Charform2mac('b')
copy(packet[0:6], dst[:])
copy(packet[6:12], src[:])
return size + 12, err
default:
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 {
switch tap.L2mode {
case KeyboardDebug:
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[10:])
return
case BoardcastAndNodeID:
size, err = os.Stdout.Write(packet[12:])
return
default:
size, err = os.Stdout.Write(packet)
return
}
} // writes a packet to the device (without any additional headers)
func (tap *StdIOTap) Flush() error {
return nil

View File

@ -3,6 +3,8 @@ package tap
import (
"fmt"
"net"
"github.com/KusakabeSi/EtherGuardVPN/config"
)
type UdpSockTap struct {
@ -10,24 +12,31 @@ type UdpSockTap struct {
mtu int
recv *net.UDPConn
send *net.UDPAddr
HumanFriendly bool
L2mode L2MODE
macaddr MacAddress
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) {
func CreateUDPSockTAP(iconfig config.InterfaceConf, listenAddr *net.UDPAddr, sendAddr *net.UDPAddr) (tapdev Device, err error) {
// Setup TUN Config
listener, err := net.ListenUDP("udp", listenAddr)
if err != nil {
fmt.Println(err.Error())
}
macaddr, err := GetMacAddr(iconfig.MacAddrPrefix, iconfig.VPPIfaceID)
if err != nil {
fmt.Println("ERROR: Failed parse mac address:", iconfig.MacAddrPrefix)
return nil, err
}
tapdev = &UdpSockTap{
name: interfaceName,
name: iconfig.Name,
mtu: 1500,
recv: listener,
send: sendAddr,
HumanFriendly: HumanFriendly,
macaddr: macaddr,
L2mode: GetL2Mode(iconfig.L2HeaderMode),
events: make(chan Event, 1<<5),
}
tapdev.Events() <- EventUp
@ -38,15 +47,24 @@ func CreateUDPSockTAP(interfaceName string, listenAddr *net.UDPAddr, sendAddr *n
// Packet on the interface.
func (tap *UdpSockTap) Read(buf []byte, offset int) (int, error) {
if tap.HumanFriendly {
switch tap.L2mode {
case KeyboardDebug:
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 {
return size + 10, err
case BoardcastAndNodeID:
size, _, err := tap.recv.ReadFromUDP(buf[offset+12:])
packet := buf[offset:]
src := tap.macaddr
dst := Charform2mac('b')
copy(packet[0:6], dst[:])
copy(packet[6:12], src[:])
return size + 12, err
default:
size, _, err := tap.recv.ReadFromUDP(buf[offset:])
return size, err
}
@ -54,16 +72,21 @@ func (tap *UdpSockTap) Read(buf []byte, offset int) (int, error) {
} // 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 {
switch tap.L2mode {
case KeyboardDebug:
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
}
case BoardcastAndNodeID:
size, err = tap.recv.WriteToUDP(packet[12:], tap.send)
return
default:
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

View File

@ -2,6 +2,7 @@ package tap
import (
"context"
"path"
"errors"
"fmt"
@ -58,7 +59,7 @@ type VppTap struct {
}
// New creates and returns a new TUN interface for the application.
func CreateVppTAP(interfaceName string, iconfig config.InterfaceConf, loglevel string) (tapdev Device, err error) {
func CreateVppTAP(iconfig config.InterfaceConf, loglevel string) (tapdev Device, err error) {
// Setup TUN Config
// Set logger
log := logger.New()
@ -75,10 +76,20 @@ func CreateVppTAP(interfaceName string, iconfig config.InterfaceConf, loglevel s
return logger.ErrorLevel
}()
libmemif.SetLogger(log)
if os.Getenv(ENV_VPP_MEMIF_SOCKET_DIR) != "" {
vppMemifSocketDir = os.Getenv(ENV_VPP_MEMIF_SOCKET_DIR)
}
if os.Getenv(ENV_VPP_SOCKET_PATH) != "" {
vppApiSocketPath = os.Getenv(ENV_VPP_SOCKET_PATH)
}
if err := os.MkdirAll(vppMemifSocketDir, 0755); err != nil {
log.Fatalln("ERROR: Failed to create VPP memif socket folder " + vppMemifSocketDir)
return nil, err
}
// connect to VPP
conn, err := govpp.Connect(vppApiSocketPath)
if err != nil {
log.Fatalln("ERROR: connecting to VPP failed:", err)
log.Fatalln("ERROR: Connecting to VPP failed:", err)
return nil, err
}
defer conn.Disconnect()
@ -105,18 +116,19 @@ func CreateVppTAP(interfaceName string, iconfig config.InterfaceConf, loglevel s
l2service := l2.NewServiceClient(conn)
interfacservice := interfaces.NewServiceClient(conn)
vppIfMacAddr, err := ethernet_types.ParseMacAddress(iconfig.MacAddr)
IfMacAddr, err := GetMacAddr(iconfig.MacAddrPrefix, iconfig.VPPIfaceID)
if err != nil {
log.Fatalln("ERROR: Failed parse mac address:", iconfig.MacAddr)
log.Fatalln("ERROR: Failed parse mac address:", iconfig.MacAddrPrefix)
return nil, err
}
vppIfMacAddr := ethernet_types.MacAddress(IfMacAddr)
tap := &VppTap{
name: iconfig.Name,
mtu: iconfig.MTU,
ifuid: iconfig.VPPIfaceID,
SwIfIndex: 0,
memifSockPath: vppMemifSocketDir + "/" + iconfig.Name,
memifSockPath: path.Join(vppMemifSocketDir, iconfig.Name+".sock"),
secret: config.RandomStr(16, iconfig.Name),
logger: log,
errors: make(chan error, 1<<5),