WIP: cleanup. Local peer discovery working.
This commit is contained in:
		| @@ -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) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										103
									
								
								hub/api/api.go
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								hub/api/api.go
									
									
									
									
									
								
							| @@ -24,7 +24,6 @@ var migrations embed.FS | |||||||
| type API struct { | type API struct { | ||||||
| 	db          *sql.DB | 	db          *sql.DB | ||||||
| 	lock        sync.Mutex | 	lock        sync.Mutex | ||||||
| 	peerIntents map[string]PeerCreateArgs |  | ||||||
| 	initIntents map[string]byte // Map from intent key to peer IP | 	initIntents map[string]byte // Map from intent key to peer IP | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -40,7 +39,6 @@ func New(dbPath string) (*API, error) { | |||||||
|  |  | ||||||
| 	a := &API{ | 	a := &API{ | ||||||
| 		db:          sqlDB, | 		db:          sqlDB, | ||||||
| 		peerIntents: map[string]PeerCreateArgs{}, |  | ||||||
| 		initIntents: map[string]byte{}, | 		initIntents: map[string]byte{}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -153,34 +151,6 @@ func (a *API) Peer_CreateNew(p *Peer) error { | |||||||
| 	return db.Peer_Insert(a.db, p) | 	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 | // Create the intention to initialize a peer. The returned code is used to | ||||||
| // complete the peer initialization. The code is valid for 5 minutes. | // complete the peer initialization. The code is valid for 5 minutes. | ||||||
| func (a *API) Peer_CreateInitIntent(peerIP byte) string { | func (a *API) Peer_CreateInitIntent(peerIP byte) string { | ||||||
| @@ -252,79 +222,6 @@ func (a *API) Peer_Init(initCode string) (*m.PeerConfig, error) { | |||||||
| 	}, nil | 	}, 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 { | func (a *API) Peer_Update(p *Peer) error { | ||||||
| 	a.lock.Lock() | 	a.lock.Lock() | ||||||
| 	defer a.lock.Unlock() | 	defer a.lock.Unlock() | ||||||
|   | |||||||
| @@ -1 +0,0 @@ | |||||||
| package api |  | ||||||
| @@ -208,7 +208,7 @@ func (a *App) _adminPeerInit(s *api.Session, w http.ResponseWriter, r *http.Requ | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	code := a.api.Peer_CreateInitIntent(peerIP) | 	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 { | 	return a.render("/admin-peer-init.html", w, struct { | ||||||
| 		Session    *api.Session | 		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}) | 	}{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 { | func (a *App) _adminPeerView(s *api.Session, w http.ResponseWriter, r *http.Request) error { | ||||||
| 	var peerIP byte | 	var peerIP byte | ||||||
| 	err := webutil.NewFormScanner(r.Form).Scan("PeerIP", &peerIP).Error() | 	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) | 	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 { | func (a *App) _peerFetchState(w http.ResponseWriter, r *http.Request) error { | ||||||
| 	_, apiKey, ok := r.BasicAuth() | 	_, apiKey, ok := r.BasicAuth() | ||||||
| 	if !ok { | 	if !ok { | ||||||
| @@ -392,6 +367,7 @@ func (a *App) _peerFetchState(w http.ResponseWriter, r *http.Request) error { | |||||||
| 				Port:       p.Port, | 				Port:       p.Port, | ||||||
| 				Relay:      p.Relay, | 				Relay:      p.Relay, | ||||||
| 				PubKey:     p.PubKey, | 				PubKey:     p.PubKey, | ||||||
|  | 				PubSignKey: p.PubSignKey, | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -20,15 +20,12 @@ func (a *App) registerRoutes() { | |||||||
| 	a.handleSignedIn("GET  /admin/peer/create/", a._adminPeerCreate) | 	a.handleSignedIn("GET  /admin/peer/create/", a._adminPeerCreate) | ||||||
| 	a.handleSignedIn("POST /admin/peer/create/", a._adminPeerCreateSubmit) | 	a.handleSignedIn("POST /admin/peer/create/", a._adminPeerCreateSubmit) | ||||||
| 	a.handleSignedIn("GET  /admin/peer/init/", a._adminPeerInit) | 	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/view/", a._adminPeerView) | ||||||
| 	a.handleSignedIn("GET  /admin/peer/edit/", a._adminPeerEdit) | 	a.handleSignedIn("GET  /admin/peer/edit/", a._adminPeerEdit) | ||||||
| 	a.handleSignedIn("POST /admin/peer/edit/", a._adminPeerEditSubmit) | 	a.handleSignedIn("POST /admin/peer/edit/", a._adminPeerEditSubmit) | ||||||
| 	a.handleSignedIn("GET  /admin/peer/delete/", a._adminPeerDelete) | 	a.handleSignedIn("GET  /admin/peer/delete/", a._adminPeerDelete) | ||||||
| 	a.handleSignedIn("POST /admin/peer/delete/", a._adminPeerDeleteSubmit) | 	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/init/", a._peerInit) | ||||||
| 	a.handlePeer("GET /peer/fetch-state/", a._peerFetchState) | 	a.handlePeer("GET /peer/fetch-state/", a._peerFetchState) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1 +0,0 @@ | |||||||
| package hub |  | ||||||
| @@ -13,21 +13,17 @@ func addrDiscoveryServer() { | |||||||
| 	) | 	) | ||||||
|  |  | ||||||
| 	for { | 	for { | ||||||
| 		pkt := <-discoveryPackets | 		msg := <-discoveryMessages | ||||||
|  | 		p := msg.Packet | ||||||
|  |  | ||||||
| 		p, ok := pkt.Payload.(addrDiscoveryPacket) | 		route := routingTable[msg.SrcIP].Load() | ||||||
| 		if !ok { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		route := routingTable[pkt.SrcIP].Load() |  | ||||||
| 		if route == nil || !route.RemoteAddr.IsValid() { | 		if route == nil || !route.RemoteAddr.IsValid() { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		_sendControlPacket(addrDiscoveryPacket{ | 		_sendControlPacket(addrDiscoveryPacket{ | ||||||
| 			TraceID: p.TraceID, | 			TraceID: p.TraceID, | ||||||
| 			ToAddr:  pkt.SrcAddr, | 			ToAddr:  msg.SrcAddr, | ||||||
| 		}, *route, buf1, buf2) | 		}, *route, buf1, buf2) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -46,9 +42,9 @@ func addrDiscoveryClient() { | |||||||
|  |  | ||||||
| 	for { | 	for { | ||||||
| 		select { | 		select { | ||||||
| 		case pkt := <-discoveryPackets: | 		case msg := <-discoveryMessages: | ||||||
| 			p, ok := pkt.Payload.(addrDiscoveryPacket) | 			p := msg.Packet | ||||||
| 			if !ok || p.TraceID != addrPacket.TraceID || !p.ToAddr.IsValid() || p.ToAddr == lAddr { | 			if p.TraceID != addrPacket.TraceID || !p.ToAddr.IsValid() || p.ToAddr == lAddr { | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
|  |  | ||||||
| go build |  | ||||||
| sudo setcap cap_net_admin+iep ./client |  | ||||||
| ./client 144.76.78.93 |  | ||||||
| @@ -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() |  | ||||||
| } |  | ||||||
| @@ -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" |  | ||||||
| @@ -1,8 +0,0 @@ | |||||||
| package main |  | ||||||
|  |  | ||||||
| import "vppn/node" |  | ||||||
|  |  | ||||||
| func main() { |  | ||||||
| 	n := node.NewTmpNodeServer() |  | ||||||
| 	n.RunServer() |  | ||||||
| } |  | ||||||
| @@ -1,10 +1,10 @@ | |||||||
| package node | package node | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"net" | ||||||
| 	"net/netip" | 	"net/netip" | ||||||
| 	"sync/atomic" | 	"sync/atomic" | ||||||
| 	"time" | 	"time" | ||||||
| 	"vppn/m" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| @@ -13,6 +13,12 @@ const ( | |||||||
| 	if_queue_len          = 2048 | 	if_queue_len          = 2048 | ||||||
| 	controlCipherOverhead = 16 | 	controlCipherOverhead = 16 | ||||||
| 	dataCipherOverhead    = 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 { | type peerRoute struct { | ||||||
| @@ -56,18 +62,9 @@ var ( | |||||||
| 		return | 		return | ||||||
| 	}() | 	}() | ||||||
|  |  | ||||||
| 	// Channels for incoming control packets. | 	messages [256]chan any = func() (out [256]chan any) { | ||||||
| 	controlPackets [256]chan controlPacket = func() (out [256]chan controlPacket) { |  | ||||||
| 		for i := range out { | 		for i := range out { | ||||||
| 			out[i] = make(chan controlPacket, 256) | 			out[i] = make(chan any, 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) |  | ||||||
| 		} | 		} | ||||||
| 		return | 		return | ||||||
| 	}() | 	}() | ||||||
| @@ -81,8 +78,10 @@ var ( | |||||||
| 		return | 		return | ||||||
| 	}() | 	}() | ||||||
|  |  | ||||||
|  | 	// Managed by the addrDiscovery* functions. | ||||||
|  | 	discoveryMessages = make(chan controlMsg[addrDiscoveryPacket], 256) | ||||||
|  |  | ||||||
| 	// Managed by the relayManager. | 	// Managed by the relayManager. | ||||||
| 	discoveryPackets = make(chan controlPacket, 256) |  | ||||||
| 	localAddr = &atomic.Pointer[netip.AddrPort]{} | 	localAddr = &atomic.Pointer[netip.AddrPort]{} | ||||||
| 	relayIP   = &atomic.Pointer[byte]{} | 	relayIP   = &atomic.Pointer[byte]{} | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -86,7 +86,7 @@ func (hp *hubPoller) applyNetworkState(state m.NetworkState) { | |||||||
| 	for i, peer := range state.Peers { | 	for i, peer := range state.Peers { | ||||||
| 		if i != int(localIP) { | 		if i != int(localIP) { | ||||||
| 			if peer != nil && peer.Version != hp.versions[i] { | 			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 | 				hp.versions[i] = peer.Version | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -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) |  | ||||||
| } |  | ||||||
| @@ -3,18 +3,11 @@ package node | |||||||
| import ( | import ( | ||||||
| 	"log" | 	"log" | ||||||
| 	"net" | 	"net" | ||||||
| 	"net/netip" |  | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"golang.org/x/crypto/nacl/sign" | 	"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() { | func localDiscovery() { | ||||||
| 	conn, err := net.ListenMulticastUDP("udp", nil, multicastAddr) | 	conn, err := net.ListenMulticastUDP("udp", nil, multicastAddr) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -32,8 +25,9 @@ func sendLocalDiscovery(conn *net.UDPConn) { | |||||||
| 		buf2 = make([]byte, bufferSize) | 		buf2 = make([]byte, bufferSize) | ||||||
| 	) | 	) | ||||||
|  |  | ||||||
| 	for range time.Tick(16 * time.Second) { | 	for range time.Tick(32 * time.Second) { | ||||||
| 		signed := buildLocalDiscoveryPacket(buf1, buf2) | 		signed := buildLocalDiscoveryPacket(buf1, buf2) | ||||||
|  | 		log.Printf("Sending discovery packet...") | ||||||
| 		if _, err := conn.WriteToUDP(signed, multicastAddr); err != nil { | 		if _, err := conn.WriteToUDP(signed, multicastAddr); err != nil { | ||||||
| 			log.Printf("Failed to write multicast UDP packet: %v", err) | 			log.Printf("Failed to write multicast UDP packet: %v", err) | ||||||
| 		} | 		} | ||||||
| @@ -51,21 +45,24 @@ func recvLocalDiscovery(conn *net.UDPConn) { | |||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Fatalf("Failed to read from UDP port (multicast): %v", err) | 			log.Fatalf("Failed to read from UDP port (multicast): %v", err) | ||||||
| 		} | 		} | ||||||
|  | 		log.Printf("Got local discovery packet...") | ||||||
|  |  | ||||||
| 		raw = raw[:n] | 		raw = raw[:n] | ||||||
| 		h, ok := openLocalDiscoveryPacket(raw, buf) | 		h, ok := openLocalDiscoveryPacket(raw, buf) | ||||||
| 		if !ok { | 		if !ok { | ||||||
|  | 			log.Printf("Failed to open discovery packet?") | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		pkt := controlPacket{ | 		msg := controlMsg[localDiscoveryPacket]{ | ||||||
| 			SrcIP:   h.SourceIP, | 			SrcIP:   h.SourceIP, | ||||||
| 			SrcAddr: remoteAddr, | 			SrcAddr: remoteAddr, | ||||||
| 			Payload: localDiscoveryPacket{}, | 			Packet:  localDiscoveryPacket{}, | ||||||
| 		} | 		} | ||||||
|  | 		log.Printf("Got local discovery packet from %d/%v...", h.SourceIP, remoteAddr) | ||||||
|  |  | ||||||
| 		select { | 		select { | ||||||
| 		case controlPackets[h.SourceIP] <- pkt: | 		case messages[h.SourceIP] <- msg: | ||||||
| 		default: | 		default: | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -92,6 +89,7 @@ func openLocalDiscoveryPacket(raw, buf []byte) (h header, ok bool) { | |||||||
| 	h.Parse(raw[signOverhead:]) | 	h.Parse(raw[signOverhead:]) | ||||||
| 	route := routingTable[h.SourceIP].Load() | 	route := routingTable[h.SourceIP].Load() | ||||||
| 	if route == nil || route.PubSignKey == nil { | 	if route == nil || route.PubSignKey == nil { | ||||||
|  | 		log.Printf("Missing signing key") | ||||||
| 		ok = false | 		ok = false | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										56
									
								
								node/main.go
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								node/main.go
									
									
									
									
									
								
							| @@ -11,6 +11,7 @@ import ( | |||||||
| 	"net/netip" | 	"net/netip" | ||||||
| 	"os" | 	"os" | ||||||
| 	"runtime/debug" | 	"runtime/debug" | ||||||
|  | 	"time" | ||||||
| 	"vppn/m" | 	"vppn/m" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -26,13 +27,11 @@ func Main() { | |||||||
| 	var ( | 	var ( | ||||||
| 		initURL  string | 		initURL  string | ||||||
| 		listenIP string | 		listenIP string | ||||||
| 		port     int |  | ||||||
| 	) | 	) | ||||||
|  |  | ||||||
| 	flag.StringVar(&netName, "name", "", "[REQUIRED] The network name.") | 	flag.StringVar(&netName, "name", "", "[REQUIRED] The network name.") | ||||||
| 	flag.StringVar(&initURL, "init-url", "", "Initializes peer from the hub URL.") | 	flag.StringVar(&initURL, "init-url", "", "Initializes peer from the hub URL.") | ||||||
| 	flag.StringVar(&listenIP, "listen-ip", "", "IP address to listen on.") | 	flag.StringVar(&listenIP, "listen-ip", "", "IP address to listen on.") | ||||||
| 	flag.IntVar(&port, "port", 0, "Port to listen on.") |  | ||||||
| 	flag.Parse() | 	flag.Parse() | ||||||
|  |  | ||||||
| 	if netName == "" { | 	if netName == "" { | ||||||
| @@ -45,7 +44,7 @@ func Main() { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	main(listenIP, uint16(port)) | 	main(listenIP) | ||||||
| } | } | ||||||
|  |  | ||||||
| func mainInit(initURL string) { | 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) | 	config, err := loadPeerConfig(netName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatalf("Failed to load configuration: %v", err) | 		log.Fatalf("Failed to load configuration: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	port = determinePort(config.Port, port) |  | ||||||
|  |  | ||||||
| 	iface, err := openInterface(config.Network, config.PeerIP, netName) | 	iface, err := openInterface(config.Network, config.PeerIP, netName) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Fatalf("Failed to open interface: %v", err) | 		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 { | 	if err != nil { | ||||||
| 		log.Fatalf("Failed to resolve UDP address: %v", err) | 		log.Fatalf("Failed to resolve UDP address: %v", err) | ||||||
| 	} | 	} | ||||||
| @@ -129,6 +126,17 @@ func main(listenIP string, port uint16) { | |||||||
| 		go localDiscovery() | 		go localDiscovery() | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	go func() { | ||||||
|  | 		for range time.Tick(pingInterval) { | ||||||
|  | 			for i := range messages { | ||||||
|  | 				select { | ||||||
|  | 				case messages[i] <- pingTimerMsg{}: | ||||||
|  | 				default: | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  |  | ||||||
| 	go newHubPoller(config).Run() | 	go newHubPoller(config).Run() | ||||||
| 	go readFromConn(conn) | 	go readFromConn(conn) | ||||||
| 	readFromIFace(iface) | 	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) { | func readFromConn(conn *net.UDPConn) { | ||||||
|  |  | ||||||
| 	defer panicHandler() | 	defer panicHandler() | ||||||
| @@ -218,32 +214,22 @@ func handleControlPacket(addr netip.AddrPort, h header, data, decBuf []byte) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	pkt := controlPacket{ | 	msg, err := parseControlMsg(h.SourceIP, addr, out) | ||||||
| 		SrcIP:   h.SourceIP, | 	if err != nil { | ||||||
| 		SrcAddr: addr, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err := pkt.ParsePayload(out); err != nil { |  | ||||||
| 		log.Printf("Failed to parse control packet: %v", err) | 		log.Printf("Failed to parse control packet: %v", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	switch pkt.Payload.(type) { | 	if dm, ok := msg.(controlMsg[addrDiscoveryPacket]); ok { | ||||||
|  | 		discoveryMessages <- dm | ||||||
| 	case addrDiscoveryPacket: | 		return | ||||||
| 		select { |  | ||||||
| 		case discoveryPackets <- pkt: |  | ||||||
| 		default: |  | ||||||
| 			log.Printf("Dropping discovery packet.") |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	default: |  | ||||||
| 	select { | 	select { | ||||||
| 		case controlPackets[h.SourceIP] <- pkt: | 	case messages[h.SourceIP] <- msg: | ||||||
| 	default: | 	default: | ||||||
| 		log.Printf("Dropping control packet.") | 		log.Printf("Dropping control packet.") | ||||||
| 	} | 	} | ||||||
| 	} |  | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1 +1,66 @@ | |||||||
| package node | 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{} | ||||||
|  |  | ||||||
|  | // ---------------------------------------------------------------------------- | ||||||
|   | |||||||
| @@ -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 { | type synPacket struct { | ||||||
| 	TraceID   uint64   // TraceID to match response w/ request. | 	TraceID   uint64   // TraceID to match response w/ request. | ||||||
| 	SharedKey [32]byte // Our shared key. | 	SharedKey [32]byte // Our shared key. | ||||||
|   | |||||||
| @@ -29,8 +29,7 @@ type peerSupervisor struct { | |||||||
| 	remotePub bool | 	remotePub bool | ||||||
|  |  | ||||||
| 	// Incoming events. | 	// Incoming events. | ||||||
| 	peerUpdates    chan *m.Peer | 	messages chan any | ||||||
| 	controlPackets chan controlPacket |  | ||||||
|  |  | ||||||
| 	// Buffers for sending control packets. | 	// Buffers for sending control packets. | ||||||
| 	buf1 []byte | 	buf1 []byte | ||||||
| @@ -41,8 +40,7 @@ func newPeerSupervisor(i int) *peerSupervisor { | |||||||
| 	return &peerSupervisor{ | 	return &peerSupervisor{ | ||||||
| 		published: routingTable[i], | 		published: routingTable[i], | ||||||
| 		remoteIP:  byte(i), | 		remoteIP:  byte(i), | ||||||
| 		peerUpdates:    peerUpdates[i], | 		messages:  messages[i], | ||||||
| 		controlPackets: controlPackets[i], |  | ||||||
| 		buf1:      make([]byte, bufferSize), | 		buf1:      make([]byte, bufferSize), | ||||||
| 		buf2:      make([]byte, bufferSize), | 		buf2:      make([]byte, bufferSize), | ||||||
| 	} | 	} | ||||||
| @@ -95,7 +93,12 @@ func (s *peerSupervisor) publish() { | |||||||
| // ---------------------------------------------------------------------------- | // ---------------------------------------------------------------------------- | ||||||
|  |  | ||||||
| func (s *peerSupervisor) noPeer() stateFunc { | 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 ( | 	var ( | ||||||
| 		syn      synPacket | 		syn      synPacket | ||||||
| 		timeoutTimer = time.NewTimer(timeoutInterval) | 		lastSeen = time.Now() | ||||||
| 	) | 	) | ||||||
|  |  | ||||||
| 	// Timer will be restarted once we have established a connection. |  | ||||||
| 	timeoutTimer.Stop() |  | ||||||
|  |  | ||||||
| 	for { | 	for { | ||||||
| 		select { | 		rawMsg := <-s.messages | ||||||
| 		case peer := <-s.peerUpdates: | 		switch msg := rawMsg.(type) { | ||||||
| 			return s.peerUpdate(peer) |  | ||||||
|  |  | ||||||
| 		case pkt := <-s.controlPackets: | 		case peerUpdateMsg: | ||||||
| 			switch p := pkt.Payload.(type) { | 			return s.peerUpdate(msg.Peer) | ||||||
|  |  | ||||||
| 			case synPacket: | 		case controlMsg[synPacket]: | ||||||
| 				timeoutTimer.Reset(timeoutInterval) | 			p := msg.Packet | ||||||
|  | 			lastSeen = time.Now() | ||||||
|  |  | ||||||
| 			// Before we can respond to this packet, we need to make sure the | 			// Before we can respond to this packet, we need to make sure the | ||||||
| 			// route is setup properly. | 			// route is setup properly. | ||||||
| @@ -183,7 +183,7 @@ func (s *peerSupervisor) server() stateFunc { | |||||||
| 				s.staged.Up = true | 				s.staged.Up = true | ||||||
| 				s.staged.Direct = syn.Direct | 				s.staged.Direct = syn.Direct | ||||||
| 				s.staged.DataCipher = newDataCipherFromKey(syn.SharedKey) | 				s.staged.DataCipher = newDataCipherFromKey(syn.SharedKey) | ||||||
| 					s.staged.RemoteAddr = pkt.SrcAddr | 				s.staged.RemoteAddr = msg.SrcAddr | ||||||
|  |  | ||||||
| 				s.publish() | 				s.publish() | ||||||
| 			} | 			} | ||||||
| @@ -206,21 +206,22 @@ func (s *peerSupervisor) server() stateFunc { | |||||||
| 			probe := probePacket{TraceID: newTraceID()} | 			probe := probePacket{TraceID: newTraceID()} | ||||||
| 			s.sendControlPacketTo(probe, syn.FromAddr) | 			s.sendControlPacketTo(probe, syn.FromAddr) | ||||||
|  |  | ||||||
| 			case probePacket: | 		case controlMsg[probePacket]: | ||||||
| 				if pkt.SrcAddr.IsValid() { | 			if !msg.SrcAddr.IsValid() { | ||||||
| 					s.sendControlPacketTo(probePacket{TraceID: p.TraceID}, pkt.SrcAddr) |  | ||||||
| 				} else { |  | ||||||
| 				logf("Invalid probe address") | 				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") | 				logf("Connection timeout") | ||||||
| 				s.staged.Up = false | 				s.staged.Up = false | ||||||
| 				s.publish() | 				s.publish() | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // ---------------------------------------------------------------------------- | // ---------------------------------------------------------------------------- | ||||||
|  |  | ||||||
| @@ -237,38 +238,36 @@ func (s *peerSupervisor) client() stateFunc { | |||||||
| 			FromAddr:  getLocalAddr(), | 			FromAddr:  getLocalAddr(), | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		lastSeen = time.Now() | ||||||
| 		ack      synAckPacket | 		ack      synAckPacket | ||||||
|  |  | ||||||
| 		probe     probePacket | 		probe     probePacket | ||||||
| 		probeAddr netip.AddrPort | 		probeAddr netip.AddrPort | ||||||
|  |  | ||||||
| 		remoteAddr netip.AddrPort | 		localProbe     probePacket | ||||||
|  | 		localProbeAddr netip.AddrPort | ||||||
|  |  | ||||||
| 		timeoutTimer = time.NewTimer(timeoutInterval) | 		lastLocalAddr netip.AddrPort | ||||||
| 		pingTimer    = time.NewTimer(pingInterval) |  | ||||||
| 	) | 	) | ||||||
|  |  | ||||||
| 	defer timeoutTimer.Stop() |  | ||||||
| 	defer pingTimer.Stop() |  | ||||||
|  |  | ||||||
| 	s.sendControlPacket(syn) | 	s.sendControlPacket(syn) | ||||||
|  |  | ||||||
| 	for { | 	for { | ||||||
| 		select { | 		rawMsg := <-s.messages | ||||||
|  | 		switch msg := rawMsg.(type) { | ||||||
|  |  | ||||||
| 		case peer := <-s.peerUpdates: | 		case peerUpdateMsg: | ||||||
| 			return s.peerUpdate(peer) | 			return s.peerUpdate(msg.Peer) | ||||||
|  |  | ||||||
| 		case pkt := <-s.controlPackets: | 		case controlMsg[synAckPacket]: | ||||||
| 			switch p := pkt.Payload.(type) { | 			p := msg.Packet | ||||||
|  |  | ||||||
| 			case synAckPacket: |  | ||||||
| 			if p.TraceID != syn.TraceID { | 			if p.TraceID != syn.TraceID { | ||||||
| 				continue // Hmm... | 				continue // Hmm... | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 				ack = p | 			lastSeen = time.Now() | ||||||
| 				timeoutTimer.Reset(timeoutInterval) | 			ack = msg.Packet | ||||||
|  |  | ||||||
| 			if !s.staged.Up { | 			if !s.staged.Up { | ||||||
| 				if s.staged.Direct { | 				if s.staged.Direct { | ||||||
| @@ -281,47 +280,64 @@ func (s *peerSupervisor) client() stateFunc { | |||||||
| 				s.publish() | 				s.publish() | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			case probePacket: | 		case controlMsg[probePacket]: | ||||||
| 			if s.staged.Direct { | 			if s.staged.Direct { | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 				if p.TraceID != probe.TraceID { | 			p := msg.Packet | ||||||
|  |  | ||||||
|  | 			if p.TraceID != localProbe.TraceID && p.TraceID != probe.TraceID { | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			// Upgrade connection. | 			// Upgrade connection. | ||||||
|  |  | ||||||
| 				logf("UP - Direct") |  | ||||||
| 			s.staged.Direct = true | 			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.staged.RemoteAddr = probeAddr | ||||||
|  | 			} | ||||||
| 			s.publish() | 			s.publish() | ||||||
|  |  | ||||||
| 			syn.TraceID = newTraceID() | 			syn.TraceID = newTraceID() | ||||||
| 			syn.Direct = true | 			syn.Direct = true | ||||||
| 			syn.FromAddr = getLocalAddr() | 			syn.FromAddr = getLocalAddr() | ||||||
| 			s.sendControlPacket(syn) | 			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 { | 			if s.staged.Direct { | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			// TODO: Check if we have local address. | 			// Send probe. | ||||||
| 			// TODO: Send local 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() { | 			if !ack.FromAddr.IsValid() { | ||||||
| 				continue | 				continue | ||||||
| @@ -331,10 +347,6 @@ func (s *peerSupervisor) client() stateFunc { | |||||||
| 			probeAddr = ack.FromAddr | 			probeAddr = ack.FromAddr | ||||||
|  |  | ||||||
| 			s.sendControlPacketTo(probe, ack.FromAddr) | 			s.sendControlPacketTo(probe, ack.FromAddr) | ||||||
|  |  | ||||||
| 		case <-timeoutTimer.C: |  | ||||||
| 			logf("Connection timeout") |  | ||||||
| 			return s.peerUpdate(s.peer) |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1 +0,0 @@ | |||||||
| package node |  | ||||||
| @@ -1 +0,0 @@ | |||||||
| ## Stage1: Point-to-point Tunnel w/ no Encryption |  | ||||||
| @@ -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) |  | ||||||
| } |  | ||||||
| @@ -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" |  | ||||||
| @@ -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]) |  | ||||||
| } |  | ||||||
| @@ -1,4 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
|  |  | ||||||
| go build |  | ||||||
| sudo setcap cap_net_admin+iep server |  | ||||||
| @@ -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]) |  | ||||||
| } |  | ||||||
| @@ -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 |  | ||||||
| } |  | ||||||
							
								
								
									
										109
									
								
								stage1/server.go
									
									
									
									
									
								
							
							
						
						
									
										109
									
								
								stage1/server.go
									
									
									
									
									
								
							| @@ -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) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1 +0,0 @@ | |||||||
| package stage1 |  | ||||||
| @@ -1,4 +0,0 @@ | |||||||
| ## Stage2: |  | ||||||
|  |  | ||||||
| * Point-to-point Tunnel w/ no Encryption |  | ||||||
| * Server gets client's addr from first packet |  | ||||||
| @@ -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) |  | ||||||
| } |  | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
|  |  | ||||||
| go build |  | ||||||
| sudo setcap cap_net_admin+iep ./client |  | ||||||
| ./client 144.76.78.93 |  | ||||||
| @@ -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]) |  | ||||||
| } |  | ||||||
| @@ -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" |  | ||||||
| @@ -1,7 +0,0 @@ | |||||||
| package main |  | ||||||
|  |  | ||||||
| import "vppn/stage2" |  | ||||||
|  |  | ||||||
| func main() { |  | ||||||
| 	stage2.RunServer() |  | ||||||
| } |  | ||||||
| @@ -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 |  | ||||||
| } |  | ||||||
							
								
								
									
										112
									
								
								stage2/server.go
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								stage2/server.go
									
									
									
									
									
								
							| @@ -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) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1 +0,0 @@ | |||||||
| package stage2 |  | ||||||
| @@ -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. |  | ||||||
| @@ -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) |  | ||||||
| } |  | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
|  |  | ||||||
| go build |  | ||||||
| sudo setcap cap_net_admin+iep ./client |  | ||||||
| ./client 144.76.78.93 |  | ||||||
| @@ -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]) |  | ||||||
| } |  | ||||||
| @@ -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" |  | ||||||
| @@ -1,7 +0,0 @@ | |||||||
| package main |  | ||||||
|  |  | ||||||
| import "vppn/stage3" |  | ||||||
|  |  | ||||||
| func main() { |  | ||||||
| 	stage3.RunServer() |  | ||||||
| } |  | ||||||
| @@ -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 |  | ||||||
| } |  | ||||||
| @@ -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 |  | ||||||
| } |  | ||||||
| @@ -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) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										147
									
								
								stage3/server.go
									
									
									
									
									
								
							
							
						
						
									
										147
									
								
								stage3/server.go
									
									
									
									
									
								
							| @@ -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) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1 +0,0 @@ | |||||||
| package stage3 |  | ||||||
		Reference in New Issue
	
	Block a user