github.com/database64128/shadowsocks-go@v1.10.2-0.20240315062903-143a773533f1/zerocopy/packet.go (about)

     1  package zerocopy
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/rand"
     7  	"errors"
     8  	"net/netip"
     9  
    10  	"github.com/database64128/shadowsocks-go/conn"
    11  )
    12  
    13  // Used in packet size calculations.
    14  const (
    15  	IPv4HeaderLength = 20
    16  	IPv6HeaderLength = 40
    17  	UDPHeaderLength  = 8
    18  
    19  	// Next Header + Hdr Ext Len + Option Type + Opt Data Len + Jumbo Payload Length (u32be)
    20  	JumboPayloadOptionLength = 8
    21  )
    22  
    23  var (
    24  	ErrPacketTooSmall = errors.New("packet too small to unpack")
    25  	ErrPayloadTooBig  = errors.New("payload too big to pack")
    26  )
    27  
    28  // MaxPacketSizeForAddr calculates the maximum packet size for the given address
    29  // based on the MTU and the address family.
    30  func MaxPacketSizeForAddr(mtu int, addr netip.Addr) int {
    31  	if addr.Is4() || addr.Is4In6() {
    32  		return mtu - IPv4HeaderLength - UDPHeaderLength
    33  	}
    34  	if mtu > 65575 {
    35  		return mtu - IPv6HeaderLength - JumboPayloadOptionLength - UDPHeaderLength
    36  	}
    37  	return mtu - IPv6HeaderLength - UDPHeaderLength
    38  }
    39  
    40  // ClientPackerInfo contains information about a client packer.
    41  type ClientPackerInfo struct {
    42  	Headroom Headroom
    43  }
    44  
    45  // ClientPacker processes raw payload into packets ready to be sent to servers.
    46  type ClientPacker interface {
    47  	// ClientPackerInfo returns information about the client packer.
    48  	ClientPackerInfo() ClientPackerInfo
    49  
    50  	// PackInPlace packs the payload in-place into a packet ready for sending and returns
    51  	// the destination address, packet start offset, packet length, or an error if packing fails.
    52  	PackInPlace(ctx context.Context, b []byte, targetAddr conn.Addr, payloadStart, payloadLen int) (destAddrPort netip.AddrPort, packetStart, packetLen int, err error)
    53  }
    54  
    55  // ServerPackerInfo contains information about a server packer.
    56  type ServerPackerInfo struct {
    57  	Headroom Headroom
    58  }
    59  
    60  // ServerPacker processes raw payload into packets ready to be sent to clients.
    61  type ServerPacker interface {
    62  	// ServerPackerInfo returns information about the server packer.
    63  	ServerPackerInfo() ServerPackerInfo
    64  
    65  	// PackInPlace packs the payload in-place into a packet ready for sending and returns
    66  	// packet start offset, packet length, or an error if packing fails.
    67  	PackInPlace(b []byte, sourceAddrPort netip.AddrPort, payloadStart, payloadLen, maxPacketLen int) (packetStart, packetLen int, err error)
    68  }
    69  
    70  // ClientUnpackerInfo contains information about a client unpacker.
    71  type ClientUnpackerInfo struct {
    72  	Headroom Headroom
    73  }
    74  
    75  // ClientUnpacker processes packets received from the server into raw payload.
    76  type ClientUnpacker interface {
    77  	// ClientUnpackerInfo returns information about the client unpacker.
    78  	ClientUnpackerInfo() ClientUnpackerInfo
    79  
    80  	// UnpackInPlace unpacks the packet in-place and returns packet source address, payload start offset, payload length, or an error if unpacking fails.
    81  	UnpackInPlace(b []byte, packetSourceAddrPort netip.AddrPort, packetStart, packetLen int) (payloadSourceAddrPort netip.AddrPort, payloadStart, payloadLen int, err error)
    82  }
    83  
    84  // ServerUnpackerInfo contains information about a server unpacker.
    85  type ServerUnpackerInfo struct {
    86  	Headroom Headroom
    87  }
    88  
    89  // ServerUnpacker processes packets received from the client into raw payload.
    90  type ServerUnpacker interface {
    91  	// ServerUnpackerInfo returns information about the server unpacker.
    92  	ServerUnpackerInfo() ServerUnpackerInfo
    93  
    94  	// UnpackInPlace unpacks the packet in-place and returns target address, payload start offset, payload length, or an error if unpacking fails.
    95  	UnpackInPlace(b []byte, sourceAddrPort netip.AddrPort, packetStart, packetLen int) (targetAddr conn.Addr, payloadStart, payloadLen int, err error)
    96  
    97  	// NewPacker creates a new server session for the current client session and returns the server session's packer, or an error.
    98  	NewPacker() (ServerPacker, error)
    99  }
   100  
   101  // ClientPackUnpacker implements both ClientPacker and ClientUnpacker interfaces.
   102  type ClientPackUnpacker interface {
   103  	ClientPacker
   104  	ClientUnpacker
   105  }
   106  
   107  // ServerPackUnpacker implements both ServerPacker and ServerUnpacker interfaces.
   108  type ServerPackUnpacker interface {
   109  	ServerPacker
   110  	ServerUnpacker
   111  }
   112  
   113  // ClientServerPackerUnpackerTestFunc tests the client and server following these steps:
   114  // 1. Client packer packs.
   115  // 2. Server unpacker unpacks.
   116  // 3. Server packer packs.
   117  // 4. Client unpacker unpacks.
   118  func ClientServerPackerUnpackerTestFunc(t tester, clientPacker ClientPacker, clientUnpacker ClientUnpacker, serverPacker ServerPacker, serverUnpacker ServerUnpacker) {
   119  	const (
   120  		packetSize = 1452
   121  		payloadLen = 1280
   122  	)
   123  
   124  	headroom := MaxHeadroom(clientPacker.ClientPackerInfo().Headroom, serverPacker.ServerPackerInfo().Headroom)
   125  
   126  	b := make([]byte, headroom.Front+payloadLen+headroom.Rear)
   127  	payload := b[headroom.Front : headroom.Front+payloadLen]
   128  	targetAddrPort := netip.AddrPortFrom(netip.IPv6Unspecified(), 53)
   129  	targetAddr := conn.AddrFromIPPort(targetAddrPort)
   130  
   131  	// Fill random payload.
   132  	_, err := rand.Read(payload)
   133  	if err != nil {
   134  		t.Fatal(err)
   135  	}
   136  
   137  	// Backup payload.
   138  	payloadBackup := make([]byte, len(payload))
   139  	copy(payloadBackup, payload)
   140  
   141  	// Client packs.
   142  	destAddr, pkts, pktl, err := clientPacker.PackInPlace(context.Background(), b, targetAddr, headroom.Front, payloadLen)
   143  	if err != nil {
   144  		t.Fatal(err)
   145  	}
   146  
   147  	// Server unpacks.
   148  	ta, ps, pl, err := serverUnpacker.UnpackInPlace(b, destAddr, pkts, pktl)
   149  	if err != nil {
   150  		t.Fatal(err)
   151  	}
   152  
   153  	// Check target address.
   154  	if !ta.Equals(targetAddr) {
   155  		t.Errorf("Target address mismatch: c: %s, s: %s", targetAddr, ta)
   156  	}
   157  
   158  	// Check payload.
   159  	p := b[ps : ps+pl]
   160  	if !bytes.Equal(payloadBackup, p) {
   161  		t.Errorf("Payload mismatch: c: %v, s: %v", payloadBackup, p)
   162  	}
   163  
   164  	// Server packs.
   165  	pkts, pktl, err = serverPacker.PackInPlace(b, targetAddrPort, ps, pl, packetSize)
   166  	if err != nil {
   167  		t.Fatal(err)
   168  	}
   169  
   170  	// Client unpacks.
   171  	tap, ps, pl, err := clientUnpacker.UnpackInPlace(b, destAddr, pkts, pktl)
   172  	if err != nil {
   173  		t.Fatal(err)
   174  	}
   175  
   176  	// Check target address.
   177  	if tap != targetAddrPort {
   178  		t.Errorf("Target address mismatch: c: %s, s: %s", targetAddrPort, tap)
   179  	}
   180  
   181  	// Check payload.
   182  	p = b[ps : ps+pl]
   183  	if !bytes.Equal(payloadBackup, p) {
   184  		t.Errorf("Payload mismatch: c: %v, s: %v", payloadBackup, p)
   185  	}
   186  }