packet_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. package iputil
  2. import (
  3. "encoding/binary"
  4. "net"
  5. "testing"
  6. "github.com/stretchr/testify/assert"
  7. "golang.org/x/net/ipv4"
  8. "golang.org/x/net/ipv6"
  9. )
  10. func Test_CreateRejectPacket(t *testing.T) {
  11. h := ipv4.Header{
  12. Len: 20,
  13. Src: net.IPv4(10, 0, 0, 1),
  14. Dst: net.IPv4(10, 0, 0, 2),
  15. Protocol: 1, // ICMP
  16. }
  17. b, err := h.Marshal()
  18. if err != nil {
  19. t.Fatalf("h.Marhshal: %v", err)
  20. }
  21. b = append(b, []byte{0, 3, 0, 4}...)
  22. expectedLen := ipv4.HeaderLen + 8 + h.Len + 4
  23. out := make([]byte, expectedLen)
  24. rejectPacket := CreateRejectPacket(b, out)
  25. assert.NotNil(t, rejectPacket)
  26. assert.Len(t, rejectPacket, expectedLen)
  27. // ICMP with max header len
  28. h = ipv4.Header{
  29. Len: 60,
  30. Src: net.IPv4(10, 0, 0, 1),
  31. Dst: net.IPv4(10, 0, 0, 2),
  32. Protocol: 1, // ICMP
  33. Options: make([]byte, 40),
  34. }
  35. b, err = h.Marshal()
  36. if err != nil {
  37. t.Fatalf("h.Marhshal: %v", err)
  38. }
  39. b = append(b, []byte{0, 3, 0, 4, 0, 0, 0, 0}...)
  40. expectedLen = maxIPv4RejectPacketSize
  41. out = make([]byte, MaxRejectPacketSize)
  42. rejectPacket = CreateRejectPacket(b, out)
  43. assert.NotNil(t, rejectPacket)
  44. assert.Len(t, rejectPacket, expectedLen)
  45. // TCP with max header len
  46. h = ipv4.Header{
  47. Len: 60,
  48. Src: net.IPv4(10, 0, 0, 1),
  49. Dst: net.IPv4(10, 0, 0, 2),
  50. Protocol: 6, // TCP
  51. Options: make([]byte, 40),
  52. }
  53. b, err = h.Marshal()
  54. if err != nil {
  55. t.Fatalf("h.Marhshal: %v", err)
  56. }
  57. b = append(b, []byte{0, 3, 0, 4}...)
  58. b = append(b, make([]byte, 16)...)
  59. expectedLen = ipv4.HeaderLen + 20
  60. out = make([]byte, expectedLen)
  61. rejectPacket = CreateRejectPacket(b, out)
  62. assert.NotNil(t, rejectPacket)
  63. assert.Len(t, rejectPacket, expectedLen)
  64. }
  65. func makeIPv6Packet(src, dst net.IP, nextHeader uint8, payload []byte) []byte {
  66. b := make([]byte, ipv6.HeaderLen+len(payload))
  67. b[0] = ipv6.Version << 4
  68. binary.BigEndian.PutUint16(b[4:], uint16(len(payload)))
  69. b[6] = nextHeader
  70. b[7] = 64
  71. copy(b[8:24], src.To16())
  72. copy(b[24:40], dst.To16())
  73. copy(b[ipv6.HeaderLen:], payload)
  74. return b
  75. }
  76. func Test_CreateRejectPacketIPv6_ICMP(t *testing.T) {
  77. src := net.ParseIP("fd00::1")
  78. dst := net.ParseIP("fd00::2")
  79. // Small UDP packet: entire original included in body
  80. udpPayload := make([]byte, 20)
  81. udpPayload[0] = 0x00 // src port high
  82. udpPayload[1] = 0x50 // src port low (80)
  83. udpPayload[2] = 0x01 // dst port high
  84. udpPayload[3] = 0xBB // dst port low (443)
  85. packet := makeIPv6Packet(src, dst, 17, udpPayload)
  86. out := make([]byte, MaxRejectPacketSize)
  87. rejectPacket := CreateRejectPacket(packet, out)
  88. assert.NotNil(t, rejectPacket)
  89. // Small packet fits entirely: 40 (ipv6 hdr) + 8 (icmpv6 hdr) + 60 (original)
  90. expectedLen := ipv6.HeaderLen + 8 + len(packet)
  91. assert.Len(t, rejectPacket, expectedLen)
  92. // Verify version
  93. assert.Equal(t, byte(ipv6.Version<<4), rejectPacket[0]&0xf0)
  94. // Verify next header is ICMPv6 (58)
  95. assert.Equal(t, byte(58), rejectPacket[6])
  96. // Verify src/dst are swapped
  97. assert.Equal(t, dst.To16(), net.IP(rejectPacket[8:24]))
  98. assert.Equal(t, src.To16(), net.IP(rejectPacket[24:40]))
  99. // Verify ICMPv6 type=1 (Dest Unreachable), code=1 (Administratively prohibited)
  100. assert.Equal(t, byte(1), rejectPacket[ipv6.HeaderLen])
  101. assert.Equal(t, byte(1), rejectPacket[ipv6.HeaderLen+1])
  102. // Verify entire original packet is included in body
  103. assert.Equal(t, packet, rejectPacket[ipv6.HeaderLen+8:])
  104. // Large packet: body is truncated to 1000 bytes
  105. largePkt := makeIPv6Packet(src, dst, 17, make([]byte, 1200))
  106. rejectPacket = CreateRejectPacket(largePkt, out)
  107. assert.NotNil(t, rejectPacket)
  108. assert.Len(t, rejectPacket, ipv6.HeaderLen+8+1000)
  109. assert.Equal(t, largePkt[:1000], rejectPacket[ipv6.HeaderLen+8:])
  110. }
  111. func Test_CreateRejectPacketIPv6_TCP(t *testing.T) {
  112. src := net.ParseIP("fd00::1")
  113. dst := net.ParseIP("fd00::2")
  114. // TCP SYN packet (next header 6)
  115. tcpPayload := make([]byte, 20)
  116. tcpPayload[0] = 0x00 // src port high
  117. tcpPayload[1] = 0x50 // src port low (80)
  118. tcpPayload[2] = 0x01 // dst port high
  119. tcpPayload[3] = 0xBB // dst port low (443)
  120. binary.BigEndian.PutUint32(tcpPayload[4:], 1000) // seq
  121. binary.BigEndian.PutUint32(tcpPayload[8:], 0) // ack seq
  122. tcpPayload[12] = (20 >> 2) << 4 // data offset
  123. tcpPayload[13] = 0b00000010 // SYN flag
  124. packet := makeIPv6Packet(src, dst, 6, tcpPayload)
  125. out := make([]byte, MaxRejectPacketSize)
  126. rejectPacket := CreateRejectPacket(packet, out)
  127. assert.NotNil(t, rejectPacket)
  128. // Expected: 40 (ipv6 hdr) + 20 (tcp RST)
  129. expectedLen := ipv6.HeaderLen + 20
  130. assert.Len(t, rejectPacket, expectedLen)
  131. // Verify version
  132. assert.Equal(t, byte(ipv6.Version<<4), rejectPacket[0]&0xf0)
  133. // Verify next header is TCP (6)
  134. assert.Equal(t, byte(6), rejectPacket[6])
  135. // Verify src/dst are swapped
  136. assert.Equal(t, dst.To16(), net.IP(rejectPacket[8:24]))
  137. assert.Equal(t, src.To16(), net.IP(rejectPacket[24:40]))
  138. // Verify ports are swapped
  139. tcpOut := rejectPacket[ipv6.HeaderLen:]
  140. assert.Equal(t, uint16(443), binary.BigEndian.Uint16(tcpOut[0:2]))
  141. assert.Equal(t, uint16(80), binary.BigEndian.Uint16(tcpOut[2:4]))
  142. // RST+ACK flags (since input was SYN without ACK)
  143. assert.Equal(t, byte(0b00010100), tcpOut[13])
  144. // ack_seq = original seq (1000) + SYN (1) + FIN (0) + segment data (0)
  145. assert.Equal(t, uint32(1001), binary.BigEndian.Uint32(tcpOut[8:]))
  146. }
  147. func Test_CreateRejectPacketIPv6_TCPWithACK(t *testing.T) {
  148. src := net.ParseIP("fd00::1")
  149. dst := net.ParseIP("fd00::2")
  150. // TCP packet with ACK set
  151. tcpPayload := make([]byte, 20)
  152. tcpPayload[0] = 0x00
  153. tcpPayload[1] = 0x50
  154. tcpPayload[2] = 0x01
  155. tcpPayload[3] = 0xBB
  156. binary.BigEndian.PutUint32(tcpPayload[4:], 1000) // seq
  157. binary.BigEndian.PutUint32(tcpPayload[8:], 2000) // ack seq
  158. tcpPayload[12] = (20 >> 2) << 4 // data offset
  159. tcpPayload[13] = 0b00010000 // ACK flag
  160. packet := makeIPv6Packet(src, dst, 6, tcpPayload)
  161. out := make([]byte, MaxRejectPacketSize)
  162. rejectPacket := CreateRejectPacket(packet, out)
  163. assert.NotNil(t, rejectPacket)
  164. tcpOut := rejectPacket[ipv6.HeaderLen:]
  165. // RST only (no ACK) since input had ACK
  166. assert.Equal(t, byte(0b00000100), tcpOut[13])
  167. // seq = original ack_seq
  168. assert.Equal(t, uint32(2000), binary.BigEndian.Uint32(tcpOut[4:]))
  169. }
  170. func Test_CreateRejectPacketIPv6_TooShort(t *testing.T) {
  171. // Packet too short to be valid IPv6
  172. out := make([]byte, MaxRejectPacketSize)
  173. assert.Nil(t, CreateRejectPacket([]byte{0x60}, out))
  174. assert.Nil(t, CreateRejectPacket(make([]byte, 39), out))
  175. }
  176. func Test_CreateRejectPacketIPv6_ExtensionHeaders(t *testing.T) {
  177. src := net.ParseIP("fd00::1")
  178. dst := net.ParseIP("fd00::2")
  179. // IPv6 + Hop-by-Hop extension header + TCP
  180. hopByHop := []byte{
  181. 6, // next header: TCP
  182. 0, // length (8 bytes total)
  183. 0, 0, // padding
  184. 0, 0, 0, 0,
  185. }
  186. tcpPayload := make([]byte, 20)
  187. tcpPayload[0] = 0x00
  188. tcpPayload[1] = 0x50
  189. tcpPayload[2] = 0x01
  190. tcpPayload[3] = 0xBB
  191. binary.BigEndian.PutUint32(tcpPayload[4:], 1000)
  192. binary.BigEndian.PutUint32(tcpPayload[8:], 2000)
  193. tcpPayload[12] = (20 >> 2) << 4
  194. tcpPayload[13] = 0b00010000 // ACK
  195. payload := append(hopByHop, tcpPayload...)
  196. packet := makeIPv6Packet(src, dst, 0, payload) // next header 0 = Hop-by-Hop
  197. out := make([]byte, MaxRejectPacketSize)
  198. rejectPacket := CreateRejectPacket(packet, out)
  199. assert.NotNil(t, rejectPacket)
  200. // Should produce TCP RST
  201. expectedLen := ipv6.HeaderLen + 20
  202. assert.Len(t, rejectPacket, expectedLen)
  203. assert.Equal(t, byte(6), rejectPacket[6]) // next header is TCP
  204. tcpOut := rejectPacket[ipv6.HeaderLen:]
  205. assert.Equal(t, byte(0b00000100), tcpOut[13]) // RST only
  206. }
  207. func TestCreateICMPEchoResponse_IPv4(t *testing.T) {
  208. // Build a simple IPv4 ICMP Echo Request
  209. packet := make([]byte, 28)
  210. packet[0] = 0x45 // version 4, IHL 5
  211. binary.BigEndian.PutUint16(packet[2:], uint16(28)) // total length
  212. packet[8] = 64 // TTL
  213. packet[9] = 1 // protocol ICMP
  214. copy(packet[12:16], net.IPv4(10, 0, 0, 1).To4()) // src
  215. copy(packet[16:20], net.IPv4(10, 0, 0, 2).To4()) // dst
  216. packet[20] = 8 // ICMP Echo Request
  217. out := make([]byte, len(packet))
  218. result := CreateICMPEchoResponse(packet, out)
  219. assert.NotNil(t, result)
  220. assert.Equal(t, byte(0x45), result[0])
  221. // src/dst swapped
  222. assert.Equal(t, net.IPv4(10, 0, 0, 2).To4(), net.IP(result[12:16]))
  223. assert.Equal(t, net.IPv4(10, 0, 0, 1).To4(), net.IP(result[16:20]))
  224. // ICMP Echo Reply
  225. assert.Equal(t, byte(0), result[20])
  226. }
  227. func TestCreateICMPEchoResponse_IPv6(t *testing.T) {
  228. src := net.ParseIP("fd00::1").To16()
  229. dst := net.ParseIP("fd00::2").To16()
  230. // Build an IPv6 ICMPv6 Echo Request packet
  231. // IPv6 header (40 bytes) + ICMPv6 (8 bytes)
  232. packet := make([]byte, 48)
  233. packet[0] = 0x60 // version 6
  234. payloadLen := uint16(8) // ICMPv6 header only
  235. binary.BigEndian.PutUint16(packet[4:], payloadLen)
  236. packet[6] = 58 // Next Header: ICMPv6
  237. packet[7] = 64 // Hop Limit
  238. copy(packet[8:24], src) // src address
  239. copy(packet[24:40], dst) // dst address
  240. // ICMPv6 Echo Request
  241. icmp := packet[40:]
  242. icmp[0] = 128 // type: Echo Request
  243. icmp[1] = 0 // code
  244. binary.BigEndian.PutUint16(icmp[4:], 1) // identifier
  245. binary.BigEndian.PutUint16(icmp[6:], 1) // sequence number
  246. // Compute correct checksum for the request
  247. csum := ipv6PseudoheaderChecksum(src, dst, 58, uint32(payloadLen))
  248. binary.BigEndian.PutUint16(icmp[2:], tcpipChecksum(icmp, csum))
  249. out := make([]byte, len(packet))
  250. result := CreateICMPEchoResponse(packet, out)
  251. assert.NotNil(t, result)
  252. // Version should still be 6
  253. assert.Equal(t, byte(6), result[0]>>4)
  254. // src/dst swapped
  255. assert.Equal(t, dst, net.IP(result[8:24]))
  256. assert.Equal(t, src, net.IP(result[24:40]))
  257. // ICMPv6 Echo Reply type
  258. assert.Equal(t, byte(129), result[40])
  259. // Verify checksum is valid (tcpipChecksum returns 0 when data+checksum is correct)
  260. respIcmp := result[40:]
  261. verifyCsum := ipv6PseudoheaderChecksum(result[8:24], result[24:40], 58, uint32(payloadLen))
  262. assert.Equal(t, uint16(0), tcpipChecksum(respIcmp, verifyCsum))
  263. }
  264. func TestCreateICMPEchoResponse_IPv6_NotEchoRequest(t *testing.T) {
  265. src := net.ParseIP("fd00::1").To16()
  266. dst := net.ParseIP("fd00::2").To16()
  267. packet := make([]byte, 48)
  268. packet[0] = 0x60
  269. binary.BigEndian.PutUint16(packet[4:], 8)
  270. packet[6] = 58
  271. packet[7] = 64
  272. copy(packet[8:24], src)
  273. copy(packet[24:40], dst)
  274. // ICMPv6 type 1 (Destination Unreachable) - not Echo Request
  275. packet[40] = 1
  276. out := make([]byte, len(packet))
  277. result := CreateICMPEchoResponse(packet, out)
  278. assert.Nil(t, result)
  279. }
  280. func TestCreateICMPEchoResponse_IPv6_NotICMPv6(t *testing.T) {
  281. src := net.ParseIP("fd00::1").To16()
  282. dst := net.ParseIP("fd00::2").To16()
  283. packet := make([]byte, 48)
  284. packet[0] = 0x60
  285. binary.BigEndian.PutUint16(packet[4:], 8)
  286. packet[6] = 6 // TCP, not ICMPv6
  287. packet[7] = 64
  288. copy(packet[8:24], src)
  289. copy(packet[24:40], dst)
  290. out := make([]byte, len(packet))
  291. result := CreateICMPEchoResponse(packet, out)
  292. assert.Nil(t, result)
  293. }