diff --git a/crc.go b/crc.go index 9854362..7ad10b0 100644 --- a/crc.go +++ b/crc.go @@ -68,8 +68,7 @@ func (c *CRC791) AddUint8(value uint8) { 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 if c.needPad { sum += uint32(c.excedent) << 8 @@ -80,5 +79,26 @@ func (c *CRC791) Sum16() uint16 { 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. func (c *CRC791) Reset() { *c = CRC791{} } diff --git a/internet/stack-ip.go b/internet/stack-ip.go index 9b69a3d..f4c763b 100644 --- a/internet/stack-ip.go +++ b/internet/stack-ip.go @@ -90,11 +90,12 @@ func (sb *StackIP) Demux(carrierData []byte, offset int) error { sb.handlers.error("ip:Demux.validate") return err } - gotCRC := ifrm.CRC() - wantCRC := ifrm.CalculateHeaderCRC() - if gotCRC != wantCRC { - sb.handlers.error("StackIP:Demux:crc-mismatch", slog.Uint64("want", uint64(wantCRC)), slog.Uint64("got", uint64(gotCRC))) - return lneto.ErrBadCRC + + // Incoming CRC Validation of common IP Protocols. + var crc lneto.CRC791 + ifrm.CRCWriteHeader(&crc) + if !crc.VerifySum16(ifrm.CRC()) { + sb.handlers.error("ip:demux.crc") } off := ifrm.HeaderLength() totalLen := ifrm.TotalLength() @@ -110,7 +111,7 @@ func (sb *StackIP) Demux(carrierData []byte, offset int) error { return lneto.ErrPacketDrop } // Incoming CRC Validation of common IP Protocols. - var crc lneto.CRC791 + crc.Reset() switch proto { case lneto.IPProtoTCP: ifrm.CRCWriteTCPPseudo(&crc) @@ -119,7 +120,7 @@ func (sb *StackIP) Demux(carrierData []byte, offset int) error { return err } tfrm.CRCWrite(&crc) - if crc.Sum16() != tfrm.CRC() { + if !crc.VerifySum16(tfrm.CRC()) { sb.handlers.error("ip:demux.tcpcrc") return lneto.ErrBadCRC } @@ -130,7 +131,8 @@ func (sb *StackIP) Demux(carrierData []byte, offset int) error { return err } 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") return lneto.ErrBadCRC } @@ -210,7 +212,7 @@ func (sb *StackIP) recvicmp(carrierData []byte, offset int) error { return err } cfrm.CRCWrite(&crc) - if crc.Sum16() != cfrm.CRC() { + if !crc.VerifySum16(cfrm.CRC()) { return errors.New("ICMP CRC mismatch") } return nil diff --git a/ipv4/frame.go b/ipv4/frame.go index 42a5032..6f2269b 100644 --- a/ipv4/frame.go +++ b/ipv4/frame.go @@ -131,9 +131,13 @@ func (ifrm Frame) SetCRC(cs uint16) { // CalculateHeaderCRC calculates the CRC for this IPv4 frame. func (ifrm Frame) CalculateHeaderCRC() uint16 { 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[12:20]) - return crc.Sum16() } func (ifrm Frame) CRCWriteTCPPseudo(crc *lneto.CRC791) {