github.com/gopacket/gopacket@v1.1.0/layers/tcp.go (about)

     1  // Copyright 2012 Google, Inc. All rights reserved.
     2  // Copyright 2009-2011 Andreas Krennmair. All rights reserved.
     3  //
     4  // Use of this source code is governed by a BSD-style license
     5  // that can be found in the LICENSE file in the root of the source
     6  // tree.
     7  
     8  package layers
     9  
    10  import (
    11  	"encoding/binary"
    12  	"encoding/hex"
    13  	"errors"
    14  	"fmt"
    15  
    16  	"github.com/gopacket/gopacket"
    17  )
    18  
    19  // TCP is the layer for TCP headers.
    20  type TCP struct {
    21  	BaseLayer
    22  	SrcPort, DstPort                           TCPPort
    23  	Seq                                        uint32
    24  	Ack                                        uint32
    25  	DataOffset                                 uint8
    26  	FIN, SYN, RST, PSH, ACK, URG, ECE, CWR, NS bool
    27  	Window                                     uint16
    28  	Checksum                                   uint16
    29  	Urgent                                     uint16
    30  	sPort, dPort                               []byte
    31  	Options                                    []TCPOption
    32  	Padding                                    []byte
    33  	opts                                       [4]TCPOption
    34  	tcpipchecksum
    35  }
    36  
    37  // TCPOptionKind represents a TCP option code.
    38  type TCPOptionKind uint8
    39  
    40  const (
    41  	TCPOptionKindEndList                         = 0
    42  	TCPOptionKindNop                             = 1
    43  	TCPOptionKindMSS                             = 2  // len = 4
    44  	TCPOptionKindWindowScale                     = 3  // len = 3
    45  	TCPOptionKindSACKPermitted                   = 4  // len = 2
    46  	TCPOptionKindSACK                            = 5  // len = n
    47  	TCPOptionKindEcho                            = 6  // len = 6, obsolete
    48  	TCPOptionKindEchoReply                       = 7  // len = 6, obsolete
    49  	TCPOptionKindTimestamps                      = 8  // len = 10
    50  	TCPOptionKindPartialOrderConnectionPermitted = 9  // len = 2, obsolete
    51  	TCPOptionKindPartialOrderServiceProfile      = 10 // len = 3, obsolete
    52  	TCPOptionKindCC                              = 11 // obsolete
    53  	TCPOptionKindCCNew                           = 12 // obsolete
    54  	TCPOptionKindCCEcho                          = 13 // obsolete
    55  	TCPOptionKindAltChecksum                     = 14 // len = 3, obsolete
    56  	TCPOptionKindAltChecksumData                 = 15 // len = n, obsolete
    57  )
    58  
    59  func (k TCPOptionKind) String() string {
    60  	switch k {
    61  	case TCPOptionKindEndList:
    62  		return "EndList"
    63  	case TCPOptionKindNop:
    64  		return "NOP"
    65  	case TCPOptionKindMSS:
    66  		return "MSS"
    67  	case TCPOptionKindWindowScale:
    68  		return "WindowScale"
    69  	case TCPOptionKindSACKPermitted:
    70  		return "SACKPermitted"
    71  	case TCPOptionKindSACK:
    72  		return "SACK"
    73  	case TCPOptionKindEcho:
    74  		return "Echo"
    75  	case TCPOptionKindEchoReply:
    76  		return "EchoReply"
    77  	case TCPOptionKindTimestamps:
    78  		return "Timestamps"
    79  	case TCPOptionKindPartialOrderConnectionPermitted:
    80  		return "PartialOrderConnectionPermitted"
    81  	case TCPOptionKindPartialOrderServiceProfile:
    82  		return "PartialOrderServiceProfile"
    83  	case TCPOptionKindCC:
    84  		return "CC"
    85  	case TCPOptionKindCCNew:
    86  		return "CCNew"
    87  	case TCPOptionKindCCEcho:
    88  		return "CCEcho"
    89  	case TCPOptionKindAltChecksum:
    90  		return "AltChecksum"
    91  	case TCPOptionKindAltChecksumData:
    92  		return "AltChecksumData"
    93  	default:
    94  		return fmt.Sprintf("Unknown(%d)", k)
    95  	}
    96  }
    97  
    98  type TCPOption struct {
    99  	OptionType   TCPOptionKind
   100  	OptionLength uint8
   101  	OptionData   []byte
   102  }
   103  
   104  func (t TCPOption) String() string {
   105  	hd := hex.EncodeToString(t.OptionData)
   106  	if len(hd) > 0 {
   107  		hd = " 0x" + hd
   108  	}
   109  	switch t.OptionType {
   110  	case TCPOptionKindMSS:
   111  		if len(t.OptionData) >= 2 {
   112  			return fmt.Sprintf("TCPOption(%s:%v%s)",
   113  				t.OptionType,
   114  				binary.BigEndian.Uint16(t.OptionData),
   115  				hd)
   116  		}
   117  
   118  	case TCPOptionKindTimestamps:
   119  		if len(t.OptionData) == 8 {
   120  			return fmt.Sprintf("TCPOption(%s:%v/%v%s)",
   121  				t.OptionType,
   122  				binary.BigEndian.Uint32(t.OptionData[:4]),
   123  				binary.BigEndian.Uint32(t.OptionData[4:8]),
   124  				hd)
   125  		}
   126  	}
   127  	return fmt.Sprintf("TCPOption(%s:%s)", t.OptionType, hd)
   128  }
   129  
   130  // LayerType returns gopacket.LayerTypeTCP
   131  func (t *TCP) LayerType() gopacket.LayerType { return LayerTypeTCP }
   132  
   133  // SerializeTo writes the serialized form of this layer into the
   134  // SerializationBuffer, implementing gopacket.SerializableLayer.
   135  // See the docs for gopacket.SerializableLayer for more info.
   136  func (t *TCP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
   137  	var optionLength int
   138  	for _, o := range t.Options {
   139  		switch o.OptionType {
   140  		case 0, 1:
   141  			optionLength += 1
   142  		default:
   143  			optionLength += 2 + len(o.OptionData)
   144  		}
   145  	}
   146  	if opts.FixLengths {
   147  		if rem := optionLength % 4; rem != 0 {
   148  			t.Padding = lotsOfZeros[:4-rem]
   149  		}
   150  		t.DataOffset = uint8((len(t.Padding) + optionLength + 20) / 4)
   151  	}
   152  	bytes, err := b.PrependBytes(20 + optionLength + len(t.Padding))
   153  	if err != nil {
   154  		return err
   155  	}
   156  	binary.BigEndian.PutUint16(bytes, uint16(t.SrcPort))
   157  	binary.BigEndian.PutUint16(bytes[2:], uint16(t.DstPort))
   158  	binary.BigEndian.PutUint32(bytes[4:], t.Seq)
   159  	binary.BigEndian.PutUint32(bytes[8:], t.Ack)
   160  	binary.BigEndian.PutUint16(bytes[12:], t.flagsAndOffset())
   161  	binary.BigEndian.PutUint16(bytes[14:], t.Window)
   162  	binary.BigEndian.PutUint16(bytes[18:], t.Urgent)
   163  	start := 20
   164  	for _, o := range t.Options {
   165  		bytes[start] = byte(o.OptionType)
   166  		switch o.OptionType {
   167  		case 0, 1:
   168  			start++
   169  		default:
   170  			if opts.FixLengths {
   171  				o.OptionLength = uint8(len(o.OptionData) + 2)
   172  			}
   173  			bytes[start+1] = o.OptionLength
   174  			copy(bytes[start+2:start+len(o.OptionData)+2], o.OptionData)
   175  			start += len(o.OptionData) + 2
   176  		}
   177  	}
   178  	copy(bytes[start:], t.Padding)
   179  	if opts.ComputeChecksums {
   180  		// zero out checksum bytes in current serialization.
   181  		bytes[16] = 0
   182  		bytes[17] = 0
   183  		csum, err := t.computeChecksum(b.Bytes(), IPProtocolTCP)
   184  		if err != nil {
   185  			return err
   186  		}
   187  		t.Checksum = csum
   188  	}
   189  	binary.BigEndian.PutUint16(bytes[16:], t.Checksum)
   190  	return nil
   191  }
   192  
   193  func (t *TCP) ComputeChecksum() (uint16, error) {
   194  	return t.computeChecksum(append(t.Contents, t.Payload...), IPProtocolTCP)
   195  }
   196  
   197  func (t *TCP) flagsAndOffset() uint16 {
   198  	f := uint16(t.DataOffset) << 12
   199  	if t.FIN {
   200  		f |= 0x0001
   201  	}
   202  	if t.SYN {
   203  		f |= 0x0002
   204  	}
   205  	if t.RST {
   206  		f |= 0x0004
   207  	}
   208  	if t.PSH {
   209  		f |= 0x0008
   210  	}
   211  	if t.ACK {
   212  		f |= 0x0010
   213  	}
   214  	if t.URG {
   215  		f |= 0x0020
   216  	}
   217  	if t.ECE {
   218  		f |= 0x0040
   219  	}
   220  	if t.CWR {
   221  		f |= 0x0080
   222  	}
   223  	if t.NS {
   224  		f |= 0x0100
   225  	}
   226  	return f
   227  }
   228  
   229  func (tcp *TCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
   230  	if len(data) < 20 {
   231  		df.SetTruncated()
   232  		return fmt.Errorf("Invalid TCP header. Length %d less than 20", len(data))
   233  	}
   234  	tcp.SrcPort = TCPPort(binary.BigEndian.Uint16(data[0:2]))
   235  	tcp.sPort = data[0:2]
   236  	tcp.DstPort = TCPPort(binary.BigEndian.Uint16(data[2:4]))
   237  	tcp.dPort = data[2:4]
   238  	tcp.Seq = binary.BigEndian.Uint32(data[4:8])
   239  	tcp.Ack = binary.BigEndian.Uint32(data[8:12])
   240  	tcp.DataOffset = data[12] >> 4
   241  	tcp.FIN = data[13]&0x01 != 0
   242  	tcp.SYN = data[13]&0x02 != 0
   243  	tcp.RST = data[13]&0x04 != 0
   244  	tcp.PSH = data[13]&0x08 != 0
   245  	tcp.ACK = data[13]&0x10 != 0
   246  	tcp.URG = data[13]&0x20 != 0
   247  	tcp.ECE = data[13]&0x40 != 0
   248  	tcp.CWR = data[13]&0x80 != 0
   249  	tcp.NS = data[12]&0x01 != 0
   250  	tcp.Window = binary.BigEndian.Uint16(data[14:16])
   251  	tcp.Checksum = binary.BigEndian.Uint16(data[16:18])
   252  	tcp.Urgent = binary.BigEndian.Uint16(data[18:20])
   253  	if tcp.Options == nil {
   254  		// Pre-allocate to avoid allocating a slice.
   255  		tcp.Options = tcp.opts[:0]
   256  	} else {
   257  		tcp.Options = tcp.Options[:0]
   258  	}
   259  	tcp.Padding = tcp.Padding[:0]
   260  	if tcp.DataOffset < 5 {
   261  		return fmt.Errorf("Invalid TCP data offset %d < 5", tcp.DataOffset)
   262  	}
   263  	dataStart := int(tcp.DataOffset) * 4
   264  	if dataStart > len(data) {
   265  		df.SetTruncated()
   266  		tcp.Payload = nil
   267  		tcp.Contents = data
   268  		return errors.New("TCP data offset greater than packet length")
   269  	}
   270  	tcp.Contents = data[:dataStart]
   271  	tcp.Payload = data[dataStart:]
   272  	// From here on, data points just to the header options.
   273  	data = data[20:dataStart]
   274  OPTIONS:
   275  	for len(data) > 0 {
   276  		tcp.Options = append(tcp.Options, TCPOption{OptionType: TCPOptionKind(data[0])})
   277  		opt := &tcp.Options[len(tcp.Options)-1]
   278  		switch opt.OptionType {
   279  		case TCPOptionKindEndList: // End of options
   280  			opt.OptionLength = 1
   281  			tcp.Padding = data[1:]
   282  			break OPTIONS
   283  		case TCPOptionKindNop: // 1 byte padding
   284  			opt.OptionLength = 1
   285  		default:
   286  			if len(data) < 2 {
   287  				df.SetTruncated()
   288  				return fmt.Errorf("Invalid TCP option length. Length %d less than 2", len(data))
   289  			}
   290  			opt.OptionLength = data[1]
   291  			if opt.OptionLength < 2 {
   292  				return fmt.Errorf("Invalid TCP option length %d < 2", opt.OptionLength)
   293  			} else if int(opt.OptionLength) > len(data) {
   294  				df.SetTruncated()
   295  				return fmt.Errorf("Invalid TCP option length %d exceeds remaining %d bytes", opt.OptionLength, len(data))
   296  			}
   297  			opt.OptionData = data[2:opt.OptionLength]
   298  		}
   299  		data = data[opt.OptionLength:]
   300  	}
   301  	return nil
   302  }
   303  
   304  func (t *TCP) CanDecode() gopacket.LayerClass {
   305  	return LayerTypeTCP
   306  }
   307  
   308  func (t *TCP) NextLayerType() gopacket.LayerType {
   309  	lt := t.DstPort.LayerType()
   310  	if lt == gopacket.LayerTypePayload {
   311  		lt = t.SrcPort.LayerType()
   312  	}
   313  	return lt
   314  }
   315  
   316  func decodeTCP(data []byte, p gopacket.PacketBuilder) error {
   317  	tcp := &TCP{}
   318  	err := tcp.DecodeFromBytes(data, p)
   319  	p.AddLayer(tcp)
   320  	p.SetTransportLayer(tcp)
   321  	if err != nil {
   322  		return err
   323  	}
   324  	if p.DecodeOptions().DecodeStreamsAsDatagrams {
   325  		return p.NextDecoder(tcp.NextLayerType())
   326  	} else {
   327  		return p.NextDecoder(gopacket.LayerTypePayload)
   328  	}
   329  }
   330  
   331  func (t *TCP) TransportFlow() gopacket.Flow {
   332  	return gopacket.NewFlow(EndpointTCPPort, t.sPort, t.dPort)
   333  }
   334  
   335  // For testing only
   336  func (t *TCP) SetInternalPortsForTesting() {
   337  	t.sPort = make([]byte, 2)
   338  	t.dPort = make([]byte, 2)
   339  	binary.BigEndian.PutUint16(t.sPort, uint16(t.SrcPort))
   340  	binary.BigEndian.PutUint16(t.dPort, uint16(t.DstPort))
   341  }