WIP: cleanup. Local peer discovery working.

This commit is contained in:
jdl 2024-12-30 15:38:08 +01:00
parent 8407fd5b48
commit bbf5202d30
49 changed files with 273 additions and 1632 deletions

View File

@ -1,79 +0,0 @@
package aestests
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"log"
"testing"
)
func must(err error) {
if err != nil {
panic(err)
}
}
func TestAES(t *testing.T) {
key := make([]byte, 32)
rand.Read(key)
block, err := aes.NewCipher(key)
must(err)
aesgcm, err := cipher.NewGCM(block)
must(err)
log.Print(aesgcm.NonceSize())
log.Print(aesgcm.Overhead())
}
func BenchmarkSeal(b *testing.B) {
key := make([]byte, 32)
rand.Read(key)
block, err := aes.NewCipher(key)
must(err)
cryptor, err := cipher.NewGCM(block)
must(err)
nonce := make([]byte, 12)
rand.Read(nonce)
data := make([]byte, 1400)
rand.Read(data)
out := make([]byte, 1500)
b.ResetTimer()
for i := 0; i < b.N; i++ {
out = cryptor.Seal(out[:0], nonce, data, nil)
}
}
func BenchmarkOpen(b *testing.B) {
key := make([]byte, 32)
rand.Read(key)
block, err := aes.NewCipher(key)
must(err)
cryptor, err := cipher.NewGCM(block)
must(err)
nonce := make([]byte, 12)
rand.Read(nonce)
data := make([]byte, 1400)
rand.Read(data)
sealed := make([]byte, 1500)
sealed = cryptor.Seal(sealed[:0], nonce, data, nil)
dec := make([]byte, 1500)
b.ResetTimer()
for i := 0; i < b.N; i++ {
dec, err = cryptor.Open(dec[:0], nonce, sealed, nil)
}
}

View File

@ -24,7 +24,6 @@ var migrations embed.FS
type API struct {
db *sql.DB
lock sync.Mutex
peerIntents map[string]PeerCreateArgs
initIntents map[string]byte // Map from intent key to peer IP
}
@ -40,7 +39,6 @@ func New(dbPath string) (*API, error) {
a := &API{
db: sqlDB,
peerIntents: map[string]PeerCreateArgs{},
initIntents: map[string]byte{},
}
@ -153,34 +151,6 @@ func (a *API) Peer_CreateNew(p *Peer) error {
return db.Peer_Insert(a.db, p)
}
// TODO: Remove
type PeerCreateArgs struct {
Name string
PublicIP []byte
Port uint16
Relay bool
}
// TODO: Remove
// Create the intention to add a peer. The returned code is used to complete
// the peer creation. The code is valid for 5 minutes.
func (a *API) Peer_CreateIntent(args PeerCreateArgs) string {
a.lock.Lock()
defer a.lock.Unlock()
code := idgen.NewToken()
a.peerIntents[code] = args
go func() {
time.Sleep(5 * time.Minute)
a.lock.Lock()
defer a.lock.Unlock()
delete(a.peerIntents, code)
}()
return code
}
// Create the intention to initialize a peer. The returned code is used to
// complete the peer initialization. The code is valid for 5 minutes.
func (a *API) Peer_CreateInitIntent(peerIP byte) string {
@ -252,79 +222,6 @@ func (a *API) Peer_Init(initCode string) (*m.PeerConfig, error) {
}, nil
}
// TODO: Remove
func (a *API) Peer_Create(creationCode string) (*m.PeerConfig, error) {
a.lock.Lock()
defer a.lock.Unlock()
args, ok := a.peerIntents[creationCode]
if !ok {
return nil, ErrNotAuthorized
}
delete(a.peerIntents, creationCode)
encPubKey, encPrivKey, err := box.GenerateKey(rand.Reader)
if err != nil {
return nil, err
}
signPubKey, signPrivKey, err := sign.GenerateKey(rand.Reader)
if err != nil {
return nil, err
}
// Get peer IP.
peerIP := byte(0)
for i := byte(1); i < 255; i++ {
exists, err := db.Peer_Exists(a.db, i)
if err != nil {
return nil, err
}
if !exists {
peerIP = i
break
}
}
if peerIP == 0 {
return nil, ErrNoIPAvailable
}
peer := &Peer{
PeerIP: peerIP,
Version: idgen.NextID(0),
APIKey: idgen.NewToken(),
Name: args.Name,
PublicIP: args.PublicIP,
Port: args.Port,
Relay: args.Relay,
PubKey: encPubKey[:],
PubSignKey: signPubKey[:],
}
if err := db.Peer_Insert(a.db, peer); err != nil {
return nil, err
}
conf := a.Config_Get()
return &m.PeerConfig{
PeerIP: peer.PeerIP,
HubAddress: conf.HubAddress,
APIKey: peer.APIKey,
Network: conf.VPNNetwork,
PublicIP: peer.PublicIP,
Port: peer.Port,
Relay: peer.Relay,
PubKey: encPubKey[:],
PrivKey: encPrivKey[:],
PubSignKey: signPubKey[:],
PrivSignKey: signPrivKey[:],
}, nil
}
func (a *API) Peer_Update(p *Peer) error {
a.lock.Lock()
defer a.lock.Unlock()

View File

@ -1 +0,0 @@
package api

View File

@ -208,7 +208,7 @@ func (a *App) _adminPeerInit(s *api.Session, w http.ResponseWriter, r *http.Requ
return err
}
code := a.api.Peer_CreateInitIntent(peerIP)
log.Printf("Got code: %v / %v", peerIP, cod)
log.Printf("Got code: %v / %v", peerIP, code)
return a.render("/admin-peer-init.html", w, struct {
Session *api.Session
@ -217,20 +217,6 @@ func (a *App) _adminPeerInit(s *api.Session, w http.ResponseWriter, r *http.Requ
}{s, a.api.Config_Get().HubAddress, code})
}
// TODO: Remove
func (a *App) _adminPeerIntentCreated(s *api.Session, w http.ResponseWriter, r *http.Request) error {
code := r.FormValue("Code")
if code == "" {
return errors.New("missing Code")
}
return a.render("/admin-peer-intent.html", w, struct {
Session *api.Session
HubAddress string
Code string
}{s, a.api.Config_Get().HubAddress, code})
}
func (a *App) _adminPeerView(s *api.Session, w http.ResponseWriter, r *http.Request) error {
var peerIP byte
err := webutil.NewFormScanner(r.Form).Scan("PeerIP", &peerIP).Error()
@ -345,17 +331,6 @@ func (a *App) _peerInit(w http.ResponseWriter, r *http.Request) error {
return a.sendJSON(w, conf)
}
// TODO: Remove
func (a *App) _peerCreate(w http.ResponseWriter, r *http.Request) error {
code := r.FormValue("Code")
conf, err := a.api.Peer_Create(code)
if err != nil {
return err
}
return a.sendJSON(w, conf)
}
func (a *App) _peerFetchState(w http.ResponseWriter, r *http.Request) error {
_, apiKey, ok := r.BasicAuth()
if !ok {
@ -392,6 +367,7 @@ func (a *App) _peerFetchState(w http.ResponseWriter, r *http.Request) error {
Port: p.Port,
Relay: p.Relay,
PubKey: p.PubKey,
PubSignKey: p.PubSignKey,
}
}
}

View File

@ -20,15 +20,12 @@ func (a *App) registerRoutes() {
a.handleSignedIn("GET /admin/peer/create/", a._adminPeerCreate)
a.handleSignedIn("POST /admin/peer/create/", a._adminPeerCreateSubmit)
a.handleSignedIn("GET /admin/peer/init/", a._adminPeerInit)
// TODO: Remove
a.handleSignedIn("GET /admin/peer/intent-created/", a._adminPeerIntentCreated)
a.handleSignedIn("GET /admin/peer/view/", a._adminPeerView)
a.handleSignedIn("GET /admin/peer/edit/", a._adminPeerEdit)
a.handleSignedIn("POST /admin/peer/edit/", a._adminPeerEditSubmit)
a.handleSignedIn("GET /admin/peer/delete/", a._adminPeerDelete)
a.handleSignedIn("POST /admin/peer/delete/", a._adminPeerDeleteSubmit)
a.handlePeer("GET /peer/create/", a._peerCreate) // TODO: Remove
a.handlePeer("GET /peer/init/", a._peerInit)
a.handlePeer("GET /peer/fetch-state/", a._peerFetchState)
}

View File

@ -1 +0,0 @@
package hub

View File

@ -13,21 +13,17 @@ func addrDiscoveryServer() {
)
for {
pkt := <-discoveryPackets
msg := <-discoveryMessages
p := msg.Packet
p, ok := pkt.Payload.(addrDiscoveryPacket)
if !ok {
continue
}
route := routingTable[pkt.SrcIP].Load()
route := routingTable[msg.SrcIP].Load()
if route == nil || !route.RemoteAddr.IsValid() {
continue
}
_sendControlPacket(addrDiscoveryPacket{
TraceID: p.TraceID,
ToAddr: pkt.SrcAddr,
ToAddr: msg.SrcAddr,
}, *route, buf1, buf2)
}
}
@ -46,9 +42,9 @@ func addrDiscoveryClient() {
for {
select {
case pkt := <-discoveryPackets:
p, ok := pkt.Payload.(addrDiscoveryPacket)
if !ok || p.TraceID != addrPacket.TraceID || !p.ToAddr.IsValid() || p.ToAddr == lAddr {
case msg := <-discoveryMessages:
p := msg.Packet
if p.TraceID != addrPacket.TraceID || !p.ToAddr.IsValid() || p.ToAddr == lAddr {
continue
}

View File

@ -1,5 +0,0 @@
#!/bin/bash
go build
sudo setcap cap_net_admin+iep ./client
./client 144.76.78.93

View File

@ -1,15 +0,0 @@
package main
import (
"log"
"os"
"vppn/node"
)
func main() {
if len(os.Args) != 2 {
log.Fatalf("Usage: %s <addr:port>", os.Args[0])
}
n := node.NewTmpNodeClient(os.Args[1])
n.RunClient()
}

View File

@ -1,7 +0,0 @@
#!/bin/bash
go build
ssh kevin "killall server"
scp server kevin:/home/jdl/tmp/
ssh root@kevin "sudo setcap cap_net_admin+iep /home/jdl/tmp/server"
ssh kevin "/home/jdl/tmp/server"

View File

@ -1,8 +0,0 @@
package main
import "vppn/node"
func main() {
n := node.NewTmpNodeServer()
n.RunServer()
}

View File

@ -1,10 +1,10 @@
package node
import (
"net"
"net/netip"
"sync/atomic"
"time"
"vppn/m"
)
const (
@ -13,6 +13,12 @@ const (
if_queue_len = 2048
controlCipherOverhead = 16
dataCipherOverhead = 16
signOverhead = 64
)
var (
multicastIP = netip.AddrFrom4([4]byte{224, 0, 0, 157})
multicastAddr = net.UDPAddrFromAddrPort(netip.AddrPortFrom(multicastIP, 4560))
)
type peerRoute struct {
@ -56,18 +62,9 @@ var (
return
}()
// Channels for incoming control packets.
controlPackets [256]chan controlPacket = func() (out [256]chan controlPacket) {
messages [256]chan any = func() (out [256]chan any) {
for i := range out {
out[i] = make(chan controlPacket, 256)
}
return
}()
// Channels for incoming peer updates from the hub.
peerUpdates [256]chan *m.Peer = func() (out [256]chan *m.Peer) {
for i := range out {
out[i] = make(chan *m.Peer)
out[i] = make(chan any, 256)
}
return
}()
@ -81,8 +78,10 @@ var (
return
}()
// Managed by the addrDiscovery* functions.
discoveryMessages = make(chan controlMsg[addrDiscoveryPacket], 256)
// Managed by the relayManager.
discoveryPackets = make(chan controlPacket, 256)
localAddr = &atomic.Pointer[netip.AddrPort]{}
relayIP = &atomic.Pointer[byte]{}
)

View File

@ -86,7 +86,7 @@ func (hp *hubPoller) applyNetworkState(state m.NetworkState) {
for i, peer := range state.Peers {
if i != int(localIP) {
if peer != nil && peer.Version != hp.versions[i] {
peerUpdates[i] <- state.Peers[i]
messages[i] <- peerUpdateMsg{Peer: state.Peers[i]}
hp.versions[i] = peer.Version
}
}

View File

@ -1,75 +0,0 @@
package node
import (
"encoding/binary"
"log"
"net"
"net/netip"
"time"
)
func localBroadcaster() {
var (
buf1 = make([]byte, bufferSize)
buf2 = make([]byte, bufferSize)
)
time.Sleep(4 * time.Second)
doBroadcast(buf1, buf2)
for range time.Tick(32 * time.Second) {
doBroadcast(buf1, buf2)
}
}
func doBroadcast(buf1, buf2 []byte) {
ifaces, err := net.Interfaces()
if err != nil {
log.Printf("Failed to list interfaces: %v", err)
return
}
for _, iface := range ifaces {
if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagRunning == 0 {
continue
}
if iface.Flags&net.FlagPointToPoint != 0 {
continue
}
if iface.Flags&net.FlagBroadcast == 0 {
continue
}
addrs, err := iface.Addrs()
if err != nil {
log.Printf("Failed to get interface addresses: %v", err)
continue
}
for _, addr := range addrs {
ipNet, ok := addr.(*net.IPNet)
if !ok {
continue
}
ip4 := ipNet.IP.To4()
if ip4 == nil {
continue
}
ip, ok := lastAddr(ipNet)
if !ok {
log.Printf("Failed to find broadcast address: %v", ipNet)
continue
}
log.Printf("Broadcasting on address: %v", ip)
//addr := netip.AddrPortFrom(ip, 456)
}
}
}
// works when the n is a prefix, otherwise...
func lastAddr(n *net.IPNet) (netip.Addr, bool) {
ip := make(net.IP, len(n.IP.To4()))
binary.BigEndian.PutUint32(ip, binary.BigEndian.Uint32(n.IP.To4())|
^binary.BigEndian.Uint32(net.IP(n.Mask).To4()))
return netip.AddrFromSlice(ip)
}

View File

@ -3,18 +3,11 @@ package node
import (
"log"
"net"
"net/netip"
"time"
"golang.org/x/crypto/nacl/sign"
)
var (
signOverhead = 64
multicastIP = netip.AddrFrom4([4]byte{224, 0, 0, 157})
multicastAddr = net.UDPAddrFromAddrPort(netip.AddrPortFrom(multicastIP, 4560))
)
func localDiscovery() {
conn, err := net.ListenMulticastUDP("udp", nil, multicastAddr)
if err != nil {
@ -32,8 +25,9 @@ func sendLocalDiscovery(conn *net.UDPConn) {
buf2 = make([]byte, bufferSize)
)
for range time.Tick(16 * time.Second) {
for range time.Tick(32 * time.Second) {
signed := buildLocalDiscoveryPacket(buf1, buf2)
log.Printf("Sending discovery packet...")
if _, err := conn.WriteToUDP(signed, multicastAddr); err != nil {
log.Printf("Failed to write multicast UDP packet: %v", err)
}
@ -51,21 +45,24 @@ func recvLocalDiscovery(conn *net.UDPConn) {
if err != nil {
log.Fatalf("Failed to read from UDP port (multicast): %v", err)
}
log.Printf("Got local discovery packet...")
raw = raw[:n]
h, ok := openLocalDiscoveryPacket(raw, buf)
if !ok {
log.Printf("Failed to open discovery packet?")
continue
}
pkt := controlPacket{
msg := controlMsg[localDiscoveryPacket]{
SrcIP: h.SourceIP,
SrcAddr: remoteAddr,
Payload: localDiscoveryPacket{},
Packet: localDiscoveryPacket{},
}
log.Printf("Got local discovery packet from %d/%v...", h.SourceIP, remoteAddr)
select {
case controlPackets[h.SourceIP] <- pkt:
case messages[h.SourceIP] <- msg:
default:
}
}
@ -92,6 +89,7 @@ func openLocalDiscoveryPacket(raw, buf []byte) (h header, ok bool) {
h.Parse(raw[signOverhead:])
route := routingTable[h.SourceIP].Load()
if route == nil || route.PubSignKey == nil {
log.Printf("Missing signing key")
ok = false
return
}

View File

@ -11,6 +11,7 @@ import (
"net/netip"
"os"
"runtime/debug"
"time"
"vppn/m"
)
@ -26,13 +27,11 @@ func Main() {
var (
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 == "" {
@ -45,7 +44,7 @@ func Main() {
return
}
main(listenIP, uint16(port))
main(listenIP)
}
func mainInit(initURL string) {
@ -74,20 +73,18 @@ func mainInit(initURL string) {
// ----------------------------------------------------------------------------
func main(listenIP string, port uint16) {
func main(listenIP string) {
config, err := loadPeerConfig(netName)
if err != nil {
log.Fatalf("Failed to load configuration: %v", err)
}
port = determinePort(config.Port, port)
iface, err := openInterface(config.Network, config.PeerIP, netName)
if err != nil {
log.Fatalf("Failed to open interface: %v", err)
}
myAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", listenIP, port))
myAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", listenIP, config.Port))
if err != nil {
log.Fatalf("Failed to resolve UDP address: %v", err)
}
@ -129,6 +126,17 @@ func main(listenIP string, port uint16) {
go localDiscovery()
}
go func() {
for range time.Tick(pingInterval) {
for i := range messages {
select {
case messages[i] <- pingTimerMsg{}:
default:
}
}
}
}()
go newHubPoller(config).Run()
go readFromConn(conn)
readFromIFace(iface)
@ -136,18 +144,6 @@ func main(listenIP string, port uint16) {
// ----------------------------------------------------------------------------
func determinePort(confPort, portFromCommandLine uint16) uint16 {
if portFromCommandLine != 0 {
return portFromCommandLine
}
if confPort != 0 {
return confPort
}
return 456
}
// ----------------------------------------------------------------------------
func readFromConn(conn *net.UDPConn) {
defer panicHandler()
@ -218,32 +214,22 @@ func handleControlPacket(addr netip.AddrPort, h header, data, decBuf []byte) {
return
}
pkt := controlPacket{
SrcIP: h.SourceIP,
SrcAddr: addr,
}
if err := pkt.ParsePayload(out); err != nil {
msg, err := parseControlMsg(h.SourceIP, addr, out)
if err != nil {
log.Printf("Failed to parse control packet: %v", err)
return
}
switch pkt.Payload.(type) {
case addrDiscoveryPacket:
select {
case discoveryPackets <- pkt:
default:
log.Printf("Dropping discovery packet.")
if dm, ok := msg.(controlMsg[addrDiscoveryPacket]); ok {
discoveryMessages <- dm
return
}
default:
select {
case controlPackets[h.SourceIP] <- pkt:
case messages[h.SourceIP] <- msg:
default:
log.Printf("Dropping control packet.")
}
}
}

View File

@ -1 +1,66 @@
package node
import (
"net/netip"
"vppn/m"
)
// ----------------------------------------------------------------------------
type controlMsg[T any] struct {
SrcIP byte
SrcAddr netip.AddrPort
Packet T
}
func parseControlMsg(srcIP byte, srcAddr netip.AddrPort, buf []byte) (any, error) {
switch buf[0] {
case packetTypeSyn:
packet, err := parseSynPacket(buf)
return controlMsg[synPacket]{
SrcIP: srcIP,
SrcAddr: srcAddr,
Packet: packet,
}, err
case packetTypeSynAck:
packet, err := parseSynAckPacket(buf)
return controlMsg[synAckPacket]{
SrcIP: srcIP,
SrcAddr: srcAddr,
Packet: packet,
}, err
case packetTypeProbe:
packet, err := parseProbePacket(buf)
return controlMsg[probePacket]{
SrcIP: srcIP,
SrcAddr: srcAddr,
Packet: packet,
}, err
case packetTypeAddrDiscovery:
packet, err := parseAddrDiscoveryPacket(buf)
return controlMsg[addrDiscoveryPacket]{
SrcIP: srcIP,
SrcAddr: srcAddr,
Packet: packet,
}, err
default:
return nil, errUnknownPacketType
}
}
// ----------------------------------------------------------------------------
type peerUpdateMsg struct {
Peer *m.Peer
}
// ----------------------------------------------------------------------------
type pingTimerMsg struct{}
// ----------------------------------------------------------------------------

View File

@ -20,30 +20,6 @@ const (
// ----------------------------------------------------------------------------
type controlPacket struct {
SrcIP byte
SrcAddr netip.AddrPort
Payload any
}
func (p *controlPacket) ParsePayload(buf []byte) (err error) {
switch buf[0] {
case packetTypeSyn:
p.Payload, err = parseSynPacket(buf)
case packetTypeSynAck:
p.Payload, err = parseSynAckPacket(buf)
case packetTypeProbe:
p.Payload, err = parseProbePacket(buf)
case packetTypeAddrDiscovery:
p.Payload, err = parseAddrDiscoveryPacket(buf)
default:
return errUnknownPacketType
}
return err
}
// ----------------------------------------------------------------------------
type synPacket struct {
TraceID uint64 // TraceID to match response w/ request.
SharedKey [32]byte // Our shared key.

View File

@ -29,8 +29,7 @@ type peerSupervisor struct {
remotePub bool
// Incoming events.
peerUpdates chan *m.Peer
controlPackets chan controlPacket
messages chan any
// Buffers for sending control packets.
buf1 []byte
@ -41,8 +40,7 @@ func newPeerSupervisor(i int) *peerSupervisor {
return &peerSupervisor{
published: routingTable[i],
remoteIP: byte(i),
peerUpdates: peerUpdates[i],
controlPackets: controlPackets[i],
messages: messages[i],
buf1: make([]byte, bufferSize),
buf2: make([]byte, bufferSize),
}
@ -95,7 +93,12 @@ func (s *peerSupervisor) publish() {
// ----------------------------------------------------------------------------
func (s *peerSupervisor) noPeer() stateFunc {
return s.peerUpdate(<-s.peerUpdates)
for {
rawMsg := <-s.messages
if msg, ok := rawMsg.(peerUpdateMsg); ok {
return s.peerUpdate(msg.Peer)
}
}
}
// ----------------------------------------------------------------------------
@ -150,22 +153,19 @@ func (s *peerSupervisor) server() stateFunc {
var (
syn synPacket
timeoutTimer = time.NewTimer(timeoutInterval)
lastSeen = time.Now()
)
// Timer will be restarted once we have established a connection.
timeoutTimer.Stop()
for {
select {
case peer := <-s.peerUpdates:
return s.peerUpdate(peer)
rawMsg := <-s.messages
switch msg := rawMsg.(type) {
case pkt := <-s.controlPackets:
switch p := pkt.Payload.(type) {
case peerUpdateMsg:
return s.peerUpdate(msg.Peer)
case synPacket:
timeoutTimer.Reset(timeoutInterval)
case controlMsg[synPacket]:
p := msg.Packet
lastSeen = time.Now()
// Before we can respond to this packet, we need to make sure the
// route is setup properly.
@ -183,7 +183,7 @@ func (s *peerSupervisor) server() stateFunc {
s.staged.Up = true
s.staged.Direct = syn.Direct
s.staged.DataCipher = newDataCipherFromKey(syn.SharedKey)
s.staged.RemoteAddr = pkt.SrcAddr
s.staged.RemoteAddr = msg.SrcAddr
s.publish()
}
@ -206,21 +206,22 @@ func (s *peerSupervisor) server() stateFunc {
probe := probePacket{TraceID: newTraceID()}
s.sendControlPacketTo(probe, syn.FromAddr)
case probePacket:
if pkt.SrcAddr.IsValid() {
s.sendControlPacketTo(probePacket{TraceID: p.TraceID}, pkt.SrcAddr)
} else {
case controlMsg[probePacket]:
if !msg.SrcAddr.IsValid() {
logf("Invalid probe address")
continue
}
}
s.sendControlPacketTo(probePacket{TraceID: msg.Packet.TraceID}, msg.SrcAddr)
case <-timeoutTimer.C:
case pingTimerMsg:
if time.Since(lastSeen) > timeoutInterval {
logf("Connection timeout")
s.staged.Up = false
s.publish()
}
}
}
}
// ----------------------------------------------------------------------------
@ -237,38 +238,36 @@ func (s *peerSupervisor) client() stateFunc {
FromAddr: getLocalAddr(),
}
lastSeen = time.Now()
ack synAckPacket
probe probePacket
probeAddr netip.AddrPort
remoteAddr netip.AddrPort
localProbe probePacket
localProbeAddr netip.AddrPort
timeoutTimer = time.NewTimer(timeoutInterval)
pingTimer = time.NewTimer(pingInterval)
lastLocalAddr netip.AddrPort
)
defer timeoutTimer.Stop()
defer pingTimer.Stop()
s.sendControlPacket(syn)
for {
select {
rawMsg := <-s.messages
switch msg := rawMsg.(type) {
case peer := <-s.peerUpdates:
return s.peerUpdate(peer)
case peerUpdateMsg:
return s.peerUpdate(msg.Peer)
case pkt := <-s.controlPackets:
switch p := pkt.Payload.(type) {
case controlMsg[synAckPacket]:
p := msg.Packet
case synAckPacket:
if p.TraceID != syn.TraceID {
continue // Hmm...
}
ack = p
timeoutTimer.Reset(timeoutInterval)
lastSeen = time.Now()
ack = msg.Packet
if !s.staged.Up {
if s.staged.Direct {
@ -281,47 +280,64 @@ func (s *peerSupervisor) client() stateFunc {
s.publish()
}
case probePacket:
case controlMsg[probePacket]:
if s.staged.Direct {
continue
}
if p.TraceID != probe.TraceID {
p := msg.Packet
if p.TraceID != localProbe.TraceID && p.TraceID != probe.TraceID {
continue
}
// Upgrade connection.
logf("UP - Direct")
s.staged.Direct = true
if p.TraceID == localProbe.TraceID {
logf("UP - Local")
s.staged.RemoteAddr = localProbeAddr
} else {
logf("UP - Direct")
s.staged.RemoteAddr = probeAddr
}
s.publish()
syn.TraceID = newTraceID()
syn.Direct = true
syn.FromAddr = getLocalAddr()
s.sendControlPacket(syn)
}
case <-pingTimer.C:
// Send syn.
syn.FromAddr = getLocalAddr()
if syn.FromAddr != remoteAddr {
syn.TraceID = newTraceID()
remoteAddr = syn.FromAddr
}
s.sendControlPacket(syn)
pingTimer.Reset(pingInterval)
case controlMsg[localDiscoveryPacket]:
if s.staged.Direct {
continue
}
// TODO: Check if we have local address.
// TODO: Send local probe
// Send probe.
//
// The source port will be the multicast port, so we'll have to
// construct the correct address using the peer's listed port.
localProbe = probePacket{TraceID: newTraceID()}
localProbeAddr = netip.AddrPortFrom(msg.SrcAddr.Addr(), s.peer.Port)
s.sendControlPacketTo(localProbe, localProbeAddr)
case pingTimerMsg:
if time.Since(lastSeen) > timeoutInterval {
logf("Connection timeout")
return s.peerUpdate(s.peer)
}
syn.FromAddr = getLocalAddr()
if syn.FromAddr != lastLocalAddr {
syn.TraceID = newTraceID()
lastLocalAddr = syn.FromAddr
}
s.sendControlPacket(syn)
if s.staged.Direct {
continue
}
if !ack.FromAddr.IsValid() {
continue
@ -331,10 +347,6 @@ func (s *peerSupervisor) client() stateFunc {
probeAddr = ack.FromAddr
s.sendControlPacketTo(probe, ack.FromAddr)
case <-timeoutTimer.C:
logf("Connection timeout")
return s.peerUpdate(s.peer)
}
}
}

View File

@ -1 +0,0 @@
package node

View File

@ -1 +0,0 @@
## Stage1: Point-to-point Tunnel w/ no Encryption

View File

@ -1,32 +0,0 @@
package stage1
import (
"fmt"
"net"
"net/netip"
"runtime/debug"
)
func RunClient(serverAddrStr string) {
defer func() {
if r := recover(); r != nil {
fmt.Printf("%v", r)
debug.PrintStack()
}
}()
iface, err := openInterface(network, clientIP, netName)
must(err)
myAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", port))
must(err)
conn, err := net.ListenUDP("udp", myAddr)
must(err)
serverAddr, err := netip.ParseAddrPort(fmt.Sprintf("%s:%d", serverAddrStr, port))
must(err)
go readFromIFace(iface, conn, serverIP, serverAddr)
readFromConn(iface, conn)
}

View File

@ -1,7 +0,0 @@
#!/bin/bash
go build
scp client kevin:/home/jdl/tmp
ssh root@home "setcap cap_net_admin+iep /home/jdl/tmp/client"
ssh home "/home/jdl/tmp/client 192.168.1.21"

View File

@ -1,14 +0,0 @@
package main
import (
"log"
"os"
"vppn/stage1"
)
func main() {
if len(os.Args) != 2 {
log.Fatalf("Usage: %s <addr:port>", os.Args[0])
}
stage1.RunClient(os.Args[1])
}

View File

@ -1,4 +0,0 @@
#!/bin/bash
go build
sudo setcap cap_net_admin+iep server

View File

@ -1,14 +0,0 @@
package main
import (
"log"
"os"
"vppn/stage1"
)
func main() {
if len(os.Args) != 2 {
log.Fatalf("Usage: %s <addr>", os.Args[0])
}
stage1.RunServer(os.Args[1])
}

View File

@ -1,142 +0,0 @@
package stage1
import (
"fmt"
"io"
"net"
"os"
"syscall"
"golang.org/x/sys/unix"
)
const (
if_mtu = 1200
if_queue_len = 1000
)
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(if_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(if_queue_len)
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
}

View File

@ -1,109 +0,0 @@
package stage1
import (
"fmt"
"io"
"log"
"net"
"net/netip"
"runtime/debug"
)
var (
network = []byte{10, 1, 1, 0}
serverIP = byte(1)
clientIP = byte(2)
port = uint16(5151)
netName = "testnet"
bufferSize = if_mtu * 2
)
func must(err error) {
if err != nil {
panic(err)
}
}
func RunServer(clientAddrStr string) {
defer func() {
if r := recover(); r != nil {
fmt.Printf("%v", r)
debug.PrintStack()
}
}()
iface, err := openInterface(network, serverIP, netName)
must(err)
myAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", port))
must(err)
conn, err := net.ListenUDP("udp", myAddr)
must(err)
clientAddr, err := netip.ParseAddrPort(fmt.Sprintf("%s:%d", clientAddrStr, port))
must(err)
go readFromIFace(iface, conn, clientIP, clientAddr)
readFromConn(iface, conn)
}
func readFromIFace(iface io.ReadWriteCloser, conn *net.UDPConn, remoteIP byte, remoteAddr netip.AddrPort) {
var (
n int
packet = make([]byte, bufferSize)
version byte
ip byte
err error
)
for {
n, err = iface.Read(packet[:bufferSize])
must(err)
packet = packet[:n]
if len(packet) < 20 {
log.Printf("Dropping small packet: %d", n)
continue
}
packet = packet[:n]
version = packet[0] >> 4
switch version {
case 4:
ip = packet[19]
case 6:
ip = packet[39]
default:
log.Printf("Dropping packet with IP version: %d", version)
continue
}
if ip != remoteIP {
log.Printf("Dropping packet for incorrect IP: %d", ip)
continue
}
_, err = conn.WriteToUDPAddrPort(packet, remoteAddr)
must(err)
}
}
func readFromConn(iface io.ReadWriteCloser, conn *net.UDPConn) {
var (
n int
packet = make([]byte, bufferSize)
err error
)
for {
// We assume that we're only receiving packets from one source.
n, err = conn.Read(packet[:bufferSize])
must(err)
packet = packet[:n]
_, err = iface.Write(packet)
must(err)
}
}

View File

@ -1 +0,0 @@
package stage1

View File

@ -1,4 +0,0 @@
## Stage2:
* Point-to-point Tunnel w/ no Encryption
* Server gets client's addr from first packet

View File

@ -1,35 +0,0 @@
package stage2
import (
"fmt"
"net"
"net/netip"
"runtime/debug"
)
func RunClient(serverAddrStr string) {
defer func() {
if r := recover(); r != nil {
fmt.Printf("%v", r)
debug.PrintStack()
}
}()
iface, err := openInterface(network, clientIP, netName)
must(err)
myAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", port))
must(err)
conn, err := net.ListenUDP("udp", myAddr)
must(err)
serverAddr, err := netip.ParseAddrPort(fmt.Sprintf("%s:%d", serverAddrStr, port))
must(err)
_, err = conn.WriteToUDPAddrPort([]byte{1, 2, 3, 4, 5, 6, 7, 8}, serverAddr)
must(err)
go readFromIFace(iface, conn, serverIP, serverAddr)
readFromConn(iface, conn)
}

View File

@ -1,5 +0,0 @@
#!/bin/bash
go build
sudo setcap cap_net_admin+iep ./client
./client 144.76.78.93

View File

@ -1,14 +0,0 @@
package main
import (
"log"
"os"
"vppn/stage2"
)
func main() {
if len(os.Args) != 2 {
log.Fatalf("Usage: %s <addr:port>", os.Args[0])
}
stage2.RunClient(os.Args[1])
}

View File

@ -1,6 +0,0 @@
#!/bin/bash
go build
scp server kevin:/home/jdl/tmp/
ssh root@kevin "sudo setcap cap_net_admin+iep /home/jdl/tmp/server"
ssh kevin "/home/jdl/tmp/server"

View File

@ -1,7 +0,0 @@
package main
import "vppn/stage2"
func main() {
stage2.RunServer()
}

View File

@ -1,142 +0,0 @@
package stage2
import (
"fmt"
"io"
"net"
"os"
"syscall"
"golang.org/x/sys/unix"
)
const (
if_mtu = 1200
if_queue_len = 1000
)
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(if_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(if_queue_len)
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
}

View File

@ -1,112 +0,0 @@
package stage2
import (
"fmt"
"io"
"log"
"net"
"net/netip"
"runtime/debug"
)
var (
network = []byte{10, 1, 1, 0}
serverIP = byte(1)
clientIP = byte(2)
port = uint16(5151)
netName = "testnet"
bufferSize = if_mtu * 2
)
func must(err error) {
if err != nil {
panic(err)
}
}
func RunServer() {
defer func() {
if r := recover(); r != nil {
fmt.Printf("%v", r)
debug.PrintStack()
}
}()
iface, err := openInterface(network, serverIP, netName)
must(err)
myAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", port))
must(err)
conn, err := net.ListenUDP("udp", myAddr)
must(err)
// Get remoteAddr from a packet.
buf := make([]byte, 8)
_, remoteAddr, err := conn.ReadFromUDPAddrPort(buf)
log.Printf("Got remote addr: %v", remoteAddr)
must(err)
go readFromIFace(iface, conn, clientIP, remoteAddr)
readFromConn(iface, conn)
}
func readFromIFace(iface io.ReadWriteCloser, conn *net.UDPConn, remoteIP byte, remoteAddr netip.AddrPort) {
var (
n int
packet = make([]byte, bufferSize)
version byte
ip byte
err error
)
for {
n, err = iface.Read(packet[:bufferSize])
must(err)
packet = packet[:n]
if len(packet) < 20 {
log.Printf("Dropping small packet: %d", n)
continue
}
packet = packet[:n]
version = packet[0] >> 4
switch version {
case 4:
ip = packet[19]
case 6:
ip = packet[39]
default:
log.Printf("Dropping packet with IP version: %d", version)
continue
}
if ip != remoteIP {
log.Printf("Dropping packet for incorrect IP: %d", ip)
continue
}
_, err = conn.WriteToUDPAddrPort(packet, remoteAddr)
must(err)
}
}
func readFromConn(iface io.ReadWriteCloser, conn *net.UDPConn) {
var (
n int
packet = make([]byte, bufferSize)
err error
)
for {
// We assume that we're only receiving packets from one source.
n, err = conn.Read(packet[:bufferSize])
must(err)
packet = packet[:n]
_, err = iface.Write(packet)
must(err)
}
}

View File

@ -1 +0,0 @@
package stage2

View File

@ -1,16 +0,0 @@
## Stage3:
* Point-to-point Tunnel w/ no Encryption
* Server gets client's addr from first packet
* Add packet counter to detect skipped and late packets
### Learnings
* Directional packet loss is an issue.
* Sending to hetzner: ~380 Mbits/sec
* From hetzner: ~800 Mbits/sec
* Runs of dropped packets are generally small < 30
* Saw a few cases of 100-200
* Runs of correctly-sequenced packets are generally >> drops
* Late packets aren't so common
* Dropping late packets causes large slow-down.

View File

@ -1,35 +0,0 @@
package stage3
import (
"fmt"
"net"
"net/netip"
"runtime/debug"
)
func RunClient(serverAddrStr string) {
defer func() {
if r := recover(); r != nil {
fmt.Printf("%v", r)
debug.PrintStack()
}
}()
iface, err := openInterface(network, clientIP, netName)
must(err)
myAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", port))
must(err)
conn, err := net.ListenUDP("udp", myAddr)
must(err)
serverAddr, err := netip.ParseAddrPort(fmt.Sprintf("%s:%d", serverAddrStr, port))
must(err)
_, err = conn.WriteToUDPAddrPort([]byte{1, 2, 3, 4, 5, 6, 7, 8}, serverAddr)
must(err)
go readFromIFace(iface, conn, clientIP, serverIP, serverAddr)
readFromConn(iface, conn, serverIP)
}

View File

@ -1,5 +0,0 @@
#!/bin/bash
go build
sudo setcap cap_net_admin+iep ./client
./client 144.76.78.93

View File

@ -1,14 +0,0 @@
package main
import (
"log"
"os"
"vppn/stage3"
)
func main() {
if len(os.Args) != 2 {
log.Fatalf("Usage: %s <addr:port>", os.Args[0])
}
stage3.RunClient(os.Args[1])
}

View File

@ -1,7 +0,0 @@
#!/bin/bash
go build
ssh kevin "killall server"
scp server kevin:/home/jdl/tmp/
ssh root@kevin "sudo setcap cap_net_admin+iep /home/jdl/tmp/server"
ssh kevin "/home/jdl/tmp/server"

View File

@ -1,7 +0,0 @@
package main
import "vppn/stage3"
func main() {
stage3.RunServer()
}

View File

@ -1,142 +0,0 @@
package stage3
import (
"fmt"
"io"
"net"
"os"
"syscall"
"golang.org/x/sys/unix"
)
const (
if_mtu = 1200
if_queue_len = 1000
)
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(if_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(if_queue_len)
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
}

View File

@ -1,23 +0,0 @@
package stage3
import "unsafe"
const headerSize = 9
type packetHeader struct {
SrcIP byte
Counter uint64
}
func (h packetHeader) Marshal(buf []byte) int {
buf = buf[:9]
buf[0] = h.SrcIP
*(*uint64)(unsafe.Pointer(&buf[1])) = h.Counter
return headerSize
}
func (h *packetHeader) Parse(buf []byte) int {
h.SrcIP = buf[0]
h.Counter = *(*uint64)(unsafe.Pointer(&buf[1]))
return headerSize
}

View File

@ -1,22 +0,0 @@
package stage3
import (
"reflect"
"testing"
)
func TestPacketHeader(t *testing.T) {
b := make([]byte, 1024)
h := packetHeader{
SrcIP: 8,
Counter: 2354,
}
n := h.Marshal(b)
h2 := packetHeader{}
h2.Parse(b[:n])
if !reflect.DeepEqual(h, h2) {
t.Fatal(h, h2)
}
}

View File

@ -1,147 +0,0 @@
package stage3
import (
"fmt"
"io"
"log"
"net"
"net/netip"
"runtime/debug"
)
var (
network = []byte{10, 1, 1, 0}
serverIP = byte(1)
clientIP = byte(2)
port = uint16(5151)
netName = "testnet"
bufferSize = if_mtu * 2
)
func must(err error) {
if err != nil {
panic(err)
}
}
func RunServer() {
defer func() {
if r := recover(); r != nil {
fmt.Printf("%v", r)
debug.PrintStack()
}
}()
iface, err := openInterface(network, serverIP, netName)
must(err)
myAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", port))
must(err)
conn, err := net.ListenUDP("udp", myAddr)
must(err)
// Get remoteAddr from a packet.
buf := make([]byte, 8)
_, remoteAddr, err := conn.ReadFromUDPAddrPort(buf)
log.Printf("Got remote addr: %v", remoteAddr)
must(err)
go readFromIFace(iface, conn, serverIP, clientIP, remoteAddr)
readFromConn(iface, conn, clientIP)
}
func readFromIFace(iface io.ReadWriteCloser, conn *net.UDPConn, localIP, remoteIP byte, remoteAddr netip.AddrPort) {
var (
n int
packet = make([]byte, bufferSize)
version byte
ip byte
err error
counter uint64
buf = make([]byte, bufferSize)
)
for {
n, err = iface.Read(packet[:bufferSize])
must(err)
packet = packet[:n]
if len(packet) < 20 {
log.Printf("Dropping small packet: %d", n)
continue
}
packet = packet[:n]
version = packet[0] >> 4
switch version {
case 4:
ip = packet[19]
case 6:
ip = packet[39]
default:
log.Printf("Dropping packet with IP version: %d", version)
continue
}
if ip != remoteIP {
log.Printf("Dropping packet for incorrect IP: %d", ip)
continue
}
h := packetHeader{SrcIP: localIP, Counter: counter}
counter++
buf = buf[:headerSize+len(packet)]
h.Marshal(buf)
copy(buf[headerSize:], packet)
_, err = conn.WriteToUDPAddrPort(buf, remoteAddr)
must(err)
}
}
func readFromConn(iface io.ReadWriteCloser, conn *net.UDPConn, remoteIP byte) {
var (
n int
packet = make([]byte, bufferSize)
err error
counter uint64
run uint64
h packetHeader
)
for {
// We assume that we're only receiving packets from one source.
n, err = conn.Read(packet[:bufferSize])
must(err)
packet = packet[:n]
if len(packet) < headerSize {
fmt.Print("_")
continue
}
h.Parse(packet)
if h.SrcIP != remoteIP {
fmt.Print("?")
continue
}
if h.Counter == counter+1 {
run++
counter = h.Counter
} else if h.Counter > counter+1 {
fmt.Printf("x(%d/%d)", h.Counter-counter+1, run)
run = 0
counter = h.Counter
} else if h.Counter <= counter {
//log.Printf("Skipped late packet: -%d", counter-h.Counter)
//continue
fmt.Print("<")
}
_, err = iface.Write(packet[headerSize:])
must(err)
}
}

View File

@ -1 +0,0 @@
package stage3