github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/tcpip/header/checksum.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package header provides the implementation of the encoding and decoding of 16 // network protocol headers. 17 package header 18 19 import ( 20 "encoding/binary" 21 "fmt" 22 23 "github.com/nicocha30/gvisor-ligolo/pkg/tcpip" 24 "github.com/nicocha30/gvisor-ligolo/pkg/tcpip/checksum" 25 ) 26 27 // PseudoHeaderChecksum calculates the pseudo-header checksum for the given 28 // destination protocol and network address. Pseudo-headers are needed by 29 // transport layers when calculating their own checksum. 30 func PseudoHeaderChecksum(protocol tcpip.TransportProtocolNumber, srcAddr tcpip.Address, dstAddr tcpip.Address, totalLen uint16) uint16 { 31 xsum := checksum.Checksum(srcAddr.AsSlice(), 0) 32 xsum = checksum.Checksum(dstAddr.AsSlice(), xsum) 33 34 // Add the length portion of the checksum to the pseudo-checksum. 35 var tmp [2]byte 36 binary.BigEndian.PutUint16(tmp[:], totalLen) 37 xsum = checksum.Checksum(tmp[:], xsum) 38 39 return checksum.Checksum([]byte{0, uint8(protocol)}, xsum) 40 } 41 42 // checksumUpdate2ByteAlignedUint16 updates a uint16 value in a calculated 43 // checksum. 44 // 45 // The value MUST begin at a 2-byte boundary in the original buffer. 46 func checksumUpdate2ByteAlignedUint16(xsum, old, new uint16) uint16 { 47 // As per RFC 1071 page 4, 48 // (4) Incremental Update 49 // 50 // ... 51 // 52 // To update the checksum, simply add the differences of the 53 // sixteen bit integers that have been changed. To see why this 54 // works, observe that every 16-bit integer has an additive inverse 55 // and that addition is associative. From this it follows that 56 // given the original value m, the new value m', and the old 57 // checksum C, the new checksum C' is: 58 // 59 // C' = C + (-m) + m' = C + (m' - m) 60 return checksum.Combine(xsum, checksum.Combine(new, ^old)) 61 } 62 63 // checksumUpdate2ByteAlignedAddress updates an address in a calculated 64 // checksum. 65 // 66 // The addresses must have the same length and must contain an even number 67 // of bytes. The address MUST begin at a 2-byte boundary in the original buffer. 68 func checksumUpdate2ByteAlignedAddress(xsum uint16, old, new tcpip.Address) uint16 { 69 const uint16Bytes = 2 70 71 if old.BitLen() != new.BitLen() { 72 panic(fmt.Sprintf("buffer lengths are different; old = %d, new = %d", old.BitLen()/8, new.BitLen()/8)) 73 } 74 75 if oldBytes := old.BitLen() % 16; oldBytes != 0 { 76 panic(fmt.Sprintf("buffer has an odd number of bytes; got = %d", oldBytes)) 77 } 78 79 oldAddr := old.AsSlice() 80 newAddr := new.AsSlice() 81 82 // As per RFC 1071 page 4, 83 // (4) Incremental Update 84 // 85 // ... 86 // 87 // To update the checksum, simply add the differences of the 88 // sixteen bit integers that have been changed. To see why this 89 // works, observe that every 16-bit integer has an additive inverse 90 // and that addition is associative. From this it follows that 91 // given the original value m, the new value m', and the old 92 // checksum C, the new checksum C' is: 93 // 94 // C' = C + (-m) + m' = C + (m' - m) 95 for len(oldAddr) != 0 { 96 // Convert the 2 byte sequences to uint16 values then apply the increment 97 // update. 98 xsum = checksumUpdate2ByteAlignedUint16(xsum, (uint16(oldAddr[0])<<8)+uint16(oldAddr[1]), (uint16(newAddr[0])<<8)+uint16(newAddr[1])) 99 oldAddr = oldAddr[uint16Bytes:] 100 newAddr = newAddr[uint16Bytes:] 101 } 102 103 return xsum 104 }