WIP: Simple working client/server
This commit is contained in:
parent
b4a6149cee
commit
62bf956c10
21
node/bitset.go
Normal file
21
node/bitset.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
const bitSetSize = 512 // Multiple of 64.
|
||||||
|
|
||||||
|
type bitSet [bitSetSize / 64]uint64
|
||||||
|
|
||||||
|
func (bs *bitSet) Set(i int) {
|
||||||
|
bs[i/64] |= 1 << (i % 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *bitSet) Clear(i int) {
|
||||||
|
bs[i/64] &= ^(1 << (i % 64))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *bitSet) ClearAll() {
|
||||||
|
clear(bs[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bs *bitSet) Get(i int) bool {
|
||||||
|
return bs[i/64]&(1<<(i%64)) != 0
|
||||||
|
}
|
48
node/bitset_test.go
Normal file
48
node/bitset_test.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBitSet(t *testing.T) {
|
||||||
|
state := make([]bool, bitSetSize)
|
||||||
|
for i := range state {
|
||||||
|
state[i] = rand.Float32() > 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
bs := bitSet{}
|
||||||
|
|
||||||
|
for i := range state {
|
||||||
|
if state[i] {
|
||||||
|
bs.Set(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range state {
|
||||||
|
if bs.Get(i) != state[i] {
|
||||||
|
t.Fatal(i, state[i], bs.Get(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range state {
|
||||||
|
if rand.Float32() > 0.5 {
|
||||||
|
state[i] = false
|
||||||
|
bs.Clear(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range state {
|
||||||
|
if bs.Get(i) != state[i] {
|
||||||
|
t.Fatal(i, state[i], bs.Get(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bs.ClearAll()
|
||||||
|
|
||||||
|
for i := range state {
|
||||||
|
if bs.Get(i) {
|
||||||
|
t.Fatal(i, bs.Get(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
node/cmd/client/build.sh
Executable file
5
node/cmd/client/build.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
go build
|
||||||
|
sudo setcap cap_net_admin+iep ./client
|
||||||
|
./client 144.76.78.93
|
15
node/cmd/client/main.go
Normal file
15
node/cmd/client/main.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
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()
|
||||||
|
n.RunClient(os.Args[1])
|
||||||
|
}
|
7
node/cmd/server/build.sh
Executable file
7
node/cmd/server/build.sh
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#!/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"
|
8
node/cmd/server/main.go
Normal file
8
node/cmd/server/main.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "vppn/node"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
n := node.NewTmpNodeServer()
|
||||||
|
n.RunServer()
|
||||||
|
}
|
103
node/conn.go
Normal file
103
node/conn.go
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"vppn/fasttime"
|
||||||
|
)
|
||||||
|
|
||||||
|
type connWriter struct {
|
||||||
|
*net.UDPConn
|
||||||
|
localIP byte
|
||||||
|
buf []byte
|
||||||
|
counters [256]uint64
|
||||||
|
lookup func(byte) *peer
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConnWriter(conn *net.UDPConn, localIP byte, lookup func(byte) *peer) *connWriter {
|
||||||
|
w := &connWriter{
|
||||||
|
UDPConn: conn,
|
||||||
|
localIP: localIP,
|
||||||
|
buf: make([]byte, bufferSize),
|
||||||
|
lookup: lookup,
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range w.counters {
|
||||||
|
w.counters[i] = uint64(fasttime.Now() << 30)
|
||||||
|
}
|
||||||
|
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *connWriter) WriteTo(remoteIP, packetType byte, data []byte) error {
|
||||||
|
peer := w.lookup(remoteIP)
|
||||||
|
if peer == nil || peer.Addr == nil {
|
||||||
|
log.Printf("No peer: %d", remoteIP)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
w.counters[remoteIP]++
|
||||||
|
|
||||||
|
h := header{
|
||||||
|
Counter: w.counters[remoteIP],
|
||||||
|
SourceIP: w.localIP,
|
||||||
|
ViaIP: 0,
|
||||||
|
DestIP: remoteIP,
|
||||||
|
PacketType: packetType,
|
||||||
|
DataSize: uint16(len(data)),
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := w.buf[:len(data)+headerSize]
|
||||||
|
h.Marshal(buf)
|
||||||
|
copy(buf[headerSize:], data)
|
||||||
|
|
||||||
|
_, err := w.WriteToUDPAddrPort(buf, *peer.Addr)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type connReader struct {
|
||||||
|
*net.UDPConn
|
||||||
|
localIP byte
|
||||||
|
counters [256]uint64
|
||||||
|
lookup func(byte) *peer
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConnReader(conn *net.UDPConn, localIP byte, lookup func(byte) *peer) *connReader {
|
||||||
|
return &connReader{
|
||||||
|
UDPConn: conn,
|
||||||
|
localIP: localIP,
|
||||||
|
lookup: lookup,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *connReader) Read(buf []byte) (remoteAddr netip.AddrPort, h header, data []byte, err error) {
|
||||||
|
var n int
|
||||||
|
|
||||||
|
for {
|
||||||
|
n, remoteAddr, err = r.ReadFromUDPAddrPort(buf[:bufferSize])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = buf[:n]
|
||||||
|
|
||||||
|
if n < headerSize {
|
||||||
|
continue // Packet it soo short.
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Parse(buf)
|
||||||
|
data = buf[headerSize:]
|
||||||
|
if len(data) != int(h.DataSize) {
|
||||||
|
continue // Packet is corrupt.
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.Counter > r.counters[h.SourceIP] {
|
||||||
|
r.counters[h.SourceIP] = h.Counter
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
76
node/dupcheck.go
Normal file
76
node/dupcheck.go
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
type dupCheck struct {
|
||||||
|
bitSet
|
||||||
|
head int
|
||||||
|
tail int
|
||||||
|
headCounter uint64
|
||||||
|
tailCounter uint64 // Also next expected counter value.
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDupCheck(headCounter uint64) *dupCheck {
|
||||||
|
return &dupCheck{
|
||||||
|
headCounter: headCounter,
|
||||||
|
tailCounter: headCounter + 1,
|
||||||
|
tail: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *dupCheck) IsDup(counter uint64) bool {
|
||||||
|
|
||||||
|
// Before head => it's late, say it's a dup.
|
||||||
|
if counter < dc.headCounter {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's within the counter bounds.
|
||||||
|
if counter < dc.tailCounter {
|
||||||
|
index := (int(counter-dc.headCounter) + dc.head) % bitSetSize
|
||||||
|
if dc.Get(index) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
dc.Set(index)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's more than 1 beyond the tail.
|
||||||
|
delta := counter - dc.tailCounter
|
||||||
|
|
||||||
|
// Full clear.
|
||||||
|
if delta >= bitSetSize {
|
||||||
|
dc.ClearAll()
|
||||||
|
dc.Set(0)
|
||||||
|
|
||||||
|
dc.tail = 1
|
||||||
|
dc.head = 2
|
||||||
|
dc.tailCounter = counter + 1
|
||||||
|
dc.headCounter = dc.tailCounter - bitSetSize
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear if necessary.
|
||||||
|
for i := 0; i < int(delta); i++ {
|
||||||
|
dc.put(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
dc.put(true)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *dupCheck) put(set bool) {
|
||||||
|
if set {
|
||||||
|
dc.Set(dc.tail)
|
||||||
|
} else {
|
||||||
|
dc.Clear(dc.tail)
|
||||||
|
}
|
||||||
|
|
||||||
|
dc.tail = (dc.tail + 1) % bitSetSize
|
||||||
|
dc.tailCounter++
|
||||||
|
|
||||||
|
if dc.head == dc.tail {
|
||||||
|
dc.head = (dc.head + 1) % bitSetSize
|
||||||
|
dc.headCounter++
|
||||||
|
}
|
||||||
|
}
|
57
node/dupcheck_test.go
Normal file
57
node/dupcheck_test.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDupCheck(t *testing.T) {
|
||||||
|
dc := newDupCheck(0)
|
||||||
|
|
||||||
|
for i := range bitSetSize {
|
||||||
|
if dc.IsDup(uint64(i)) {
|
||||||
|
t.Fatal("!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TestCase struct {
|
||||||
|
Counter uint64
|
||||||
|
Dup bool
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []TestCase{
|
||||||
|
{0, true},
|
||||||
|
{1, true},
|
||||||
|
{2, true},
|
||||||
|
{3, true},
|
||||||
|
{63, true},
|
||||||
|
{256, true},
|
||||||
|
{510, true},
|
||||||
|
{511, true},
|
||||||
|
{512, false},
|
||||||
|
{0, true},
|
||||||
|
{512, true},
|
||||||
|
{513, false},
|
||||||
|
{517, false},
|
||||||
|
{512, true},
|
||||||
|
{513, true},
|
||||||
|
{514, false},
|
||||||
|
{515, false},
|
||||||
|
{516, false},
|
||||||
|
{517, true},
|
||||||
|
{2512, false},
|
||||||
|
{2000, true},
|
||||||
|
{2001, false},
|
||||||
|
{4000, false},
|
||||||
|
{4000 - 512, true}, // Too old.
|
||||||
|
{4000 - 511, false}, // Just in the window.
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range testCases {
|
||||||
|
if ok := dc.IsDup(tc.Counter); ok != tc.Dup {
|
||||||
|
log.Printf("%b", dc.bitSet)
|
||||||
|
log.Printf("%+v", *dc)
|
||||||
|
t.Fatal(i, ok, tc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
node/globals.go
Normal file
3
node/globals.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
const bufferSize = if_mtu + 128
|
32
node/header.go
Normal file
32
node/header.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
const headerSize = 24
|
||||||
|
|
||||||
|
type header struct {
|
||||||
|
Counter uint64 // Init with fasttime.Now() << 30 to ensure monotonic.
|
||||||
|
SourceIP byte
|
||||||
|
ViaIP byte
|
||||||
|
DestIP byte
|
||||||
|
PacketType byte // The packet type. See PACKET_* constants.
|
||||||
|
DataSize uint16 // Data size following associated data.
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hdr *header) Parse(nb []byte) {
|
||||||
|
hdr.Counter = *(*uint64)(unsafe.Pointer(&nb[0]))
|
||||||
|
hdr.SourceIP = nb[8]
|
||||||
|
hdr.ViaIP = nb[9]
|
||||||
|
hdr.DestIP = nb[10]
|
||||||
|
hdr.PacketType = nb[11]
|
||||||
|
hdr.DataSize = *(*uint16)(unsafe.Pointer(&nb[12]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hdr header) Marshal(buf []byte) {
|
||||||
|
*(*uint64)(unsafe.Pointer(&buf[0])) = hdr.Counter
|
||||||
|
buf[8] = hdr.SourceIP
|
||||||
|
buf[9] = hdr.ViaIP
|
||||||
|
buf[10] = hdr.DestIP
|
||||||
|
buf[11] = hdr.PacketType
|
||||||
|
*(*uint16)(unsafe.Pointer(&buf[12])) = hdr.DataSize
|
||||||
|
}
|
23
node/header_test.go
Normal file
23
node/header_test.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestHeaderMarshalParse(t *testing.T) {
|
||||||
|
nIn := header{
|
||||||
|
Counter: 3212,
|
||||||
|
SourceIP: 34,
|
||||||
|
ViaIP: 20,
|
||||||
|
DestIP: 200,
|
||||||
|
PacketType: 44,
|
||||||
|
DataSize: 1235,
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, headerSize)
|
||||||
|
nIn.Marshal(buf)
|
||||||
|
|
||||||
|
nOut := header{}
|
||||||
|
nOut.Parse(buf)
|
||||||
|
if nIn != nOut {
|
||||||
|
t.Fatal(nIn, nOut)
|
||||||
|
}
|
||||||
|
}
|
177
node/interface.go
Normal file
177
node/interface.go
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get next packet, returning packet, ip, and possible error.
|
||||||
|
func readNextPacket(iface io.ReadWriteCloser, buf []byte) ([]byte, byte, error) {
|
||||||
|
var (
|
||||||
|
version byte
|
||||||
|
ip byte
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
n, err := iface.Read(buf[:cap(buf)])
|
||||||
|
if err != nil {
|
||||||
|
return nil, ip, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < 20 {
|
||||||
|
continue // Packet too short.
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = buf[:n]
|
||||||
|
version = buf[0] >> 4
|
||||||
|
|
||||||
|
switch version {
|
||||||
|
case 4:
|
||||||
|
ip = buf[19]
|
||||||
|
case 6:
|
||||||
|
if len(buf) < 40 {
|
||||||
|
continue // Packet too short.
|
||||||
|
}
|
||||||
|
ip = buf[39]
|
||||||
|
default:
|
||||||
|
continue // Invalid version.
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf, ip, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
33
node/peer.go
Normal file
33
node/peer.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type peer struct {
|
||||||
|
IP byte
|
||||||
|
// TODO: Version
|
||||||
|
Addr *netip.AddrPort
|
||||||
|
// TODO: ViaIP
|
||||||
|
// TODO: EncPubKey
|
||||||
|
// TODO: SignPrivKey
|
||||||
|
}
|
||||||
|
|
||||||
|
type peerRepo [256]*atomic.Pointer[peer]
|
||||||
|
|
||||||
|
func newPeerRepo() peerRepo {
|
||||||
|
pr := peerRepo{}
|
||||||
|
for i := range pr {
|
||||||
|
pr[i] = &atomic.Pointer[peer]{}
|
||||||
|
}
|
||||||
|
return pr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr peerRepo) Get(ip byte) *peer {
|
||||||
|
return pr[ip].Load()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr peerRepo) Set(ip byte, p *peer) {
|
||||||
|
pr[ip].Store(p)
|
||||||
|
}
|
164
node/tmp-server.go
Normal file
164
node/tmp-server.go
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
func must(err error) {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TmpNode struct {
|
||||||
|
network []byte
|
||||||
|
localIP byte
|
||||||
|
peers peerRepo
|
||||||
|
port uint16
|
||||||
|
netName string
|
||||||
|
iface io.ReadWriteCloser
|
||||||
|
w *connWriter
|
||||||
|
r *connReader
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTmpNodeServer() *TmpNode {
|
||||||
|
n := &TmpNode{
|
||||||
|
localIP: serverIP,
|
||||||
|
network: network,
|
||||||
|
peers: newPeerRepo(),
|
||||||
|
port: port,
|
||||||
|
netName: netName,
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
n.iface, err = openInterface(n.network, n.localIP, n.netName)
|
||||||
|
must(err)
|
||||||
|
|
||||||
|
myAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", n.port))
|
||||||
|
must(err)
|
||||||
|
|
||||||
|
conn, err := net.ListenUDP("udp", myAddr)
|
||||||
|
must(err)
|
||||||
|
|
||||||
|
n.w = newConnWriter(conn, n.localIP, n.peers.Get)
|
||||||
|
n.r = newConnReader(conn, n.localIP, n.peers.Get)
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTmpNodeClient() *TmpNode {
|
||||||
|
n := &TmpNode{
|
||||||
|
localIP: clientIP,
|
||||||
|
network: network,
|
||||||
|
peers: newPeerRepo(),
|
||||||
|
port: port,
|
||||||
|
netName: netName,
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
n.iface, err = openInterface(n.network, n.localIP, n.netName)
|
||||||
|
must(err)
|
||||||
|
|
||||||
|
myAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", n.port))
|
||||||
|
must(err)
|
||||||
|
|
||||||
|
conn, err := net.ListenUDP("udp", myAddr)
|
||||||
|
must(err)
|
||||||
|
|
||||||
|
n.w = newConnWriter(conn, n.localIP, n.peers.Get)
|
||||||
|
n.r = newConnReader(conn, n.localIP, n.peers.Get)
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *TmpNode) RunServer() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf("%v", r)
|
||||||
|
debug.PrintStack()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Get remoteAddr from a packet.
|
||||||
|
buf := make([]byte, bufferSize)
|
||||||
|
remoteAddr, h, _, err := n.r.Read(buf)
|
||||||
|
must(err)
|
||||||
|
log.Printf("Got remote addr: %d -> %v", h.SourceIP, remoteAddr)
|
||||||
|
must(err)
|
||||||
|
|
||||||
|
n.peers.Set(h.SourceIP, &peer{
|
||||||
|
IP: h.SourceIP,
|
||||||
|
Addr: &remoteAddr,
|
||||||
|
})
|
||||||
|
|
||||||
|
go n.readFromIFace()
|
||||||
|
n.readFromConn()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *TmpNode) RunClient(srvAddrStr string) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Printf("%v", r)
|
||||||
|
debug.PrintStack()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
serverAddr, err := netip.ParseAddrPort(fmt.Sprintf("%s:%d", srvAddrStr, port))
|
||||||
|
must(err)
|
||||||
|
|
||||||
|
log.Printf("Setting %d => %v", serverIP, serverAddr)
|
||||||
|
n.peers.Set(serverIP, &peer{
|
||||||
|
IP: serverIP,
|
||||||
|
Addr: &serverAddr,
|
||||||
|
})
|
||||||
|
|
||||||
|
must(n.w.WriteTo(serverIP, 1, []byte{1, 2, 3, 4, 5, 6, 7, 8}))
|
||||||
|
|
||||||
|
go n.readFromIFace()
|
||||||
|
n.readFromConn()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *TmpNode) readFromIFace() {
|
||||||
|
var (
|
||||||
|
buf = make([]byte, bufferSize)
|
||||||
|
packet []byte
|
||||||
|
remoteIP byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
packet, remoteIP, err = readNextPacket(n.iface, buf)
|
||||||
|
must(err)
|
||||||
|
must(n.w.WriteTo(remoteIP, 1, packet))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *TmpNode) readFromConn() {
|
||||||
|
var (
|
||||||
|
buf = make([]byte, bufferSize)
|
||||||
|
packet []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
_, _, packet, err = node.r.Read(buf)
|
||||||
|
must(err)
|
||||||
|
// We assume that we're only receiving packets from one source.
|
||||||
|
|
||||||
|
_, err = node.iface.Write(packet)
|
||||||
|
must(err)
|
||||||
|
}
|
||||||
|
}
|
1
stage1/README.md
Normal file
1
stage1/README.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
## Stage1: Point-to-point Tunnel w/ no Encryption
|
32
stage1/client.go
Normal file
32
stage1/client.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
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)
|
||||||
|
}
|
7
stage1/cmd/client/build.sh
Executable file
7
stage1/cmd/client/build.sh
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#!/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"
|
14
stage1/cmd/client/main.go
Normal file
14
stage1/cmd/client/main.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
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])
|
||||||
|
}
|
4
stage1/cmd/server/build.sh
Executable file
4
stage1/cmd/server/build.sh
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
go build
|
||||||
|
sudo setcap cap_net_admin+iep server
|
14
stage1/cmd/server/main.go
Normal file
14
stage1/cmd/server/main.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
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])
|
||||||
|
}
|
142
stage1/interface.go
Normal file
142
stage1/interface.go
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
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
Normal file
109
stage1/server.go
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
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
stage1/startup.go
Normal file
1
stage1/startup.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package stage1
|
4
stage2/README.md
Normal file
4
stage2/README.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
## Stage2:
|
||||||
|
|
||||||
|
* Point-to-point Tunnel w/ no Encryption
|
||||||
|
* Server gets client's addr from first packet
|
35
stage2/client.go
Normal file
35
stage2/client.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
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)
|
||||||
|
}
|
5
stage2/cmd/client/build.sh
Executable file
5
stage2/cmd/client/build.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
go build
|
||||||
|
sudo setcap cap_net_admin+iep ./client
|
||||||
|
./client 144.76.78.93
|
14
stage2/cmd/client/main.go
Normal file
14
stage2/cmd/client/main.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
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])
|
||||||
|
}
|
6
stage2/cmd/server/build.sh
Executable file
6
stage2/cmd/server/build.sh
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
#!/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"
|
7
stage2/cmd/server/main.go
Normal file
7
stage2/cmd/server/main.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "vppn/stage2"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
stage2.RunServer()
|
||||||
|
}
|
142
stage2/interface.go
Normal file
142
stage2/interface.go
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
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
Normal file
112
stage2/server.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
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
stage2/startup.go
Normal file
1
stage2/startup.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package stage2
|
16
stage3/README.md
Normal file
16
stage3/README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
## 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.
|
35
stage3/client.go
Normal file
35
stage3/client.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
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)
|
||||||
|
}
|
5
stage3/cmd/client/build.sh
Executable file
5
stage3/cmd/client/build.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
go build
|
||||||
|
sudo setcap cap_net_admin+iep ./client
|
||||||
|
./client 144.76.78.93
|
14
stage3/cmd/client/main.go
Normal file
14
stage3/cmd/client/main.go
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
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])
|
||||||
|
}
|
7
stage3/cmd/server/build.sh
Executable file
7
stage3/cmd/server/build.sh
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#!/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"
|
7
stage3/cmd/server/main.go
Normal file
7
stage3/cmd/server/main.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "vppn/stage3"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
stage3.RunServer()
|
||||||
|
}
|
142
stage3/interface.go
Normal file
142
stage3/interface.go
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
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
|
||||||
|
}
|
23
stage3/packet.go
Normal file
23
stage3/packet.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
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
|
||||||
|
}
|
22
stage3/packet_test.go
Normal file
22
stage3/packet_test.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
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
Normal file
147
stage3/server.go
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
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
stage3/startup.go
Normal file
1
stage3/startup.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package stage3
|
Loading…
x
Reference in New Issue
Block a user