This commit is contained in:
jdl
2024-12-08 09:45:29 +01:00
parent 55a9bf9dc3
commit 03ff1aac80
60 changed files with 3165 additions and 2 deletions

32
peer/crypto.go Normal file
View File

@@ -0,0 +1,32 @@
package peer
import (
"golang.org/x/crypto/nacl/box"
"golang.org/x/crypto/nacl/sign"
)
func encryptPacket(sharedKey, nonce, packet, out []byte) []byte {
out = box.SealAfterPrecomputation(out[:0], packet, (*[24]byte)(nonce), (*[32]byte)(sharedKey))
return append(out, nonce...)
}
func decryptPacket(sharedKey, packet, out []byte) (decrypted []byte, ok bool) {
cut := len(packet) - NONCE_SIZE
decrypted, ok = box.OpenAfterPrecomputation(out[:0], packet[:cut], (*[24]byte)(packet[cut:]), (*[32]byte)(sharedKey))
return decrypted, ok
}
// Signed packet should be encrypted with the encryptPacket function first.
func signPacket(privKey, packet, out []byte) []byte {
return sign.Sign(out[:0], packet, (*[64]byte)(privKey))
}
func openPacket(pubKey, packet, out []byte) (encPacket []byte, ok bool) {
return sign.Open(out[:0], packet, (*[32]byte)(pubKey))
}
func computeSharedKey(peerPubKey, privKey []byte) []byte {
shared := [32]byte{}
box.Precompute(&shared, (*[32]byte)(peerPubKey), (*[32]byte)(privKey))
return shared[:]
}

129
peer/crypto_test.go Normal file
View File

@@ -0,0 +1,129 @@
package peer
import (
"bytes"
"crypto/rand"
"testing"
"golang.org/x/crypto/nacl/box"
"golang.org/x/crypto/nacl/sign"
)
func TestEncryptDecryptPacket(t *testing.T) {
pubKey1, privKey1, err := box.GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
pubKey2, privKey2, err := box.GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
sharedEncKey := [32]byte{}
box.Precompute(&sharedEncKey, pubKey2, privKey1)
sharedDecKey := [32]byte{}
box.Precompute(&sharedDecKey, pubKey1, privKey2)
original := make([]byte, MTU)
rand.Read(original)
nonce := make([]byte, NONCE_SIZE)
rand.Read(nonce)
encrypted := make([]byte, BUFFER_SIZE)
encrypted = encryptPacket(sharedEncKey[:], nonce, original, encrypted)
decrypted := make([]byte, MTU)
var ok bool
decrypted, ok = decryptPacket(sharedDecKey[:], encrypted, decrypted)
if !ok {
t.Fatal(ok)
}
if !bytes.Equal(original, decrypted) {
t.Fatal("mismatch")
}
}
func BenchmarkEncryptPacket(b *testing.B) {
_, privKey1, err := box.GenerateKey(rand.Reader)
if err != nil {
b.Fatal(err)
}
pubKey2, _, err := box.GenerateKey(rand.Reader)
if err != nil {
b.Fatal(err)
}
sharedEncKey := [32]byte{}
box.Precompute(&sharedEncKey, pubKey2, privKey1)
original := make([]byte, MTU)
rand.Read(original)
nonce := make([]byte, NONCE_SIZE)
rand.Read(nonce)
encrypted := make([]byte, BUFFER_SIZE)
for i := 0; i < b.N; i++ {
encrypted = encryptPacket(sharedEncKey[:], nonce, original, encrypted)
}
}
func BenchmarkDecryptPacket(b *testing.B) {
pubKey1, privKey1, err := box.GenerateKey(rand.Reader)
if err != nil {
b.Fatal(err)
}
pubKey2, privKey2, err := box.GenerateKey(rand.Reader)
if err != nil {
b.Fatal(err)
}
sharedEncKey := [32]byte{}
box.Precompute(&sharedEncKey, pubKey2, privKey1)
sharedDecKey := [32]byte{}
box.Precompute(&sharedDecKey, pubKey1, privKey2)
original := make([]byte, MTU)
rand.Read(original)
nonce := make([]byte, NONCE_SIZE)
rand.Read(nonce)
encrypted := make([]byte, BUFFER_SIZE)
encrypted = encryptPacket(sharedEncKey[:], nonce, original, encrypted)
decrypted := make([]byte, MTU)
for i := 0; i < b.N; i++ {
decrypted, _ = decryptPacket(sharedDecKey[:], encrypted, decrypted)
}
}
func TestSignOpenPacket(t *testing.T) {
pubKey, privKey, err := sign.GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
packet := make([]byte, MTU)
rand.Read(packet)
signedPacket := signPacket(privKey[:], packet, make([]byte, BUFFER_SIZE))
encPacket, ok := openPacket(pubKey[:], signedPacket, make([]byte, BUFFER_SIZE))
if !ok {
t.Fatal(ok)
}
if !bytes.Equal(encPacket, packet) {
t.Fatal("not equal")
}
}

82
peer/files.go Normal file
View File

@@ -0,0 +1,82 @@
package peer
import (
"encoding/json"
"log"
"os"
"path/filepath"
"vppn/m"
)
func configDir(netName string) string {
d, err := os.UserHomeDir()
if err != nil {
log.Fatalf("Failed to get user home directory: %v", err)
}
return filepath.Join(d, ".vppn", netName)
}
func peerConfigPath(netName string) string {
return filepath.Join(configDir(netName), "peer-config.json")
}
func peerStatePath(netName string) string {
return filepath.Join(configDir(netName), "peer-state.json")
}
func storeJson(x any, outPath string) error {
outDir := filepath.Dir(outPath)
_ = os.MkdirAll(outDir, 0700)
tmpPath := outPath + ".tmp"
buf, err := json.Marshal(x)
if err != nil {
return err
}
f, err := os.Create(tmpPath)
if err != nil {
return err
}
if _, err := f.Write(buf); err != nil {
f.Close()
return err
}
if err := f.Sync(); err != nil {
f.Close()
return err
}
if err := f.Close(); err != nil {
return err
}
return os.Rename(tmpPath, outPath)
}
func storePeerConfig(netName string, pc m.PeerConfig) error {
return storeJson(pc, peerConfigPath(netName))
}
func storePeerState(netName string, ps m.NetworkState) error {
return storeJson(ps, peerStatePath(netName))
}
func loadJson(dataPath string, ptr any) error {
data, err := os.ReadFile(dataPath)
if err != nil {
return err
}
return json.Unmarshal(data, ptr)
}
func loadPeerConfig(netName string) (pc m.PeerConfig, err error) {
return pc, loadJson(peerConfigPath(netName), &pc)
}
func loadPeerState(netName string) (ps m.NetworkState, err error) {
return ps, loadJson(peerStatePath(netName), &ps)
}

16
peer/globals.go Normal file
View File

@@ -0,0 +1,16 @@
package peer
const (
DEFAULT_PORT = 515
NONCE_SIZE = 24
KEY_SIZE = 32
SIG_SIZE = 64
MTU = 1408
BUFFER_SIZE = MTU + NONCE_SIZE + SIG_SIZE
STREAM_DATA = 0
STREAM_ROUTING = 1 // Routing queries and responses.
// Basic packet types
PACKET_TYPE_DATA = 0
)

86
peer/main.go Normal file
View File

@@ -0,0 +1,86 @@
package peer
import (
"encoding/json"
"flag"
"fmt"
"io"
"log"
"net/http"
"os"
"runtime/debug"
"vppn/m"
_ "net/http/pprof"
)
func Main() {
defer func() {
if r := recover(); r != nil {
fmt.Println("stacktrace from panic: \n" + string(debug.Stack()))
}
}()
go func() {
log.Printf("Serving on localhost:6060...")
log.Println(http.ListenAndServe("localhost:6060", nil))
}()
var (
netName string
initURL string
listenIP string
port int
)
flag.StringVar(&netName, "name", "", "[REQUIRED] The network name.")
flag.StringVar(&initURL, "init-url", "", "Initializes peer from the hub URL.")
flag.StringVar(&listenIP, "listen-ip", "", "IP address to listen on.")
flag.IntVar(&port, "port", 0, "Port to listen on.")
flag.Parse()
if netName == "" {
flag.Usage()
os.Exit(1)
}
if initURL != "" {
mainInit(netName, initURL)
return
}
peer, err := NewPeer(netName, listenIP, uint16(port))
if err != nil {
log.Fatalf("Failed to create peer: %v", err)
}
peer.Run()
}
func mainInit(netName, initURL string) {
if _, err := loadPeerConfig(netName); err == nil {
log.Fatalf("Network is already initialized.")
}
resp, err := http.Get(initURL)
if err != nil {
log.Fatalf("Failed to fetch data from hub: %v", err)
}
defer resp.Body.Close()
data, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatalf("Failed to read response body: %v", err)
}
peerConfig := m.PeerConfig{}
if err := json.Unmarshal(data, &peerConfig); err != nil {
log.Fatalf("Failed to parse configuration: %v", err)
}
if err := storePeerConfig(netName, peerConfig); err != nil {
log.Fatalf("Failed to store configuration: %v", err)
}
log.Print("Initialization successful.")
}

26
peer/nonce.go Normal file
View File

@@ -0,0 +1,26 @@
package peer
import "unsafe"
func ParseNonceBytes(nb []byte, nonce *Nonce) {
nonce.Counter = *(*uint64)(unsafe.Pointer(&nb[0]))
nonce.SourceIP = nb[8]
nonce.ViaIP = nb[9]
nonce.DestIP = nb[10]
nonce.StreamID = nb[11]
nonce.PacketType = nb[12]
}
func MarshalNonce(nonce Nonce, buf []byte) {
*(*uint64)(unsafe.Pointer(&buf[0])) = nonce.Counter
buf[8] = nonce.SourceIP
buf[9] = nonce.ViaIP
buf[10] = nonce.DestIP
buf[11] = nonce.StreamID
buf[12] = nonce.PacketType
clear(buf[13:])
}
func CounterTimestamp(counter uint64) int64 {
return int64(counter >> 30)
}

25
peer/nonce_test.go Normal file
View File

@@ -0,0 +1,25 @@
package peer
import (
"testing"
)
func TestMarshalParseNonce(t *testing.T) {
nIn := Nonce{
Counter: 3212,
SourceIP: 34,
ViaIP: 20,
DestIP: 200,
StreamID: 4,
PacketType: 44,
}
buf := make([]byte, NONCE_SIZE)
MarshalNonce(nIn, buf)
nOut := Nonce{}
ParseNonceBytes(buf, &nOut)
if nIn != nOut {
t.Fatal(nIn, nOut)
}
}

114
peer/peer-ifreader.go Normal file
View File

@@ -0,0 +1,114 @@
package peer
import (
"fmt"
"log"
"net"
"net/netip"
"runtime/debug"
"vppn/fasttime"
)
func (peer *Peer) ifReader() {
defer func() {
if r := recover(); r != nil {
fmt.Println("stacktrace from panic: \n" + string(debug.Stack()))
}
}()
var (
n int
destIP byte
router = peer.router
route *route
iface = peer.iface
nonce = Nonce{
SourceIP: peer.ip,
PacketType: PACKET_TYPE_DATA,
StreamID: STREAM_DATA,
}
err error
now uint64
counterTS uint64
counter uint64
packet = make([]byte, BUFFER_SIZE)
encrypted = make([]byte, BUFFER_SIZE)
nonceBuf = make([]byte, NONCE_SIZE)
toSend []byte
signingKey = peer.signPrivKey
reqPool = make(chan udpWriteReq, 1024)
writeChan = make(chan udpWriteReq, 1024)
)
for range cap(reqPool) {
reqPool <- udpWriteReq{Packet: make([]byte, BUFFER_SIZE)}
}
go udpWriter(writeChan, peer.conn, reqPool)
for {
n, err = iface.Read(packet[:BUFFER_SIZE])
if err != nil {
log.Fatalf("Failed to read from interface: %v", err)
}
if n < 20 {
log.Printf("Dropping small packet: %d", n)
continue
}
packet = packet[:n]
destIP = packet[19]
route = router.GetRoute(destIP)
if route == nil {
log.Printf("Dropping packet for non-existent IP: %d", destIP)
continue
}
now = uint64(fasttime.Now())
if counterTS < now {
counterTS = now
counter = now << 30
}
counter++
nonce.Counter = counter
nonce.ViaIP = route.ViaIP
nonce.DestIP = destIP
MarshalNonce(nonce, nonceBuf)
encrypted = encryptPacket(route.EncSharedKey, nonceBuf, packet, encrypted)
if route.ViaIP != 0 {
toSend = signPacket(signingKey, encrypted, packet)
} else {
toSend = encrypted
}
req := <-reqPool
req.Addr = route.Addr
req.Packet = req.Packet[:len(toSend)]
copy(req.Packet, toSend)
writeChan <- req
}
}
type udpWriteReq struct {
Addr netip.AddrPort
Packet []byte
}
func udpWriter(in chan udpWriteReq, conn *net.UDPConn, reqPool chan udpWriteReq) {
var err error
for req := range in {
if _, err = conn.WriteToUDPAddrPort(req.Packet, req.Addr); err != nil {
log.Fatalf("Failed to write UDP packet: %v", err)
}
reqPool <- req
}
}

158
peer/peer-netreader.go Normal file
View File

@@ -0,0 +1,158 @@
package peer
import (
"fmt"
"io"
"log"
"runtime/debug"
"vppn/fasttime"
)
func (peer *Peer) netReader() {
defer func() {
if r := recover(); r != nil {
fmt.Println("stacktrace from panic: \n" + string(debug.Stack()))
}
}()
var (
n int
//srcAddr *net.UDPAddr
nonce Nonce
packet = make([]byte, BUFFER_SIZE)
decrypted = make([]byte, BUFFER_SIZE)
toWrite []byte
route *route
ok bool
err error
conn = peer.conn
ip = peer.ip
counters = [2][256]uint64{} // Counter by stream and IP.
ifaceChan = make(chan []byte, 1024)
reqPool = make(chan []byte, 1024)
)
for range cap(reqPool) {
reqPool <- make([]byte, BUFFER_SIZE)
}
go ifWriter(ifaceChan, peer.iface, reqPool)
NEXT_PACKET:
n, _, err = conn.ReadFromUDPAddrPort(packet[:BUFFER_SIZE])
if err != nil {
log.Fatalf("Failed to read UDP packet: %v", err)
}
if n < NONCE_SIZE {
log.Printf("Dropping short UDP packet: %d", n)
goto NEXT_PACKET
}
packet = packet[:n]
ParseNonceBytes(packet[n-NONCE_SIZE:], &nonce)
// Drop after 8 seconds.
if CounterTimestamp(nonce.Counter) < fasttime.Now()-8 {
log.Printf("Dropping old packet: %d", CounterTimestamp(nonce.Counter))
goto NEXT_PACKET
}
if nonce.StreamID > 1 {
log.Printf("Dropping invalid stream ID: %v", nonce)
goto NEXT_PACKET
}
// Check source counter.
if nonce.Counter <= counters[nonce.StreamID][nonce.SourceIP] {
log.Printf("Dropping packet with bad counter: %v", nonce)
goto NEXT_PACKET
}
counters[nonce.StreamID][nonce.SourceIP] = nonce.Counter
route = peer.router.GetRoute(nonce.SourceIP)
if route == nil {
log.Printf("Dropping packet without route: %v", nonce)
goto NEXT_PACKET
}
switch ip {
case nonce.DestIP:
goto DECRYPT
case nonce.ViaIP:
goto VALIDATE_SIGNATURE
default:
log.Printf("Bad packet: %v", nonce)
goto NEXT_PACKET
}
DECRYPT:
decrypted, ok = decryptPacket(route.EncSharedKey, packet, decrypted)
if !ok {
log.Printf("Failed to decrypt packet: %v", nonce)
goto NEXT_PACKET
}
switch nonce.StreamID {
case STREAM_DATA:
goto WRITE_IFACE_DATA
case STREAM_ROUTING:
goto WRITE_ROUTING_PACKET
default:
log.Printf("Invalid stream ID: %d", nonce.StreamID)
goto NEXT_PACKET
}
WRITE_IFACE_DATA:
//toWrite = toWrite[:len(decrypted)]
//copy(toWrite, decrypted)
toWrite = <-reqPool
ifaceChan <- append(toWrite[:0], decrypted...)
goto NEXT_PACKET
WRITE_ROUTING_PACKET:
//peer.processRoutingPacket(decrypted)
//peer.routeManager.ProcessPacket(decrypted)
goto NEXT_PACKET
VALIDATE_SIGNATURE:
// We don't forward twice.
if route.Mediator {
log.Printf("Dropping double-forward packet: %v", nonce)
goto NEXT_PACKET
}
decrypted, ok = openPacket(route.SignPubKey, packet, decrypted)
if !ok {
log.Printf("Failed to open signed packet: %v", nonce)
goto NEXT_PACKET
}
if _, err = conn.WriteToUDPAddrPort(decrypted, route.Addr); err != nil {
log.Fatalf("Failed to forward packet: %v", err)
}
goto NEXT_PACKET
}
func ifWriter(in chan []byte, iface io.ReadWriteCloser, out chan []byte) {
var err error
for packet := range in {
if _, err = iface.Write(packet); err != nil {
log.Printf("Size: %d", len(packet))
log.Fatalf("Failed to write to interface: %v", err)
}
out <- packet
}
}

67
peer/peer.go Normal file
View File

@@ -0,0 +1,67 @@
package peer
import (
"io"
"log"
"net"
)
type Peer struct {
// Immutable data.
ip byte // Last byte of IPv4 address.
hubAddr string
apiKey string
encPubKey []byte
encPrivKey []byte
signPubKey []byte
signPrivKey []byte
conn *net.UDPConn
iface io.ReadWriteCloser
router *Router
}
func NewPeer(netName, listenIP string, port uint16) (*Peer, error) {
conf, err := loadPeerConfig(netName)
if err != nil {
return nil, err
}
peer := &Peer{
ip: conf.PeerIP,
hubAddr: conf.HubAddress,
apiKey: conf.APIKey,
encPubKey: conf.EncPubKey,
encPrivKey: conf.EncPrivKey,
signPubKey: conf.SignPubKey,
signPrivKey: conf.SignPrivKey,
}
peer.router = NewRouter(conf)
port = determinePort(conf.Port, port)
conn, err := openUDPConn(listenIP, port)
if err != nil {
return nil, err
}
peer.conn = conn
peer.iface, err = openInterface(conf.Network, conf.PeerIP, netName)
if err != nil {
log.Fatal(err)
}
if err != nil {
return nil, err
}
return peer, nil
}
func (p *Peer) Run() {
go p.netReader()
p.ifReader()
}

18
peer/router-ping.go Normal file
View File

@@ -0,0 +1,18 @@
package peer
type RoutingPingReq struct {
PeerIP byte
Type byte // 0 => local, 1 => direct, 2 => Via
Addr []byte
Port int
SentAt int64 // unix milli
}
type RoutingPingResp struct {
PeerIP byte
Type byte // 0 => local, 1 => direct, 2 => Via
Addr []byte
Port int
SentAt int64
RecvdAt int64
}

123
peer/router.go Normal file
View File

@@ -0,0 +1,123 @@
package peer
import (
"encoding/json"
"io"
"log"
"net/http"
"net/netip"
"net/url"
"sync/atomic"
"time"
"vppn/m"
)
var zeroAddrPort netip.AddrPort
type routeInfo struct {
Up bool
Route route
}
type Router struct {
conf m.PeerConfig
routes [256]*atomic.Pointer[routeInfo]
}
func NewRouter(conf m.PeerConfig) *Router {
rm := &Router{
conf: conf,
}
for i := range rm.routes {
rm.routes[i] = &atomic.Pointer[routeInfo]{}
rm.routes[i].Store(&routeInfo{})
}
go rm.pollHub()
return rm
}
func (rm *Router) GetRoute(ip byte) *route {
if route := rm.routes[ip].Load(); route != nil && route.Up {
return &route.Route
}
return nil
}
func (rm *Router) pollHub() {
u, err := url.Parse(rm.conf.HubAddress)
if err != nil {
log.Fatalf("Failed to parse hub address %s: %v", rm.conf.HubAddress, err)
}
u.Path = "/peer/fetch-state/"
client := &http.Client{Timeout: 8 * time.Second}
req := &http.Request{
Method: http.MethodGet,
URL: u,
Header: http.Header{},
}
req.SetBasicAuth("", rm.conf.APIKey)
rm._pollHub(client, req)
for range time.Tick(time.Minute) {
rm._pollHub(client, req)
}
}
func (rm *Router) _pollHub(client *http.Client, req *http.Request) {
var state m.NetworkState
log.Printf("Fetching peer state from %s...", rm.conf.HubAddress)
resp, err := client.Do(req)
if err != nil {
log.Printf("Failed to fetch peer state: %v", err)
return
}
body, err := io.ReadAll(resp.Body)
_ = resp.Body.Close()
if err != nil {
log.Printf("Failed to read body: %v", err)
return
}
if err := json.Unmarshal(body, &state); err != nil {
log.Printf("Failed to unmarshal response from hub: %v", err)
return
}
for i, peer := range state.Peers {
if peer == nil {
continue
}
route := rm.routes[i].Load()
rm.routes[i].Store(rm.updateRoute(route, peer))
}
}
func (rm *Router) updateRoute(routePtr *routeInfo, peer *m.Peer) *routeInfo {
if peer == nil {
return &routeInfo{}
}
route := *routePtr
addr, ok := netip.AddrFromSlice(peer.IP)
if !ok {
return &routeInfo{}
}
route.Up = true
route.Route.Addr = netip.AddrPortFrom(addr, peer.Port)
route.Route.Mediator = peer.Mediator
route.Route.ViaIP = 0
if len(route.Route.SignPubKey) == 0 {
route.Route.SignPubKey = peer.SignPubKey
route.Route.EncSharedKey = computeSharedKey(peer.EncPubKey, rm.conf.EncPrivKey)
}
return &route
}

156
peer/startup.go Normal file
View File

@@ -0,0 +1,156 @@
package peer
import (
"fmt"
"io"
"net"
"os"
"syscall"
"golang.org/x/sys/unix"
)
func determinePort(confPort, portFromCommandLine uint16) uint16 {
if portFromCommandLine != 0 {
return portFromCommandLine
}
if confPort != 0 {
return confPort
}
return DEFAULT_PORT
}
func openUDPConn(listenIP string, port uint16) (*net.UDPConn, error) {
myAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", listenIP, port))
if err != nil {
return nil, fmt.Errorf("failed to construct UDP address: %w", err)
}
return net.ListenUDP("udp", myAddr)
}
func openInterface(network []byte, localIP byte, name string) (io.ReadWriteCloser, error) {
if len(network) != 4 {
return nil, fmt.Errorf("expected network to be 4 bytes, got %d", len(network))
}
ip := net.IPv4(network[0], network[1], network[2], localIP)
//////////////////////////
// Create TUN Interface //
//////////////////////////
tunFD, err := syscall.Open("/dev/net/tun", syscall.O_RDWR|unix.O_CLOEXEC, 0600)
if err != nil {
return nil, fmt.Errorf("failed to open TUN device: %w", err)
}
// New interface request.
req, err := unix.NewIfreq(name)
if err != nil {
return nil, fmt.Errorf("failed to create new TUN interface request: %w", err)
}
// Flags:
//
// IFF_NO_PI => don't add packet info data to packets sent to the interface.
// IFF_TUN => create a TUN device handling IP packets.
req.SetUint16(unix.IFF_NO_PI | unix.IFF_TUN)
err = unix.IoctlIfreq(tunFD, unix.TUNSETIFF, req)
if err != nil {
return nil, fmt.Errorf("failed to set TUN device settings: %w", err)
}
// Name may not be exactly the same?
name = req.Name()
/////////////
// Set MTU //
/////////////
// We need a socket file descriptor to set other options for some reason.
sockFD, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, unix.IPPROTO_IP)
if err != nil {
return nil, fmt.Errorf("failed to open socket: %w", err)
}
defer unix.Close(sockFD)
req, err = unix.NewIfreq(name)
if err != nil {
return nil, fmt.Errorf("failed to create MTU interface request: %w", err)
}
req.SetUint32(MTU)
if err = unix.IoctlIfreq(sockFD, unix.SIOCSIFMTU, req); err != nil {
return nil, fmt.Errorf("failed to set interface MTU: %w", err)
}
//////////////////////
// Set Queue Length //
//////////////////////
req, err = unix.NewIfreq(name)
if err != nil {
return nil, fmt.Errorf("failed to create IP interface request: %w", err)
}
req.SetUint16(1000)
if err = unix.IoctlIfreq(sockFD, unix.SIOCSIFTXQLEN, req); err != nil {
return nil, fmt.Errorf("failed to set interface queue length: %w", err)
}
/////////////////////
// Set IP and Mask //
/////////////////////
req, err = unix.NewIfreq(name)
if err != nil {
return nil, fmt.Errorf("failed to create IP interface request: %w", err)
}
if err := req.SetInet4Addr(ip.To4()); err != nil {
return nil, fmt.Errorf("failed to set interface request IP: %w", err)
}
if err = unix.IoctlIfreq(sockFD, unix.SIOCSIFADDR, req); err != nil {
return nil, fmt.Errorf("failed to set interface IP: %w", err)
}
// SET MASK - must happen after setting address.
req, err = unix.NewIfreq(name)
if err != nil {
return nil, fmt.Errorf("failed to create mask interface request: %w", err)
}
if err := req.SetInet4Addr(net.IPv4(255, 255, 255, 0).To4()); err != nil {
return nil, fmt.Errorf("failed to set interface request mask: %w", err)
}
if err := unix.IoctlIfreq(sockFD, unix.SIOCSIFNETMASK, req); err != nil {
return nil, fmt.Errorf("failed to set interface mask: %w", err)
}
////////////////////////
// Bring Interface Up //
////////////////////////
req, err = unix.NewIfreq(name)
if err != nil {
return nil, fmt.Errorf("failed to create up interface request: %w", err)
}
// Get current flags.
if err = unix.IoctlIfreq(sockFD, unix.SIOCGIFFLAGS, req); err != nil {
return nil, fmt.Errorf("failed to get interface flags: %w", err)
}
flags := req.Uint16() | unix.IFF_UP | unix.IFF_RUNNING
// Set UP flag / broadcast flags.
req.SetUint16(flags)
if err = unix.IoctlIfreq(sockFD, unix.SIOCSIFFLAGS, req); err != nil {
return nil, fmt.Errorf("failed to set interface up: %w", err)
}
return os.NewFile(uintptr(tunFD), "tun"), nil
}

22
peer/types.go Normal file
View File

@@ -0,0 +1,22 @@
package peer
import (
"net/netip"
)
type route struct {
Mediator bool // Route is via a mediator.
SignPubKey []byte
EncSharedKey []byte // Shared key for encoding / decoding packets.
Addr netip.AddrPort // Address to send to.
ViaIP byte // If != 0, this is a forwarding address.
}
type Nonce struct {
Counter uint64
SourceIP byte
ViaIP byte
DestIP byte
StreamID byte // The stream, see STREAM_* constants
PacketType byte // The packet type. See PACKET_* constants.
}