1 // Copyright 2012 Google, Inc. All rights reserved.
2 // Copyright 2009-2011 Andreas Krennmair. All rights reserved.
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
16 "github.com/google/gopacket"
19 // TCP is the layer for TCP headers.
22 SrcPort, DstPort TCPPort
26 FIN, SYN, RST, PSH, ACK, URG, ECE, CWR, NS bool
37 // TCPOptionKind represents a TCP option code.
38 type TCPOptionKind uint8
41 TCPOptionKindEndList = 0
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
59 func (k TCPOptionKind) String() string {
61 case TCPOptionKindEndList:
63 case TCPOptionKindNop:
65 case TCPOptionKindMSS:
67 case TCPOptionKindWindowScale:
69 case TCPOptionKindSACKPermitted:
70 return "SACKPermitted"
71 case TCPOptionKindSACK:
73 case TCPOptionKindEcho:
75 case TCPOptionKindEchoReply:
77 case TCPOptionKindTimestamps:
79 case TCPOptionKindPartialOrderConnectionPermitted:
80 return "PartialOrderConnectionPermitted"
81 case TCPOptionKindPartialOrderServiceProfile:
82 return "PartialOrderServiceProfile"
85 case TCPOptionKindCCNew:
87 case TCPOptionKindCCEcho:
89 case TCPOptionKindAltChecksum:
91 case TCPOptionKindAltChecksumData:
92 return "AltChecksumData"
94 return fmt.Sprintf("Unknown(%d)", k)
98 type TCPOption struct {
99 OptionType TCPOptionKind
104 func (t TCPOption) String() string {
105 hd := hex.EncodeToString(t.OptionData)
109 switch t.OptionType {
110 case TCPOptionKindMSS:
111 return fmt.Sprintf("TCPOption(%s:%v%s)",
113 binary.BigEndian.Uint16(t.OptionData),
116 case TCPOptionKindTimestamps:
117 if len(t.OptionData) == 8 {
118 return fmt.Sprintf("TCPOption(%s:%v/%v%s)",
120 binary.BigEndian.Uint32(t.OptionData[:4]),
121 binary.BigEndian.Uint32(t.OptionData[4:8]),
125 return fmt.Sprintf("TCPOption(%s:%s)", t.OptionType, hd)
128 // LayerType returns gopacket.LayerTypeTCP
129 func (t *TCP) LayerType() gopacket.LayerType { return LayerTypeTCP }
131 // SerializeTo writes the serialized form of this layer into the
132 // SerializationBuffer, implementing gopacket.SerializableLayer.
133 // See the docs for gopacket.SerializableLayer for more info.
134 func (t *TCP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
136 for _, o := range t.Options {
137 switch o.OptionType {
141 optionLength += 2 + len(o.OptionData)
145 if rem := optionLength % 4; rem != 0 {
146 t.Padding = lotsOfZeros[:4-rem]
148 t.DataOffset = uint8((len(t.Padding) + optionLength + 20) / 4)
150 bytes, err := b.PrependBytes(20 + optionLength + len(t.Padding))
154 binary.BigEndian.PutUint16(bytes, uint16(t.SrcPort))
155 binary.BigEndian.PutUint16(bytes[2:], uint16(t.DstPort))
156 binary.BigEndian.PutUint32(bytes[4:], t.Seq)
157 binary.BigEndian.PutUint32(bytes[8:], t.Ack)
158 binary.BigEndian.PutUint16(bytes[12:], t.flagsAndOffset())
159 binary.BigEndian.PutUint16(bytes[14:], t.Window)
160 binary.BigEndian.PutUint16(bytes[18:], t.Urgent)
162 for _, o := range t.Options {
163 bytes[start] = byte(o.OptionType)
164 switch o.OptionType {
169 o.OptionLength = uint8(len(o.OptionData) + 2)
171 bytes[start+1] = o.OptionLength
172 copy(bytes[start+2:start+len(o.OptionData)+2], o.OptionData)
173 start += int(o.OptionLength)
176 copy(bytes[start:], t.Padding)
177 if opts.ComputeChecksums {
178 // zero out checksum bytes in current serialization.
181 csum, err := t.computeChecksum(b.Bytes(), IPProtocolTCP)
187 binary.BigEndian.PutUint16(bytes[16:], t.Checksum)
191 func (t *TCP) ComputeChecksum() (uint16, error) {
192 return t.computeChecksum(append(t.Contents, t.Payload...), IPProtocolTCP)
195 func (t *TCP) flagsAndOffset() uint16 {
196 f := uint16(t.DataOffset) << 12
227 func (tcp *TCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
228 tcp.SrcPort = TCPPort(binary.BigEndian.Uint16(data[0:2]))
229 tcp.sPort = data[0:2]
230 tcp.DstPort = TCPPort(binary.BigEndian.Uint16(data[2:4]))
231 tcp.dPort = data[2:4]
232 tcp.Seq = binary.BigEndian.Uint32(data[4:8])
233 tcp.Ack = binary.BigEndian.Uint32(data[8:12])
234 tcp.DataOffset = data[12] >> 4
235 tcp.FIN = data[13]&0x01 != 0
236 tcp.SYN = data[13]&0x02 != 0
237 tcp.RST = data[13]&0x04 != 0
238 tcp.PSH = data[13]&0x08 != 0
239 tcp.ACK = data[13]&0x10 != 0
240 tcp.URG = data[13]&0x20 != 0
241 tcp.ECE = data[13]&0x40 != 0
242 tcp.CWR = data[13]&0x80 != 0
243 tcp.NS = data[12]&0x01 != 0
244 tcp.Window = binary.BigEndian.Uint16(data[14:16])
245 tcp.Checksum = binary.BigEndian.Uint16(data[16:18])
246 tcp.Urgent = binary.BigEndian.Uint16(data[18:20])
247 tcp.Options = tcp.opts[:0]
248 if tcp.DataOffset < 5 {
249 return fmt.Errorf("Invalid TCP data offset %d < 5", tcp.DataOffset)
251 dataStart := int(tcp.DataOffset) * 4
252 if dataStart > len(data) {
256 return errors.New("TCP data offset greater than packet length")
258 tcp.Contents = data[:dataStart]
259 tcp.Payload = data[dataStart:]
260 // From here on, data points just to the header options.
261 data = data[20:dataStart]
263 if tcp.Options == nil {
264 // Pre-allocate to avoid allocating a slice.
265 tcp.Options = tcp.opts[:0]
267 tcp.Options = append(tcp.Options, TCPOption{OptionType: TCPOptionKind(data[0])})
268 opt := &tcp.Options[len(tcp.Options)-1]
269 switch opt.OptionType {
270 case TCPOptionKindEndList: // End of options
272 tcp.Padding = data[1:]
274 case TCPOptionKindNop: // 1 byte padding
277 opt.OptionLength = data[1]
278 if opt.OptionLength < 2 {
279 return fmt.Errorf("Invalid TCP option length %d < 2", opt.OptionLength)
280 } else if int(opt.OptionLength) > len(data) {
281 return fmt.Errorf("Invalid TCP option length %d exceeds remaining %d bytes", opt.OptionLength, len(data))
283 opt.OptionData = data[2:opt.OptionLength]
285 data = data[opt.OptionLength:]
290 func (t *TCP) CanDecode() gopacket.LayerClass {
294 func (t *TCP) NextLayerType() gopacket.LayerType {
295 lt := t.DstPort.LayerType()
296 if lt == gopacket.LayerTypePayload {
297 lt = t.SrcPort.LayerType()
302 func decodeTCP(data []byte, p gopacket.PacketBuilder) error {
304 err := tcp.DecodeFromBytes(data, p)
306 p.SetTransportLayer(tcp)
310 if p.DecodeOptions().DecodeStreamsAsDatagrams {
311 return p.NextDecoder(tcp.NextLayerType())
313 return p.NextDecoder(gopacket.LayerTypePayload)
317 func (t *TCP) TransportFlow() gopacket.Flow {
318 return gopacket.NewFlow(EndpointTCPPort, t.sPort, t.dPort)
322 func (t *TCP) SetInternalPortsForTesting() {
323 t.sPort = make([]byte, 2)
324 t.dPort = make([]byte, 2)
325 binary.BigEndian.PutUint16(t.sPort, uint16(t.SrcPort))
326 binary.BigEndian.PutUint16(t.dPort, uint16(t.DstPort))