Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [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 | "errors" |
| 13 | "fmt" |
| 14 | "net" |
| 15 | "strings" |
| 16 | |
| 17 | "github.com/google/gopacket" |
| 18 | ) |
| 19 | |
| 20 | type IPv4Flag uint8 |
| 21 | |
| 22 | const ( |
| 23 | IPv4EvilBit IPv4Flag = 1 << 2 // http://tools.ietf.org/html/rfc3514 ;) |
| 24 | IPv4DontFragment IPv4Flag = 1 << 1 |
| 25 | IPv4MoreFragments IPv4Flag = 1 << 0 |
| 26 | ) |
| 27 | |
| 28 | func (f IPv4Flag) String() string { |
| 29 | var s []string |
| 30 | if f&IPv4EvilBit != 0 { |
| 31 | s = append(s, "Evil") |
| 32 | } |
| 33 | if f&IPv4DontFragment != 0 { |
| 34 | s = append(s, "DF") |
| 35 | } |
| 36 | if f&IPv4MoreFragments != 0 { |
| 37 | s = append(s, "MF") |
| 38 | } |
| 39 | return strings.Join(s, "|") |
| 40 | } |
| 41 | |
| 42 | // IPv4 is the header of an IP packet. |
| 43 | type IPv4 struct { |
| 44 | BaseLayer |
| 45 | Version uint8 |
| 46 | IHL uint8 |
| 47 | TOS uint8 |
| 48 | Length uint16 |
| 49 | Id uint16 |
| 50 | Flags IPv4Flag |
| 51 | FragOffset uint16 |
| 52 | TTL uint8 |
| 53 | Protocol IPProtocol |
| 54 | Checksum uint16 |
| 55 | SrcIP net.IP |
| 56 | DstIP net.IP |
| 57 | Options []IPv4Option |
| 58 | Padding []byte |
| 59 | } |
| 60 | |
| 61 | // LayerType returns LayerTypeIPv4 |
| 62 | func (i *IPv4) LayerType() gopacket.LayerType { return LayerTypeIPv4 } |
| 63 | func (i *IPv4) NetworkFlow() gopacket.Flow { |
| 64 | return gopacket.NewFlow(EndpointIPv4, i.SrcIP, i.DstIP) |
| 65 | } |
| 66 | |
| 67 | type IPv4Option struct { |
| 68 | OptionType uint8 |
| 69 | OptionLength uint8 |
| 70 | OptionData []byte |
| 71 | } |
| 72 | |
| 73 | func (i IPv4Option) String() string { |
| 74 | return fmt.Sprintf("IPv4Option(%v:%v)", i.OptionType, i.OptionData) |
| 75 | } |
| 76 | |
| 77 | // for the current ipv4 options, return the number of bytes (including |
| 78 | // padding that the options used) |
| 79 | func (ip *IPv4) getIPv4OptionSize() uint8 { |
| 80 | optionSize := uint8(0) |
| 81 | for _, opt := range ip.Options { |
| 82 | switch opt.OptionType { |
| 83 | case 0: |
| 84 | // this is the end of option lists |
| 85 | optionSize++ |
| 86 | case 1: |
| 87 | // this is the padding |
| 88 | optionSize++ |
| 89 | default: |
| 90 | optionSize += opt.OptionLength |
| 91 | |
| 92 | } |
| 93 | } |
| 94 | // make sure the options are aligned to 32 bit boundary |
| 95 | if (optionSize % 4) != 0 { |
| 96 | optionSize += 4 - (optionSize % 4) |
| 97 | } |
| 98 | return optionSize |
| 99 | } |
| 100 | |
| 101 | // SerializeTo writes the serialized form of this layer into the |
| 102 | // SerializationBuffer, implementing gopacket.SerializableLayer. |
| 103 | func (ip *IPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { |
| 104 | optionLength := ip.getIPv4OptionSize() |
| 105 | bytes, err := b.PrependBytes(20 + int(optionLength)) |
| 106 | if err != nil { |
| 107 | return err |
| 108 | } |
| 109 | if opts.FixLengths { |
| 110 | ip.IHL = 5 + (optionLength / 4) |
| 111 | ip.Length = uint16(len(b.Bytes())) |
| 112 | } |
| 113 | bytes[0] = (ip.Version << 4) | ip.IHL |
| 114 | bytes[1] = ip.TOS |
| 115 | binary.BigEndian.PutUint16(bytes[2:], ip.Length) |
| 116 | binary.BigEndian.PutUint16(bytes[4:], ip.Id) |
| 117 | binary.BigEndian.PutUint16(bytes[6:], ip.flagsfrags()) |
| 118 | bytes[8] = ip.TTL |
| 119 | bytes[9] = byte(ip.Protocol) |
| 120 | if err := ip.AddressTo4(); err != nil { |
| 121 | return err |
| 122 | } |
| 123 | copy(bytes[12:16], ip.SrcIP) |
| 124 | copy(bytes[16:20], ip.DstIP) |
| 125 | |
| 126 | curLocation := 20 |
| 127 | // Now, we will encode the options |
| 128 | for _, opt := range ip.Options { |
| 129 | switch opt.OptionType { |
| 130 | case 0: |
| 131 | // this is the end of option lists |
| 132 | bytes[curLocation] = 0 |
| 133 | curLocation++ |
| 134 | case 1: |
| 135 | // this is the padding |
| 136 | bytes[curLocation] = 1 |
| 137 | curLocation++ |
| 138 | default: |
| 139 | bytes[curLocation] = opt.OptionType |
| 140 | bytes[curLocation+1] = opt.OptionLength |
| 141 | |
| 142 | // sanity checking to protect us from buffer overrun |
| 143 | if len(opt.OptionData) > int(opt.OptionLength-2) { |
| 144 | return errors.New("option length is smaller than length of option data") |
| 145 | } |
| 146 | copy(bytes[curLocation+2:curLocation+int(opt.OptionLength)], opt.OptionData) |
| 147 | curLocation += int(opt.OptionLength) |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | if opts.ComputeChecksums { |
| 152 | ip.Checksum = checksum(bytes) |
| 153 | } |
| 154 | binary.BigEndian.PutUint16(bytes[10:], ip.Checksum) |
| 155 | return nil |
| 156 | } |
| 157 | |
| 158 | func checksum(bytes []byte) uint16 { |
| 159 | // Clear checksum bytes |
| 160 | bytes[10] = 0 |
| 161 | bytes[11] = 0 |
| 162 | |
| 163 | // Compute checksum |
| 164 | var csum uint32 |
| 165 | for i := 0; i < len(bytes); i += 2 { |
| 166 | csum += uint32(bytes[i]) << 8 |
| 167 | csum += uint32(bytes[i+1]) |
| 168 | } |
| 169 | for { |
| 170 | // Break when sum is less or equals to 0xFFFF |
| 171 | if csum <= 65535 { |
| 172 | break |
| 173 | } |
| 174 | // Add carry to the sum |
| 175 | csum = (csum >> 16) + uint32(uint16(csum)) |
| 176 | } |
| 177 | // Flip all the bits |
| 178 | return ^uint16(csum) |
| 179 | } |
| 180 | |
| 181 | func (ip *IPv4) flagsfrags() (ff uint16) { |
| 182 | ff |= uint16(ip.Flags) << 13 |
| 183 | ff |= ip.FragOffset |
| 184 | return |
| 185 | } |
| 186 | |
| 187 | // DecodeFromBytes decodes the given bytes into this layer. |
| 188 | func (ip *IPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { |
| 189 | if len(data) < 20 { |
| 190 | df.SetTruncated() |
| 191 | return fmt.Errorf("Invalid ip4 header. Length %d less than 20", len(data)) |
| 192 | } |
| 193 | flagsfrags := binary.BigEndian.Uint16(data[6:8]) |
| 194 | |
| 195 | ip.Version = uint8(data[0]) >> 4 |
| 196 | ip.IHL = uint8(data[0]) & 0x0F |
| 197 | ip.TOS = data[1] |
| 198 | ip.Length = binary.BigEndian.Uint16(data[2:4]) |
| 199 | ip.Id = binary.BigEndian.Uint16(data[4:6]) |
| 200 | ip.Flags = IPv4Flag(flagsfrags >> 13) |
| 201 | ip.FragOffset = flagsfrags & 0x1FFF |
| 202 | ip.TTL = data[8] |
| 203 | ip.Protocol = IPProtocol(data[9]) |
| 204 | ip.Checksum = binary.BigEndian.Uint16(data[10:12]) |
| 205 | ip.SrcIP = data[12:16] |
| 206 | ip.DstIP = data[16:20] |
| 207 | ip.Options = ip.Options[:0] |
| 208 | ip.Padding = nil |
| 209 | // Set up an initial guess for contents/payload... we'll reset these soon. |
| 210 | ip.BaseLayer = BaseLayer{Contents: data} |
| 211 | |
| 212 | // This code is added for the following enviroment: |
| 213 | // * Windows 10 with TSO option activated. ( tested on Hyper-V, RealTek ethernet driver ) |
| 214 | if ip.Length == 0 { |
| 215 | // If using TSO(TCP Segmentation Offload), length is zero. |
| 216 | // The actual packet length is the length of data. |
| 217 | ip.Length = uint16(len(data)) |
| 218 | } |
| 219 | |
| 220 | if ip.Length < 20 { |
| 221 | return fmt.Errorf("Invalid (too small) IP length (%d < 20)", ip.Length) |
| 222 | } else if ip.IHL < 5 { |
| 223 | return fmt.Errorf("Invalid (too small) IP header length (%d < 5)", ip.IHL) |
| 224 | } else if int(ip.IHL*4) > int(ip.Length) { |
| 225 | return fmt.Errorf("Invalid IP header length > IP length (%d > %d)", ip.IHL, ip.Length) |
| 226 | } |
| 227 | if cmp := len(data) - int(ip.Length); cmp > 0 { |
| 228 | data = data[:ip.Length] |
| 229 | } else if cmp < 0 { |
| 230 | df.SetTruncated() |
| 231 | if int(ip.IHL)*4 > len(data) { |
| 232 | return errors.New("Not all IP header bytes available") |
| 233 | } |
| 234 | } |
| 235 | ip.Contents = data[:ip.IHL*4] |
| 236 | ip.Payload = data[ip.IHL*4:] |
| 237 | // From here on, data contains the header options. |
| 238 | data = data[20 : ip.IHL*4] |
| 239 | // Pull out IP options |
| 240 | for len(data) > 0 { |
| 241 | if ip.Options == nil { |
| 242 | // Pre-allocate to avoid growing the slice too much. |
| 243 | ip.Options = make([]IPv4Option, 0, 4) |
| 244 | } |
| 245 | opt := IPv4Option{OptionType: data[0]} |
| 246 | switch opt.OptionType { |
| 247 | case 0: // End of options |
| 248 | opt.OptionLength = 1 |
| 249 | ip.Options = append(ip.Options, opt) |
| 250 | ip.Padding = data[1:] |
| 251 | return nil |
| 252 | case 1: // 1 byte padding |
| 253 | opt.OptionLength = 1 |
| 254 | data = data[1:] |
| 255 | ip.Options = append(ip.Options, opt) |
| 256 | default: |
| 257 | if len(data) < 2 { |
| 258 | df.SetTruncated() |
| 259 | return fmt.Errorf("Invalid ip4 option length. Length %d less than 2", len(data)) |
| 260 | } |
| 261 | opt.OptionLength = data[1] |
| 262 | if len(data) < int(opt.OptionLength) { |
| 263 | df.SetTruncated() |
| 264 | return fmt.Errorf("IP option length exceeds remaining IP header size, option type %v length %v", opt.OptionType, opt.OptionLength) |
| 265 | } |
| 266 | if opt.OptionLength <= 2 { |
| 267 | return fmt.Errorf("Invalid IP option type %v length %d. Must be greater than 2", opt.OptionType, opt.OptionLength) |
| 268 | } |
| 269 | opt.OptionData = data[2:opt.OptionLength] |
| 270 | data = data[opt.OptionLength:] |
| 271 | ip.Options = append(ip.Options, opt) |
| 272 | } |
| 273 | } |
| 274 | return nil |
| 275 | } |
| 276 | |
| 277 | func (i *IPv4) CanDecode() gopacket.LayerClass { |
| 278 | return LayerTypeIPv4 |
| 279 | } |
| 280 | |
| 281 | func (i *IPv4) NextLayerType() gopacket.LayerType { |
| 282 | if i.Flags&IPv4MoreFragments != 0 || i.FragOffset != 0 { |
| 283 | return gopacket.LayerTypeFragment |
| 284 | } |
| 285 | return i.Protocol.LayerType() |
| 286 | } |
| 287 | |
| 288 | func decodeIPv4(data []byte, p gopacket.PacketBuilder) error { |
| 289 | ip := &IPv4{} |
| 290 | err := ip.DecodeFromBytes(data, p) |
| 291 | p.AddLayer(ip) |
| 292 | p.SetNetworkLayer(ip) |
| 293 | if err != nil { |
| 294 | return err |
| 295 | } |
| 296 | return p.NextDecoder(ip.NextLayerType()) |
| 297 | } |
| 298 | |
| 299 | func checkIPv4Address(addr net.IP) (net.IP, error) { |
| 300 | if c := addr.To4(); c != nil { |
| 301 | return c, nil |
| 302 | } |
| 303 | if len(addr) == net.IPv6len { |
| 304 | return nil, errors.New("address is IPv6") |
| 305 | } |
| 306 | return nil, fmt.Errorf("wrong length of %d bytes instead of %d", len(addr), net.IPv4len) |
| 307 | } |
| 308 | |
| 309 | func (ip *IPv4) AddressTo4() error { |
| 310 | var src, dst net.IP |
| 311 | |
| 312 | if addr, err := checkIPv4Address(ip.SrcIP); err != nil { |
| 313 | return fmt.Errorf("Invalid source IPv4 address (%s)", err) |
| 314 | } else { |
| 315 | src = addr |
| 316 | } |
| 317 | if addr, err := checkIPv4Address(ip.DstIP); err != nil { |
| 318 | return fmt.Errorf("Invalid destination IPv4 address (%s)", err) |
| 319 | } else { |
| 320 | dst = addr |
| 321 | } |
| 322 | ip.SrcIP = src |
| 323 | ip.DstIP = dst |
| 324 | return nil |
| 325 | } |