Skip incremental checksum update for unmodified fields

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
This commit is contained in:
Peter Johnston
2024-02-27 20:29:26 -08:00
committed by gVisor bot
parent 8841a6e25c
commit ccc3c2cbd2
2 changed files with 22 additions and 0 deletions
+3
View File
@@ -57,6 +57,9 @@ func checksumUpdate2ByteAlignedUint16(xsum, old, new uint16) uint16 {
// 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))
}
+19
View File
@@ -17,6 +17,7 @@
package header_test
import (
"bytes"
"fmt"
"math/rand"
"sync"
@@ -89,6 +90,24 @@ func TestICMPv4Checksum(t *testing.T) {
}, want, fmt.Sprintf("header: {% x} data {% x}", h, b.Flatten()))
}
func TestICMPv4ChecksumUpdate(t *testing.T) {
const icmpIdent = 0
data := make([]byte, header.ICMPv4MinimumSize)
h := header.ICMPv4(data)
h.SetType(header.ICMPv4EchoReply)
h.SetCode(header.ICMPv4UnusedCode)
h.SetIdent(icmpIdent)
h.SetChecksum(^checksum.Checksum(data, 0))
updated := header.ICMPv4(bytes.Clone(data))
// Perform an incremental checksum update where we aren't actually changing the ID.
updated.SetIdentWithChecksumUpdate(icmpIdent)
if updated.Checksum() != h.Checksum() {
t.Errorf("got updated.Checksum() = %x, want = %x", updated.Checksum(), h.Checksum())
}
}
func TestICMPv6Checksum(t *testing.T) {
rnd := rand.New(rand.NewSource(42))