blob: bcdeb4b3c35894bf79d073acb869287856d48b68 [file] [log] [blame]
Naveen Sampath04696f72022-06-13 15:19:14 +05301// 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 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
131func (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.
136func (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
193func (t *TCP) ComputeChecksum() (uint16, error) {
194 return t.computeChecksum(append(t.Contents, t.Payload...), IPProtocolTCP)
195}
196
197func (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
229func (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]
274OPTIONS:
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
304func (t *TCP) CanDecode() gopacket.LayerClass {
305 return LayerTypeTCP
306}
307
308func (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
316func 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
331func (t *TCP) TransportFlow() gopacket.Flow {
332 return gopacket.NewFlow(EndpointTCPPort, t.sPort, t.dPort)
333}
334
335// For testing only
336func (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}