From fba97c990a5062e7ea430d2da942172ae18332bd Mon Sep 17 00:00:00 2001 From: ddirect <62487612+ddirect@users.noreply.github.com> Date: Thu, 5 Feb 2026 22:35:42 +0200 Subject: [PATCH] Checksum simplification proposal (#29) * Simplified checksum calculation and verification * Fixed tests * UDP: using NewBoundedFrame to create a Frame instance limited to the actual frame size. * Minor: renamed some functions * Commented and made more readable consecutive SetCRC calls. * Fixed icmp checksum calculation after rebase * Replaced NewBoundedFrame with explicit validation --- crc.go | 101 ++++++++++++---------------------- internal/ltesto/ltesto.go | 12 ++-- internet/pcap/capture.go | 86 +++++++++++------------------ internet/pcap/capture_test.go | 1 + internet/stack-ip.go | 64 ++++++++++----------- ipv4/frame.go | 18 +++--- ipv4/icmpv4/icmpv4.go | 8 --- ipv6/frame.go | 7 ++- lneto_test.go | 7 ++- tcp/frame.go | 6 -- udp/frame.go | 38 +------------ x/xnet/stack-async.go | 3 +- x/xnet/xnet_dns_test.go | 16 ++++-- 13 files changed, 139 insertions(+), 228 deletions(-) diff --git a/crc.go b/crc.go index 7ad10b0..b4fe215 100644 --- a/crc.go +++ b/crc.go @@ -11,35 +11,25 @@ import ( // // The zero value of CRC791 is ready to use. type CRC791 struct { - sum uint32 - excedent uint8 - needPad bool + sum uint32 } -// Write adds the bytes in p to the running checksum. -func (c *CRC791) Write(buff []byte) (n int, err error) { - if len(buff) == 0 { - return 0, nil +func sum16(sum uint32) uint16 { + sum = (sum & 0xffff) + sum>>16 + // the max value of sum at this point is 0x1fffe, so an additional round is enough + return ^uint16(sum + sum>>16) +} + +func sumWriteEven(sum uint32, buff []byte) uint32 { + for i := 0; i < len(buff); i += 2 { + sum += uint32(binary.BigEndian.Uint16(buff[i:])) } - if c.needPad { - c.sum += uint32(c.excedent)<<8 + uint32(buff[0]) - buff = buff[1:] - c.excedent = 0 - c.needPad = false - if len(buff) == 0 { - return 1, nil - } - } - count := len(buff) - for count > 1 { - c.sum += uint32(binary.BigEndian.Uint16(buff[len(buff)-count:])) - count -= 2 - } - if count != 0 { - c.excedent = buff[len(buff)-1] - c.needPad = true - } - return len(buff), nil + return sum +} + +// Write adds the bytes in p to the running checksum. The buffer size must be even or the function will panic. +func (c *CRC791) WriteEven(buff []byte) { + c.sum = sumWriteEven(c.sum, buff) } // AddUint32 adds a 32 bit value to the running checksum interpreted as BigEndian (network order). @@ -50,55 +40,32 @@ func (c *CRC791) AddUint32(value uint32) { // Add16 adds a 16 bit value to the running checksum interpreted as BigEndian (network order). func (c *CRC791) AddUint16(value uint16) { - if c.needPad { - c.sum += uint32(c.excedent)<<8 | uint32(value>>8) - c.excedent = byte(value) - } else { - c.sum += uint32(value) - } -} - -// Add16 adds value to the running checksum interpreted as BigEndian (network order). -func (c *CRC791) AddUint8(value uint8) { - if c.needPad { - c.sum += uint32(c.excedent)<<8 | uint32(value) - } else { - c.excedent = value - } - c.needPad = !c.needPad -} - -func (c *CRC791) sum16() uint16 { - sum := c.sum - if c.needPad { - sum += uint32(c.excedent) << 8 - } - for sum>>16 != 0 { - sum = (sum & 0xffff) + (sum >> 16) - } - return uint16(^sum) + c.sum += uint32(value) } // 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 + return sum16(c.sum) } -// 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) +// PayloadSum16 returns the checksum resulting by adding the bytes in p to the running checksum. +func (c *CRC791) PayloadSum16(buff []byte) uint16 { + odd := len(buff) & 1 + sum := sumWriteEven(c.sum, buff[:len(buff)-odd]) + if odd > 0 { + sum += uint32(buff[len(buff)-1]) << 8 } - cc.AddUint16(expectedSum16) - return cc.sum16() == 0 + return sum16(sum) } // Reset zeros out the CRC791, resetting it to the initial state. func (c *CRC791) Reset() { *c = CRC791{} } + +// NonZeroChecksum ensures that the given checksum is not zero, by returning 0xffff instead. +func NeverZeroSum(sum16 uint16) uint16 { + // 0x0000 and 0xffff are the same number in ones' complement math + if sum16 == 0 { + return 0xffff + } + return sum16 +} diff --git a/internal/ltesto/ltesto.go b/internal/ltesto/ltesto.go index 1e2ce1d..2d6f804 100644 --- a/internal/ltesto/ltesto.go +++ b/internal/ltesto/ltesto.go @@ -94,7 +94,10 @@ func (gen *PacketGen) AppendRandomIPv4TCPPacket(dst []byte, rng *rand.Rand, seg ifrm.SetProtocol(lneto.IPProtoTCP) *ifrm.SourceAddr() = gen.SrcIPv4 *ifrm.DestinationAddr() = gen.DstIPv4 - ifrm.SetCRC(ifrm.CalculateHeaderCRC()) + // Zero the CRC field so its value does not add to the final result. + ifrm.SetCRC(0) + crcValue := ifrm.CalculateHeaderCRC() + ifrm.SetCRC(crcValue) ipPayload := ifrm.Payload() tfrm, err := tcp.NewFrame(ipPayload) @@ -124,9 +127,10 @@ func (gen *PacketGen) AppendRandomIPv4TCPPacket(dst []byte, rng *rand.Rand, seg copy(tfrm.Options(), tcpOpts) var crc lneto.CRC791 ifrm.CRCWriteTCPPseudo(&crc) - tfrm.CRCWrite(&crc) - - tfrm.SetCRC(crc.Sum16()) + // Zero the CRC field so its value does not add to the final result. + tfrm.SetCRC(0) + crcValue = crc.PayloadSum16(tfrm.RawData()) + tfrm.SetCRC(crcValue) switch { case gen.SrcTCP != tfrm.SourcePort(): panic("IP options overwrite TCP header") diff --git a/internet/pcap/capture.go b/internet/pcap/capture.go index d3930d7..a7e0c6b 100644 --- a/internet/pcap/capture.go +++ b/internet/pcap/capture.go @@ -161,27 +161,26 @@ func (pc *PacketBreakdown) CaptureIPv6(dst []Frame, pkt []byte, bitOffset int) ( end := bitOffset + 40*octet var protoErrs []error var crc lneto.CRC791 - if proto == lneto.IPProtoTCP { - ifrm6.CRCWritePseudo(&crc) - tfrm, err := tcp.NewFrame(ifrm6.Payload()) - if err == nil { - tfrm.CRCWrite(&crc) - wantSum := crc.Sum16() - gotSum := tfrm.CRC() - if wantSum != gotSum { - protoErrs = append(protoErrs, &crcError16{protocol: "ipv6+tcp", want: wantSum, got: gotSum}) - } + ifrm6.CRCWritePseudo(&crc) + switch proto { + case lneto.IPProtoTCP: + if crc.PayloadSum16(ifrm6.Payload()) != 0 { + protoErrs = append(protoErrs, lneto.ErrBadCRC) } - } else if proto == lneto.IPProtoUDP || proto == lneto.IPProtoUDPLite { - ifrm6.CRCWritePseudo(&crc) + case lneto.IPProtoUDP, lneto.IPProtoUDPLite: ufrm, err := udp.NewFrame(ifrm6.Payload()) - if err == nil { - ufrm.CRCWriteIPv6(&crc) - wantSum := crc.Sum16() - gotSum := ufrm.CRC() - if wantSum != gotSum { - protoErrs = append(protoErrs, &crcError16{protocol: "ipv6+udp", want: wantSum, got: gotSum}) - } + if err != nil { + protoErrs = append(protoErrs, err) + break + } + ufrm.ValidateSize(pc.validator()) + if err = pc.validator().ErrPop(); err != nil { + protoErrs = append(protoErrs, err) + break + } + frameLen := ufrm.Length() + if crc.PayloadSum16(ufrm.RawData()[:frameLen]) != 0 { + protoErrs = append(protoErrs, lneto.ErrBadCRC) } } return pc.captureIPProto(proto, dst, pkt, end, protoErrs...) @@ -214,57 +213,48 @@ func (pc *PacketBreakdown) CaptureIPv4(dst []Frame, pkt []byte, bitOffset int) ( BitLength: octet * len(options), }) } - gotSum := ifrm4.CRC() - wantSum := ifrm4.CalculateHeaderCRC() - if gotSum != wantSum { - finfo.Errors = append(finfo.Errors, &crcError16{protocol: "ipv4", want: wantSum, got: gotSum}) + if ifrm4.CalculateHeaderCRC() != 0 { + finfo.Errors = append(finfo.Errors, lneto.ErrBadCRC) } dst = append(dst, finfo) proto := ifrm4.Protocol() end := bitOffset + octet*ifrm4.HeaderLength() var protoErrs []error var crc lneto.CRC791 + payload := ifrm4.Payload() switch proto { case lneto.IPProtoTCP: - ifrm4.CRCWriteTCPPseudo(&crc) - tfrm, err := tcp.NewFrame(ifrm4.Payload()) + tfrm, err := tcp.NewFrame(payload) if err == nil { tfrm.ValidateSize(pc.validator()) if pc.vld.HasError() { println("BAD TCP") return dst, pc.vld.ErrPop() } - tfrm.CRCWrite(&crc) - wantSum := crc.Sum16() - gotSum := tfrm.CRC() - if wantSum != gotSum { - protoErrs = append(protoErrs, &crcError16{protocol: "ipv4+tcp", want: wantSum, got: gotSum}) + ifrm4.CRCWriteTCPPseudo(&crc) + if crc.PayloadSum16(payload) != 0 { + protoErrs = append(protoErrs, lneto.ErrBadCRC) } } case lneto.IPProtoUDP: - ifrm4.CRCWriteUDPPseudo(&crc) - ufrm, err := udp.NewFrame(ifrm4.Payload()) + ufrm, err := udp.NewFrame(payload) if err == nil { ufrm.ValidateSize(pc.validator()) if pc.vld.HasError() { println("BAD UDP") return dst, pc.vld.ErrPop() } - ufrm.CRCWriteIPv4(&crc) - wantSum := crc.Sum16() - gotSum := ufrm.CRC() - if wantSum != gotSum { - protoErrs = append(protoErrs, &crcError16{protocol: "ipv4+udp", want: wantSum, got: gotSum}) + frameLen := ufrm.Length() + ifrm4.CRCWriteUDPPseudo(&crc, frameLen) + if crc.PayloadSum16(ufrm.RawData()[:frameLen]) != 0 { + protoErrs = append(protoErrs, lneto.ErrBadCRC) } } case lneto.IPProtoICMP: - ifrm, err := icmpv4.NewFrame(ifrm4.Payload()) + _, err := icmpv4.NewFrame(payload) if err == nil { - ifrm.CRCWrite(&crc) - wantSum := crc.Sum16() - gotSum := ifrm.CRC() - if wantSum != gotSum { - protoErrs = append(protoErrs, &crcError16{protocol: "icmpv4", want: wantSum, got: gotSum}) + if crc.PayloadSum16(payload) != 0 { + protoErrs = append(protoErrs, lneto.ErrBadCRC) } } } @@ -1309,13 +1299,3 @@ func remainingFrameInfo(proto any, class FieldClass, pktBitOffset, pktBitLen int }}, } } - -type crcError16 struct { - protocol string - want uint16 - got uint16 -} - -func (cerr *crcError16) Error() string { - return fmt.Sprintf("%s:incorrect checksum. want 0x%x, got 0x%x", cerr.protocol, cerr.want, cerr.got) -} diff --git a/internet/pcap/capture_test.go b/internet/pcap/capture_test.go index f89f549..adc4f67 100644 --- a/internet/pcap/capture_test.go +++ b/internet/pcap/capture_test.go @@ -447,6 +447,7 @@ func ExampleFormatter_dhcp() { totalLen := ipv4Size + udpSize + dhcpLen ifrm.SetTotalLength(uint16(totalLen)) ufrm.SetLength(uint16(udpSize + dhcpLen)) + // The CRC field is already zero here. ifrm.SetCRC(ifrm.CalculateHeaderCRC()) pkt = pkt[:ethSize+totalLen] diff --git a/internet/stack-ip.go b/internet/stack-ip.go index f4c763b..a7274dd 100644 --- a/internet/stack-ip.go +++ b/internet/stack-ip.go @@ -10,7 +10,6 @@ import ( "github.com/soypat/lneto/ethernet" "github.com/soypat/lneto/internal" "github.com/soypat/lneto/ipv4" - "github.com/soypat/lneto/ipv4/icmpv4" "github.com/soypat/lneto/tcp" "github.com/soypat/lneto/udp" ) @@ -91,11 +90,9 @@ func (sb *StackIP) Demux(carrierData []byte, offset int) error { return err } - // Incoming CRC Validation of common IP Protocols. - var crc lneto.CRC791 - ifrm.CRCWriteHeader(&crc) - if !crc.VerifySum16(ifrm.CRC()) { + if ifrm.CalculateHeaderCRC() != 0 { sb.handlers.error("ip:demux.crc") + return lneto.ErrBadCRC } off := ifrm.HeaderLength() totalLen := ifrm.TotalLength() @@ -111,28 +108,27 @@ func (sb *StackIP) Demux(carrierData []byte, offset int) error { return lneto.ErrPacketDrop } // Incoming CRC Validation of common IP Protocols. - crc.Reset() + var crc lneto.CRC791 switch proto { case lneto.IPProtoTCP: ifrm.CRCWriteTCPPseudo(&crc) - tfrm, err := tcp.NewFrame(ifrm.Payload()) - if err != nil { - return err - } - tfrm.CRCWrite(&crc) - if !crc.VerifySum16(tfrm.CRC()) { + if crc.PayloadSum16(ifrm.Payload()) != 0 { sb.handlers.error("ip:demux.tcpcrc") return lneto.ErrBadCRC } case lneto.IPProtoUDP: - ifrm.CRCWriteUDPPseudo(&crc) ufrm, err := udp.NewFrame(ifrm.Payload()) if err != nil { return err } - ufrm.CRCWriteIPv4(&crc) - // checksums are optional in UDP: the field is set to zero in this case - if gotCrc := ufrm.CRC(); gotCrc != 0 && !crc.VerifySum16(gotCrc) { + ufrm.ValidateSize(&sb.validator) + if err = sb.validator.ErrPop(); err != nil { + sb.handlers.error("ip:demux.udpvalidatesize") + return err + } + frameLen := ufrm.Length() + ifrm.CRCWriteUDPPseudo(&crc, frameLen) + if crc.PayloadSum16(ufrm.RawData()[:frameLen]) != 0 { sb.handlers.error("ip:demux.udpcrc") return lneto.ErrBadCRC } @@ -174,25 +170,29 @@ func (sb *StackIP) Encapsulate(carrierData []byte, offsetToIP, offsetToFrame int totalLen := n + headerlen ifrm.SetTotalLength(uint16(totalLen)) ifrm.SetProtocol(proto) - ifrm.SetCRC(ifrm.CalculateHeaderCRC()) + // Zero the CRC field so its value does not add to the final result. + ifrm.SetCRC(0) + crcValue := ifrm.CalculateHeaderCRC() + ifrm.SetCRC(crcValue) // Calculate CRC for our newly generated packet. var crc lneto.CRC791 + payload := ifrm.Payload() switch proto { case lneto.IPProtoTCP: ifrm.CRCWriteTCPPseudo(&crc) - tfrm, _ := tcp.NewFrame(ifrm.Payload()) - tfrm.CRCWrite(&crc) - tfrm.SetCRC(crc.Sum16()) + tfrm, _ := tcp.NewFrame(payload) + // Zero the CRC field so its value does not add to the final result. + tfrm.SetCRC(0) + crcValue = crc.PayloadSum16(payload) + tfrm.SetCRC(crcValue) case lneto.IPProtoUDP: - ifrm.CRCWriteUDPPseudo(&crc) - ufrm, _ := udp.NewFrame(ifrm.Payload()) + ufrm, _ := udp.NewFrame(payload) + ifrm.CRCWriteUDPPseudo(&crc, uint16(n)) ufrm.SetLength(uint16(n)) - ufrm.CRCWriteIPv4(&crc) - ufrm.SetCRC(crc.Sum16()) - if n != int(ufrm.Length()) { - sb.handlers.error("StackIP:encaps", slog.Int("n", n), slog.Int("un", int(ufrm.Length()))) - return 0, errors.New("invalid UDP length") - } + // Zero the CRC field so its value does not add to the final result. + ufrm.SetCRC(0) + crcValue = lneto.NeverZeroSum(crc.PayloadSum16(payload)) + ufrm.SetCRC(crcValue) } return totalLen, err } @@ -206,13 +206,9 @@ func (sb *StackIP) Register(h StackNode) error { } func (sb *StackIP) recvicmp(carrierData []byte, offset int) error { + frameData := carrierData[offset:] var crc lneto.CRC791 - cfrm, err := icmpv4.NewFrame(carrierData[offset:]) - if err != nil { - return err - } - cfrm.CRCWrite(&crc) - if !crc.VerifySum16(cfrm.CRC()) { + if crc.PayloadSum16(frameData) != 0 { return errors.New("ICMP CRC mismatch") } return nil diff --git a/ipv4/frame.go b/ipv4/frame.go index 6f2269b..7e56a59 100644 --- a/ipv4/frame.go +++ b/ipv4/frame.go @@ -136,23 +136,25 @@ func (ifrm Frame) CalculateHeaderCRC() uint16 { } func (ifrm Frame) CRCWriteHeader(crc *lneto.CRC791) { - crc.Write(ifrm.buf[0:10]) - crc.Write(ifrm.buf[12:20]) + crc.WriteEven(ifrm.buf[:20]) } func (ifrm Frame) CRCWriteTCPPseudo(crc *lneto.CRC791) { - crc.Write(ifrm.SourceAddr()[:]) - crc.Write(ifrm.DestinationAddr()[:]) - crc.AddUint16(ifrm.TotalLength() - 4*uint16(ifrm.ihl())) + crc.WriteEven(ifrm.sourceAndDestinationAddr()) + crc.AddUint16(ifrm.TotalLength() - uint16(ifrm.HeaderLength())) crc.AddUint16(uint16(ifrm.Protocol())) } -func (ifrm Frame) CRCWriteUDPPseudo(crc *lneto.CRC791) { - crc.Write(ifrm.SourceAddr()[:]) - crc.Write(ifrm.DestinationAddr()[:]) +func (ifrm Frame) CRCWriteUDPPseudo(crc *lneto.CRC791, udpLength uint16) { + crc.WriteEven(ifrm.sourceAndDestinationAddr()) + crc.AddUint16(udpLength) crc.AddUint16(uint16(ifrm.Protocol())) } +func (ifrm Frame) sourceAndDestinationAddr() []byte { + return ifrm.buf[12:20] +} + // SourceAddr returns pointer to the source IPv4 address in the IP header. func (ifrm Frame) SourceAddr() *[4]byte { return (*[4]byte)(ifrm.buf[12:16]) diff --git a/ipv4/icmpv4/icmpv4.go b/ipv4/icmpv4/icmpv4.go index 317e1d7..aad016d 100644 --- a/ipv4/icmpv4/icmpv4.go +++ b/ipv4/icmpv4/icmpv4.go @@ -3,8 +3,6 @@ package icmpv4 import ( "encoding/binary" "errors" - - "github.com/soypat/lneto" ) type Type uint8 @@ -87,12 +85,6 @@ func (frm Frame) SetCRC(crc uint16) { binary.BigEndian.PutUint16(frm.buf[2:4], crc) } -// CRCWrite calculates the checksum of the ICMP packet. Treats the checksum field as zero as per RFC 792. -func (frm Frame) CRCWrite(crc *lneto.CRC791) { - crc.AddUint16(binary.BigEndian.Uint16(frm.buf[0:2])) - crc.Write(frm.buf[4:]) -} - func (frm Frame) payload() []byte { return frm.buf[4:] } diff --git a/ipv6/frame.go b/ipv6/frame.go index 970bede..c0877d9 100644 --- a/ipv6/frame.go +++ b/ipv6/frame.go @@ -88,6 +88,10 @@ func (i6frm Frame) SetHopLimit(hop uint8) { i6frm.buf[7] = hop } +func (i6frm Frame) sourceAndDestinationAddr() []byte { + return i6frm.buf[8:40] +} + // SourceAddr returns pointer to the sending node unicast IPv6 address in the IP header. func (i6frm Frame) SourceAddr() *[16]byte { return (*[16]byte)(i6frm.buf[8:24]) @@ -99,8 +103,7 @@ func (i6frm Frame) DestinationAddr() *[16]byte { } func (i6frm Frame) CRCWritePseudo(crc *lneto.CRC791) { - crc.Write(i6frm.SourceAddr()[:]) - crc.Write(i6frm.DestinationAddr()[:]) + crc.WriteEven(i6frm.sourceAndDestinationAddr()) crc.AddUint32(uint32(i6frm.PayloadLength())) crc.AddUint32(uint32(i6frm.NextHeader())) } diff --git a/lneto_test.go b/lneto_test.go index c0a9e8f..9ae11a2 100644 --- a/lneto_test.go +++ b/lneto_test.go @@ -141,6 +141,8 @@ func TestIPv4TCPChecksum(t *testing.T) { t.Fatal(err) } wantCRC := ifrm.CRC() + // Zero the CRC field so its value does not add to the final result. + ifrm.SetCRC(0) gotCRC := ifrm.CalculateHeaderCRC() if wantCRC != gotCRC { t.Errorf("IPv4 CRC miscalculated. want %x, got %x", wantCRC, gotCRC) @@ -148,8 +150,9 @@ func TestIPv4TCPChecksum(t *testing.T) { wantCRC = tfrm.CRC() var crc lneto.CRC791 ifrm.CRCWriteTCPPseudo(&crc) - tfrm.CRCWrite(&crc) - gotCRC = crc.Sum16() + // Zero the CRC field so its value does not add to the final result. + tfrm.SetCRC(0) + gotCRC = crc.PayloadSum16(tfrm.RawData()) if wantCRC != gotCRC { t.Errorf("TCP CRC miscalculated. want %x, got %x", wantCRC, gotCRC) } diff --git a/tcp/frame.go b/tcp/frame.go index 62c44f3..57a94e6 100644 --- a/tcp/frame.go +++ b/tcp/frame.go @@ -140,12 +140,6 @@ func (tfrm Frame) Segment(payloadSize int) Segment { } } -func (tfrm Frame) CRCWrite(crc *lneto.CRC791) { - // Write excluding CRC - crc.Write(tfrm.buf[:16]) - crc.Write(tfrm.buf[18:]) -} - // SetSegment sets the sequence, acknowledgment, offset, window and flag fields of the TCP header from the the [Segment]. // Offset, like in [Frame.SetOffset], is expressed in words with minimum being 5. func (tfrm Frame) SetSegment(seg Segment, offset uint8) { diff --git a/udp/frame.go b/udp/frame.go index 5697519..ad91d22 100644 --- a/udp/frame.go +++ b/udp/frame.go @@ -7,7 +7,7 @@ import ( "github.com/soypat/lneto" ) -// NewUDPFrame returns a new UDPFrame with data set to buf. +// NewFrame returns a new udp.Frame with data set to buf. // An error is returned if the buffer size is smaller than 8. // Users should still call [Frame.ValidateSize] before working // with payload/options of frames to avoid panics. @@ -79,42 +79,6 @@ func (ufrm Frame) Payload() []byte { return ufrm.buf[sizeHeader:l] } -func (ufrm Frame) CRCWriteIPv4(crc *lneto.CRC791) { - crc.AddUint16(ufrm.Length()) - crc.AddUint16(ufrm.SourcePort()) - crc.AddUint16(ufrm.DestinationPort()) - crc.AddUint16(ufrm.Length()) // Length double tap for IPv4. - crc.Write(ufrm.Payload()) -} - -func (ufrm Frame) CRCWriteIPv6(crc *lneto.CRC791) { - crc.AddUint16(ufrm.SourcePort()) - crc.AddUint16(ufrm.DestinationPort()) - crc.AddUint16(ufrm.Length()) - crc.Write(ufrm.Payload()) -} - -// func (ufrm Frame) CalculateIPv4Checksum(ifrm IPv4Frame) uint16 { -// var crc lneto.CRC791 -// ifrm.crcWriteUDPPseudo(&crc) -// crc.AddUint16(ufrm.Length()) -// crc.AddUint16(ufrm.SourcePort()) -// crc.AddUint16(ufrm.DestinationPort()) -// crc.AddUint16(ufrm.Length()) // Length double tap. -// crc.Write(ufrm.Payload()) -// return crc.Sum16() -// } - -// func (ufrm Frame) CalculateIPv6Checksum(ifrm IPv6Frame) uint16 { -// var crc lneto.CRC791 -// ifrm.crcWritePseudo(&crc) -// crc.AddUint16(ufrm.SourcePort()) -// crc.AddUint16(ufrm.DestinationPort()) -// crc.AddUint16(ufrm.Length()) // Length double tap. -// crc.Write(ufrm.Payload()) -// return crc.Sum16() -// } - // ClearHeader zeros out the header contents. func (frm Frame) ClearHeader() { for i := range frm.buf[:sizeHeader] { diff --git a/x/xnet/stack-async.go b/x/xnet/stack-async.go index 24db1e2..9549b2d 100644 --- a/x/xnet/stack-async.go +++ b/x/xnet/stack-async.go @@ -567,6 +567,5 @@ func addr4(addr [4]byte, ok bool) netip.Addr { func hash(b []byte) uint16 { var csum lneto.CRC791 - csum.Write(b) - return csum.Sum16() + return csum.PayloadSum16(b) } diff --git a/x/xnet/xnet_dns_test.go b/x/xnet/xnet_dns_test.go index 4820cac..f8fd94d 100644 --- a/x/xnet/xnet_dns_test.go +++ b/x/xnet/xnet_dns_test.go @@ -205,7 +205,10 @@ func buildDNSResponsePacket(t *testing.T, txid uint16, dstPort uint16, hostname ifrm.SetProtocol(lneto.IPProtoUDP) *ifrm.SourceAddr() = srcIP.As4() *ifrm.DestinationAddr() = dstIP.As4() - ifrm.SetCRC(ifrm.CalculateHeaderCRC()) + // Zero the CRC field so its value does not add to the final result. + ifrm.SetCRC(0) + crcValue := ifrm.CalculateHeaderCRC() + ifrm.SetCRC(crcValue) // UDP header. udpStart := ipStart + ipHdrLen @@ -215,7 +218,8 @@ func buildDNSResponsePacket(t *testing.T, txid uint16, dstPort uint16, hostname } udpFrame.SetSourcePort(dns.ServerPort) udpFrame.SetDestinationPort(dstPort) - udpFrame.SetLength(uint16(udpHdrLen + len(dnsPayload))) + udpLen := udpHdrLen + len(dnsPayload) + udpFrame.SetLength(uint16(udpLen)) // Copy DNS payload before calculating checksum. dnsStart := udpStart + udpHdrLen @@ -223,9 +227,11 @@ func buildDNSResponsePacket(t *testing.T, txid uint16, dstPort uint16, hostname // Calculate UDP checksum using pseudo header. var crc lneto.CRC791 - ifrm.CRCWriteUDPPseudo(&crc) - udpFrame.CRCWriteIPv4(&crc) - udpFrame.SetCRC(crc.Sum16()) + ifrm.CRCWriteUDPPseudo(&crc, uint16(udpLen)) + // Zero the CRC field so its value does not add to the final result. + udpFrame.SetCRC(0) + crcValue = crc.PayloadSum16(udpFrame.RawData()) + udpFrame.SetCRC(crcValue) return pkt, nil }