From 50089779cc4e848ccd08c60901a9bee87f84f352 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sun, 8 Nov 2020 20:52:09 +0000 Subject: [PATCH 001/121] readme with scripts --- Makefile | 4 +-- README.md | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 78 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 2c4d8dd..6f4ceda 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ manual: docker run --rm -v /tmp:/tmp -v ${PWD}:/app -w /app golang:1.15-buster go build -o /tmp/mpbl3p - rsync -p /tmp/mpbl3p 10.21.12.101: - rsync -p /tmp/mpbl3p 10.21.12.102: + rsync -p /tmp/mpbl3p 10.21.10.3: + rsync -p /tmp/mpbl3p 10.21.10.4: manual-bsd: GOOS=freebsd go build -o /tmp/mpbl3p diff --git a/README.md b/README.md index a4292f6..250b5d1 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,84 @@ ### Linux #### Policy Based Routing - ip route flush 11 - ip route add table 11 to 1.1.1.0/24 dev eth1 - ip rule add from 1.1.1.4 table 11 priority 11 - ip route flush 10 - ip route add table 10 to 1.1.1.0/24 dev eth2 - ip rule add from 1.1.1.5 table 10 priority 10 + ip route add table 10 to 1.1.1.0/24 dev eth1 + ip rule add from 1.1.1.4 table 10 priority 10 + + ip route flush 11 + ip route add table 11 to 1.1.1.0/24 dev eth2 + ip rule add from 1.1.1.5 table 11 priority 11 #### ARP Flux sysctl -w net.ipv4.conf.all.arp_announce=1 - sysctl -w net.ipv4.conf.all.arp_ignore=2 + sysctl -w net.ipv4.conf.all.arp_ignore=1 + +See http://kb.linuxvirtualserver.org/wiki/Using_arp_announce/arp_ignore_to_disable_ARP + +### Setup Scripts +These are functional setup scripts that make the application run as intended on Linux. They should later be split into +component parts, or incorporated into the main application. + +#### Remote Portal + + #!/bin/bash + set -e + + # IPv4 Forwarding + sysctl -w net.ipv4.ip_forward=1 + + # Tunnel addr/up + ip addr add 172.19.152.2/31 dev nc0 + ip link set up nc0 + + # Deliberately break local routing + ip rule add from all table local priority 20 + ip rule del 0 || true + + # Route packets to the interface but for nc to this host + ip rule add to 1.1.1.3 dport 1234 table local priority 9 + + # Route packets to the interface but not for nc via the tunnel + ip route flush 10 + ip route add table 10 to 1.1.1.3 via 172.19.152.3 dev nc0 + ip rule add to 1.1.1.3 table 10 priority 10 + +#### Local Portal + + #!/bin/bash + set -e + + # Fix ARP + sysctl -w net.ipv4.conf.all.arp_announce=1 + sysctl -w net.ipv4.conf.all.arp_ignore=1 + + # IPv4 Forwarding + sysctl -w net.ipv4.ip_forward=1 + + # Tunnel addr/up + ip addr add 172.19.152.3/31 dev nc0 + ip link set up nc0 + + # Fix routing out of the correct interfaces + ip route flush 10 + ip route add table 10 to 1.1.1.0/24 dev eth1 + ip rule add from 1.1.1.4 table 10 priority 10 + + ip route flush 11 + ip route add table 11 to 1.1.1.0/24 dev eth2 + ip rule add from 1.1.1.5 table 11 priority 11 + + # Route packets from the remote portal's address on the client interface via the tunnel + ip route flush 12 + ip route add table 12 to 1.1.1.0/24 via 172.19.152.2 dev nc0 + ip rule add from 1.1.1.3 iif eth3 table 12 priority 12 + + # Route packets to the remote portal's address out of the client interface + ip route flush 13 + ip route add table 13 to 1.1.1.3 dev eth3 + ip rule add to 1.1.1.3 table 13 priority 13 + +#### Client + +No configuration needed. Simply set the IP to that of the remote server/32 with no gateway. -- 2.47.0 From e36c0cae295320a793cff331b558d60988692d53 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Wed, 25 Nov 2020 14:38:22 +0000 Subject: [PATCH 002/121] readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 250b5d1..5dc8d52 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ component parts, or incorporated into the main application. # IPv4 Forwarding sysctl -w net.ipv4.ip_forward=1 + sysctl -w net.ipv4.conf.eth0.proxy_arp=1 # Tunnel addr/up ip addr add 172.19.152.2/31 dev nc0 @@ -84,4 +85,4 @@ component parts, or incorporated into the main application. #### Client -No configuration needed. Simply set the IP to that of the remote server/32 with no gateway. +No configuration needed. Simply set the IP to that of the remote server/32 with a gateway of 192.168.1.1. -- 2.47.0 From 7c2b8782f1b3d070bd45b8829c7ee0a36b1eea50 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Wed, 25 Nov 2020 19:35:31 +0000 Subject: [PATCH 003/121] began udp --- proxy/packet.go | 19 +++++++---- tcp/flow.go | 4 +-- tun/tun.go | 6 ++-- udp/congestion.go | 11 ++++++ udp/congestion/reno.go | 58 +++++++++++++++++++++++++++++++ udp/flow.go | 57 +++++++++++++++++++++++++++++++ udp/listener.go | 77 ++++++++++++++++++++++++++++++++++++++++++ udp/packet.go | 11 ++++++ utils/heap.go | 64 +++++++++++++++++++++++++++++++++++ utils/heap_test.go | 54 +++++++++++++++++++++++++++++ utils/utils.go | 13 ------- 11 files changed, 349 insertions(+), 25 deletions(-) create mode 100644 udp/congestion.go create mode 100644 udp/congestion/reno.go create mode 100644 udp/flow.go create mode 100644 udp/listener.go create mode 100644 udp/packet.go create mode 100644 utils/heap.go create mode 100644 utils/heap_test.go delete mode 100644 utils/utils.go diff --git a/proxy/packet.go b/proxy/packet.go index 1588278..b6d9ffc 100644 --- a/proxy/packet.go +++ b/proxy/packet.go @@ -5,14 +5,19 @@ import ( "time" ) -type Packet struct { +type Packet interface { + Marshal(generator MacGenerator) []byte + Raw() []byte +} + +type SimplePacket struct { Data []byte timestamp time.Time } // create a packet from the raw data of an IP packet -func NewPacket(data []byte) Packet { - return Packet{ +func NewSimplePacket(data []byte) Packet { + return SimplePacket{ Data: data, timestamp: time.Now(), } @@ -25,10 +30,10 @@ func UnmarshalPacket(raw []byte, verifier MacVerifier) (Packet, error) { sum := raw[len(raw)-verifier.CodeLength():] if err := verifier.Verify(data, sum); err != nil { - return Packet{}, err + return SimplePacket{}, err } - p := Packet{ + p := SimplePacket{ Data: data[:len(data)-8], } @@ -39,12 +44,12 @@ func UnmarshalPacket(raw []byte, verifier MacVerifier) (Packet, error) { } // get the raw data of the IP packet -func (p Packet) Raw() []byte { +func (p SimplePacket) Raw() []byte { return p.Data } // produce the wrapped format of a packet -func (p Packet) Marshal(generator MacGenerator) []byte { +func (p SimplePacket) Marshal(generator MacGenerator) []byte { // length of data + length of timestamp (8 byte) + length of checksum slice := make([]byte, len(p.Data)+8+generator.CodeLength()) diff --git a/tcp/flow.go b/tcp/flow.go index 26607e8..ff4e58e 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -121,13 +121,13 @@ func (f *InitiatedFlow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { if !f.isAlive { - return proxy.Packet{}, shared.ErrDeadConnection + return nil, shared.ErrDeadConnection } data, err := f.produceMarshalled() if err != nil { f.isAlive = false - return proxy.Packet{}, err + return nil, err } return proxy.UnmarshalPacket(data, v) diff --git a/tun/tun.go b/tun/tun.go index 2f5c5cb..7fe7e2f 100644 --- a/tun/tun.go +++ b/tun/tun.go @@ -61,14 +61,14 @@ func (t *SourceSink) Source() (proxy.Packet, error) { read, err := t.tun.Read(buf) if err != nil { - return proxy.Packet{}, err + return nil, err } if read == 0 { - return proxy.Packet{}, io.EOF + return nil, io.EOF } - return proxy.NewPacket(buf[:read]), nil + return proxy.NewSimplePacket(buf[:read]), nil } var good, bad float64 diff --git a/udp/congestion.go b/udp/congestion.go new file mode 100644 index 0000000..4ff8d36 --- /dev/null +++ b/udp/congestion.go @@ -0,0 +1,11 @@ +package udp + +type Congestion interface { + Sequence() uint64 + + ReceivedAck(uint64) + NextAck() uint64 + + ReceivedNack(uint64) + NextNack() uint64 +} diff --git a/udp/congestion/reno.go b/udp/congestion/reno.go new file mode 100644 index 0000000..d8b2367 --- /dev/null +++ b/udp/congestion/reno.go @@ -0,0 +1,58 @@ +package congestion + +import "time" + +type NewReno struct { + sequence chan uint64 + packetTimes map[uint64]time.Time + + nextAck uint64 + nextNack uint64 + + rtt float64 +} + +func NewNewReno() *NewReno { + c := NewReno{ + sequence: make(chan uint64), + packetTimes: make(map[uint64]time.Time), + } + + go func() { + var s uint64 + for { + if s == 0 { + continue + } + + c.sequence <- s + s++ + } + }() + + return &c +} + +// It is assumed that ReceivedAck will only be called by one thread +func (c *NewReno) ReceivedAck(uint64) { + +} + +// It is assumed that ReceivedNack will only be called by one thread +func (c *NewReno) ReceivedNack(uint64) { + +} + +func (c *NewReno) Sequence() uint64 { + s := <-c.sequence + c.packetTimes[s] = time.Now() + return s +} + +func (c *NewReno) NextAck() uint64 { + return c.nextAck +} + +func (c *NewReno) NextNack() uint64 { + return c.nextNack +} diff --git a/udp/flow.go b/udp/flow.go new file mode 100644 index 0000000..7a2430f --- /dev/null +++ b/udp/flow.go @@ -0,0 +1,57 @@ +package udp + +import ( + "errors" + "mpbl3p/proxy" + "net" + "time" +) + +type PacketWriter interface { + WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) +} + +type PacketConn interface { + PacketWriter + ReadFromUDP(b []byte) (int, *net.UDPAddr, error) +} + +type Ack struct { + time time.Time + packet uint64 + + sent bool +} + +type Flow struct { + writer PacketWriter + raddr net.UDPAddr + + isAlive bool + congestion Congestion +} + +func (f *Flow) IsAlive() bool { + return f.isAlive +} + +func (f *Flow) Consume(pp proxy.Packet, g proxy.MacGenerator) error { + p := Packet{ + seq: f.congestion.Sequence(), + data: pp, + } + + p.ack = f.congestion.NextAck() + p.nack = f.congestion.NextNack() + + return errors.New("not implemented") +} + +func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { + return proxy.Packet{}, errors.New("not implemented") +} + +func (f *Flow) handleDatagram(p []byte) { + // TODO: Congestion control - read off the ACKs for the send half + // TODO: Congestion control - schedule ACKs for this side +} diff --git a/udp/listener.go b/udp/listener.go new file mode 100644 index 0000000..2ad59ff --- /dev/null +++ b/udp/listener.go @@ -0,0 +1,77 @@ +package udp + +import ( + "log" + "mpbl3p/proxy" + "net" +) + +type ComparableUdpAddress struct { + IP [16]byte + Port int + Zone string +} + +func fromUdpAddress(address net.UDPAddr) ComparableUdpAddress { + var ip [16]byte + for i, b := range []byte(address.IP) { + ip[i] = b + } + + return ComparableUdpAddress{ + IP: ip, + Port: address.Port, + Zone: address.Zone, + } +} + +func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier) error { + laddr, err := net.ResolveUDPAddr("udp", local) + if err != nil { + return err + } + + pconn, err := net.ListenUDP("udp", laddr) + if err != nil { + return err + } + + err = pconn.SetWriteBuffer(0) + if err != nil { + panic(err) + } + + receivedConnections := make(map[ComparableUdpAddress]*Flow) + + go func() { + for { + buf := make([]byte, 1500) + + _, addr, err := pconn.ReadFromUDP(buf) + if err != nil { + panic(err) + } + + raddr := fromUdpAddress(*addr) + if f, exists := receivedConnections[raddr]; exists { + f.handleDatagram(buf) + continue + } + + f := Flow{ + writer: pconn, + raddr: *addr, + isAlive: true, + } + + receivedConnections[raddr] = &f + + log.Printf("received new connection: %v\n", f) + + p.AddConsumer(&f) + p.AddProducer(&f, v) + } + }() + + return nil +} diff --git a/udp/packet.go b/udp/packet.go new file mode 100644 index 0000000..619dde0 --- /dev/null +++ b/udp/packet.go @@ -0,0 +1,11 @@ +package udp + +import "mpbl3p/proxy" + +type Packet struct { + ack uint64 + nack uint64 + seq uint64 + + data proxy.Packet +} diff --git a/utils/heap.go b/utils/heap.go new file mode 100644 index 0000000..6014055 --- /dev/null +++ b/utils/heap.go @@ -0,0 +1,64 @@ +package utils + +import "errors" + +var ErrorEmptyHeap = errors.New("attempted to extract from empty heap") + +// A MinHeap for Uint64 +type Uint64Heap struct { + data []uint64 +} + +func (h *Uint64Heap) swap(x, y int) { + h.data[x] = h.data[x] ^ h.data[y] + h.data[y] = h.data[y] ^ h.data[x] + h.data[x] = h.data[x] ^ h.data[y] +} + +func (h *Uint64Heap) Insert(new uint64) uint64 { + h.data = append(h.data, new) + + child := len(h.data) - 1 + for child != 0 { + parent := (child - 1) / 2 + if h.data[parent] > h.data[child] { + h.swap(parent, child) + } else { + break + } + child = parent + } + + return h.data[0] +} + +func (h *Uint64Heap) Extract() (uint64, error) { + if len(h.data) == 0 { + return 0, ErrorEmptyHeap + } + min := h.data[0] + + h.data[0] = h.data[len(h.data)-1] + h.data = h.data[:len(h.data)-1] + + parent := 0 + for { + left, right := parent*2+1, parent*2+2 + + if (left < len(h.data) && h.data[parent] > h.data[left]) || (right < len(h.data) && h.data[parent] > h.data[right]) { + if right < len(h.data) && h.data[left] > h.data[right] { + h.swap(parent, right) + parent = right + } else { + h.swap(parent, left) + parent = left + } + } else { + return min, nil + } + } +} + +func (h *Uint64Heap) Peek() uint64 { + return h.data[0] +} diff --git a/utils/heap_test.go b/utils/heap_test.go new file mode 100644 index 0000000..da27a2c --- /dev/null +++ b/utils/heap_test.go @@ -0,0 +1,54 @@ +package utils + +import ( + "fmt" + "github.com/stretchr/testify/assert" + "math/rand" + "testing" + "time" +) + +func SlowHeapSort(in []uint64) []uint64 { + out := make([]uint64, len(in)) + + var heap Uint64Heap + + for _, x := range in { + heap.Insert(x) + } + for i := range out { + var err error + out[i], err = heap.Extract() + if err != nil { + panic(err) + } + } + + return out +} + +func TestUint64Heap(t *testing.T) { + t.Run("EquivalentToMerge", func(t *testing.T) { + const ArrayLength = 50 + + sortedArray := make([]uint64, ArrayLength) + array := make([]uint64, ArrayLength) + + for i := range array { + sortedArray[i] = uint64(i) + array[i] = uint64(i) + } + + rand.Seed(time.Now().Unix()) + + for i := 0; i < 100; i++ { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + rand.Shuffle(50, func(i, j int) { array[i], array[j] = array[j], array[i] }) + + heapSorted := SlowHeapSort(array) + + assert.Equal(t, sortedArray, heapSorted) + }) + } + }) +} diff --git a/utils/utils.go b/utils/utils.go deleted file mode 100644 index 2548b3b..0000000 --- a/utils/utils.go +++ /dev/null @@ -1,13 +0,0 @@ -package utils - -var NextId = make(chan int) - -func init() { - go func() { - i := 0 - for { - NextId <- i - i += 1 - } - }() -} -- 2.47.0 From 3ac4d1f618f2a207f7b5754c71c515763dbd6eb3 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Thu, 26 Nov 2020 18:55:29 +0000 Subject: [PATCH 004/121] initial udp code --- Makefile | 4 +- config/builder.go | 37 ++++++++++++++++- config/config.go | 2 +- proxy/packet.go | 49 +++++++++++----------- proxy/packet_test.go | 4 +- tcp/flow.go | 11 ++++- tcp/flow_test.go | 4 +- tun/tun.go | 2 +- udp/congestion.go | 11 ++--- udp/congestion/newreno.go | 85 +++++++++++++++++++++++++++++++++++++++ udp/congestion/reno.go | 58 -------------------------- udp/flow.go | 70 +++++++++++++++++++++++++++----- udp/packet.go | 34 ++++++++++++++-- utils/heap.go | 17 ++++---- utils/heap_test.go | 2 +- 15 files changed, 268 insertions(+), 122 deletions(-) create mode 100644 udp/congestion/newreno.go delete mode 100644 udp/congestion/reno.go diff --git a/Makefile b/Makefile index 6f4ceda..2c4d8dd 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ manual: docker run --rm -v /tmp:/tmp -v ${PWD}:/app -w /app golang:1.15-buster go build -o /tmp/mpbl3p - rsync -p /tmp/mpbl3p 10.21.10.3: - rsync -p /tmp/mpbl3p 10.21.10.4: + rsync -p /tmp/mpbl3p 10.21.12.101: + rsync -p /tmp/mpbl3p 10.21.12.102: manual-bsd: GOOS=freebsd go build -o /tmp/mpbl3p diff --git a/config/builder.go b/config/builder.go index 6f94d8b..2fa7f5a 100644 --- a/config/builder.go +++ b/config/builder.go @@ -5,6 +5,7 @@ import ( "mpbl3p/proxy" "mpbl3p/tcp" "mpbl3p/tun" + "mpbl3p/udp" ) // TODO: Delete this code as soon as an alternative is available @@ -45,6 +46,11 @@ func (c Configuration) Build() (*proxy.Proxy, error) { if err != nil { return nil, err } + case "UDP": + err := buildUdp(p, peer) + if err != nil { + return nil, err + } } } @@ -58,10 +64,14 @@ func buildTcp(p *proxy.Proxy, peer Peer) error { fmt.Sprintf("%s:%d", peer.RemoteHost, peer.RemotePort), ) + if err != nil { + return err + } + p.AddConsumer(f) p.AddProducer(f, UselessMac{}) - return err + return nil } err := tcp.NewListener(p, fmt.Sprintf("%s:%d", peer.LocalHost, peer.LocalPort), UselessMac{}) @@ -71,3 +81,28 @@ func buildTcp(p *proxy.Proxy, peer Peer) error { return nil } + +func buildUdp(p *proxy.Proxy, peer Peer) error { + if peer.RemoteHost != "" { + f, err := udp.InitiateFlow( + fmt.Sprintf("%s:", peer.LocalHost), + fmt.Sprintf("%s:%d", peer.RemoteHost, peer.RemotePort), + ) + + if err != nil { + return err + } + + p.AddConsumer(f) + p.AddProducer(f, UselessMac{}) + + return nil + } + + err := udp.NewListener(p, fmt.Sprintf("%s:%d", peer.LocalHost, peer.LocalPort), UselessMac{}) + if err != nil { + return err + } + + return nil +} diff --git a/config/config.go b/config/config.go index 76de54c..0e6a81d 100644 --- a/config/config.go +++ b/config/config.go @@ -16,7 +16,7 @@ type Host struct { type Peer struct { PublicKey string `validate:"required"` - Method string `validate:"oneof=TCP"` + Method string `validate:"oneof=TCP,UDP"` LocalHost string `validate:"omitempty,ip"` LocalPort uint `validate:"max=65535"` diff --git a/proxy/packet.go b/proxy/packet.go index b6d9ffc..f56a84a 100644 --- a/proxy/packet.go +++ b/proxy/packet.go @@ -6,8 +6,7 @@ import ( ) type Packet interface { - Marshal(generator MacGenerator) []byte - Raw() []byte + Marshal() []byte } type SimplePacket struct { @@ -24,15 +23,7 @@ func NewSimplePacket(data []byte) Packet { } // rebuild a packet from the wrapped format -func UnmarshalPacket(raw []byte, verifier MacVerifier) (Packet, error) { - // the MAC is the last N bytes - data := raw[:len(raw)-verifier.CodeLength()] - sum := raw[len(raw)-verifier.CodeLength():] - - if err := verifier.Verify(data, sum); err != nil { - return SimplePacket{}, err - } - +func UnmarshalSimplePacket(data []byte) (SimplePacket, error) { p := SimplePacket{ Data: data[:len(data)-8], } @@ -44,22 +35,28 @@ func UnmarshalPacket(raw []byte, verifier MacVerifier) (Packet, error) { } // get the raw data of the IP packet -func (p SimplePacket) Raw() []byte { - return p.Data -} - -// produce the wrapped format of a packet -func (p SimplePacket) Marshal(generator MacGenerator) []byte { - // length of data + length of timestamp (8 byte) + length of checksum - slice := make([]byte, len(p.Data)+8+generator.CodeLength()) - - copy(slice, p.Data) +func (p SimplePacket) Marshal() []byte { + footer := make([]byte, 8) unixTime := uint64(p.timestamp.Unix()) - binary.LittleEndian.PutUint64(slice[len(p.Data):], unixTime) + binary.LittleEndian.PutUint64(footer, unixTime) - mac := generator.Generate(slice) - copy(slice[len(p.Data)+8:], mac) - - return slice + return append(p.Data, footer...) +} + +func AppendMac(b []byte, g MacGenerator) []byte { + mac := g.Generate(b) + b = append(b, mac...) + return b +} + +func StripMac(b []byte, v MacVerifier) ([]byte, error) { + data := b[:len(b)-v.CodeLength()] + sum := b[len(b)-v.CodeLength():] + + if err := v.Verify(data, sum); err != nil { + return nil, err + } + + return data, nil } diff --git a/proxy/packet_test.go b/proxy/packet_test.go index 5e14843..0db7222 100644 --- a/proxy/packet_test.go +++ b/proxy/packet_test.go @@ -26,9 +26,9 @@ func TestUnmarshalPacket(t *testing.T) { testMarshalled := testPacket.Marshal(testMac) t.Run("Length", func(t *testing.T) { - p, err := UnmarshalPacket(testMarshalled, testMac) + p, err := UnmarshalSimplePacket(testMarshalled, testMac) require.Nil(t, err) - assert.Len(t, p.Raw(), len(testContent)) + assert.Len(t, p.Marshal(), len(testContent)) }) } diff --git a/tcp/flow.go b/tcp/flow.go index ff4e58e..df19bb6 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -91,7 +91,9 @@ func (f *Flow) Consume(p proxy.Packet, g proxy.MacGenerator) (err error) { return shared.ErrDeadConnection } - data := p.Marshal(g) + marshalled := p.Marshal() + data := proxy.AppendMac(marshalled, g) + err = f.consumeMarshalled(data) if err != nil { f.isAlive = false @@ -130,7 +132,12 @@ func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { return nil, err } - return proxy.UnmarshalPacket(data, v) + b, err := proxy.StripMac(data, v) + if err != nil { + return nil, err + } + + return proxy.UnmarshalSimplePacket(b) } func (f *Flow) produceMarshalled() ([]byte, error) { diff --git a/tcp/flow_test.go b/tcp/flow_test.go index 88fbf1c..cb02839 100644 --- a/tcp/flow_test.go +++ b/tcp/flow_test.go @@ -48,7 +48,7 @@ func TestFlow_Produce(t *testing.T) { p, err := flowA.Produce(testMac) require.Nil(t, err) - assert.Equal(t, len(testContent), len(p.Raw())) + assert.Equal(t, len(testContent), len(p.Marshal())) }) t.Run("Value", func(t *testing.T) { @@ -61,6 +61,6 @@ func TestFlow_Produce(t *testing.T) { p, err := flowA.Produce(testMac) require.Nil(t, err) - assert.Equal(t, testContent, string(p.Raw())) + assert.Equal(t, testContent, string(p.Marshal())) }) } diff --git a/tun/tun.go b/tun/tun.go index 7fe7e2f..837b496 100644 --- a/tun/tun.go +++ b/tun/tun.go @@ -79,7 +79,7 @@ func (t *SourceSink) Sink(packet proxy.Packet) error { t.upMu.Unlock() } - _, err := t.tun.Write(packet.Raw()) + _, err := t.tun.Write(packet.Marshal()) if err != nil { switch err.(type) { case *os.PathError: diff --git a/udp/congestion.go b/udp/congestion.go index 4ff8d36..1d4d821 100644 --- a/udp/congestion.go +++ b/udp/congestion.go @@ -1,11 +1,12 @@ package udp type Congestion interface { - Sequence() uint64 + Sequence() uint32 + ReceivedPacket(seq uint32) - ReceivedAck(uint64) - NextAck() uint64 + ReceivedAck(uint32) + NextAck() uint32 - ReceivedNack(uint64) - NextNack() uint64 + ReceivedNack(uint32) + NextNack() uint32 } diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go new file mode 100644 index 0000000..3676b39 --- /dev/null +++ b/udp/congestion/newreno.go @@ -0,0 +1,85 @@ +package congestion + +import ( + "mpbl3p/utils" + "time" +) + +type NewReno struct { + sequence chan uint32 + packetTimes map[uint32]time.Time + + nextAck uint32 + nextNack uint32 + + fastStart bool + windowSize uint + rtt time.Duration + + acksToSend utils.Uint32Heap +} + +func NewNewReno() *NewReno { + c := NewReno{ + sequence: make(chan uint32), + packetTimes: make(map[uint32]time.Time), + windowSize: 1, + } + + go func() { + var s uint32 + for { + if s == 0 { + continue + } + + c.sequence <- s + s++ + } + }() + + return &c +} + +// It is assumed that ReceivedAck will only be called by one thread +func (c *NewReno) ReceivedAck(ack uint32) { + // RTT + // Update using an exponential average + + + // GROW + // CASE: exponential. increase window size by one per ack + // CASE: standard. increase window size by one per window of acks +} + +// It is assumed that ReceivedNack will only be called by one thread +func (c *NewReno) ReceivedNack(nack uint32) { + // Back off +} + +func (c *NewReno) ReceivedPacket(seq uint32) { + c.acksToSend.Insert(seq) + + ack, err := c.acksToSend.Extract() + if err != nil { + panic(err) + } + + for a, _ := c.acksToSend.Peek(); a == ack+1; { + ack, _ = c.acksToSend.Extract() + } +} + +func (c *NewReno) Sequence() uint32 { + s := <-c.sequence + c.packetTimes[s] = time.Now() + return s +} + +func (c *NewReno) NextAck() uint32 { + return c.nextAck +} + +func (c *NewReno) NextNack() uint32 { + return c.nextNack +} diff --git a/udp/congestion/reno.go b/udp/congestion/reno.go deleted file mode 100644 index d8b2367..0000000 --- a/udp/congestion/reno.go +++ /dev/null @@ -1,58 +0,0 @@ -package congestion - -import "time" - -type NewReno struct { - sequence chan uint64 - packetTimes map[uint64]time.Time - - nextAck uint64 - nextNack uint64 - - rtt float64 -} - -func NewNewReno() *NewReno { - c := NewReno{ - sequence: make(chan uint64), - packetTimes: make(map[uint64]time.Time), - } - - go func() { - var s uint64 - for { - if s == 0 { - continue - } - - c.sequence <- s - s++ - } - }() - - return &c -} - -// It is assumed that ReceivedAck will only be called by one thread -func (c *NewReno) ReceivedAck(uint64) { - -} - -// It is assumed that ReceivedNack will only be called by one thread -func (c *NewReno) ReceivedNack(uint64) { - -} - -func (c *NewReno) Sequence() uint64 { - s := <-c.sequence - c.packetTimes[s] = time.Now() - return s -} - -func (c *NewReno) NextAck() uint64 { - return c.nextAck -} - -func (c *NewReno) NextNack() uint64 { - return c.nextNack -} diff --git a/udp/flow.go b/udp/flow.go index 7a2430f..359a46c 100644 --- a/udp/flow.go +++ b/udp/flow.go @@ -1,10 +1,9 @@ package udp import ( - "errors" "mpbl3p/proxy" "net" - "time" + "sync" ) type PacketWriter interface { @@ -16,11 +15,12 @@ type PacketConn interface { ReadFromUDP(b []byte) (int, *net.UDPAddr, error) } -type Ack struct { - time time.Time - packet uint64 +type InitiatedFlow struct { + Local string + Remote string - sent bool + mu sync.RWMutex + Flow } type Flow struct { @@ -29,6 +29,31 @@ type Flow struct { isAlive bool congestion Congestion + + inboundDatagrams chan []byte +} + +func newOutboundFlow(c Congestion) *Flow { + return &Flow{ + congestion: c, + inboundDatagrams: make(chan []byte), + } +} + +func newInboundFlow(c Congestion) *Flow { + return &Flow{ + congestion: c, + inboundDatagrams: make(chan []byte), + } +} + +func InitiateFlow(local, remote string) (*InitiatedFlow, error) { + f := InitiatedFlow{ + Local: local, + Remote: remote, + } + + return &f, nil } func (f *Flow) IsAlive() bool { @@ -44,14 +69,39 @@ func (f *Flow) Consume(pp proxy.Packet, g proxy.MacGenerator) error { p.ack = f.congestion.NextAck() p.nack = f.congestion.NextNack() - return errors.New("not implemented") + b := p.Marshal() + b = proxy.AppendMac(b, g) + + _, err := f.writer.WriteToUDP(b, &f.raddr) + return err } func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { - return proxy.Packet{}, errors.New("not implemented") + b, err := proxy.StripMac(<-f.inboundDatagrams, v) + if err != nil { + return nil, err + } + + p, err := UnmarshalPacket(b) + if err != nil { + return nil, err + } + + // schedule an ack for this sequence number + f.congestion.ReceivedPacket(p.seq) + + // adjust our sending congestion control based on their acks + if p.ack != 0 { + f.congestion.ReceivedAck(p.ack) + } + // adjust our sending congestion control based on their nacks + if p.nack != 0 { + f.congestion.ReceivedNack(p.nack) + } + + return p, nil } func (f *Flow) handleDatagram(p []byte) { - // TODO: Congestion control - read off the ACKs for the send half - // TODO: Congestion control - schedule ACKs for this side + f.inboundDatagrams <- p } diff --git a/udp/packet.go b/udp/packet.go index 619dde0..b7a4f34 100644 --- a/udp/packet.go +++ b/udp/packet.go @@ -1,11 +1,37 @@ package udp -import "mpbl3p/proxy" +import ( + "encoding/binary" + "mpbl3p/proxy" +) type Packet struct { - ack uint64 - nack uint64 - seq uint64 + ack uint32 + nack uint32 + seq uint32 data proxy.Packet } + +func UnmarshalPacket(b []byte) (p Packet, err error) { + p.ack = binary.LittleEndian.Uint32(b[0:4]) + p.nack = binary.LittleEndian.Uint32(b[4:8]) + p.seq = binary.LittleEndian.Uint32(b[8:12]) + + p.data, err = proxy.UnmarshalSimplePacket(b[12:]) + if err != nil { + return Packet{}, err + } + return p, nil +} + +func (p Packet) Marshal() []byte { + data := p.data.Marshal() + header := make([]byte, 12) + + binary.LittleEndian.PutUint32(header[0:4], p.ack) + binary.LittleEndian.PutUint32(header[4:8], p.nack) + binary.LittleEndian.PutUint32(header[8:12], p.seq) + + return append(header, data...) +} diff --git a/utils/heap.go b/utils/heap.go index 6014055..ccadc0f 100644 --- a/utils/heap.go +++ b/utils/heap.go @@ -5,17 +5,17 @@ import "errors" var ErrorEmptyHeap = errors.New("attempted to extract from empty heap") // A MinHeap for Uint64 -type Uint64Heap struct { - data []uint64 +type Uint32Heap struct { + data []uint32 } -func (h *Uint64Heap) swap(x, y int) { +func (h *Uint32Heap) swap(x, y int) { h.data[x] = h.data[x] ^ h.data[y] h.data[y] = h.data[y] ^ h.data[x] h.data[x] = h.data[x] ^ h.data[y] } -func (h *Uint64Heap) Insert(new uint64) uint64 { +func (h *Uint32Heap) Insert(new uint32) uint32 { h.data = append(h.data, new) child := len(h.data) - 1 @@ -32,7 +32,7 @@ func (h *Uint64Heap) Insert(new uint64) uint64 { return h.data[0] } -func (h *Uint64Heap) Extract() (uint64, error) { +func (h *Uint32Heap) Extract() (uint32, error) { if len(h.data) == 0 { return 0, ErrorEmptyHeap } @@ -59,6 +59,9 @@ func (h *Uint64Heap) Extract() (uint64, error) { } } -func (h *Uint64Heap) Peek() uint64 { - return h.data[0] +func (h *Uint32Heap) Peek() (uint32, error) { + if len(h.data) == 0 { + return 0, ErrorEmptyHeap + } + return h.data[0], nil } diff --git a/utils/heap_test.go b/utils/heap_test.go index da27a2c..d0393f0 100644 --- a/utils/heap_test.go +++ b/utils/heap_test.go @@ -11,7 +11,7 @@ import ( func SlowHeapSort(in []uint64) []uint64 { out := make([]uint64, len(in)) - var heap Uint64Heap + var heap Uint32Heap for _, x := range in { heap.Insert(x) -- 2.47.0 From 787f80dc90cc80c16c5e815be0ea18414710dfe6 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Thu, 26 Nov 2020 19:18:07 +0000 Subject: [PATCH 005/121] validation and logging improvements --- config/config.go | 2 +- tcp/flow.go | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 0e6a81d..e5b6d18 100644 --- a/config/config.go +++ b/config/config.go @@ -16,7 +16,7 @@ type Host struct { type Peer struct { PublicKey string `validate:"required"` - Method string `validate:"oneof=TCP,UDP"` + Method string `validate:"oneof=TCP UDP"` LocalHost string `validate:"omitempty,ip"` LocalPort uint `validate:"max=65535"` diff --git a/tcp/flow.go b/tcp/flow.go index df19bb6..bf399bd 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -3,6 +3,7 @@ package tcp import ( "encoding/binary" "errors" + "fmt" "io" "mpbl3p/proxy" "mpbl3p/shared" @@ -17,6 +18,10 @@ type Conn interface { Read(b []byte) (n int, err error) Write(b []byte) (n int, err error) SetWriteDeadline(time.Time) error + + // For printing + LocalAddr() net.Addr + RemoteAddr() net.Addr } type InitiatedFlow struct { @@ -28,11 +33,19 @@ type InitiatedFlow struct { Flow } +func (f *InitiatedFlow) String() string { + return fmt.Sprintf("TcpOutbound{%v -> %v}", f.Local, f.Remote) +} + type Flow struct { conn Conn isAlive bool } +func (f Flow) String() string { + return fmt.Sprintf("TcpInbound{%v -> %v}", f.conn.RemoteAddr(), f.conn.LocalAddr()) +} + func InitiateFlow(local, remote string) (*InitiatedFlow, error) { f := InitiatedFlow{ Local: local, -- 2.47.0 From d65e8d357154145c442c6b3c37a50f771ef51a9d Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Thu, 26 Nov 2020 22:10:37 +0000 Subject: [PATCH 006/121] udp testing --- main.go | 9 ++- proxy/proxy.go | 12 +-- tcp/flow.go | 22 +++--- tcp/listener.go | 2 +- udp/congestion.go | 4 + udp/congestion/newreno.go | 127 +++++++++++++++++++++++++++----- udp/flow.go | 150 +++++++++++++++++++++++++++++++++----- udp/listener.go | 24 ++++-- utils/heap.go | 34 ++++----- utils/heap_test.go | 14 ++-- 10 files changed, 312 insertions(+), 86 deletions(-) diff --git a/main.go b/main.go index d7f2633..493adad 100644 --- a/main.go +++ b/main.go @@ -13,7 +13,14 @@ func main() { log.Println("loading config...") - c, err := config.LoadConfig("config.ini") + var configLoc string + if v, ok := os.LookupEnv("CONFIG_LOC"); ok { + configLoc = v + } else { + configLoc = "config.ini" + } + + c, err := config.LoadConfig(configLoc) if err != nil { panic(err) } diff --git a/proxy/proxy.go b/proxy/proxy.go index e98a836..2bea389 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -75,13 +75,13 @@ func (p Proxy) AddConsumer(c Consumer) { if reconnectable { var err error for once := true; err != nil || once; once = false { - log.Printf("attempting to connect `%v`\n", c) + log.Printf("attempting to connect consumer `%v`\n", c) err = c.(Reconnectable).Reconnect() if !once { time.Sleep(time.Second) } } - log.Printf("connected `%v`\n", c) + log.Printf("connected consumer `%v`\n", c) } for c.IsAlive() { @@ -92,7 +92,7 @@ func (p Proxy) AddConsumer(c Consumer) { } } - log.Printf("closed connection `%v`\n", c) + log.Printf("closed consumer `%v`\n", c) }() } @@ -104,13 +104,13 @@ func (p Proxy) AddProducer(pr Producer, v MacVerifier) { if reconnectable { var err error for once := true; err != nil || once; once = false { - log.Printf("attempting to connect `%v`\n", pr) + log.Printf("attempting to connect producer `%v`\n", pr) err = pr.(Reconnectable).Reconnect() if !once { time.Sleep(time.Second) } } - log.Printf("connected `%v`\n", pr) + log.Printf("connected producer `%v`\n", pr) } for pr.IsAlive() { @@ -123,6 +123,6 @@ func (p Proxy) AddProducer(pr Producer, v MacVerifier) { } } - log.Printf("closed connection `%v`\n", pr) + log.Printf("closed producer `%v`\n", pr) }() } diff --git a/tcp/flow.go b/tcp/flow.go index bf399bd..c626e49 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -88,10 +88,6 @@ func (f *InitiatedFlow) Reconnect() error { return nil } -func (f *Flow) IsAlive() bool { - return f.isAlive -} - func (f *InitiatedFlow) Consume(p proxy.Packet, g proxy.MacGenerator) error { f.mu.RLock() defer f.mu.RUnlock() @@ -99,6 +95,17 @@ func (f *InitiatedFlow) Consume(p proxy.Packet, g proxy.MacGenerator) error { return f.Flow.Consume(p, g) } +func (f *InitiatedFlow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { + f.mu.RLock() + defer f.mu.RUnlock() + + return f.Flow.Produce(v) +} + +func (f *Flow) IsAlive() bool { + return f.isAlive +} + func (f *Flow) Consume(p proxy.Packet, g proxy.MacGenerator) (err error) { if !f.isAlive { return shared.ErrDeadConnection @@ -127,13 +134,6 @@ func (f *Flow) consumeMarshalled(data []byte) error { return err } -func (f *InitiatedFlow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { - f.mu.RLock() - defer f.mu.RUnlock() - - return f.Flow.Produce(v) -} - func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { if !f.isAlive { return nil, shared.ErrDeadConnection diff --git a/tcp/listener.go b/tcp/listener.go index 5da9761..52892e0 100644 --- a/tcp/listener.go +++ b/tcp/listener.go @@ -31,7 +31,7 @@ func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier) error { f := Flow{conn: conn, isAlive: true} - log.Printf("received new connection: %v\n", f) + log.Printf("received new tcp connection: %v\n", f) p.AddConsumer(&f) p.AddProducer(&f, v) diff --git a/udp/congestion.go b/udp/congestion.go index 1d4d821..b3c593b 100644 --- a/udp/congestion.go +++ b/udp/congestion.go @@ -1,5 +1,7 @@ package udp +import "time" + type Congestion interface { Sequence() uint32 ReceivedPacket(seq uint32) @@ -9,4 +11,6 @@ type Congestion interface { ReceivedNack(uint32) NextNack() uint32 + + AwaitEarlyUpdate(keepalive time.Duration) } diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index 3676b39..1414077 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -1,20 +1,33 @@ package congestion import ( + "math" "mpbl3p/utils" + "sync/atomic" "time" ) +const RttExponentialFactor = 0.1 + type NewReno struct { - sequence chan uint32 - packetTimes map[uint32]time.Time + sequence chan uint32 + keepalive chan bool - nextAck uint32 - nextNack uint32 + outboundTimes map[uint32]time.Time + inboundTimes map[uint32]time.Time - fastStart bool - windowSize uint - rtt time.Duration + ack, lastAck uint32 + nack, lastNack uint32 + + slowStart bool + rtt float64 + windowSize int32 + windowCount int32 + inFlight int32 + + ackNotifier chan struct{} + + lastSent time.Time acksToSend utils.Uint32Heap } @@ -22,8 +35,14 @@ type NewReno struct { func NewNewReno() *NewReno { c := NewReno{ sequence: make(chan uint32), - packetTimes: make(map[uint32]time.Time), - windowSize: 1, + ackNotifier: make(chan struct{}), + + outboundTimes: make(map[uint32]time.Time), + inboundTimes: make(map[uint32]time.Time), + + windowSize: 1, + rtt: (1 * time.Millisecond).Seconds(), + slowStart: true, } go func() { @@ -45,41 +64,113 @@ func NewNewReno() *NewReno { func (c *NewReno) ReceivedAck(ack uint32) { // RTT // Update using an exponential average + rtt := time.Now().Sub(c.outboundTimes[ack]).Seconds() + delete(c.outboundTimes, ack) + c.rtt = c.rtt*(1-RttExponentialFactor) + rtt*RttExponentialFactor + // Free Window + atomic.AddInt32(&c.inFlight, -1) + select { + case c.ackNotifier <- struct{}{}: + default: + } // GROW // CASE: exponential. increase window size by one per ack // CASE: standard. increase window size by one per window of acks + if c.slowStart { + atomic.AddInt32(&c.windowSize, 1) + } else { + c.windowCount++ + if c.windowCount == c.windowSize { + c.windowCount = 0 + atomic.AddInt32(&c.windowSize, 1) + } + } } // It is assumed that ReceivedNack will only be called by one thread func (c *NewReno) ReceivedNack(nack uint32) { - // Back off + // End slow start + c.slowStart = false + if s := c.windowSize; s > 1 { + atomic.StoreInt32(&c.windowSize, s/2) + } } func (c *NewReno) ReceivedPacket(seq uint32) { + c.inboundTimes[seq] = time.Now() c.acksToSend.Insert(seq) - ack, err := c.acksToSend.Extract() - if err != nil { - panic(err) + findAck := func(start uint32) uint32 { + ack := start + for len(c.acksToSend) > 0 { + if a, _ := c.acksToSend.Peek(); a == ack+1 { + ack, _ = c.acksToSend.Extract() + } else { + break + } + } + return ack } - for a, _ := c.acksToSend.Peek(); a == ack+1; { - ack, _ = c.acksToSend.Extract() + ack := findAck(c.ack) + if ack == c.ack { + // check if there is a nack to send + // decide this based on whether there have been 3RTTs between the offset packet + if len(c.acksToSend) > 0 { + nextAck, _ := c.acksToSend.Peek() + if time.Now().Sub(c.inboundTimes[nextAck]).Seconds() > c.rtt*3 { + atomic.StoreUint32(&c.nack, nextAck-1) + ack, _ = c.acksToSend.Extract() + ack = findAck(ack) + } + } } + + atomic.StoreUint32(&c.ack, ack) } func (c *NewReno) Sequence() uint32 { + for c.inFlight >= c.windowSize { + <-c.ackNotifier + } + atomic.AddInt32(&c.inFlight, 1) + s := <-c.sequence - c.packetTimes[s] = time.Now() + + n := time.Now() + c.lastSent = n + c.outboundTimes[s] = n + return s } func (c *NewReno) NextAck() uint32 { - return c.nextAck + a := c.ack + c.lastAck = a + return a } func (c *NewReno) NextNack() uint32 { - return c.nextNack + n := c.nack + c.lastNack = n + return n +} + +func (c *NewReno) AwaitEarlyUpdate(keepalive time.Duration) { + for { + rtt := time.Duration(math.Round(c.rtt * float64(time.Second))) + time.Sleep(rtt) + + // CASE 1: > 5 waiting ACKs or any waiting NACKs and no message sent in the last RTT + if (c.lastAck-c.ack) > 5 || (c.lastNack != c.nack) && time.Now().After(c.lastSent.Add(rtt)) { + return + } + + // CASE 3: No message sent within the keepalive time + if keepalive != 0 && time.Now().After(c.lastSent.Add(keepalive)) { + return + } + } } diff --git a/udp/flow.go b/udp/flow.go index 359a46c..4009876 100644 --- a/udp/flow.go +++ b/udp/flow.go @@ -1,13 +1,20 @@ package udp import ( + "errors" + "fmt" + "log" + "mpbl3p/config" "mpbl3p/proxy" + "mpbl3p/shared" "net" "sync" + "time" ) type PacketWriter interface { WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) + LocalAddr() net.Addr } type PacketConn interface { @@ -16,13 +23,20 @@ type PacketConn interface { } type InitiatedFlow struct { - Local string + Local string Remote string + g proxy.MacGenerator + keepalive time.Duration + mu sync.RWMutex Flow } +func (f *InitiatedFlow) String() string { + return fmt.Sprintf("UdpOutbound{%v -> %v}", f.Local, f.Remote) +} + type Flow struct { writer PacketWriter raddr net.UDPAddr @@ -33,34 +47,107 @@ type Flow struct { inboundDatagrams chan []byte } -func newOutboundFlow(c Congestion) *Flow { - return &Flow{ - congestion: c, - inboundDatagrams: make(chan []byte), - } +func (f Flow) String() string { + return fmt.Sprintf("UdpInbound{%v -> %v}", f.raddr, f.writer.LocalAddr()) } -func newInboundFlow(c Congestion) *Flow { - return &Flow{ - congestion: c, - inboundDatagrams: make(chan []byte), - } -} - -func InitiateFlow(local, remote string) (*InitiatedFlow, error) { +func InitiateFlow( + local, remote string, + g proxy.MacGenerator, + c Congestion, + keepalive time.Duration, +) (*InitiatedFlow, error) { f := InitiatedFlow{ - Local: local, - Remote: remote, + Local: local, + Remote: remote, + Flow: newFlow(c), + g: g, + keepalive: keepalive, } return &f, nil } +func newFlow(c Congestion) Flow { + return Flow{ + inboundDatagrams: make(chan []byte), + congestion: c, + } +} + +func (f *InitiatedFlow) Reconnect() error { + f.mu.Lock() + defer f.mu.Unlock() + + if f.isAlive { + return nil + } + + localAddr, err := net.ResolveUDPAddr("udp", f.Local) + if err != nil { + return err + } + + remoteAddr, err := net.ResolveUDPAddr("udp", f.Remote) + if err != nil { + return err + } + + conn, err := net.DialUDP("udp", localAddr, remoteAddr) + if err != nil { + return err + } + + f.writer = conn + f.isAlive = true + + go func() { + for { + buf := make([]byte, 6000) + n, _, err := conn.ReadFromUDP(buf) + if err != nil { + panic(err) + } + + f.inboundDatagrams <- buf[:n] + } + }() + + go func() { + var err error + for !errors.Is(err, shared.ErrDeadConnection) { + f.congestion.AwaitEarlyUpdate(f.keepalive) + err = f.Consume(proxy.NewSimplePacket(nil), f.g) + } + }() + + return nil +} + +func (f *InitiatedFlow) Consume(p proxy.Packet, g proxy.MacGenerator) error { + f.mu.RLock() + defer f.mu.RUnlock() + + return f.Flow.Consume(p, g) +} + +func (f *InitiatedFlow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { + f.mu.RLock() + defer f.mu.RUnlock() + + return f.Flow.Produce(v) +} + func (f *Flow) IsAlive() bool { return f.isAlive } func (f *Flow) Consume(pp proxy.Packet, g proxy.MacGenerator) error { + if !f.isAlive { + return shared.ErrDeadConnection + } + + // Sequence is the congestion controllers opportunity to block p := Packet{ seq: f.congestion.Sequence(), data: pp, @@ -77,6 +164,10 @@ func (f *Flow) Consume(pp proxy.Packet, g proxy.MacGenerator) error { } func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { + if !f.isAlive { + return nil, shared.ErrDeadConnection + } + b, err := proxy.StripMac(<-f.inboundDatagrams, v) if err != nil { return nil, err @@ -103,5 +194,30 @@ func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { } func (f *Flow) handleDatagram(p []byte) { - f.inboundDatagrams <- p + // TODO: Fix with security + // 12 bytes for header + the MAC + a timestamp + if len(p) == 12+(config.UselessMac{}).CodeLength()+8 { + b, err := proxy.StripMac(<-f.inboundDatagrams, config.UselessMac{}) + if err != nil { + log.Println(err) + return + } + + p, err := UnmarshalPacket(b) + if err != nil { + log.Println(err) + return + } + + // TODO: Decide whether to use this line. It means an ACK loop will start, but also is a packet loss. + f.congestion.ReceivedPacket(p.seq) + if p.ack != 0 { + f.congestion.ReceivedAck(p.ack) + } + if p.nack != 0 { + f.congestion.ReceivedNack(p.nack) + } + } else { + f.inboundDatagrams <- p + } } diff --git a/udp/listener.go b/udp/listener.go index 2ad59ff..1b7119e 100644 --- a/udp/listener.go +++ b/udp/listener.go @@ -1,8 +1,10 @@ package udp import ( + "errors" "log" "mpbl3p/proxy" + "mpbl3p/shared" "net" ) @@ -25,7 +27,7 @@ func fromUdpAddress(address net.UDPAddr) ComparableUdpAddress { } } -func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier) error { +func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier, g proxy.MacGenerator, c Congestion) error { laddr, err := net.ResolveUDPAddr("udp", local) if err != nil { return err @@ -58,15 +60,23 @@ func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier) error { continue } - f := Flow{ - writer: pconn, - raddr: *addr, - isAlive: true, - } + f := newFlow(c) + + f.writer = pconn + f.raddr = *addr + f.isAlive = true + + go func() { + var err error + for !errors.Is(err, shared.ErrDeadConnection) { + f.congestion.AwaitEarlyUpdate(0) + err = f.Consume(proxy.NewSimplePacket(nil), g) + } + }() receivedConnections[raddr] = &f - log.Printf("received new connection: %v\n", f) + log.Printf("received new udp connection: %v\n", f) p.AddConsumer(&f) p.AddProducer(&f, v) diff --git a/utils/heap.go b/utils/heap.go index ccadc0f..96f2c34 100644 --- a/utils/heap.go +++ b/utils/heap.go @@ -5,23 +5,21 @@ import "errors" var ErrorEmptyHeap = errors.New("attempted to extract from empty heap") // A MinHeap for Uint64 -type Uint32Heap struct { - data []uint32 -} +type Uint32Heap []uint32 func (h *Uint32Heap) swap(x, y int) { - h.data[x] = h.data[x] ^ h.data[y] - h.data[y] = h.data[y] ^ h.data[x] - h.data[x] = h.data[x] ^ h.data[y] + (*h)[x] = (*h)[x] ^ (*h)[y] + (*h)[y] = (*h)[y] ^ (*h)[x] + (*h)[x] = (*h)[x] ^ (*h)[y] } func (h *Uint32Heap) Insert(new uint32) uint32 { - h.data = append(h.data, new) + *h = append(*h, new) - child := len(h.data) - 1 + child := len(*h) - 1 for child != 0 { parent := (child - 1) / 2 - if h.data[parent] > h.data[child] { + if (*h)[parent] > (*h)[child] { h.swap(parent, child) } else { break @@ -29,24 +27,24 @@ func (h *Uint32Heap) Insert(new uint32) uint32 { child = parent } - return h.data[0] + return (*h)[0] } func (h *Uint32Heap) Extract() (uint32, error) { - if len(h.data) == 0 { + if len(*h) == 0 { return 0, ErrorEmptyHeap } - min := h.data[0] + min := (*h)[0] - h.data[0] = h.data[len(h.data)-1] - h.data = h.data[:len(h.data)-1] + (*h)[0] = (*h)[len(*h)-1] + *h = (*h)[:len(*h)-1] parent := 0 for { left, right := parent*2+1, parent*2+2 - if (left < len(h.data) && h.data[parent] > h.data[left]) || (right < len(h.data) && h.data[parent] > h.data[right]) { - if right < len(h.data) && h.data[left] > h.data[right] { + if (left < len(*h) && (*h)[parent] > (*h)[left]) || (right < len(*h) && (*h)[parent] > (*h)[right]) { + if right < len(*h) && (*h)[left] > (*h)[right] { h.swap(parent, right) parent = right } else { @@ -60,8 +58,8 @@ func (h *Uint32Heap) Extract() (uint32, error) { } func (h *Uint32Heap) Peek() (uint32, error) { - if len(h.data) == 0 { + if len(*h) == 0 { return 0, ErrorEmptyHeap } - return h.data[0], nil + return (*h)[0], nil } diff --git a/utils/heap_test.go b/utils/heap_test.go index d0393f0..61a1bbd 100644 --- a/utils/heap_test.go +++ b/utils/heap_test.go @@ -8,8 +8,8 @@ import ( "time" ) -func SlowHeapSort(in []uint64) []uint64 { - out := make([]uint64, len(in)) +func SlowHeapSort(in []uint32) []uint32 { + out := make([]uint32, len(in)) var heap Uint32Heap @@ -27,16 +27,16 @@ func SlowHeapSort(in []uint64) []uint64 { return out } -func TestUint64Heap(t *testing.T) { +func TestUint32Heap(t *testing.T) { t.Run("EquivalentToMerge", func(t *testing.T) { const ArrayLength = 50 - sortedArray := make([]uint64, ArrayLength) - array := make([]uint64, ArrayLength) + sortedArray := make([]uint32, ArrayLength) + array := make([]uint32, ArrayLength) for i := range array { - sortedArray[i] = uint64(i) - array[i] = uint64(i) + sortedArray[i] = uint32(i) + array[i] = uint32(i) } rand.Seed(time.Now().Unix()) -- 2.47.0 From 96b57f65cadb5fe52d9e8a2a8265b9a88c4c1f96 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Thu, 26 Nov 2020 22:15:22 +0000 Subject: [PATCH 007/121] fixed types --- config/builder.go | 14 +++++++++++++- udp/flow.go | 13 ++++++++----- udp/listener.go | 2 +- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/config/builder.go b/config/builder.go index 2fa7f5a..e8784d8 100644 --- a/config/builder.go +++ b/config/builder.go @@ -6,6 +6,8 @@ import ( "mpbl3p/tcp" "mpbl3p/tun" "mpbl3p/udp" + "mpbl3p/udp/congestion" + "time" ) // TODO: Delete this code as soon as an alternative is available @@ -87,6 +89,10 @@ func buildUdp(p *proxy.Proxy, peer Peer) error { f, err := udp.InitiateFlow( fmt.Sprintf("%s:", peer.LocalHost), fmt.Sprintf("%s:%d", peer.RemoteHost, peer.RemotePort), + UselessMac{}, + UselessMac{}, + congestion.NewNewReno(), + time.Duration(peer.KeepAlive)*time.Second, ) if err != nil { @@ -99,7 +105,13 @@ func buildUdp(p *proxy.Proxy, peer Peer) error { return nil } - err := udp.NewListener(p, fmt.Sprintf("%s:%d", peer.LocalHost, peer.LocalPort), UselessMac{}) + err := udp.NewListener( + p, + fmt.Sprintf("%s:%d", peer.LocalHost, peer.LocalPort), + UselessMac{}, + UselessMac{}, + congestion.NewNewReno(), + ) if err != nil { return err } diff --git a/udp/flow.go b/udp/flow.go index 4009876..c8e6222 100644 --- a/udp/flow.go +++ b/udp/flow.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "log" - "mpbl3p/config" "mpbl3p/proxy" "mpbl3p/shared" "net" @@ -44,6 +43,8 @@ type Flow struct { isAlive bool congestion Congestion + v proxy.MacVerifier + inboundDatagrams chan []byte } @@ -53,6 +54,7 @@ func (f Flow) String() string { func InitiateFlow( local, remote string, + v proxy.MacVerifier, g proxy.MacGenerator, c Congestion, keepalive time.Duration, @@ -60,7 +62,7 @@ func InitiateFlow( f := InitiatedFlow{ Local: local, Remote: remote, - Flow: newFlow(c), + Flow: newFlow(c, v), g: g, keepalive: keepalive, } @@ -68,10 +70,11 @@ func InitiateFlow( return &f, nil } -func newFlow(c Congestion) Flow { +func newFlow(c Congestion, v proxy.MacVerifier) Flow { return Flow{ inboundDatagrams: make(chan []byte), congestion: c, + v: v, } } @@ -196,8 +199,8 @@ func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { func (f *Flow) handleDatagram(p []byte) { // TODO: Fix with security // 12 bytes for header + the MAC + a timestamp - if len(p) == 12+(config.UselessMac{}).CodeLength()+8 { - b, err := proxy.StripMac(<-f.inboundDatagrams, config.UselessMac{}) + if len(p) == 12+f.v.CodeLength()+8 { + b, err := proxy.StripMac(<-f.inboundDatagrams, f.v) if err != nil { log.Println(err) return diff --git a/udp/listener.go b/udp/listener.go index 1b7119e..7edb0ee 100644 --- a/udp/listener.go +++ b/udp/listener.go @@ -60,7 +60,7 @@ func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier, g proxy.MacG continue } - f := newFlow(c) + f := newFlow(c, v) f.writer = pconn f.raddr = *addr -- 2.47.0 From cc9d036f43b2a25353798f5b76df9bc304ada122 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Thu, 26 Nov 2020 22:26:01 +0000 Subject: [PATCH 008/121] added none congestion control --- config/builder.go | 14 ++++++++++++-- config/config.go | 2 ++ udp/congestion/none.go | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 udp/congestion/none.go diff --git a/config/builder.go b/config/builder.go index e8784d8..260905c 100644 --- a/config/builder.go +++ b/config/builder.go @@ -85,13 +85,23 @@ func buildTcp(p *proxy.Proxy, peer Peer) error { } func buildUdp(p *proxy.Proxy, peer Peer) error { + var c udp.Congestion + switch peer.Congestion { + case "None": + c = congestion.NewNone() + default: + fallthrough + case "NewReno": + c = congestion.NewNewReno() + } + if peer.RemoteHost != "" { f, err := udp.InitiateFlow( fmt.Sprintf("%s:", peer.LocalHost), fmt.Sprintf("%s:%d", peer.RemoteHost, peer.RemotePort), UselessMac{}, UselessMac{}, - congestion.NewNewReno(), + c, time.Duration(peer.KeepAlive)*time.Second, ) @@ -110,7 +120,7 @@ func buildUdp(p *proxy.Proxy, peer Peer) error { fmt.Sprintf("%s:%d", peer.LocalHost, peer.LocalPort), UselessMac{}, UselessMac{}, - congestion.NewNewReno(), + c, ) if err != nil { return err diff --git a/config/config.go b/config/config.go index e5b6d18..73b086a 100644 --- a/config/config.go +++ b/config/config.go @@ -24,6 +24,8 @@ type Peer struct { RemoteHost string `validate:"required_with=RemotePort,omitempty,fqdn|ip"` RemotePort uint `validate:"required_with=RemoteHost,omitempty,max=65535"` + Congestion string `validate:"oneof=NewReno None"` + KeepAlive uint Timeout uint RetryWait uint diff --git a/udp/congestion/none.go b/udp/congestion/none.go new file mode 100644 index 0000000..fe2bcee --- /dev/null +++ b/udp/congestion/none.go @@ -0,0 +1,40 @@ +package congestion + +import "time" + +type None struct { + sequence chan uint32 +} + +func NewNone() *None { + c := None{ + sequence: make(chan uint32), + } + + go func() { + var s uint32 + for { + if s == 0 { + continue + } + + c.sequence <- s + s++ + } + }() + + return &c +} + +func (c *None) Sequence() uint32 { + return <-c.sequence +} + +func (c *None) ReceivedPacket(seq uint32) {} +func (c *None) ReceivedAck(uint32) {} +func (c *None) NextAck() uint32 { return 0 } +func (c *None) ReceivedNack(uint32) {} +func (c *None) NextNack() uint32 { return 0 } +func (c *None) AwaitEarlyUpdate(keepalive time.Duration) { + select {} +} -- 2.47.0 From 0f88a97075acd681070aa61652cfce5770a0d3bb Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Thu, 26 Nov 2020 22:30:03 +0000 Subject: [PATCH 009/121] missing increment --- udp/congestion/newreno.go | 1 + udp/congestion/none.go | 1 + 2 files changed, 2 insertions(+) diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index 1414077..ab69477 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -49,6 +49,7 @@ func NewNewReno() *NewReno { var s uint32 for { if s == 0 { + s++ continue } diff --git a/udp/congestion/none.go b/udp/congestion/none.go index fe2bcee..5cf6cd4 100644 --- a/udp/congestion/none.go +++ b/udp/congestion/none.go @@ -15,6 +15,7 @@ func NewNone() *None { var s uint32 for { if s == 0 { + s++ continue } -- 2.47.0 From 76d49da0628c964d1fceef10eea42bb48f48175b Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Thu, 26 Nov 2020 22:39:07 +0000 Subject: [PATCH 010/121] fixed tun refactor --- proxy/packet.go | 5 +++++ tun/tun.go | 2 +- udp/flow.go | 4 ++-- udp/packet.go | 4 ++++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/proxy/packet.go b/proxy/packet.go index f56a84a..260b9f9 100644 --- a/proxy/packet.go +++ b/proxy/packet.go @@ -7,6 +7,7 @@ import ( type Packet interface { Marshal() []byte + Contents() []byte } type SimplePacket struct { @@ -44,6 +45,10 @@ func (p SimplePacket) Marshal() []byte { return append(p.Data, footer...) } +func (p SimplePacket) Contents() []byte { + return p.Data +} + func AppendMac(b []byte, g MacGenerator) []byte { mac := g.Generate(b) b = append(b, mac...) diff --git a/tun/tun.go b/tun/tun.go index 837b496..1b55eb1 100644 --- a/tun/tun.go +++ b/tun/tun.go @@ -79,7 +79,7 @@ func (t *SourceSink) Sink(packet proxy.Packet) error { t.upMu.Unlock() } - _, err := t.tun.Write(packet.Marshal()) + _, err := t.tun.Write(packet.Contents()) if err != nil { switch err.(type) { case *os.PathError: diff --git a/udp/flow.go b/udp/flow.go index c8e6222..2a19f11 100644 --- a/udp/flow.go +++ b/udp/flow.go @@ -12,7 +12,7 @@ import ( ) type PacketWriter interface { - WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) + Write(b []byte) (int, error) LocalAddr() net.Addr } @@ -162,7 +162,7 @@ func (f *Flow) Consume(pp proxy.Packet, g proxy.MacGenerator) error { b := p.Marshal() b = proxy.AppendMac(b, g) - _, err := f.writer.WriteToUDP(b, &f.raddr) + _, err := f.writer.Write(b) return err } diff --git a/udp/packet.go b/udp/packet.go index b7a4f34..8ab0091 100644 --- a/udp/packet.go +++ b/udp/packet.go @@ -35,3 +35,7 @@ func (p Packet) Marshal() []byte { return append(header, data...) } + +func (p Packet) Contents() []byte { + return p.data.Contents() +} -- 2.47.0 From 47842900db9ce20a2557f4404b38d1a761ad5675 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Thu, 26 Nov 2020 22:46:37 +0000 Subject: [PATCH 011/121] None CC works --- udp/flow.go | 12 +++++++++--- udp/listener.go | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/udp/flow.go b/udp/flow.go index 2a19f11..342f1da 100644 --- a/udp/flow.go +++ b/udp/flow.go @@ -13,6 +13,7 @@ import ( type PacketWriter interface { Write(b []byte) (int, error) + WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) LocalAddr() net.Addr } @@ -38,7 +39,7 @@ func (f *InitiatedFlow) String() string { type Flow struct { writer PacketWriter - raddr net.UDPAddr + raddr *net.UDPAddr isAlive bool congestion Congestion @@ -162,8 +163,13 @@ func (f *Flow) Consume(pp proxy.Packet, g proxy.MacGenerator) error { b := p.Marshal() b = proxy.AppendMac(b, g) - _, err := f.writer.Write(b) - return err + if f.raddr == nil { + _, err := f.writer.Write(b) + return err + } else { + _, err := f.writer.WriteToUDP(b, f.raddr) + return err + } } func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { diff --git a/udp/listener.go b/udp/listener.go index 7edb0ee..3abc810 100644 --- a/udp/listener.go +++ b/udp/listener.go @@ -63,7 +63,7 @@ func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier, g proxy.MacG f := newFlow(c, v) f.writer = pconn - f.raddr = *addr + f.raddr = addr f.isAlive = true go func() { -- 2.47.0 From 5e0dc6969d027ddccf6b578cfc83f61495205d92 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Fri, 27 Nov 2020 17:31:32 +0000 Subject: [PATCH 012/121] udp expansions --- udp/congestion.go | 4 +- udp/congestion/newreno.go | 56 ++++++++++-- udp/congestion/none.go | 6 +- udp/flow.go | 175 ++++++++++++++++++++++-------------- udp/listener.go | 29 +++--- udp/wireshark_dissector.lua | 39 ++++++++ 6 files changed, 221 insertions(+), 88 deletions(-) create mode 100644 udp/wireshark_dissector.lua diff --git a/udp/congestion.go b/udp/congestion.go index b3c593b..826910e 100644 --- a/udp/congestion.go +++ b/udp/congestion.go @@ -12,5 +12,7 @@ type Congestion interface { ReceivedNack(uint32) NextNack() uint32 - AwaitEarlyUpdate(keepalive time.Duration) + AwaitEarlyUpdate(keepalive time.Duration) uint32 + AwaitAck(timeout time.Duration) bool + Reset() } diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index ab69477..cc12eeb 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -1,8 +1,11 @@ package congestion import ( + "fmt" + "log" "math" "mpbl3p/utils" + "sync" "sync/atomic" "time" ) @@ -28,8 +31,15 @@ type NewReno struct { ackNotifier chan struct{} lastSent time.Time + hasAcked bool acksToSend utils.Uint32Heap + + mu sync.Mutex +} + +func (c *NewReno) String() string { + return fmt.Sprintf("{NewReno %t %d %d %d %d}", c.slowStart, c.windowSize, c.inFlight, c.lastAck, c.lastNack) } func NewNewReno() *NewReno { @@ -40,7 +50,7 @@ func NewNewReno() *NewReno { outboundTimes: make(map[uint32]time.Time), inboundTimes: make(map[uint32]time.Time), - windowSize: 1, + windowSize: 8, rtt: (1 * time.Millisecond).Seconds(), slowStart: true, } @@ -61,8 +71,20 @@ func NewNewReno() *NewReno { return &c } +func (c *NewReno) Reset() { + c.outboundTimes = make(map[uint32]time.Time) + c.inboundTimes = make(map[uint32]time.Time) + c.windowSize = 8 + c.rtt = (1 * time.Millisecond).Seconds() + c.slowStart = true + c.hasAcked = false +} + // It is assumed that ReceivedAck will only be called by one thread func (c *NewReno) ReceivedAck(ack uint32) { + log.Printf("ack received for %d", ack) + c.hasAcked = true + // RTT // Update using an exponential average rtt := time.Now().Sub(c.outboundTimes[ack]).Seconds() @@ -92,6 +114,8 @@ func (c *NewReno) ReceivedAck(ack uint32) { // It is assumed that ReceivedNack will only be called by one thread func (c *NewReno) ReceivedNack(nack uint32) { + log.Printf("nack received for %d", nack) + // End slow start c.slowStart = false if s := c.windowSize; s > 1 { @@ -100,8 +124,20 @@ func (c *NewReno) ReceivedNack(nack uint32) { } func (c *NewReno) ReceivedPacket(seq uint32) { + log.Printf("seq received for %d", seq) + c.inboundTimes[seq] = time.Now() + + c.mu.Lock() c.acksToSend.Insert(seq) + c.mu.Unlock() + + c.updateAckNack() +} + +func (c *NewReno) updateAckNack() { + c.mu.Lock() + defer c.mu.Unlock() findAck := func(start uint32) uint32 { ack := start @@ -159,19 +195,29 @@ func (c *NewReno) NextNack() uint32 { return n } -func (c *NewReno) AwaitEarlyUpdate(keepalive time.Duration) { +func (c *NewReno) AwaitEarlyUpdate(keepalive time.Duration) uint32 { for { rtt := time.Duration(math.Round(c.rtt * float64(time.Second))) time.Sleep(rtt) + c.updateAckNack() + // CASE 1: > 5 waiting ACKs or any waiting NACKs and no message sent in the last RTT - if (c.lastAck-c.ack) > 5 || (c.lastNack != c.nack) && time.Now().After(c.lastSent.Add(rtt)) { - return + if ((c.lastAck!=c.ack) || (c.lastNack != c.nack)) && time.Now().After(c.lastSent.Add(rtt)) { + return 0 } // CASE 3: No message sent within the keepalive time if keepalive != 0 && time.Now().After(c.lastSent.Add(keepalive)) { - return + return c.Sequence() } } } + +func (c *NewReno) AwaitAck(timeout time.Duration) bool { + if c.hasAcked { + return true + } + time.Sleep(timeout) + return c.hasAcked +} diff --git a/udp/congestion/none.go b/udp/congestion/none.go index 5cf6cd4..1089dc2 100644 --- a/udp/congestion/none.go +++ b/udp/congestion/none.go @@ -31,11 +31,13 @@ func (c *None) Sequence() uint32 { return <-c.sequence } -func (c *None) ReceivedPacket(seq uint32) {} +func (c *None) ReceivedPacket(uint32) {} func (c *None) ReceivedAck(uint32) {} func (c *None) NextAck() uint32 { return 0 } func (c *None) ReceivedNack(uint32) {} func (c *None) NextNack() uint32 { return 0 } -func (c *None) AwaitEarlyUpdate(keepalive time.Duration) { +func (c *None) AwaitEarlyUpdate(time.Duration) uint32 { select {} } +func (c *None) AwaitAck(time.Duration) bool { return true } +func (c *None) Reset() {} diff --git a/udp/flow.go b/udp/flow.go index 342f1da..cebd8ad 100644 --- a/udp/flow.go +++ b/udp/flow.go @@ -42,6 +42,7 @@ type Flow struct { raddr *net.UDPAddr isAlive bool + startup bool congestion Congestion v proxy.MacVerifier @@ -81,7 +82,6 @@ func newFlow(c Congestion, v proxy.MacVerifier) Flow { func (f *InitiatedFlow) Reconnect() error { f.mu.Lock() - defer f.mu.Unlock() if f.isAlive { return nil @@ -103,28 +103,55 @@ func (f *InitiatedFlow) Reconnect() error { } f.writer = conn - f.isAlive = true + f.startup = true + // prod the connection once a second until we get an ack, then consider it alive go func() { - for { - buf := make([]byte, 6000) - n, _, err := conn.ReadFromUDP(buf) - if err != nil { - panic(err) + seq := f.congestion.Sequence() + + defer f.mu.Unlock() + for !f.isAlive { + p := Packet{ + ack: 0, + nack: 0, + seq: seq, + data: proxy.NewSimplePacket(nil), } - f.inboundDatagrams <- buf[:n] + _ = f.sendPacket(p, f.g) + + if f.congestion.AwaitAck(1 * time.Second) { + f.isAlive = true + f.startup = false + } } }() go func() { - var err error - for !errors.Is(err, shared.ErrDeadConnection) { - f.congestion.AwaitEarlyUpdate(f.keepalive) - err = f.Consume(proxy.NewSimplePacket(nil), f.g) + for f.startup || f.isAlive { + func() { + if f.isAlive { + f.mu.RLock() + defer f.mu.RUnlock() + } + + buf := make([]byte, 6000) + n, _, err := conn.ReadFromUDP(buf) + if err != nil { + log.Println(err) + time.Sleep(1) + } else { + f.handleDatagram(buf[:n]) + } + }() } }() + go func() { + _, _ = f.produceInternal(f.v, false) + }() + go f.earlyUpdateLoop(f.g, f.keepalive) + return nil } @@ -151,15 +178,75 @@ func (f *Flow) Consume(pp proxy.Packet, g proxy.MacGenerator) error { return shared.ErrDeadConnection } + log.Println(f.congestion) + // Sequence is the congestion controllers opportunity to block + log.Println("awaiting sequence") p := Packet{ seq: f.congestion.Sequence(), data: pp, } + log.Println("received sequence") + // Choose up to date ACK/NACK even after blocking p.ack = f.congestion.NextAck() p.nack = f.congestion.NextNack() + return f.sendPacket(p, g) +} + +func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { + if !f.isAlive { + return nil, shared.ErrDeadConnection + } + + return f.produceInternal(v, true) +} + +func (f *Flow) produceInternal(v proxy.MacVerifier, mustReturn bool) (proxy.Packet, error) { + for once := true; mustReturn || once; once = false { + log.Println(f.congestion) + + b, err := proxy.StripMac(<-f.inboundDatagrams, v) + if err != nil { + return nil, err + } + + p, err := UnmarshalPacket(b) + if err != nil { + return nil, err + } + + // schedule an ack for this sequence number + if p.seq != 0 { + f.congestion.ReceivedPacket(p.seq) + } + // adjust our sending congestion control based on their acks + if p.ack != 0 { + f.congestion.ReceivedAck(p.ack) + } + // adjust our sending congestion control based on their nacks + if p.nack != 0 { + f.congestion.ReceivedNack(p.nack) + } + + // 12 bytes for header + the MAC + a timestamp + if len(b) == 12+f.v.CodeLength()+8 { + log.Println("handled keepalive/ack only packet") + continue + } + + return p, nil + } + + return nil, nil +} + +func (f *Flow) handleDatagram(p []byte) { + f.inboundDatagrams <- p +} + +func (f *Flow) sendPacket(p Packet, g proxy.MacGenerator) error { b := p.Marshal() b = proxy.AppendMac(b, g) @@ -172,61 +259,17 @@ func (f *Flow) Consume(pp proxy.Packet, g proxy.MacGenerator) error { } } -func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { - if !f.isAlive { - return nil, shared.ErrDeadConnection - } - - b, err := proxy.StripMac(<-f.inboundDatagrams, v) - if err != nil { - return nil, err - } - - p, err := UnmarshalPacket(b) - if err != nil { - return nil, err - } - - // schedule an ack for this sequence number - f.congestion.ReceivedPacket(p.seq) - - // adjust our sending congestion control based on their acks - if p.ack != 0 { - f.congestion.ReceivedAck(p.ack) - } - // adjust our sending congestion control based on their nacks - if p.nack != 0 { - f.congestion.ReceivedNack(p.nack) - } - - return p, nil -} - -func (f *Flow) handleDatagram(p []byte) { - // TODO: Fix with security - // 12 bytes for header + the MAC + a timestamp - if len(p) == 12+f.v.CodeLength()+8 { - b, err := proxy.StripMac(<-f.inboundDatagrams, f.v) - if err != nil { - log.Println(err) - return +func (f *Flow) earlyUpdateLoop(g proxy.MacGenerator, keepalive time.Duration) { + var err error + for !errors.Is(err, shared.ErrDeadConnection) { + seq := f.congestion.AwaitEarlyUpdate(keepalive) + p := Packet{ + ack: f.congestion.NextAck(), + nack: f.congestion.NextNack(), + seq: seq, + data: proxy.NewSimplePacket(nil), } - p, err := UnmarshalPacket(b) - if err != nil { - log.Println(err) - return - } - - // TODO: Decide whether to use this line. It means an ACK loop will start, but also is a packet loss. - f.congestion.ReceivedPacket(p.seq) - if p.ack != 0 { - f.congestion.ReceivedAck(p.ack) - } - if p.nack != 0 { - f.congestion.ReceivedNack(p.nack) - } - } else { - f.inboundDatagrams <- p + _ = f.sendPacket(p, g) } } diff --git a/udp/listener.go b/udp/listener.go index 3abc810..b93aea7 100644 --- a/udp/listener.go +++ b/udp/listener.go @@ -1,10 +1,8 @@ package udp import ( - "errors" "log" "mpbl3p/proxy" - "mpbl3p/shared" "net" ) @@ -47,16 +45,21 @@ func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier, g proxy.MacG go func() { for { - buf := make([]byte, 1500) + buf := make([]byte, 6000) - _, addr, err := pconn.ReadFromUDP(buf) + log.Println("listening...") + n, addr, err := pconn.ReadFromUDP(buf) if err != nil { panic(err) } + log.Println("listened") raddr := fromUdpAddress(*addr) if f, exists := receivedConnections[raddr]; exists { - f.handleDatagram(buf) + log.Println("existing flow") + log.Println("handling...") + f.handleDatagram(buf[:n]) + log.Println("handled") continue } @@ -66,20 +69,18 @@ func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier, g proxy.MacG f.raddr = addr f.isAlive = true - go func() { - var err error - for !errors.Is(err, shared.ErrDeadConnection) { - f.congestion.AwaitEarlyUpdate(0) - err = f.Consume(proxy.NewSimplePacket(nil), g) - } - }() + log.Printf("received new udp connection: %v\n", f) + + go f.earlyUpdateLoop(g, 0) receivedConnections[raddr] = &f - log.Printf("received new udp connection: %v\n", f) - p.AddConsumer(&f) p.AddProducer(&f, v) + + log.Println("handling...") + f.handleDatagram(buf[:n]) + log.Println("handled") } }() diff --git a/udp/wireshark_dissector.lua b/udp/wireshark_dissector.lua new file mode 100644 index 0000000..f889e03 --- /dev/null +++ b/udp/wireshark_dissector.lua @@ -0,0 +1,39 @@ +local ip_dissector = Dissector.get("ip") + +mpbl3p_udp = Proto("mpbl3p_udp", "Multi Path Proxy Custom UDP") + +ack_F = ProtoField.uint32("mpbl3p_udp.ack", "Acknowledgement") +nack_F = ProtoField.uint32("mpbl3p_udp.nack", "Negative Acknowledgement") +seq_F = ProtoField.uint32("mpbl3p_udp.seq", "Sequence Number") +time_F = ProtoField.absolute_time("mpbl3p_udp.time", "Timestamp") +proxied_F = ProtoField.bytes("mpbl3p_udp.data", "Proxied Data") + +mpbl3p_udp.fields = { ack_F, nack_F, seq_F, time_F, proxied_F } + +function mpbl3p_udp.dissector(buffer, pinfo, tree) + if buffer:len() < 20 then + return + end + + pinfo.cols.protocol = "MPBL3P_UDP" + + local ack = buffer(0, 4):le_uint() + local nack = buffer(4, 4):le_uint() + local seq = buffer(8, 4):le_uint() + + local unix_time = buffer(buffer:len() - 8, 8):le_uint64() + + local subtree = tree:add(mpbl3p_udp, buffer(), "Multi Path Proxy Header, SEQ: " .. seq .. " ACK: " .. ack .. " NACK: " .. nack) + + subtree:add(ack_F, ack) + subtree:add(nack_F, nack) + subtree:add(seq_F, seq) + subtree:add(time_F, NSTime.new(unix_time:tonumber())) + if buffer:len() > 20 then + subtree:add(proxied_F, buffer(12, buffer:len() - 12 - 8)) + end + + --Dissector.call(buffer(12, buffer:len()-12-8), pinfo, tree) +end + +DissectorTable.get("udp.port"):add(1234, mpbl3p_udp) -- 2.47.0 From 8c74a7c1800acf7035269635e9dbed8931bdd056 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Fri, 27 Nov 2020 18:15:10 +0000 Subject: [PATCH 013/121] formatting --- udp/congestion/newreno.go | 2 +- udp/congestion/none.go | 28 +++++++++++++++------------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index cc12eeb..5e671f0 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -203,7 +203,7 @@ func (c *NewReno) AwaitEarlyUpdate(keepalive time.Duration) uint32 { c.updateAckNack() // CASE 1: > 5 waiting ACKs or any waiting NACKs and no message sent in the last RTT - if ((c.lastAck!=c.ack) || (c.lastNack != c.nack)) && time.Now().After(c.lastSent.Add(rtt)) { + if ((c.lastAck != c.ack) || (c.lastNack != c.nack)) && time.Now().After(c.lastSent.Add(rtt)) { return 0 } diff --git a/udp/congestion/none.go b/udp/congestion/none.go index 1089dc2..3405f52 100644 --- a/udp/congestion/none.go +++ b/udp/congestion/none.go @@ -1,6 +1,9 @@ package congestion -import "time" +import ( + "fmt" + "time" +) type None struct { sequence chan uint32 @@ -27,17 +30,16 @@ func NewNone() *None { return &c } -func (c *None) Sequence() uint32 { - return <-c.sequence +func (c *None) String() string { + return fmt.Sprintf("{None}") } -func (c *None) ReceivedPacket(uint32) {} -func (c *None) ReceivedAck(uint32) {} -func (c *None) NextAck() uint32 { return 0 } -func (c *None) ReceivedNack(uint32) {} -func (c *None) NextNack() uint32 { return 0 } -func (c *None) AwaitEarlyUpdate(time.Duration) uint32 { - select {} -} -func (c *None) AwaitAck(time.Duration) bool { return true } -func (c *None) Reset() {} +func (c *None) ReceivedPacket(uint32) {} +func (c *None) ReceivedAck(uint32) {} +func (c *None) ReceivedNack(uint32) {} +func (c *None) Reset() {} +func (c *None) AwaitAck(time.Duration) bool { return true } +func (c *None) NextNack() uint32 { return 0 } +func (c *None) NextAck() uint32 { return 0 } +func (c *None) AwaitEarlyUpdate(time.Duration) uint32 { select {} } +func (c *None) Sequence() uint32 { return <-c.sequence } -- 2.47.0 From ff4ce07b0552785e282567d41646893bb9683a9e Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Fri, 27 Nov 2020 20:17:59 +0000 Subject: [PATCH 014/121] functional udp --- config/builder.go | 8 ++-- mocks/conn.go | 67 --------------------------- mocks/mac.go | 4 +- mocks/packetconn.go | 62 +++++++++++++++++++++++++ mocks/streamconn.go | 95 +++++++++++++++++++++++++++++++++++++++ proxy/packet_test.go | 77 +++++++++++++++++++++++++++---- tcp/flow_test.go | 14 +++--- udp/congestion.go | 1 - udp/congestion/newreno.go | 35 ++++++++------- udp/congestion/none.go | 1 - udp/flow.go | 62 ++++++++++++++----------- udp/flow_test.go | 85 +++++++++++++++++++++++++++++++++++ udp/listener.go | 4 +- udp/packet_test.go | 59 ++++++++++++++++++++++++ 14 files changed, 440 insertions(+), 134 deletions(-) delete mode 100644 mocks/conn.go create mode 100644 mocks/packetconn.go create mode 100644 mocks/streamconn.go create mode 100644 udp/flow_test.go create mode 100644 udp/packet_test.go diff --git a/config/builder.go b/config/builder.go index 260905c..c51e640 100644 --- a/config/builder.go +++ b/config/builder.go @@ -85,14 +85,14 @@ func buildTcp(p *proxy.Proxy, peer Peer) error { } func buildUdp(p *proxy.Proxy, peer Peer) error { - var c udp.Congestion + var c func() udp.Congestion switch peer.Congestion { case "None": - c = congestion.NewNone() + c = func() udp.Congestion {return congestion.NewNone()} default: fallthrough case "NewReno": - c = congestion.NewNewReno() + c = func() udp.Congestion {return congestion.NewNewReno()} } if peer.RemoteHost != "" { @@ -101,7 +101,7 @@ func buildUdp(p *proxy.Proxy, peer Peer) error { fmt.Sprintf("%s:%d", peer.RemoteHost, peer.RemotePort), UselessMac{}, UselessMac{}, - c, + c(), time.Duration(peer.KeepAlive)*time.Second, ) diff --git a/mocks/conn.go b/mocks/conn.go deleted file mode 100644 index 3ad384f..0000000 --- a/mocks/conn.go +++ /dev/null @@ -1,67 +0,0 @@ -package mocks - -import "time" - -type MockPerfectBiConn struct { - directionA chan byte - directionB chan byte -} - -func NewMockPerfectBiConn(bufSize int) MockPerfectBiConn { - return MockPerfectBiConn{ - directionA: make(chan byte, bufSize), - directionB: make(chan byte, bufSize), - } -} - -func (bc MockPerfectBiConn) SideA() MockPerfectConn { - return MockPerfectConn{inbound: bc.directionA, outbound: bc.directionB} -} - -func (bc MockPerfectBiConn) SideB() MockPerfectConn { - return MockPerfectConn{inbound: bc.directionB, outbound: bc.directionA} -} - -type MockPerfectConn struct { - inbound chan byte - outbound chan byte -} - -func (c MockPerfectConn) SetWriteDeadline(time.Time) error { - return nil -} - -func (c MockPerfectConn) Read(p []byte) (n int, err error) { - for i := range p { - if i == 0 { - p[i] = <-c.inbound - } else { - select { - case b := <-c.inbound: - p[i] = b - default: - return i, nil - } - } - } - return len(p), nil -} - -func (c MockPerfectConn) Write(p []byte) (n int, err error) { - for _, b := range p { - c.outbound <- b - } - return len(p), nil -} - -func (c MockPerfectConn) NonBlockingRead(p []byte) (n int, err error) { - for i := range p { - select { - case b := <-c.inbound: - p[i] = b - default: - return i, nil - } - } - return len(p), nil -} diff --git a/mocks/mac.go b/mocks/mac.go index 60d2167..5d7ab7a 100644 --- a/mocks/mac.go +++ b/mocks/mac.go @@ -1,6 +1,8 @@ package mocks -import "mpbl3p/shared" +import ( + "mpbl3p/shared" +) type AlmostUselessMac struct{} diff --git a/mocks/packetconn.go b/mocks/packetconn.go new file mode 100644 index 0000000..9360db4 --- /dev/null +++ b/mocks/packetconn.go @@ -0,0 +1,62 @@ +package mocks + +import "net" + +type MockPerfectBiPacketConn struct { + directionA chan []byte + directionB chan []byte +} + +func NewMockPerfectBiPacketConn(bufSize int) MockPerfectBiPacketConn { + return MockPerfectBiPacketConn{ + directionA: make(chan []byte, bufSize), + directionB: make(chan []byte, bufSize), + } +} + +func (bc MockPerfectBiPacketConn) SideA() MockPerfectPacketConn { + return MockPerfectPacketConn{inbound: bc.directionA, outbound: bc.directionB} +} + +func (bc MockPerfectBiPacketConn) SideB() MockPerfectPacketConn { + return MockPerfectPacketConn{inbound: bc.directionB, outbound: bc.directionA} +} + +type MockPerfectPacketConn struct { + inbound chan []byte + outbound chan []byte +} + +func (c MockPerfectPacketConn) Write(b []byte) (int, error) { + c.outbound <- b + return len(b), nil +} + +func (c MockPerfectPacketConn) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) { + c.outbound <- b + return len(b), nil +} + +func (c MockPerfectPacketConn) LocalAddr() net.Addr { + return &net.UDPAddr{ + IP: net.IPv4(127, 0, 0, 1), + Port: 1234, + } +} + +func (c MockPerfectPacketConn) ReadFromUDP(b []byte) (int, *net.UDPAddr, error) { + p := <-c.inbound + return copy(b, p), &net.UDPAddr{ + IP: net.IPv4(127, 0, 0, 1), + Port: 1234, + }, nil +} + +func (c MockPerfectPacketConn) NonBlockingRead(p []byte) (n int, err error) { + select { + case b := <-c.inbound: + return copy(p, b), nil + default: + return 0, nil + } +} diff --git a/mocks/streamconn.go b/mocks/streamconn.go new file mode 100644 index 0000000..956d2d2 --- /dev/null +++ b/mocks/streamconn.go @@ -0,0 +1,95 @@ +package mocks + +import ( + "net" + "time" +) + +type MockPerfectBiStreamConn struct { + directionA chan byte + directionB chan byte +} + +func NewMockPerfectBiStreamConn(bufSize int) MockPerfectBiStreamConn { + return MockPerfectBiStreamConn{ + directionA: make(chan byte, bufSize), + directionB: make(chan byte, bufSize), + } +} + +func (bc MockPerfectBiStreamConn) SideA() MockPerfectStreamConn { + return MockPerfectStreamConn{inbound: bc.directionA, outbound: bc.directionB} +} + +func (bc MockPerfectBiStreamConn) SideB() MockPerfectStreamConn { + return MockPerfectStreamConn{inbound: bc.directionB, outbound: bc.directionA} +} + +type MockPerfectStreamConn struct { + inbound chan byte + outbound chan byte +} + +type Conn interface { + Read(b []byte) (n int, err error) + Write(b []byte) (n int, err error) + SetWriteDeadline(time.Time) error + + // For printing + LocalAddr() net.Addr + RemoteAddr() net.Addr +} + +func (c MockPerfectStreamConn) Read(p []byte) (n int, err error) { + for i := range p { + if i == 0 { + p[i] = <-c.inbound + } else { + select { + case b := <-c.inbound: + p[i] = b + default: + return i, nil + } + } + } + return len(p), nil +} + +func (c MockPerfectStreamConn) Write(p []byte) (n int, err error) { + for _, b := range p { + c.outbound <- b + } + return len(p), nil +} + +func (c MockPerfectStreamConn) SetWriteDeadline(time.Time) error { + return nil +} + +// Only used for printing flow information +func (c MockPerfectStreamConn) LocalAddr() net.Addr { + return &net.TCPAddr{ + IP: net.IPv4(127, 0, 0, 1), + Port: 499, + } +} + +func (c MockPerfectStreamConn) RemoteAddr() net.Addr { + return &net.TCPAddr{ + IP: net.IPv4(127, 0, 0, 1), + Port: 500, + } +} + +func (c MockPerfectStreamConn) NonBlockingRead(p []byte) (n int, err error) { + for i := range p { + select { + case b := <-c.inbound: + p[i] = b + default: + return i, nil + } + } + return len(p), nil +} diff --git a/proxy/packet_test.go b/proxy/packet_test.go index 0db7222..d97a79e 100644 --- a/proxy/packet_test.go +++ b/proxy/packet_test.go @@ -4,31 +4,90 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "mpbl3p/mocks" + "mpbl3p/shared" "testing" ) func TestPacket_Marshal(t *testing.T) { testContent := []byte("A test string is the content of this packet.") - testPacket := NewPacket(testContent) - testMac := mocks.AlmostUselessMac{} + testPacket := NewSimplePacket(testContent) t.Run("Length", func(t *testing.T) { - marshalled := testPacket.Marshal(testMac) + marshalled := testPacket.Marshal() - assert.Len(t, marshalled, len(testContent)+8+4) + assert.Len(t, marshalled, len(testContent)+8) }) } func TestUnmarshalPacket(t *testing.T) { testContent := []byte("A test string is the content of this packet.") - testPacket := NewPacket(testContent) - testMac := mocks.AlmostUselessMac{} - testMarshalled := testPacket.Marshal(testMac) + testPacket := NewSimplePacket(testContent) + testMarshalled := testPacket.Marshal() t.Run("Length", func(t *testing.T) { - p, err := UnmarshalSimplePacket(testMarshalled, testMac) + p, err := UnmarshalSimplePacket(testMarshalled) require.Nil(t, err) - assert.Len(t, p.Marshal(), len(testContent)) + assert.Len(t, p.Contents(), len(testContent)) + }) + + t.Run("Contents", func(t *testing.T) { + p, err := UnmarshalSimplePacket(testMarshalled) + + require.Nil(t, err) + assert.Equal(t, p.Contents(), testContent) + }) +} + +func TestAppendMac(t *testing.T) { + testContent := []byte("A test string is the content of this packet.") + testMac := mocks.AlmostUselessMac{} + testPacket := NewSimplePacket(testContent) + testMarshalled := testPacket.Marshal() + + appended := AppendMac(testMarshalled, testMac) + + t.Run("Length", func(t *testing.T) { + assert.Len(t, appended, len(testMarshalled)+4) + }) + + t.Run("Mac", func(t *testing.T) { + assert.Equal(t, []byte{'a', 'b', 'c', 'd'}, appended[len(testMarshalled):]) + }) + + t.Run("Original", func(t *testing.T) { + assert.Equal(t, testMarshalled, appended[:len(testMarshalled)]) + }) +} + +func TestStripMac(t *testing.T) { + testContent := []byte("A test string is the content of this packet.") + testMac := mocks.AlmostUselessMac{} + testPacket := NewSimplePacket(testContent) + testMarshalled := testPacket.Marshal() + + appended := AppendMac(testMarshalled, testMac) + + t.Run("Length", func(t *testing.T) { + cut, err := StripMac(appended, testMac) + + require.Nil(t, err) + assert.Len(t, cut, len(testMarshalled)) + }) + + t.Run("IncorrectMac", func(t *testing.T) { + badMac := make([]byte, len(testMarshalled)+4) + copy(badMac, testMarshalled) + copy(badMac[:len(testMarshalled)], "dcba") + _, err := StripMac(badMac, testMac) + + assert.Error(t, err, shared.ErrBadChecksum) + }) + + t.Run("Original", func(t *testing.T) { + cut, err := StripMac(appended, testMac) + + require.Nil(t, err) + assert.Equal(t, testMarshalled, cut) }) } diff --git a/tcp/flow_test.go b/tcp/flow_test.go index cb02839..6e0e7b3 100644 --- a/tcp/flow_test.go +++ b/tcp/flow_test.go @@ -2,7 +2,7 @@ package tcp import ( "encoding/binary" - "github.com/go-playground/assert/v2" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "mpbl3p/mocks" "mpbl3p/proxy" @@ -11,11 +11,11 @@ import ( func TestFlow_Consume(t *testing.T) { testContent := []byte("A test string is the content of this packet.") - testPacket := proxy.NewPacket(testContent) + testPacket := proxy.NewSimplePacket(testContent) testMac := mocks.AlmostUselessMac{} t.Run("Length", func(t *testing.T) { - testConn := mocks.NewMockPerfectBiConn(100) + testConn := mocks.NewMockPerfectBiStreamConn(100) flowA := Flow{conn: testConn.SideA(), isAlive: true} @@ -39,7 +39,7 @@ func TestFlow_Produce(t *testing.T) { testMac := mocks.AlmostUselessMac{} t.Run("Length", func(t *testing.T) { - testConn := mocks.NewMockPerfectBiConn(100) + testConn := mocks.NewMockPerfectBiStreamConn(100) flowA := Flow{conn: testConn.SideA(), isAlive: true} @@ -48,11 +48,11 @@ func TestFlow_Produce(t *testing.T) { p, err := flowA.Produce(testMac) require.Nil(t, err) - assert.Equal(t, len(testContent), len(p.Marshal())) + assert.Equal(t, len(testContent), len(p.Contents())) }) t.Run("Value", func(t *testing.T) { - testConn := mocks.NewMockPerfectBiConn(100) + testConn := mocks.NewMockPerfectBiStreamConn(100) flowA := Flow{conn: testConn.SideA(), isAlive: true} @@ -61,6 +61,6 @@ func TestFlow_Produce(t *testing.T) { p, err := flowA.Produce(testMac) require.Nil(t, err) - assert.Equal(t, testContent, string(p.Marshal())) + assert.Equal(t, testContent, string(p.Contents())) }) } diff --git a/udp/congestion.go b/udp/congestion.go index 826910e..705a193 100644 --- a/udp/congestion.go +++ b/udp/congestion.go @@ -13,6 +13,5 @@ type Congestion interface { NextNack() uint32 AwaitEarlyUpdate(keepalive time.Duration) uint32 - AwaitAck(timeout time.Duration) bool Reset() } diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index 5e671f0..a12fc16 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -16,8 +16,9 @@ type NewReno struct { sequence chan uint32 keepalive chan bool - outboundTimes map[uint32]time.Time - inboundTimes map[uint32]time.Time + outboundTimes, inboundTimes map[uint32]time.Time + outboundTimesLock sync.Mutex + inboundTimesLock sync.RWMutex ack, lastAck uint32 nack, lastNack uint32 @@ -34,8 +35,7 @@ type NewReno struct { hasAcked bool acksToSend utils.Uint32Heap - - mu sync.Mutex + acksToSendLock sync.Mutex } func (c *NewReno) String() string { @@ -82,12 +82,16 @@ func (c *NewReno) Reset() { // It is assumed that ReceivedAck will only be called by one thread func (c *NewReno) ReceivedAck(ack uint32) { + c.outboundTimesLock.Lock() + defer c.outboundTimesLock.Unlock() + log.Printf("ack received for %d", ack) c.hasAcked = true // RTT // Update using an exponential average rtt := time.Now().Sub(c.outboundTimes[ack]).Seconds() + delete(c.outboundTimes, ack) c.rtt = c.rtt*(1-RttExponentialFactor) + rtt*RttExponentialFactor @@ -128,22 +132,26 @@ func (c *NewReno) ReceivedPacket(seq uint32) { c.inboundTimes[seq] = time.Now() - c.mu.Lock() + c.acksToSendLock.Lock() c.acksToSend.Insert(seq) - c.mu.Unlock() + c.acksToSendLock.Unlock() c.updateAckNack() } func (c *NewReno) updateAckNack() { - c.mu.Lock() - defer c.mu.Unlock() + c.acksToSendLock.Lock() + defer c.acksToSendLock.Unlock() + + c.inboundTimesLock.Lock() + defer c.inboundTimesLock.Unlock() findAck := func(start uint32) uint32 { ack := start for len(c.acksToSend) > 0 { if a, _ := c.acksToSend.Peek(); a == ack+1 { ack, _ = c.acksToSend.Extract() + delete(c.inboundTimes, ack) } else { break } @@ -169,6 +177,9 @@ func (c *NewReno) updateAckNack() { } func (c *NewReno) Sequence() uint32 { + c.outboundTimesLock.Lock() + defer c.outboundTimesLock.Unlock() + for c.inFlight >= c.windowSize { <-c.ackNotifier } @@ -213,11 +224,3 @@ func (c *NewReno) AwaitEarlyUpdate(keepalive time.Duration) uint32 { } } } - -func (c *NewReno) AwaitAck(timeout time.Duration) bool { - if c.hasAcked { - return true - } - time.Sleep(timeout) - return c.hasAcked -} diff --git a/udp/congestion/none.go b/udp/congestion/none.go index 3405f52..4640a04 100644 --- a/udp/congestion/none.go +++ b/udp/congestion/none.go @@ -38,7 +38,6 @@ func (c *None) ReceivedPacket(uint32) {} func (c *None) ReceivedAck(uint32) {} func (c *None) ReceivedNack(uint32) {} func (c *None) Reset() {} -func (c *None) AwaitAck(time.Duration) bool { return true } func (c *None) NextNack() uint32 { return 0 } func (c *None) NextAck() uint32 { return 0 } func (c *None) AwaitEarlyUpdate(time.Duration) uint32 { select {} } diff --git a/udp/flow.go b/udp/flow.go index cebd8ad..e6e01c7 100644 --- a/udp/flow.go +++ b/udp/flow.go @@ -82,6 +82,7 @@ func newFlow(c Congestion, v proxy.MacVerifier) Flow { func (f *InitiatedFlow) Reconnect() error { f.mu.Lock() + defer f.mu.Unlock() if f.isAlive { return nil @@ -109,7 +110,6 @@ func (f *InitiatedFlow) Reconnect() error { go func() { seq := f.congestion.Sequence() - defer f.mu.Unlock() for !f.isAlive { p := Packet{ ack: 0, @@ -119,31 +119,6 @@ func (f *InitiatedFlow) Reconnect() error { } _ = f.sendPacket(p, f.g) - - if f.congestion.AwaitAck(1 * time.Second) { - f.isAlive = true - f.startup = false - } - } - }() - - go func() { - for f.startup || f.isAlive { - func() { - if f.isAlive { - f.mu.RLock() - defer f.mu.RUnlock() - } - - buf := make([]byte, 6000) - n, _, err := conn.ReadFromUDP(buf) - if err != nil { - log.Println(err) - time.Sleep(1) - } else { - f.handleDatagram(buf[:n]) - } - }() } }() @@ -152,6 +127,30 @@ func (f *InitiatedFlow) Reconnect() error { }() go f.earlyUpdateLoop(f.g, f.keepalive) + if err := f.acceptPacket(conn); err != nil { + return err + } + + f.isAlive = true + f.startup = false + + go func() { + lockedAccept := func() { + f.mu.RLock() + defer f.mu.RUnlock() + + if err := f.acceptPacket(conn); err != nil { + log.Println(err) + } + } + + for f.isAlive { + log.Println("alive and listening for packets") + lockedAccept() + } + log.Println("no longer alive") + }() + return nil } @@ -273,3 +272,14 @@ func (f *Flow) earlyUpdateLoop(g proxy.MacGenerator, keepalive time.Duration) { _ = f.sendPacket(p, g) } } + +func (f *Flow) acceptPacket(c PacketConn) error { + buf := make([]byte, 6000) + n, _, err := c.ReadFromUDP(buf) + if err != nil { + return err + } + + f.handleDatagram(buf[:n]) + return nil +} diff --git a/udp/flow_test.go b/udp/flow_test.go new file mode 100644 index 0000000..e102ffe --- /dev/null +++ b/udp/flow_test.go @@ -0,0 +1,85 @@ +package udp + +import ( + "fmt" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "mpbl3p/mocks" + "mpbl3p/proxy" + "mpbl3p/udp/congestion" + "testing" + "time" +) + +func TestFlow_Consume(t *testing.T) { + testContent := []byte("A test string is the content of this packet.") + testPacket := proxy.NewSimplePacket(testContent) + testMac := mocks.AlmostUselessMac{} + + t.Run("Length", func(t *testing.T) { + testConn := mocks.NewMockPerfectBiPacketConn(10) + + flowA := newFlow(congestion.NewNone(), testMac) + + flowA.writer = testConn.SideB() + flowA.isAlive = true + + err := flowA.Consume(testPacket, testMac) + require.Nil(t, err) + + buf := make([]byte, 100) + n, _, err := testConn.SideA().ReadFromUDP(buf) + require.Nil(t, err) + + // 12 header, 8 timestamp, 4 MAC + assert.Equal(t, len(testContent)+12+8+4, n) + }) +} + +func TestFlow_Produce(t *testing.T) { + testContent := []byte("A test string is the content of this packet.") + testPacket := Packet{ + ack: 42, + nack: 26, + seq: 128, + data: proxy.NewSimplePacket(testContent), + } + testMac := mocks.AlmostUselessMac{} + + testMarshalled := proxy.AppendMac(testPacket.Marshal(), testMac) + + t.Run("Length", func(t *testing.T) { + done := make(chan struct{}) + + go func() { + testConn := mocks.NewMockPerfectBiPacketConn(10) + + _, err := testConn.SideA().Write(testMarshalled) + require.Nil(t, err) + + flowA := newFlow(congestion.NewNone(), testMac) + + flowA.writer = testConn.SideB() + flowA.isAlive = true + + go func() { + err := flowA.acceptPacket(testConn.SideB()) + assert.Nil(t, err) + }() + p, err := flowA.Produce(testMac) + + require.Nil(t, err) + assert.Len(t, p.Contents(), len(testContent)) + + done <- struct{}{} + }() + + timer := time.NewTimer(500 * time.Millisecond) + select { + case <-done: + case <-timer.C: + fmt.Println("timed out") + t.FailNow() + } + }) +} diff --git a/udp/listener.go b/udp/listener.go index b93aea7..847731d 100644 --- a/udp/listener.go +++ b/udp/listener.go @@ -25,7 +25,7 @@ func fromUdpAddress(address net.UDPAddr) ComparableUdpAddress { } } -func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier, g proxy.MacGenerator, c Congestion) error { +func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier, g proxy.MacGenerator, c func() Congestion) error { laddr, err := net.ResolveUDPAddr("udp", local) if err != nil { return err @@ -63,7 +63,7 @@ func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier, g proxy.MacG continue } - f := newFlow(c, v) + f := newFlow(c(), v) f.writer = pconn f.raddr = addr diff --git a/udp/packet_test.go b/udp/packet_test.go new file mode 100644 index 0000000..8770774 --- /dev/null +++ b/udp/packet_test.go @@ -0,0 +1,59 @@ +package udp + +import ( + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "mpbl3p/proxy" + "testing" +) + +func TestPacket_Marshal(t *testing.T) { + testContent := []byte("A test string is the content of this packet.") + testPacket := Packet{ + ack: 18, + nack: 29, + seq: 431, + data: proxy.NewSimplePacket(testContent), + } + + t.Run("Length", func(t *testing.T) { + marshalled := testPacket.Marshal() + + // 12 header + 8 timestamp + assert.Len(t, marshalled, len(testContent)+12+8) + }) +} + +func TestUnmarshalPacket(t *testing.T) { + testContent := []byte("A test string is the content of this packet.") + testPacket := Packet{ + ack: 18, + nack: 29, + seq: 431, + data: proxy.NewSimplePacket(testContent), + } + testMarshalled := testPacket.Marshal() + + t.Run("Length", func(t *testing.T) { + p, err := UnmarshalPacket(testMarshalled) + + require.Nil(t, err) + assert.Len(t, p.Contents(), len(testContent)) + }) + + t.Run("Contents", func(t *testing.T) { + p, err := UnmarshalPacket(testMarshalled) + + require.Nil(t, err) + assert.Equal(t, p.Contents(), testContent) + }) + + t.Run("Header", func(t *testing.T) { + p, err := UnmarshalPacket(testMarshalled) + require.Nil(t, err) + + assert.Equal(t, p.ack, uint32(18)) + assert.Equal(t, p.nack, uint32(29)) + assert.Equal(t, p.seq, uint32(431)) + }) +} -- 2.47.0 From d13159ddde05f6c515f58694c746684d322cdcb0 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 28 Nov 2020 13:31:12 +0000 Subject: [PATCH 015/121] removed comment --- udp/wireshark_dissector.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/udp/wireshark_dissector.lua b/udp/wireshark_dissector.lua index f889e03..3514eb0 100644 --- a/udp/wireshark_dissector.lua +++ b/udp/wireshark_dissector.lua @@ -32,8 +32,6 @@ function mpbl3p_udp.dissector(buffer, pinfo, tree) if buffer:len() > 20 then subtree:add(proxied_F, buffer(12, buffer:len() - 12 - 8)) end - - --Dissector.call(buffer(12, buffer:len()-12-8), pinfo, tree) end DissectorTable.get("udp.port"):add(1234, mpbl3p_udp) -- 2.47.0 From c25317b89009328fcefd2e3283140fae772bb9c8 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 28 Nov 2020 16:37:41 +0000 Subject: [PATCH 016/121] removed unused package --- go.mod | 1 - 1 file changed, 1 deletion(-) diff --git a/go.mod b/go.mod index 22a3f7d..dd6edf3 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module mpbl3p go 1.15 require ( - github.com/go-playground/assert/v2 v2.0.1 github.com/go-playground/validator/v10 v10.4.1 github.com/pkg/taptun v0.0.0-20160424131934-bbbd335672ab github.com/smartystreets/goconvey v1.6.4 // indirect -- 2.47.0 From 3e7b7d4452e431be5d92fc0cd9d5d7de0e897138 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 28 Nov 2020 16:51:47 +0000 Subject: [PATCH 017/121] pr cleanup --- udp/congestion/newreno.go | 2 +- udp/wireshark_dissector.lua | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index a12fc16..5b2e179 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -213,7 +213,7 @@ func (c *NewReno) AwaitEarlyUpdate(keepalive time.Duration) uint32 { c.updateAckNack() - // CASE 1: > 5 waiting ACKs or any waiting NACKs and no message sent in the last RTT + // CASE 1: waiting ACKs or NACKs and no message sent in the last RTT if ((c.lastAck != c.ack) || (c.lastNack != c.nack)) && time.Now().After(c.lastSent.Add(rtt)) { return 0 } diff --git a/udp/wireshark_dissector.lua b/udp/wireshark_dissector.lua index 3514eb0..95f1c4b 100644 --- a/udp/wireshark_dissector.lua +++ b/udp/wireshark_dissector.lua @@ -1,5 +1,3 @@ -local ip_dissector = Dissector.get("ip") - mpbl3p_udp = Proto("mpbl3p_udp", "Multi Path Proxy Custom UDP") ack_F = ProtoField.uint32("mpbl3p_udp.ack", "Acknowledgement") -- 2.47.0 From 179025ad2bebe41c4801b04eb1898bd0ae97378f Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 28 Nov 2020 17:15:56 +0000 Subject: [PATCH 018/121] refactored timestamping --- proxy/packet.go | 45 +++++++++++--------------------------------- proxy/packet_test.go | 39 ++++---------------------------------- shared/errors.go | 1 + tcp/flow.go | 7 ++----- tcp/flow_test.go | 2 +- tun/tun.go | 2 +- udp/flow.go | 4 ++-- udp/flow_test.go | 4 ++-- udp/packet.go | 10 ++++++---- udp/packet_test.go | 6 +++--- 10 files changed, 33 insertions(+), 87 deletions(-) diff --git a/proxy/packet.go b/proxy/packet.go index 260b9f9..f24115f 100644 --- a/proxy/packet.go +++ b/proxy/packet.go @@ -10,49 +10,26 @@ type Packet interface { Contents() []byte } -type SimplePacket struct { - Data []byte - timestamp time.Time -} - -// create a packet from the raw data of an IP packet -func NewSimplePacket(data []byte) Packet { - return SimplePacket{ - Data: data, - timestamp: time.Now(), - } -} - -// rebuild a packet from the wrapped format -func UnmarshalSimplePacket(data []byte) (SimplePacket, error) { - p := SimplePacket{ - Data: data[:len(data)-8], - } - - unixTime := int64(binary.LittleEndian.Uint64(data[len(data)-8:])) - p.timestamp = time.Unix(unixTime, 0) - - return p, nil -} +type SimplePacket []byte // get the raw data of the IP packet func (p SimplePacket) Marshal() []byte { - footer := make([]byte, 8) - - unixTime := uint64(p.timestamp.Unix()) - binary.LittleEndian.PutUint64(footer, unixTime) - - return append(p.Data, footer...) + return p } func (p SimplePacket) Contents() []byte { - return p.Data + return p } func AppendMac(b []byte, g MacGenerator) []byte { + footer := make([]byte, 8) + unixTime := uint64(time.Now().Unix()) + binary.LittleEndian.PutUint64(footer, unixTime) + + b = append(b, footer...) + mac := g.Generate(b) - b = append(b, mac...) - return b + return append(b, mac...) } func StripMac(b []byte, v MacVerifier) ([]byte, error) { @@ -63,5 +40,5 @@ func StripMac(b []byte, v MacVerifier) ([]byte, error) { return nil, err } - return data, nil + return data[:len(data)-8], nil } diff --git a/proxy/packet_test.go b/proxy/packet_test.go index d97a79e..af2d626 100644 --- a/proxy/packet_test.go +++ b/proxy/packet_test.go @@ -8,51 +8,20 @@ import ( "testing" ) -func TestPacket_Marshal(t *testing.T) { - testContent := []byte("A test string is the content of this packet.") - testPacket := NewSimplePacket(testContent) - - t.Run("Length", func(t *testing.T) { - marshalled := testPacket.Marshal() - - assert.Len(t, marshalled, len(testContent)+8) - }) -} - -func TestUnmarshalPacket(t *testing.T) { - testContent := []byte("A test string is the content of this packet.") - testPacket := NewSimplePacket(testContent) - testMarshalled := testPacket.Marshal() - - t.Run("Length", func(t *testing.T) { - p, err := UnmarshalSimplePacket(testMarshalled) - - require.Nil(t, err) - assert.Len(t, p.Contents(), len(testContent)) - }) - - t.Run("Contents", func(t *testing.T) { - p, err := UnmarshalSimplePacket(testMarshalled) - - require.Nil(t, err) - assert.Equal(t, p.Contents(), testContent) - }) -} - func TestAppendMac(t *testing.T) { testContent := []byte("A test string is the content of this packet.") testMac := mocks.AlmostUselessMac{} - testPacket := NewSimplePacket(testContent) + testPacket := SimplePacket(testContent) testMarshalled := testPacket.Marshal() appended := AppendMac(testMarshalled, testMac) t.Run("Length", func(t *testing.T) { - assert.Len(t, appended, len(testMarshalled)+4) + assert.Len(t, appended, len(testMarshalled)+8+4) }) t.Run("Mac", func(t *testing.T) { - assert.Equal(t, []byte{'a', 'b', 'c', 'd'}, appended[len(testMarshalled):]) + assert.Equal(t, []byte{'a', 'b', 'c', 'd'}, appended[len(testMarshalled)+8:]) }) t.Run("Original", func(t *testing.T) { @@ -63,7 +32,7 @@ func TestAppendMac(t *testing.T) { func TestStripMac(t *testing.T) { testContent := []byte("A test string is the content of this packet.") testMac := mocks.AlmostUselessMac{} - testPacket := NewSimplePacket(testContent) + testPacket := SimplePacket(testContent) testMarshalled := testPacket.Marshal() appended := AppendMac(testMarshalled, testMac) diff --git a/shared/errors.go b/shared/errors.go index aaae8ba..0db0c92 100644 --- a/shared/errors.go +++ b/shared/errors.go @@ -4,3 +4,4 @@ import "errors" var ErrBadChecksum = errors.New("the packet had a bad checksum") var ErrDeadConnection = errors.New("the connection is dead") +var ErrNotEnoughBytes = errors.New("not enough bytes") diff --git a/tcp/flow.go b/tcp/flow.go index c626e49..057b233 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -2,7 +2,6 @@ package tcp import ( "encoding/binary" - "errors" "fmt" "io" "mpbl3p/proxy" @@ -12,8 +11,6 @@ import ( "time" ) -var ErrNotEnoughBytes = errors.New("not enough bytes") - type Conn interface { Read(b []byte) (n int, err error) Write(b []byte) (n int, err error) @@ -150,7 +147,7 @@ func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { return nil, err } - return proxy.UnmarshalSimplePacket(b) + return proxy.SimplePacket(b), nil } func (f *Flow) produceMarshalled() ([]byte, error) { @@ -158,7 +155,7 @@ func (f *Flow) produceMarshalled() ([]byte, error) { if n, err := io.LimitReader(f.conn, 4).Read(lengthBytes); err != nil { return nil, err } else if n != 4 { - return nil, ErrNotEnoughBytes + return nil, shared.ErrNotEnoughBytes } length := binary.LittleEndian.Uint32(lengthBytes) diff --git a/tcp/flow_test.go b/tcp/flow_test.go index 6e0e7b3..17214e0 100644 --- a/tcp/flow_test.go +++ b/tcp/flow_test.go @@ -11,7 +11,7 @@ import ( func TestFlow_Consume(t *testing.T) { testContent := []byte("A test string is the content of this packet.") - testPacket := proxy.NewSimplePacket(testContent) + testPacket := proxy.SimplePacket(testContent) testMac := mocks.AlmostUselessMac{} t.Run("Length", func(t *testing.T) { diff --git a/tun/tun.go b/tun/tun.go index 1b55eb1..b4141f2 100644 --- a/tun/tun.go +++ b/tun/tun.go @@ -68,7 +68,7 @@ func (t *SourceSink) Source() (proxy.Packet, error) { return nil, io.EOF } - return proxy.NewSimplePacket(buf[:read]), nil + return proxy.SimplePacket(buf[:read]), nil } var good, bad float64 diff --git a/udp/flow.go b/udp/flow.go index e6e01c7..d8c842e 100644 --- a/udp/flow.go +++ b/udp/flow.go @@ -115,7 +115,7 @@ func (f *InitiatedFlow) Reconnect() error { ack: 0, nack: 0, seq: seq, - data: proxy.NewSimplePacket(nil), + data: proxy.SimplePacket(nil), } _ = f.sendPacket(p, f.g) @@ -266,7 +266,7 @@ func (f *Flow) earlyUpdateLoop(g proxy.MacGenerator, keepalive time.Duration) { ack: f.congestion.NextAck(), nack: f.congestion.NextNack(), seq: seq, - data: proxy.NewSimplePacket(nil), + data: proxy.SimplePacket(nil), } _ = f.sendPacket(p, g) diff --git a/udp/flow_test.go b/udp/flow_test.go index e102ffe..b2f20c2 100644 --- a/udp/flow_test.go +++ b/udp/flow_test.go @@ -13,7 +13,7 @@ import ( func TestFlow_Consume(t *testing.T) { testContent := []byte("A test string is the content of this packet.") - testPacket := proxy.NewSimplePacket(testContent) + testPacket := proxy.SimplePacket(testContent) testMac := mocks.AlmostUselessMac{} t.Run("Length", func(t *testing.T) { @@ -42,7 +42,7 @@ func TestFlow_Produce(t *testing.T) { ack: 42, nack: 26, seq: 128, - data: proxy.NewSimplePacket(testContent), + data: proxy.SimplePacket(testContent), } testMac := mocks.AlmostUselessMac{} diff --git a/udp/packet.go b/udp/packet.go index 8ab0091..08757d8 100644 --- a/udp/packet.go +++ b/udp/packet.go @@ -3,6 +3,7 @@ package udp import ( "encoding/binary" "mpbl3p/proxy" + "mpbl3p/shared" ) type Packet struct { @@ -14,14 +15,15 @@ type Packet struct { } func UnmarshalPacket(b []byte) (p Packet, err error) { + if len(b) < 12 { + return Packet{}, shared.ErrNotEnoughBytes + } + p.ack = binary.LittleEndian.Uint32(b[0:4]) p.nack = binary.LittleEndian.Uint32(b[4:8]) p.seq = binary.LittleEndian.Uint32(b[8:12]) - p.data, err = proxy.UnmarshalSimplePacket(b[12:]) - if err != nil { - return Packet{}, err - } + p.data = proxy.SimplePacket(b[12:]) return p, nil } diff --git a/udp/packet_test.go b/udp/packet_test.go index 8770774..e1ded3d 100644 --- a/udp/packet_test.go +++ b/udp/packet_test.go @@ -13,14 +13,14 @@ func TestPacket_Marshal(t *testing.T) { ack: 18, nack: 29, seq: 431, - data: proxy.NewSimplePacket(testContent), + data: proxy.SimplePacket(testContent), } t.Run("Length", func(t *testing.T) { marshalled := testPacket.Marshal() // 12 header + 8 timestamp - assert.Len(t, marshalled, len(testContent)+12+8) + assert.Len(t, marshalled, len(testContent)+12) }) } @@ -30,7 +30,7 @@ func TestUnmarshalPacket(t *testing.T) { ack: 18, nack: 29, seq: 431, - data: proxy.NewSimplePacket(testContent), + data: proxy.SimplePacket(testContent), } testMarshalled := testPacket.Marshal() -- 2.47.0 From 5066f8a823fefac9ef4478bf0fe0a7ae59572e74 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sun, 29 Nov 2020 22:06:38 +0000 Subject: [PATCH 019/121] Security start --- config/builder.go | 78 ++++++++++++++++++++----------------- config/config.go | 5 ++- crypto/none.go | 15 +++++++ crypto/sharedkey/blake2s.go | 40 +++++++++++++++++++ go.mod | 1 + proxy/proxy.go | 6 +-- tcp/listener.go | 6 +-- udp/listener.go | 7 +++- 8 files changed, 111 insertions(+), 47 deletions(-) create mode 100644 crypto/none.go create mode 100644 crypto/sharedkey/blake2s.go diff --git a/config/builder.go b/config/builder.go index c51e640..0f4fce4 100644 --- a/config/builder.go +++ b/config/builder.go @@ -1,7 +1,10 @@ package config import ( + "encoding/base64" "fmt" + "mpbl3p/crypto" + "mpbl3p/crypto/sharedkey" "mpbl3p/proxy" "mpbl3p/tcp" "mpbl3p/tun" @@ -10,24 +13,33 @@ import ( "time" ) -// TODO: Delete this code as soon as an alternative is available -type UselessMac struct{} - -func (UselessMac) CodeLength() int { - return 0 -} - -func (UselessMac) Generate([]byte) []byte { - return nil -} - -func (u UselessMac) Verify([]byte, []byte) error { - return nil -} - func (c Configuration) Build() (*proxy.Proxy, error) { p := proxy.NewProxy(0) - p.Generator = UselessMac{} + + var g func() proxy.MacGenerator + var v func() proxy.MacVerifier + + switch c.Host.Crypto { + case "None": + g = func() proxy.MacGenerator { return crypto.None{} } + v = func() proxy.MacVerifier { return crypto.None{} } + case "Blake2s": + key, err := base64.StdEncoding.DecodeString(c.Host.SharedKey) + if err != nil { + return nil, err + } + if _, err := sharedkey.NewBlake2s(key); err != nil { + return nil, err + } + g = func() proxy.MacGenerator { + g, _ := sharedkey.NewBlake2s(key) + return g + } + v = func() proxy.MacVerifier { + v, _ := sharedkey.NewBlake2s(key) + return v + } + } if c.Host.InterfaceName == "" { c.Host.InterfaceName = "nc%d" @@ -44,12 +56,12 @@ func (c Configuration) Build() (*proxy.Proxy, error) { for _, peer := range c.Peers { switch peer.Method { case "TCP": - err := buildTcp(p, peer) + err := buildTcp(p, peer, g, v) if err != nil { return nil, err } case "UDP": - err := buildUdp(p, peer) + err := buildUdp(p, peer, g, v) if err != nil { return nil, err } @@ -59,7 +71,7 @@ func (c Configuration) Build() (*proxy.Proxy, error) { return p, nil } -func buildTcp(p *proxy.Proxy, peer Peer) error { +func buildTcp(p *proxy.Proxy, peer Peer, v func() proxy.MacGenerator, g func() proxy.MacVerifier) error { if peer.RemoteHost != "" { f, err := tcp.InitiateFlow( fmt.Sprintf("%s:", peer.LocalHost), @@ -70,13 +82,13 @@ func buildTcp(p *proxy.Proxy, peer Peer) error { return err } - p.AddConsumer(f) - p.AddProducer(f, UselessMac{}) + p.AddConsumer(f, v()) + p.AddProducer(f, g()) return nil } - err := tcp.NewListener(p, fmt.Sprintf("%s:%d", peer.LocalHost, peer.LocalPort), UselessMac{}) + err := tcp.NewListener(p, fmt.Sprintf("%s:%d", peer.LocalHost, peer.LocalPort), g, v) if err != nil { return err } @@ -84,23 +96,23 @@ func buildTcp(p *proxy.Proxy, peer Peer) error { return nil } -func buildUdp(p *proxy.Proxy, peer Peer) error { +func buildUdp(p *proxy.Proxy, peer Peer, v func() proxy.MacGenerator, g func() proxy.MacVerifier) error { var c func() udp.Congestion switch peer.Congestion { case "None": - c = func() udp.Congestion {return congestion.NewNone()} + c = func() udp.Congestion { return congestion.NewNone() } default: fallthrough case "NewReno": - c = func() udp.Congestion {return congestion.NewNewReno()} + c = func() udp.Congestion { return congestion.NewNewReno() } } if peer.RemoteHost != "" { f, err := udp.InitiateFlow( fmt.Sprintf("%s:", peer.LocalHost), fmt.Sprintf("%s:%d", peer.RemoteHost, peer.RemotePort), - UselessMac{}, - UselessMac{}, + crypto.None{}, + crypto.None{}, c(), time.Duration(peer.KeepAlive)*time.Second, ) @@ -109,19 +121,13 @@ func buildUdp(p *proxy.Proxy, peer Peer) error { return err } - p.AddConsumer(f) - p.AddProducer(f, UselessMac{}) + p.AddConsumer(f, v()) + p.AddProducer(f, g()) return nil } - err := udp.NewListener( - p, - fmt.Sprintf("%s:%d", peer.LocalHost, peer.LocalPort), - UselessMac{}, - UselessMac{}, - c, - ) + err := udp.NewListener(p, fmt.Sprintf("%s:%d", peer.LocalHost, peer.LocalPort), g, v, c) if err != nil { return err } diff --git a/config/config.go b/config/config.go index 73b086a..0f8f06a 100644 --- a/config/config.go +++ b/config/config.go @@ -10,12 +10,13 @@ type Configuration struct { } type Host struct { - PrivateKey string `validate:"required"` InterfaceName string + + Crypto string `validate:"required,oneof=None Blake2s"` + SharedKey string `validate:"required_if=Crypto Blake2s"` } type Peer struct { - PublicKey string `validate:"required"` Method string `validate:"oneof=TCP UDP"` LocalHost string `validate:"omitempty,ip"` diff --git a/crypto/none.go b/crypto/none.go new file mode 100644 index 0000000..82a74f2 --- /dev/null +++ b/crypto/none.go @@ -0,0 +1,15 @@ +package crypto + +type None struct{} + +func (None) CodeLength() int { + return 0 +} + +func (None) Generate([]byte) []byte { + return nil +} + +func (None) Verify([]byte, []byte) error { + return nil +} diff --git a/crypto/sharedkey/blake2s.go b/crypto/sharedkey/blake2s.go new file mode 100644 index 0000000..3ded854 --- /dev/null +++ b/crypto/sharedkey/blake2s.go @@ -0,0 +1,40 @@ +package sharedkey + +import ( + "bytes" + "golang.org/x/crypto/blake2s" + "mpbl3p/shared" +) + +type Blake2s struct { + key []byte +} + +func NewBlake2s(key []byte) (*Blake2s, error) { + _, err := blake2s.New128(key) + if err != nil { + return nil, err + } + + return &Blake2s{key: key}, nil +} + +func (b Blake2s) CodeLength() int { + return blake2s.Size128 +} + +func (b Blake2s) Generate(d []byte) []byte { + h, _ := blake2s.New128(b.key) + h.Write(d) + return h.Sum([]byte{}) +} + +func (b Blake2s) Verify(d []byte, s []byte) error { + h, _ := blake2s.New128(b.key) + h.Write(d) + sum := h.Sum([]byte{}) + if !bytes.Equal(sum, s) { + return shared.ErrBadChecksum + } + return nil +} diff --git a/go.mod b/go.mod index dd6edf3..ad76837 100644 --- a/go.mod +++ b/go.mod @@ -7,5 +7,6 @@ require ( github.com/pkg/taptun v0.0.0-20160424131934-bbbd335672ab github.com/smartystreets/goconvey v1.6.4 // indirect github.com/stretchr/testify v1.4.0 + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 gopkg.in/ini.v1 v1.62.0 ) diff --git a/proxy/proxy.go b/proxy/proxy.go index 2bea389..a82698b 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -31,8 +31,6 @@ type Proxy struct { Source Source Sink Sink - Generator MacGenerator - proxyChan chan Packet sinkChan chan Packet } @@ -67,7 +65,7 @@ func (p Proxy) Start() { }() } -func (p Proxy) AddConsumer(c Consumer) { +func (p Proxy) AddConsumer(c Consumer, g MacGenerator) { go func() { _, reconnectable := c.(Reconnectable) @@ -85,7 +83,7 @@ func (p Proxy) AddConsumer(c Consumer) { } for c.IsAlive() { - if err := c.Consume(<-p.proxyChan, p.Generator); err != nil { + if err := c.Consume(<-p.proxyChan, g); err != nil { log.Println(err) break } diff --git a/tcp/listener.go b/tcp/listener.go index 52892e0..2ca2e3e 100644 --- a/tcp/listener.go +++ b/tcp/listener.go @@ -6,7 +6,7 @@ import ( "net" ) -func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier) error { +func NewListener(p *proxy.Proxy, local string, v func() proxy.MacVerifier, g func() proxy.MacGenerator) error { laddr, err := net.ResolveTCPAddr("tcp", local) if err != nil { return err @@ -33,8 +33,8 @@ func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier) error { log.Printf("received new tcp connection: %v\n", f) - p.AddConsumer(&f) - p.AddProducer(&f, v) + p.AddConsumer(&f, g()) + p.AddProducer(&f, v()) } }() diff --git a/udp/listener.go b/udp/listener.go index 847731d..09cc19a 100644 --- a/udp/listener.go +++ b/udp/listener.go @@ -25,7 +25,7 @@ func fromUdpAddress(address net.UDPAddr) ComparableUdpAddress { } } -func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier, g proxy.MacGenerator, c func() Congestion) error { +func NewListener(p *proxy.Proxy, local string, v func() proxy.MacVerifier, g func() proxy.MacGenerator, c func() Congestion) error { laddr, err := net.ResolveUDPAddr("udp", local) if err != nil { return err @@ -63,6 +63,9 @@ func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier, g proxy.MacG continue } + v := v() + g := g() + f := newFlow(c(), v) f.writer = pconn @@ -75,7 +78,7 @@ func NewListener(p *proxy.Proxy, local string, v proxy.MacVerifier, g proxy.MacG receivedConnections[raddr] = &f - p.AddConsumer(&f) + p.AddConsumer(&f, g) p.AddProducer(&f, v) log.Println("handling...") -- 2.47.0 From 8e9310fb0281653809ab8db7d584341a1489b278 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sun, 29 Nov 2020 22:07:30 +0000 Subject: [PATCH 020/121] added timestamp todo --- proxy/packet.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/proxy/packet.go b/proxy/packet.go index f24115f..9e0f592 100644 --- a/proxy/packet.go +++ b/proxy/packet.go @@ -40,5 +40,7 @@ func StripMac(b []byte, v MacVerifier) ([]byte, error) { return nil, err } + // TODO: Verify timestamp + return data[:len(data)-8], nil } -- 2.47.0 From 9c0ceef9ac35f27cdd53539ef8cdf467d4da36d2 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sun, 29 Nov 2020 22:22:56 +0000 Subject: [PATCH 021/121] added todo --- udp/congestion/newreno.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index 5b2e179..31fce00 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -96,6 +96,9 @@ func (c *NewReno) ReceivedAck(ack uint32) { c.rtt = c.rtt*(1-RttExponentialFactor) + rtt*RttExponentialFactor // Free Window + // TODO: Check for freshness + // TODO: Don't expect an ACK per packet + atomic.AddInt32(&c.inFlight, -1) select { case c.ackNotifier <- struct{}{}: @@ -120,6 +123,8 @@ func (c *NewReno) ReceivedAck(ack uint32) { func (c *NewReno) ReceivedNack(nack uint32) { log.Printf("nack received for %d", nack) + // TODO : Check for freshness + // End slow start c.slowStart = false if s := c.windowSize; s > 1 { -- 2.47.0 From 8fdb176f148434aebaa8a532c7edcfccad64617f Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Fri, 11 Dec 2020 18:03:47 +0000 Subject: [PATCH 022/121] improved config validation --- config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 73b086a..b869335 100644 --- a/config/config.go +++ b/config/config.go @@ -24,7 +24,7 @@ type Peer struct { RemoteHost string `validate:"required_with=RemotePort,omitempty,fqdn|ip"` RemotePort uint `validate:"required_with=RemoteHost,omitempty,max=65535"` - Congestion string `validate:"oneof=NewReno None"` + Congestion string `validate:"required_unless=Method TCP,omitempty,oneof=NewReno None"` KeepAlive uint Timeout uint -- 2.47.0 From 71742d379005c58cbc441fded8c41172e1e1eb69 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Mon, 21 Dec 2020 14:38:18 +0000 Subject: [PATCH 023/121] drone formatting --- .drone.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index c5d91e0..1e00e52 100644 --- a/.drone.yml +++ b/.drone.yml @@ -3,6 +3,11 @@ type: docker name: default steps: + - name: format + image: golang:1.15 + commands: + - bash -c "gofmt -l . | wc -l | cmp -s <(echo 0) || (gofmt -l . && exit 1)" + - name: install image: golang:1.15 environment: @@ -47,4 +52,4 @@ steps: volumes: - name: cache - temp: {} + temp: { } -- 2.47.0 From 147d1c39c750278c14911b635727f6975a1ecf62 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Mon, 21 Dec 2020 14:38:54 +0000 Subject: [PATCH 024/121] fixed formatting --- config/builder.go | 4 ++-- proxy/packet_test.go | 2 +- udp/congestion/newreno.go | 6 +++--- udp/packet_test.go | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/config/builder.go b/config/builder.go index c51e640..18a0c9d 100644 --- a/config/builder.go +++ b/config/builder.go @@ -88,11 +88,11 @@ func buildUdp(p *proxy.Proxy, peer Peer) error { var c func() udp.Congestion switch peer.Congestion { case "None": - c = func() udp.Congestion {return congestion.NewNone()} + c = func() udp.Congestion { return congestion.NewNone() } default: fallthrough case "NewReno": - c = func() udp.Congestion {return congestion.NewNewReno()} + c = func() udp.Congestion { return congestion.NewNewReno() } } if peer.RemoteHost != "" { diff --git a/proxy/packet_test.go b/proxy/packet_test.go index d97a79e..094a3d2 100644 --- a/proxy/packet_test.go +++ b/proxy/packet_test.go @@ -30,7 +30,7 @@ func TestUnmarshalPacket(t *testing.T) { require.Nil(t, err) assert.Len(t, p.Contents(), len(testContent)) }) - + t.Run("Contents", func(t *testing.T) { p, err := UnmarshalSimplePacket(testMarshalled) diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index 5b2e179..2feea35 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -17,8 +17,8 @@ type NewReno struct { keepalive chan bool outboundTimes, inboundTimes map[uint32]time.Time - outboundTimesLock sync.Mutex - inboundTimesLock sync.RWMutex + outboundTimesLock sync.Mutex + inboundTimesLock sync.RWMutex ack, lastAck uint32 nack, lastNack uint32 @@ -34,7 +34,7 @@ type NewReno struct { lastSent time.Time hasAcked bool - acksToSend utils.Uint32Heap + acksToSend utils.Uint32Heap acksToSendLock sync.Mutex } diff --git a/udp/packet_test.go b/udp/packet_test.go index 8770774..24e7314 100644 --- a/udp/packet_test.go +++ b/udp/packet_test.go @@ -33,7 +33,7 @@ func TestUnmarshalPacket(t *testing.T) { data: proxy.NewSimplePacket(testContent), } testMarshalled := testPacket.Marshal() - + t.Run("Length", func(t *testing.T) { p, err := UnmarshalPacket(testMarshalled) @@ -47,7 +47,7 @@ func TestUnmarshalPacket(t *testing.T) { require.Nil(t, err) assert.Equal(t, p.Contents(), testContent) }) - + t.Run("Header", func(t *testing.T) { p, err := UnmarshalPacket(testMarshalled) require.Nil(t, err) -- 2.47.0 From 5eef9e06fe002e71ab7b0f719796564ee4610681 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Mon, 21 Dec 2020 14:42:36 +0000 Subject: [PATCH 025/121] signed dronefile --- .drone.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.drone.yml b/.drone.yml index 1e00e52..a94ea68 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,3 +1,4 @@ +--- kind: pipeline type: docker name: default @@ -53,3 +54,9 @@ steps: volumes: - name: cache temp: { } + +--- +kind: signature +hmac: cda16fe11c713d765607225c1cad7a62c76a668f47d6c9dafc9901242b4eab59 + +... -- 2.47.0 From 0831f04ae0ad959d5dfd438288ba31082d8499d9 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Mon, 21 Dec 2020 14:43:15 +0000 Subject: [PATCH 026/121] resigned reformatted dronefile --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index a94ea68..008d4a0 100644 --- a/.drone.yml +++ b/.drone.yml @@ -57,6 +57,6 @@ volumes: --- kind: signature -hmac: cda16fe11c713d765607225c1cad7a62c76a668f47d6c9dafc9901242b4eab59 +hmac: 8c21312bcfbaa9b7d2d3c31b1a5c13b54b4ecd1da853aa66a8ed11d72154fcca ... -- 2.47.0 From e6bc8b99b9363605ec5660d869f74bf14167d267 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Mon, 21 Dec 2020 14:51:32 +0000 Subject: [PATCH 027/121] formatting fixes --- config/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index 79b231e..163eb48 100644 --- a/config/config.go +++ b/config/config.go @@ -12,12 +12,12 @@ type Configuration struct { type Host struct { InterfaceName string - Crypto string `validate:"required,oneof=None Blake2s"` + Crypto string `validate:"required,oneof=None Blake2s"` SharedKey string `validate:"required_if=Crypto Blake2s"` } type Peer struct { - Method string `validate:"oneof=TCP UDP"` + Method string `validate:"oneof=TCP UDP"` LocalHost string `validate:"omitempty,ip"` LocalPort uint `validate:"max=65535"` -- 2.47.0 From 9ec92b676821405ca36c12b95a10d89f798a0978 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Wed, 20 Jan 2021 11:10:49 +0000 Subject: [PATCH 028/121] updated readme --- README.md | 109 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 5dc8d52..df4fc09 100644 --- a/README.md +++ b/README.md @@ -20,69 +20,102 @@ See http://kb.linuxvirtualserver.org/wiki/Using_arp_announce/arp_ignore_to_disable_ARP ### Setup Scripts -These are functional setup scripts that make the application run as intended on Linux. They should later be split into -component parts, or incorporated into the main application. +These are functional setup scripts that make the application run as intended on Linux. -#### Remote Portal +### Remote Portal +#### Pre-Start #!/bin/bash set -e - - # IPv4 Forwarding + + ## Set up variables + REMOTE_PORTAL_ADDRESS=A.B.C.D + + ## IPv4 Forwarding sysctl -w net.ipv4.ip_forward=1 sysctl -w net.ipv4.conf.eth0.proxy_arp=1 - # Tunnel addr/up + ## Tunnel addr/up ip addr add 172.19.152.2/31 dev nc0 ip link set up nc0 - # Deliberately break local routing + ## Transfer the local routing table to a much lower priority ip rule add from all table local priority 20 ip rule del 0 || true - # Route packets to the interface but for nc to this host - ip rule add to 1.1.1.3 dport 1234 table local priority 9 + ## Ports to route locally + ip rule add to "$REMOTE_PORTAL_ADDRESS" dport 1234 table local priority 1 + ip rule add to "$REMOTE_PORTAL_ADDRESS" dport 22 table local priority 2 + +#### Post-Start + + #!/bin/bash + set -e + + ## Set up variables + REMOTE_PORTAL_ADDRESS=A.B.C.D # Route packets to the interface but not for nc via the tunnel - ip route flush 10 - ip route add table 10 to 1.1.1.3 via 172.19.152.3 dev nc0 - ip rule add to 1.1.1.3 table 10 priority 10 + ip route flush 19 + ip route add table 19 to "$REMOTE_PORTAL_ADDRESS" via 172.19.152.3 dev nc0 + ip rule add to "$REMOTE_PORTAL_ADDRESS" table 19 priority 19 -#### Local Portal +### Local Portal +#### Pre-Start + + #!/bin/bash + set -e + + ## Set up variables + GATEWAY_INTERFACE=eth0 + GATEWAY_ADDRESS=10.36.12.1 + + ## Fix ARP + sysctl -w net.ipv4.conf.all.arp_announce=1 + sysctl -w net.ipv4.conf.all.arp_ignore=1 + + ## IPv4 Forwarding + sysctl -w net.ipv4.ip_forward=1 + + ## Gateway Interface Setup + ip addr add "$GATEWAY_ADDRESS"/32 dev "$GATEWAY_INTERFACE" + ip link set up "$GATEWAY_INTERFACE" + + ## Per-Interface Routing Tables + + ### 10.10.0.0/24 + ip route flush 10 + ip route add table 10 default via 10.10.0.1 + ip rule add from 10.10.0.0/24 table 10 priority 10 + + ### 192.168.0.0/24 + ip route flush 11 + ip route add table 11 default via 192.168.0.1 + ip rule add from 192.168.0.0/24 table 11 priority 11 + +#### Post-Start #!/bin/bash set -e - # Fix ARP - sysctl -w net.ipv4.conf.all.arp_announce=1 - sysctl -w net.ipv4.conf.all.arp_ignore=1 + ## Set up variables + REMOTE_PORTAL_ADDRESS=A.B.C.D + GATEWAY_INTERFACE=eth0 - # IPv4 Forwarding - sysctl -w net.ipv4.ip_forward=1 - - # Tunnel addr/up + ## Tunnel Address and Enable ip addr add 172.19.152.3/31 dev nc0 ip link set up nc0 - # Fix routing out of the correct interfaces - ip route flush 10 - ip route add table 10 to 1.1.1.0/24 dev eth1 - ip rule add from 1.1.1.4 table 10 priority 10 + ## Route Outbound Packets Correctly + ip route flush 20 + ip route add table 20 default via 172.19.152.2 dev nc0 + ip rule add from "$REMOTE_PORTAL_ADDRESS" iif "$GATEWAY_INTERFACE" table 20 priority 20 - ip route flush 11 - ip route add table 11 to 1.1.1.0/24 dev eth2 - ip rule add from 1.1.1.5 table 11 priority 11 - - # Route packets from the remote portal's address on the client interface via the tunnel - ip route flush 12 - ip route add table 12 to 1.1.1.0/24 via 172.19.152.2 dev nc0 - ip rule add from 1.1.1.3 iif eth3 table 12 priority 12 - - # Route packets to the remote portal's address out of the client interface - ip route flush 13 - ip route add table 13 to 1.1.1.3 dev eth3 - ip rule add to 1.1.1.3 table 13 priority 13 + ## Route Inbound Packets Correctly + ip route flush 21 + ip route add table 21 to "$REMOTE_PORTAL_ADDRESS" dev "$GATEWAY_INTERFACE" + ip rule add to "$REMOTE_PORTAL_ADDRESS" table 21 priority 21 #### Client -No configuration needed. Simply set the IP to that of the remote server/32 with a gateway of 192.168.1.1. +Connect to `GATEWAY_INTERFACE` and set the IP to `REMOTE_PORTAL_ADDRESS`/32 with a gateway of `GATEWAY_ADDRESS`. -- 2.47.0 From 34a373afc2ff29c91e4c608c59bfcda099751b85 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Wed, 20 Jan 2021 13:14:31 +0000 Subject: [PATCH 029/121] increased script resilience --- README.md | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index df4fc09..1cf9600 100644 --- a/README.md +++ b/README.md @@ -35,16 +35,18 @@ These are functional setup scripts that make the application run as intended on sysctl -w net.ipv4.ip_forward=1 sysctl -w net.ipv4.conf.eth0.proxy_arp=1 - ## Tunnel addr/up - ip addr add 172.19.152.2/31 dev nc0 - ip link set up nc0 - ## Transfer the local routing table to a much lower priority - ip rule add from all table local priority 20 - ip rule del 0 || true + (ip rule show | grep '20:') > /dev/null || ip rule add from all table local priority 20 + ip rule del 0 2> /dev/null || true ## Ports to route locally + + ### MPBL3P + ip rule del 1 2> /dev/null || true ip rule add to "$REMOTE_PORTAL_ADDRESS" dport 1234 table local priority 1 + + ### SSH + ip rule del 2 2> /dev/null || true ip rule add to "$REMOTE_PORTAL_ADDRESS" dport 22 table local priority 2 #### Post-Start @@ -54,10 +56,15 @@ These are functional setup scripts that make the application run as intended on ## Set up variables REMOTE_PORTAL_ADDRESS=A.B.C.D + + ## Tunnel addr/up + ip addr add 172.19.152.2/31 dev nc0 + ip link set up nc0 # Route packets to the interface but not for nc via the tunnel ip route flush 19 ip route add table 19 to "$REMOTE_PORTAL_ADDRESS" via 172.19.152.3 dev nc0 + ip rule del 19 2> /dev/null || true ip rule add to "$REMOTE_PORTAL_ADDRESS" table 19 priority 19 ### Local Portal @@ -86,11 +93,13 @@ These are functional setup scripts that make the application run as intended on ### 10.10.0.0/24 ip route flush 10 ip route add table 10 default via 10.10.0.1 + ip rule del 10 2> /dev/null || true ip rule add from 10.10.0.0/24 table 10 priority 10 ### 192.168.0.0/24 ip route flush 11 ip route add table 11 default via 192.168.0.1 + ip rule del 11 2> /dev/null || true ip rule add from 192.168.0.0/24 table 11 priority 11 #### Post-Start @@ -109,11 +118,13 @@ These are functional setup scripts that make the application run as intended on ## Route Outbound Packets Correctly ip route flush 20 ip route add table 20 default via 172.19.152.2 dev nc0 + ip rule del 20 2> /dev/null || true ip rule add from "$REMOTE_PORTAL_ADDRESS" iif "$GATEWAY_INTERFACE" table 20 priority 20 ## Route Inbound Packets Correctly ip route flush 21 ip route add table 21 to "$REMOTE_PORTAL_ADDRESS" dev "$GATEWAY_INTERFACE" + ip rule del 21 2> /dev/null || true ip rule add to "$REMOTE_PORTAL_ADDRESS" table 21 priority 21 #### Client -- 2.47.0 From a44b3747ddc395f5222978ab71b05d235c0c291a Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Wed, 20 Jan 2021 13:37:15 +0000 Subject: [PATCH 030/121] script compatability fixes --- README.md | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 1cf9600..7a67939 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@ ### Linux #### Policy Based Routing - ip route flush 10 + ip route flush table 10 ip route add table 10 to 1.1.1.0/24 dev eth1 ip rule add from 1.1.1.4 table 10 priority 10 - ip route flush 11 + ip route flush table 11 ip route add table 11 to 1.1.1.0/24 dev eth2 ip rule add from 1.1.1.5 table 11 priority 11 @@ -37,16 +37,16 @@ These are functional setup scripts that make the application run as intended on ## Transfer the local routing table to a much lower priority (ip rule show | grep '20:') > /dev/null || ip rule add from all table local priority 20 - ip rule del 0 2> /dev/null || true + ip rule del priority 0 2> /dev/null || true ## Ports to route locally ### MPBL3P - ip rule del 1 2> /dev/null || true + ip rule del priority 1 2> /dev/null || true ip rule add to "$REMOTE_PORTAL_ADDRESS" dport 1234 table local priority 1 ### SSH - ip rule del 2 2> /dev/null || true + ip rule del priority 2 2> /dev/null || true ip rule add to "$REMOTE_PORTAL_ADDRESS" dport 22 table local priority 2 #### Post-Start @@ -62,9 +62,9 @@ These are functional setup scripts that make the application run as intended on ip link set up nc0 # Route packets to the interface but not for nc via the tunnel - ip route flush 19 + ip route flush table 19 ip route add table 19 to "$REMOTE_PORTAL_ADDRESS" via 172.19.152.3 dev nc0 - ip rule del 19 2> /dev/null || true + ip rule del priority 19 2> /dev/null || true ip rule add to "$REMOTE_PORTAL_ADDRESS" table 19 priority 19 ### Local Portal @@ -85,21 +85,22 @@ These are functional setup scripts that make the application run as intended on sysctl -w net.ipv4.ip_forward=1 ## Gateway Interface Setup + ip addr flush dev "$GATEWAY_INTERFACE" ip addr add "$GATEWAY_ADDRESS"/32 dev "$GATEWAY_INTERFACE" ip link set up "$GATEWAY_INTERFACE" ## Per-Interface Routing Tables ### 10.10.0.0/24 - ip route flush 10 + ip route flush table 10 ip route add table 10 default via 10.10.0.1 - ip rule del 10 2> /dev/null || true + ip rule del priority 10 2> /dev/null || true ip rule add from 10.10.0.0/24 table 10 priority 10 ### 192.168.0.0/24 - ip route flush 11 + ip route flush table 11 ip route add table 11 default via 192.168.0.1 - ip rule del 11 2> /dev/null || true + ip rule del priority 11 2> /dev/null || true ip rule add from 192.168.0.0/24 table 11 priority 11 #### Post-Start @@ -116,15 +117,15 @@ These are functional setup scripts that make the application run as intended on ip link set up nc0 ## Route Outbound Packets Correctly - ip route flush 20 + ip route flush table 20 ip route add table 20 default via 172.19.152.2 dev nc0 - ip rule del 20 2> /dev/null || true + ip rule del priority 20 2> /dev/null || true ip rule add from "$REMOTE_PORTAL_ADDRESS" iif "$GATEWAY_INTERFACE" table 20 priority 20 ## Route Inbound Packets Correctly - ip route flush 21 + ip route flush table 21 ip route add table 21 to "$REMOTE_PORTAL_ADDRESS" dev "$GATEWAY_INTERFACE" - ip rule del 21 2> /dev/null || true + ip rule del priority 21 2> /dev/null || true ip rule add to "$REMOTE_PORTAL_ADDRESS" table 21 priority 21 #### Client -- 2.47.0 From ae84b68922cea31ea71649cc268abd5c59be9dc6 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Wed, 20 Jan 2021 13:43:38 +0000 Subject: [PATCH 031/121] cross compiling --- .drone.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index c5d91e0..0fc77ae 100644 --- a/.drone.yml +++ b/.drone.yml @@ -30,7 +30,8 @@ steps: - name: cache path: /go commands: - - go build + - GOOS=linux GOARCH=amd64 go build -o linux_amd64 + - GOOS=linux GOARCH=arm64 GOARM=8 go build -o linux_arm64_v8 - name: upload image: minio/mc @@ -43,7 +44,8 @@ steps: from_secret: s3_secret_key commands: - mc alias set s3 http://10.20.0.25:3900 $${ACCESS_KEY} $${SECRET_KEY} - - mc cp mpbl3p s3/dissertation/binaries/debian/${DRONE_BRANCH} + - mc cp linux_amd64 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_amd64 + - mc cp linux_arm64_v8 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_arm64_v8 volumes: - name: cache -- 2.47.0 From 150f76132bdf407040db4e70673a118b7eb72212 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Wed, 20 Jan 2021 13:45:17 +0000 Subject: [PATCH 032/121] armv7 --- .drone.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index 637a1c2..a5ba30e 100644 --- a/.drone.yml +++ b/.drone.yml @@ -37,7 +37,7 @@ steps: path: /go commands: - GOOS=linux GOARCH=amd64 go build -o linux_amd64 - - GOOS=linux GOARCH=arm64 GOARM=8 go build -o linux_arm64_v8 + - GOOS=linux GOARCH=arm64 GOARM=7 go build -o linux_arm64_v7 - name: upload image: minio/mc @@ -51,7 +51,7 @@ steps: commands: - mc alias set s3 http://10.20.0.25:3900 $${ACCESS_KEY} $${SECRET_KEY} - mc cp linux_amd64 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_amd64 - - mc cp linux_arm64_v8 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_arm64_v8 + - mc cp linux_arm64_v7 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_arm64_v7 volumes: - name: cache -- 2.47.0 From d7147ba18af71e64bfd8f33bf55c8d219c6036bd Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Wed, 20 Jan 2021 13:53:18 +0000 Subject: [PATCH 033/121] renamed files for b2 --- .drone.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index a5ba30e..319126e 100644 --- a/.drone.yml +++ b/.drone.yml @@ -50,8 +50,8 @@ steps: from_secret: s3_secret_key commands: - mc alias set s3 http://10.20.0.25:3900 $${ACCESS_KEY} $${SECRET_KEY} - - mc cp linux_amd64 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_amd64 - - mc cp linux_arm64_v7 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_arm64_v7 + - mc cp linux_amd64 s3/dissertation/binaries/debian/${DRONE_BRANCH}-linux-amd64 + - mc cp linux_arm64_v7 s3/dissertation/binaries/debian/${DRONE_BRANCH}-linux-arm64-v7 volumes: - name: cache -- 2.47.0 From c8a2f37c154830fbea749079faeb021c79ad9d10 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Wed, 20 Jan 2021 13:54:25 +0000 Subject: [PATCH 034/121] reverted name change --- .drone.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index 319126e..a5ba30e 100644 --- a/.drone.yml +++ b/.drone.yml @@ -50,8 +50,8 @@ steps: from_secret: s3_secret_key commands: - mc alias set s3 http://10.20.0.25:3900 $${ACCESS_KEY} $${SECRET_KEY} - - mc cp linux_amd64 s3/dissertation/binaries/debian/${DRONE_BRANCH}-linux-amd64 - - mc cp linux_arm64_v7 s3/dissertation/binaries/debian/${DRONE_BRANCH}-linux-arm64-v7 + - mc cp linux_amd64 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_amd64 + - mc cp linux_arm64_v7 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_arm64_v7 volumes: - name: cache -- 2.47.0 From 936031f863eee687463d5e30ded7be57361aef05 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Wed, 20 Jan 2021 13:55:14 +0000 Subject: [PATCH 035/121] signed dronefile --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index a5ba30e..d1e431e 100644 --- a/.drone.yml +++ b/.drone.yml @@ -59,6 +59,6 @@ volumes: --- kind: signature -hmac: 8c21312bcfbaa9b7d2d3c31b1a5c13b54b4ecd1da853aa66a8ed11d72154fcca +hmac: a25d1abe0f31592ed19ac1e114804fcef9b3e8a54f829714647127c220b38595 ... -- 2.47.0 From e28a633cdcbcd3e9f0312156bffa795802af6231 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Wed, 20 Jan 2021 14:33:34 +0000 Subject: [PATCH 036/121] arm v8 --- .drone.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.drone.yml b/.drone.yml index d1e431e..e7646e1 100644 --- a/.drone.yml +++ b/.drone.yml @@ -36,8 +36,8 @@ steps: - name: cache path: /go commands: - - GOOS=linux GOARCH=amd64 go build -o linux_amd64 - - GOOS=linux GOARCH=arm64 GOARM=7 go build -o linux_arm64_v7 + - GOOS=linux GOARCH=amd64 go build -o linux_amd64 + - GOOS=linux GOARCH=arm64 go build -o linux_arm64_v8 - name: upload image: minio/mc @@ -51,7 +51,7 @@ steps: commands: - mc alias set s3 http://10.20.0.25:3900 $${ACCESS_KEY} $${SECRET_KEY} - mc cp linux_amd64 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_amd64 - - mc cp linux_arm64_v7 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_arm64_v7 + - mc cp linux_arm64_v8 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_arm64_v8 volumes: - name: cache -- 2.47.0 From 64ea6e355092aaced582f0f99fd5a9e54d8c98db Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Wed, 20 Jan 2021 14:39:56 +0000 Subject: [PATCH 037/121] armv7 --- .drone.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.drone.yml b/.drone.yml index e7646e1..49d2953 100644 --- a/.drone.yml +++ b/.drone.yml @@ -36,8 +36,8 @@ steps: - name: cache path: /go commands: - - GOOS=linux GOARCH=amd64 go build -o linux_amd64 - - GOOS=linux GOARCH=arm64 go build -o linux_arm64_v8 + - GOOS=linux GOARCH=amd64 go build -o linux_amd64 + - GOOS=linux GOARCH=arm GOARM=7 go build -o linux_arm_v7 - name: upload image: minio/mc @@ -50,8 +50,8 @@ steps: from_secret: s3_secret_key commands: - mc alias set s3 http://10.20.0.25:3900 $${ACCESS_KEY} $${SECRET_KEY} - - mc cp linux_amd64 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_amd64 - - mc cp linux_arm64_v8 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_arm64_v8 + - mc cp linux_amd64 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_amd64 + - mc cp linux_arm_v7 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_arm_v7 volumes: - name: cache -- 2.47.0 From 7ebc684ce4309fbc32ab11c0c2675e17304a3c22 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Wed, 20 Jan 2021 14:41:58 +0000 Subject: [PATCH 038/121] signed dronefile --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index 49d2953..8a3147e 100644 --- a/.drone.yml +++ b/.drone.yml @@ -59,6 +59,6 @@ volumes: --- kind: signature -hmac: a25d1abe0f31592ed19ac1e114804fcef9b3e8a54f829714647127c220b38595 +hmac: a7c498332fbf43f422a68475c53daa0a65b7801004f09300e40275007dec9bee ... -- 2.47.0 From b870e2707d984c08aefe23d271941aecaf4d3249 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 23 Jan 2021 14:36:44 +0000 Subject: [PATCH 039/121] fixed udp security --- config/builder.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/config/builder.go b/config/builder.go index 0f4fce4..35b2e69 100644 --- a/config/builder.go +++ b/config/builder.go @@ -71,7 +71,7 @@ func (c Configuration) Build() (*proxy.Proxy, error) { return p, nil } -func buildTcp(p *proxy.Proxy, peer Peer, v func() proxy.MacGenerator, g func() proxy.MacVerifier) error { +func buildTcp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() proxy.MacVerifier) error { if peer.RemoteHost != "" { f, err := tcp.InitiateFlow( fmt.Sprintf("%s:", peer.LocalHost), @@ -82,13 +82,13 @@ func buildTcp(p *proxy.Proxy, peer Peer, v func() proxy.MacGenerator, g func() p return err } - p.AddConsumer(f, v()) - p.AddProducer(f, g()) + p.AddConsumer(f, g()) + p.AddProducer(f, v()) return nil } - err := tcp.NewListener(p, fmt.Sprintf("%s:%d", peer.LocalHost, peer.LocalPort), g, v) + err := tcp.NewListener(p, fmt.Sprintf("%s:%d", peer.LocalHost, peer.LocalPort), v, g) if err != nil { return err } @@ -96,7 +96,7 @@ func buildTcp(p *proxy.Proxy, peer Peer, v func() proxy.MacGenerator, g func() p return nil } -func buildUdp(p *proxy.Proxy, peer Peer, v func() proxy.MacGenerator, g func() proxy.MacVerifier) error { +func buildUdp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() proxy.MacVerifier) error { var c func() udp.Congestion switch peer.Congestion { case "None": @@ -111,8 +111,8 @@ func buildUdp(p *proxy.Proxy, peer Peer, v func() proxy.MacGenerator, g func() p f, err := udp.InitiateFlow( fmt.Sprintf("%s:", peer.LocalHost), fmt.Sprintf("%s:%d", peer.RemoteHost, peer.RemotePort), - crypto.None{}, - crypto.None{}, + v(), + g(), c(), time.Duration(peer.KeepAlive)*time.Second, ) @@ -121,13 +121,13 @@ func buildUdp(p *proxy.Proxy, peer Peer, v func() proxy.MacGenerator, g func() p return err } - p.AddConsumer(f, v()) - p.AddProducer(f, g()) + p.AddConsumer(f, g()) + p.AddProducer(f, v()) return nil } - err := udp.NewListener(p, fmt.Sprintf("%s:%d", peer.LocalHost, peer.LocalPort), g, v, c) + err := udp.NewListener(p, fmt.Sprintf("%s:%d", peer.LocalHost, peer.LocalPort), v, g, c) if err != nil { return err } -- 2.47.0 From c34a47550fc21fb5c57003ad64884ad30067f761 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 23 Jan 2021 15:59:37 +0000 Subject: [PATCH 040/121] cleanup --- config/builder.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/config/builder.go b/config/builder.go index 35b2e69..b7995b9 100644 --- a/config/builder.go +++ b/config/builder.go @@ -56,13 +56,11 @@ func (c Configuration) Build() (*proxy.Proxy, error) { for _, peer := range c.Peers { switch peer.Method { case "TCP": - err := buildTcp(p, peer, g, v) - if err != nil { + if err := buildTcp(p, peer, g, v); err != nil { return nil, err } case "UDP": - err := buildUdp(p, peer, g, v) - if err != nil { + if err := buildUdp(p, peer, g, v); err != nil { return nil, err } } -- 2.47.0 From bb599056787cc1a1a460eeea409bd5c05b39533a Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 23 Jan 2021 16:07:16 +0000 Subject: [PATCH 041/121] single packet buffer size --- tcp/flow.go | 3 +-- tcp/listener.go | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tcp/flow.go b/tcp/flow.go index 057b233..7d441c2 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -75,8 +75,7 @@ func (f *InitiatedFlow) Reconnect() error { return err } - err = conn.SetWriteBuffer(0) - if err != nil { + if err := conn.SetWriteBuffer(20); err != nil { return err } diff --git a/tcp/listener.go b/tcp/listener.go index 2ca2e3e..3fc3674 100644 --- a/tcp/listener.go +++ b/tcp/listener.go @@ -24,8 +24,7 @@ func NewListener(p *proxy.Proxy, local string, v func() proxy.MacVerifier, g fun panic(err) } - err = conn.SetWriteBuffer(0) - if err != nil { + if err := conn.SetWriteBuffer(20); err != nil { panic(err) } -- 2.47.0 From 8c4ee6c6d92914b6e3d5701a36fa70bb6100686a Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 23 Jan 2021 17:13:06 +0000 Subject: [PATCH 042/121] localhost interface --- config/config.go | 55 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index 163eb48..1f63079 100644 --- a/config/config.go +++ b/config/config.go @@ -1,9 +1,36 @@ package config -import "github.com/go-playground/validator/v10" +import ( + "github.com/go-playground/validator/v10" + "log" + "net" +) var v = validator.New() +func init() { + if err := v.RegisterValidation("iface", func(fl validator.FieldLevel) bool { + name, ok := fl.Field().Interface().(string) + if ok { + ifaces, err := net.Interfaces() + if err != nil { + log.Printf("error getting interfaces: %v", err) + return false + } + + for _, i := range ifaces { + if i.Name == name { + return true + } + } + } + + return false + }); err != nil { + + } +} + type Configuration struct { Host Host Peers []Peer `validate:"dive"` @@ -19,7 +46,7 @@ type Host struct { type Peer struct { Method string `validate:"oneof=TCP UDP"` - LocalHost string `validate:"omitempty,ip"` + LocalHost string `validate:"omitempty,ip|iface"` LocalPort uint `validate:"max=65535"` RemoteHost string `validate:"required_with=RemotePort,omitempty,fqdn|ip"` @@ -32,6 +59,30 @@ type Peer struct { RetryWait uint } +func (p Peer) GetLocalHost() string { + if err := v.Var(p.LocalHost, "ip"); err == nil { + return p.LocalHost + } + + iface, err := net.InterfaceByName(p.LocalHost) + if err != nil { + panic(err) + } + + if iface != nil { + addrs, err := iface.Addrs() + if err != nil { + panic(err) + } + + if len(addrs) > 0 { + return addrs[0].String() + } + } + + return "invalid" +} + func (c Configuration) Validate() error { return v.Struct(c) } -- 2.47.0 From a096874bd73319c8d1721ba8f47f85af2ec5b099 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 23 Jan 2021 17:20:52 +0000 Subject: [PATCH 043/121] resolve log --- config/config.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 1f63079..0dad3d3 100644 --- a/config/config.go +++ b/config/config.go @@ -76,7 +76,9 @@ func (p Peer) GetLocalHost() string { } if len(addrs) > 0 { - return addrs[0].String() + addr := addrs[0].String() + log.Printf("resolved interface `%s` to `%v`", p.LocalHost, addr) + return addr } } -- 2.47.0 From c331a323a2da15967dde6e9ee2a81f059b2312ef Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 23 Jan 2021 17:23:05 +0000 Subject: [PATCH 044/121] uses getlocalhost --- config/builder.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/builder.go b/config/builder.go index b7995b9..c7bc089 100644 --- a/config/builder.go +++ b/config/builder.go @@ -72,7 +72,7 @@ func (c Configuration) Build() (*proxy.Proxy, error) { func buildTcp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() proxy.MacVerifier) error { if peer.RemoteHost != "" { f, err := tcp.InitiateFlow( - fmt.Sprintf("%s:", peer.LocalHost), + fmt.Sprintf("%s:", peer.GetLocalHost()), fmt.Sprintf("%s:%d", peer.RemoteHost, peer.RemotePort), ) @@ -86,7 +86,7 @@ func buildTcp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() p return nil } - err := tcp.NewListener(p, fmt.Sprintf("%s:%d", peer.LocalHost, peer.LocalPort), v, g) + err := tcp.NewListener(p, fmt.Sprintf("%s:%d", peer.GetLocalHost(), peer.LocalPort), v, g) if err != nil { return err } @@ -107,7 +107,7 @@ func buildUdp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() p if peer.RemoteHost != "" { f, err := udp.InitiateFlow( - fmt.Sprintf("%s:", peer.LocalHost), + fmt.Sprintf("%s:", peer.GetLocalHost()), fmt.Sprintf("%s:%d", peer.RemoteHost, peer.RemotePort), v(), g(), @@ -125,7 +125,7 @@ func buildUdp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() p return nil } - err := udp.NewListener(p, fmt.Sprintf("%s:%d", peer.LocalHost, peer.LocalPort), v, g, c) + err := udp.NewListener(p, fmt.Sprintf("%s:%d", peer.GetLocalHost(), peer.LocalPort), v, g, c) if err != nil { return err } -- 2.47.0 From 151b608b9bb026013563bfb4c71abc435adccb5c Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 23 Jan 2021 17:39:52 +0000 Subject: [PATCH 045/121] regular local evaluation --- config/builder.go | 4 ++-- tcp/flow.go | 6 +++--- udp/flow.go | 7 ++++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/config/builder.go b/config/builder.go index c7bc089..50c797d 100644 --- a/config/builder.go +++ b/config/builder.go @@ -72,7 +72,7 @@ func (c Configuration) Build() (*proxy.Proxy, error) { func buildTcp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() proxy.MacVerifier) error { if peer.RemoteHost != "" { f, err := tcp.InitiateFlow( - fmt.Sprintf("%s:", peer.GetLocalHost()), + func() string { return fmt.Sprintf("%s:", peer.GetLocalHost()) }, fmt.Sprintf("%s:%d", peer.RemoteHost, peer.RemotePort), ) @@ -107,7 +107,7 @@ func buildUdp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() p if peer.RemoteHost != "" { f, err := udp.InitiateFlow( - fmt.Sprintf("%s:", peer.GetLocalHost()), + func() string { return fmt.Sprintf("%s:", peer.GetLocalHost()) }, fmt.Sprintf("%s:%d", peer.RemoteHost, peer.RemotePort), v(), g(), diff --git a/tcp/flow.go b/tcp/flow.go index 057b233..b6e072b 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -22,7 +22,7 @@ type Conn interface { } type InitiatedFlow struct { - Local string + Local func() string Remote string mu sync.RWMutex @@ -43,7 +43,7 @@ func (f Flow) String() string { return fmt.Sprintf("TcpInbound{%v -> %v}", f.conn.RemoteAddr(), f.conn.LocalAddr()) } -func InitiateFlow(local, remote string) (*InitiatedFlow, error) { +func InitiateFlow(local func() string, remote string) (*InitiatedFlow, error) { f := InitiatedFlow{ Local: local, Remote: remote, @@ -60,7 +60,7 @@ func (f *InitiatedFlow) Reconnect() error { return nil } - localAddr, err := net.ResolveTCPAddr("tcp", f.Local) + localAddr, err := net.ResolveTCPAddr("tcp", f.Local()) if err != nil { return err } diff --git a/udp/flow.go b/udp/flow.go index d8c842e..825da6a 100644 --- a/udp/flow.go +++ b/udp/flow.go @@ -23,7 +23,7 @@ type PacketConn interface { } type InitiatedFlow struct { - Local string + Local func() string Remote string g proxy.MacGenerator @@ -55,7 +55,8 @@ func (f Flow) String() string { } func InitiateFlow( - local, remote string, + local func() string, + remote string, v proxy.MacVerifier, g proxy.MacGenerator, c Congestion, @@ -88,7 +89,7 @@ func (f *InitiatedFlow) Reconnect() error { return nil } - localAddr, err := net.ResolveUDPAddr("udp", f.Local) + localAddr, err := net.ResolveUDPAddr("udp", f.Local()) if err != nil { return err } -- 2.47.0 From 0b4c26005febf20468ee16891aaa9e02041a4c4b Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 23 Jan 2021 17:42:17 +0000 Subject: [PATCH 046/121] strip netmask --- config/config.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/config.go b/config/config.go index 0dad3d3..d8956d0 100644 --- a/config/config.go +++ b/config/config.go @@ -4,6 +4,7 @@ import ( "github.com/go-playground/validator/v10" "log" "net" + "strings" ) var v = validator.New() @@ -77,6 +78,7 @@ func (p Peer) GetLocalHost() string { if len(addrs) > 0 { addr := addrs[0].String() + addr = strings.Split(addr, "/")[0] log.Printf("resolved interface `%s` to `%v`", p.LocalHost, addr) return addr } -- 2.47.0 From 0849ed1b7ce9fadf99b2793ab2973906e14d17ec Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 23 Jan 2021 17:43:02 +0000 Subject: [PATCH 047/121] fixed function calls --- tcp/flow.go | 2 +- udp/flow.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tcp/flow.go b/tcp/flow.go index b6e072b..849f3bf 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -31,7 +31,7 @@ type InitiatedFlow struct { } func (f *InitiatedFlow) String() string { - return fmt.Sprintf("TcpOutbound{%v -> %v}", f.Local, f.Remote) + return fmt.Sprintf("TcpOutbound{%v -> %v}", f.Local(), f.Remote) } type Flow struct { diff --git a/udp/flow.go b/udp/flow.go index 825da6a..19c47d5 100644 --- a/udp/flow.go +++ b/udp/flow.go @@ -34,7 +34,7 @@ type InitiatedFlow struct { } func (f *InitiatedFlow) String() string { - return fmt.Sprintf("UdpOutbound{%v -> %v}", f.Local, f.Remote) + return fmt.Sprintf("UdpOutbound{%v -> %v}", f.Local(), f.Remote) } type Flow struct { -- 2.47.0 From 96ef7f4cfeabc0ceef7ae5caa6b5662154cfde23 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 23 Jan 2021 17:46:01 +0000 Subject: [PATCH 048/121] missed panic --- config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index d8956d0..8cbb77e 100644 --- a/config/config.go +++ b/config/config.go @@ -28,7 +28,7 @@ func init() { return false }); err != nil { - + panic(err) } } -- 2.47.0 From 061a5b70f7d4ea5c93d2ba60da90c59f663cf332 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 23 Jan 2021 18:24:04 +0000 Subject: [PATCH 049/121] tcp double threading --- tcp/flow.go | 131 ++++++++++++++++++++++++++++++++++------------- tcp/flow_test.go | 6 +-- tcp/listener.go | 2 +- 3 files changed, 98 insertions(+), 41 deletions(-) diff --git a/tcp/flow.go b/tcp/flow.go index 4d571a8..d095556 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -37,16 +37,51 @@ func (f *InitiatedFlow) String() string { type Flow struct { conn Conn isAlive bool + + toConsume, produced chan []byte + consumeErrors, produceErrors chan error +} + +func NewFlow() Flow { + return Flow{ + toConsume: make(chan []byte), + produced: make(chan []byte), + consumeErrors: make(chan error), + produceErrors: make(chan error), + } +} + +func NewFlowConn(conn Conn) Flow { + f := Flow{ + conn: conn, + isAlive: true, + + toConsume: make(chan []byte), + produced: make(chan []byte), + consumeErrors: make(chan error), + produceErrors: make(chan error), + } + + go f.produceMarshalled() + go f.consumeMarshalled() + + return f } func (f Flow) String() string { return fmt.Sprintf("TcpInbound{%v -> %v}", f.conn.RemoteAddr(), f.conn.LocalAddr()) } +func (f *Flow) IsAlive() bool { + return f.isAlive +} + func InitiateFlow(local func() string, remote string) (*InitiatedFlow, error) { f := InitiatedFlow{ Local: local, Remote: remote, + + Flow: NewFlow(), } return &f, nil @@ -81,6 +116,10 @@ func (f *InitiatedFlow) Reconnect() error { f.conn = conn f.isAlive = true + + go f.produceMarshalled() + go f.consumeMarshalled() + return nil } @@ -98,10 +137,6 @@ func (f *InitiatedFlow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { return f.Flow.Produce(v) } -func (f *Flow) IsAlive() bool { - return f.isAlive -} - func (f *Flow) Consume(p proxy.Packet, g proxy.MacGenerator) (err error) { if !f.isAlive { return shared.ErrDeadConnection @@ -110,24 +145,21 @@ func (f *Flow) Consume(p proxy.Packet, g proxy.MacGenerator) (err error) { marshalled := p.Marshal() data := proxy.AppendMac(marshalled, g) - err = f.consumeMarshalled(data) - if err != nil { - f.isAlive = false - } - return -} - -func (f *Flow) consumeMarshalled(data []byte) error { prefixedData := make([]byte, len(data)+4) binary.LittleEndian.PutUint32(prefixedData, uint32(len(data))) copy(prefixedData[4:], data) - err := f.conn.SetWriteDeadline(time.Now().Add(5 * time.Second)) - if err != nil { - return err + f.toConsume <- prefixedData + + select { + case err = <-f.consumeErrors: + default: } - _, err = f.conn.Write(prefixedData) - return err + + if err != nil { + f.isAlive = false + } + return } func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { @@ -135,8 +167,11 @@ func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { return nil, shared.ErrDeadConnection } - data, err := f.produceMarshalled() - if err != nil { + var data []byte + + select { + case data = <-f.produced: + case err := <-f.produceErrors: f.isAlive = false return nil, err } @@ -149,25 +184,47 @@ func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { return proxy.SimplePacket(b), nil } -func (f *Flow) produceMarshalled() ([]byte, error) { - lengthBytes := make([]byte, 4) - if n, err := io.LimitReader(f.conn, 4).Read(lengthBytes); err != nil { - return nil, err - } else if n != 4 { - return nil, shared.ErrNotEnoughBytes - } +func (f *Flow) consumeMarshalled() { + for { + data := <-f.toConsume - length := binary.LittleEndian.Uint32(lengthBytes) - dataBytes := make([]byte, length) - - var read uint32 - for read < length { - if n, err := io.LimitReader(f.conn, int64(length-read)).Read(dataBytes[read:]); err != nil { - return nil, err - } else { - read += uint32(n) + err := f.conn.SetWriteDeadline(time.Now().Add(5 * time.Second)) + if err != nil { + f.consumeErrors <- err + return + } + _, err = f.conn.Write(data) + if err != nil { + f.consumeErrors <- err + return } } - - return dataBytes, nil +} + +func (f *Flow) produceMarshalled() { + for { + lengthBytes := make([]byte, 4) + if n, err := io.LimitReader(f.conn, 4).Read(lengthBytes); err != nil { + f.produceErrors <- err + return + } else if n != 4 { + f.produceErrors <- shared.ErrNotEnoughBytes + return + } + + length := binary.LittleEndian.Uint32(lengthBytes) + dataBytes := make([]byte, length) + + var read uint32 + for read < length { + if n, err := io.LimitReader(f.conn, int64(length-read)).Read(dataBytes[read:]); err != nil { + f.produceErrors <- err + return + } else { + read += uint32(n) + } + } + + f.produced <- dataBytes + } } diff --git a/tcp/flow_test.go b/tcp/flow_test.go index 17214e0..f0f3e21 100644 --- a/tcp/flow_test.go +++ b/tcp/flow_test.go @@ -17,7 +17,7 @@ func TestFlow_Consume(t *testing.T) { t.Run("Length", func(t *testing.T) { testConn := mocks.NewMockPerfectBiStreamConn(100) - flowA := Flow{conn: testConn.SideA(), isAlive: true} + flowA := NewFlowConn(testConn.SideA()) err := flowA.Consume(testPacket, testMac) require.Nil(t, err) @@ -41,7 +41,7 @@ func TestFlow_Produce(t *testing.T) { t.Run("Length", func(t *testing.T) { testConn := mocks.NewMockPerfectBiStreamConn(100) - flowA := Flow{conn: testConn.SideA(), isAlive: true} + flowA := NewFlowConn(testConn.SideA()) _, err := testConn.SideB().Write(testMarshalled) require.Nil(t, err) @@ -54,7 +54,7 @@ func TestFlow_Produce(t *testing.T) { t.Run("Value", func(t *testing.T) { testConn := mocks.NewMockPerfectBiStreamConn(100) - flowA := Flow{conn: testConn.SideA(), isAlive: true} + flowA := NewFlowConn(testConn.SideA()) _, err := testConn.SideB().Write(testMarshalled) require.Nil(t, err) diff --git a/tcp/listener.go b/tcp/listener.go index 3fc3674..e1427f5 100644 --- a/tcp/listener.go +++ b/tcp/listener.go @@ -28,7 +28,7 @@ func NewListener(p *proxy.Proxy, local string, v func() proxy.MacVerifier, g fun panic(err) } - f := Flow{conn: conn, isAlive: true} + f := NewFlowConn(conn) log.Printf("received new tcp connection: %v\n", f) -- 2.47.0 From 50e58454c354580233e0d111c2d83dbe0b13b661 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 23 Jan 2021 18:26:20 +0000 Subject: [PATCH 050/121] reset buffers --- tcp/flow.go | 6 +++--- tcp/listener.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tcp/flow.go b/tcp/flow.go index d095556..663d06e 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -110,9 +110,9 @@ func (f *InitiatedFlow) Reconnect() error { return err } - if err := conn.SetWriteBuffer(20); err != nil { - return err - } + //if err := conn.SetWriteBuffer(20); err != nil { + // return err + //} f.conn = conn f.isAlive = true diff --git a/tcp/listener.go b/tcp/listener.go index e1427f5..50d4abf 100644 --- a/tcp/listener.go +++ b/tcp/listener.go @@ -24,9 +24,9 @@ func NewListener(p *proxy.Proxy, local string, v func() proxy.MacVerifier, g fun panic(err) } - if err := conn.SetWriteBuffer(20); err != nil { - panic(err) - } + //if err := conn.SetWriteBuffer(20); err != nil { + // panic(err) + //} f := NewFlowConn(conn) -- 2.47.0 From 18d3b23658bfc1509be2a95c1d8763d7e382de8d Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 23 Jan 2021 18:33:52 +0000 Subject: [PATCH 051/121] delay profiling --- tcp/flow.go | 17 ++++++++++++++--- tcp/listener.go | 6 +++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/tcp/flow.go b/tcp/flow.go index 663d06e..af9d8c2 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "fmt" "io" + "log" "mpbl3p/proxy" "mpbl3p/shared" "net" @@ -110,9 +111,9 @@ func (f *InitiatedFlow) Reconnect() error { return err } - //if err := conn.SetWriteBuffer(20); err != nil { - // return err - //} + if err := conn.SetWriteBuffer(20); err != nil { + return err + } f.conn = conn f.isAlive = true @@ -186,7 +187,9 @@ func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { func (f *Flow) consumeMarshalled() { for { + t1 := time.Now() data := <-f.toConsume + t2 := time.Now() err := f.conn.SetWriteDeadline(time.Now().Add(5 * time.Second)) if err != nil { @@ -198,11 +201,15 @@ func (f *Flow) consumeMarshalled() { f.consumeErrors <- err return } + + t3 := time.Now() + log.Printf("consumer: `%dns` spent waiting, `%dns` spent consuming", t2.Sub(t1).Nanoseconds(), t3.Sub(t2).Nanoseconds()) } } func (f *Flow) produceMarshalled() { for { + t1 := time.Now() lengthBytes := make([]byte, 4) if n, err := io.LimitReader(f.conn, 4).Read(lengthBytes); err != nil { f.produceErrors <- err @@ -225,6 +232,10 @@ func (f *Flow) produceMarshalled() { } } + t2 := time.Now() f.produced <- dataBytes + t3 := time.Now() + + log.Printf("producer: `%dns` spent producing, `%dns` spent waiting", t2.Sub(t1).Nanoseconds(), t3.Sub(t2).Nanoseconds()) } } diff --git a/tcp/listener.go b/tcp/listener.go index 50d4abf..e1427f5 100644 --- a/tcp/listener.go +++ b/tcp/listener.go @@ -24,9 +24,9 @@ func NewListener(p *proxy.Proxy, local string, v func() proxy.MacVerifier, g fun panic(err) } - //if err := conn.SetWriteBuffer(20); err != nil { - // panic(err) - //} + if err := conn.SetWriteBuffer(20); err != nil { + panic(err) + } f := NewFlowConn(conn) -- 2.47.0 From 1e0bda09f4c53b7d2aa2860e8aba08532086ab15 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 23 Jan 2021 18:39:01 +0000 Subject: [PATCH 052/121] buffered reader --- tcp/flow.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tcp/flow.go b/tcp/flow.go index af9d8c2..1732e0b 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -1,6 +1,7 @@ package tcp import ( + "bufio" "encoding/binary" "fmt" "io" @@ -208,10 +209,12 @@ func (f *Flow) consumeMarshalled() { } func (f *Flow) produceMarshalled() { + buf := bufio.NewReader(f.conn) + for { t1 := time.Now() lengthBytes := make([]byte, 4) - if n, err := io.LimitReader(f.conn, 4).Read(lengthBytes); err != nil { + if n, err := io.LimitReader(buf, 4).Read(lengthBytes); err != nil { f.produceErrors <- err return } else if n != 4 { @@ -224,7 +227,7 @@ func (f *Flow) produceMarshalled() { var read uint32 for read < length { - if n, err := io.LimitReader(f.conn, int64(length-read)).Read(dataBytes[read:]); err != nil { + if n, err := io.LimitReader(buf, int64(length-read)).Read(dataBytes[read:]); err != nil { f.produceErrors <- err return } else { -- 2.47.0 From f90d65b11e47929f4b181eb2ad9399dbb778fe12 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 23 Jan 2021 18:41:11 +0000 Subject: [PATCH 053/121] packet buffer --- config/builder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/builder.go b/config/builder.go index 50c797d..691f565 100644 --- a/config/builder.go +++ b/config/builder.go @@ -14,7 +14,7 @@ import ( ) func (c Configuration) Build() (*proxy.Proxy, error) { - p := proxy.NewProxy(0) + p := proxy.NewProxy(32) var g func() proxy.MacGenerator var v func() proxy.MacVerifier -- 2.47.0 From 3b7eacce397edd68778f9b5c0ecc19eff960decc Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 23 Jan 2021 20:36:07 +0000 Subject: [PATCH 054/121] massive read buffer --- tcp/flow.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcp/flow.go b/tcp/flow.go index 1732e0b..5436883 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -209,7 +209,7 @@ func (f *Flow) consumeMarshalled() { } func (f *Flow) produceMarshalled() { - buf := bufio.NewReader(f.conn) + buf := bufio.NewReaderSize(f.conn, 100000) for { t1 := time.Now() -- 2.47.0 From 3291eb4a6ce0658c849146118808a2be59138b8b Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 23 Jan 2021 20:46:06 +0000 Subject: [PATCH 055/121] reset write buffer --- tcp/flow.go | 8 ++++---- tcp/listener.go | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tcp/flow.go b/tcp/flow.go index 5436883..924b80e 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -112,9 +112,9 @@ func (f *InitiatedFlow) Reconnect() error { return err } - if err := conn.SetWriteBuffer(20); err != nil { - return err - } + //if err := conn.SetWriteBuffer(20); err != nil { + // return err + //} f.conn = conn f.isAlive = true @@ -209,7 +209,7 @@ func (f *Flow) consumeMarshalled() { } func (f *Flow) produceMarshalled() { - buf := bufio.NewReaderSize(f.conn, 100000) + buf := bufio.NewReader(f.conn) for { t1 := time.Now() diff --git a/tcp/listener.go b/tcp/listener.go index e1427f5..50d4abf 100644 --- a/tcp/listener.go +++ b/tcp/listener.go @@ -24,9 +24,9 @@ func NewListener(p *proxy.Proxy, local string, v func() proxy.MacVerifier, g fun panic(err) } - if err := conn.SetWriteBuffer(20); err != nil { - panic(err) - } + //if err := conn.SetWriteBuffer(20); err != nil { + // panic(err) + //} f := NewFlowConn(conn) -- 2.47.0 From 5346e5e94db94da80e51f17d97332d76b1a922b7 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 23 Jan 2021 20:52:37 +0000 Subject: [PATCH 056/121] huge write buffers --- tcp/flow.go | 6 +++--- tcp/listener.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tcp/flow.go b/tcp/flow.go index 924b80e..e08528b 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -112,9 +112,9 @@ func (f *InitiatedFlow) Reconnect() error { return err } - //if err := conn.SetWriteBuffer(20); err != nil { - // return err - //} + if err := conn.SetWriteBuffer(200*1024); err != nil { + return err + } f.conn = conn f.isAlive = true diff --git a/tcp/listener.go b/tcp/listener.go index 50d4abf..0058154 100644 --- a/tcp/listener.go +++ b/tcp/listener.go @@ -24,9 +24,9 @@ func NewListener(p *proxy.Proxy, local string, v func() proxy.MacVerifier, g fun panic(err) } - //if err := conn.SetWriteBuffer(20); err != nil { - // panic(err) - //} + if err := conn.SetWriteBuffer(200*1024); err != nil { + panic(err) + } f := NewFlowConn(conn) -- 2.47.0 From fbe30df88ca244d081f189d38d7ad1af4309976c Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 23 Jan 2021 20:53:27 +0000 Subject: [PATCH 057/121] formatting --- tcp/flow.go | 2 +- tcp/listener.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tcp/flow.go b/tcp/flow.go index e08528b..b7813aa 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -112,7 +112,7 @@ func (f *InitiatedFlow) Reconnect() error { return err } - if err := conn.SetWriteBuffer(200*1024); err != nil { + if err := conn.SetWriteBuffer(200 * 1024); err != nil { return err } diff --git a/tcp/listener.go b/tcp/listener.go index 0058154..0917116 100644 --- a/tcp/listener.go +++ b/tcp/listener.go @@ -24,7 +24,7 @@ func NewListener(p *proxy.Proxy, local string, v func() proxy.MacVerifier, g fun panic(err) } - if err := conn.SetWriteBuffer(200*1024); err != nil { + if err := conn.SetWriteBuffer(200 * 1024); err != nil { panic(err) } -- 2.47.0 From d15be09654d8c79ff691328780cbd0c34e924f1c Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Mon, 25 Jan 2021 19:38:39 +0000 Subject: [PATCH 058/121] zero write buffers --- tcp/flow.go | 2 +- tcp/listener.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tcp/flow.go b/tcp/flow.go index b7813aa..b23afc4 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -112,7 +112,7 @@ func (f *InitiatedFlow) Reconnect() error { return err } - if err := conn.SetWriteBuffer(200 * 1024); err != nil { + if err := conn.SetWriteBuffer(0); err != nil { return err } diff --git a/tcp/listener.go b/tcp/listener.go index 0917116..7f32a3a 100644 --- a/tcp/listener.go +++ b/tcp/listener.go @@ -24,7 +24,7 @@ func NewListener(p *proxy.Proxy, local string, v func() proxy.MacVerifier, g fun panic(err) } - if err := conn.SetWriteBuffer(200 * 1024); err != nil { + if err := conn.SetWriteBuffer(0); err != nil { panic(err) } -- 2.47.0 From ebab98971b5fd06d36d2c51df8ec031f8f1ec40d Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Tue, 26 Jan 2021 18:47:23 +0000 Subject: [PATCH 059/121] reset proxy buffers --- config/builder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/builder.go b/config/builder.go index 691f565..50c797d 100644 --- a/config/builder.go +++ b/config/builder.go @@ -14,7 +14,7 @@ import ( ) func (c Configuration) Build() (*proxy.Proxy, error) { - p := proxy.NewProxy(32) + p := proxy.NewProxy(0) var g func() proxy.MacGenerator var v func() proxy.MacVerifier -- 2.47.0 From c9d68d4d2f9be4e1aeac53753496c4860c1e6aa4 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Mon, 22 Feb 2021 15:10:01 +0000 Subject: [PATCH 060/121] wip: adding test for newreno --- udp/congestion/newreno.go | 6 +- udp/congestion/newreno_test.go | 155 +++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 udp/congestion/newreno_test.go diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index 2cf8681..aa94209 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -50,8 +50,8 @@ func NewNewReno() *NewReno { outboundTimes: make(map[uint32]time.Time), inboundTimes: make(map[uint32]time.Time), - windowSize: 8, - rtt: (1 * time.Millisecond).Seconds(), + windowSize: 1, + rtt: (10 * time.Millisecond).Seconds(), slowStart: true, } @@ -213,7 +213,7 @@ func (c *NewReno) NextNack() uint32 { func (c *NewReno) AwaitEarlyUpdate(keepalive time.Duration) uint32 { for { - rtt := time.Duration(math.Round(c.rtt * float64(time.Second))) + rtt := time.Duration(math.Round(c.rtt) * float64(time.Second)) time.Sleep(rtt) c.updateAckNack() diff --git a/udp/congestion/newreno_test.go b/udp/congestion/newreno_test.go new file mode 100644 index 0000000..11154ba --- /dev/null +++ b/udp/congestion/newreno_test.go @@ -0,0 +1,155 @@ +package congestion + +import ( + "context" + "fmt" + "testing" + "time" +) + +type congestionPacket struct { + seq, nack, ack uint32 +} + +type newRenoTest struct { + sideA, sideB *NewReno + + aOutbound, bOutbound chan congestionPacket + aInbound, bInbound chan congestionPacket + + rtt time.Duration +} + +func newNewRenoTest(rtt time.Duration) *newRenoTest { + return &newRenoTest{ + sideA: NewNewReno(), + sideB: NewNewReno(), + + aOutbound: make(chan congestionPacket), + bOutbound: make(chan congestionPacket), + + aInbound: make(chan congestionPacket), + bInbound: make(chan congestionPacket), + + rtt: rtt, + } +} + +func (n *newRenoTest) Start(ctx context.Context) { + go func() { + for { + select { + case <-ctx.Done(): + return + case p := <-n.aOutbound: + time.AfterFunc(n.rtt/2, func() { + n.bInbound <- p + }) + case p := <-n.bOutbound: + time.AfterFunc(n.rtt/2, func() { + n.aInbound <- p + }) + } + } + }() +} + +func (n *newRenoTest) RunSideA(ctx context.Context) { + go func() { + for { + select { + case <-ctx.Done(): + return + case p := <-n.aInbound: + n.sideA.ReceivedPacket(p.seq) + n.sideA.ReceivedAck(p.ack) + n.sideA.ReceivedNack(p.nack) + } + } + }() + + go func() { + for { + if ctx.Err() != nil { + return + } + + seq := n.sideA.AwaitEarlyUpdate(500 * time.Millisecond) + if seq != 0 { + p := congestionPacket{ + seq: seq, + nack: n.sideA.NextAck(), + ack: n.sideA.NextNack(), + } + n.aOutbound <- p + } + } + }() +} + +func (n *newRenoTest) RunSideB(ctx context.Context) { + go func() { + for { + select { + case <-ctx.Done(): + return + case p := <-n.bInbound: + n.sideB.ReceivedPacket(p.seq) + n.sideB.ReceivedAck(p.ack) + n.sideB.ReceivedNack(p.nack) + } + } + }() + + go func() { + for { + if ctx.Err() != nil { + return + } + + seq := n.sideB.AwaitEarlyUpdate(500 * time.Millisecond) + if seq != 0 { + p := congestionPacket{ + seq: seq, + nack: n.sideB.NextAck(), + ack: n.sideB.NextNack(), + } + n.bOutbound <- p + } + } + }() +} + +func TestNewReno_Congestion(t *testing.T) { + t.Run("OneWay", func(t *testing.T) { + t.Run("Lossless", func(t *testing.T) { + ctx := context.Background() + + var cancel context.CancelFunc + if d, ok := t.Deadline(); ok { + ctx, cancel = context.WithDeadline(ctx, d) + } else { + ctx, cancel = context.WithCancel(ctx) + } + defer cancel() + + c := newNewRenoTest(10 * time.Millisecond) + c.Start(ctx) + c.RunSideA(ctx) + c.RunSideB(ctx) + + for i := 0; i < 20; i++ { + t1 := time.Now() + seq := c.sideA.Sequence() + t2 := time.Now() + fmt.Printf("waited %dms for sequence\n", t2.Sub(t1).Milliseconds()) + + c.aOutbound <- congestionPacket{ + seq: seq, + nack: c.sideA.NextNack(), + ack: c.sideA.NextAck(), + } + } + }) + }) +} -- 2.47.0 From d0a23a38cbfc7702db99d79e12ebefd0d0bc078b Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Mon, 22 Feb 2021 15:53:16 +0000 Subject: [PATCH 061/121] simplified receivedpacket implementation --- udp/congestion.go | 7 ++----- udp/congestion/newreno.go | 32 ++++++++++++++++++-------------- udp/congestion/newreno_test.go | 8 ++------ udp/congestion/none.go | 4 +--- udp/flow.go | 14 ++------------ 5 files changed, 25 insertions(+), 40 deletions(-) diff --git a/udp/congestion.go b/udp/congestion.go index 705a193..8b4c5e1 100644 --- a/udp/congestion.go +++ b/udp/congestion.go @@ -4,14 +4,11 @@ import "time" type Congestion interface { Sequence() uint32 - ReceivedPacket(seq uint32) - - ReceivedAck(uint32) NextAck() uint32 - - ReceivedNack(uint32) NextNack() uint32 + ReceivedPacket(seq, nack, ack uint32) + AwaitEarlyUpdate(keepalive time.Duration) uint32 Reset() } diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index aa94209..ef8c506 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -80,8 +80,24 @@ func (c *NewReno) Reset() { c.hasAcked = false } +func (c *NewReno) ReceivedPacket(seq, nack, ack uint32) { + c.receivedSequence(seq) + c.receivedNack(nack) + c.receivedAck(ack) +} + +func (c *NewReno) receivedSequence(seq uint32) { + c.inboundTimes[seq] = time.Now() + + c.acksToSendLock.Lock() + c.acksToSend.Insert(seq) + c.acksToSendLock.Unlock() + + c.updateAckNack() +} + // It is assumed that ReceivedAck will only be called by one thread -func (c *NewReno) ReceivedAck(ack uint32) { +func (c *NewReno) receivedAck(ack uint32) { c.outboundTimesLock.Lock() defer c.outboundTimesLock.Unlock() @@ -120,7 +136,7 @@ func (c *NewReno) ReceivedAck(ack uint32) { } // It is assumed that ReceivedNack will only be called by one thread -func (c *NewReno) ReceivedNack(nack uint32) { +func (c *NewReno) receivedNack(nack uint32) { log.Printf("nack received for %d", nack) // TODO : Check for freshness @@ -132,18 +148,6 @@ func (c *NewReno) ReceivedNack(nack uint32) { } } -func (c *NewReno) ReceivedPacket(seq uint32) { - log.Printf("seq received for %d", seq) - - c.inboundTimes[seq] = time.Now() - - c.acksToSendLock.Lock() - c.acksToSend.Insert(seq) - c.acksToSendLock.Unlock() - - c.updateAckNack() -} - func (c *NewReno) updateAckNack() { c.acksToSendLock.Lock() defer c.acksToSendLock.Unlock() diff --git a/udp/congestion/newreno_test.go b/udp/congestion/newreno_test.go index 11154ba..1ca7bea 100644 --- a/udp/congestion/newreno_test.go +++ b/udp/congestion/newreno_test.go @@ -61,9 +61,7 @@ func (n *newRenoTest) RunSideA(ctx context.Context) { case <-ctx.Done(): return case p := <-n.aInbound: - n.sideA.ReceivedPacket(p.seq) - n.sideA.ReceivedAck(p.ack) - n.sideA.ReceivedNack(p.nack) + n.sideA.ReceivedPacket(p.seq, p.nack, p.ack) } } }() @@ -94,9 +92,7 @@ func (n *newRenoTest) RunSideB(ctx context.Context) { case <-ctx.Done(): return case p := <-n.bInbound: - n.sideB.ReceivedPacket(p.seq) - n.sideB.ReceivedAck(p.ack) - n.sideB.ReceivedNack(p.nack) + n.sideB.ReceivedPacket(p.seq, p.nack, p.ack) } } }() diff --git a/udp/congestion/none.go b/udp/congestion/none.go index 4640a04..d5679ea 100644 --- a/udp/congestion/none.go +++ b/udp/congestion/none.go @@ -34,9 +34,7 @@ func (c *None) String() string { return fmt.Sprintf("{None}") } -func (c *None) ReceivedPacket(uint32) {} -func (c *None) ReceivedAck(uint32) {} -func (c *None) ReceivedNack(uint32) {} +func (c *None) ReceivedPacket(seq, nack, ack uint32) {} func (c *None) Reset() {} func (c *None) NextNack() uint32 { return 0 } func (c *None) NextAck() uint32 { return 0 } diff --git a/udp/flow.go b/udp/flow.go index 19c47d5..e045767 100644 --- a/udp/flow.go +++ b/udp/flow.go @@ -217,18 +217,8 @@ func (f *Flow) produceInternal(v proxy.MacVerifier, mustReturn bool) (proxy.Pack return nil, err } - // schedule an ack for this sequence number - if p.seq != 0 { - f.congestion.ReceivedPacket(p.seq) - } - // adjust our sending congestion control based on their acks - if p.ack != 0 { - f.congestion.ReceivedAck(p.ack) - } - // adjust our sending congestion control based on their nacks - if p.nack != 0 { - f.congestion.ReceivedNack(p.nack) - } + // adjust congestion control based on this packet's congestion header + f.congestion.ReceivedPacket(p.seq, p.nack, p.ack) // 12 bytes for header + the MAC + a timestamp if len(b) == 12+f.v.CodeLength()+8 { -- 2.47.0 From 87c0b57502bf34d739d071a71335f8efd8bcb35f Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Mon, 22 Feb 2021 16:28:55 +0000 Subject: [PATCH 062/121] removed cc reset --- udp/congestion.go | 1 - udp/congestion/newreno.go | 108 ++++++++++++++++++-------------------- udp/congestion/none.go | 3 +- 3 files changed, 51 insertions(+), 61 deletions(-) diff --git a/udp/congestion.go b/udp/congestion.go index 8b4c5e1..ea2f7bb 100644 --- a/udp/congestion.go +++ b/udp/congestion.go @@ -10,5 +10,4 @@ type Congestion interface { ReceivedPacket(seq, nack, ack uint32) AwaitEarlyUpdate(keepalive time.Duration) uint32 - Reset() } diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index ef8c506..cd84ac4 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -11,6 +11,7 @@ import ( ) const RttExponentialFactor = 0.1 +const RttLossDelay = 1.5 type NewReno struct { sequence chan uint32 @@ -71,21 +72,61 @@ func NewNewReno() *NewReno { return &c } -func (c *NewReno) Reset() { - c.outboundTimes = make(map[uint32]time.Time) - c.inboundTimes = make(map[uint32]time.Time) - c.windowSize = 8 - c.rtt = (1 * time.Millisecond).Seconds() - c.slowStart = true - c.hasAcked = false -} - func (c *NewReno) ReceivedPacket(seq, nack, ack uint32) { c.receivedSequence(seq) c.receivedNack(nack) c.receivedAck(ack) } +func (c *NewReno) Sequence() uint32 { + c.outboundTimesLock.Lock() + defer c.outboundTimesLock.Unlock() + + for c.inFlight >= c.windowSize { + <-c.ackNotifier + } + atomic.AddInt32(&c.inFlight, 1) + + s := <-c.sequence + + n := time.Now() + c.lastSent = n + c.outboundTimes[s] = n + + return s +} + +func (c *NewReno) NextAck() uint32 { + a := c.ack + c.lastAck = a + return a +} + +func (c *NewReno) NextNack() uint32 { + n := c.nack + c.lastNack = n + return n +} + +func (c *NewReno) AwaitEarlyUpdate(keepalive time.Duration) uint32 { + for { + rtt := time.Duration(math.Round(c.rtt) * float64(time.Second)) + time.Sleep(rtt) + + c.updateAckNack() + + // CASE 1: waiting ACKs or NACKs and no message sent in the last RTT + if ((c.lastAck != c.ack) || (c.lastNack != c.nack)) && time.Now().After(c.lastSent.Add(rtt)) { + return 0 + } + + // CASE 3: No message sent within the keepalive time + if keepalive != 0 && time.Now().After(c.lastSent.Add(keepalive)) { + return c.Sequence() + } + } +} + func (c *NewReno) receivedSequence(seq uint32) { c.inboundTimes[seq] = time.Now() @@ -184,52 +225,3 @@ func (c *NewReno) updateAckNack() { atomic.StoreUint32(&c.ack, ack) } - -func (c *NewReno) Sequence() uint32 { - c.outboundTimesLock.Lock() - defer c.outboundTimesLock.Unlock() - - for c.inFlight >= c.windowSize { - <-c.ackNotifier - } - atomic.AddInt32(&c.inFlight, 1) - - s := <-c.sequence - - n := time.Now() - c.lastSent = n - c.outboundTimes[s] = n - - return s -} - -func (c *NewReno) NextAck() uint32 { - a := c.ack - c.lastAck = a - return a -} - -func (c *NewReno) NextNack() uint32 { - n := c.nack - c.lastNack = n - return n -} - -func (c *NewReno) AwaitEarlyUpdate(keepalive time.Duration) uint32 { - for { - rtt := time.Duration(math.Round(c.rtt) * float64(time.Second)) - time.Sleep(rtt) - - c.updateAckNack() - - // CASE 1: waiting ACKs or NACKs and no message sent in the last RTT - if ((c.lastAck != c.ack) || (c.lastNack != c.nack)) && time.Now().After(c.lastSent.Add(rtt)) { - return 0 - } - - // CASE 3: No message sent within the keepalive time - if keepalive != 0 && time.Now().After(c.lastSent.Add(keepalive)) { - return c.Sequence() - } - } -} diff --git a/udp/congestion/none.go b/udp/congestion/none.go index d5679ea..be7564f 100644 --- a/udp/congestion/none.go +++ b/udp/congestion/none.go @@ -34,8 +34,7 @@ func (c *None) String() string { return fmt.Sprintf("{None}") } -func (c *None) ReceivedPacket(seq, nack, ack uint32) {} -func (c *None) Reset() {} +func (c *None) ReceivedPacket(uint32, uint32, uint32) {} func (c *None) NextNack() uint32 { return 0 } func (c *None) NextAck() uint32 { return 0 } func (c *None) AwaitEarlyUpdate(time.Duration) uint32 { select {} } -- 2.47.0 From a222db615e243a4eff219131675e006bb7157fa0 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Mon, 22 Feb 2021 19:08:10 +0000 Subject: [PATCH 063/121] initial newreno rewrite --- udp/congestion/newreno.go | 296 +++++++++++++++++++-------------- udp/congestion/newreno_test.go | 10 ++ udp/flow.go | 4 +- 3 files changed, 186 insertions(+), 124 deletions(-) diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index cd84ac4..0faa157 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -2,9 +2,8 @@ package congestion import ( "fmt" - "log" "math" - "mpbl3p/utils" + "sort" "sync" "sync/atomic" "time" @@ -14,45 +13,53 @@ const RttExponentialFactor = 0.1 const RttLossDelay = 1.5 type NewReno struct { - sequence chan uint32 - keepalive chan bool + sequence chan uint32 - outboundTimes, inboundTimes map[uint32]time.Time - outboundTimesLock sync.Mutex - inboundTimesLock sync.RWMutex + inFlight []flightInfo + lastSent time.Time + inFlightMu sync.Mutex - ack, lastAck uint32 - nack, lastNack uint32 + awaitingAck sortableFlights + ack, nack uint32 + lastAck, lastNack uint32 + ackNackMu sync.Mutex - slowStart bool - rtt float64 - windowSize int32 - windowCount int32 - inFlight int32 + rttNanos float64 + windowSize, windowCount uint32 + slowStart bool + windowNotifier chan struct{} +} - ackNotifier chan struct{} +type flightInfo struct { + time time.Time + sequence uint32 +} - lastSent time.Time - hasAcked bool +type sortableFlights []flightInfo - acksToSend utils.Uint32Heap - acksToSendLock sync.Mutex +func (f sortableFlights) Len() int { + return len(f) +} + +func (f sortableFlights) Swap(i, j int) { + f[i], f[j] = f[j], f[i] +} + +func (f sortableFlights) Less(i, j int) bool { + return f[i].sequence < f[j].sequence } func (c *NewReno) String() string { - return fmt.Sprintf("{NewReno %t %d %d %d %d}", c.slowStart, c.windowSize, c.inFlight, c.lastAck, c.lastNack) + return fmt.Sprintf("{NewReno %t %d %d %d %d}", c.slowStart, c.windowSize, len(c.inFlight), c.lastAck, c.lastNack) } func NewNewReno() *NewReno { c := NewReno{ - sequence: make(chan uint32), - ackNotifier: make(chan struct{}), - - outboundTimes: make(map[uint32]time.Time), - inboundTimes: make(map[uint32]time.Time), + sequence: make(chan uint32), + windowNotifier: make(chan struct{}), windowSize: 1, - rtt: (10 * time.Millisecond).Seconds(), + rttNanos: float64((10 * time.Millisecond).Nanoseconds()), slowStart: true, } @@ -73,47 +80,55 @@ func NewNewReno() *NewReno { } func (c *NewReno) ReceivedPacket(seq, nack, ack uint32) { - c.receivedSequence(seq) - c.receivedNack(nack) - c.receivedAck(ack) + if seq != 0 { + c.receivedSequence(seq) + } + if nack != 0 { + c.receivedNack(nack) + } + if ack != 0 { + c.receivedAck(ack) + } } func (c *NewReno) Sequence() uint32 { - c.outboundTimesLock.Lock() - defer c.outboundTimesLock.Unlock() - - for c.inFlight >= c.windowSize { - <-c.ackNotifier + for len(c.inFlight) >= int(c.windowSize) { + <-c.windowNotifier } - atomic.AddInt32(&c.inFlight, 1) + + c.inFlightMu.Lock() + defer c.inFlightMu.Unlock() s := <-c.sequence + t := time.Now() - n := time.Now() - c.lastSent = n - c.outboundTimes[s] = n + c.inFlight = append(c.inFlight, flightInfo{ + time: t, + sequence: s, + }) + c.lastSent = t return s } func (c *NewReno) NextAck() uint32 { a := c.ack - c.lastAck = a + atomic.StoreUint32(&c.lastAck, a) return a } func (c *NewReno) NextNack() uint32 { n := c.nack - c.lastNack = n + atomic.StoreUint32(&c.lastNack, n) return n } func (c *NewReno) AwaitEarlyUpdate(keepalive time.Duration) uint32 { for { - rtt := time.Duration(math.Round(c.rtt) * float64(time.Second)) - time.Sleep(rtt) + rtt := time.Duration(math.Round(c.rttNanos)) + time.Sleep(rtt / 2) - c.updateAckNack() + c.checkNack() // CASE 1: waiting ACKs or NACKs and no message sent in the last RTT if ((c.lastAck != c.ack) || (c.lastNack != c.nack)) && time.Now().After(c.lastSent.Add(rtt)) { @@ -128,100 +143,139 @@ func (c *NewReno) AwaitEarlyUpdate(keepalive time.Duration) uint32 { } func (c *NewReno) receivedSequence(seq uint32) { - c.inboundTimes[seq] = time.Now() + c.ackNackMu.Lock() + defer c.ackNackMu.Unlock() - c.acksToSendLock.Lock() - c.acksToSend.Insert(seq) - c.acksToSendLock.Unlock() - - c.updateAckNack() -} - -// It is assumed that ReceivedAck will only be called by one thread -func (c *NewReno) receivedAck(ack uint32) { - c.outboundTimesLock.Lock() - defer c.outboundTimesLock.Unlock() - - log.Printf("ack received for %d", ack) - c.hasAcked = true - - // RTT - // Update using an exponential average - rtt := time.Now().Sub(c.outboundTimes[ack]).Seconds() - - delete(c.outboundTimes, ack) - c.rtt = c.rtt*(1-RttExponentialFactor) + rtt*RttExponentialFactor - - // Free Window - // TODO: Check for freshness - // TODO: Don't expect an ACK per packet - - atomic.AddInt32(&c.inFlight, -1) - select { - case c.ackNotifier <- struct{}{}: - default: + if !(c.ack != seq-1 && c.nack != seq-1) { + c.awaitingAck = append(c.awaitingAck, flightInfo{ + time: time.Now(), + sequence: seq, + }) + return // if this seq doesn't change the ack field, awaitingAck will be unchanged } - // GROW - // CASE: exponential. increase window size by one per ack - // CASE: standard. increase window size by one per window of acks - if c.slowStart { - atomic.AddInt32(&c.windowSize, 1) - } else { - c.windowCount++ - if c.windowCount == c.windowSize { - c.windowCount = 0 - atomic.AddInt32(&c.windowSize, 1) + c.ack = seq + sort.Sort(c.awaitingAck) + + a := c.ack + + var i int + var e flightInfo + + for i, e = range c.awaitingAck { + if a+1 == e.sequence { + a += 1 + } else { + break } } + + c.ack = a + c.awaitingAck = c.awaitingAck[i:] +} + +func (c *NewReno) checkNack() { + c.ackNackMu.Lock() + defer c.ackNackMu.Unlock() + + if len(c.awaitingAck) == 0 { + return + } + + sort.Sort(c.awaitingAck) + rtt := time.Duration(c.rttNanos * RttLossDelay) + + if !c.awaitingAck[0].time.Before(time.Now().Add(-rtt)) { + return + } + + c.nack = c.awaitingAck[0].sequence - 1 + + a := c.ack + + var i int + var e flightInfo + + for i, e = range c.awaitingAck { + if a+1 == e.sequence { + a += 1 + } else { + break + } + } + + c.ack = a + c.awaitingAck = c.awaitingAck[i:] } // It is assumed that ReceivedNack will only be called by one thread func (c *NewReno) receivedNack(nack uint32) { - log.Printf("nack received for %d", nack) + c.ackNackMu.Lock() + defer c.ackNackMu.Unlock() - // TODO : Check for freshness + c.inFlightMu.Lock() + defer c.inFlightMu.Unlock() - // End slow start + // as both ack and nack are cumulative, inflight will always be ordered by seq + var i int + for i < len(c.inFlight) && c.inFlight[i].sequence <= nack { + i++ + } + + c.inFlight = c.inFlight[i-1:] c.slowStart = false - if s := c.windowSize; s > 1 { - atomic.StoreInt32(&c.windowSize, s/2) - } -} -func (c *NewReno) updateAckNack() { - c.acksToSendLock.Lock() - defer c.acksToSendLock.Unlock() - - c.inboundTimesLock.Lock() - defer c.inboundTimesLock.Unlock() - - findAck := func(start uint32) uint32 { - ack := start - for len(c.acksToSend) > 0 { - if a, _ := c.acksToSend.Peek(); a == ack+1 { - ack, _ = c.acksToSend.Extract() - delete(c.inboundTimes, ack) - } else { - break - } - } - return ack + if i == 0 { + return } - ack := findAck(c.ack) - if ack == c.ack { - // check if there is a nack to send - // decide this based on whether there have been 3RTTs between the offset packet - if len(c.acksToSend) > 0 { - nextAck, _ := c.acksToSend.Peek() - if time.Now().Sub(c.inboundTimes[nextAck]).Seconds() > c.rtt*3 { - atomic.StoreUint32(&c.nack, nextAck-1) - ack, _ = c.acksToSend.Extract() - ack = findAck(ack) - } + for i > 0 { + s := c.windowSize + if s > 1 && atomic.CompareAndSwapUint32(&c.windowSize, s, s/2) { + break } } - atomic.StoreUint32(&c.ack, ack) + select { + case c.windowNotifier <- struct{}{}: + default: + } +} + +// It is assumed that ReceivedAck will only be called by one thread +func (c *NewReno) receivedAck(ack uint32) { + c.ackNackMu.Lock() + defer c.ackNackMu.Unlock() + + c.inFlightMu.Lock() + defer c.inFlightMu.Unlock() + + // as both ack and nack are cumulative, inflight will always be ordered by seq + var i int + for i < len(c.inFlight) && c.inFlight[i].sequence <= ack { + rtt := float64(time.Now().Sub(c.inFlight[i].time).Nanoseconds()) + c.rttNanos = c.rttNanos*(1-RttExponentialFactor) + rtt*RttExponentialFactor + + i++ + } + + if i == 0 { + return + } + + c.inFlight = c.inFlight[i-1:] + if c.slowStart { + c.windowSize += uint32(i) + } else { + c.windowCount += uint32(i) + if c.windowCount > c.windowSize { + c.windowCount -= uint32(i) + atomic.AddUint32(&c.windowSize, 1) + } + } + + select { + case c.windowNotifier <- struct{}{}: + default: + } } diff --git a/udp/congestion/newreno_test.go b/udp/congestion/newreno_test.go index 1ca7bea..a91b649 100644 --- a/udp/congestion/newreno_test.go +++ b/udp/congestion/newreno_test.go @@ -3,6 +3,8 @@ package congestion import ( "context" "fmt" + "github.com/stretchr/testify/assert" + "sort" "testing" "time" ) @@ -149,3 +151,11 @@ func TestNewReno_Congestion(t *testing.T) { }) }) } + +func TestSortableFlights_Less(t *testing.T) { + a := []flightInfo{{sequence: 0}, {sequence: 6}, {sequence: 3}, {sequence: 2}} + + sort.Sort(sortableFlights(a)) + + assert.Equal(t, []flightInfo{{sequence: 0}, {sequence: 2}, {sequence: 3}, {sequence: 6}}, a) +} diff --git a/udp/flow.go b/udp/flow.go index e045767..14835d0 100644 --- a/udp/flow.go +++ b/udp/flow.go @@ -1,7 +1,6 @@ package udp import ( - "errors" "fmt" "log" "mpbl3p/proxy" @@ -250,8 +249,7 @@ func (f *Flow) sendPacket(p Packet, g proxy.MacGenerator) error { } func (f *Flow) earlyUpdateLoop(g proxy.MacGenerator, keepalive time.Duration) { - var err error - for !errors.Is(err, shared.ErrDeadConnection) { + for f.isAlive { seq := f.congestion.AwaitEarlyUpdate(keepalive) p := Packet{ ack: f.congestion.NextAck(), -- 2.47.0 From a6a6d97e883f1fd9b8338d5ac1d5b06ae6be7bdf Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Mon, 22 Feb 2021 20:47:30 +0000 Subject: [PATCH 064/121] improved comments --- udp/congestion/newreno.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index 0faa157..6c96d18 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -130,14 +130,15 @@ func (c *NewReno) AwaitEarlyUpdate(keepalive time.Duration) uint32 { c.checkNack() - // CASE 1: waiting ACKs or NACKs and no message sent in the last RTT - if ((c.lastAck != c.ack) || (c.lastNack != c.nack)) && time.Now().After(c.lastSent.Add(rtt)) { - return 0 + // CASE 1: waiting ACKs or NACKs and no message sent in the last half-RTT + // this targets arrival in 0.5+0.5 ± 0.5 RTTs (1±0.5 RTTs) + if ((c.lastAck != c.ack) || (c.lastNack != c.nack)) && time.Now().After(c.lastSent.Add(rtt / 2)) { + return 0 // no ack needed } - // CASE 3: No message sent within the keepalive time + // CASE 2: No message sent within the keepalive time if keepalive != 0 && time.Now().After(c.lastSent.Add(keepalive)) { - return c.Sequence() + return c.Sequence() // require an ack } } } -- 2.47.0 From 8533c54395fc36ee62ba3e8d878803fa115870e8 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Mon, 22 Feb 2021 20:48:07 +0000 Subject: [PATCH 065/121] formatting --- udp/congestion/newreno.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index 6c96d18..2ee6d4c 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -132,7 +132,7 @@ func (c *NewReno) AwaitEarlyUpdate(keepalive time.Duration) uint32 { // CASE 1: waiting ACKs or NACKs and no message sent in the last half-RTT // this targets arrival in 0.5+0.5 ± 0.5 RTTs (1±0.5 RTTs) - if ((c.lastAck != c.ack) || (c.lastNack != c.nack)) && time.Now().After(c.lastSent.Add(rtt / 2)) { + if ((c.lastAck != c.ack) || (c.lastNack != c.nack)) && time.Now().After(c.lastSent.Add(rtt/2)) { return 0 // no ack needed } -- 2.47.0 From 5a3471a65a55ff0a684184b4a89a51e4fe65d7d6 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Thu, 25 Feb 2021 10:21:34 +0000 Subject: [PATCH 066/121] improved logging and added debugging --- udp/congestion/newreno.go | 6 ++---- udp/congestion/newreno_test.go | 14 ++++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index 2ee6d4c..084bd38 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -147,7 +147,7 @@ func (c *NewReno) receivedSequence(seq uint32) { c.ackNackMu.Lock() defer c.ackNackMu.Unlock() - if !(c.ack != seq-1 && c.nack != seq-1) { + if c.ack != seq-1 && c.nack != seq-1 { c.awaitingAck = append(c.awaitingAck, flightInfo{ time: time.Now(), sequence: seq, @@ -155,10 +155,8 @@ func (c *NewReno) receivedSequence(seq uint32) { return // if this seq doesn't change the ack field, awaitingAck will be unchanged } - c.ack = seq sort.Sort(c.awaitingAck) - - a := c.ack + a := seq var i int var e flightInfo diff --git a/udp/congestion/newreno_test.go b/udp/congestion/newreno_test.go index a91b649..aff7556 100644 --- a/udp/congestion/newreno_test.go +++ b/udp/congestion/newreno_test.go @@ -63,6 +63,7 @@ func (n *newRenoTest) RunSideA(ctx context.Context) { case <-ctx.Done(): return case p := <-n.aInbound: + fmt.Printf("side A received: %d,%d,%d\n", p.seq, p.nack, p.ack) n.sideA.ReceivedPacket(p.seq, p.nack, p.ack) } } @@ -75,11 +76,11 @@ func (n *newRenoTest) RunSideA(ctx context.Context) { } seq := n.sideA.AwaitEarlyUpdate(500 * time.Millisecond) - if seq != 0 { + if seq == 0 { // discard keepalives p := congestionPacket{ seq: seq, - nack: n.sideA.NextAck(), - ack: n.sideA.NextNack(), + nack: n.sideA.NextNack(), + ack: n.sideA.NextAck(), } n.aOutbound <- p } @@ -94,6 +95,7 @@ func (n *newRenoTest) RunSideB(ctx context.Context) { case <-ctx.Done(): return case p := <-n.bInbound: + fmt.Printf("side B received: %d,%d,%d\n", p.seq, p.nack, p.ack) n.sideB.ReceivedPacket(p.seq, p.nack, p.ack) } } @@ -106,11 +108,11 @@ func (n *newRenoTest) RunSideB(ctx context.Context) { } seq := n.sideB.AwaitEarlyUpdate(500 * time.Millisecond) - if seq != 0 { + if seq == 0 { // discard keepalives p := congestionPacket{ seq: seq, - nack: n.sideB.NextAck(), - ack: n.sideB.NextNack(), + nack: n.sideB.NextNack(), + ack: n.sideB.NextAck(), } n.bOutbound <- p } -- 2.47.0 From 4f35c9e721419c465c409f68ccd52eb277e567fc Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Thu, 25 Feb 2021 11:05:59 +0000 Subject: [PATCH 067/121] refactored updateAck --- udp/congestion/newreno.go | 38 ++++++++++-------------------- udp/congestion/newreno_test.go | 43 ++++++++++++++++++++++++---------- 2 files changed, 43 insertions(+), 38 deletions(-) diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index 084bd38..7234c7a 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -147,30 +147,17 @@ func (c *NewReno) receivedSequence(seq uint32) { c.ackNackMu.Lock() defer c.ackNackMu.Unlock() - if c.ack != seq-1 && c.nack != seq-1 { - c.awaitingAck = append(c.awaitingAck, flightInfo{ - time: time.Now(), - sequence: seq, - }) + if seq != c.ack+1 && seq != c.nack+1 { + if seq > c.ack && seq > c.nack { + c.awaitingAck = append(c.awaitingAck, flightInfo{ + time: time.Now(), + sequence: seq, + }) + } // else discard as it's already been cumulatively ACKed/NACKed return // if this seq doesn't change the ack field, awaitingAck will be unchanged } - sort.Sort(c.awaitingAck) - a := seq - - var i int - var e flightInfo - - for i, e = range c.awaitingAck { - if a+1 == e.sequence { - a += 1 - } else { - break - } - } - - c.ack = a - c.awaitingAck = c.awaitingAck[i:] + c.updateAck(seq) } func (c *NewReno) checkNack() { @@ -189,9 +176,10 @@ func (c *NewReno) checkNack() { } c.nack = c.awaitingAck[0].sequence - 1 + c.updateAck(c.nack) +} - a := c.ack - +func (c *NewReno) updateAck(a uint32) { var i int var e flightInfo @@ -221,13 +209,13 @@ func (c *NewReno) receivedNack(nack uint32) { i++ } - c.inFlight = c.inFlight[i-1:] c.slowStart = false - if i == 0 { return } + c.inFlight = c.inFlight[i-1:] + for i > 0 { s := c.windowSize if s > 1 && atomic.CompareAndSwapUint32(&c.windowSize, s, s/2) { diff --git a/udp/congestion/newreno_test.go b/udp/congestion/newreno_test.go index aff7556..7c37688 100644 --- a/udp/congestion/newreno_test.go +++ b/udp/congestion/newreno_test.go @@ -19,10 +19,10 @@ type newRenoTest struct { aOutbound, bOutbound chan congestionPacket aInbound, bInbound chan congestionPacket - rtt time.Duration + halfRtt time.Duration } -func newNewRenoTest(rtt time.Duration) *newRenoTest { +func newNewRenoTest(halfRtt time.Duration) *newRenoTest { return &newRenoTest{ sideA: NewNewReno(), sideB: NewNewReno(), @@ -33,7 +33,7 @@ func newNewRenoTest(rtt time.Duration) *newRenoTest { aInbound: make(chan congestionPacket), bInbound: make(chan congestionPacket), - rtt: rtt, + halfRtt: halfRtt, } } @@ -44,11 +44,13 @@ func (n *newRenoTest) Start(ctx context.Context) { case <-ctx.Done(): return case p := <-n.aOutbound: - time.AfterFunc(n.rtt/2, func() { + // deliver the packet at least half an halfRtt in the future (non-deterministic) + time.AfterFunc(n.halfRtt/2, func() { n.bInbound <- p }) case p := <-n.bOutbound: - time.AfterFunc(n.rtt/2, func() { + // deliver the packet at least half an halfRtt in the future (non-deterministic) + time.AfterFunc(n.halfRtt/2, func() { n.aInbound <- p }) } @@ -123,14 +125,8 @@ func (n *newRenoTest) RunSideB(ctx context.Context) { func TestNewReno_Congestion(t *testing.T) { t.Run("OneWay", func(t *testing.T) { t.Run("Lossless", func(t *testing.T) { - ctx := context.Background() - - var cancel context.CancelFunc - if d, ok := t.Deadline(); ok { - ctx, cancel = context.WithDeadline(ctx, d) - } else { - ctx, cancel = context.WithCancel(ctx) - } + // ASSIGN + ctx, cancel := context.WithCancel(context.Background()) defer cancel() c := newNewRenoTest(10 * time.Millisecond) @@ -138,6 +134,7 @@ func TestNewReno_Congestion(t *testing.T) { c.RunSideA(ctx) c.RunSideB(ctx) + // ACT for i := 0; i < 20; i++ { t1 := time.Now() seq := c.sideA.Sequence() @@ -150,14 +147,34 @@ func TestNewReno_Congestion(t *testing.T) { ack: c.sideA.NextAck(), } } + + // allow the systems to catch up before asserting + time.Sleep(30 * time.Millisecond) + + // ASSERT + + assert.InDelta(t, + float64(2*10*time.Millisecond.Nanoseconds()), + c.sideA.rttNanos, + float64(time.Millisecond.Nanoseconds()), + ) + + assert.Equal(t, uint32(0), c.sideA.nack) + assert.Equal(t, uint32(0), c.sideA.ack) + + assert.Equal(t, uint32(0), c.sideB.nack) + assert.Equal(t, uint32(20), c.sideB.ack) }) }) } func TestSortableFlights_Less(t *testing.T) { + // ASSIGN a := []flightInfo{{sequence: 0}, {sequence: 6}, {sequence: 3}, {sequence: 2}} + // ACT sort.Sort(sortableFlights(a)) + // ASSERT assert.Equal(t, []flightInfo{{sequence: 0}, {sequence: 2}, {sequence: 3}, {sequence: 6}}, a) } -- 2.47.0 From 772302c76feb4cede42403db86e83721e5b0415d Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Thu, 25 Feb 2021 19:32:45 +0000 Subject: [PATCH 068/121] test final ack and nack --- udp/congestion/newreno.go | 49 +++++++++++++++++++--------------- udp/congestion/newreno_test.go | 23 +++++++--------- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index 7234c7a..c1e3448 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -80,9 +80,12 @@ func NewNewReno() *NewReno { } func (c *NewReno) ReceivedPacket(seq, nack, ack uint32) { + // decide what acks and nacks to send if seq != 0 { c.receivedSequence(seq) } + + // decide how window size was affected if nack != 0 { c.receivedNack(nack) } @@ -147,16 +150,21 @@ func (c *NewReno) receivedSequence(seq uint32) { c.ackNackMu.Lock() defer c.ackNackMu.Unlock() - if seq != c.ack+1 && seq != c.nack+1 { - if seq > c.ack && seq > c.nack { - c.awaitingAck = append(c.awaitingAck, flightInfo{ - time: time.Now(), - sequence: seq, - }) - } // else discard as it's already been cumulatively ACKed/NACKed - return // if this seq doesn't change the ack field, awaitingAck will be unchanged + if seq < c.nack || seq < c.ack { + // packet received out of order has already been cumulatively NACKed + // or duplicate packet received and already ACKed + return } + if seq != c.ack+1 && seq != c.nack+1 { + c.awaitingAck = append(c.awaitingAck, flightInfo{ + time: time.Now(), + sequence: seq, + }) + return // if this seq doesn't change the ack field, awaitingAck will still not do anything useful + } + + sort.Sort(c.awaitingAck) c.updateAck(seq) } @@ -169,14 +177,15 @@ func (c *NewReno) checkNack() { } sort.Sort(c.awaitingAck) + rtt := time.Duration(c.rttNanos * RttLossDelay) - - if !c.awaitingAck[0].time.Before(time.Now().Add(-rtt)) { - return + if c.awaitingAck[0].time.Before(time.Now().Add(-rtt)) { + // if the next packet sequence to ack was received more than an rttlossdelay ago + // mark the packet(s) blocking it as missing with a nack + // then update ack from the delayed packet + c.nack = c.awaitingAck[0].sequence - 1 + c.updateAck(c.nack) } - - c.nack = c.awaitingAck[0].sequence - 1 - c.updateAck(c.nack) } func (c *NewReno) updateAck(a uint32) { @@ -184,8 +193,8 @@ func (c *NewReno) updateAck(a uint32) { var e flightInfo for i, e = range c.awaitingAck { - if a+1 == e.sequence { - a += 1 + if e.sequence == a+1 { + a = e.sequence } else { break } @@ -195,7 +204,6 @@ func (c *NewReno) updateAck(a uint32) { c.awaitingAck = c.awaitingAck[i:] } -// It is assumed that ReceivedNack will only be called by one thread func (c *NewReno) receivedNack(nack uint32) { c.ackNackMu.Lock() defer c.ackNackMu.Unlock() @@ -209,14 +217,14 @@ func (c *NewReno) receivedNack(nack uint32) { i++ } - c.slowStart = false if i == 0 { return } - c.inFlight = c.inFlight[i-1:] + c.slowStart = false + c.inFlight = c.inFlight[i:] - for i > 0 { + for { s := c.windowSize if s > 1 && atomic.CompareAndSwapUint32(&c.windowSize, s, s/2) { break @@ -229,7 +237,6 @@ func (c *NewReno) receivedNack(nack uint32) { } } -// It is assumed that ReceivedAck will only be called by one thread func (c *NewReno) receivedAck(ack uint32) { c.ackNackMu.Lock() defer c.ackNackMu.Unlock() diff --git a/udp/congestion/newreno_test.go b/udp/congestion/newreno_test.go index 7c37688..44e6f6b 100644 --- a/udp/congestion/newreno_test.go +++ b/udp/congestion/newreno_test.go @@ -22,7 +22,7 @@ type newRenoTest struct { halfRtt time.Duration } -func newNewRenoTest(halfRtt time.Duration) *newRenoTest { +func newNewRenoTest(rtt time.Duration) *newRenoTest { return &newRenoTest{ sideA: NewNewReno(), sideB: NewNewReno(), @@ -33,7 +33,7 @@ func newNewRenoTest(halfRtt time.Duration) *newRenoTest { aInbound: make(chan congestionPacket), bInbound: make(chan congestionPacket), - halfRtt: halfRtt, + halfRtt: rtt / 2, } } @@ -126,20 +126,21 @@ func TestNewReno_Congestion(t *testing.T) { t.Run("OneWay", func(t *testing.T) { t.Run("Lossless", func(t *testing.T) { // ASSIGN + rtt := 80*time.Millisecond + ctx, cancel := context.WithCancel(context.Background()) defer cancel() - c := newNewRenoTest(10 * time.Millisecond) + c := newNewRenoTest(rtt) c.Start(ctx) c.RunSideA(ctx) c.RunSideB(ctx) // ACT - for i := 0; i < 20; i++ { - t1 := time.Now() + for i := 0; i < 50; i++ { + // sleep to simulate preparing packet + time.Sleep(1*time.Millisecond) seq := c.sideA.Sequence() - t2 := time.Now() - fmt.Printf("waited %dms for sequence\n", t2.Sub(t1).Milliseconds()) c.aOutbound <- congestionPacket{ seq: seq, @@ -153,17 +154,11 @@ func TestNewReno_Congestion(t *testing.T) { // ASSERT - assert.InDelta(t, - float64(2*10*time.Millisecond.Nanoseconds()), - c.sideA.rttNanos, - float64(time.Millisecond.Nanoseconds()), - ) - assert.Equal(t, uint32(0), c.sideA.nack) assert.Equal(t, uint32(0), c.sideA.ack) assert.Equal(t, uint32(0), c.sideB.nack) - assert.Equal(t, uint32(20), c.sideB.ack) + assert.Equal(t, uint32(50), c.sideB.ack) }) }) } -- 2.47.0 From ce211bb142bc8af109bce3b8cb9dddb0887f906a Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Thu, 25 Feb 2021 19:33:13 +0000 Subject: [PATCH 069/121] formatting --- udp/congestion/newreno_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/udp/congestion/newreno_test.go b/udp/congestion/newreno_test.go index 44e6f6b..90c3681 100644 --- a/udp/congestion/newreno_test.go +++ b/udp/congestion/newreno_test.go @@ -126,7 +126,7 @@ func TestNewReno_Congestion(t *testing.T) { t.Run("OneWay", func(t *testing.T) { t.Run("Lossless", func(t *testing.T) { // ASSIGN - rtt := 80*time.Millisecond + rtt := 80 * time.Millisecond ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -139,7 +139,7 @@ func TestNewReno_Congestion(t *testing.T) { // ACT for i := 0; i < 50; i++ { // sleep to simulate preparing packet - time.Sleep(1*time.Millisecond) + time.Sleep(1 * time.Millisecond) seq := c.sideA.Sequence() c.aOutbound <- congestionPacket{ -- 2.47.0 From a2bb734d8d3b943c8eca936c023b459639c4c7d0 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 27 Feb 2021 14:01:17 +0000 Subject: [PATCH 070/121] rtt finding test --- udp/congestion/newreno_test.go | 76 +++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 5 deletions(-) diff --git a/udp/congestion/newreno_test.go b/udp/congestion/newreno_test.go index 90c3681..2595c68 100644 --- a/udp/congestion/newreno_test.go +++ b/udp/congestion/newreno_test.go @@ -45,12 +45,12 @@ func (n *newRenoTest) Start(ctx context.Context) { return case p := <-n.aOutbound: // deliver the packet at least half an halfRtt in the future (non-deterministic) - time.AfterFunc(n.halfRtt/2, func() { + time.AfterFunc(n.halfRtt, func() { n.bInbound <- p }) case p := <-n.bOutbound: // deliver the packet at least half an halfRtt in the future (non-deterministic) - time.AfterFunc(n.halfRtt/2, func() { + time.AfterFunc(n.halfRtt, func() { n.aInbound <- p }) } @@ -127,6 +127,7 @@ func TestNewReno_Congestion(t *testing.T) { t.Run("Lossless", func(t *testing.T) { // ASSIGN rtt := 80 * time.Millisecond + numPackets := 50 ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -137,7 +138,7 @@ func TestNewReno_Congestion(t *testing.T) { c.RunSideB(ctx) // ACT - for i := 0; i < 50; i++ { + for i := 0; i < numPackets; i++ { // sleep to simulate preparing packet time.Sleep(1 * time.Millisecond) seq := c.sideA.Sequence() @@ -150,7 +151,7 @@ func TestNewReno_Congestion(t *testing.T) { } // allow the systems to catch up before asserting - time.Sleep(30 * time.Millisecond) + time.Sleep(rtt + 30*time.Millisecond) // ASSERT @@ -158,7 +159,72 @@ func TestNewReno_Congestion(t *testing.T) { assert.Equal(t, uint32(0), c.sideA.ack) assert.Equal(t, uint32(0), c.sideB.nack) - assert.Equal(t, uint32(50), c.sideB.ack) + assert.Equal(t, uint32(numPackets), c.sideB.ack) + }) + }) + + t.Run("TwoWay", func(t *testing.T) { + t.Run("PredictsRtt", func(t *testing.T) { + // ASSIGN + rtt := 160 * time.Millisecond + numPackets := 100 + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := newNewRenoTest(rtt) + c.Start(ctx) + c.RunSideA(ctx) + c.RunSideB(ctx) + + // ACT + done := make(chan struct{}) + + go func() { + for i := 0; i < numPackets; i++ { + time.Sleep(1 * time.Millisecond) + seq := c.sideA.Sequence() + + c.aOutbound <- congestionPacket{ + seq: seq, + nack: c.sideA.NextNack(), + ack: c.sideA.NextAck(), + } + } + + done <- struct{}{} + }() + + go func() { + for i := 0; i < numPackets; i++ { + time.Sleep(1 * time.Millisecond) + seq := c.sideB.Sequence() + + c.bOutbound <- congestionPacket{ + seq: seq, + nack: c.sideB.NextNack(), + ack: c.sideB.NextAck(), + } + } + + done <- struct{}{} + }() + + <-done + <-done + + time.Sleep(rtt + 30*time.Millisecond) + + // ASSERT + + assert.Equal(t, uint32(0), c.sideA.nack) + assert.Equal(t, uint32(numPackets), c.sideA.ack) + + assert.Equal(t, uint32(0), c.sideB.nack) + assert.Equal(t, uint32(numPackets), c.sideB.ack) + + assert.InDelta(t, float64(rtt.Nanoseconds()), c.sideA.rttNanos, float64(8*time.Millisecond.Nanoseconds())) + assert.InDelta(t, float64(rtt.Nanoseconds()), c.sideB.rttNanos, float64(8*time.Millisecond.Nanoseconds())) }) }) } -- 2.47.0 From 2ebf48ae3484119ba3bdd98d4fe4895620a2005d Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 27 Feb 2021 18:30:41 +0000 Subject: [PATCH 071/121] local port for outbound conn --- config/builder.go | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/config/builder.go b/config/builder.go index 50c797d..a5b9004 100644 --- a/config/builder.go +++ b/config/builder.go @@ -70,11 +70,15 @@ func (c Configuration) Build() (*proxy.Proxy, error) { } func buildTcp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() proxy.MacVerifier) error { + var laddr func() string + if peer.LocalPort == 0 { + laddr = func() string { return fmt.Sprintf("%s:", peer.GetLocalHost()) } + } else { + laddr = func() string { return fmt.Sprintf("%s:%d", peer.GetLocalHost(), peer.LocalPort) } + } + if peer.RemoteHost != "" { - f, err := tcp.InitiateFlow( - func() string { return fmt.Sprintf("%s:", peer.GetLocalHost()) }, - fmt.Sprintf("%s:%d", peer.RemoteHost, peer.RemotePort), - ) + f, err := tcp.InitiateFlow(laddr, fmt.Sprintf("%s:%d", peer.RemoteHost, peer.RemotePort)) if err != nil { return err @@ -86,7 +90,7 @@ func buildTcp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() p return nil } - err := tcp.NewListener(p, fmt.Sprintf("%s:%d", peer.GetLocalHost(), peer.LocalPort), v, g) + err := tcp.NewListener(p, laddr(), v, g) if err != nil { return err } @@ -95,6 +99,13 @@ func buildTcp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() p } func buildUdp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() proxy.MacVerifier) error { + var laddr func() string + if peer.LocalPort == 0 { + laddr = func() string { return fmt.Sprintf("%s:", peer.GetLocalHost()) } + } else { + laddr = func() string { return fmt.Sprintf("%s:%d", peer.GetLocalHost(), peer.LocalPort) } + } + var c func() udp.Congestion switch peer.Congestion { case "None": @@ -107,7 +118,7 @@ func buildUdp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() p if peer.RemoteHost != "" { f, err := udp.InitiateFlow( - func() string { return fmt.Sprintf("%s:", peer.GetLocalHost()) }, + laddr, fmt.Sprintf("%s:%d", peer.RemoteHost, peer.RemotePort), v(), g(), @@ -125,7 +136,7 @@ func buildUdp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() p return nil } - err := udp.NewListener(p, fmt.Sprintf("%s:%d", peer.GetLocalHost(), peer.LocalPort), v, g, c) + err := udp.NewListener(p, laddr(), v, g, c) if err != nil { return err } -- 2.47.0 From c1f7c325c6c94366dd01eaefcb9681689f0381d9 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 27 Feb 2021 19:01:59 +0000 Subject: [PATCH 072/121] added packet repeat delay --- udp/flow.go | 1 + udp/wireshark_dissector.lua | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/udp/flow.go b/udp/flow.go index 14835d0..6fd7219 100644 --- a/udp/flow.go +++ b/udp/flow.go @@ -119,6 +119,7 @@ func (f *InitiatedFlow) Reconnect() error { } _ = f.sendPacket(p, f.g) + time.Sleep(1 * time.Second) } }() diff --git a/udp/wireshark_dissector.lua b/udp/wireshark_dissector.lua index 95f1c4b..6803f97 100644 --- a/udp/wireshark_dissector.lua +++ b/udp/wireshark_dissector.lua @@ -32,4 +32,5 @@ function mpbl3p_udp.dissector(buffer, pinfo, tree) end end -DissectorTable.get("udp.port"):add(1234, mpbl3p_udp) +DissectorTable.get("udp.port"):add(4724, mpbl3p_udp) +DissectorTable.get("udp.port"):add(4725, mpbl3p_udp) -- 2.47.0 From 8bd3cafd4b0d3051cea1ed666881ddb0ad1d79a6 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 27 Feb 2021 21:41:43 +0000 Subject: [PATCH 073/121] Added loss testing TestNewReno_Congestion/TwoWay/SequenceLoss fails. This is due debugging. --- udp/congestion/newreno.go | 18 ++-- udp/congestion/newreno_test.go | 154 ++++++++++++++++++++++++++++++--- 2 files changed, 152 insertions(+), 20 deletions(-) diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index c1e3448..5694331 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -178,8 +178,11 @@ func (c *NewReno) checkNack() { sort.Sort(c.awaitingAck) - rtt := time.Duration(c.rttNanos * RttLossDelay) - if c.awaitingAck[0].time.Before(time.Now().Add(-rtt)) { + fmt.Printf("got here: seq to consider %d, %dms ago\n", c.awaitingAck[0].sequence, time.Now().Sub(c.awaitingAck[0].time).Milliseconds()) + + lossThreshold := time.Duration(c.rttNanos * RttLossDelay) + fmt.Printf("threshold time: %dms\n", lossThreshold.Milliseconds()) + if c.awaitingAck[0].time.Before(time.Now().Add(-lossThreshold)) { // if the next packet sequence to ack was received more than an rttlossdelay ago // mark the packet(s) blocking it as missing with a nack // then update ack from the delayed packet @@ -188,20 +191,21 @@ func (c *NewReno) checkNack() { } } -func (c *NewReno) updateAck(a uint32) { - var i int - var e flightInfo +func (c *NewReno) updateAck(start uint32) { + a := start - for i, e = range c.awaitingAck { + var removed int + for _, e := range c.awaitingAck { if e.sequence == a+1 { a = e.sequence + removed++ } else { break } } c.ack = a - c.awaitingAck = c.awaitingAck[i:] + c.awaitingAck = c.awaitingAck[removed:] } func (c *NewReno) receivedNack(nack uint32) { diff --git a/udp/congestion/newreno_test.go b/udp/congestion/newreno_test.go index 2595c68..e3f80a9 100644 --- a/udp/congestion/newreno_test.go +++ b/udp/congestion/newreno_test.go @@ -38,21 +38,40 @@ func newNewRenoTest(rtt time.Duration) *newRenoTest { } func (n *newRenoTest) Start(ctx context.Context) { + type packetWithTime struct { + t time.Time + p congestionPacket + } + go func() { + aOutboundDelayed := make(chan packetWithTime, 128) + bOutboundDelayed := make(chan packetWithTime, 128) + + delayer := func(tp chan packetWithTime, cp chan congestionPacket) { + for { + select { + case p := <-tp: + s := p.t.Add(n.halfRtt).Sub(time.Now()) + fmt.Printf("delayed packet for %dms\n", s.Milliseconds()) + time.Sleep(s) + cp <- p.p + case <-ctx.Done(): + return + } + } + } + + go delayer(aOutboundDelayed, n.bInbound) + go delayer(bOutboundDelayed, n.aInbound) + for { select { case <-ctx.Done(): return case p := <-n.aOutbound: - // deliver the packet at least half an halfRtt in the future (non-deterministic) - time.AfterFunc(n.halfRtt, func() { - n.bInbound <- p - }) + aOutboundDelayed <- packetWithTime{t: time.Now(), p: p} case p := <-n.bOutbound: - // deliver the packet at least half an halfRtt in the future (non-deterministic) - time.AfterFunc(n.halfRtt, func() { - n.aInbound <- p - }) + bOutboundDelayed <- packetWithTime{t: time.Now(), p: p} } } }() @@ -161,13 +180,55 @@ func TestNewReno_Congestion(t *testing.T) { assert.Equal(t, uint32(0), c.sideB.nack) assert.Equal(t, uint32(numPackets), c.sideB.ack) }) + + t.Run("SequenceLoss", func(t *testing.T) { + // ASSIGN + rtt := 80 * time.Millisecond + numPackets := 50 + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := newNewRenoTest(rtt) + c.Start(ctx) + c.RunSideA(ctx) + c.RunSideB(ctx) + + // ACT + for i := 0; i < numPackets; i++ { + // sleep to simulate preparing packet + time.Sleep(1 * time.Millisecond) + seq := c.sideA.Sequence() + + if seq == 20 { + // Simulate packet loss of sequence 20 + continue + } + + c.aOutbound <- congestionPacket{ + seq: seq, + nack: c.sideA.NextNack(), + ack: c.sideA.NextAck(), + } + } + + time.Sleep(rtt + 30*time.Millisecond) + + // ASSERT + + assert.Equal(t, uint32(0), c.sideA.nack) + assert.Equal(t, uint32(0), c.sideA.ack) + + assert.Equal(t, uint32(20), c.sideB.nack) + assert.Equal(t, uint32(numPackets), c.sideB.ack) + }) }) t.Run("TwoWay", func(t *testing.T) { - t.Run("PredictsRtt", func(t *testing.T) { + t.Run("Lossless", func(t *testing.T) { // ASSIGN - rtt := 160 * time.Millisecond - numPackets := 100 + rtt := 80 * time.Millisecond + numPackets := 50 ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -222,9 +283,76 @@ func TestNewReno_Congestion(t *testing.T) { assert.Equal(t, uint32(0), c.sideB.nack) assert.Equal(t, uint32(numPackets), c.sideB.ack) + }) - assert.InDelta(t, float64(rtt.Nanoseconds()), c.sideA.rttNanos, float64(8*time.Millisecond.Nanoseconds())) - assert.InDelta(t, float64(rtt.Nanoseconds()), c.sideB.rttNanos, float64(8*time.Millisecond.Nanoseconds())) + t.Run("SequenceLoss", func(t *testing.T) { + // ASSIGN + rtt := 80 * time.Millisecond + numPackets := 100 + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + c := newNewRenoTest(rtt) + c.Start(ctx) + c.RunSideA(ctx) + c.RunSideB(ctx) + + // ACT + done := make(chan struct{}) + + go func() { + for i := 0; i < numPackets; i++ { + time.Sleep(1 * time.Millisecond) + seq := c.sideA.Sequence() + + if seq == 9 { + // Simulate packet loss of sequence 9 + continue + } + + c.aOutbound <- congestionPacket{ + seq: seq, + nack: c.sideA.NextNack(), + ack: c.sideA.NextAck(), + } + } + + done <- struct{}{} + }() + + go func() { + for i := 0; i < numPackets; i++ { + time.Sleep(1 * time.Millisecond) + seq := c.sideB.Sequence() + + if seq == 13 { + // Simulate packet loss of sequence 13 + continue + } + + c.bOutbound <- congestionPacket{ + seq: seq, + nack: c.sideB.NextNack(), + ack: c.sideB.NextAck(), + } + } + + done <- struct{}{} + }() + + <-done + <-done + + time.Sleep(rtt + 30*time.Millisecond) + + // ASSERT + + assert.Equal(t, uint32(13), c.sideA.nack) + assert.Equal(t, uint32(numPackets), c.sideA.ack) + + assert.Equal(t, uint32(9), c.sideB.nack) + assert.Equal(t, uint32(numPackets), c.sideB.ack) }) }) } -- 2.47.0 From 6317df7aba336bffb90af9dc0d99da292fc297ef Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 27 Feb 2021 21:43:28 +0000 Subject: [PATCH 074/121] formatting --- udp/congestion/newreno_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/udp/congestion/newreno_test.go b/udp/congestion/newreno_test.go index e3f80a9..3cd4d19 100644 --- a/udp/congestion/newreno_test.go +++ b/udp/congestion/newreno_test.go @@ -50,13 +50,13 @@ func (n *newRenoTest) Start(ctx context.Context) { delayer := func(tp chan packetWithTime, cp chan congestionPacket) { for { select { - case p := <-tp: - s := p.t.Add(n.halfRtt).Sub(time.Now()) - fmt.Printf("delayed packet for %dms\n", s.Milliseconds()) - time.Sleep(s) - cp <- p.p - case <-ctx.Done(): - return + case p := <-tp: + s := p.t.Add(n.halfRtt).Sub(time.Now()) + fmt.Printf("delayed packet for %dms\n", s.Milliseconds()) + time.Sleep(s) + cp <- p.p + case <-ctx.Done(): + return } } } -- 2.47.0 From 5d22a86f04c6326dd86bad42b2ca4ad7742e1134 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 20 Mar 2021 12:33:00 +0000 Subject: [PATCH 075/121] freebsd build --- .drone.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index 8a3147e..0f5af10 100644 --- a/.drone.yml +++ b/.drone.yml @@ -36,8 +36,9 @@ steps: - name: cache path: /go commands: - - GOOS=linux GOARCH=amd64 go build -o linux_amd64 - - GOOS=linux GOARCH=arm GOARM=7 go build -o linux_arm_v7 + - GOOS=linux GOARCH=amd64 go build -o linux_amd64 + - GOOS=linux GOARCH=arm GOARM=7 go build -o linux_arm_v7 + - GOOS=freebsd GOARCH=arm go build -o freebsd_arm_v8a - name: upload image: minio/mc -- 2.47.0 From 3605cb32ba03c5e136a5a74f548164d1685b47cf Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 20 Mar 2021 12:36:18 +0000 Subject: [PATCH 076/121] updated goproxy --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index 0f5af10..9ad8ec6 100644 --- a/.drone.yml +++ b/.drone.yml @@ -12,7 +12,7 @@ steps: - name: install image: golang:1.15 environment: - GOPROXY: http://10.20.0.25:3142|direct + GOPROXY: http://containers.internal.hillion.co.uk:3142,direct volumes: - name: cache path: /go -- 2.47.0 From 902a9aed61c2d4c259a23607cf50b880594850e0 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 20 Mar 2021 12:37:49 +0000 Subject: [PATCH 077/121] bumped go version --- .drone.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.drone.yml b/.drone.yml index 9ad8ec6..179f128 100644 --- a/.drone.yml +++ b/.drone.yml @@ -5,12 +5,12 @@ name: default steps: - name: format - image: golang:1.15 + image: golang:1.16 commands: - bash -c "gofmt -l . | wc -l | cmp -s <(echo 0) || (gofmt -l . && exit 1)" - name: install - image: golang:1.15 + image: golang:1.16 environment: GOPROXY: http://containers.internal.hillion.co.uk:3142,direct volumes: @@ -20,7 +20,7 @@ steps: - go test -i ./... - name: test - image: golang:1.15 + image: golang:1.16 volumes: - name: cache path: /go @@ -28,7 +28,7 @@ steps: - go test ./... - name: build (debian) - image: golang:1.15-buster + image: golang:1.16-buster when: event: - push -- 2.47.0 From 2dcf821a6fc016617a5ddca4f5979b24c1ac9a0a Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 20 Mar 2021 12:39:18 +0000 Subject: [PATCH 078/121] updated s3 --- .drone.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index 179f128..8ff5c2f 100644 --- a/.drone.yml +++ b/.drone.yml @@ -50,9 +50,10 @@ steps: SECRET_KEY: from_secret: s3_secret_key commands: - - mc alias set s3 http://10.20.0.25:3900 $${ACCESS_KEY} $${SECRET_KEY} + - mc alias set s3 https://s3.us-west-001.backblazeb2.com $${ACCESS_KEY} $${SECRET_KEY} - mc cp linux_amd64 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_amd64 - mc cp linux_arm_v7 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_arm_v7 + - mc cp freebsd_arm_v8a s3/dissertation/binaries/debian/${DRONE_BRANCH}_freebsd_arm_v8a volumes: - name: cache -- 2.47.0 From 546e7eca0bec6ec409c793646ce499b2cbe90d64 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 20 Mar 2021 13:38:12 +0000 Subject: [PATCH 079/121] freebsd arm64 --- .drone.yml | 8 ++++---- go.mod | 2 +- go.sum | 6 ++++-- tun/tun.go | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.drone.yml b/.drone.yml index 8ff5c2f..bccbe38 100644 --- a/.drone.yml +++ b/.drone.yml @@ -36,9 +36,9 @@ steps: - name: cache path: /go commands: - - GOOS=linux GOARCH=amd64 go build -o linux_amd64 - - GOOS=linux GOARCH=arm GOARM=7 go build -o linux_arm_v7 - - GOOS=freebsd GOARCH=arm go build -o freebsd_arm_v8a + - GOOS=linux GOARCH=amd64 go build -o linux_amd64 + - GOOS=linux GOARCH=arm GOARM=7 go build -o linux_arm_v7 + - GOOS=freebsd GOARCH=arm64 GOARM=8 go build -o freebsd_arm64_v8a - name: upload image: minio/mc @@ -53,7 +53,7 @@ steps: - mc alias set s3 https://s3.us-west-001.backblazeb2.com $${ACCESS_KEY} $${SECRET_KEY} - mc cp linux_amd64 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_amd64 - mc cp linux_arm_v7 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_arm_v7 - - mc cp freebsd_arm_v8a s3/dissertation/binaries/debian/${DRONE_BRANCH}_freebsd_arm_v8a + - mc cp freebsd_arm64_v8a s3/dissertation/binaries/debian/${DRONE_BRANCH}_freebsd_arm64_v8a volumes: - name: cache diff --git a/go.mod b/go.mod index ad76837..999cca7 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module mpbl3p go 1.15 require ( + github.com/JakeHillion/taptun v0.0.0-20210320133200-cf0ef75b1bff // indirect github.com/go-playground/validator/v10 v10.4.1 - github.com/pkg/taptun v0.0.0-20160424131934-bbbd335672ab github.com/smartystreets/goconvey v1.6.4 // indirect github.com/stretchr/testify v1.4.0 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 diff --git a/go.sum b/go.sum index 2c6ebc9..cc4beb1 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,7 @@ +github.com/JakeHillion/taptun v0.0.0-20160424131934-bbbd335672ab h1:/UB98lLA11PJZOqhdzqeITMTMz6eiTjti9Z9kYq1SWQ= +github.com/JakeHillion/taptun v0.0.0-20160424131934-bbbd335672ab/go.mod h1:8WBFCKR7ZdT+WVtgyiSPJf6gqXiNZUvfglN8vwkoyBE= +github.com/JakeHillion/taptun v0.0.0-20210320133200-cf0ef75b1bff h1:O+wiKpOHS2BidwDz6ZuR3dVQNsrD55raE9mY4yub6Wc= +github.com/JakeHillion/taptun v0.0.0-20210320133200-cf0ef75b1bff/go.mod h1:8WBFCKR7ZdT+WVtgyiSPJf6gqXiNZUvfglN8vwkoyBE= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= @@ -14,8 +18,6 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/pkg/taptun v0.0.0-20160424131934-bbbd335672ab h1:dAXDRtXYxj4sTR5WeRuTFJGH18QMT6AUpUgRwedI6es= -github.com/pkg/taptun v0.0.0-20160424131934-bbbd335672ab/go.mod h1:N5a/Ll2ZNk5wjiLNW9LIiNtO9RNYcaYmcXSYKMYrlDg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= diff --git a/tun/tun.go b/tun/tun.go index b4141f2..d76fabb 100644 --- a/tun/tun.go +++ b/tun/tun.go @@ -1,7 +1,7 @@ package tun import ( - "github.com/pkg/taptun" + "github.com/JakeHillion/taptun" "io" "log" "mpbl3p/proxy" -- 2.47.0 From 233460a5de924feabea169515db9ad0af06815f2 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 20 Mar 2021 13:39:42 +0000 Subject: [PATCH 080/121] removed goarm --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index bccbe38..911a7d7 100644 --- a/.drone.yml +++ b/.drone.yml @@ -38,7 +38,7 @@ steps: commands: - GOOS=linux GOARCH=amd64 go build -o linux_amd64 - GOOS=linux GOARCH=arm GOARM=7 go build -o linux_arm_v7 - - GOOS=freebsd GOARCH=arm64 GOARM=8 go build -o freebsd_arm64_v8a + - GOOS=freebsd GOARCH=arm64 go build -o freebsd_arm64_v8a - name: upload image: minio/mc -- 2.47.0 From bd52357f85ce5febe4d1f744c16a5d8cd9cdba59 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 20 Mar 2021 13:52:44 +0000 Subject: [PATCH 081/121] signed dronefile --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index 911a7d7..8c66333 100644 --- a/.drone.yml +++ b/.drone.yml @@ -61,6 +61,6 @@ volumes: --- kind: signature -hmac: a7c498332fbf43f422a68475c53daa0a65b7801004f09300e40275007dec9bee +hmac: de07a3ab113028b48c590c406f7ab8f74aeae49679287862168d912ec10e9920 ... -- 2.47.0 From 9f14c9af8c5a4c2eaf8a8438b15ee0bd606a2c0c Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 20 Mar 2021 14:25:19 +0000 Subject: [PATCH 082/121] removed incorrect indirect --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 999cca7..edfdcc1 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module mpbl3p go 1.15 require ( - github.com/JakeHillion/taptun v0.0.0-20210320133200-cf0ef75b1bff // indirect + github.com/JakeHillion/taptun v0.0.0-20210320133200-cf0ef75b1bff github.com/go-playground/validator/v10 v10.4.1 github.com/smartystreets/goconvey v1.6.4 // indirect github.com/stretchr/testify v1.4.0 -- 2.47.0 From ee5395629e7c3a1a282d4aa5acc19faee31dbe43 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Mon, 22 Mar 2021 19:35:24 +0000 Subject: [PATCH 083/121] corrected rtt estimation --- udp/congestion/newreno.go | 4 ++-- udp/congestion/newreno_test.go | 34 ++++++++++++++++++++-------------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index 5694331..c6d70cc 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -161,7 +161,7 @@ func (c *NewReno) receivedSequence(seq uint32) { time: time.Now(), sequence: seq, }) - return // if this seq doesn't change the ack field, awaitingAck will still not do anything useful + return // if this seq doesn't change the ack field, updateAck will still not do anything useful } sort.Sort(c.awaitingAck) @@ -261,7 +261,7 @@ func (c *NewReno) receivedAck(ack uint32) { return } - c.inFlight = c.inFlight[i-1:] + c.inFlight = c.inFlight[i:] if c.slowStart { c.windowSize += uint32(i) } else { diff --git a/udp/congestion/newreno_test.go b/udp/congestion/newreno_test.go index 3cd4d19..c20b4bc 100644 --- a/udp/congestion/newreno_test.go +++ b/udp/congestion/newreno_test.go @@ -97,14 +97,17 @@ func (n *newRenoTest) RunSideA(ctx context.Context) { } seq := n.sideA.AwaitEarlyUpdate(500 * time.Millisecond) - if seq == 0 { // discard keepalives - p := congestionPacket{ - seq: seq, - nack: n.sideA.NextNack(), - ack: n.sideA.NextAck(), - } - n.aOutbound <- p + if seq != 0 { + // skip keepalive + // required to ensure AwaitEarlyUpdate terminates + continue } + p := congestionPacket{ + seq: seq, + nack: n.sideA.NextNack(), + ack: n.sideA.NextAck(), + } + n.aOutbound <- p } }() } @@ -129,14 +132,17 @@ func (n *newRenoTest) RunSideB(ctx context.Context) { } seq := n.sideB.AwaitEarlyUpdate(500 * time.Millisecond) - if seq == 0 { // discard keepalives - p := congestionPacket{ - seq: seq, - nack: n.sideB.NextNack(), - ack: n.sideB.NextAck(), - } - n.bOutbound <- p + if seq != 0 { + // skip keepalive + // required to ensure AwaitEarlyUpdate terminates + continue } + p := congestionPacket{ + seq: seq, + nack: n.sideB.NextNack(), + ack: n.sideB.NextAck(), + } + n.bOutbound <- p } }() } -- 2.47.0 From 0cc72ca142022a429f9f4e1d27829042aeaa025e Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Mon, 22 Mar 2021 19:45:48 +0000 Subject: [PATCH 084/121] removed unused heap --- utils/heap.go | 65 ---------------------------------------------- utils/heap_test.go | 54 -------------------------------------- 2 files changed, 119 deletions(-) delete mode 100644 utils/heap.go delete mode 100644 utils/heap_test.go diff --git a/utils/heap.go b/utils/heap.go deleted file mode 100644 index 96f2c34..0000000 --- a/utils/heap.go +++ /dev/null @@ -1,65 +0,0 @@ -package utils - -import "errors" - -var ErrorEmptyHeap = errors.New("attempted to extract from empty heap") - -// A MinHeap for Uint64 -type Uint32Heap []uint32 - -func (h *Uint32Heap) swap(x, y int) { - (*h)[x] = (*h)[x] ^ (*h)[y] - (*h)[y] = (*h)[y] ^ (*h)[x] - (*h)[x] = (*h)[x] ^ (*h)[y] -} - -func (h *Uint32Heap) Insert(new uint32) uint32 { - *h = append(*h, new) - - child := len(*h) - 1 - for child != 0 { - parent := (child - 1) / 2 - if (*h)[parent] > (*h)[child] { - h.swap(parent, child) - } else { - break - } - child = parent - } - - return (*h)[0] -} - -func (h *Uint32Heap) Extract() (uint32, error) { - if len(*h) == 0 { - return 0, ErrorEmptyHeap - } - min := (*h)[0] - - (*h)[0] = (*h)[len(*h)-1] - *h = (*h)[:len(*h)-1] - - parent := 0 - for { - left, right := parent*2+1, parent*2+2 - - if (left < len(*h) && (*h)[parent] > (*h)[left]) || (right < len(*h) && (*h)[parent] > (*h)[right]) { - if right < len(*h) && (*h)[left] > (*h)[right] { - h.swap(parent, right) - parent = right - } else { - h.swap(parent, left) - parent = left - } - } else { - return min, nil - } - } -} - -func (h *Uint32Heap) Peek() (uint32, error) { - if len(*h) == 0 { - return 0, ErrorEmptyHeap - } - return (*h)[0], nil -} diff --git a/utils/heap_test.go b/utils/heap_test.go deleted file mode 100644 index 61a1bbd..0000000 --- a/utils/heap_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package utils - -import ( - "fmt" - "github.com/stretchr/testify/assert" - "math/rand" - "testing" - "time" -) - -func SlowHeapSort(in []uint32) []uint32 { - out := make([]uint32, len(in)) - - var heap Uint32Heap - - for _, x := range in { - heap.Insert(x) - } - for i := range out { - var err error - out[i], err = heap.Extract() - if err != nil { - panic(err) - } - } - - return out -} - -func TestUint32Heap(t *testing.T) { - t.Run("EquivalentToMerge", func(t *testing.T) { - const ArrayLength = 50 - - sortedArray := make([]uint32, ArrayLength) - array := make([]uint32, ArrayLength) - - for i := range array { - sortedArray[i] = uint32(i) - array[i] = uint32(i) - } - - rand.Seed(time.Now().Unix()) - - for i := 0; i < 100; i++ { - t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - rand.Shuffle(50, func(i, j int) { array[i], array[j] = array[j], array[i] }) - - heapSorted := SlowHeapSort(array) - - assert.Equal(t, sortedArray, heapSorted) - }) - } - }) -} -- 2.47.0 From 2d456c9871065cfe5522e2897f8c691372abb3c2 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Mon, 22 Mar 2021 19:53:38 +0000 Subject: [PATCH 085/121] removed test logging --- udp/congestion/newreno.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index c6d70cc..353afee 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -178,10 +178,7 @@ func (c *NewReno) checkNack() { sort.Sort(c.awaitingAck) - fmt.Printf("got here: seq to consider %d, %dms ago\n", c.awaitingAck[0].sequence, time.Now().Sub(c.awaitingAck[0].time).Milliseconds()) - lossThreshold := time.Duration(c.rttNanos * RttLossDelay) - fmt.Printf("threshold time: %dms\n", lossThreshold.Milliseconds()) if c.awaitingAck[0].time.Before(time.Now().Add(-lossThreshold)) { // if the next packet sequence to ack was received more than an rttlossdelay ago // mark the packet(s) blocking it as missing with a nack -- 2.47.0 From 27f87c26452ff83a353a8f7ee8305d1dc3c8d92a Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Mon, 22 Mar 2021 19:57:54 +0000 Subject: [PATCH 086/121] fixed infinite loop --- udp/congestion/newreno.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index 353afee..5f14fcd 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -227,7 +227,7 @@ func (c *NewReno) receivedNack(nack uint32) { for { s := c.windowSize - if s > 1 && atomic.CompareAndSwapUint32(&c.windowSize, s, s/2) { + if s == 1 || atomic.CompareAndSwapUint32(&c.windowSize, s, s/2) { break } } -- 2.47.0 From 7a58c510373afdffe56ec1ddc7c148f48e1abcf3 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Mon, 22 Mar 2021 21:26:58 +0000 Subject: [PATCH 087/121] windowsize atomicity --- udp/congestion/newreno.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index 5f14fcd..4e94a41 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -260,11 +260,12 @@ func (c *NewReno) receivedAck(ack uint32) { c.inFlight = c.inFlight[i:] if c.slowStart { - c.windowSize += uint32(i) + atomic.AddUint32(&c.windowSize, uint32(i)) } else { c.windowCount += uint32(i) - if c.windowCount > c.windowSize { - c.windowCount -= uint32(i) + s := c.windowSize + if c.windowCount > s { + c.windowCount -= s atomic.AddUint32(&c.windowSize, 1) } } -- 2.47.0 From 1f98ff49ae02f70f8260ceda7aca663f7e1f8ca5 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Mon, 22 Mar 2021 21:28:33 +0000 Subject: [PATCH 088/121] removed debugging --- udp/congestion/newreno_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/udp/congestion/newreno_test.go b/udp/congestion/newreno_test.go index c20b4bc..ed3b235 100644 --- a/udp/congestion/newreno_test.go +++ b/udp/congestion/newreno_test.go @@ -2,7 +2,6 @@ package congestion import ( "context" - "fmt" "github.com/stretchr/testify/assert" "sort" "testing" @@ -52,7 +51,6 @@ func (n *newRenoTest) Start(ctx context.Context) { select { case p := <-tp: s := p.t.Add(n.halfRtt).Sub(time.Now()) - fmt.Printf("delayed packet for %dms\n", s.Milliseconds()) time.Sleep(s) cp <- p.p case <-ctx.Done(): @@ -84,7 +82,6 @@ func (n *newRenoTest) RunSideA(ctx context.Context) { case <-ctx.Done(): return case p := <-n.aInbound: - fmt.Printf("side A received: %d,%d,%d\n", p.seq, p.nack, p.ack) n.sideA.ReceivedPacket(p.seq, p.nack, p.ack) } } @@ -119,7 +116,6 @@ func (n *newRenoTest) RunSideB(ctx context.Context) { case <-ctx.Done(): return case p := <-n.bInbound: - fmt.Printf("side B received: %d,%d,%d\n", p.seq, p.nack, p.ack) n.sideB.ReceivedPacket(p.seq, p.nack, p.ack) } } -- 2.47.0 From a98e9e7902fb29c061c32bdccfbce320884961dd Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Tue, 23 Mar 2021 19:36:51 +0000 Subject: [PATCH 089/121] removed logging --- tcp/flow.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tcp/flow.go b/tcp/flow.go index b23afc4..b260a27 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -5,7 +5,6 @@ import ( "encoding/binary" "fmt" "io" - "log" "mpbl3p/proxy" "mpbl3p/shared" "net" @@ -188,9 +187,7 @@ func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { func (f *Flow) consumeMarshalled() { for { - t1 := time.Now() data := <-f.toConsume - t2 := time.Now() err := f.conn.SetWriteDeadline(time.Now().Add(5 * time.Second)) if err != nil { @@ -202,9 +199,6 @@ func (f *Flow) consumeMarshalled() { f.consumeErrors <- err return } - - t3 := time.Now() - log.Printf("consumer: `%dns` spent waiting, `%dns` spent consuming", t2.Sub(t1).Nanoseconds(), t3.Sub(t2).Nanoseconds()) } } @@ -212,7 +206,6 @@ func (f *Flow) produceMarshalled() { buf := bufio.NewReader(f.conn) for { - t1 := time.Now() lengthBytes := make([]byte, 4) if n, err := io.LimitReader(buf, 4).Read(lengthBytes); err != nil { f.produceErrors <- err @@ -235,10 +228,6 @@ func (f *Flow) produceMarshalled() { } } - t2 := time.Now() f.produced <- dataBytes - t3 := time.Now() - - log.Printf("producer: `%dns` spent producing, `%dns` spent waiting", t2.Sub(t1).Nanoseconds(), t3.Sub(t2).Nanoseconds()) } } -- 2.47.0 From 64b73b26d425c71b9120cb56f00c433d46d3bc16 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Tue, 23 Mar 2021 19:38:34 +0000 Subject: [PATCH 090/121] error earlier in consume --- tcp/flow.go | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/tcp/flow.go b/tcp/flow.go index b260a27..73e8001 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -138,11 +138,18 @@ func (f *InitiatedFlow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { return f.Flow.Produce(v) } -func (f *Flow) Consume(p proxy.Packet, g proxy.MacGenerator) (err error) { +func (f *Flow) Consume(p proxy.Packet, g proxy.MacGenerator) (error) { if !f.isAlive { return shared.ErrDeadConnection } + select { + case err := <-f.consumeErrors: + f.isAlive = false + return err + default: + } + marshalled := p.Marshal() data := proxy.AppendMac(marshalled, g) @@ -151,16 +158,7 @@ func (f *Flow) Consume(p proxy.Packet, g proxy.MacGenerator) (err error) { copy(prefixedData[4:], data) f.toConsume <- prefixedData - - select { - case err = <-f.consumeErrors: - default: - } - - if err != nil { - f.isAlive = false - } - return + return nil } func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { -- 2.47.0 From a255b609c5c6c49b958c160769bff3b1c046afcc Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Tue, 23 Mar 2021 19:39:01 +0000 Subject: [PATCH 091/121] formatting --- tcp/flow.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcp/flow.go b/tcp/flow.go index 73e8001..b24a353 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -138,7 +138,7 @@ func (f *InitiatedFlow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { return f.Flow.Produce(v) } -func (f *Flow) Consume(p proxy.Packet, g proxy.MacGenerator) (error) { +func (f *Flow) Consume(p proxy.Packet, g proxy.MacGenerator) error { if !f.isAlive { return shared.ErrDeadConnection } -- 2.47.0 From 9326593844e636dd589922b136bf2d1f07a2bad8 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Thu, 25 Mar 2021 13:45:24 +0000 Subject: [PATCH 092/121] Added flags --- flags/flags.go | 33 +++++++++++++++++++++++++++++++++ flags/locs_freebsd.go | 4 ++++ flags/locs_linux.go | 4 ++++ go.mod | 1 + go.sum | 7 ++++--- 5 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 flags/flags.go create mode 100644 flags/locs_freebsd.go create mode 100644 flags/locs_linux.go diff --git a/flags/flags.go b/flags/flags.go new file mode 100644 index 0000000..52608e7 --- /dev/null +++ b/flags/flags.go @@ -0,0 +1,33 @@ +package flags + +import ( + "fmt" + "github.com/jessevdk/go-flags" +) + +type Options struct { + Config string `short:"c" long:"config" description:"Configuration file location" value-name:"FILE"` + PidFile string `short:"p" long:"pid" description:"PID file location"` + + InterfaceName string +} + +func ParseFlags() (*Options, error) { + o := new(Options) + args, err := flags.Parse(o) + if err != nil { + return nil, err + } + + if len(args) > 0 { + o.InterfaceName = args[0] + if o.Config == "" { + o.Config = fmt.Sprintf(DefaultConfigFile, o.InterfaceName) + } + if o.PidFile == "" { + o.PidFile = fmt.Sprintf(DefaultPidFile, o.InterfaceName) + } + } + + return o, err +} diff --git a/flags/locs_freebsd.go b/flags/locs_freebsd.go new file mode 100644 index 0000000..13bbda0 --- /dev/null +++ b/flags/locs_freebsd.go @@ -0,0 +1,4 @@ +package flags + +const DefaultConfigFile = "/usr/local/etc/netcombiner/%s" +const DefaultPidFile = "/var/run/netcombiner/%s.pid" diff --git a/flags/locs_linux.go b/flags/locs_linux.go new file mode 100644 index 0000000..82a7f21 --- /dev/null +++ b/flags/locs_linux.go @@ -0,0 +1,4 @@ +package flags + +const DefaultConfigFile = "/etc/netcombiner/%s" +const DefaultPidFile = "/var/run/netcombiner/%s.pid" diff --git a/go.mod b/go.mod index edfdcc1..1157795 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.15 require ( github.com/JakeHillion/taptun v0.0.0-20210320133200-cf0ef75b1bff github.com/go-playground/validator/v10 v10.4.1 + github.com/jessevdk/go-flags v1.5.0 github.com/smartystreets/goconvey v1.6.4 // indirect github.com/stretchr/testify v1.4.0 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 diff --git a/go.sum b/go.sum index cc4beb1..91065b9 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/JakeHillion/taptun v0.0.0-20160424131934-bbbd335672ab h1:/UB98lLA11PJZOqhdzqeITMTMz6eiTjti9Z9kYq1SWQ= -github.com/JakeHillion/taptun v0.0.0-20160424131934-bbbd335672ab/go.mod h1:8WBFCKR7ZdT+WVtgyiSPJf6gqXiNZUvfglN8vwkoyBE= github.com/JakeHillion/taptun v0.0.0-20210320133200-cf0ef75b1bff h1:O+wiKpOHS2BidwDz6ZuR3dVQNsrD55raE9mY4yub6Wc= github.com/JakeHillion/taptun v0.0.0-20210320133200-cf0ef75b1bff/go.mod h1:8WBFCKR7ZdT+WVtgyiSPJf6gqXiNZUvfglN8vwkoyBE= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= @@ -14,6 +12,8 @@ github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7a github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= @@ -33,8 +33,9 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 h1:EZ2mChiOa8udjfp6rRmswTbtZN/QzUQp4ptM4rnjHvc= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -- 2.47.0 From 5587f1f52e11e8e8d43241cf756ccc207ec44804 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Thu, 25 Mar 2021 13:50:48 +0000 Subject: [PATCH 093/121] daemonize --- flags/flags.go | 8 ++++---- go.mod | 2 ++ go.sum | 4 ++++ main.go | 32 +++++++++++++++++++++++--------- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/flags/flags.go b/flags/flags.go index 52608e7..d00cae2 100644 --- a/flags/flags.go +++ b/flags/flags.go @@ -6,8 +6,8 @@ import ( ) type Options struct { - Config string `short:"c" long:"config" description:"Configuration file location" value-name:"FILE"` - PidFile string `short:"p" long:"pid" description:"PID file location"` + ConfigFile string `short:"c" long:"config" description:"Configuration file location" value-name:"FILE"` + PidFile string `short:"p" long:"pid" description:"PID file location"` InterfaceName string } @@ -21,8 +21,8 @@ func ParseFlags() (*Options, error) { if len(args) > 0 { o.InterfaceName = args[0] - if o.Config == "" { - o.Config = fmt.Sprintf(DefaultConfigFile, o.InterfaceName) + if o.ConfigFile == "" { + o.ConfigFile = fmt.Sprintf(DefaultConfigFile, o.InterfaceName) } if o.PidFile == "" { o.PidFile = fmt.Sprintf(DefaultPidFile, o.InterfaceName) diff --git a/go.mod b/go.mod index 1157795..a817bfe 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,8 @@ require ( github.com/JakeHillion/taptun v0.0.0-20210320133200-cf0ef75b1bff github.com/go-playground/validator/v10 v10.4.1 github.com/jessevdk/go-flags v1.5.0 + github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect + github.com/sevlyar/go-daemon v0.1.5 github.com/smartystreets/goconvey v1.6.4 // indirect github.com/stretchr/testify v1.4.0 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 diff --git a/go.sum b/go.sum index 91065b9..e5b003f 100644 --- a/go.sum +++ b/go.sum @@ -16,10 +16,14 @@ github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LF github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= +github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sevlyar/go-daemon v0.1.5 h1:Zy/6jLbM8CfqJ4x4RPr7MJlSKt90f00kNM1D401C+Qk= +github.com/sevlyar/go-daemon v0.1.5/go.mod h1:6dJpPatBT9eUwM5VCw9Bt6CdX9Tk6UWvhW3MebLDRKE= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= diff --git a/main.go b/main.go index 493adad..afc75a9 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,10 @@ package main import ( + "github.com/sevlyar/go-daemon" "log" "mpbl3p/config" + "mpbl3p/flags" "os" "os/signal" "syscall" @@ -11,16 +13,14 @@ import ( func main() { log.SetFlags(log.Ldate | log.Ltime | log.Llongfile) - log.Println("loading config...") - - var configLoc string - if v, ok := os.LookupEnv("CONFIG_LOC"); ok { - configLoc = v - } else { - configLoc = "config.ini" + o, err := flags.ParseFlags() + if err != nil { + panic(err) } - c, err := config.LoadConfig(configLoc) + log.Println("loading config...") + + c, err := config.LoadConfig(o.ConfigFile) if err != nil { panic(err) } @@ -34,7 +34,21 @@ func main() { log.Println("starting...") p.Start() - log.Println("running") + log.Println("forking...") + + ctx := &daemon.Context{ + PidFileName: o.PidFile, + PidFilePerm: 0644, + } + + d, err := ctx.Reborn() + if err != nil { + panic(err) + } + + if d != nil { + return + } signals := make(chan os.Signal) signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT) -- 2.47.0 From e32269cdc7452cb245ef53edb120fa0ed3a428e6 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Thu, 25 Mar 2021 13:54:12 +0000 Subject: [PATCH 094/121] interface name logic --- config/builder.go | 10 ++++++---- config/config.go | 2 -- main.go | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/config/builder.go b/config/builder.go index a5b9004..1c1e176 100644 --- a/config/builder.go +++ b/config/builder.go @@ -5,6 +5,7 @@ import ( "fmt" "mpbl3p/crypto" "mpbl3p/crypto/sharedkey" + "mpbl3p/flags" "mpbl3p/proxy" "mpbl3p/tcp" "mpbl3p/tun" @@ -13,7 +14,7 @@ import ( "time" ) -func (c Configuration) Build() (*proxy.Proxy, error) { +func (c Configuration) Build(o *flags.Options) (*proxy.Proxy, error) { p := proxy.NewProxy(0) var g func() proxy.MacGenerator @@ -41,11 +42,12 @@ func (c Configuration) Build() (*proxy.Proxy, error) { } } - if c.Host.InterfaceName == "" { - c.Host.InterfaceName = "nc%d" + ifName := o.InterfaceName + if ifName == "" { + ifName = "nc%d" } - ss, err := tun.NewTun(c.Host.InterfaceName, 1500) + ss, err := tun.NewTun(ifName, 1500) if err != nil { return nil, err } diff --git a/config/config.go b/config/config.go index 8cbb77e..237732a 100644 --- a/config/config.go +++ b/config/config.go @@ -38,8 +38,6 @@ type Configuration struct { } type Host struct { - InterfaceName string - Crypto string `validate:"required,oneof=None Blake2s"` SharedKey string `validate:"required_if=Crypto Blake2s"` } diff --git a/main.go b/main.go index afc75a9..62ede12 100644 --- a/main.go +++ b/main.go @@ -26,7 +26,7 @@ func main() { } log.Println("building config...") - p, err := c.Build() + p, err := c.Build(o) if err != nil { panic(err) } -- 2.47.0 From 5daef1845e2a21f42ee1fd28bd64735595f42b0c Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Thu, 25 Mar 2021 16:56:11 +0000 Subject: [PATCH 095/121] context releasing --- main.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/main.go b/main.go index 62ede12..c520c40 100644 --- a/main.go +++ b/main.go @@ -49,6 +49,11 @@ func main() { if d != nil { return } + defer func() { + if err := ctx.Release(); err != nil { + panic(err) + } + }() signals := make(chan os.Signal) signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT) -- 2.47.0 From 1944eb620a6a748f1aeef4ebc2718f9e2086ea14 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Fri, 26 Mar 2021 11:58:03 +0000 Subject: [PATCH 096/121] started daemonisation --- config/builder.go | 18 ++------- config/config.go | 1 + flags/flags.go | 32 ++++++++++------ go.mod | 5 +-- go.sum | 16 +++++--- main.go | 93 +++++++++++++++++++++++++++++++++++------------ tun/tun.go | 42 +++++++++++++++------ 7 files changed, 137 insertions(+), 70 deletions(-) diff --git a/config/builder.go b/config/builder.go index 1c1e176..318073a 100644 --- a/config/builder.go +++ b/config/builder.go @@ -5,16 +5,14 @@ import ( "fmt" "mpbl3p/crypto" "mpbl3p/crypto/sharedkey" - "mpbl3p/flags" "mpbl3p/proxy" "mpbl3p/tcp" - "mpbl3p/tun" "mpbl3p/udp" "mpbl3p/udp/congestion" "time" ) -func (c Configuration) Build(o *flags.Options) (*proxy.Proxy, error) { +func (c Configuration) Build(source proxy.Source, sink proxy.Sink) (*proxy.Proxy, error) { p := proxy.NewProxy(0) var g func() proxy.MacGenerator @@ -42,18 +40,8 @@ func (c Configuration) Build(o *flags.Options) (*proxy.Proxy, error) { } } - ifName := o.InterfaceName - if ifName == "" { - ifName = "nc%d" - } - - ss, err := tun.NewTun(ifName, 1500) - if err != nil { - return nil, err - } - - p.Source = ss - p.Sink = ss + p.Source = source + p.Sink = sink for _, peer := range c.Peers { switch peer.Method { diff --git a/config/config.go b/config/config.go index 237732a..ec8642b 100644 --- a/config/config.go +++ b/config/config.go @@ -40,6 +40,7 @@ type Configuration struct { type Host struct { Crypto string `validate:"required,oneof=None Blake2s"` SharedKey string `validate:"required_if=Crypto Blake2s"` + MTU uint } type Peer struct { diff --git a/flags/flags.go b/flags/flags.go index d00cae2..236d72a 100644 --- a/flags/flags.go +++ b/flags/flags.go @@ -1,33 +1,41 @@ package flags import ( + "errors" "fmt" - "github.com/jessevdk/go-flags" + goflags "github.com/jessevdk/go-flags" + "os" ) +var PrintedHelpErr = goflags.ErrHelp +var NotEnoughArgs = errors.New("not enough arguments") + type Options struct { + Foreground bool `short:"f" long:"foreground" description:"Run in the foreground"` ConfigFile string `short:"c" long:"config" description:"Configuration file location" value-name:"FILE"` PidFile string `short:"p" long:"pid" description:"PID file location"` - InterfaceName string + Positional struct { + InterfaceName string `required:"yes" positional-arg-name:"INTERFACE-NAME" description:"Interface name"` + } `positional-args:"yes"` } func ParseFlags() (*Options, error) { o := new(Options) - args, err := flags.Parse(o) + parser := goflags.NewParser(o, goflags.Default) + + _, err := parser.Parse() if err != nil { + parser.WriteHelp(os.Stdout) return nil, err } - if len(args) > 0 { - o.InterfaceName = args[0] - if o.ConfigFile == "" { - o.ConfigFile = fmt.Sprintf(DefaultConfigFile, o.InterfaceName) - } - if o.PidFile == "" { - o.PidFile = fmt.Sprintf(DefaultPidFile, o.InterfaceName) - } + if o.ConfigFile == "" { + o.ConfigFile = fmt.Sprintf(DefaultConfigFile, o.Positional.InterfaceName) + } + if o.PidFile == "" { + o.PidFile = fmt.Sprintf(DefaultPidFile, o.Positional.InterfaceName) } - return o, err + return o, nil } diff --git a/go.mod b/go.mod index a817bfe..3556aad 100644 --- a/go.mod +++ b/go.mod @@ -6,10 +6,9 @@ require ( github.com/JakeHillion/taptun v0.0.0-20210320133200-cf0ef75b1bff github.com/go-playground/validator/v10 v10.4.1 github.com/jessevdk/go-flags v1.5.0 - github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect - github.com/sevlyar/go-daemon v0.1.5 github.com/smartystreets/goconvey v1.6.4 // indirect github.com/stretchr/testify v1.4.0 - golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 + golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9 + golang.zx2c4.com/wireguard v0.0.20201118 gopkg.in/ini.v1 v1.62.0 ) diff --git a/go.sum b/go.sum index e5b003f..24efca5 100644 --- a/go.sum +++ b/go.sum @@ -16,14 +16,10 @@ github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LF github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= -github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sevlyar/go-daemon v0.1.5 h1:Zy/6jLbM8CfqJ4x4RPr7MJlSKt90f00kNM1D401C+Qk= -github.com/sevlyar/go-daemon v0.1.5/go.mod h1:6dJpPatBT9eUwM5VCw9Bt6CdX9Tk6UWvhW3MebLDRKE= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= @@ -32,18 +28,28 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9 h1:phUcVbl53swtrUN8kQEXFhUxPlIlWyBfKmidCu7P95o= +golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201117222635-ba5294a509c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 h1:EZ2mChiOa8udjfp6rRmswTbtZN/QzUQp4ptM4rnjHvc= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.zx2c4.com/wireguard v0.0.20201118 h1:QL8y2C7uO8T6z1GY+UX/hSeWiYEBurQkXjOTRFtCvXU= +golang.zx2c4.com/wireguard v0.0.20201118/go.mod h1:Dz+cq5bnrai9EpgYj4GDof/+qaGzbRWbeaAOs1bUYa0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= diff --git a/main.go b/main.go index c520c40..e0d0c20 100644 --- a/main.go +++ b/main.go @@ -1,62 +1,109 @@ package main import ( - "github.com/sevlyar/go-daemon" + "errors" + "fmt" "log" "mpbl3p/config" "mpbl3p/flags" + "mpbl3p/tun" "os" "os/signal" + "strconv" "syscall" ) +const ( + ENV_NC_TUN_FD = "NC_TUN_FD" + ENV_NC_CONFIG_PATH = "NC_CONFIG_PATH" +) + func main() { log.SetFlags(log.Ldate | log.Ltime | log.Llongfile) - o, err := flags.ParseFlags() - if err != nil { - panic(err) + if _, exists := os.LookupEnv(ENV_NC_TUN_FD); !exists { + // we are the parent process + // 1) process arguments + // 2) validate config + // 2) create a tun adapter + // 3) spawn a child + // 4) exit + o, err := flags.ParseFlags() + if err != nil { + if errors.Is(err, flags.PrintedHelpErr) { + return + } + panic(err) + } + + log.Println("loading config...") + + c, err := config.LoadConfig(o.ConfigFile) + if err != nil { + panic(err) + } + + log.Println("creating tun adapter...") + t, err := tun.NewTun(o.Positional.InterfaceName, int(c.Host.MTU)) + if err != nil { + panic(err) + } + + if o.Foreground { + if err := os.Setenv(ENV_NC_TUN_FD, fmt.Sprintf("%d", t.File().Fd())); err != nil { + panic(err) + } + if err := os.Setenv(ENV_NC_CONFIG_PATH, o.ConfigFile); err != nil { + panic(err) + } + goto FOREGROUND + } + return } + // we are the child process + // 1) recreate tun adapter from file descriptor + // 2) launch proxy + +FOREGROUND: + log.Println("loading config...") - c, err := config.LoadConfig(o.ConfigFile) + c, err := config.LoadConfig(os.Getenv(ENV_NC_CONFIG_PATH)) if err != nil { panic(err) } - log.Println("building config...") - p, err := c.Build(o) + log.Println("connecting tun adapter...") + tunFd, err := strconv.ParseUint(os.Getenv(ENV_NC_TUN_FD), 10, 64) if err != nil { panic(err) } - log.Println("starting...") - p.Start() - - log.Println("forking...") - - ctx := &daemon.Context{ - PidFileName: o.PidFile, - PidFilePerm: 0644, - } - - d, err := ctx.Reborn() + t, err := tun.NewFromFile(uintptr(tunFd), int(c.Host.MTU)) if err != nil { panic(err) } - - if d != nil { - return - } defer func() { - if err := ctx.Release(); err != nil { + if err := t.Close(); err != nil { panic(err) } }() + log.Println("building config...") + p, err := c.Build(t, t) + if err != nil { + panic(err) + } + + log.Println("starting proxy...") + p.Start() + + log.Println("proxy started") + signals := make(chan os.Signal) signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT) <-signals + log.Println("exiting...") } diff --git a/tun/tun.go b/tun/tun.go index d76fabb..0e908ed 100644 --- a/tun/tun.go +++ b/tun/tun.go @@ -1,7 +1,7 @@ package tun import ( - "github.com/JakeHillion/taptun" + wgtun "golang.zx2c4.com/wireguard/tun" "io" "log" "mpbl3p/proxy" @@ -13,34 +13,45 @@ import ( ) type SourceSink struct { - tun *taptun.Tun - bufferSize int + tun wgtun.Device + mtu int up bool upMu sync.Mutex } -func NewTun(namingScheme string, bufferSize int) (ss *SourceSink, err error) { - ss = &SourceSink{} +func NewTun(name string, mtu int) (t wgtun.Device, err error) { + return wgtun.CreateTUN(name, mtu) +} - ss.tun, err = taptun.NewTun(namingScheme) +func NewFromFile(fd uintptr, mtu int) (ss *SourceSink, err error) { + ss = new(SourceSink) + + file := os.NewFile(fd, "") + ss.tun, err = wgtun.CreateTUNFromFile(file, mtu) if err != nil { return } - ss.bufferSize = bufferSize + ss.mtu = mtu ss.upMu.Lock() go func() { defer ss.upMu.Unlock() for { - iface, err := net.InterfaceByName(ss.tun.String()) + tunName, err := ss.tun.Name() if err != nil { panic(err) } + iface, err := net.InterfaceByName(tunName) + if err != nil { + panic(err) + } + + if strings.Contains(iface.Flags.String(), "up") { - log.Println("tun is up") + log.Println("wgtun is up") ss.up = true return } @@ -51,15 +62,22 @@ func NewTun(namingScheme string, bufferSize int) (ss *SourceSink, err error) { return } +func (t *SourceSink) Close() error { + t.upMu.Lock() + t.up = false + + return t.tun.Close() +} + func (t *SourceSink) Source() (proxy.Packet, error) { if !t.up { t.upMu.Lock() t.upMu.Unlock() } - buf := make([]byte, t.bufferSize) + buf := make([]byte, t.mtu) - read, err := t.tun.Read(buf) + read, err := t.tun.Read(buf, t.mtu) if err != nil { return nil, err } @@ -79,7 +97,7 @@ func (t *SourceSink) Sink(packet proxy.Packet) error { t.upMu.Unlock() } - _, err := t.tun.Write(packet.Contents()) + _, err := t.tun.Write(packet.Contents(), t.mtu) if err != nil { switch err.(type) { case *os.PathError: -- 2.47.0 From e8bb8910e7fcc1f988e0fccf899c7c36a8039ea2 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Fri, 26 Mar 2021 18:13:39 +0000 Subject: [PATCH 097/121] implemented child process --- config/config.go | 2 +- main.go | 33 +++++++++++++++++++++++++++++++++ tun/tun.go | 6 +++--- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/config/config.go b/config/config.go index ec8642b..d6a2db1 100644 --- a/config/config.go +++ b/config/config.go @@ -40,7 +40,7 @@ type Configuration struct { type Host struct { Crypto string `validate:"required,oneof=None Blake2s"` SharedKey string `validate:"required_if=Crypto Blake2s"` - MTU uint + MTU uint `validate:"required,min=576"` } type Peer struct { diff --git a/main.go b/main.go index e0d0c20..0066662 100644 --- a/main.go +++ b/main.go @@ -28,6 +28,7 @@ func main() { // 2) create a tun adapter // 3) spawn a child // 4) exit + o, err := flags.ParseFlags() if err != nil { if errors.Is(err, flags.PrintedHelpErr) { @@ -56,8 +57,40 @@ func main() { if err := os.Setenv(ENV_NC_CONFIG_PATH, o.ConfigFile); err != nil { panic(err) } + log.Println("switching to foreground") goto FOREGROUND } + + files := make([]*os.File, 4) + files[0], _ = os.Open(os.DevNull) // stdin + files[1], _ = os.Open(os.DevNull) // stderr + files[2], _ = os.Open(os.DevNull) // stdout + files[3] = t.File() + + env := os.Environ() + env = append(env, fmt.Sprintf("%s=3", ENV_NC_TUN_FD)) + env = append(env, fmt.Sprintf("%s=%s", ENV_NC_CONFIG_PATH, o.ConfigFile)) + + attr := os.ProcAttr{ + Env: env, + Files: files, + } + + path, err := os.Executable() + if err != nil { + panic(err) + } + + process, err := os.StartProcess( + path, + os.Args, + &attr, + ) + if err != nil { + panic(err) + } + + _ = process.Release() return } diff --git a/tun/tun.go b/tun/tun.go index 0e908ed..a936a82 100644 --- a/tun/tun.go +++ b/tun/tun.go @@ -51,7 +51,7 @@ func NewFromFile(fd uintptr, mtu int) (ss *SourceSink, err error) { if strings.Contains(iface.Flags.String(), "up") { - log.Println("wgtun is up") + log.Println("tun is up") ss.up = true return } @@ -77,7 +77,7 @@ func (t *SourceSink) Source() (proxy.Packet, error) { buf := make([]byte, t.mtu) - read, err := t.tun.Read(buf, t.mtu) + read, err := t.tun.Read(buf, 4) if err != nil { return nil, err } @@ -97,7 +97,7 @@ func (t *SourceSink) Sink(packet proxy.Packet) error { t.upMu.Unlock() } - _, err := t.tun.Write(packet.Contents(), t.mtu) + _, err := t.tun.Write(packet.Contents(), 4) if err != nil { switch err.(type) { case *os.PathError: -- 2.47.0 From 51a66c89118d607d2e0efac2c7f7c93465da1b50 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Fri, 26 Mar 2021 20:29:12 +0000 Subject: [PATCH 098/121] formatting --- config/config.go | 2 +- tun/tun.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index d6a2db1..fc2260f 100644 --- a/config/config.go +++ b/config/config.go @@ -40,7 +40,7 @@ type Configuration struct { type Host struct { Crypto string `validate:"required,oneof=None Blake2s"` SharedKey string `validate:"required_if=Crypto Blake2s"` - MTU uint `validate:"required,min=576"` + MTU uint `validate:"required,min=576"` } type Peer struct { diff --git a/tun/tun.go b/tun/tun.go index a936a82..b1057ff 100644 --- a/tun/tun.go +++ b/tun/tun.go @@ -49,7 +49,6 @@ func NewFromFile(fd uintptr, mtu int) (ss *SourceSink, err error) { panic(err) } - if strings.Contains(iface.Flags.String(), "up") { log.Println("tun is up") ss.up = true -- 2.47.0 From 62ae0c105903e50d23288761608e4c95e38ef2f5 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Fri, 26 Mar 2021 20:31:31 +0000 Subject: [PATCH 099/121] gomod --- go.mod | 4 ++-- go.sum | 11 +++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 3556aad..0ea3fd4 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,12 @@ module mpbl3p go 1.15 require ( - github.com/JakeHillion/taptun v0.0.0-20210320133200-cf0ef75b1bff github.com/go-playground/validator/v10 v10.4.1 github.com/jessevdk/go-flags v1.5.0 github.com/smartystreets/goconvey v1.6.4 // indirect github.com/stretchr/testify v1.4.0 - golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9 + golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 + golang.org/x/net v0.0.0-20210326060303-6b1517762897 // indirect golang.zx2c4.com/wireguard v0.0.20201118 gopkg.in/ini.v1 v1.62.0 ) diff --git a/go.sum b/go.sum index 24efca5..2a75a7c 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/JakeHillion/taptun v0.0.0-20210320133200-cf0ef75b1bff h1:O+wiKpOHS2BidwDz6ZuR3dVQNsrD55raE9mY4yub6Wc= -github.com/JakeHillion/taptun v0.0.0-20210320133200-cf0ef75b1bff/go.mod h1:8WBFCKR7ZdT+WVtgyiSPJf6gqXiNZUvfglN8vwkoyBE= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= @@ -31,18 +29,27 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9 h1:phUcVbl53swtrUN8kQEXFhUxPlIlWyBfKmidCu7P95o= golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210326060303-6b1517762897 h1:KrsHThm5nFk34YtATK1LsThyGhGbGe1olrte/HInHvs= +golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201117222635-ba5294a509c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 h1:EZ2mChiOa8udjfp6rRmswTbtZN/QzUQp4ptM4rnjHvc= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492 h1:Paq34FxTluEPvVyayQqMPgHm+vTOrIifmcYxFBx9TLg= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -- 2.47.0 From 38e60233b0a3e522ad666bc7f875eb7491338f76 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Fri, 26 Mar 2021 20:40:40 +0000 Subject: [PATCH 100/121] use github for wireguard dep --- go.mod | 2 ++ go.sum | 7 ++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 0ea3fd4..4337438 100644 --- a/go.mod +++ b/go.mod @@ -12,3 +12,5 @@ require ( golang.zx2c4.com/wireguard v0.0.20201118 gopkg.in/ini.v1 v1.62.0 ) + +replace golang.zx2c4.com/wireguard v0.0.20201118 => github.com/Wireguard/wireguard-go v0.0.20201118 diff --git a/go.sum b/go.sum index 2a75a7c..759329b 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/Wireguard/wireguard-go v0.0.20201118 h1:1rSMjOCDNTvE0r1VEOEilxgMHAFaD/Jm/fJpE2dCENI= +github.com/Wireguard/wireguard-go v0.0.20201118/go.mod h1:Dz+cq5bnrai9EpgYj4GDof/+qaGzbRWbeaAOs1bUYa0= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= @@ -27,13 +29,11 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9 h1:phUcVbl53swtrUN8kQEXFhUxPlIlWyBfKmidCu7P95o= golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210326060303-6b1517762897 h1:KrsHThm5nFk34YtATK1LsThyGhGbGe1olrte/HInHvs= @@ -44,7 +44,6 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201117222635-ba5294a509c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4 h1:EZ2mChiOa8udjfp6rRmswTbtZN/QzUQp4ptM4rnjHvc= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492 h1:Paq34FxTluEPvVyayQqMPgHm+vTOrIifmcYxFBx9TLg= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -55,8 +54,6 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.zx2c4.com/wireguard v0.0.20201118 h1:QL8y2C7uO8T6z1GY+UX/hSeWiYEBurQkXjOTRFtCvXU= -golang.zx2c4.com/wireguard v0.0.20201118/go.mod h1:Dz+cq5bnrai9EpgYj4GDof/+qaGzbRWbeaAOs1bUYa0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= -- 2.47.0 From d84cc6dc274fd86f3602f7169032ebc476a7000c Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Fri, 26 Mar 2021 20:41:54 +0000 Subject: [PATCH 101/121] specify wireguard commit --- go.mod | 4 +--- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 4337438..7682057 100644 --- a/go.mod +++ b/go.mod @@ -9,8 +9,6 @@ require ( github.com/stretchr/testify v1.4.0 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 golang.org/x/net v0.0.0-20210326060303-6b1517762897 // indirect - golang.zx2c4.com/wireguard v0.0.20201118 + golang.zx2c4.com/wireguard v0.0.0-20201118132417-da19db415a58 gopkg.in/ini.v1 v1.62.0 ) - -replace golang.zx2c4.com/wireguard v0.0.20201118 => github.com/Wireguard/wireguard-go v0.0.20201118 diff --git a/go.sum b/go.sum index 759329b..04dd5cd 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/Wireguard/wireguard-go v0.0.20201118 h1:1rSMjOCDNTvE0r1VEOEilxgMHAFaD/Jm/fJpE2dCENI= -github.com/Wireguard/wireguard-go v0.0.20201118/go.mod h1:Dz+cq5bnrai9EpgYj4GDof/+qaGzbRWbeaAOs1bUYa0= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= @@ -54,6 +52,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.zx2c4.com/wireguard v0.0.0-20201118132417-da19db415a58 h1:HiPOx0boQr3qv0HkZ4fGLtTXJ5tmkbv0d8UmkNcxdv0= +golang.zx2c4.com/wireguard v0.0.0-20201118132417-da19db415a58/go.mod h1:Dz+cq5bnrai9EpgYj4GDof/+qaGzbRWbeaAOs1bUYa0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= -- 2.47.0 From 9f75a7a432586beef51092cc95b23eb68acf8d21 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Fri, 26 Mar 2021 20:59:54 +0000 Subject: [PATCH 102/121] pid file --- main.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/main.go b/main.go index 0066662..c995e04 100644 --- a/main.go +++ b/main.go @@ -90,6 +90,14 @@ func main() { panic(err) } + pidFile, err := os.Create(o.PidFile) + if err != nil { + panic(err) + } + if _, err := fmt.Fprintf(pidFile, "%d", process.Pid); err != nil { + panic(err) + } + _ = process.Release() return } -- 2.47.0 From 8e58995a1449684abdfa0d3bd123b8574dcba255 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 27 Mar 2021 17:23:03 +0000 Subject: [PATCH 103/121] added freebsd amd64 --- .drone.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.drone.yml b/.drone.yml index 8c66333..9dc816d 100644 --- a/.drone.yml +++ b/.drone.yml @@ -38,6 +38,7 @@ steps: commands: - GOOS=linux GOARCH=amd64 go build -o linux_amd64 - GOOS=linux GOARCH=arm GOARM=7 go build -o linux_arm_v7 + - GOOS=freebsd GOARCH=amd64 go build -o freebsd_amd64 - GOOS=freebsd GOARCH=arm64 go build -o freebsd_arm64_v8a - name: upload -- 2.47.0 From dd6e92eae04bdb8002da034dfdb44261bd3944cb Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 27 Mar 2021 17:24:12 +0000 Subject: [PATCH 104/121] uploaded freebsd amd64 --- .drone.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.drone.yml b/.drone.yml index 9dc816d..48b1f36 100644 --- a/.drone.yml +++ b/.drone.yml @@ -52,9 +52,10 @@ steps: from_secret: s3_secret_key commands: - mc alias set s3 https://s3.us-west-001.backblazeb2.com $${ACCESS_KEY} $${SECRET_KEY} - - mc cp linux_amd64 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_amd64 - - mc cp linux_arm_v7 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_arm_v7 - - mc cp freebsd_arm64_v8a s3/dissertation/binaries/debian/${DRONE_BRANCH}_freebsd_arm64_v8a + - mc cp linux_amd64 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_amd64 + - mc cp linux_arm_v7 s3/dissertation/binaries/debian/${DRONE_BRANCH}_linux_arm_v7 + - mc cp freebsd_amd64 s3/dissertation/binaries/debian/${DRONE_BRANCH}_freebsd_amd64 + - mc cp freebsd_arm64_v8a s3/dissertation/binaries/debian/${DRONE_BRANCH}_freebsd_arm64_v8a volumes: - name: cache -- 2.47.0 From f69bb47966dad960f8d3af566f416a3a94da6ebd Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sat, 27 Mar 2021 17:26:56 +0000 Subject: [PATCH 105/121] signed dronefile --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index 48b1f36..d5701b8 100644 --- a/.drone.yml +++ b/.drone.yml @@ -63,6 +63,6 @@ volumes: --- kind: signature -hmac: de07a3ab113028b48c590c406f7ab8f74aeae49679287862168d912ec10e9920 +hmac: 7960420c7d02f9bce56d6429b612676d24cbe1d1608cf44a77da9afc411eccb8 ... -- 2.47.0 From 710f89446bc4f04cb939352a8fad084af597c349 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Sun, 28 Mar 2021 19:49:25 +0100 Subject: [PATCH 106/121] removed up check and header --- tun/tun.go | 46 +--------------------------------------------- 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/tun/tun.go b/tun/tun.go index b1057ff..4b651ca 100644 --- a/tun/tun.go +++ b/tun/tun.go @@ -5,19 +5,12 @@ import ( "io" "log" "mpbl3p/proxy" - "net" "os" - "strings" - "sync" - "time" ) type SourceSink struct { tun wgtun.Device mtu int - - up bool - upMu sync.Mutex } func NewTun(name string, mtu int) (t wgtun.Device, err error) { @@ -34,46 +27,14 @@ func NewFromFile(fd uintptr, mtu int) (ss *SourceSink, err error) { } ss.mtu = mtu - - ss.upMu.Lock() - go func() { - defer ss.upMu.Unlock() - - for { - tunName, err := ss.tun.Name() - if err != nil { - panic(err) - } - iface, err := net.InterfaceByName(tunName) - if err != nil { - panic(err) - } - - if strings.Contains(iface.Flags.String(), "up") { - log.Println("tun is up") - ss.up = true - return - } - time.Sleep(100 * time.Millisecond) - } - }() - return } func (t *SourceSink) Close() error { - t.upMu.Lock() - t.up = false - return t.tun.Close() } func (t *SourceSink) Source() (proxy.Packet, error) { - if !t.up { - t.upMu.Lock() - t.upMu.Unlock() - } - buf := make([]byte, t.mtu) read, err := t.tun.Read(buf, 4) @@ -85,17 +46,12 @@ func (t *SourceSink) Source() (proxy.Packet, error) { return nil, io.EOF } - return proxy.SimplePacket(buf[:read]), nil + return proxy.SimplePacket(buf[4:read]), nil } var good, bad float64 func (t *SourceSink) Sink(packet proxy.Packet) error { - if !t.up { - t.upMu.Lock() - t.upMu.Unlock() - } - _, err := t.tun.Write(packet.Contents(), 4) if err != nil { switch err.(type) { -- 2.47.0 From fad829803aa52b96dc3724ae9bd0c6d1ec8ae3ba Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Tue, 30 Mar 2021 20:57:53 +0100 Subject: [PATCH 107/121] initial context propagation --- config/builder.go | 23 ++++----- main.go | 6 ++- proxy/proxy.go | 57 +++++++++++++++++----- tcp/flow.go | 22 ++++++--- tcp/flow_test.go | 7 +-- tcp/listener.go | 7 +-- udp/congestion.go | 9 ++-- udp/congestion/newreno.go | 19 +++++--- udp/congestion/newreno_test.go | 22 ++++----- udp/congestion/none.go | 43 ++++++----------- udp/flow.go | 87 ++++++++++++++++++++++------------ udp/flow_test.go | 7 +-- udp/listener.go | 20 ++++---- 13 files changed, 200 insertions(+), 129 deletions(-) diff --git a/config/builder.go b/config/builder.go index 318073a..b83995c 100644 --- a/config/builder.go +++ b/config/builder.go @@ -1,6 +1,7 @@ package config import ( + "context" "encoding/base64" "fmt" "mpbl3p/crypto" @@ -12,7 +13,7 @@ import ( "time" ) -func (c Configuration) Build(source proxy.Source, sink proxy.Sink) (*proxy.Proxy, error) { +func (c Configuration) Build(ctx context.Context, source proxy.Source, sink proxy.Sink) (*proxy.Proxy, error) { p := proxy.NewProxy(0) var g func() proxy.MacGenerator @@ -46,11 +47,11 @@ func (c Configuration) Build(source proxy.Source, sink proxy.Sink) (*proxy.Proxy for _, peer := range c.Peers { switch peer.Method { case "TCP": - if err := buildTcp(p, peer, g, v); err != nil { + if err := buildTcp(ctx, p, peer, g, v); err != nil { return nil, err } case "UDP": - if err := buildUdp(p, peer, g, v); err != nil { + if err := buildUdp(ctx, p, peer, g, v); err != nil { return nil, err } } @@ -59,7 +60,7 @@ func (c Configuration) Build(source proxy.Source, sink proxy.Sink) (*proxy.Proxy return p, nil } -func buildTcp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() proxy.MacVerifier) error { +func buildTcp(ctx context.Context, p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() proxy.MacVerifier) error { var laddr func() string if peer.LocalPort == 0 { laddr = func() string { return fmt.Sprintf("%s:", peer.GetLocalHost()) } @@ -74,13 +75,13 @@ func buildTcp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() p return err } - p.AddConsumer(f, g()) - p.AddProducer(f, v()) + p.AddConsumer(ctx, f, g()) + p.AddProducer(ctx, f, v()) return nil } - err := tcp.NewListener(p, laddr(), v, g) + err := tcp.NewListener(ctx, p, laddr(), v, g) if err != nil { return err } @@ -88,7 +89,7 @@ func buildTcp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() p return nil } -func buildUdp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() proxy.MacVerifier) error { +func buildUdp(ctx context.Context, p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() proxy.MacVerifier) error { var laddr func() string if peer.LocalPort == 0 { laddr = func() string { return fmt.Sprintf("%s:", peer.GetLocalHost()) } @@ -120,13 +121,13 @@ func buildUdp(p *proxy.Proxy, peer Peer, g func() proxy.MacGenerator, v func() p return err } - p.AddConsumer(f, g()) - p.AddProducer(f, v()) + p.AddConsumer(ctx, f, g()) + p.AddProducer(ctx, f, v()) return nil } - err := udp.NewListener(p, laddr(), v, g, c) + err := udp.NewListener(ctx, p, laddr(), v, g, c) if err != nil { return err } diff --git a/main.go b/main.go index c995e04..0c31e25 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "context" "errors" "fmt" "log" @@ -132,7 +133,10 @@ FOREGROUND: }() log.Println("building config...") - p, err := c.Build(t, t) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + p, err := c.Build(ctx, t, t) if err != nil { panic(err) } diff --git a/proxy/proxy.go b/proxy/proxy.go index a82698b..593d65b 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -1,22 +1,24 @@ package proxy import ( + "context" + "errors" "log" "time" ) type Producer interface { IsAlive() bool - Produce(MacVerifier) (Packet, error) + Produce(context.Context, MacVerifier) (Packet, error) } type Consumer interface { IsAlive() bool - Consume(Packet, MacGenerator) error + Consume(context.Context, Packet, MacGenerator) error } type Reconnectable interface { - Reconnect() error + Reconnect(context.Context) error } type Source interface { @@ -65,7 +67,7 @@ func (p Proxy) Start() { }() } -func (p Proxy) AddConsumer(c Consumer, g MacGenerator) { +func (p Proxy) AddConsumer(ctx context.Context, c Consumer, g MacGenerator) { go func() { _, reconnectable := c.(Reconnectable) @@ -74,7 +76,11 @@ func (p Proxy) AddConsumer(c Consumer, g MacGenerator) { var err error for once := true; err != nil || once; once = false { log.Printf("attempting to connect consumer `%v`\n", c) - err = c.(Reconnectable).Reconnect() + err = c.(Reconnectable).Reconnect(ctx) + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + log.Printf("closed consumer `%v` (context)\n", c) + return + } if !once { time.Sleep(time.Second) } @@ -83,9 +89,19 @@ func (p Proxy) AddConsumer(c Consumer, g MacGenerator) { } for c.IsAlive() { - if err := c.Consume(<-p.proxyChan, g); err != nil { - log.Println(err) - break + select { + case <-ctx.Done(): + log.Printf("closed consumer `%v` (context)\n", c) + return + case packet := <-p.proxyChan: + if err := c.Consume(ctx, packet, g); err != nil { + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + log.Printf("closed consumer `%v` (context)\n", c) + return + } + log.Println(err) + break + } } } } @@ -94,7 +110,7 @@ func (p Proxy) AddConsumer(c Consumer, g MacGenerator) { }() } -func (p Proxy) AddProducer(pr Producer, v MacVerifier) { +func (p Proxy) AddProducer(ctx context.Context, pr Producer, v MacVerifier) { go func() { _, reconnectable := pr.(Reconnectable) @@ -103,20 +119,37 @@ func (p Proxy) AddProducer(pr Producer, v MacVerifier) { var err error for once := true; err != nil || once; once = false { log.Printf("attempting to connect producer `%v`\n", pr) - err = pr.(Reconnectable).Reconnect() + err = pr.(Reconnectable).Reconnect(ctx) + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + log.Printf("closed producer `%v` (context)\n", pr) + return + } if !once { time.Sleep(time.Second) } + if ctx.Err() != nil { + return + } + } log.Printf("connected producer `%v`\n", pr) } for pr.IsAlive() { - if packet, err := pr.Produce(v); err != nil { + if packet, err := pr.Produce(ctx, v); err != nil { + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + log.Printf("closed producer `%v` (context)\n", pr) + return + } log.Println(err) break } else { - p.sinkChan <- packet + select { + case <-ctx.Done(): + log.Printf("closed producer `%v` (context)\n", pr) + return + case p.sinkChan <- packet: + } } } } diff --git a/tcp/flow.go b/tcp/flow.go index b24a353..cf59fde 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -2,6 +2,7 @@ package tcp import ( "bufio" + "context" "encoding/binary" "fmt" "io" @@ -124,21 +125,21 @@ func (f *InitiatedFlow) Reconnect() error { return nil } -func (f *InitiatedFlow) Consume(p proxy.Packet, g proxy.MacGenerator) error { +func (f *InitiatedFlow) Consume(ctx context.Context, p proxy.Packet, g proxy.MacGenerator) error { f.mu.RLock() defer f.mu.RUnlock() - return f.Flow.Consume(p, g) + return f.Flow.Consume(ctx, p, g) } -func (f *InitiatedFlow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { +func (f *InitiatedFlow) Produce(ctx context.Context, v proxy.MacVerifier) (proxy.Packet, error) { f.mu.RLock() defer f.mu.RUnlock() - return f.Flow.Produce(v) + return f.Flow.Produce(ctx, v) } -func (f *Flow) Consume(p proxy.Packet, g proxy.MacGenerator) error { +func (f *Flow) Consume(ctx context.Context, p proxy.Packet, g proxy.MacGenerator) error { if !f.isAlive { return shared.ErrDeadConnection } @@ -157,11 +158,16 @@ func (f *Flow) Consume(p proxy.Packet, g proxy.MacGenerator) error { binary.LittleEndian.PutUint32(prefixedData, uint32(len(data))) copy(prefixedData[4:], data) - f.toConsume <- prefixedData + select { + case f.toConsume <- prefixedData: + case <-ctx.Done(): + return ctx.Err() + } + return nil } -func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { +func (f *Flow) Produce(ctx context.Context, v proxy.MacVerifier) (proxy.Packet, error) { if !f.isAlive { return nil, shared.ErrDeadConnection } @@ -169,6 +175,8 @@ func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { var data []byte select { + case <-ctx.Done(): + return nil, ctx.Err() case data = <-f.produced: case err := <-f.produceErrors: f.isAlive = false diff --git a/tcp/flow_test.go b/tcp/flow_test.go index f0f3e21..3d0b49c 100644 --- a/tcp/flow_test.go +++ b/tcp/flow_test.go @@ -1,6 +1,7 @@ package tcp import ( + "context" "encoding/binary" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -19,7 +20,7 @@ func TestFlow_Consume(t *testing.T) { flowA := NewFlowConn(testConn.SideA()) - err := flowA.Consume(testPacket, testMac) + err := flowA.Consume(context.Background(), testPacket, testMac) require.Nil(t, err) buf := make([]byte, 100) @@ -46,7 +47,7 @@ func TestFlow_Produce(t *testing.T) { _, err := testConn.SideB().Write(testMarshalled) require.Nil(t, err) - p, err := flowA.Produce(testMac) + p, err := flowA.Produce(context.Background(), testMac) require.Nil(t, err) assert.Equal(t, len(testContent), len(p.Contents())) }) @@ -59,7 +60,7 @@ func TestFlow_Produce(t *testing.T) { _, err := testConn.SideB().Write(testMarshalled) require.Nil(t, err) - p, err := flowA.Produce(testMac) + p, err := flowA.Produce(context.Background(), testMac) require.Nil(t, err) assert.Equal(t, testContent, string(p.Contents())) }) diff --git a/tcp/listener.go b/tcp/listener.go index 7f32a3a..aa4c789 100644 --- a/tcp/listener.go +++ b/tcp/listener.go @@ -1,12 +1,13 @@ package tcp import ( + "context" "log" "mpbl3p/proxy" "net" ) -func NewListener(p *proxy.Proxy, local string, v func() proxy.MacVerifier, g func() proxy.MacGenerator) error { +func NewListener(ctx context.Context, p *proxy.Proxy, local string, v func() proxy.MacVerifier, g func() proxy.MacGenerator) error { laddr, err := net.ResolveTCPAddr("tcp", local) if err != nil { return err @@ -32,8 +33,8 @@ func NewListener(p *proxy.Proxy, local string, v func() proxy.MacVerifier, g fun log.Printf("received new tcp connection: %v\n", f) - p.AddConsumer(&f, g()) - p.AddProducer(&f, v()) + p.AddConsumer(ctx, &f, g()) + p.AddProducer(ctx, &f, v()) } }() diff --git a/udp/congestion.go b/udp/congestion.go index ea2f7bb..239645b 100644 --- a/udp/congestion.go +++ b/udp/congestion.go @@ -1,13 +1,16 @@ package udp -import "time" +import ( + "context" + "time" +) type Congestion interface { - Sequence() uint32 + Sequence(ctx context.Context) (uint32, error) NextAck() uint32 NextNack() uint32 ReceivedPacket(seq, nack, ack uint32) - AwaitEarlyUpdate(keepalive time.Duration) uint32 + AwaitEarlyUpdate(ctx context.Context, keepalive time.Duration) (uint32, error) } diff --git a/udp/congestion/newreno.go b/udp/congestion/newreno.go index 4e94a41..d796e11 100644 --- a/udp/congestion/newreno.go +++ b/udp/congestion/newreno.go @@ -1,6 +1,7 @@ package congestion import ( + "context" "fmt" "math" "sort" @@ -94,7 +95,7 @@ func (c *NewReno) ReceivedPacket(seq, nack, ack uint32) { } } -func (c *NewReno) Sequence() uint32 { +func (c *NewReno) Sequence(ctx context.Context) (uint32, error) { for len(c.inFlight) >= int(c.windowSize) { <-c.windowNotifier } @@ -102,7 +103,13 @@ func (c *NewReno) Sequence() uint32 { c.inFlightMu.Lock() defer c.inFlightMu.Unlock() - s := <-c.sequence + var s uint32 + select { + case s = <-c.sequence: + case <-ctx.Done(): + return 0, ctx.Err() + } + t := time.Now() c.inFlight = append(c.inFlight, flightInfo{ @@ -111,7 +118,7 @@ func (c *NewReno) Sequence() uint32 { }) c.lastSent = t - return s + return s, nil } func (c *NewReno) NextAck() uint32 { @@ -126,7 +133,7 @@ func (c *NewReno) NextNack() uint32 { return n } -func (c *NewReno) AwaitEarlyUpdate(keepalive time.Duration) uint32 { +func (c *NewReno) AwaitEarlyUpdate(ctx context.Context, keepalive time.Duration) (uint32, error) { for { rtt := time.Duration(math.Round(c.rttNanos)) time.Sleep(rtt / 2) @@ -136,12 +143,12 @@ func (c *NewReno) AwaitEarlyUpdate(keepalive time.Duration) uint32 { // CASE 1: waiting ACKs or NACKs and no message sent in the last half-RTT // this targets arrival in 0.5+0.5 ± 0.5 RTTs (1±0.5 RTTs) if ((c.lastAck != c.ack) || (c.lastNack != c.nack)) && time.Now().After(c.lastSent.Add(rtt/2)) { - return 0 // no ack needed + return 0, nil // no ack needed } // CASE 2: No message sent within the keepalive time if keepalive != 0 && time.Now().After(c.lastSent.Add(keepalive)) { - return c.Sequence() // require an ack + return c.Sequence(ctx) // require an ack } } } diff --git a/udp/congestion/newreno_test.go b/udp/congestion/newreno_test.go index ed3b235..f9f572a 100644 --- a/udp/congestion/newreno_test.go +++ b/udp/congestion/newreno_test.go @@ -89,11 +89,10 @@ func (n *newRenoTest) RunSideA(ctx context.Context) { go func() { for { - if ctx.Err() != nil { + seq, err := n.sideA.AwaitEarlyUpdate(ctx, 500 * time.Millisecond) + if err != nil { return } - - seq := n.sideA.AwaitEarlyUpdate(500 * time.Millisecond) if seq != 0 { // skip keepalive // required to ensure AwaitEarlyUpdate terminates @@ -123,11 +122,10 @@ func (n *newRenoTest) RunSideB(ctx context.Context) { go func() { for { - if ctx.Err() != nil { + seq, err := n.sideB.AwaitEarlyUpdate(ctx, 500 * time.Millisecond) + if err != nil { return } - - seq := n.sideB.AwaitEarlyUpdate(500 * time.Millisecond) if seq != 0 { // skip keepalive // required to ensure AwaitEarlyUpdate terminates @@ -162,7 +160,7 @@ func TestNewReno_Congestion(t *testing.T) { for i := 0; i < numPackets; i++ { // sleep to simulate preparing packet time.Sleep(1 * time.Millisecond) - seq := c.sideA.Sequence() + seq, _ := c.sideA.Sequence(ctx) c.aOutbound <- congestionPacket{ seq: seq, @@ -200,7 +198,7 @@ func TestNewReno_Congestion(t *testing.T) { for i := 0; i < numPackets; i++ { // sleep to simulate preparing packet time.Sleep(1 * time.Millisecond) - seq := c.sideA.Sequence() + seq, _ := c.sideA.Sequence(ctx) if seq == 20 { // Simulate packet loss of sequence 20 @@ -246,7 +244,7 @@ func TestNewReno_Congestion(t *testing.T) { go func() { for i := 0; i < numPackets; i++ { time.Sleep(1 * time.Millisecond) - seq := c.sideA.Sequence() + seq, _ := c.sideA.Sequence(ctx) c.aOutbound <- congestionPacket{ seq: seq, @@ -261,7 +259,7 @@ func TestNewReno_Congestion(t *testing.T) { go func() { for i := 0; i < numPackets; i++ { time.Sleep(1 * time.Millisecond) - seq := c.sideB.Sequence() + seq, _ := c.sideB.Sequence(ctx) c.bOutbound <- congestionPacket{ seq: seq, @@ -306,7 +304,7 @@ func TestNewReno_Congestion(t *testing.T) { go func() { for i := 0; i < numPackets; i++ { time.Sleep(1 * time.Millisecond) - seq := c.sideA.Sequence() + seq, _ := c.sideA.Sequence(ctx) if seq == 9 { // Simulate packet loss of sequence 9 @@ -326,7 +324,7 @@ func TestNewReno_Congestion(t *testing.T) { go func() { for i := 0; i < numPackets; i++ { time.Sleep(1 * time.Millisecond) - seq := c.sideB.Sequence() + seq, _ := c.sideB.Sequence(ctx) if seq == 13 { // Simulate packet loss of sequence 13 diff --git a/udp/congestion/none.go b/udp/congestion/none.go index be7564f..b7ef6a6 100644 --- a/udp/congestion/none.go +++ b/udp/congestion/none.go @@ -1,41 +1,26 @@ package congestion import ( + "context" "fmt" "time" ) -type None struct { - sequence chan uint32 +type None struct {} + +func NewNone() None { + return None{} } -func NewNone() *None { - c := None{ - sequence: make(chan uint32), - } - - go func() { - var s uint32 - for { - if s == 0 { - s++ - continue - } - - c.sequence <- s - s++ - } - }() - - return &c -} - -func (c *None) String() string { +func (c None) String() string { return fmt.Sprintf("{None}") } -func (c *None) ReceivedPacket(uint32, uint32, uint32) {} -func (c *None) NextNack() uint32 { return 0 } -func (c *None) NextAck() uint32 { return 0 } -func (c *None) AwaitEarlyUpdate(time.Duration) uint32 { select {} } -func (c *None) Sequence() uint32 { return <-c.sequence } +func (c None) ReceivedPacket(uint32, uint32, uint32) {} +func (c None) NextNack() uint32 { return 0 } +func (c None) NextAck() uint32 { return 0 } +func (c None) AwaitEarlyUpdate(ctx context.Context, _ time.Duration) (uint32, error) { + <-ctx.Done() + return 0, ctx.Err() +} +func (c None) Sequence(context.Context) (uint32, error) { return 0, nil } diff --git a/udp/flow.go b/udp/flow.go index 6fd7219..81b67d3 100644 --- a/udp/flow.go +++ b/udp/flow.go @@ -1,6 +1,7 @@ package udp import ( + "context" "fmt" "log" "mpbl3p/proxy" @@ -80,7 +81,7 @@ func newFlow(c Congestion, v proxy.MacVerifier) Flow { } } -func (f *InitiatedFlow) Reconnect() error { +func (f *InitiatedFlow) Reconnect(ctx context.Context) error { f.mu.Lock() defer f.mu.Unlock() @@ -108,7 +109,10 @@ func (f *InitiatedFlow) Reconnect() error { // prod the connection once a second until we get an ack, then consider it alive go func() { - seq := f.congestion.Sequence() + seq, err := f.congestion.Sequence(ctx) + if err != nil { + + } for !f.isAlive { p := Packet{ @@ -124,11 +128,11 @@ func (f *InitiatedFlow) Reconnect() error { }() go func() { - _, _ = f.produceInternal(f.v, false) + _, _ = f.produceInternal(ctx, f.v, false) }() - go f.earlyUpdateLoop(f.g, f.keepalive) + go f.earlyUpdateLoop(ctx, f.g, f.keepalive) - if err := f.acceptPacket(conn); err != nil { + if err := f.readQueuePacket(ctx, conn); err != nil { return err } @@ -140,7 +144,7 @@ func (f *InitiatedFlow) Reconnect() error { f.mu.RLock() defer f.mu.RUnlock() - if err := f.acceptPacket(conn); err != nil { + if err := f.readQueuePacket(ctx, conn); err != nil { log.Println(err) } } @@ -155,25 +159,25 @@ func (f *InitiatedFlow) Reconnect() error { return nil } -func (f *InitiatedFlow) Consume(p proxy.Packet, g proxy.MacGenerator) error { +func (f *InitiatedFlow) Consume(ctx context.Context, p proxy.Packet, g proxy.MacGenerator) error { f.mu.RLock() defer f.mu.RUnlock() - return f.Flow.Consume(p, g) + return f.Flow.Consume(ctx, p, g) } -func (f *InitiatedFlow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { +func (f *InitiatedFlow) Produce(ctx context.Context, v proxy.MacVerifier) (proxy.Packet, error) { f.mu.RLock() defer f.mu.RUnlock() - return f.Flow.Produce(v) + return f.Flow.Produce(ctx, v) } func (f *Flow) IsAlive() bool { return f.isAlive } -func (f *Flow) Consume(pp proxy.Packet, g proxy.MacGenerator) error { +func (f *Flow) Consume(ctx context.Context, pp proxy.Packet, g proxy.MacGenerator) error { if !f.isAlive { return shared.ErrDeadConnection } @@ -182,32 +186,43 @@ func (f *Flow) Consume(pp proxy.Packet, g proxy.MacGenerator) error { // Sequence is the congestion controllers opportunity to block log.Println("awaiting sequence") - p := Packet{ - seq: f.congestion.Sequence(), - data: pp, + seq, err := f.congestion.Sequence(ctx) + if err != nil { + return err } log.Println("received sequence") // Choose up to date ACK/NACK even after blocking - p.ack = f.congestion.NextAck() - p.nack = f.congestion.NextNack() + p := Packet{ + seq: seq, + data: pp, + ack: f.congestion.NextAck(), + nack: f.congestion.NextNack(), + } return f.sendPacket(p, g) } -func (f *Flow) Produce(v proxy.MacVerifier) (proxy.Packet, error) { +func (f *Flow) Produce(ctx context.Context, v proxy.MacVerifier) (proxy.Packet, error) { if !f.isAlive { return nil, shared.ErrDeadConnection } - return f.produceInternal(v, true) + return f.produceInternal(ctx, v, true) } -func (f *Flow) produceInternal(v proxy.MacVerifier, mustReturn bool) (proxy.Packet, error) { +func (f *Flow) produceInternal(ctx context.Context, v proxy.MacVerifier, mustReturn bool) (proxy.Packet, error) { for once := true; mustReturn || once; once = false { log.Println(f.congestion) - b, err := proxy.StripMac(<-f.inboundDatagrams, v) + var received []byte + select { + case received = <-f.inboundDatagrams: + case <-ctx.Done(): + return nil, ctx.Err() + } + + b, err := proxy.StripMac(received, v) if err != nil { return nil, err } @@ -232,8 +247,13 @@ func (f *Flow) produceInternal(v proxy.MacVerifier, mustReturn bool) (proxy.Pack return nil, nil } -func (f *Flow) handleDatagram(p []byte) { - f.inboundDatagrams <- p +func (f *Flow) queueDatagram(ctx context.Context, p []byte) error { + select { + case f.inboundDatagrams <- p: + return nil + case <-ctx.Done(): + return ctx.Err() + } } func (f *Flow) sendPacket(p Packet, g proxy.MacGenerator) error { @@ -249,27 +269,34 @@ func (f *Flow) sendPacket(p Packet, g proxy.MacGenerator) error { } } -func (f *Flow) earlyUpdateLoop(g proxy.MacGenerator, keepalive time.Duration) { +func (f *Flow) earlyUpdateLoop(ctx context.Context, g proxy.MacGenerator, keepalive time.Duration) { for f.isAlive { - seq := f.congestion.AwaitEarlyUpdate(keepalive) + seq, err := f.congestion.AwaitEarlyUpdate(ctx, keepalive) + if err != nil { + fmt.Printf("terminating earlyupdateloop for `%v`\n", f) + return + } p := Packet{ - ack: f.congestion.NextAck(), - nack: f.congestion.NextNack(), seq: seq, data: proxy.SimplePacket(nil), + ack: f.congestion.NextAck(), + nack: f.congestion.NextNack(), } - _ = f.sendPacket(p, g) + err = f.sendPacket(p, g) + if err != nil { + fmt.Printf("error sending early update packet: `%v`\n", err) + } } } -func (f *Flow) acceptPacket(c PacketConn) error { +func (f *Flow) readQueuePacket(ctx context.Context, c PacketConn) error { + // TODO: Replace 6000 with MTU+header size buf := make([]byte, 6000) n, _, err := c.ReadFromUDP(buf) if err != nil { return err } - f.handleDatagram(buf[:n]) - return nil + return f.queueDatagram(ctx, buf[:n]) } diff --git a/udp/flow_test.go b/udp/flow_test.go index b2f20c2..d044477 100644 --- a/udp/flow_test.go +++ b/udp/flow_test.go @@ -1,6 +1,7 @@ package udp import ( + "context" "fmt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -24,7 +25,7 @@ func TestFlow_Consume(t *testing.T) { flowA.writer = testConn.SideB() flowA.isAlive = true - err := flowA.Consume(testPacket, testMac) + err := flowA.Consume(context.Background(), testPacket, testMac) require.Nil(t, err) buf := make([]byte, 100) @@ -63,10 +64,10 @@ func TestFlow_Produce(t *testing.T) { flowA.isAlive = true go func() { - err := flowA.acceptPacket(testConn.SideB()) + err := flowA.readQueuePacket(context.Background(), testConn.SideB()) assert.Nil(t, err) }() - p, err := flowA.Produce(testMac) + p, err := flowA.Produce(context.Background(), testMac) require.Nil(t, err) assert.Len(t, p.Contents(), len(testContent)) diff --git a/udp/listener.go b/udp/listener.go index 09cc19a..f2b9e0d 100644 --- a/udp/listener.go +++ b/udp/listener.go @@ -1,6 +1,7 @@ package udp import ( + "context" "log" "mpbl3p/proxy" "net" @@ -25,7 +26,7 @@ func fromUdpAddress(address net.UDPAddr) ComparableUdpAddress { } } -func NewListener(p *proxy.Proxy, local string, v func() proxy.MacVerifier, g func() proxy.MacGenerator, c func() Congestion) error { +func NewListener(ctx context.Context, p *proxy.Proxy, local string, v func() proxy.MacVerifier, g func() proxy.MacGenerator, c func() Congestion) error { laddr, err := net.ResolveUDPAddr("udp", local) if err != nil { return err @@ -56,10 +57,11 @@ func NewListener(p *proxy.Proxy, local string, v func() proxy.MacVerifier, g fun raddr := fromUdpAddress(*addr) if f, exists := receivedConnections[raddr]; exists { - log.Println("existing flow") - log.Println("handling...") - f.handleDatagram(buf[:n]) - log.Println("handled") + log.Println("existing flow. queuing...") + if err := f.queueDatagram(ctx, buf[:n]); err != nil { + + } + log.Println("queued") continue } @@ -74,15 +76,15 @@ func NewListener(p *proxy.Proxy, local string, v func() proxy.MacVerifier, g fun log.Printf("received new udp connection: %v\n", f) - go f.earlyUpdateLoop(g, 0) + go f.earlyUpdateLoop(ctx, g, 0) receivedConnections[raddr] = &f - p.AddConsumer(&f, g) - p.AddProducer(&f, v) + p.AddConsumer(ctx, &f, g) + p.AddProducer(ctx, &f, v) log.Println("handling...") - f.handleDatagram(buf[:n]) + f.queueDatagram(ctx, buf[:n]) log.Println("handled") } }() -- 2.47.0 From 9a19ecd0d4e14c7a99f5888ca6ed3800de3af34f Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Tue, 30 Mar 2021 21:28:42 +0100 Subject: [PATCH 108/121] formatting --- udp/congestion/newreno_test.go | 4 ++-- udp/congestion/none.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/udp/congestion/newreno_test.go b/udp/congestion/newreno_test.go index f9f572a..f97dfb9 100644 --- a/udp/congestion/newreno_test.go +++ b/udp/congestion/newreno_test.go @@ -89,7 +89,7 @@ func (n *newRenoTest) RunSideA(ctx context.Context) { go func() { for { - seq, err := n.sideA.AwaitEarlyUpdate(ctx, 500 * time.Millisecond) + seq, err := n.sideA.AwaitEarlyUpdate(ctx, 500*time.Millisecond) if err != nil { return } @@ -122,7 +122,7 @@ func (n *newRenoTest) RunSideB(ctx context.Context) { go func() { for { - seq, err := n.sideB.AwaitEarlyUpdate(ctx, 500 * time.Millisecond) + seq, err := n.sideB.AwaitEarlyUpdate(ctx, 500*time.Millisecond) if err != nil { return } diff --git a/udp/congestion/none.go b/udp/congestion/none.go index b7ef6a6..ded9f2b 100644 --- a/udp/congestion/none.go +++ b/udp/congestion/none.go @@ -6,7 +6,7 @@ import ( "time" ) -type None struct {} +type None struct{} func NewNone() None { return None{} -- 2.47.0 From 6f72047caf193989cc6326f3734adf75899aa5c8 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Wed, 31 Mar 2021 18:12:20 +0100 Subject: [PATCH 109/121] improved sinking --- tun/tun.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tun/tun.go b/tun/tun.go index 4b651ca..7cdecad 100644 --- a/tun/tun.go +++ b/tun/tun.go @@ -52,7 +52,11 @@ func (t *SourceSink) Source() (proxy.Packet, error) { var good, bad float64 func (t *SourceSink) Sink(packet proxy.Packet) error { - _, err := t.tun.Write(packet.Contents(), 4) + // make space for tun header + content := make([]byte, len(packet.Contents())+4) + copy(content[4:], packet.Contents()) + + _, err := t.tun.Write(content, 4) if err != nil { switch err.(type) { case *os.PathError: -- 2.47.0 From 6d44a75138b245099368004a7031127118a98149 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Wed, 31 Mar 2021 18:32:27 +0100 Subject: [PATCH 110/121] udp start context --- udp/flow.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/udp/flow.go b/udp/flow.go index 81b67d3..d552d93 100644 --- a/udp/flow.go +++ b/udp/flow.go @@ -111,10 +111,14 @@ func (f *InitiatedFlow) Reconnect(ctx context.Context) error { go func() { seq, err := f.congestion.Sequence(ctx) if err != nil { - + return } for !f.isAlive { + if ctx.Err() != nil { + return + } + p := Packet{ ack: 0, nack: 0, -- 2.47.0 From 9985e0996932b954268a93532290f159b94d6399 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Wed, 31 Mar 2021 18:45:41 +0100 Subject: [PATCH 111/121] udp listener context --- udp/listener.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/udp/listener.go b/udp/listener.go index f2b9e0d..cf3e04f 100644 --- a/udp/listener.go +++ b/udp/listener.go @@ -5,6 +5,7 @@ import ( "log" "mpbl3p/proxy" "net" + "time" ) type ComparableUdpAddress struct { @@ -45,15 +46,19 @@ func NewListener(ctx context.Context, p *proxy.Proxy, local string, v func() pro receivedConnections := make(map[ComparableUdpAddress]*Flow) go func() { - for { + for ctx.Err() == nil { buf := make([]byte, 6000) - log.Println("listening...") - n, addr, err := pconn.ReadFromUDP(buf) - if err != nil { + if err := pconn.SetReadDeadline(time.Now().Add(time.Second)); err != nil { + panic(err) + } + n, addr, err := pconn.ReadFromUDP(buf) + if err != nil { + if e, ok := err.(net.Error); ok && e.Timeout() { + continue + } panic(err) } - log.Println("listened") raddr := fromUdpAddress(*addr) if f, exists := receivedConnections[raddr]; exists { @@ -84,7 +89,9 @@ func NewListener(ctx context.Context, p *proxy.Proxy, local string, v func() pro p.AddProducer(ctx, &f, v) log.Println("handling...") - f.queueDatagram(ctx, buf[:n]) + if err := f.queueDatagram(ctx, buf[:n]); err != nil { + return + } log.Println("handled") } }() -- 2.47.0 From d804ca0749dfdf3eb64e836b48e9cb3487af784c Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Tue, 6 Apr 2021 15:47:48 +0100 Subject: [PATCH 112/121] improved if name logic --- config/config.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index fc2260f..1328116 100644 --- a/config/config.go +++ b/config/config.go @@ -12,7 +12,7 @@ var v = validator.New() func init() { if err := v.RegisterValidation("iface", func(fl validator.FieldLevel) bool { name, ok := fl.Field().Interface().(string) - if ok { + if ok && name != "" { ifaces, err := net.Interfaces() if err != nil { log.Printf("error getting interfaces: %v", err) @@ -60,6 +60,10 @@ type Peer struct { } func (p Peer) GetLocalHost() string { + if p.LocalHost == "" { + return "" + } + if err := v.Var(p.LocalHost, "ip"); err == nil { return p.LocalHost } -- 2.47.0 From 1cf9cc880dcf8aa348006a3fe336ea495a6ed4ca Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Fri, 9 Apr 2021 18:50:00 +0100 Subject: [PATCH 113/121] disable producer/consumer --- config/builder.go | 16 ++++++++++++---- config/config.go | 3 +++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/config/builder.go b/config/builder.go index b83995c..afe29f3 100644 --- a/config/builder.go +++ b/config/builder.go @@ -75,8 +75,12 @@ func buildTcp(ctx context.Context, p *proxy.Proxy, peer Peer, g func() proxy.Mac return err } - p.AddConsumer(ctx, f, g()) - p.AddProducer(ctx, f, v()) + if !peer.DisableConsumer { + p.AddConsumer(ctx, f, g()) + } + if !peer.DisableProducer { + p.AddProducer(ctx, f, v()) + } return nil } @@ -121,8 +125,12 @@ func buildUdp(ctx context.Context, p *proxy.Proxy, peer Peer, g func() proxy.Mac return err } - p.AddConsumer(ctx, f, g()) - p.AddProducer(ctx, f, v()) + if !peer.DisableConsumer { + p.AddConsumer(ctx, f, g()) + } + if !peer.DisableProducer { + p.AddProducer(ctx, f, v()) + } return nil } diff --git a/config/config.go b/config/config.go index 1328116..ed7bea5 100644 --- a/config/config.go +++ b/config/config.go @@ -57,6 +57,9 @@ type Peer struct { KeepAlive uint Timeout uint RetryWait uint + + DisableConsumer bool `validate:"omitempty,nefield=DisableProducer"` + DisableProducer bool `validate:"omitempty,nefield=DisableConsumer"` } func (p Peer) GetLocalHost() string { -- 2.47.0 From a0654b001684ea82ceeb6a1f6de5601cacc4efc2 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Fri, 9 Apr 2021 19:00:35 +0100 Subject: [PATCH 114/121] enable/disable for listeners --- config/builder.go | 4 ++-- tcp/listener.go | 10 +++++++--- udp/listener.go | 10 +++++++--- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/config/builder.go b/config/builder.go index afe29f3..c5bea4f 100644 --- a/config/builder.go +++ b/config/builder.go @@ -85,7 +85,7 @@ func buildTcp(ctx context.Context, p *proxy.Proxy, peer Peer, g func() proxy.Mac return nil } - err := tcp.NewListener(ctx, p, laddr(), v, g) + err := tcp.NewListener(ctx, p, laddr(), v, g, !peer.DisableConsumer, !peer.DisableProducer) if err != nil { return err } @@ -135,7 +135,7 @@ func buildUdp(ctx context.Context, p *proxy.Proxy, peer Peer, g func() proxy.Mac return nil } - err := udp.NewListener(ctx, p, laddr(), v, g, c) + err := udp.NewListener(ctx, p, laddr(), v, g, c, !peer.DisableConsumer, !peer.DisableProducer) if err != nil { return err } diff --git a/tcp/listener.go b/tcp/listener.go index aa4c789..ba44492 100644 --- a/tcp/listener.go +++ b/tcp/listener.go @@ -7,7 +7,7 @@ import ( "net" ) -func NewListener(ctx context.Context, p *proxy.Proxy, local string, v func() proxy.MacVerifier, g func() proxy.MacGenerator) error { +func NewListener(ctx context.Context, p *proxy.Proxy, local string, v func() proxy.MacVerifier, g func() proxy.MacGenerator, enableConsumers bool, enableProducers bool) error { laddr, err := net.ResolveTCPAddr("tcp", local) if err != nil { return err @@ -33,8 +33,12 @@ func NewListener(ctx context.Context, p *proxy.Proxy, local string, v func() pro log.Printf("received new tcp connection: %v\n", f) - p.AddConsumer(ctx, &f, g()) - p.AddProducer(ctx, &f, v()) + if enableConsumers { + p.AddConsumer(ctx, &f, g()) + } + if enableProducers { + p.AddProducer(ctx, &f, v()) + } } }() diff --git a/udp/listener.go b/udp/listener.go index cf3e04f..7c91679 100644 --- a/udp/listener.go +++ b/udp/listener.go @@ -27,7 +27,7 @@ func fromUdpAddress(address net.UDPAddr) ComparableUdpAddress { } } -func NewListener(ctx context.Context, p *proxy.Proxy, local string, v func() proxy.MacVerifier, g func() proxy.MacGenerator, c func() Congestion) error { +func NewListener(ctx context.Context, p *proxy.Proxy, local string, v func() proxy.MacVerifier, g func() proxy.MacGenerator, c func() Congestion, enableConsumers bool, enableProducers bool) error { laddr, err := net.ResolveUDPAddr("udp", local) if err != nil { return err @@ -85,8 +85,12 @@ func NewListener(ctx context.Context, p *proxy.Proxy, local string, v func() pro receivedConnections[raddr] = &f - p.AddConsumer(ctx, &f, g) - p.AddProducer(ctx, &f, v) + if enableConsumers { + p.AddConsumer(ctx, &f, g) + } + if enableProducers { + p.AddProducer(ctx, &f, v) + } log.Println("handling...") if err := f.queueDatagram(ctx, buf[:n]); err != nil { -- 2.47.0 From edfd5a900979c46a2f3295c1d0189c81d8af92a4 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Tue, 11 May 2021 21:43:47 +0100 Subject: [PATCH 115/121] tcp context fixes --- .gitignore | 2 +- tcp/flow.go | 16 ++++++++-------- tcp/listener.go | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 7d235d7..42e21ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -config.ini +*.conf logs/ # Created by https://www.toptal.com/developers/gitignore/api/intellij+all,go diff --git a/tcp/flow.go b/tcp/flow.go index cf59fde..bf54675 100644 --- a/tcp/flow.go +++ b/tcp/flow.go @@ -53,7 +53,7 @@ func NewFlow() Flow { } } -func NewFlowConn(conn Conn) Flow { +func NewFlowConn(ctx context.Context, conn Conn) Flow { f := Flow{ conn: conn, isAlive: true, @@ -64,8 +64,8 @@ func NewFlowConn(conn Conn) Flow { produceErrors: make(chan error), } - go f.produceMarshalled() - go f.consumeMarshalled() + go f.produceMarshalled(ctx) + go f.consumeMarshalled(ctx) return f } @@ -89,7 +89,7 @@ func InitiateFlow(local func() string, remote string) (*InitiatedFlow, error) { return &f, nil } -func (f *InitiatedFlow) Reconnect() error { +func (f *InitiatedFlow) Reconnect(ctx context.Context) error { f.mu.Lock() defer f.mu.Unlock() @@ -119,8 +119,8 @@ func (f *InitiatedFlow) Reconnect() error { f.conn = conn f.isAlive = true - go f.produceMarshalled() - go f.consumeMarshalled() + go f.produceMarshalled(ctx) + go f.consumeMarshalled(ctx) return nil } @@ -191,7 +191,7 @@ func (f *Flow) Produce(ctx context.Context, v proxy.MacVerifier) (proxy.Packet, return proxy.SimplePacket(b), nil } -func (f *Flow) consumeMarshalled() { +func (f *Flow) consumeMarshalled(ctx context.Context) { for { data := <-f.toConsume @@ -208,7 +208,7 @@ func (f *Flow) consumeMarshalled() { } } -func (f *Flow) produceMarshalled() { +func (f *Flow) produceMarshalled(ctx context.Context) { buf := bufio.NewReader(f.conn) for { diff --git a/tcp/listener.go b/tcp/listener.go index ba44492..cf87400 100644 --- a/tcp/listener.go +++ b/tcp/listener.go @@ -29,7 +29,7 @@ func NewListener(ctx context.Context, p *proxy.Proxy, local string, v func() pro panic(err) } - f := NewFlowConn(conn) + f := NewFlowConn(ctx, conn) log.Printf("received new tcp connection: %v\n", f) -- 2.47.0 From 44c0d24ecabed3840424220b711c0e1edc94e9e4 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Tue, 11 May 2021 21:45:06 +0100 Subject: [PATCH 116/121] tcp text context --- tcp/flow_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tcp/flow_test.go b/tcp/flow_test.go index 3d0b49c..eba5a0a 100644 --- a/tcp/flow_test.go +++ b/tcp/flow_test.go @@ -18,7 +18,7 @@ func TestFlow_Consume(t *testing.T) { t.Run("Length", func(t *testing.T) { testConn := mocks.NewMockPerfectBiStreamConn(100) - flowA := NewFlowConn(testConn.SideA()) + flowA := NewFlowConn(context.Background(), testConn.SideA()) err := flowA.Consume(context.Background(), testPacket, testMac) require.Nil(t, err) @@ -42,7 +42,7 @@ func TestFlow_Produce(t *testing.T) { t.Run("Length", func(t *testing.T) { testConn := mocks.NewMockPerfectBiStreamConn(100) - flowA := NewFlowConn(testConn.SideA()) + flowA := NewFlowConn(context.Background(), testConn.SideA()) _, err := testConn.SideB().Write(testMarshalled) require.Nil(t, err) @@ -55,7 +55,7 @@ func TestFlow_Produce(t *testing.T) { t.Run("Value", func(t *testing.T) { testConn := mocks.NewMockPerfectBiStreamConn(100) - flowA := NewFlowConn(testConn.SideA()) + flowA := NewFlowConn(context.Background(), testConn.SideA()) _, err := testConn.SideB().Write(testMarshalled) require.Nil(t, err) -- 2.47.0 From 7f7818115ff77f4c239c869c7a84ca9172a09933 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Tue, 11 May 2021 22:36:53 +0100 Subject: [PATCH 117/121] Updated validator Fixed nefield errors with booleans. --- go.mod | 2 +- go.sum | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 7682057..7a73cee 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module mpbl3p go 1.15 require ( - github.com/go-playground/validator/v10 v10.4.1 + github.com/go-playground/validator/v10 v10.6.0 github.com/jessevdk/go-flags v1.5.0 github.com/smartystreets/goconvey v1.6.4 // indirect github.com/stretchr/testify v1.4.0 diff --git a/go.sum b/go.sum index 04dd5cd..a201b0b 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87 github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-playground/validator/v10 v10.6.0 h1:UGIt4xR++fD9QrBOoo/ascJfGe3AGHEB9s6COnss4Rk= +github.com/go-playground/validator/v10 v10.6.0/go.mod h1:xm76BBt941f7yWdGnI2DVPFFg1UK3YY04qifoXU3lOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= @@ -49,6 +51,7 @@ golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXR golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -- 2.47.0 From 6b6aab0cd972822951913b93ceac31447ae0a584 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Tue, 11 May 2021 22:37:15 +0100 Subject: [PATCH 118/121] Cleaner config error reporting --- main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 0c31e25..db3e4b4 100644 --- a/main.go +++ b/main.go @@ -42,7 +42,8 @@ func main() { c, err := config.LoadConfig(o.ConfigFile) if err != nil { - panic(err) + log.Fatalf("error validating config: %s", err.Error()) + return } log.Println("creating tun adapter...") -- 2.47.0 From 648aab7817a08cfaee94a4278a76732fa8a02a86 Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Tue, 11 May 2021 22:37:39 +0100 Subject: [PATCH 119/121] corrected tun offset and improved mtu --- tun/tun.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tun/tun.go b/tun/tun.go index 7cdecad..a1c1f91 100644 --- a/tun/tun.go +++ b/tun/tun.go @@ -10,7 +10,6 @@ import ( type SourceSink struct { tun wgtun.Device - mtu int } func NewTun(name string, mtu int) (t wgtun.Device, err error) { @@ -26,7 +25,6 @@ func NewFromFile(fd uintptr, mtu int) (ss *SourceSink, err error) { return } - ss.mtu = mtu return } @@ -35,7 +33,12 @@ func (t *SourceSink) Close() error { } func (t *SourceSink) Source() (proxy.Packet, error) { - buf := make([]byte, t.mtu) + mtu, err := t.tun.MTU() + if err != nil { + return nil, err + } + + buf := make([]byte, mtu+4) read, err := t.tun.Read(buf, 4) if err != nil { @@ -46,7 +49,7 @@ func (t *SourceSink) Source() (proxy.Packet, error) { return nil, io.EOF } - return proxy.SimplePacket(buf[4:read]), nil + return proxy.SimplePacket(buf[4:read+4]), nil } var good, bad float64 -- 2.47.0 From 6ae0ef54a4836280a216eb3c53bd40d8a102150c Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Tue, 11 May 2021 23:26:14 +0100 Subject: [PATCH 120/121] Blake2s tests --- crypto/sharedkey/blake2s_test.go | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 crypto/sharedkey/blake2s_test.go diff --git a/crypto/sharedkey/blake2s_test.go b/crypto/sharedkey/blake2s_test.go new file mode 100644 index 0000000..f6cc546 --- /dev/null +++ b/crypto/sharedkey/blake2s_test.go @@ -0,0 +1,45 @@ +package sharedkey + +import ( + "github.com/stretchr/testify/assert" + "math/rand" + "mpbl3p/shared" + "testing" +) + +func TestBlake2s_GenerateVerify(t *testing.T) { + t.Run("GeneratedVerifies", func(t *testing.T) { + // ASSIGN + key := make([]byte, 16) + rand.Read(key) + buf := make([]byte, 500) + rand.Read(buf) + + // ACT + b := Blake2s{key} + code := b.Generate(buf) + + // ASSERT + err := b.Verify(buf, code) + assert.Nil(t, err) + }) + + t.Run("FlippedBitFailsVerify", func(t *testing.T) { + // ASSIGN + key := make([]byte, 16) + rand.Read(key) + buf := make([]byte, 500) + rand.Read(buf) + + // ACT + b := Blake2s{key} + code := b.Generate(buf) + + offset := rand.Intn(len(buf) * 8) + buf[offset/8] ^= 1 << (offset % 8) + + // ASSERT + err := b.Verify(buf, code) + assert.Equal(t, shared.ErrBadChecksum, err) + }) +} -- 2.47.0 From 58a65b10ca35dd5bc0b111f40a03abe99dc3c94a Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Tue, 11 May 2021 23:26:54 +0100 Subject: [PATCH 121/121] formatting --- tun/tun.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tun/tun.go b/tun/tun.go index a1c1f91..252140e 100644 --- a/tun/tun.go +++ b/tun/tun.go @@ -49,7 +49,7 @@ func (t *SourceSink) Source() (proxy.Packet, error) { return nil, io.EOF } - return proxy.SimplePacket(buf[4:read+4]), nil + return proxy.SimplePacket(buf[4 : read+4]), nil } var good, bad float64 -- 2.47.0