From eaac2806ffadbb3db6317e58c61b855b1350f0aa Mon Sep 17 00:00:00 2001 From: Bhasker Hariharan Date: Tue, 9 Apr 2019 11:22:28 -0700 Subject: Add TCP checksum verification. PiperOrigin-RevId: 242704699 Change-Id: I87db368ca343b3b4bf4f969b17d3aa4ce2f8bd4f --- pkg/tcpip/transport/tcp/connect.go | 2 +- pkg/tcpip/transport/tcp/endpoint.go | 9 ++++++++- pkg/tcpip/transport/tcp/forwarder.go | 2 +- pkg/tcpip/transport/tcp/protocol.go | 2 +- pkg/tcpip/transport/tcp/segment.go | 31 ++++++++++++++++++++++++++++--- pkg/tcpip/transport/tcp/tcp_test.go | 29 +++++++++++++++++++++++++++-- pkg/tcpip/transport/udp/endpoint.go | 2 +- 7 files changed, 67 insertions(+), 10 deletions(-) (limited to 'pkg/tcpip/transport') diff --git a/pkg/tcpip/transport/tcp/connect.go b/pkg/tcpip/transport/tcp/connect.go index 056e0b09a..6c4a4d95e 100644 --- a/pkg/tcpip/transport/tcp/connect.go +++ b/pkg/tcpip/transport/tcp/connect.go @@ -595,7 +595,7 @@ func sendTCP(r *stack.Route, id stack.TransportEndpointID, data buffer.Vectorise // TCP header, then the kernel calculate a checksum of the // header and data and get the right sum of the TCP packet. tcp.SetChecksum(xsum) - } else if r.Capabilities()&stack.CapabilityChecksumOffload == 0 { + } else if r.Capabilities()&stack.CapabilityTXChecksumOffload == 0 { xsum = header.ChecksumVV(data, xsum) tcp.SetChecksum(^tcp.CalculateChecksum(xsum)) } diff --git a/pkg/tcpip/transport/tcp/endpoint.go b/pkg/tcpip/transport/tcp/endpoint.go index 41c87cc7e..b5d05af7d 100644 --- a/pkg/tcpip/transport/tcp/endpoint.go +++ b/pkg/tcpip/transport/tcp/endpoint.go @@ -1447,6 +1447,13 @@ func (e *endpoint) HandlePacket(r *stack.Route, id stack.TransportEndpointID, vv return } + if !s.csumValid { + e.stack.Stats().MalformedRcvdPackets.Increment() + e.stack.Stats().TCP.ChecksumErrors.Increment() + s.decRef() + return + } + e.stack.Stats().TCP.ValidSegmentsReceived.Increment() if (s.flags & header.TCPFlagRst) != 0 { e.stack.Stats().TCP.ResetsReceived.Increment() @@ -1721,7 +1728,7 @@ func (e *endpoint) initGSO() { panic(fmt.Sprintf("Unknown netProto: %v", e.netProto)) } gso.NeedsCsum = true - gso.CsumOffset = header.TCPChecksumOffset() + gso.CsumOffset = header.TCPChecksumOffset gso.MaxSize = e.route.GSOMaxSize() e.gso = gso } diff --git a/pkg/tcpip/transport/tcp/forwarder.go b/pkg/tcpip/transport/tcp/forwarder.go index 7a6589cfd..6a7efaf1d 100644 --- a/pkg/tcpip/transport/tcp/forwarder.go +++ b/pkg/tcpip/transport/tcp/forwarder.go @@ -68,7 +68,7 @@ func (f *Forwarder) HandlePacket(r *stack.Route, id stack.TransportEndpointID, n defer s.decRef() // We only care about well-formed SYN packets. - if !s.parse() || s.flags != header.TCPFlagSyn { + if !s.parse() || !s.csumValid || s.flags != header.TCPFlagSyn { return false } diff --git a/pkg/tcpip/transport/tcp/protocol.go b/pkg/tcpip/transport/tcp/protocol.go index 230668b5d..b5fb160bc 100644 --- a/pkg/tcpip/transport/tcp/protocol.go +++ b/pkg/tcpip/transport/tcp/protocol.go @@ -130,7 +130,7 @@ func (*protocol) HandleUnknownDestinationPacket(r *stack.Route, id stack.Transpo s := newSegment(r, id, vv) defer s.decRef() - if !s.parse() { + if !s.parse() || !s.csumValid { return false } diff --git a/pkg/tcpip/transport/tcp/segment.go b/pkg/tcpip/transport/tcp/segment.go index df8402bf9..c603fe713 100644 --- a/pkg/tcpip/transport/tcp/segment.go +++ b/pkg/tcpip/transport/tcp/segment.go @@ -45,6 +45,10 @@ type segment struct { ackNumber seqnum.Value flags uint8 window seqnum.Size + // csum is only populated for received segments. + csum uint16 + // csumValid is true if the csum in the received segment is valid. + csumValid bool // parsedOptions stores the parsed values from the options in the segment. parsedOptions header.TCPOptions @@ -124,7 +128,13 @@ func (s *segment) logicalLen() seqnum.Size { // parse populates the sequence & ack numbers, flags, and window fields of the // segment from the TCP header stored in the data. It then updates the view to -// skip the data. Returns boolean indicating if the parsing was successful. +// skip the header. +// +// Returns boolean indicating if the parsing was successful. +// +// If checksum verification is not offloaded then parse also verifies the +// TCP checksum and stores the checksum and result of checksum verification in +// the csum and csumValid fields of the segment. func (s *segment) parse() bool { h := header.TCP(s.data.First()) @@ -145,12 +155,27 @@ func (s *segment) parse() bool { s.options = []byte(h[header.TCPMinimumSize:offset]) s.parsedOptions = header.ParseTCPOptions(s.options) - s.data.TrimFront(offset) + + // Query the link capabilities to decide if checksum validation is + // required. + verifyChecksum := true + if s.route.Capabilities()&stack.CapabilityRXChecksumOffload != 0 { + s.csumValid = true + verifyChecksum = false + s.data.TrimFront(offset) + } + if verifyChecksum { + s.csum = h.Checksum() + xsum := s.route.PseudoHeaderChecksum(ProtocolNumber, uint16(s.data.Size())) + xsum = h.CalculateChecksum(xsum) + s.data.TrimFront(offset) + xsum = header.ChecksumVV(s.data, xsum) + s.csumValid = xsum == 0xffff + } s.sequenceNumber = seqnum.Value(h.SequenceNumber()) s.ackNumber = seqnum.Value(h.AckNumber()) s.flags = h.Flags() s.window = seqnum.Size(h.WindowSize()) - return true } diff --git a/pkg/tcpip/transport/tcp/tcp_test.go b/pkg/tcpip/transport/tcp/tcp_test.go index 7f2615ca9..af50ac8af 100644 --- a/pkg/tcpip/transport/tcp/tcp_test.go +++ b/pkg/tcpip/transport/tcp/tcp_test.go @@ -2963,8 +2963,7 @@ func TestReceivedInvalidSegmentCountIncrement(t *testing.T) { RcvWnd: 30000, }) tcpbuf := vv.First()[header.IPv4MinimumSize:] - // 12 is the TCP header data offset. - tcpbuf[12] = ((header.TCPMinimumSize - 1) / 4) << 4 + tcpbuf[header.TCPDataOffset] = ((header.TCPMinimumSize - 1) / 4) << 4 c.SendSegment(vv) @@ -2973,6 +2972,32 @@ func TestReceivedInvalidSegmentCountIncrement(t *testing.T) { } } +func TestReceivedIncorrectChecksumIncrement(t *testing.T) { + c := context.New(t, defaultMTU) + defer c.Cleanup() + c.CreateConnected(789, 30000, nil) + stats := c.Stack().Stats() + want := stats.TCP.ChecksumErrors.Value() + 1 + vv := c.BuildSegment([]byte{0x1, 0x2, 0x3}, &context.Headers{ + SrcPort: context.TestPort, + DstPort: c.Port, + Flags: header.TCPFlagAck, + SeqNum: seqnum.Value(790), + AckNum: c.IRS.Add(1), + RcvWnd: 30000, + }) + tcpbuf := vv.First()[header.IPv4MinimumSize:] + // Overwrite a byte in the payload which should cause checksum + // verification to fail. + tcpbuf[(tcpbuf[header.TCPDataOffset]>>4)*4] = 0x4 + + c.SendSegment(vv) + + if got := stats.TCP.ChecksumErrors.Value(); got != want { + t.Errorf("got stats.TCP.ChecksumErrors.Value() = %d, want = %d", got, want) + } +} + func TestReceivedSegmentQueuing(t *testing.T) { // This test sends 200 segments containing a few bytes each to an // endpoint and checks that they're all received and acknowledged by diff --git a/pkg/tcpip/transport/udp/endpoint.go b/pkg/tcpip/transport/udp/endpoint.go index 19e532180..1f9251de3 100644 --- a/pkg/tcpip/transport/udp/endpoint.go +++ b/pkg/tcpip/transport/udp/endpoint.go @@ -640,7 +640,7 @@ func sendUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort u }) // Only calculate the checksum if offloading isn't supported. - if r.Capabilities()&stack.CapabilityChecksumOffload == 0 { + if r.Capabilities()&stack.CapabilityTXChecksumOffload == 0 { xsum := r.PseudoHeaderChecksum(ProtocolNumber, length) for _, v := range data.Views() { xsum = header.Checksum(v, xsum) -- cgit v1.2.3