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 }