diff --git a/pkg/tcpip/header/udp.go b/pkg/tcpip/header/udp.go index 98bcf1f5e3..ef53d6be35 100644 --- a/pkg/tcpip/header/udp.go +++ b/pkg/tcpip/header/udp.go @@ -140,7 +140,15 @@ func (b UDP) SetSourcePortWithChecksumUpdate(new uint16) { } old := b.SourcePort() b.SetSourcePort(new) - b.SetChecksum(^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new)) + xsum := ^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new) + // RFC 768: + // If the computed checksum is zero, it is transmitted as all ones. + // An all zero transmitted checksum value means that + // the transmitter generated no checksum. + if xsum == 0 { + xsum = 0xFFFF + } + b.SetChecksum(xsum) } // SetDestinationPortWithChecksumUpdate implements ChecksummableTransport. @@ -151,12 +159,16 @@ func (b UDP) SetDestinationPortWithChecksumUpdate(new uint16) { } old := b.DestinationPort() b.SetDestinationPort(new) - b.SetChecksum(^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new)) + xsum := ^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new) + if xsum == 0 { + xsum = 0xFFFF + } + b.SetChecksum(xsum) } // UpdateChecksumPseudoHeaderAddress implements ChecksummableTransport. func (b UDP) UpdateChecksumPseudoHeaderAddress(old, new tcpip.Address, fullChecksum bool) { - if b.Checksum() == 0 { + if fullChecksum && b.Checksum() == 0 { return } xsum := b.Checksum() @@ -167,6 +179,13 @@ func (b UDP) UpdateChecksumPseudoHeaderAddress(old, new tcpip.Address, fullCheck xsum = checksumUpdate2ByteAlignedAddress(xsum, old, new) if fullChecksum { xsum = ^xsum + // RFC 768: + // If the computed checksum is zero, it is transmitted as all ones. + // An all zero transmitted checksum value means that + // the transmitter generated no checksum. + if xsum == 0 { + xsum = 0xFFFF + } } b.SetChecksum(xsum) diff --git a/pkg/tcpip/network/ipv4/ipv4.go b/pkg/tcpip/network/ipv4/ipv4.go index f357eb48d9..7e809b04ea 100644 --- a/pkg/tcpip/network/ipv4/ipv4.go +++ b/pkg/tcpip/network/ipv4/ipv4.go @@ -549,7 +549,12 @@ func recalculateChecksum(pkt *stack.PacketBuffer, r *stack.Route) tcpip.Error { xsum := r.PseudoHeaderChecksum(header.UDPProtocolNumber, netHdr.PayloadLength()) xsum = checksum.Combine(xsum, pkt.Data().Checksum()) udp.SetChecksum(0) - udp.SetChecksum(^udp.CalculateChecksum(xsum)) + csum := ^udp.CalculateChecksum(xsum) + // RFC 768: If the computed checksum is zero, it is transmitted as all ones. + if csum == 0 { + csum = 0xFFFF + } + udp.SetChecksum(csum) } return nil } diff --git a/pkg/tcpip/nftables/nft_payload_set.go b/pkg/tcpip/nftables/nft_payload_set.go index e7b90892b1..706c44224b 100644 --- a/pkg/tcpip/nftables/nft_payload_set.go +++ b/pkg/tcpip/nftables/nft_payload_set.go @@ -160,11 +160,23 @@ func (op payloadSet) evaluate(regs *registerSet, evalCtx opEvalCtx) { // Reads the old checksum from the packet payload. oldTotalCsum := binary.BigEndian.Uint16(payload[op.csumOffset:]) + updateUDPCsum := op.base == linux.NFT_PAYLOAD_TRANSPORT_HEADER && pkt.TransportProtocolNumber == header.UDPProtocolNumber + + // If the UDP checksum is 0, it means the checksum is not set. + if updateUDPCsum && oldTotalCsum == 0 { + return + } + // New Total = Old Total - Old Data + New Data // Logic is very similar to checksum.checksumUpdate2ByteAlignedUint16 // in gvisor/pkg/tcpip/header/checksum.go newTotalCsum := checksum.Combine(^oldTotalCsum, checksum.Combine(newDataCsum, ^oldDataCsum)) - checksum.Put(payload[op.csumOffset:], ^newTotalCsum) + csum := ^newTotalCsum + // If the UDP checksum is 0, set it to all ones. + if updateUDPCsum && csum == 0 { + csum = 0xFFFF + } + checksum.Put(payload[op.csumOffset:], csum) } // Separately updates the L4 checksum if the pseudo-header flag is set. @@ -189,8 +201,16 @@ func (op payloadSet) evaluate(regs *registerSet, evalCtx opEvalCtx) { transport = header.IGMP(tBytes) } if transport != nil { // only updates if the transport header is present. + isUDP := pkt.TransportProtocolNumber == header.UDPProtocolNumber + if isUDP && transport.Checksum() == 0 { + return + } // New Total = Old Total - Old Data + New Data (same as above) - transport.SetChecksum(^checksum.Combine(^transport.Checksum(), checksum.Combine(newDataCsum, ^oldDataCsum))) + csum := ^checksum.Combine(^transport.Checksum(), checksum.Combine(newDataCsum, ^oldDataCsum)) + if isUDP && csum == 0 { + csum = 0xFFFF + } + transport.SetChecksum(csum) } } } diff --git a/pkg/tcpip/stack/packet_buffer.go b/pkg/tcpip/stack/packet_buffer.go index 5288802ee0..64998cf24e 100644 --- a/pkg/tcpip/stack/packet_buffer.go +++ b/pkg/tcpip/stack/packet_buffer.go @@ -1118,6 +1118,11 @@ func (pk *PacketBuffer) CalculateTransportChecksum() { xsum = header.PseudoHeaderChecksum(proto, src, dst, totalLen) xsum = checksum.Combine(xsum, pk.Data().Checksum()) t.SetChecksum(0) - t.SetChecksum(^t.CalculateChecksum(xsum)) + csum := ^t.CalculateChecksum(xsum) + // udp csum RFC 768. + if csum == 0 { + csum = 0xFFFF + } + t.SetChecksum(csum) } }