blob: 6b37f56d0cd215a99332ceca6ff0e42ffa40713d [file] [log] [blame]
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -07001// 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
8package layers
9
10import (
11 "encoding/binary"
12 "encoding/hex"
13 "errors"
14 "fmt"
15
16 "github.com/google/gopacket"
17)
18
19// TCP is the layer for TCP headers.
20type 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.
38type TCPOptionKind uint8
39
40const (
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
59func (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
98type TCPOption struct {
99 OptionType TCPOptionKind
100 OptionLength uint8
101 OptionData []byte
102}
103
104func (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 return fmt.Sprintf("TCPOption(%s:%v%s)",
112 t.OptionType,
113 binary.BigEndian.Uint16(t.OptionData),
114 hd)
115
116 case TCPOptionKindTimestamps:
117 if len(t.OptionData) == 8 {
118 return fmt.Sprintf("TCPOption(%s:%v/%v%s)",
119 t.OptionType,
120 binary.BigEndian.Uint32(t.OptionData[:4]),
121 binary.BigEndian.Uint32(t.OptionData[4:8]),
122 hd)
123 }
124 }
125 return fmt.Sprintf("TCPOption(%s:%s)", t.OptionType, hd)
126}
127
128// LayerType returns gopacket.LayerTypeTCP
129func (t *TCP) LayerType() gopacket.LayerType { return LayerTypeTCP }
130
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.
134func (t *TCP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
135 var optionLength int
136 for _, o := range t.Options {
137 switch o.OptionType {
138 case 0, 1:
139 optionLength += 1
140 default:
141 optionLength += 2 + len(o.OptionData)
142 }
143 }
144 if opts.FixLengths {
145 if rem := optionLength % 4; rem != 0 {
146 t.Padding = lotsOfZeros[:4-rem]
147 }
148 t.DataOffset = uint8((len(t.Padding) + optionLength + 20) / 4)
149 }
150 bytes, err := b.PrependBytes(20 + optionLength + len(t.Padding))
151 if err != nil {
152 return err
153 }
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)
161 start := 20
162 for _, o := range t.Options {
163 bytes[start] = byte(o.OptionType)
164 switch o.OptionType {
165 case 0, 1:
166 start++
167 default:
168 if opts.FixLengths {
169 o.OptionLength = uint8(len(o.OptionData) + 2)
170 }
171 bytes[start+1] = o.OptionLength
172 copy(bytes[start+2:start+len(o.OptionData)+2], o.OptionData)
173 start += len(o.OptionData) + 2
174 }
175 }
176 copy(bytes[start:], t.Padding)
177 if opts.ComputeChecksums {
178 // zero out checksum bytes in current serialization.
179 bytes[16] = 0
180 bytes[17] = 0
181 csum, err := t.computeChecksum(b.Bytes(), IPProtocolTCP)
182 if err != nil {
183 return err
184 }
185 t.Checksum = csum
186 }
187 binary.BigEndian.PutUint16(bytes[16:], t.Checksum)
188 return nil
189}
190
191func (t *TCP) ComputeChecksum() (uint16, error) {
192 return t.computeChecksum(append(t.Contents, t.Payload...), IPProtocolTCP)
193}
194
195func (t *TCP) flagsAndOffset() uint16 {
196 f := uint16(t.DataOffset) << 12
197 if t.FIN {
198 f |= 0x0001
199 }
200 if t.SYN {
201 f |= 0x0002
202 }
203 if t.RST {
204 f |= 0x0004
205 }
206 if t.PSH {
207 f |= 0x0008
208 }
209 if t.ACK {
210 f |= 0x0010
211 }
212 if t.URG {
213 f |= 0x0020
214 }
215 if t.ECE {
216 f |= 0x0040
217 }
218 if t.CWR {
219 f |= 0x0080
220 }
221 if t.NS {
222 f |= 0x0100
223 }
224 return f
225}
226
227func (tcp *TCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
228 if len(data) < 20 {
229 df.SetTruncated()
230 return fmt.Errorf("Invalid TCP header. Length %d less than 20", len(data))
231 }
232 tcp.SrcPort = TCPPort(binary.BigEndian.Uint16(data[0:2]))
233 tcp.sPort = data[0:2]
234 tcp.DstPort = TCPPort(binary.BigEndian.Uint16(data[2:4]))
235 tcp.dPort = data[2:4]
236 tcp.Seq = binary.BigEndian.Uint32(data[4:8])
237 tcp.Ack = binary.BigEndian.Uint32(data[8:12])
238 tcp.DataOffset = data[12] >> 4
239 tcp.FIN = data[13]&0x01 != 0
240 tcp.SYN = data[13]&0x02 != 0
241 tcp.RST = data[13]&0x04 != 0
242 tcp.PSH = data[13]&0x08 != 0
243 tcp.ACK = data[13]&0x10 != 0
244 tcp.URG = data[13]&0x20 != 0
245 tcp.ECE = data[13]&0x40 != 0
246 tcp.CWR = data[13]&0x80 != 0
247 tcp.NS = data[12]&0x01 != 0
248 tcp.Window = binary.BigEndian.Uint16(data[14:16])
249 tcp.Checksum = binary.BigEndian.Uint16(data[16:18])
250 tcp.Urgent = binary.BigEndian.Uint16(data[18:20])
251 if tcp.Options == nil {
252 // Pre-allocate to avoid allocating a slice.
253 tcp.Options = tcp.opts[:0]
254 } else {
255 tcp.Options = tcp.Options[:0]
256 }
257 if tcp.DataOffset < 5 {
258 return fmt.Errorf("Invalid TCP data offset %d < 5", tcp.DataOffset)
259 }
260 dataStart := int(tcp.DataOffset) * 4
261 if dataStart > len(data) {
262 df.SetTruncated()
263 tcp.Payload = nil
264 tcp.Contents = data
265 return errors.New("TCP data offset greater than packet length")
266 }
267 tcp.Contents = data[:dataStart]
268 tcp.Payload = data[dataStart:]
269 // From here on, data points just to the header options.
270 data = data[20:dataStart]
271 for len(data) > 0 {
272 tcp.Options = append(tcp.Options, TCPOption{OptionType: TCPOptionKind(data[0])})
273 opt := &tcp.Options[len(tcp.Options)-1]
274 switch opt.OptionType {
275 case TCPOptionKindEndList: // End of options
276 opt.OptionLength = 1
277 tcp.Padding = data[1:]
278 break
279 case TCPOptionKindNop: // 1 byte padding
280 opt.OptionLength = 1
281 default:
282 if len(data) < 2 {
283 df.SetTruncated()
284 return fmt.Errorf("Invalid TCP option length. Length %d less than 2", len(data))
285 }
286 opt.OptionLength = data[1]
287 if opt.OptionLength < 2 {
288 return fmt.Errorf("Invalid TCP option length %d < 2", opt.OptionLength)
289 } else if int(opt.OptionLength) > len(data) {
290 df.SetTruncated()
291 return fmt.Errorf("Invalid TCP option length %d exceeds remaining %d bytes", opt.OptionLength, len(data))
292 }
293 opt.OptionData = data[2:opt.OptionLength]
294 }
295 data = data[opt.OptionLength:]
296 }
297 return nil
298}
299
300func (t *TCP) CanDecode() gopacket.LayerClass {
301 return LayerTypeTCP
302}
303
304func (t *TCP) NextLayerType() gopacket.LayerType {
305 lt := t.DstPort.LayerType()
306 if lt == gopacket.LayerTypePayload {
307 lt = t.SrcPort.LayerType()
308 }
309 return lt
310}
311
312func decodeTCP(data []byte, p gopacket.PacketBuilder) error {
313 tcp := &TCP{}
314 err := tcp.DecodeFromBytes(data, p)
315 p.AddLayer(tcp)
316 p.SetTransportLayer(tcp)
317 if err != nil {
318 return err
319 }
320 if p.DecodeOptions().DecodeStreamsAsDatagrams {
321 return p.NextDecoder(tcp.NextLayerType())
322 } else {
323 return p.NextDecoder(gopacket.LayerTypePayload)
324 }
325}
326
327func (t *TCP) TransportFlow() gopacket.Flow {
328 return gopacket.NewFlow(EndpointTCPPort, t.sPort, t.dPort)
329}
330
331// For testing only
332func (t *TCP) SetInternalPortsForTesting() {
333 t.sPort = make([]byte, 2)
334 t.dPort = make([]byte, 2)
335 binary.BigEndian.PutUint16(t.sPort, uint16(t.SrcPort))
336 binary.BigEndian.PutUint16(t.dPort, uint16(t.DstPort))
337}