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 }