packet.go 18 KB

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