packet.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. package iputil
  2. import (
  3. "encoding/binary"
  4. "golang.org/x/net/ipv4"
  5. "golang.org/x/net/ipv6"
  6. )
  7. const (
  8. // MaxIPv4RejectPacketSize is the largest IPv4 reject packet:
  9. // - 20 byte ipv4 header
  10. // - 8 byte icmpv4 header
  11. // - 68 byte body (60 byte max orig ipv4 header + 8 byte orig icmpv4 header)
  12. maxIPv4RejectPacketSize = ipv4.HeaderLen + 8 + 60 + 8
  13. // MaxRejectPacketSize is sized for the largest possible reject packet (IPv6):
  14. // - 40 byte ipv6 header
  15. // - 8 byte icmpv6 header
  16. // - up to 1000 byte body (original packet, possibly truncated. We want to stay
  17. // under the MTU with Nebula overhead included)
  18. maxIPv6RejectPacketSize = ipv6.HeaderLen + 8 + 1000
  19. MaxRejectPacketSize = maxIPv6RejectPacketSize
  20. )
  21. func CreateRejectPacket(packet []byte, out []byte) []byte {
  22. if len(packet) < 1 {
  23. return nil
  24. }
  25. version := int(packet[0] >> 4)
  26. switch version {
  27. case ipv4.Version:
  28. if len(packet) < ipv4.HeaderLen {
  29. return nil
  30. }
  31. switch packet[9] {
  32. case 6: // tcp
  33. return ipv4CreateRejectTCPPacket(packet, out)
  34. default:
  35. return ipv4CreateRejectICMPPacket(packet, out)
  36. }
  37. case ipv6.Version:
  38. if len(packet) < ipv6.HeaderLen {
  39. return nil
  40. }
  41. return ipv6CreateRejectPacket(packet, out)
  42. default:
  43. return nil
  44. }
  45. }
  46. func ipv4CreateRejectICMPPacket(packet []byte, out []byte) []byte {
  47. ihl := int(packet[0]&0x0f) << 2
  48. if len(packet) < ihl {
  49. // We need at least this many bytes for this to be a valid packet
  50. return nil
  51. }
  52. // ICMP reply includes original header and first 8 bytes of the packet
  53. packetLen := min(len(packet), ihl+8)
  54. outLen := ipv4.HeaderLen + 8 + packetLen
  55. if outLen > cap(out) {
  56. return nil
  57. }
  58. out = out[:outLen]
  59. ipHdr := out[0:ipv4.HeaderLen]
  60. ipHdr[0] = ipv4.Version<<4 | (ipv4.HeaderLen >> 2) // version, ihl
  61. ipHdr[1] = 0 // DSCP, ECN
  62. binary.BigEndian.PutUint16(ipHdr[2:], uint16(outLen)) // Total Length
  63. ipHdr[4] = 0 // id
  64. ipHdr[5] = 0 // .
  65. ipHdr[6] = 0 // flags, fragment offset
  66. ipHdr[7] = 0 // .
  67. ipHdr[8] = 64 // TTL
  68. ipHdr[9] = 1 // protocol (icmp)
  69. ipHdr[10] = 0 // checksum
  70. ipHdr[11] = 0 // .
  71. // Swap dest / src IPs
  72. copy(ipHdr[12:16], packet[16:20])
  73. copy(ipHdr[16:20], packet[12:16])
  74. // Calculate checksum
  75. binary.BigEndian.PutUint16(ipHdr[10:], tcpipChecksum(ipHdr, 0))
  76. // ICMP Destination Unreachable
  77. icmpOut := out[ipv4.HeaderLen:]
  78. icmpOut[0] = 3 // type (Destination unreachable)
  79. icmpOut[1] = 13 // code (Communication administratively prohibited)
  80. icmpOut[2] = 0 // checksum
  81. icmpOut[3] = 0 // .
  82. icmpOut[4] = 0 // unused
  83. icmpOut[5] = 0 // .
  84. icmpOut[6] = 0 // .
  85. icmpOut[7] = 0 // .
  86. // Copy original IP header and first 8 bytes as body
  87. copy(icmpOut[8:], packet[:packetLen])
  88. // Calculate checksum
  89. binary.BigEndian.PutUint16(icmpOut[2:], tcpipChecksum(icmpOut, 0))
  90. return out
  91. }
  92. func ipv4CreateRejectTCPPacket(packet []byte, out []byte) []byte {
  93. const tcpLen = 20
  94. ihl := int(packet[0]&0x0f) << 2
  95. outLen := ipv4.HeaderLen + tcpLen
  96. if len(packet) < ihl+tcpLen {
  97. // We need at least this many bytes for this to be a valid packet
  98. return nil
  99. }
  100. if outLen > cap(out) {
  101. return nil
  102. }
  103. out = out[:outLen]
  104. ipHdr := out[0:ipv4.HeaderLen]
  105. ipHdr[0] = ipv4.Version<<4 | (ipv4.HeaderLen >> 2) // version, ihl
  106. ipHdr[1] = 0 // DSCP, ECN
  107. binary.BigEndian.PutUint16(ipHdr[2:], uint16(outLen)) // Total Length
  108. ipHdr[4] = 0 // id
  109. ipHdr[5] = 0 // .
  110. ipHdr[6] = 0 // flags, fragment offset
  111. ipHdr[7] = 0 // .
  112. ipHdr[8] = 64 // TTL
  113. ipHdr[9] = 6 // protocol (tcp)
  114. ipHdr[10] = 0 // checksum
  115. ipHdr[11] = 0 // .
  116. // Swap dest / src IPs
  117. copy(ipHdr[12:16], packet[16:20])
  118. copy(ipHdr[16:20], packet[12:16])
  119. // Calculate checksum
  120. binary.BigEndian.PutUint16(ipHdr[10:], tcpipChecksum(ipHdr, 0))
  121. // TCP RST
  122. tcpIn := packet[ihl:]
  123. var ackSeq, seq uint32
  124. outFlags := byte(0b00000100) // RST
  125. // Set seq and ackSeq based on how iptables/netfilter does it in Linux:
  126. // - https://github.com/torvalds/linux/blob/v5.19/net/ipv4/netfilter/nf_reject_ipv4.c#L193-L221
  127. inAck := tcpIn[13]&0b00010000 != 0
  128. if inAck {
  129. seq = binary.BigEndian.Uint32(tcpIn[8:])
  130. } else {
  131. inSyn := uint32((tcpIn[13] & 0b00000010) >> 1)
  132. inFin := uint32(tcpIn[13] & 0b00000001)
  133. // seq from the packet + syn + fin + tcp segment length
  134. ackSeq = binary.BigEndian.Uint32(tcpIn[4:]) + inSyn + inFin + uint32(len(tcpIn)) - uint32(tcpIn[12]>>4)<<2
  135. outFlags |= 0b00010000 // ACK
  136. }
  137. tcpOut := out[ipv4.HeaderLen:]
  138. // Swap dest / src ports
  139. copy(tcpOut[0:2], tcpIn[2:4])
  140. copy(tcpOut[2:4], tcpIn[0:2])
  141. binary.BigEndian.PutUint32(tcpOut[4:], seq)
  142. binary.BigEndian.PutUint32(tcpOut[8:], ackSeq)
  143. tcpOut[12] = (tcpLen >> 2) << 4 // data offset, reserved, NS
  144. tcpOut[13] = outFlags // CWR, ECE, URG, ACK, PSH, RST, SYN, FIN
  145. tcpOut[14] = 0 // window size
  146. tcpOut[15] = 0 // .
  147. tcpOut[16] = 0 // checksum
  148. tcpOut[17] = 0 // .
  149. tcpOut[18] = 0 // URG Pointer
  150. tcpOut[19] = 0 // .
  151. // Calculate checksum
  152. csum := ipv4PseudoheaderChecksum(ipHdr[12:16], ipHdr[16:20], 6, tcpLen)
  153. binary.BigEndian.PutUint16(tcpOut[16:], tcpipChecksum(tcpOut, csum))
  154. return out
  155. }
  156. func ipv6CreateRejectPacket(packet []byte, out []byte) []byte {
  157. proto := ipv6FindUpperProtocol(packet)
  158. switch proto {
  159. case 6: // tcp
  160. return ipv6CreateRejectTCPPacket(packet, out)
  161. default:
  162. return ipv6CreateRejectICMPPacket(packet, out)
  163. }
  164. }
  165. func ipv6FindUpperProtocol(packet []byte) uint8 {
  166. nextHeader := packet[6]
  167. offset := ipv6.HeaderLen
  168. for {
  169. switch nextHeader {
  170. case 0, 43, 60: // Hop-by-Hop, Routing, Destination
  171. if len(packet) < offset+2 {
  172. return nextHeader
  173. }
  174. nextHeader = packet[offset]
  175. offset += int(packet[offset+1]+1) << 3
  176. case 44: // Fragment
  177. if len(packet) < offset+8 {
  178. return nextHeader
  179. }
  180. nextHeader = packet[offset]
  181. offset += 8
  182. case 51: // AH
  183. if len(packet) < offset+2 {
  184. return nextHeader
  185. }
  186. nextHeader = packet[offset]
  187. offset += int(packet[offset+1]+2) << 2
  188. default:
  189. return nextHeader
  190. }
  191. }
  192. }
  193. func ipv6CreateRejectICMPPacket(packet []byte, out []byte) []byte {
  194. // Include as much of the original packet as possible, up to 1000 bytes,
  195. // so the response fits comfortably within any tunnel MTU.
  196. packetLen := min(len(packet), 1000)
  197. outLen := ipv6.HeaderLen + 8 + packetLen
  198. if outLen > cap(out) {
  199. return nil
  200. }
  201. out = out[:outLen]
  202. // IPv6 header
  203. ipHdr := out[0:ipv6.HeaderLen]
  204. ipHdr[0] = ipv6.Version << 4 // version, traffic class (high bits)
  205. ipHdr[1] = 0 // traffic class (low bits), flow label (high bits)
  206. ipHdr[2] = 0 // flow label
  207. ipHdr[3] = 0 // flow label
  208. payloadLen := uint16(outLen - ipv6.HeaderLen)
  209. binary.BigEndian.PutUint16(ipHdr[4:], payloadLen) // payload length
  210. ipHdr[6] = 58 // next header (ICMPv6)
  211. ipHdr[7] = 64 // hop limit
  212. // Swap dest / src IPs (each 16 bytes, src at 8, dst at 24)
  213. copy(ipHdr[8:24], packet[24:40])
  214. copy(ipHdr[24:40], packet[8:24])
  215. // ICMPv6 Destination Unreachable
  216. icmpOut := out[ipv6.HeaderLen:]
  217. icmpOut[0] = 1 // type (Destination Unreachable)
  218. icmpOut[1] = 1 // code (Communication with destination administratively prohibited)
  219. icmpOut[2] = 0 // checksum
  220. icmpOut[3] = 0 // .
  221. icmpOut[4] = 0 // unused
  222. icmpOut[5] = 0 // .
  223. icmpOut[6] = 0 // .
  224. icmpOut[7] = 0 // .
  225. copy(icmpOut[8:], packet[:packetLen])
  226. // ICMPv6 checksum uses a pseudo-header
  227. csum := ipv6PseudoheaderChecksum(ipHdr[8:24], ipHdr[24:40], 58, uint32(payloadLen))
  228. binary.BigEndian.PutUint16(icmpOut[2:], tcpipChecksum(icmpOut, csum))
  229. return out
  230. }
  231. func ipv6CreateRejectTCPPacket(packet []byte, out []byte) []byte {
  232. const tcpLen = 20
  233. offset := ipv6FindUpperProtocolOffset(packet)
  234. if len(packet) < offset+tcpLen {
  235. return nil
  236. }
  237. outLen := ipv6.HeaderLen + tcpLen
  238. if outLen > cap(out) {
  239. return nil
  240. }
  241. out = out[:outLen]
  242. // IPv6 header
  243. ipHdr := out[0:ipv6.HeaderLen]
  244. ipHdr[0] = ipv6.Version << 4 // version, traffic class (high bits)
  245. ipHdr[1] = 0 // traffic class (low bits), flow label (high bits)
  246. ipHdr[2] = 0 // flow label
  247. ipHdr[3] = 0 // flow label
  248. binary.BigEndian.PutUint16(ipHdr[4:], tcpLen) // payload length
  249. ipHdr[6] = 6 // next header (TCP)
  250. ipHdr[7] = 64 // hop limit
  251. // Swap dest / src IPs
  252. copy(ipHdr[8:24], packet[24:40])
  253. copy(ipHdr[24:40], packet[8:24])
  254. // TCP RST
  255. tcpIn := packet[offset:]
  256. var ackSeq, seq uint32
  257. outFlags := byte(0b00000100) // RST
  258. inAck := tcpIn[13]&0b00010000 != 0
  259. if inAck {
  260. seq = binary.BigEndian.Uint32(tcpIn[8:])
  261. } else {
  262. inSyn := uint32((tcpIn[13] & 0b00000010) >> 1)
  263. inFin := uint32(tcpIn[13] & 0b00000001)
  264. ackSeq = binary.BigEndian.Uint32(tcpIn[4:]) + inSyn + inFin + uint32(len(tcpIn)) - uint32(tcpIn[12]>>4)<<2
  265. outFlags |= 0b00010000 // ACK
  266. }
  267. tcpOut := out[ipv6.HeaderLen:]
  268. // Swap dest / src ports
  269. copy(tcpOut[0:2], tcpIn[2:4])
  270. copy(tcpOut[2:4], tcpIn[0:2])
  271. binary.BigEndian.PutUint32(tcpOut[4:], seq)
  272. binary.BigEndian.PutUint32(tcpOut[8:], ackSeq)
  273. tcpOut[12] = (tcpLen >> 2) << 4 // data offset, reserved, NS
  274. tcpOut[13] = outFlags // CWR, ECE, URG, ACK, PSH, RST, SYN, FIN
  275. tcpOut[14] = 0 // window size
  276. tcpOut[15] = 0 // .
  277. tcpOut[16] = 0 // checksum
  278. tcpOut[17] = 0 // .
  279. tcpOut[18] = 0 // URG Pointer
  280. tcpOut[19] = 0 // .
  281. // Calculate checksum with IPv6 pseudo-header
  282. csum := ipv6PseudoheaderChecksum(ipHdr[8:24], ipHdr[24:40], 6, tcpLen)
  283. binary.BigEndian.PutUint16(tcpOut[16:], tcpipChecksum(tcpOut, csum))
  284. return out
  285. }
  286. func ipv6FindUpperProtocolOffset(packet []byte) int {
  287. nextHeader := packet[6]
  288. offset := ipv6.HeaderLen
  289. for {
  290. switch nextHeader {
  291. case 0, 43, 60: // Hop-by-Hop, Routing, Destination
  292. if len(packet) < offset+2 {
  293. return offset
  294. }
  295. nextHeader = packet[offset]
  296. offset += int(packet[offset+1]+1) << 3
  297. case 44: // Fragment
  298. if len(packet) < offset+8 {
  299. return offset
  300. }
  301. nextHeader = packet[offset]
  302. offset += 8
  303. case 51: // AH
  304. if len(packet) < offset+2 {
  305. return offset
  306. }
  307. nextHeader = packet[offset]
  308. offset += int(packet[offset+1]+2) << 2
  309. default:
  310. return offset
  311. }
  312. }
  313. }
  314. func CreateICMPEchoResponse(packet, out []byte) []byte {
  315. if len(packet) < 1 {
  316. return nil
  317. }
  318. switch packet[0] >> 4 {
  319. case 4:
  320. return createICMPv4EchoResponse(packet, out)
  321. case 6:
  322. return createICMPv6EchoResponse(packet, out)
  323. default:
  324. return nil
  325. }
  326. }
  327. func createICMPv4EchoResponse(packet, out []byte) []byte {
  328. // Return early if this is not a simple ICMP Echo Request
  329. //TODO: make constants out of these
  330. if !(len(packet) >= 28 && len(packet) <= 9001 && packet[0] == 0x45 && packet[9] == 0x01 && packet[20] == 0x08) {
  331. return nil
  332. }
  333. // We don't support fragmented packets
  334. if packet[7] != 0 || (packet[6]&0x2F != 0) {
  335. return nil
  336. }
  337. out = out[:len(packet)]
  338. copy(out, packet)
  339. // Swap dest / src IPs and recalculate checksum
  340. ipv4 := out[0:20]
  341. copy(ipv4[12:16], packet[16:20])
  342. copy(ipv4[16:20], packet[12:16])
  343. ipv4[10] = 0
  344. ipv4[11] = 0
  345. binary.BigEndian.PutUint16(ipv4[10:], tcpipChecksum(ipv4, 0))
  346. // Change type to ICMP Echo Reply and recalculate checksum
  347. icmp := out[20:]
  348. icmp[0] = 0
  349. icmp[2] = 0
  350. icmp[3] = 0
  351. binary.BigEndian.PutUint16(icmp[2:], tcpipChecksum(icmp, 0))
  352. return out
  353. }
  354. func createICMPv6EchoResponse(packet, out []byte) []byte {
  355. // IPv6 header (40 bytes) + ICMPv6 header (8 bytes minimum)
  356. if len(packet) < ipv6.HeaderLen+8 || len(packet) > 9001 {
  357. return nil
  358. }
  359. // Next Header must be ICMPv6 (58)
  360. if packet[6] != 58 {
  361. return nil
  362. }
  363. // ICMPv6 type must be Echo Request (128)
  364. if packet[ipv6.HeaderLen] != 128 {
  365. return nil
  366. }
  367. out = out[:len(packet)]
  368. copy(out, packet)
  369. // Swap src/dst addresses (bytes 8-23 and 24-39)
  370. copy(out[8:24], packet[24:40])
  371. copy(out[24:40], packet[8:24])
  372. // Change ICMPv6 type to Echo Reply (129)
  373. icmp := out[ipv6.HeaderLen:]
  374. icmp[0] = 129
  375. icmp[2] = 0
  376. icmp[3] = 0
  377. // ICMPv6 checksum uses a pseudo-header with src, dst, length, and next header
  378. payloadLen := uint32(len(icmp))
  379. csum := ipv6PseudoheaderChecksum(out[8:24], out[24:40], 58, payloadLen)
  380. binary.BigEndian.PutUint16(icmp[2:], tcpipChecksum(icmp, csum))
  381. return out
  382. }
  383. // calculates the TCP/IP checksum defined in rfc1071. The passed-in
  384. // csum is any initial checksum data that's already been computed.
  385. //
  386. // based on:
  387. // - https://github.com/google/gopacket/blob/v1.1.19/layers/tcpip.go#L50-L70
  388. func tcpipChecksum(data []byte, csum uint32) uint16 {
  389. // to handle odd lengths, we loop to length - 1, incrementing by 2, then
  390. // handle the last byte specifically by checking against the original
  391. // length.
  392. length := len(data) - 1
  393. for i := 0; i < length; i += 2 {
  394. // For our test packet, doing this manually is about 25% faster
  395. // (740 ns vs. 1000ns) than doing it by calling binary.BigEndian.Uint16.
  396. csum += uint32(data[i]) << 8
  397. csum += uint32(data[i+1])
  398. }
  399. if len(data)%2 == 1 {
  400. csum += uint32(data[length]) << 8
  401. }
  402. for csum > 0xffff {
  403. csum = (csum >> 16) + (csum & 0xffff)
  404. }
  405. return ^uint16(csum)
  406. }
  407. // based on:
  408. // - https://github.com/google/gopacket/blob/v1.1.19/layers/tcpip.go#L26-L35
  409. func ipv4PseudoheaderChecksum(src, dst []byte, proto, length uint32) (csum uint32) {
  410. csum += (uint32(src[0]) + uint32(src[2])) << 8
  411. csum += uint32(src[1]) + uint32(src[3])
  412. csum += (uint32(dst[0]) + uint32(dst[2])) << 8
  413. csum += uint32(dst[1]) + uint32(dst[3])
  414. csum += proto
  415. csum += length & 0xffff
  416. csum += length >> 16
  417. return csum
  418. }
  419. // based on:
  420. // - https://github.com/google/gopacket/blob/v1.1.19/layers/tcpip.go#L37-L48
  421. func ipv6PseudoheaderChecksum(src, dst []byte, proto, length uint32) (csum uint32) {
  422. for i := 0; i < 16; i += 2 {
  423. csum += uint32(src[i]) << 8
  424. csum += uint32(src[i+1])
  425. csum += uint32(dst[i]) << 8
  426. csum += uint32(dst[i+1])
  427. }
  428. csum += proto
  429. csum += length & 0xffff
  430. csum += length >> 16
  431. return csum
  432. }