gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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 "gvisor.dev/gvisor/pkg/tcpip" 24 "gvisor.dev/gvisor/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 if old == new { 61 return xsum 62 } 63 return checksum.Combine(xsum, checksum.Combine(new, ^old)) 64 } 65 66 // checksumUpdate2ByteAlignedAddress updates an address in a calculated 67 // checksum. 68 // 69 // The addresses must have the same length and must contain an even number 70 // of bytes. The address MUST begin at a 2-byte boundary in the original buffer. 71 func checksumUpdate2ByteAlignedAddress(xsum uint16, old, new tcpip.Address) uint16 { 72 const uint16Bytes = 2 73 74 if old.BitLen() != new.BitLen() { 75 panic(fmt.Sprintf("buffer lengths are different; old = %d, new = %d", old.BitLen()/8, new.BitLen()/8)) 76 } 77 78 if oldBytes := old.BitLen() % 16; oldBytes != 0 { 79 panic(fmt.Sprintf("buffer has an odd number of bytes; got = %d", oldBytes)) 80 } 81 82 oldAddr := old.AsSlice() 83 newAddr := new.AsSlice() 84 85 // As per RFC 1071 page 4, 86 // (4) Incremental Update 87 // 88 // ... 89 // 90 // To update the checksum, simply add the differences of the 91 // sixteen bit integers that have been changed. To see why this 92 // works, observe that every 16-bit integer has an additive inverse 93 // and that addition is associative. From this it follows that 94 // given the original value m, the new value m', and the old 95 // checksum C, the new checksum C' is: 96 // 97 // C' = C + (-m) + m' = C + (m' - m) 98 for len(oldAddr) != 0 { 99 // Convert the 2 byte sequences to uint16 values then apply the increment 100 // update. 101 xsum = checksumUpdate2ByteAlignedUint16(xsum, (uint16(oldAddr[0])<<8)+uint16(oldAddr[1]), (uint16(newAddr[0])<<8)+uint16(newAddr[1])) 102 oldAddr = oldAddr[uint16Bytes:] 103 newAddr = newAddr[uint16Bytes:] 104 } 105 106 return xsum 107 }