mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
This is important for correctness, because the current incremental checksum
update routine does not work correctly when the field is set to the same value.
See this equation from RFC 1071:
C - one's complement sum of old header
C' - one's complement sum of new header
m - old value of a 16-bit field
m' - new value of a 16-bit field
C' = C + (-m) + m' = C + (m' - m)
Assuming m is a 16-bit field, when m == m', then (m' - m) == (m + ~m) will
always equal -0, or 0xFFFF in one's complement arithmetic, rather than 0x0000.
The additive identity is typically 0, but since -0 has a different
representation, it is *not* an identity and therefore the equation produces a
different value.
PiperOrigin-RevId: 610963455
108 lines
3.8 KiB
Go
108 lines
3.8 KiB
Go
// Copyright 2018 The gVisor Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Package header provides the implementation of the encoding and decoding of
|
|
// network protocol headers.
|
|
package header
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
|
|
"gvisor.dev/gvisor/pkg/tcpip"
|
|
"gvisor.dev/gvisor/pkg/tcpip/checksum"
|
|
)
|
|
|
|
// PseudoHeaderChecksum calculates the pseudo-header checksum for the given
|
|
// destination protocol and network address. Pseudo-headers are needed by
|
|
// transport layers when calculating their own checksum.
|
|
func PseudoHeaderChecksum(protocol tcpip.TransportProtocolNumber, srcAddr tcpip.Address, dstAddr tcpip.Address, totalLen uint16) uint16 {
|
|
xsum := checksum.Checksum(srcAddr.AsSlice(), 0)
|
|
xsum = checksum.Checksum(dstAddr.AsSlice(), xsum)
|
|
|
|
// Add the length portion of the checksum to the pseudo-checksum.
|
|
var tmp [2]byte
|
|
binary.BigEndian.PutUint16(tmp[:], totalLen)
|
|
xsum = checksum.Checksum(tmp[:], xsum)
|
|
|
|
return checksum.Checksum([]byte{0, uint8(protocol)}, xsum)
|
|
}
|
|
|
|
// checksumUpdate2ByteAlignedUint16 updates a uint16 value in a calculated
|
|
// checksum.
|
|
//
|
|
// The value MUST begin at a 2-byte boundary in the original buffer.
|
|
func checksumUpdate2ByteAlignedUint16(xsum, old, new uint16) uint16 {
|
|
// As per RFC 1071 page 4,
|
|
// (4) Incremental Update
|
|
//
|
|
// ...
|
|
//
|
|
// To update the checksum, simply add the differences of the
|
|
// sixteen bit integers that have been changed. To see why this
|
|
// works, observe that every 16-bit integer has an additive inverse
|
|
// and that addition is associative. From this it follows that
|
|
// given the original value m, the new value m', and the old
|
|
// checksum C, the new checksum C' is:
|
|
//
|
|
// C' = C + (-m) + m' = C + (m' - m)
|
|
if old == new {
|
|
return xsum
|
|
}
|
|
return checksum.Combine(xsum, checksum.Combine(new, ^old))
|
|
}
|
|
|
|
// checksumUpdate2ByteAlignedAddress updates an address in a calculated
|
|
// checksum.
|
|
//
|
|
// The addresses must have the same length and must contain an even number
|
|
// of bytes. The address MUST begin at a 2-byte boundary in the original buffer.
|
|
func checksumUpdate2ByteAlignedAddress(xsum uint16, old, new tcpip.Address) uint16 {
|
|
const uint16Bytes = 2
|
|
|
|
if old.BitLen() != new.BitLen() {
|
|
panic(fmt.Sprintf("buffer lengths are different; old = %d, new = %d", old.BitLen()/8, new.BitLen()/8))
|
|
}
|
|
|
|
if oldBytes := old.BitLen() % 16; oldBytes != 0 {
|
|
panic(fmt.Sprintf("buffer has an odd number of bytes; got = %d", oldBytes))
|
|
}
|
|
|
|
oldAddr := old.AsSlice()
|
|
newAddr := new.AsSlice()
|
|
|
|
// As per RFC 1071 page 4,
|
|
// (4) Incremental Update
|
|
//
|
|
// ...
|
|
//
|
|
// To update the checksum, simply add the differences of the
|
|
// sixteen bit integers that have been changed. To see why this
|
|
// works, observe that every 16-bit integer has an additive inverse
|
|
// and that addition is associative. From this it follows that
|
|
// given the original value m, the new value m', and the old
|
|
// checksum C, the new checksum C' is:
|
|
//
|
|
// C' = C + (-m) + m' = C + (m' - m)
|
|
for len(oldAddr) != 0 {
|
|
// Convert the 2 byte sequences to uint16 values then apply the increment
|
|
// update.
|
|
xsum = checksumUpdate2ByteAlignedUint16(xsum, (uint16(oldAddr[0])<<8)+uint16(oldAddr[1]), (uint16(newAddr[0])<<8)+uint16(newAddr[1]))
|
|
oldAddr = oldAddr[uint16Bytes:]
|
|
newAddr = newAddr[uint16Bytes:]
|
|
}
|
|
|
|
return xsum
|
|
}
|