Matteo Scandolo | a6a3aee | 2019-11-26 13:30:14 -0700 | [diff] [blame] | 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/google/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 | 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 |
| 129 | func (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. |
| 134 | func (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 | |
| 191 | func (t *TCP) ComputeChecksum() (uint16, error) { |
| 192 | return t.computeChecksum(append(t.Contents, t.Payload...), IPProtocolTCP) |
| 193 | } |
| 194 | |
| 195 | func (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 | |
| 227 | func (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 | |
| 300 | func (t *TCP) CanDecode() gopacket.LayerClass { |
| 301 | return LayerTypeTCP |
| 302 | } |
| 303 | |
| 304 | func (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 | |
| 312 | func 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 | |
| 327 | func (t *TCP) TransportFlow() gopacket.Flow { |
| 328 | return gopacket.NewFlow(EndpointTCPPort, t.sPort, t.dPort) |
| 329 | } |
| 330 | |
| 331 | // For testing only |
| 332 | func (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 | } |