| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- package aerotun
- import (
- "encoding/binary"
- "errors"
- "fmt"
- "net"
- "net/netip"
- "sync/atomic"
- "syscall"
- "time"
- "github.com/pierrec/lz4/v4"
- "golang.org/x/net/ipv4"
- "golang.org/x/net/ipv6"
- )
- const secureBatchSize = 16
- type batchSocket interface {
- ReadBatch([]ipv4.Message, int) (int, error)
- WriteBatch([]ipv4.Message, int) (int, error)
- }
- func newBatchSocket(c *net.UDPConn) batchSocket {
- if c.LocalAddr().(*net.UDPAddr).IP.To4() != nil {
- return ipv4.NewPacketConn(c)
- }
- return ipv6.NewPacketConn(c)
- }
- type securePacket struct {
- buf []byte
- n, wire int
- compressed bool
- }
- func (t *TunnelRunner) txLoopSecure() {
- size := t.cfg.MTU + secureHeaderSize + 16
- pool := make(chan *securePacket, secureBatchSize*2)
- queue := make(chan *securePacket, secureBatchSize)
- for range cap(pool) {
- pool <- &securePacket{buf: make([]byte, size)}
- }
- done := make(chan struct{})
- go func() {
- defer close(done)
- defer close(queue)
- for {
- var p *securePacket
- select {
- case <-t.stopChan:
- return
- case p = <-pool:
- }
- n, err := t.dev.Read(p.buf[secureHeaderSize : secureHeaderSize+t.cfg.MTU])
- if err != nil {
- select {
- case <-t.stopChan:
- return
- default:
- }
- t.setError(fmt.Errorf("secure interface read: %w", err))
- pool <- p
- time.Sleep(10 * time.Millisecond)
- continue
- }
- if n == 0 || n > int(t.effectiveMTU.Load()) || !validIPPacket(p.buf[secureHeaderSize:secureHeaderSize+n]) {
- atomic.AddUint64(&t.droppedPackets, 1)
- pool <- p
- continue
- }
- p.n = n
- select {
- case <-t.stopChan:
- return
- case queue <- p:
- }
- }
- }()
- defer func() { <-done }()
- conn := newBatchSocket(t.udpConn)
- messages := make([]ipv4.Message, secureBatchSize)
- var buffers [secureBatchSize][1][]byte
- for i := range messages {
- messages[i].Buffers = buffers[i][:]
- }
- var packets [secureBatchSize]*securePacket
- var nonce [24]byte
- var comp []byte
- if t.cfg.Compression {
- comp = make([]byte, lz4.CompressBlockBound(t.cfg.MTU))
- }
- for {
- var first *securePacket
- select {
- case <-t.stopChan:
- return
- case p, ok := <-queue:
- if !ok {
- return
- }
- first = p
- }
- count := 1
- packets[0] = first
- drain:
- for count < secureBatchSize {
- select {
- case p, ok := <-queue:
- if !ok {
- break drain
- }
- packets[count] = p
- count++
- default:
- break drain
- }
- }
- target := t.target()
- ready := 0
- for i := 0; i < count; i++ {
- p := packets[i]
- payload := p.buf[secureHeaderSize : secureHeaderSize+p.n]
- flags := byte(0)
- p.compressed = false
- p.wire = p.n
- if target != nil && t.secure.compressionEnabled() && len(comp) > 0 && p.n > 40 {
- if n, err := lz4.CompressBlock(payload, comp, nil); err == nil && n > 0 && n < p.n-16 {
- payload = comp[:n]
- flags = FlagCompressedLZ4
- p.compressed = true
- p.wire = n
- }
- }
- if target == nil {
- atomic.AddUint64(&t.droppedPackets, 1)
- pool <- p
- continue
- }
- out, err := t.secure.sealInto(p.buf[:0], &nonce, flags, payload)
- if err != nil {
- atomic.AddUint64(&t.droppedPackets, 1)
- pool <- p
- continue
- }
- packets[ready] = p
- messages[ready].Buffers[0] = out
- messages[ready].Addr = target
- ready++
- }
- sent := 0
- for sent < ready {
- n, err := conn.WriteBatch(messages[sent:ready], 0)
- for j := sent; j < sent+n; j++ {
- p := packets[j]
- atomic.AddUint64(&t.txBytes, uint64(p.n))
- atomic.AddUint64(&t.rawBytesOut, uint64(p.n))
- atomic.AddUint64(&t.packetsTx, 1)
- atomic.AddUint64(&t.wireBytesOut, uint64(len(messages[j].Buffers[0])))
- if p.compressed {
- atomic.AddUint64(&t.compressedPackets, 1)
- atomic.AddUint64(&t.savedBytes, uint64(p.n-p.wire))
- } else {
- atomic.AddUint64(&t.rawPackets, 1)
- }
- }
- sent += n
- if err != nil || n == 0 {
- if err != nil {
- t.setError(err)
- }
- atomic.AddUint64(&t.droppedPackets, uint64(ready-sent))
- break
- }
- }
- for i := 0; i < ready; i++ {
- pool <- packets[i]
- }
- }
- }
- func (t *TunnelRunner) allowedSource(b []byte) bool {
- var src netip.Addr
- if b[0]>>4 == 4 {
- var ip [4]byte
- copy(ip[:], b[12:16])
- src = netip.AddrFrom4(ip)
- } else {
- var ip [16]byte
- copy(ip[:], b[8:24])
- src = netip.AddrFrom16(ip)
- }
- for _, prefix := range t.allowed {
- if prefix.Contains(src) {
- return true
- }
- }
- return false
- }
- func (t *TunnelRunner) rxLoopSecure() {
- conn := newBatchSocket(t.udpConn)
- size := max(t.cfg.MTU+secureHeaderSize+16, handshakeMaxSize)
- messages := make([]ipv4.Message, secureBatchSize)
- buffers := make([][]byte, secureBatchSize)
- var vectors [secureBatchSize][1][]byte
- for i := range messages {
- buffers[i] = make([]byte, size)
- vectors[i][0] = buffers[i]
- messages[i].Buffers = vectors[i][:]
- }
- plain := make([]byte, t.cfg.MTU)
- var decomp []byte
- if t.cfg.Compression {
- decomp = make([]byte, t.cfg.MTU)
- }
- var nonce [24]byte
- for {
- n, err := conn.ReadBatch(messages, 0)
- if err != nil {
- select {
- case <-t.stopChan:
- return
- default:
- }
- t.setError(err)
- continue
- }
- for i := 0; i < n; i++ {
- m := &messages[i]
- src, ok := m.Addr.(*net.UDPAddr)
- if !ok || m.Flags&syscall.MSG_TRUNC != 0 || m.N < secureHeaderSize+16 {
- atomic.AddUint64(&t.droppedPackets, 1)
- continue
- }
- if t.cfg.Mode == "client" && !udpAddrsEqual(t.target(), src) {
- continue
- }
- b := m.Buffers[0][:m.N]
- if b[2] != 3 {
- atomic.AddUint64(&t.authFailures, 1)
- continue
- }
- if b[3] == secureInit || b[3] == secureResponse || b[3] == secureCookie {
- response, peerMTU, err := t.secure.handleHandshake(b, int(t.effectiveMTU.Load()), src.AddrPort())
- if err != nil {
- if !errors.Is(err, errHandshakeRate) {
- atomic.AddUint64(&t.authFailures, 1)
- }
- continue
- }
- atomic.AddUint64(&t.wireBytesIn, uint64(m.N))
- if response != nil {
- if n, err := t.udpConn.WriteToUDP(response, src); err == nil {
- atomic.AddUint64(&t.wireBytesOut, uint64(n))
- } else {
- t.setError(err)
- }
- }
- if peerMTU > 0 {
- if err := t.adjustMTU(src.IP, peerMTU); err != nil {
- t.setError(err)
- continue
- }
- t.markSecurePeer(src)
- if err := t.sendControl(FlagKeepalive, src, zeroSession); err != nil {
- t.setError(err)
- }
- }
- continue
- }
- payload, flags, peerMTU, err := t.secure.openInto(plain, &nonce, b)
- if err != nil {
- if errors.Is(err, errReplay) {
- atomic.AddUint64(&t.replayDrops, 1)
- } else {
- atomic.AddUint64(&t.authFailures, 1)
- }
- continue
- }
- atomic.AddUint64(&t.wireBytesIn, uint64(m.N))
- oldMTU := t.effectiveMTU.Load()
- if flags == FlagKeepalive {
- peerMTU = int(binary.BigEndian.Uint16(payload))
- }
- if peerMTU > 0 {
- if err := t.adjustMTU(src.IP, peerMTU); err != nil {
- t.setError(err)
- continue
- }
- }
- t.markSecurePeer(src)
- if flags == FlagKeepalive {
- if t.effectiveMTU.Load() < oldMTU {
- if err := t.sendControl(FlagKeepalive, src, zeroSession); err != nil {
- t.setError(err)
- }
- }
- continue
- }
- if flags == FlagCompressedLZ4 {
- if len(decomp) == 0 {
- atomic.AddUint64(&t.droppedPackets, 1)
- continue
- }
- size, err := lz4.UncompressBlock(payload, decomp[:int(t.effectiveMTU.Load())])
- if err != nil || size == 0 {
- atomic.AddUint64(&t.droppedPackets, 1)
- continue
- }
- payload = decomp[:size]
- }
- if len(payload) > int(t.effectiveMTU.Load()) || !validIPPacket(payload) || !t.allowedSource(payload) {
- atomic.AddUint64(&t.droppedPackets, 1)
- continue
- }
- wrote, err := t.dev.Write(payload)
- if err != nil || wrote != len(payload) {
- atomic.AddUint64(&t.droppedPackets, 1)
- if err != nil {
- t.setError(err)
- }
- continue
- }
- atomic.AddUint64(&t.rxBytes, uint64(wrote))
- atomic.AddUint64(&t.rawBytesIn, uint64(wrote))
- atomic.AddUint64(&t.packetsRx, 1)
- }
- }
- }
- func (t *TunnelRunner) markSecurePeer(src *net.UDPAddr) {
- t.mu.Lock()
- if !udpAddrsEqual(t.remoteAddr, src) {
- t.remoteAddr = src
- }
- t.lastHandshake = time.Now()
- t.mu.Unlock()
- }
- func (t *TunnelRunner) keepaliveSecure() {
- ticker := time.NewTicker(time.Second)
- defer ticker.Stop()
- lastSent := time.Time{}
- for {
- t.secure.expire()
- target := t.target()
- t.mu.RLock()
- connected := !t.lastHandshake.IsZero() && time.Since(t.lastHandshake) < peerTimeout
- t.mu.RUnlock()
- if target != nil {
- b, err := t.secure.initiation(int(t.effectiveMTU.Load()), !connected)
- if err != nil {
- t.setError(err)
- } else if b != nil {
- if n, err := t.udpConn.WriteToUDP(b, target); err == nil {
- atomic.AddUint64(&t.wireBytesOut, uint64(n))
- } else {
- t.setError(err)
- }
- }
- if connected && time.Since(lastSent) >= 20*time.Second {
- if err := t.sendControl(FlagKeepalive, target, zeroSession); err != nil {
- t.setError(err)
- }
- lastSent = time.Now()
- }
- }
- select {
- case <-t.stopChan:
- return
- case <-ticker.C:
- }
- }
- }
|