Checksum fixes (#24)

* Checksum 0x0000 is returned as 0xFFFF (mandatory for UDP, common for TCP)

* Both checksum zero values (0x0000 and 0xFFFF) are accepted when receiving

* Rename parameter in VerifySum16 to expectedSum16

---------

Co-authored-by: Pat Whittingslow <graded.sp@gmail.com>
This commit is contained in:
ddirect
2026-01-26 22:22:54 +02:00
committed by GitHub
parent 85f018a02c
commit 5e61b4600d
3 changed files with 38 additions and 12 deletions
+22 -2
View File
@@ -68,8 +68,7 @@ func (c *CRC791) AddUint8(value uint8) {
c.needPad = !c.needPad c.needPad = !c.needPad
} }
// Sum16 calculates the checksum with the data written to c thus far. func (c *CRC791) sum16() uint16 {
func (c *CRC791) Sum16() uint16 {
sum := c.sum sum := c.sum
if c.needPad { if c.needPad {
sum += uint32(c.excedent) << 8 sum += uint32(c.excedent) << 8
@@ -80,5 +79,26 @@ func (c *CRC791) Sum16() uint16 {
return uint16(^sum) return uint16(^sum)
} }
// Sum16 calculates the checksum with the data written to c thus far.
func (c *CRC791) Sum16() uint16 {
sum16 := c.sum16()
if sum16 == 0 {
// The zero value is always transmitted as 0xFFFF, as required by UDP (RFC 768), and still valid for TCP and IP.
sum16 = 0xffff
}
return sum16
}
// VerifySum16 verifies that the given checksum matches the data written to c thus far.
func (c *CRC791) VerifySum16(expectedSum16 uint16) bool {
// as recommended by RFC 1624, this implementation supports both 0x0000 and 0xFFFF as zero value for the checksum
cc := *c
if cc.needPad {
cc.AddUint8(0)
}
cc.AddUint16(expectedSum16)
return cc.sum16() == 0
}
// Reset zeros out the CRC791, resetting it to the initial state. // Reset zeros out the CRC791, resetting it to the initial state.
func (c *CRC791) Reset() { *c = CRC791{} } func (c *CRC791) Reset() { *c = CRC791{} }
+11 -9
View File
@@ -90,11 +90,12 @@ func (sb *StackIP) Demux(carrierData []byte, offset int) error {
sb.handlers.error("ip:Demux.validate") sb.handlers.error("ip:Demux.validate")
return err return err
} }
gotCRC := ifrm.CRC()
wantCRC := ifrm.CalculateHeaderCRC() // Incoming CRC Validation of common IP Protocols.
if gotCRC != wantCRC { var crc lneto.CRC791
sb.handlers.error("StackIP:Demux:crc-mismatch", slog.Uint64("want", uint64(wantCRC)), slog.Uint64("got", uint64(gotCRC))) ifrm.CRCWriteHeader(&crc)
return lneto.ErrBadCRC if !crc.VerifySum16(ifrm.CRC()) {
sb.handlers.error("ip:demux.crc")
} }
off := ifrm.HeaderLength() off := ifrm.HeaderLength()
totalLen := ifrm.TotalLength() totalLen := ifrm.TotalLength()
@@ -110,7 +111,7 @@ func (sb *StackIP) Demux(carrierData []byte, offset int) error {
return lneto.ErrPacketDrop return lneto.ErrPacketDrop
} }
// Incoming CRC Validation of common IP Protocols. // Incoming CRC Validation of common IP Protocols.
var crc lneto.CRC791 crc.Reset()
switch proto { switch proto {
case lneto.IPProtoTCP: case lneto.IPProtoTCP:
ifrm.CRCWriteTCPPseudo(&crc) ifrm.CRCWriteTCPPseudo(&crc)
@@ -119,7 +120,7 @@ func (sb *StackIP) Demux(carrierData []byte, offset int) error {
return err return err
} }
tfrm.CRCWrite(&crc) tfrm.CRCWrite(&crc)
if crc.Sum16() != tfrm.CRC() { if !crc.VerifySum16(tfrm.CRC()) {
sb.handlers.error("ip:demux.tcpcrc") sb.handlers.error("ip:demux.tcpcrc")
return lneto.ErrBadCRC return lneto.ErrBadCRC
} }
@@ -130,7 +131,8 @@ func (sb *StackIP) Demux(carrierData []byte, offset int) error {
return err return err
} }
ufrm.CRCWriteIPv4(&crc) ufrm.CRCWriteIPv4(&crc)
if crc.Sum16() != ufrm.CRC() { // checksums are optional in UDP: the field is set to zero in this case
if gotCrc := ufrm.CRC(); gotCrc != 0 && !crc.VerifySum16(gotCrc) {
sb.handlers.error("ip:demux.udpcrc") sb.handlers.error("ip:demux.udpcrc")
return lneto.ErrBadCRC return lneto.ErrBadCRC
} }
@@ -210,7 +212,7 @@ func (sb *StackIP) recvicmp(carrierData []byte, offset int) error {
return err return err
} }
cfrm.CRCWrite(&crc) cfrm.CRCWrite(&crc)
if crc.Sum16() != cfrm.CRC() { if !crc.VerifySum16(cfrm.CRC()) {
return errors.New("ICMP CRC mismatch") return errors.New("ICMP CRC mismatch")
} }
return nil return nil
+5 -1
View File
@@ -131,9 +131,13 @@ func (ifrm Frame) SetCRC(cs uint16) {
// CalculateHeaderCRC calculates the CRC for this IPv4 frame. // CalculateHeaderCRC calculates the CRC for this IPv4 frame.
func (ifrm Frame) CalculateHeaderCRC() uint16 { func (ifrm Frame) CalculateHeaderCRC() uint16 {
var crc lneto.CRC791 var crc lneto.CRC791
ifrm.CRCWriteHeader(&crc)
return crc.Sum16()
}
func (ifrm Frame) CRCWriteHeader(crc *lneto.CRC791) {
crc.Write(ifrm.buf[0:10]) crc.Write(ifrm.buf[0:10])
crc.Write(ifrm.buf[12:20]) crc.Write(ifrm.buf[12:20])
return crc.Sum16()
} }
func (ifrm Frame) CRCWriteTCPPseudo(crc *lneto.CRC791) { func (ifrm Frame) CRCWriteTCPPseudo(crc *lneto.CRC791) {