Expose header methods that validate checksums

This is done for IPv4, UDP and TCP headers.

This also changes the packet checkers used in tests to error on
zero-checksum, not sure why it was allowed before.

And while I'm here, make comments' case consistent.

RELNOTES: n/a

Fixes #5049

PiperOrigin-RevId: 369383862
This commit is contained in:
Arthur Sfez
2021-04-20 00:28:42 -07:00
committed by gVisor bot
parent 82eecd2e93
commit 2c8379d957
7 changed files with 105 additions and 92 deletions
+6 -14
View File
@@ -53,9 +53,8 @@ func IPv4(t *testing.T, b []byte, checkers ...NetworkChecker) {
t.Error("Not a valid IPv4 packet")
}
xsum := ipv4.CalculateChecksum()
if xsum != 0 && xsum != 0xffff {
t.Errorf("Bad checksum: 0x%x, checksum in packet: 0x%x", xsum, ipv4.Checksum())
if !ipv4.IsChecksumValid() {
t.Errorf("Bad checksum, got = %d", ipv4.Checksum())
}
for _, f := range checkers {
@@ -400,18 +399,11 @@ func TCP(checkers ...TransportChecker) NetworkChecker {
t.Errorf("Bad protocol, got = %d, want = %d", p, header.TCPProtocolNumber)
}
// Verify the checksum.
tcp := header.TCP(last.Payload())
l := uint16(len(tcp))
xsum := header.Checksum([]byte(first.SourceAddress()), 0)
xsum = header.Checksum([]byte(first.DestinationAddress()), xsum)
xsum = header.Checksum([]byte{0, byte(last.TransportProtocol())}, xsum)
xsum = header.Checksum([]byte{byte(l >> 8), byte(l)}, xsum)
xsum = header.Checksum(tcp, xsum)
if xsum != 0 && xsum != 0xffff {
t.Errorf("Bad checksum: 0x%x, checksum in segment: 0x%x", xsum, tcp.Checksum())
payload := tcp.Payload()
payloadChecksum := header.Checksum(payload, 0)
if !tcp.IsChecksumValid(first.SourceAddress(), first.DestinationAddress(), payloadChecksum, uint16(len(payload))) {
t.Errorf("Bad checksum, got = %d", tcp.Checksum())
}
// Run the transport checkers.
+26
View File
@@ -455,6 +455,32 @@ func IsV4LinkLocalMulticastAddress(addr tcpip.Address) bool {
return ipv4LinkLocalMulticastSubnet.Contains(addr)
}
// IsChecksumValid returns true iff the IPv4 header's checksum is valid.
func (b IPv4) IsChecksumValid() bool {
// There has been some confusion regarding verifying checksums. We need
// just look for negative 0 (0xffff) as the checksum, as it's not possible to
// get positive 0 (0) for the checksum. Some bad implementations could get it
// when doing entry replacement in the early days of the Internet,
// however the lore that one needs to check for both persists.
//
// RFC 1624 section 1 describes the source of this confusion as:
// [the partial recalculation method described in RFC 1071] computes a
// result for certain cases that differs from the one obtained from
// scratch (one's complement of one's complement sum of the original
// fields).
//
// However RFC 1624 section 5 clarifies that if using the verification method
// "recommended by RFC 1071, it does not matter if an intermediate system
// generated a -0 instead of +0".
//
// RFC1071 page 1 specifies the verification method as:
// (3) To check a checksum, the 1's complement sum is computed over the
// same set of octets, including the checksum field. If the result
// is all 1 bits (-0 in 1's complement arithmetic), the check
// succeeds.
return b.CalculateChecksum() == 0xffff
}
// IsV4MulticastAddress determines if the provided address is an IPv4 multicast
// address (range 224.0.0.0 to 239.255.255.255). The four most significant bits
// will be 1110 = 0xe0.
+30 -23
View File
@@ -216,104 +216,104 @@ const (
TCPDefaultMSS = 536
)
// SourcePort returns the "source port" field of the tcp header.
// SourcePort returns the "source port" field of the TCP header.
func (b TCP) SourcePort() uint16 {
return binary.BigEndian.Uint16(b[TCPSrcPortOffset:])
}
// DestinationPort returns the "destination port" field of the tcp header.
// DestinationPort returns the "destination port" field of the TCP header.
func (b TCP) DestinationPort() uint16 {
return binary.BigEndian.Uint16(b[TCPDstPortOffset:])
}
// SequenceNumber returns the "sequence number" field of the tcp header.
// SequenceNumber returns the "sequence number" field of the TCP header.
func (b TCP) SequenceNumber() uint32 {
return binary.BigEndian.Uint32(b[TCPSeqNumOffset:])
}
// AckNumber returns the "ack number" field of the tcp header.
// AckNumber returns the "ack number" field of the TCP header.
func (b TCP) AckNumber() uint32 {
return binary.BigEndian.Uint32(b[TCPAckNumOffset:])
}
// DataOffset returns the "data offset" field of the tcp header. The return
// DataOffset returns the "data offset" field of the TCP header. The return
// value is the length of the TCP header in bytes.
func (b TCP) DataOffset() uint8 {
return (b[TCPDataOffset] >> 4) * 4
}
// Payload returns the data in the tcp packet.
// Payload returns the data in the TCP packet.
func (b TCP) Payload() []byte {
return b[b.DataOffset():]
}
// Flags returns the flags field of the tcp header.
// Flags returns the flags field of the TCP header.
func (b TCP) Flags() TCPFlags {
return TCPFlags(b[TCPFlagsOffset])
}
// WindowSize returns the "window size" field of the tcp header.
// WindowSize returns the "window size" field of the TCP header.
func (b TCP) WindowSize() uint16 {
return binary.BigEndian.Uint16(b[TCPWinSizeOffset:])
}
// Checksum returns the "checksum" field of the tcp header.
// Checksum returns the "checksum" field of the TCP header.
func (b TCP) Checksum() uint16 {
return binary.BigEndian.Uint16(b[TCPChecksumOffset:])
}
// UrgentPointer returns the "urgent pointer" field of the tcp header.
// UrgentPointer returns the "urgent pointer" field of the TCP header.
func (b TCP) UrgentPointer() uint16 {
return binary.BigEndian.Uint16(b[TCPUrgentPtrOffset:])
}
// SetSourcePort sets the "source port" field of the tcp header.
// SetSourcePort sets the "source port" field of the TCP header.
func (b TCP) SetSourcePort(port uint16) {
binary.BigEndian.PutUint16(b[TCPSrcPortOffset:], port)
}
// SetDestinationPort sets the "destination port" field of the tcp header.
// SetDestinationPort sets the "destination port" field of the TCP header.
func (b TCP) SetDestinationPort(port uint16) {
binary.BigEndian.PutUint16(b[TCPDstPortOffset:], port)
}
// SetChecksum sets the checksum field of the tcp header.
// SetChecksum sets the checksum field of the TCP header.
func (b TCP) SetChecksum(checksum uint16) {
binary.BigEndian.PutUint16(b[TCPChecksumOffset:], checksum)
}
// SetDataOffset sets the data offset field of the tcp header. headerLen should
// SetDataOffset sets the data offset field of the TCP header. headerLen should
// be the length of the TCP header in bytes.
func (b TCP) SetDataOffset(headerLen uint8) {
b[TCPDataOffset] = (headerLen / 4) << 4
}
// SetSequenceNumber sets the sequence number field of the tcp header.
// SetSequenceNumber sets the sequence number field of the TCP header.
func (b TCP) SetSequenceNumber(seqNum uint32) {
binary.BigEndian.PutUint32(b[TCPSeqNumOffset:], seqNum)
}
// SetAckNumber sets the ack number field of the tcp header.
// SetAckNumber sets the ack number field of the TCP header.
func (b TCP) SetAckNumber(ackNum uint32) {
binary.BigEndian.PutUint32(b[TCPAckNumOffset:], ackNum)
}
// SetFlags sets the flags field of the tcp header.
// SetFlags sets the flags field of the TCP header.
func (b TCP) SetFlags(flags uint8) {
b[TCPFlagsOffset] = flags
}
// SetWindowSize sets the window size field of the tcp header.
// SetWindowSize sets the window size field of the TCP header.
func (b TCP) SetWindowSize(rcvwnd uint16) {
binary.BigEndian.PutUint16(b[TCPWinSizeOffset:], rcvwnd)
}
// SetUrgentPoiner sets the window size field of the tcp header.
// SetUrgentPoiner sets the window size field of the TCP header.
func (b TCP) SetUrgentPoiner(urgentPointer uint16) {
binary.BigEndian.PutUint16(b[TCPUrgentPtrOffset:], urgentPointer)
}
// CalculateChecksum calculates the checksum of the tcp segment.
// CalculateChecksum calculates the checksum of the TCP segment.
// partialChecksum is the checksum of the network-layer pseudo-header
// and the checksum of the segment data.
func (b TCP) CalculateChecksum(partialChecksum uint16) uint16 {
@@ -321,6 +321,13 @@ func (b TCP) CalculateChecksum(partialChecksum uint16) uint16 {
return Checksum(b[:b.DataOffset()], partialChecksum)
}
// IsChecksumValid returns true iff the TCP header's checksum is valid.
func (b TCP) IsChecksumValid(src, dst tcpip.Address, payloadChecksum, payloadLength uint16) bool {
xsum := PseudoHeaderChecksum(TCPProtocolNumber, src, dst, uint16(b.DataOffset())+payloadLength)
xsum = ChecksumCombine(xsum, payloadChecksum)
return b.CalculateChecksum(xsum) == 0xffff
}
// Options returns a slice that holds the unparsed TCP options in the segment.
func (b TCP) Options() []byte {
return b[TCPMinimumSize:b.DataOffset()]
@@ -340,7 +347,7 @@ func (b TCP) encodeSubset(seq, ack uint32, flags TCPFlags, rcvwnd uint16) {
binary.BigEndian.PutUint16(b[TCPWinSizeOffset:], rcvwnd)
}
// Encode encodes all the fields of the tcp header.
// Encode encodes all the fields of the TCP header.
func (b TCP) Encode(t *TCPFields) {
b.encodeSubset(t.SeqNum, t.AckNum, t.Flags, t.WindowSize)
binary.BigEndian.PutUint16(b[TCPSrcPortOffset:], t.SrcPort)
@@ -350,7 +357,7 @@ func (b TCP) Encode(t *TCPFields) {
binary.BigEndian.PutUint16(b[TCPUrgentPtrOffset:], t.UrgentPointer)
}
// EncodePartial updates a subset of the fields of the tcp header. It is useful
// EncodePartial updates a subset of the fields of the TCP header. It is useful
// in cases when similar segments are produced.
func (b TCP) EncodePartial(partialChecksum, length uint16, seqnum, acknum uint32, flags TCPFlags, rcvwnd uint16) {
// Add the total length and "flags" field contributions to the checksum.
@@ -374,7 +381,7 @@ func (b TCP) EncodePartial(partialChecksum, length uint16, seqnum, acknum uint32
}
// ParseSynOptions parses the options received in a SYN segment and returns the
// relevant ones. opts should point to the option part of the TCP Header.
// relevant ones. opts should point to the option part of the TCP header.
func ParseSynOptions(opts []byte, isAck bool) TCPSynOptions {
limit := len(opts)
+17 -10
View File
@@ -64,17 +64,17 @@ const (
UDPProtocolNumber tcpip.TransportProtocolNumber = 17
)
// SourcePort returns the "source port" field of the udp header.
// SourcePort returns the "source port" field of the UDP header.
func (b UDP) SourcePort() uint16 {
return binary.BigEndian.Uint16(b[udpSrcPort:])
}
// DestinationPort returns the "destination port" field of the udp header.
// DestinationPort returns the "destination port" field of the UDP header.
func (b UDP) DestinationPort() uint16 {
return binary.BigEndian.Uint16(b[udpDstPort:])
}
// Length returns the "length" field of the udp header.
// Length returns the "length" field of the UDP header.
func (b UDP) Length() uint16 {
return binary.BigEndian.Uint16(b[udpLength:])
}
@@ -84,39 +84,46 @@ func (b UDP) Payload() []byte {
return b[UDPMinimumSize:]
}
// Checksum returns the "checksum" field of the udp header.
// Checksum returns the "checksum" field of the UDP header.
func (b UDP) Checksum() uint16 {
return binary.BigEndian.Uint16(b[udpChecksum:])
}
// SetSourcePort sets the "source port" field of the udp header.
// SetSourcePort sets the "source port" field of the UDP header.
func (b UDP) SetSourcePort(port uint16) {
binary.BigEndian.PutUint16(b[udpSrcPort:], port)
}
// SetDestinationPort sets the "destination port" field of the udp header.
// SetDestinationPort sets the "destination port" field of the UDP header.
func (b UDP) SetDestinationPort(port uint16) {
binary.BigEndian.PutUint16(b[udpDstPort:], port)
}
// SetChecksum sets the "checksum" field of the udp header.
// SetChecksum sets the "checksum" field of the UDP header.
func (b UDP) SetChecksum(checksum uint16) {
binary.BigEndian.PutUint16(b[udpChecksum:], checksum)
}
// SetLength sets the "length" field of the udp header.
// SetLength sets the "length" field of the UDP header.
func (b UDP) SetLength(length uint16) {
binary.BigEndian.PutUint16(b[udpLength:], length)
}
// CalculateChecksum calculates the checksum of the udp packet, given the
// CalculateChecksum calculates the checksum of the UDP packet, given the
// checksum of the network-layer pseudo-header and the checksum of the payload.
func (b UDP) CalculateChecksum(partialChecksum uint16) uint16 {
// Calculate the rest of the checksum.
return Checksum(b[:UDPMinimumSize], partialChecksum)
}
// Encode encodes all the fields of the udp header.
// IsChecksumValid returns true iff the UDP header's checksum is valid.
func (b UDP) IsChecksumValid(src, dst tcpip.Address, payloadChecksum uint16) bool {
xsum := PseudoHeaderChecksum(UDPProtocolNumber, dst, src, b.Length())
xsum = ChecksumCombine(xsum, payloadChecksum)
return b.CalculateChecksum(xsum) == 0xffff
}
// Encode encodes all the fields of the UDP header.
func (b UDP) Encode(u *UDPFields) {
binary.BigEndian.PutUint16(b[udpSrcPort:], u.SrcPort)
binary.BigEndian.PutUint16(b[udpDstPort:], u.DstPort)
+1 -22
View File
@@ -1178,28 +1178,7 @@ func (p *protocol) parseAndValidate(pkt *stack.PacketBuffer) (header.IPv4, bool)
return nil, false
}
// There has been some confusion regarding verifying checksums. We need
// just look for negative 0 (0xffff) as the checksum, as it's not possible to
// get positive 0 (0) for the checksum. Some bad implementations could get it
// when doing entry replacement in the early days of the Internet,
// however the lore that one needs to check for both persists.
//
// RFC 1624 section 1 describes the source of this confusion as:
// [the partial recalculation method described in RFC 1071] computes a
// result for certain cases that differs from the one obtained from
// scratch (one's complement of one's complement sum of the original
// fields).
//
// However RFC 1624 section 5 clarifies that if using the verification method
// "recommended by RFC 1071, it does not matter if an intermediate system
// generated a -0 instead of +0".
//
// RFC1071 page 1 specifies the verification method as:
// (3) To check a checksum, the 1's complement sum is computed over the
// same set of octets, including the checksum field. If the result
// is all 1 bits (-0 in 1's complement arithmetic), the check
// succeeds.
if h.CalculateChecksum() != 0xffff {
if !h.IsChecksumValid() {
return nil, false
}
+4 -10
View File
@@ -236,20 +236,14 @@ func (s *segment) parse(skipChecksumValidation bool) bool {
s.options = []byte(s.hdr[header.TCPMinimumSize:])
s.parsedOptions = header.ParseTCPOptions(s.options)
verifyChecksum := true
if skipChecksumValidation {
s.csumValid = true
verifyChecksum = false
}
if verifyChecksum {
} else {
s.csum = s.hdr.Checksum()
xsum := header.PseudoHeaderChecksum(ProtocolNumber, s.srcAddr, s.dstAddr, uint16(s.data.Size()+len(s.hdr)))
xsum = s.hdr.CalculateChecksum(xsum)
xsum = header.ChecksumVV(s.data, xsum)
s.csumValid = xsum == 0xffff
payloadChecksum := header.ChecksumVV(s.data, 0)
payloadLength := uint16(s.data.Size())
s.csumValid = s.hdr.IsChecksumValid(s.srcAddr, s.dstAddr, payloadChecksum, payloadLength)
}
s.sequenceNumber = seqnum.Value(s.hdr.SequenceNumber())
s.ackNumber = seqnum.Value(s.hdr.AckNumber())
s.flags = s.hdr.Flags()
+21 -13
View File
@@ -1255,20 +1255,29 @@ func (e *endpoint) Readiness(mask waiter.EventMask) waiter.EventMask {
}
// verifyChecksum verifies the checksum unless RX checksum offload is enabled.
// On IPv4, UDP checksum is optional, and a zero value means the transmitter
// omitted the checksum generation (RFC768).
// On IPv6, UDP checksum is not optional (RFC2460 Section 8.1).
func verifyChecksum(hdr header.UDP, pkt *stack.PacketBuffer) bool {
if !pkt.RXTransportChecksumValidated &&
(hdr.Checksum() != 0 || pkt.NetworkProtocolNumber == header.IPv6ProtocolNumber) {
netHdr := pkt.Network()
xsum := header.PseudoHeaderChecksum(ProtocolNumber, netHdr.DestinationAddress(), netHdr.SourceAddress(), hdr.Length())
for _, v := range pkt.Data().Views() {
xsum = header.Checksum(v, xsum)
}
return hdr.CalculateChecksum(xsum) == 0xffff
if pkt.RXTransportChecksumValidated {
return true
}
return true
// On IPv4, UDP checksum is optional, and a zero value means the transmitter
// omitted the checksum generation, as per RFC 768:
//
// An all zero transmitted checksum value means that the transmitter
// generated no checksum (for debugging or for higher level protocols that
// don't care).
//
// On IPv6, UDP checksum is not optional, as per RFC 2460 Section 8.1:
//
// Unlike IPv4, when UDP packets are originated by an IPv6 node, the UDP
// checksum is not optional.
if pkt.NetworkProtocolNumber == header.IPv4ProtocolNumber && hdr.Checksum() == 0 {
return true
}
netHdr := pkt.Network()
payloadChecksum := pkt.Data().AsRange().Checksum()
return hdr.IsChecksumValid(netHdr.SourceAddress(), netHdr.DestinationAddress(), payloadChecksum)
}
// HandlePacket is called by the stack when new packets arrive to this transport
@@ -1284,7 +1293,6 @@ func (e *endpoint) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketB
}
if !verifyChecksum(hdr, pkt) {
// Checksum Error.
e.stack.Stats().UDP.ChecksumErrors.Increment()
e.stats.ReceiveErrors.ChecksumErrors.Increment()
return