Cleanup
This commit is contained in:
parent
c69b52abf0
commit
ee4f5e012c
@ -3,6 +3,9 @@
|
|||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
* Node: use symmetric encryption after handshake
|
* Node: use symmetric encryption after handshake
|
||||||
|
* AEAD-AES uses a 12 byte nonce. We need to shrink the header:
|
||||||
|
* Remove Forward and replace it with a HeaderFlags bitfield.
|
||||||
|
* Forward, Asym/Sym, ...
|
||||||
* Use default port 456
|
* Use default port 456
|
||||||
* Remove signing key from hub
|
* Remove signing key from hub
|
||||||
* Peer: UDP hole-punching
|
* Peer: UDP hole-punching
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
digraph d {
|
|
||||||
init -> null;
|
|
||||||
init -> unconnectedServer;
|
|
||||||
init -> unconnectedClient;
|
|
||||||
init -> unconnectedMediated;
|
|
||||||
unconnectedServer -> connectedServer;
|
|
||||||
unconnectedClient -> connectedClient;
|
|
||||||
unconnectedMediated -> connectedMediated;
|
|
||||||
}
|
|
@ -1,82 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/netip"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
"vppn/m"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
pingInterval = time.Second * 8
|
|
||||||
timeoutInterval = 32 * time.Second
|
|
||||||
)
|
|
||||||
|
|
||||||
type connData struct {
|
|
||||||
// Shared data.
|
|
||||||
routes [MAX_IP]*atomic.Pointer[route]
|
|
||||||
route *atomic.Pointer[route]
|
|
||||||
|
|
||||||
// Peer data.
|
|
||||||
server bool // Never changes.
|
|
||||||
peerIP byte // Never changes.
|
|
||||||
encPrivKey []byte // Never changes.
|
|
||||||
|
|
||||||
peer *m.Peer // From hub.
|
|
||||||
encSharedKey []byte // From hub + private key.
|
|
||||||
publicAddr netip.AddrPort // From hub.
|
|
||||||
|
|
||||||
// Connection establishment and maintenance.
|
|
||||||
pingTimer *time.Timer
|
|
||||||
timeoutTimer *time.Timer
|
|
||||||
|
|
||||||
// Routing data.
|
|
||||||
addr netip.AddrPort
|
|
||||||
useMediator bool
|
|
||||||
up bool
|
|
||||||
|
|
||||||
// For sending.
|
|
||||||
buf []byte
|
|
||||||
sender *safeConnSender
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *connData) Route() *route {
|
|
||||||
return &route{
|
|
||||||
PeerIP: d.peerIP,
|
|
||||||
Up: d.up,
|
|
||||||
Mediator: d.peer.Mediator,
|
|
||||||
EncSharedKey: d.encSharedKey,
|
|
||||||
Addr: d.addr,
|
|
||||||
useMediator: d.useMediator,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *connData) HandlePeerUpdate(state connState, update peerUpdate) connState {
|
|
||||||
if d.peer != nil && update.Peer != nil && d.peer.Version == update.Peer.Version {
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
if d.peer == nil && update.Peer == nil {
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
return newStateFromPeerUpdate(update, d)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *connData) HandleSendPing() {
|
|
||||||
route := d.route.Load()
|
|
||||||
req := Ping{SentAt: time.Now().UnixMilli()}
|
|
||||||
d.buf = req.Marshal(d.buf)
|
|
||||||
d.sender.send(PACKET_TYPE_PING, d.buf, route, nil)
|
|
||||||
d.pingTimer.Reset(pingInterval)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *connData) sendPong(w wrapper) {
|
|
||||||
ping := w.Packet.(*Ping)
|
|
||||||
route := d.route.Load()
|
|
||||||
pong := Pong{
|
|
||||||
SentAt: ping.SentAt,
|
|
||||||
RecvdAt: time.Now().UnixMilli(),
|
|
||||||
}
|
|
||||||
|
|
||||||
d.buf = pong.Marshal(d.buf)
|
|
||||||
d.sender.send(PACKET_TYPE_PONG, d.buf, route, nil)
|
|
||||||
}
|
|
@ -1,99 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"runtime/debug"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type connHandler struct {
|
|
||||||
// Communication.
|
|
||||||
mediatorUpdates chan byte
|
|
||||||
peerUpdates chan peerUpdate
|
|
||||||
packets chan wrapper
|
|
||||||
|
|
||||||
data *connData
|
|
||||||
}
|
|
||||||
|
|
||||||
func newConnHandler(
|
|
||||||
server bool,
|
|
||||||
peerIP byte,
|
|
||||||
routes [MAX_IP]*atomic.Pointer[route],
|
|
||||||
encPrivKey []byte,
|
|
||||||
sender *safeConnSender,
|
|
||||||
) *connHandler {
|
|
||||||
d := &connData{
|
|
||||||
server: server,
|
|
||||||
pingTimer: time.NewTimer(pingInterval),
|
|
||||||
timeoutTimer: time.NewTimer(timeoutInterval),
|
|
||||||
routes: routes,
|
|
||||||
route: routes[peerIP],
|
|
||||||
peerIP: peerIP,
|
|
||||||
encPrivKey: encPrivKey,
|
|
||||||
buf: make([]byte, BUFFER_SIZE),
|
|
||||||
sender: sender,
|
|
||||||
}
|
|
||||||
|
|
||||||
h := &connHandler{
|
|
||||||
mediatorUpdates: make(chan byte, 1),
|
|
||||||
peerUpdates: make(chan peerUpdate, 1),
|
|
||||||
packets: make(chan wrapper, 1),
|
|
||||||
data: d,
|
|
||||||
}
|
|
||||||
|
|
||||||
go h.mainLoop()
|
|
||||||
return h
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *connHandler) mainLoop() {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
fmt.Println("stacktrace from panic: \n" + string(debug.Stack()))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
var (
|
|
||||||
data = h.data
|
|
||||||
state = newConnNull(data)
|
|
||||||
name = state.Name()
|
|
||||||
)
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
|
|
||||||
case update := <-h.peerUpdates:
|
|
||||||
state = data.HandlePeerUpdate(state, update)
|
|
||||||
|
|
||||||
case <-data.pingTimer.C:
|
|
||||||
data.HandleSendPing()
|
|
||||||
|
|
||||||
case w := <-h.packets:
|
|
||||||
state = state.HandlePacket(w)
|
|
||||||
|
|
||||||
case <-data.timeoutTimer.C:
|
|
||||||
log.Printf("[%s] Connection timeout.", state.Name())
|
|
||||||
state = state.HandleTimeout()
|
|
||||||
}
|
|
||||||
|
|
||||||
if state.Name() != name {
|
|
||||||
log.Printf("[%03d] STATE: %s", data.peerIP, state.Name())
|
|
||||||
name = state.Name()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *connHandler) HandlePacket(w wrapper) {
|
|
||||||
select {
|
|
||||||
case c.packets <- w:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *connHandler) UpdatePeer(update peerUpdate) {
|
|
||||||
select {
|
|
||||||
case c.peerUpdates <- update:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,83 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"runtime/debug"
|
|
||||||
"sync"
|
|
||||||
"vppn/fasttime"
|
|
||||||
)
|
|
||||||
|
|
||||||
type connSender struct {
|
|
||||||
conn *net.UDPConn
|
|
||||||
sourceIP byte
|
|
||||||
streamID byte
|
|
||||||
encrypted []byte
|
|
||||||
nonceBuf []byte
|
|
||||||
counter uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
func newConnSender(conn *net.UDPConn, srcIP, streamID byte) *connSender {
|
|
||||||
return &connSender{
|
|
||||||
conn: conn,
|
|
||||||
sourceIP: srcIP,
|
|
||||||
streamID: streamID,
|
|
||||||
encrypted: make([]byte, BUFFER_SIZE),
|
|
||||||
nonceBuf: make([]byte, NONCE_SIZE),
|
|
||||||
counter: uint64(fasttime.Now()) << 30, // Ensure counter is always increasing.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *connSender) send(packetType byte, packet []byte, dstRoute, viaRoute *route) {
|
|
||||||
if dstRoute.useMediator && viaRoute == nil {
|
|
||||||
log.Printf("Dropping forwarded packet: no mediator.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cs.counter++
|
|
||||||
|
|
||||||
nonce := Nonce{
|
|
||||||
Timestamp: fasttime.Now(),
|
|
||||||
Counter: cs.counter,
|
|
||||||
SourceIP: cs.sourceIP,
|
|
||||||
DestIP: dstRoute.PeerIP,
|
|
||||||
StreamID: cs.streamID,
|
|
||||||
PacketType: packetType,
|
|
||||||
}
|
|
||||||
|
|
||||||
if dstRoute.useMediator {
|
|
||||||
nonce.ViaIP = viaRoute.PeerIP
|
|
||||||
}
|
|
||||||
|
|
||||||
nonce.Marshal(cs.nonceBuf)
|
|
||||||
|
|
||||||
addr := dstRoute.Addr
|
|
||||||
|
|
||||||
encrypted := encryptPacket(dstRoute.EncSharedKey, cs.nonceBuf, packet, cs.encrypted)
|
|
||||||
if viaRoute != nil {
|
|
||||||
packet, encrypted = encrypted, packet
|
|
||||||
encrypted = encryptPacket(viaRoute.EncSharedKey, cs.nonceBuf, packet, encrypted)
|
|
||||||
addr = viaRoute.Addr
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := cs.conn.WriteToUDPAddrPort(encrypted, addr); err != nil {
|
|
||||||
log.Fatalf("Failed to write UDP packet: %v\n%s", err, debug.Stack())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
type safeConnSender struct {
|
|
||||||
lock sync.Mutex
|
|
||||||
sender *connSender
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSafeConnSender(sender *connSender) *safeConnSender {
|
|
||||||
return &safeConnSender{sender: sender}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *safeConnSender) send(packetType byte, packet []byte, route, viaRoute *route) {
|
|
||||||
s.lock.Lock()
|
|
||||||
defer s.lock.Unlock()
|
|
||||||
s.sender.send(packetType, packet, route, viaRoute)
|
|
||||||
}
|
|
@ -1,254 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net/netip"
|
|
||||||
"time"
|
|
||||||
"vppn/m"
|
|
||||||
)
|
|
||||||
|
|
||||||
func logState(s connState, msg string, args ...any) {
|
|
||||||
log.Printf("["+s.Name()+"] "+msg, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The connection state corresponds to what we're connected TO.
|
|
||||||
type connState interface {
|
|
||||||
Name() string
|
|
||||||
HandlePacket(wrapper) connState
|
|
||||||
HandleTimeout() connState
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper functions.
|
|
||||||
|
|
||||||
func newStateFromPeerUpdate(update peerUpdate, data *connData) connState {
|
|
||||||
if update.Peer != nil {
|
|
||||||
return newStateFromPeer(update.Peer, data)
|
|
||||||
}
|
|
||||||
return newConnNull(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newStateFromPeer(peer *m.Peer, data *connData) connState {
|
|
||||||
if _, isPublic := netip.AddrFromSlice(peer.PublicIP); isPublic {
|
|
||||||
return newStateServerDown(data, peer)
|
|
||||||
} else if data.server {
|
|
||||||
return newStateClientDown(data, peer)
|
|
||||||
} else {
|
|
||||||
return newStateMediated(data, peer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////
|
|
||||||
// Null Connection //
|
|
||||||
/////////////////////
|
|
||||||
|
|
||||||
type connNull struct{ *connData }
|
|
||||||
|
|
||||||
func newConnNull(data *connData) connState {
|
|
||||||
c := connNull{data}
|
|
||||||
c.peer = nil
|
|
||||||
c.encSharedKey = nil
|
|
||||||
c.publicAddr = netip.AddrPort{}
|
|
||||||
c.pingTimer.Stop()
|
|
||||||
c.timeoutTimer.Stop()
|
|
||||||
c.addr = c.publicAddr
|
|
||||||
c.useMediator = false
|
|
||||||
c.up = false
|
|
||||||
c.route.Store(nil)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c connNull) Name() string { return "NoPeer" }
|
|
||||||
|
|
||||||
func (c connNull) HandlePacket(w wrapper) connState { return c }
|
|
||||||
|
|
||||||
func (c connNull) HandleTimeout() connState {
|
|
||||||
logState(c, "Unexpected timeout.")
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////
|
|
||||||
// Unconnected Server //
|
|
||||||
////////////////////////
|
|
||||||
|
|
||||||
type stateServerDown struct{ *connData }
|
|
||||||
|
|
||||||
func newStateServerDown(data *connData, peer *m.Peer) connState {
|
|
||||||
addr, _ := netip.AddrFromSlice(peer.PublicIP)
|
|
||||||
pubAddr := netip.AddrPortFrom(addr, peer.Port)
|
|
||||||
|
|
||||||
c := stateServerDown{data}
|
|
||||||
c.peer = peer
|
|
||||||
c.encSharedKey = computeSharedKey(peer.EncPubKey, c.encPrivKey)
|
|
||||||
c.publicAddr = pubAddr
|
|
||||||
c.pingTimer.Reset(time.Second) // Ping right away to bring up.
|
|
||||||
c.timeoutTimer.Stop() // No timeouts yet.
|
|
||||||
c.addr = c.publicAddr
|
|
||||||
c.useMediator = false
|
|
||||||
c.up = false
|
|
||||||
c.route.Store(c.Route())
|
|
||||||
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c stateServerDown) Name() string { return "Server:DOWN" }
|
|
||||||
|
|
||||||
func (c stateServerDown) HandlePacket(w wrapper) connState {
|
|
||||||
switch p := w.Packet.(type) {
|
|
||||||
case *Pong:
|
|
||||||
return newStateServerUp(c.connData, w, p)
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c stateServerDown) HandleTimeout() connState {
|
|
||||||
logState(c, "Unexpected timeout.")
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////
|
|
||||||
// Connected Server //
|
|
||||||
//////////////////////
|
|
||||||
|
|
||||||
type stateServerUp struct{ *connData }
|
|
||||||
|
|
||||||
func newStateServerUp(data *connData, w wrapper, pong *Pong) connState {
|
|
||||||
c := stateServerUp{data}
|
|
||||||
c.pingTimer.Reset(pingInterval)
|
|
||||||
c.timeoutTimer.Reset(timeoutInterval)
|
|
||||||
c.addr = w.SrcAddr
|
|
||||||
c.useMediator = false
|
|
||||||
c.up = true
|
|
||||||
c.route.Store(c.Route())
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c stateServerUp) Name() string { return "Server:UP" }
|
|
||||||
|
|
||||||
func (c stateServerUp) HandlePacket(w wrapper) connState {
|
|
||||||
switch w.Packet.(type) {
|
|
||||||
case *Pong:
|
|
||||||
c.timeoutTimer.Reset(timeoutInterval)
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c stateServerUp) HandleTimeout() connState {
|
|
||||||
return newStateFromPeer(c.peer, c.connData)
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////
|
|
||||||
// Unconnected Client //
|
|
||||||
////////////////////////
|
|
||||||
|
|
||||||
type stateClientDown struct{ *connData }
|
|
||||||
|
|
||||||
func newStateClientDown(data *connData, peer *m.Peer) connState {
|
|
||||||
addr, _ := netip.AddrFromSlice(peer.PublicIP)
|
|
||||||
pubAddr := netip.AddrPortFrom(addr, peer.Port)
|
|
||||||
|
|
||||||
c := stateClientDown{data}
|
|
||||||
c.peer = peer
|
|
||||||
c.publicAddr = pubAddr
|
|
||||||
c.encPrivKey = data.encPrivKey
|
|
||||||
c.encSharedKey = computeSharedKey(peer.EncPubKey, c.encPrivKey)
|
|
||||||
c.addr = c.publicAddr
|
|
||||||
c.useMediator = false
|
|
||||||
c.up = false
|
|
||||||
c.route.Store(c.Route())
|
|
||||||
|
|
||||||
c.pingTimer.Stop() // Conncection is from client => pings incoming.
|
|
||||||
c.timeoutTimer.Stop() // No timeouts yet.
|
|
||||||
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c stateClientDown) Name() string { return "Client:DOWN" }
|
|
||||||
|
|
||||||
func (c stateClientDown) HandlePacket(w wrapper) connState {
|
|
||||||
switch w.Packet.(type) {
|
|
||||||
case *Ping:
|
|
||||||
next := newStateClientUp(c.connData, w)
|
|
||||||
c.sendPong(w)
|
|
||||||
return next
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c stateClientDown) HandleTimeout() connState {
|
|
||||||
logState(c, "Unexpected timeout.")
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////
|
|
||||||
// Connected Client //
|
|
||||||
//////////////////////
|
|
||||||
|
|
||||||
type stateClientUp struct{ *connData }
|
|
||||||
|
|
||||||
func newStateClientUp(data *connData, w wrapper) connState {
|
|
||||||
c := stateClientUp{data}
|
|
||||||
c.addr = w.SrcAddr
|
|
||||||
c.useMediator = false
|
|
||||||
c.up = true
|
|
||||||
c.route.Store(c.Route())
|
|
||||||
|
|
||||||
c.pingTimer.Stop() // Conncection is from client => pings incoming.
|
|
||||||
c.timeoutTimer.Reset(timeoutInterval)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c stateClientUp) Name() string { return "Client:UP" }
|
|
||||||
|
|
||||||
func (c stateClientUp) HandlePacket(w wrapper) connState {
|
|
||||||
switch w.Packet.(type) {
|
|
||||||
case *Ping:
|
|
||||||
// The connection is from a client. If the client's address changes, we
|
|
||||||
// should follow that change.
|
|
||||||
if c.addr != w.SrcAddr {
|
|
||||||
c.addr = w.SrcAddr
|
|
||||||
c.route.Store(c.Route())
|
|
||||||
}
|
|
||||||
c.sendPong(w)
|
|
||||||
c.timeoutTimer.Reset(timeoutInterval)
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c stateClientUp) HandleTimeout() connState {
|
|
||||||
return newStateFromPeer(c.peer, c.connData)
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////
|
|
||||||
// Mediated //
|
|
||||||
//////////////
|
|
||||||
|
|
||||||
type stateMediated struct{ *connData }
|
|
||||||
|
|
||||||
func newStateMediated(data *connData, peer *m.Peer) connState {
|
|
||||||
addr, _ := netip.AddrFromSlice(peer.PublicIP)
|
|
||||||
pubAddr := netip.AddrPortFrom(addr, peer.Port)
|
|
||||||
|
|
||||||
c := stateMediated{data}
|
|
||||||
c.peer = peer
|
|
||||||
c.publicAddr = pubAddr
|
|
||||||
c.encPrivKey = data.encPrivKey
|
|
||||||
c.encSharedKey = computeSharedKey(peer.EncPubKey, c.encPrivKey)
|
|
||||||
c.addr = c.publicAddr
|
|
||||||
c.useMediator = true
|
|
||||||
c.up = true
|
|
||||||
c.route.Store(c.Route())
|
|
||||||
|
|
||||||
c.pingTimer.Stop() // No pings for mediators.
|
|
||||||
c.timeoutTimer.Stop() // No timeouts yet.
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c stateMediated) Name() string { return "Mediated:UP" }
|
|
||||||
|
|
||||||
func (c stateMediated) HandlePacket(w wrapper) connState { return c }
|
|
||||||
|
|
||||||
func (c stateMediated) HandleTimeout() connState {
|
|
||||||
logState(c, "Unexpected timeout.")
|
|
||||||
return c
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/crypto/nacl/box"
|
|
||||||
)
|
|
||||||
|
|
||||||
func encryptPacket(sharedKey, nonce, packet, out []byte) []byte {
|
|
||||||
out = box.SealAfterPrecomputation(out[:0], packet, (*[24]byte)(nonce), (*[32]byte)(sharedKey))
|
|
||||||
return append(out, nonce...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func decryptPacket(sharedKey, packet, out []byte) (decrypted []byte, ok bool) {
|
|
||||||
cut := len(packet) - NONCE_SIZE
|
|
||||||
decrypted, ok = box.OpenAfterPrecomputation(out[:0], packet[:cut], (*[24]byte)(packet[cut:]), (*[32]byte)(sharedKey))
|
|
||||||
return decrypted, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func computeSharedKey(peerPubKey, privKey []byte) []byte {
|
|
||||||
shared := [32]byte{}
|
|
||||||
box.Precompute(&shared, (*[32]byte)(peerPubKey), (*[32]byte)(privKey))
|
|
||||||
return shared[:]
|
|
||||||
}
|
|
@ -1,106 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/rand"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/nacl/box"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEncryptDecryptPacket(t *testing.T) {
|
|
||||||
pubKey1, privKey1, err := box.GenerateKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pubKey2, privKey2, err := box.GenerateKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sharedEncKey := [32]byte{}
|
|
||||||
box.Precompute(&sharedEncKey, pubKey2, privKey1)
|
|
||||||
|
|
||||||
sharedDecKey := [32]byte{}
|
|
||||||
box.Precompute(&sharedDecKey, pubKey1, privKey2)
|
|
||||||
|
|
||||||
original := make([]byte, MTU)
|
|
||||||
rand.Read(original)
|
|
||||||
|
|
||||||
nonce := make([]byte, NONCE_SIZE)
|
|
||||||
rand.Read(nonce)
|
|
||||||
|
|
||||||
encrypted := make([]byte, BUFFER_SIZE)
|
|
||||||
encrypted = encryptPacket(sharedEncKey[:], nonce, original, encrypted)
|
|
||||||
|
|
||||||
decrypted := make([]byte, MTU)
|
|
||||||
var ok bool
|
|
||||||
decrypted, ok = decryptPacket(sharedDecKey[:], encrypted, decrypted)
|
|
||||||
if !ok {
|
|
||||||
t.Fatal(ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(original, decrypted) {
|
|
||||||
t.Fatal("mismatch")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkEncryptPacket(b *testing.B) {
|
|
||||||
_, privKey1, err := box.GenerateKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pubKey2, _, err := box.GenerateKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sharedEncKey := [32]byte{}
|
|
||||||
box.Precompute(&sharedEncKey, pubKey2, privKey1)
|
|
||||||
|
|
||||||
original := make([]byte, MTU)
|
|
||||||
rand.Read(original)
|
|
||||||
|
|
||||||
nonce := make([]byte, NONCE_SIZE)
|
|
||||||
rand.Read(nonce)
|
|
||||||
|
|
||||||
encrypted := make([]byte, BUFFER_SIZE)
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
encrypted = encryptPacket(sharedEncKey[:], nonce, original, encrypted)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkDecryptPacket(b *testing.B) {
|
|
||||||
pubKey1, privKey1, err := box.GenerateKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pubKey2, privKey2, err := box.GenerateKey(rand.Reader)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sharedEncKey := [32]byte{}
|
|
||||||
box.Precompute(&sharedEncKey, pubKey2, privKey1)
|
|
||||||
|
|
||||||
sharedDecKey := [32]byte{}
|
|
||||||
box.Precompute(&sharedDecKey, pubKey1, privKey2)
|
|
||||||
|
|
||||||
original := make([]byte, MTU)
|
|
||||||
rand.Read(original)
|
|
||||||
|
|
||||||
nonce := make([]byte, NONCE_SIZE)
|
|
||||||
rand.Read(nonce)
|
|
||||||
|
|
||||||
encrypted := make([]byte, BUFFER_SIZE)
|
|
||||||
encrypted = encryptPacket(sharedEncKey[:], nonce, original, encrypted)
|
|
||||||
|
|
||||||
decrypted := make([]byte, MTU)
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
decrypted, _ = decryptPacket(sharedDecKey[:], encrypted, decrypted)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
const DUP_LIST_SIZE = 32
|
|
||||||
|
|
||||||
type dupList struct {
|
|
||||||
items [DUP_LIST_SIZE]uint64
|
|
||||||
index int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *dupList) isDuplicate(in uint64) bool {
|
|
||||||
for _, i := range l.items {
|
|
||||||
if i == in {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
l.items[l.index] = in
|
|
||||||
l.index = (l.index + 1) % DUP_LIST_SIZE
|
|
||||||
return false
|
|
||||||
}
|
|
@ -1,82 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"vppn/m"
|
|
||||||
)
|
|
||||||
|
|
||||||
func configDir(netName string) string {
|
|
||||||
d, err := os.UserHomeDir()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to get user home directory: %v", err)
|
|
||||||
}
|
|
||||||
return filepath.Join(d, ".vppn", netName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func peerConfigPath(netName string) string {
|
|
||||||
return filepath.Join(configDir(netName), "peer-config.json")
|
|
||||||
}
|
|
||||||
|
|
||||||
func peerStatePath(netName string) string {
|
|
||||||
return filepath.Join(configDir(netName), "peer-state.json")
|
|
||||||
}
|
|
||||||
|
|
||||||
func storeJson(x any, outPath string) error {
|
|
||||||
outDir := filepath.Dir(outPath)
|
|
||||||
_ = os.MkdirAll(outDir, 0700)
|
|
||||||
|
|
||||||
tmpPath := outPath + ".tmp"
|
|
||||||
buf, err := json.Marshal(x)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := os.Create(tmpPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := f.Write(buf); err != nil {
|
|
||||||
f.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f.Sync(); err != nil {
|
|
||||||
f.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f.Close(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return os.Rename(tmpPath, outPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func storePeerConfig(netName string, pc m.PeerConfig) error {
|
|
||||||
return storeJson(pc, peerConfigPath(netName))
|
|
||||||
}
|
|
||||||
|
|
||||||
func storePeerState(netName string, ps m.NetworkState) error {
|
|
||||||
return storeJson(ps, peerStatePath(netName))
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadJson(dataPath string, ptr any) error {
|
|
||||||
data, err := os.ReadFile(dataPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.Unmarshal(data, ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadPeerConfig(netName string) (pc m.PeerConfig, err error) {
|
|
||||||
return pc, loadJson(peerConfigPath(netName), &pc)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadPeerState(netName string) (ps m.NetworkState, err error) {
|
|
||||||
return ps, loadJson(peerStatePath(netName), &ps)
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
const (
|
|
||||||
MAX_IP = 65
|
|
||||||
DEFAULT_PORT = 515
|
|
||||||
NONCE_SIZE = 24
|
|
||||||
KEY_SIZE = 32
|
|
||||||
SIG_SIZE = 64
|
|
||||||
MTU = 1436
|
|
||||||
BUFFER_SIZE = 1536 // Definitely big enough.
|
|
||||||
|
|
||||||
STREAM_DATA = 0
|
|
||||||
STREAM_ROUTING = 1 // Routing queries and responses.
|
|
||||||
)
|
|
79
peer/main.go
79
peer/main.go
@ -1,79 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"runtime/debug"
|
|
||||||
"vppn/m"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Main() {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
fmt.Println("stacktrace from panic: \n" + string(debug.Stack()))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
var (
|
|
||||||
netName string
|
|
||||||
initURL string
|
|
||||||
listenIP string
|
|
||||||
port int
|
|
||||||
)
|
|
||||||
|
|
||||||
flag.StringVar(&netName, "name", "", "[REQUIRED] The network name.")
|
|
||||||
flag.StringVar(&initURL, "init-url", "", "Initializes peer from the hub URL.")
|
|
||||||
flag.StringVar(&listenIP, "listen-ip", "", "IP address to listen on.")
|
|
||||||
flag.IntVar(&port, "port", 0, "Port to listen on.")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if netName == "" {
|
|
||||||
flag.Usage()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if initURL != "" {
|
|
||||||
mainInit(netName, initURL)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
peer, err := NewPeer(netName, listenIP, uint16(port))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to create peer: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
peer.Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
func mainInit(netName, initURL string) {
|
|
||||||
if _, err := loadPeerConfig(netName); err == nil {
|
|
||||||
log.Fatalf("Network is already initialized.")
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := http.Get(initURL)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to fetch data from hub: %v", err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
data, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to read response body: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
peerConfig := m.PeerConfig{}
|
|
||||||
if err := json.Unmarshal(data, &peerConfig); err != nil {
|
|
||||||
log.Fatalf("Failed to parse configuration: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := storePeerConfig(netName, peerConfig); err != nil {
|
|
||||||
log.Fatalf("Failed to store configuration: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Initialization successful.")
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import "unsafe"
|
|
||||||
|
|
||||||
type Nonce struct {
|
|
||||||
Timestamp int64
|
|
||||||
Counter uint64
|
|
||||||
SourceIP byte
|
|
||||||
ViaIP byte
|
|
||||||
DestIP byte
|
|
||||||
StreamID byte // The stream, see STREAM_* constants
|
|
||||||
PacketType byte // The packet type. See PACKET_* constants.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nonce *Nonce) Parse(nb []byte) {
|
|
||||||
nonce.Timestamp = *(*int64)(unsafe.Pointer(&nb[0]))
|
|
||||||
nonce.Counter = *(*uint64)(unsafe.Pointer(&nb[8]))
|
|
||||||
nonce.SourceIP = nb[16]
|
|
||||||
nonce.ViaIP = nb[17]
|
|
||||||
nonce.DestIP = nb[18]
|
|
||||||
nonce.StreamID = nb[19]
|
|
||||||
nonce.PacketType = nb[20]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nonce Nonce) Marshal(buf []byte) {
|
|
||||||
*(*int64)(unsafe.Pointer(&buf[0])) = nonce.Timestamp
|
|
||||||
*(*uint64)(unsafe.Pointer(&buf[8])) = nonce.Counter
|
|
||||||
buf[16] = nonce.SourceIP
|
|
||||||
buf[17] = nonce.ViaIP
|
|
||||||
buf[18] = nonce.DestIP
|
|
||||||
buf[19] = nonce.StreamID
|
|
||||||
buf[20] = nonce.PacketType
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMarshalParseNonce(t *testing.T) {
|
|
||||||
nIn := Nonce{
|
|
||||||
Counter: 3212,
|
|
||||||
SourceIP: 34,
|
|
||||||
ViaIP: 20,
|
|
||||||
DestIP: 200,
|
|
||||||
StreamID: 4,
|
|
||||||
PacketType: 44,
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := make([]byte, NONCE_SIZE)
|
|
||||||
nIn.Marshal(buf)
|
|
||||||
|
|
||||||
nOut := Nonce{}
|
|
||||||
nOut.Parse(buf)
|
|
||||||
if nIn != nOut {
|
|
||||||
t.Fatal(nIn, nOut)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func unmarshalPacket(nonce Nonce, data []byte) Packet {
|
|
||||||
var packet Packet
|
|
||||||
|
|
||||||
switch nonce.PacketType {
|
|
||||||
case PACKET_TYPE_PING:
|
|
||||||
packet = &Ping{}
|
|
||||||
case PACKET_TYPE_PONG:
|
|
||||||
packet = &Pong{}
|
|
||||||
case PACKET_TYPE_LIST_ADDR_REQ:
|
|
||||||
packet = &AddrListReq{}
|
|
||||||
case PACKET_TYPE_LIST_ADDR_RESP:
|
|
||||||
packet = &AddrListResp{}
|
|
||||||
default:
|
|
||||||
// TODO: Log.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(data) < packet.Size() {
|
|
||||||
log.Printf("Short packet[%d]: %d", nonce.PacketType, len(data))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
packet.Parse(data[:packet.Size()])
|
|
||||||
|
|
||||||
return packet
|
|
||||||
}
|
|
138
peer/packets.go
138
peer/packets.go
@ -1,138 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import "unsafe"
|
|
||||||
|
|
||||||
const (
|
|
||||||
PACKET_TYPE_DATA = 0
|
|
||||||
PACKET_TYPE_PING = 1
|
|
||||||
PACKET_TYPE_PONG = 2
|
|
||||||
PACKET_TYPE_CONN_REQ = 3
|
|
||||||
PACKET_TYPE_CONN_RESP = 4
|
|
||||||
PACKET_TYPE_CONN_RESP_ACK = 5
|
|
||||||
PACKET_TYPE_LIST_ADDR_REQ = 6
|
|
||||||
PACKET_TYPE_LIST_ADDR_RESP = 7
|
|
||||||
|
|
||||||
CONN_REQ_SIZE = 24
|
|
||||||
CONN_RESP_SIZE = 8
|
|
||||||
CONN_RESP_ACK_SIZE = 8
|
|
||||||
)
|
|
||||||
|
|
||||||
type Packet interface {
|
|
||||||
Size() int
|
|
||||||
Parse(buf []byte)
|
|
||||||
Marshal(buf []byte) []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
type AddrListReq struct{} // Nothing.
|
|
||||||
|
|
||||||
func (r *AddrListReq) Size() int { return 0 }
|
|
||||||
|
|
||||||
func (r *AddrListReq) Parse(buf []byte) {}
|
|
||||||
|
|
||||||
func (r *AddrListReq) Marshal(buf []byte) []byte { return buf[:0] }
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
type AddrListResp struct {
|
|
||||||
Addrs [][16]byte // All addresses are IPv6 or mapped IPv6.
|
|
||||||
Ports []uint16 // Ports.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *AddrListResp) Size() int {
|
|
||||||
return 18 * MAX_IP
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *AddrListResp) Parse(buf []byte) {
|
|
||||||
r.Addrs = unsafe.Slice((*[16]byte)(unsafe.Pointer(&buf[0])), MAX_IP)
|
|
||||||
r.Ports = unsafe.Slice((*uint16)(unsafe.Pointer(&buf[16*MAX_IP])), MAX_IP)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *AddrListResp) Marshal(buf []byte) []byte {
|
|
||||||
buf = buf[:18:MAX_IP]
|
|
||||||
addrs := unsafe.Slice((*[16]byte)(unsafe.Pointer(&buf[0])), MAX_IP)
|
|
||||||
copy(addrs, r.Addrs)
|
|
||||||
ports := unsafe.Slice((*uint16)(unsafe.Pointer(&buf[16*MAX_IP])), MAX_IP)
|
|
||||||
copy(ports, r.Ports)
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
type ConnReq struct {
|
|
||||||
ReqID uint64
|
|
||||||
SentAt int64
|
|
||||||
Tiebreaker int64 // To determine which side is client or server.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ConnReq) Parse(buf []byte) {
|
|
||||||
r.ReqID = *(*uint64)(unsafe.Pointer(&buf[0]))
|
|
||||||
r.SentAt = *(*int64)(unsafe.Pointer(&buf[8]))
|
|
||||||
r.Tiebreaker = *(*int64)(unsafe.Pointer(&buf[16]))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ConnReq) Marshal(buf []byte) []byte {
|
|
||||||
buf = buf[:24]
|
|
||||||
*(*uint64)(unsafe.Pointer(&buf[0])) = r.ReqID
|
|
||||||
*(*int64)(unsafe.Pointer(&buf[8])) = r.SentAt
|
|
||||||
*(*int64)(unsafe.Pointer(&buf[16])) = r.Tiebreaker
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
type ConnResp struct {
|
|
||||||
ReqID uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ConnResp) Parse(buf []byte) {
|
|
||||||
r.ReqID = *(*uint64)(unsafe.Pointer(&buf[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ConnResp) Marshal(buf []byte) {
|
|
||||||
*(*uint64)(unsafe.Pointer(&buf[0])) = r.ReqID
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
type ConnRespAck = ConnResp
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
type Ping struct {
|
|
||||||
SentAt int64 // unix milli
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Ping) Size() int { return 8 }
|
|
||||||
|
|
||||||
func (p *Ping) Parse(buf []byte) {
|
|
||||||
p.SentAt = *(*int64)(unsafe.Pointer(&buf[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Ping) Marshal(buf []byte) []byte {
|
|
||||||
buf = buf[:8]
|
|
||||||
*(*int64)(unsafe.Pointer(&buf[0])) = p.SentAt
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
type Pong struct {
|
|
||||||
SentAt int64 // unix mili
|
|
||||||
RecvdAt int64 // unix mili
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Pong) Size() int { return 16 }
|
|
||||||
|
|
||||||
func (p *Pong) Parse(buf []byte) {
|
|
||||||
p.SentAt = *(*int64)(unsafe.Pointer(&buf[0]))
|
|
||||||
p.RecvdAt = *(*int64)(unsafe.Pointer(&buf[8]))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Pong) Marshal(buf []byte) []byte {
|
|
||||||
buf = buf[:16]
|
|
||||||
*(*int64)(unsafe.Pointer(&buf[0])) = p.SentAt
|
|
||||||
*(*int64)(unsafe.Pointer(&buf[8])) = p.RecvdAt
|
|
||||||
return buf
|
|
||||||
}
|
|
@ -1,70 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"runtime/debug"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (peer *Peer) ifReader() {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
fmt.Println("stacktrace from panic: \n" + string(debug.Stack()))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
var (
|
|
||||||
sender = newConnSender(peer.conn, peer.ip, STREAM_DATA)
|
|
||||||
n int
|
|
||||||
destIP byte
|
|
||||||
router = peer.router
|
|
||||||
viaRoute *route
|
|
||||||
route *route
|
|
||||||
iface = peer.iface
|
|
||||||
err error
|
|
||||||
packet = make([]byte, BUFFER_SIZE)
|
|
||||||
version byte
|
|
||||||
)
|
|
||||||
|
|
||||||
for {
|
|
||||||
n, err = iface.Read(packet[:BUFFER_SIZE])
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to read from interface: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if n < 20 {
|
|
||||||
log.Printf("Dropping small packet: %d", n)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
packet = packet[:n]
|
|
||||||
version = packet[0] >> 4
|
|
||||||
|
|
||||||
switch version {
|
|
||||||
case 4:
|
|
||||||
destIP = packet[19]
|
|
||||||
case 6:
|
|
||||||
destIP = packet[39]
|
|
||||||
default:
|
|
||||||
log.Printf("Dropping packet with IP version: %d", version)
|
|
||||||
}
|
|
||||||
|
|
||||||
route = router.GetRoute(destIP)
|
|
||||||
if route == nil || !route.Up {
|
|
||||||
log.Printf("Dropping packet for non-existent IP: %d", destIP)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if route.useMediator {
|
|
||||||
viaRoute = router.GetMediator()
|
|
||||||
if viaRoute == nil || !viaRoute.Up {
|
|
||||||
log.Printf("Dropping packet due to no mediator: %d", destIP)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
viaRoute = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
sender.send(PACKET_TYPE_DATA, packet, route, viaRoute)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,137 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net/netip"
|
|
||||||
"runtime/debug"
|
|
||||||
"vppn/fasttime"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (peer *Peer) netReader() {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
fmt.Println("stacktrace from panic: \n" + string(debug.Stack()))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
var (
|
|
||||||
dupLists = [MAX_IP]dupList{}
|
|
||||||
n int
|
|
||||||
srcAddr netip.AddrPort
|
|
||||||
nonce Nonce
|
|
||||||
packet = make([]byte, BUFFER_SIZE)
|
|
||||||
decrypted = make([]byte, BUFFER_SIZE)
|
|
||||||
route *route
|
|
||||||
ok bool
|
|
||||||
err error
|
|
||||||
conn = peer.conn
|
|
||||||
ip = peer.ip
|
|
||||||
counters = [2][256]uint64{} // Counter by stream and IP.
|
|
||||||
)
|
|
||||||
|
|
||||||
NEXT_PACKET:
|
|
||||||
|
|
||||||
n, srcAddr, err = conn.ReadFromUDPAddrPort(packet[:BUFFER_SIZE])
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to read UDP packet: %v", err)
|
|
||||||
}
|
|
||||||
srcAddr = netip.AddrPortFrom(srcAddr.Addr().Unmap(), srcAddr.Port())
|
|
||||||
|
|
||||||
if n < NONCE_SIZE {
|
|
||||||
log.Printf("Dropping short UDP packet: %d", n)
|
|
||||||
goto NEXT_PACKET
|
|
||||||
}
|
|
||||||
|
|
||||||
packet = packet[:n]
|
|
||||||
nonce.Parse(packet[n-NONCE_SIZE:])
|
|
||||||
|
|
||||||
// Drop after 8 seconds.
|
|
||||||
if nonce.Timestamp < fasttime.Now()-8 {
|
|
||||||
log.Printf("Dropping old packet: %d", nonce.Timestamp)
|
|
||||||
goto NEXT_PACKET
|
|
||||||
}
|
|
||||||
|
|
||||||
if nonce.StreamID > 1 {
|
|
||||||
log.Printf("Dropping invalid stream ID: %+v", nonce)
|
|
||||||
goto NEXT_PACKET
|
|
||||||
}
|
|
||||||
|
|
||||||
if nonce.Counter <= counters[nonce.StreamID][nonce.SourceIP] {
|
|
||||||
log.Printf("Dropping packet with bad counter: %d (-%d) - %v", nonce.Counter, counters[nonce.StreamID][nonce.SourceIP]-nonce.Counter, srcAddr)
|
|
||||||
goto NEXT_PACKET
|
|
||||||
}
|
|
||||||
|
|
||||||
route = peer.router.GetRoute(nonce.SourceIP)
|
|
||||||
if route == nil {
|
|
||||||
log.Printf("Dropping packet without route: %+v", nonce)
|
|
||||||
goto NEXT_PACKET
|
|
||||||
}
|
|
||||||
|
|
||||||
decrypted, ok = decryptPacket(route.EncSharedKey, packet, decrypted)
|
|
||||||
if !ok {
|
|
||||||
log.Printf("Failed to decrypt packet: %v", nonce)
|
|
||||||
goto NEXT_PACKET
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only updated after we've decrypted.
|
|
||||||
if nonce.Counter > counters[nonce.StreamID][nonce.SourceIP] {
|
|
||||||
counters[nonce.StreamID][nonce.SourceIP] = nonce.Counter
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ip {
|
|
||||||
case nonce.DestIP:
|
|
||||||
goto PROCESS_LOCAL
|
|
||||||
case nonce.ViaIP:
|
|
||||||
goto FORWARD
|
|
||||||
default:
|
|
||||||
log.Printf("Invalid nonce: %+v", nonce)
|
|
||||||
goto NEXT_PACKET
|
|
||||||
}
|
|
||||||
|
|
||||||
PROCESS_LOCAL:
|
|
||||||
|
|
||||||
switch nonce.StreamID {
|
|
||||||
case STREAM_DATA:
|
|
||||||
goto WRITE_IFACE_DATA
|
|
||||||
case STREAM_ROUTING:
|
|
||||||
goto WRITE_ROUTING_PACKET
|
|
||||||
default:
|
|
||||||
log.Printf("Invalid stream ID: %d", nonce.StreamID)
|
|
||||||
goto NEXT_PACKET
|
|
||||||
}
|
|
||||||
|
|
||||||
WRITE_IFACE_DATA:
|
|
||||||
|
|
||||||
if _, err = peer.iface.Write(decrypted); err != nil {
|
|
||||||
log.Fatalf("Failed to write to interface: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
goto NEXT_PACKET
|
|
||||||
|
|
||||||
WRITE_ROUTING_PACKET:
|
|
||||||
|
|
||||||
peer.router.HandlePacket(srcAddr, nonce, decrypted)
|
|
||||||
|
|
||||||
goto NEXT_PACKET
|
|
||||||
|
|
||||||
FORWARD:
|
|
||||||
|
|
||||||
route = peer.router.GetRoute(nonce.DestIP)
|
|
||||||
if route == nil || !route.Up {
|
|
||||||
log.Printf("Dropping mediated packet, route not available: %v", nonce)
|
|
||||||
goto NEXT_PACKET
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't forward twice.
|
|
||||||
if route.useMediator {
|
|
||||||
log.Printf("Dropping double-forward packet: %v", nonce)
|
|
||||||
goto NEXT_PACKET
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = conn.WriteToUDPAddrPort(decrypted, route.Addr); err != nil {
|
|
||||||
log.Fatalf("Failed to forward packet: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
goto NEXT_PACKET
|
|
||||||
}
|
|
65
peer/peer.go
65
peer/peer.go
@ -1,65 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"net/netip"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Peer struct {
|
|
||||||
ip byte // Last byte of IPv4 address.
|
|
||||||
hubAddr string
|
|
||||||
apiKey string
|
|
||||||
isServer bool
|
|
||||||
isMediator bool
|
|
||||||
encPubKey []byte
|
|
||||||
encPrivKey []byte
|
|
||||||
conn *net.UDPConn
|
|
||||||
iface io.ReadWriteCloser
|
|
||||||
|
|
||||||
router *Router
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPeer(netName, listenIP string, port uint16) (*Peer, error) {
|
|
||||||
conf, err := loadPeerConfig(netName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
peer := &Peer{
|
|
||||||
ip: conf.PeerIP,
|
|
||||||
hubAddr: conf.HubAddress,
|
|
||||||
isMediator: conf.Mediator,
|
|
||||||
apiKey: conf.APIKey,
|
|
||||||
encPubKey: conf.EncPubKey,
|
|
||||||
encPrivKey: conf.EncPrivKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
_, peer.isServer = netip.AddrFromSlice(conf.PublicIP)
|
|
||||||
|
|
||||||
port = determinePort(conf.Port, port)
|
|
||||||
|
|
||||||
peer.conn, err = openUDPConn(listenIP, port)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
peer.router = NewRouter(conf, peer.conn)
|
|
||||||
|
|
||||||
peer.iface, err = openInterface(conf.Network, conf.PeerIP, netName)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return peer, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Peer) Run() {
|
|
||||||
go p.netReader()
|
|
||||||
p.ifReader()
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"math/rand"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (r *Router) manageMediator() {
|
|
||||||
var (
|
|
||||||
ip = byte(0)
|
|
||||||
mediators = make([]*route, 0, 32)
|
|
||||||
)
|
|
||||||
|
|
||||||
for range time.Tick(8 * time.Second) {
|
|
||||||
// If the current mediator is up, keep it.
|
|
||||||
route := r.routes[ip].Load()
|
|
||||||
if route != nil && route.Up {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the current mediator is up, keep it.
|
|
||||||
mediators = mediators[:0]
|
|
||||||
|
|
||||||
for i := range r.routes {
|
|
||||||
route := r.routes[i].Load()
|
|
||||||
if route == nil || !route.Mediator {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
mediators = append(mediators, route)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(mediators) == 0 {
|
|
||||||
r.mediatorIP.Store(nil)
|
|
||||||
} else {
|
|
||||||
ip = mediators[rand.Intn(len(mediators))].PeerIP
|
|
||||||
log.Printf("Got mediator IP: %d", ip)
|
|
||||||
r.mediatorIP.Store(&ip)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMarshalParsePingReq(t *testing.T) {
|
|
||||||
in := Ping{
|
|
||||||
SentAt: 4553252,
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := make([]byte, PING_SIZE)
|
|
||||||
in.Marshal(buf)
|
|
||||||
|
|
||||||
out := Ping{}
|
|
||||||
out.Parse(buf)
|
|
||||||
if !reflect.DeepEqual(in, out) {
|
|
||||||
t.Fatal(in, out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMarshalParsePingResp(t *testing.T) {
|
|
||||||
in := Pong{
|
|
||||||
SentAt: 4553252,
|
|
||||||
RecvdAt: 4553253,
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := make([]byte, PONG_SIZE)
|
|
||||||
in.Marshal(buf)
|
|
||||||
|
|
||||||
out := Pong{}
|
|
||||||
out.Parse(buf)
|
|
||||||
if !reflect.DeepEqual(in, out) {
|
|
||||||
t.Fatal(in, out)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"time"
|
|
||||||
"vppn/m"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (r *Router) pollHub() {
|
|
||||||
u, err := url.Parse(r.conf.HubAddress)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to parse hub address %s: %v", r.conf.HubAddress, err)
|
|
||||||
}
|
|
||||||
u.Path = "/peer/fetch-state/"
|
|
||||||
|
|
||||||
client := &http.Client{Timeout: 8 * time.Second}
|
|
||||||
|
|
||||||
req := &http.Request{
|
|
||||||
Method: http.MethodGet,
|
|
||||||
URL: u,
|
|
||||||
Header: http.Header{},
|
|
||||||
}
|
|
||||||
req.SetBasicAuth("", r.conf.APIKey)
|
|
||||||
|
|
||||||
// TODO: Before we start polling, load state from the file system.
|
|
||||||
r._pollHub(client, req)
|
|
||||||
|
|
||||||
for range time.Tick(32 * time.Second) {
|
|
||||||
r._pollHub(client, req)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Router) _pollHub(client *http.Client, req *http.Request) {
|
|
||||||
var state m.NetworkState
|
|
||||||
|
|
||||||
log.Printf("Fetching peer state from %s...", r.conf.HubAddress)
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to fetch peer state: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
|
||||||
_ = resp.Body.Close()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Failed to read body from hub: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := json.Unmarshal(body, &state); err != nil {
|
|
||||||
log.Printf("Failed to unmarshal response from hub: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range r.conns {
|
|
||||||
if r.conns[i] != nil {
|
|
||||||
r.conns[i].UpdatePeer(peerUpdate{PeerIP: byte(i), Peer: state.Peers[i]})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/netip"
|
|
||||||
"vppn/m"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// A route is used by a peer to send or receive packets. A peer won't send
|
|
||||||
// packets on a route that isn't up, but will process incoming packets on
|
|
||||||
// that route.
|
|
||||||
type route struct {
|
|
||||||
PeerIP byte
|
|
||||||
Up bool
|
|
||||||
Mediator bool
|
|
||||||
EncSharedKey []byte // Shared key for encoding / decoding packets.
|
|
||||||
Addr netip.AddrPort
|
|
||||||
useMediator bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type peerUpdate struct {
|
|
||||||
PeerIP byte
|
|
||||||
*m.Peer // nil => delete.
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Wrapper for routing packets.
|
|
||||||
type wrapper struct {
|
|
||||||
Packet Packet
|
|
||||||
Nonce Nonce
|
|
||||||
SrcAddr netip.AddrPort
|
|
||||||
}
|
|
@ -1,85 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"net/netip"
|
|
||||||
"sync/atomic"
|
|
||||||
"vppn/m"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Router struct {
|
|
||||||
conf m.PeerConfig
|
|
||||||
|
|
||||||
// Routes used by the peer.
|
|
||||||
conns [MAX_IP]*connHandler
|
|
||||||
routes [MAX_IP]*atomic.Pointer[route]
|
|
||||||
addrs [MAX_IP]*atomic.Pointer[netip.AddrPort]
|
|
||||||
mediatorIP *atomic.Pointer[byte]
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRouter(conf m.PeerConfig, conn *net.UDPConn) *Router {
|
|
||||||
r := &Router{
|
|
||||||
conf: conf,
|
|
||||||
mediatorIP: &atomic.Pointer[byte]{},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range r.routes {
|
|
||||||
r.routes[i] = &atomic.Pointer[route]{}
|
|
||||||
}
|
|
||||||
|
|
||||||
_, isServer := netip.AddrFromSlice(conf.PublicIP)
|
|
||||||
|
|
||||||
sender := newConnSender(conn, conf.PeerIP, STREAM_ROUTING)
|
|
||||||
|
|
||||||
for i := range r.conns {
|
|
||||||
if byte(i) != conf.PeerIP {
|
|
||||||
r.conns[i] = newConnHandler(
|
|
||||||
isServer,
|
|
||||||
byte(i),
|
|
||||||
r.routes,
|
|
||||||
conf.EncPrivKey,
|
|
||||||
newSafeConnSender(sender))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
go r.pollHub()
|
|
||||||
if !isServer {
|
|
||||||
go r.manageMediator()
|
|
||||||
}
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Peer Methods
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
func (rm *Router) GetRoute(ip byte) *route {
|
|
||||||
return rm.routes[ip].Load()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rm *Router) GetMediator() *route {
|
|
||||||
if ip := rm.mediatorIP.Load(); ip != nil {
|
|
||||||
return rm.GetRoute(*ip)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Router) HandlePacket(src netip.AddrPort, nonce Nonce, data []byte) {
|
|
||||||
if nonce.SourceIP == r.conf.PeerIP {
|
|
||||||
log.Printf("Packet to self...")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
packet := unmarshalPacket(nonce, data)
|
|
||||||
if packet == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
r.conns[nonce.SourceIP].HandlePacket(wrapper{
|
|
||||||
Packet: packet,
|
|
||||||
Nonce: nonce,
|
|
||||||
SrcAddr: src,
|
|
||||||
})
|
|
||||||
}
|
|
156
peer/startup.go
156
peer/startup.go
@ -1,156 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
func determinePort(confPort, portFromCommandLine uint16) uint16 {
|
|
||||||
if portFromCommandLine != 0 {
|
|
||||||
return portFromCommandLine
|
|
||||||
}
|
|
||||||
if confPort != 0 {
|
|
||||||
return confPort
|
|
||||||
}
|
|
||||||
return DEFAULT_PORT
|
|
||||||
}
|
|
||||||
|
|
||||||
func openUDPConn(listenIP string, port uint16) (*net.UDPConn, error) {
|
|
||||||
myAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", listenIP, port))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to construct UDP address: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return net.ListenUDP("udp", myAddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func openInterface(network []byte, localIP byte, name string) (io.ReadWriteCloser, error) {
|
|
||||||
if len(network) != 4 {
|
|
||||||
return nil, fmt.Errorf("expected network to be 4 bytes, got %d", len(network))
|
|
||||||
}
|
|
||||||
ip := net.IPv4(network[0], network[1], network[2], localIP)
|
|
||||||
|
|
||||||
//////////////////////////
|
|
||||||
// Create TUN Interface //
|
|
||||||
//////////////////////////
|
|
||||||
|
|
||||||
tunFD, err := syscall.Open("/dev/net/tun", syscall.O_RDWR|unix.O_CLOEXEC, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to open TUN device: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// New interface request.
|
|
||||||
req, err := unix.NewIfreq(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create new TUN interface request: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flags:
|
|
||||||
//
|
|
||||||
// IFF_NO_PI => don't add packet info data to packets sent to the interface.
|
|
||||||
// IFF_TUN => create a TUN device handling IP packets.
|
|
||||||
req.SetUint16(unix.IFF_NO_PI | unix.IFF_TUN)
|
|
||||||
|
|
||||||
err = unix.IoctlIfreq(tunFD, unix.TUNSETIFF, req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to set TUN device settings: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name may not be exactly the same?
|
|
||||||
name = req.Name()
|
|
||||||
|
|
||||||
/////////////
|
|
||||||
// Set MTU //
|
|
||||||
/////////////
|
|
||||||
|
|
||||||
// We need a socket file descriptor to set other options for some reason.
|
|
||||||
sockFD, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_IP)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to open socket: %w", err)
|
|
||||||
}
|
|
||||||
defer unix.Close(sockFD)
|
|
||||||
|
|
||||||
req, err = unix.NewIfreq(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create MTU interface request: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req.SetUint32(MTU)
|
|
||||||
if err = unix.IoctlIfreq(sockFD, unix.SIOCSIFMTU, req); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to set interface MTU: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////
|
|
||||||
// Set Queue Length //
|
|
||||||
//////////////////////
|
|
||||||
|
|
||||||
req, err = unix.NewIfreq(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create IP interface request: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req.SetUint16(1000)
|
|
||||||
if err = unix.IoctlIfreq(sockFD, unix.SIOCSIFTXQLEN, req); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to set interface queue length: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////
|
|
||||||
// Set IP and Mask //
|
|
||||||
/////////////////////
|
|
||||||
|
|
||||||
req, err = unix.NewIfreq(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create IP interface request: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := req.SetInet4Addr(ip.To4()); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to set interface request IP: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = unix.IoctlIfreq(sockFD, unix.SIOCSIFADDR, req); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to set interface IP: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SET MASK - must happen after setting address.
|
|
||||||
req, err = unix.NewIfreq(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create mask interface request: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := req.SetInet4Addr(net.IPv4(255, 255, 255, 0).To4()); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to set interface request mask: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := unix.IoctlIfreq(sockFD, unix.SIOCSIFNETMASK, req); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to set interface mask: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////
|
|
||||||
// Bring Interface Up //
|
|
||||||
////////////////////////
|
|
||||||
|
|
||||||
req, err = unix.NewIfreq(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create up interface request: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get current flags.
|
|
||||||
if err = unix.IoctlIfreq(sockFD, unix.SIOCGIFFLAGS, req); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get interface flags: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
flags := req.Uint16() | unix.IFF_UP | unix.IFF_RUNNING
|
|
||||||
|
|
||||||
// Set UP flag / broadcast flags.
|
|
||||||
req.SetUint16(flags)
|
|
||||||
if err = unix.IoctlIfreq(sockFD, unix.SIOCSIFFLAGS, req); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to set interface up: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return os.NewFile(uintptr(tunFD), "tun"), nil
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user