blob: 91757365522c605f0b40745735fbb0a05478010b [file] [log] [blame]
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tcp
import (
"sync/atomic"
"time"
"github.com/google/netstack/tcpip/buffer"
"github.com/google/netstack/tcpip/header"
"github.com/google/netstack/tcpip/seqnum"
"github.com/google/netstack/tcpip/stack"
)
// segment represents a TCP segment. It holds the payload and parsed TCP segment
// information, and can be added to intrusive lists.
// segment is mostly immutable, the only field allowed to change is viewToDeliver.
//
// +stateify savable
type segment struct {
segmentEntry
refCnt int32
id stack.TransportEndpointID
route stack.Route
data buffer.VectorisedView
// views is used as buffer for data when its length is large
// enough to store a VectorisedView.
views [8]buffer.View
// viewToDeliver keeps track of the next View that should be
// delivered by the Read endpoint.
viewToDeliver int
sequenceNumber seqnum.Value
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
options []byte
hasNewSACKInfo bool
rcvdTime time.Time
// xmitTime is the last transmit time of this segment. A zero value
// indicates that the segment has yet to be transmitted.
xmitTime time.Time
}
func newSegment(r *stack.Route, id stack.TransportEndpointID, vv buffer.VectorisedView) *segment {
s := &segment{
refCnt: 1,
id: id,
route: r.Clone(),
}
s.data = vv.Clone(s.views[:])
s.rcvdTime = time.Now()
return s
}
func newSegmentFromView(r *stack.Route, id stack.TransportEndpointID, v buffer.View) *segment {
s := &segment{
refCnt: 1,
id: id,
route: r.Clone(),
}
s.views[0] = v
s.data = buffer.NewVectorisedView(len(v), s.views[:1])
s.rcvdTime = time.Now()
return s
}
func (s *segment) clone() *segment {
t := &segment{
refCnt: 1,
id: s.id,
sequenceNumber: s.sequenceNumber,
ackNumber: s.ackNumber,
flags: s.flags,
window: s.window,
route: s.route.Clone(),
viewToDeliver: s.viewToDeliver,
rcvdTime: s.rcvdTime,
}
t.data = s.data.Clone(t.views[:])
return t
}
func (s *segment) flagIsSet(flag uint8) bool {
return (s.flags & flag) != 0
}
func (s *segment) decRef() {
if atomic.AddInt32(&s.refCnt, -1) == 0 {
s.route.Release()
}
}
func (s *segment) incRef() {
atomic.AddInt32(&s.refCnt, 1)
}
// logicalLen is the segment length in the sequence number space. It's defined
// as the data length plus one for each of the SYN and FIN bits set.
func (s *segment) logicalLen() seqnum.Size {
l := seqnum.Size(s.data.Size())
if s.flagIsSet(header.TCPFlagSyn) {
l++
}
if s.flagIsSet(header.TCPFlagFin) {
l++
}
return l
}
// 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 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())
// h is the header followed by the payload. We check that the offset to
// the data respects the following constraints:
// 1. That it's at least the minimum header size; if we don't do this
// then part of the header would be delivered to user.
// 2. That the header fits within the buffer; if we don't do this, we
// would panic when we tried to access data beyond the buffer.
//
// N.B. The segment has already been validated as having at least the
// minimum TCP size before reaching here, so it's safe to read the
// fields.
offset := int(h.DataOffset())
if offset < header.TCPMinimumSize || offset > len(h) {
return false
}
s.options = []byte(h[header.TCPMinimumSize:offset])
s.parsedOptions = header.ParseTCPOptions(s.options)
// 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
}
// sackBlock returns a header.SACKBlock that represents this segment.
func (s *segment) sackBlock() header.SACKBlock {
return header.SACKBlock{s.sequenceNumber, s.sequenceNumber.Add(s.logicalLen())}
}