123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- // Copyright 2012 Google, Inc. All rights reserved.
- // Copyright 2009-2011 Andreas Krennmair. All rights reserved.
- //
- // Use of this source code is governed by a BSD-style license
- // that can be found in the LICENSE file in the root of the source
- // tree.
- package layers
- import (
- "encoding/binary"
- "errors"
- "fmt"
- "net"
- "strings"
- "github.com/google/gopacket"
- )
- type IPv4Flag uint8
- const (
- IPv4EvilBit IPv4Flag = 1 << 2 // http://tools.ietf.org/html/rfc3514 ;)
- IPv4DontFragment IPv4Flag = 1 << 1
- IPv4MoreFragments IPv4Flag = 1 << 0
- )
- func (f IPv4Flag) String() string {
- var s []string
- if f&IPv4EvilBit != 0 {
- s = append(s, "Evil")
- }
- if f&IPv4DontFragment != 0 {
- s = append(s, "DF")
- }
- if f&IPv4MoreFragments != 0 {
- s = append(s, "MF")
- }
- return strings.Join(s, "|")
- }
- // IPv4 is the header of an IP packet.
- type IPv4 struct {
- BaseLayer
- Version uint8
- IHL uint8
- TOS uint8
- Length uint16
- Id uint16
- Flags IPv4Flag
- FragOffset uint16
- TTL uint8
- Protocol IPProtocol
- Checksum uint16
- SrcIP net.IP
- DstIP net.IP
- Options []IPv4Option
- Padding []byte
- }
- // LayerType returns LayerTypeIPv4
- func (i *IPv4) LayerType() gopacket.LayerType { return LayerTypeIPv4 }
- func (i *IPv4) NetworkFlow() gopacket.Flow {
- return gopacket.NewFlow(EndpointIPv4, i.SrcIP, i.DstIP)
- }
- type IPv4Option struct {
- OptionType uint8
- OptionLength uint8
- OptionData []byte
- }
- func (i IPv4Option) String() string {
- return fmt.Sprintf("IPv4Option(%v:%v)", i.OptionType, i.OptionData)
- }
- // for the current ipv4 options, return the number of bytes (including
- // padding that the options used)
- func (ip *IPv4) getIPv4OptionSize() uint8 {
- optionSize := uint8(0)
- for _, opt := range ip.Options {
- switch opt.OptionType {
- case 0:
- // this is the end of option lists
- optionSize++
- case 1:
- // this is the padding
- optionSize++
- default:
- optionSize += opt.OptionLength
- }
- }
- // make sure the options are aligned to 32 bit boundary
- if (optionSize % 4) != 0 {
- optionSize += 4 - (optionSize % 4)
- }
- return optionSize
- }
- // SerializeTo writes the serialized form of this layer into the
- // SerializationBuffer, implementing gopacket.SerializableLayer.
- func (ip *IPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
- optionLength := ip.getIPv4OptionSize()
- bytes, err := b.PrependBytes(20 + int(optionLength))
- if err != nil {
- return err
- }
- if opts.FixLengths {
- ip.IHL = 5 + (optionLength / 4)
- ip.Length = uint16(len(b.Bytes()))
- }
- bytes[0] = (ip.Version << 4) | ip.IHL
- bytes[1] = ip.TOS
- binary.BigEndian.PutUint16(bytes[2:], ip.Length)
- binary.BigEndian.PutUint16(bytes[4:], ip.Id)
- binary.BigEndian.PutUint16(bytes[6:], ip.flagsfrags())
- bytes[8] = ip.TTL
- bytes[9] = byte(ip.Protocol)
- if err := ip.AddressTo4(); err != nil {
- return err
- }
- copy(bytes[12:16], ip.SrcIP)
- copy(bytes[16:20], ip.DstIP)
- curLocation := 20
- // Now, we will encode the options
- for _, opt := range ip.Options {
- switch opt.OptionType {
- case 0:
- // this is the end of option lists
- bytes[curLocation] = 0
- curLocation++
- case 1:
- // this is the padding
- bytes[curLocation] = 1
- curLocation++
- default:
- bytes[curLocation] = opt.OptionType
- bytes[curLocation+1] = opt.OptionLength
- // sanity checking to protect us from buffer overrun
- if len(opt.OptionData) > int(opt.OptionLength-2) {
- return errors.New("option length is smaller than length of option data")
- }
- copy(bytes[curLocation+2:curLocation+int(opt.OptionLength)], opt.OptionData)
- curLocation += int(opt.OptionLength)
- }
- }
- if opts.ComputeChecksums {
- ip.Checksum = checksum(bytes)
- }
- binary.BigEndian.PutUint16(bytes[10:], ip.Checksum)
- return nil
- }
- func checksum(bytes []byte) uint16 {
- // Clear checksum bytes
- bytes[10] = 0
- bytes[11] = 0
- // Compute checksum
- var csum uint32
- for i := 0; i < len(bytes); i += 2 {
- csum += uint32(bytes[i]) << 8
- csum += uint32(bytes[i+1])
- }
- for {
- // Break when sum is less or equals to 0xFFFF
- if csum <= 65535 {
- break
- }
- // Add carry to the sum
- csum = (csum >> 16) + uint32(uint16(csum))
- }
- // Flip all the bits
- return ^uint16(csum)
- }
- func (ip *IPv4) flagsfrags() (ff uint16) {
- ff |= uint16(ip.Flags) << 13
- ff |= ip.FragOffset
- return
- }
- // DecodeFromBytes decodes the given bytes into this layer.
- func (ip *IPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
- if len(data) < 20 {
- df.SetTruncated()
- return fmt.Errorf("Invalid ip4 header. Length %d less than 20", len(data))
- }
- flagsfrags := binary.BigEndian.Uint16(data[6:8])
- ip.Version = uint8(data[0]) >> 4
- ip.IHL = uint8(data[0]) & 0x0F
- ip.TOS = data[1]
- ip.Length = binary.BigEndian.Uint16(data[2:4])
- ip.Id = binary.BigEndian.Uint16(data[4:6])
- ip.Flags = IPv4Flag(flagsfrags >> 13)
- ip.FragOffset = flagsfrags & 0x1FFF
- ip.TTL = data[8]
- ip.Protocol = IPProtocol(data[9])
- ip.Checksum = binary.BigEndian.Uint16(data[10:12])
- ip.SrcIP = data[12:16]
- ip.DstIP = data[16:20]
- ip.Options = ip.Options[:0]
- ip.Padding = nil
- // Set up an initial guess for contents/payload... we'll reset these soon.
- ip.BaseLayer = BaseLayer{Contents: data}
- // This code is added for the following enviroment:
- // * Windows 10 with TSO option activated. ( tested on Hyper-V, RealTek ethernet driver )
- if ip.Length == 0 {
- // If using TSO(TCP Segmentation Offload), length is zero.
- // The actual packet length is the length of data.
- ip.Length = uint16(len(data))
- }
- if ip.Length < 20 {
- return fmt.Errorf("Invalid (too small) IP length (%d < 20)", ip.Length)
- } else if ip.IHL < 5 {
- return fmt.Errorf("Invalid (too small) IP header length (%d < 5)", ip.IHL)
- } else if int(ip.IHL*4) > int(ip.Length) {
- return fmt.Errorf("Invalid IP header length > IP length (%d > %d)", ip.IHL, ip.Length)
- }
- if cmp := len(data) - int(ip.Length); cmp > 0 {
- data = data[:ip.Length]
- } else if cmp < 0 {
- df.SetTruncated()
- if int(ip.IHL)*4 > len(data) {
- return errors.New("Not all IP header bytes available")
- }
- }
- ip.Contents = data[:ip.IHL*4]
- ip.Payload = data[ip.IHL*4:]
- // From here on, data contains the header options.
- data = data[20 : ip.IHL*4]
- // Pull out IP options
- for len(data) > 0 {
- if ip.Options == nil {
- // Pre-allocate to avoid growing the slice too much.
- ip.Options = make([]IPv4Option, 0, 4)
- }
- opt := IPv4Option{OptionType: data[0]}
- switch opt.OptionType {
- case 0: // End of options
- opt.OptionLength = 1
- ip.Options = append(ip.Options, opt)
- ip.Padding = data[1:]
- return nil
- case 1: // 1 byte padding
- opt.OptionLength = 1
- data = data[1:]
- ip.Options = append(ip.Options, opt)
- default:
- if len(data) < 2 {
- df.SetTruncated()
- return fmt.Errorf("Invalid ip4 option length. Length %d less than 2", len(data))
- }
- opt.OptionLength = data[1]
- if len(data) < int(opt.OptionLength) {
- df.SetTruncated()
- return fmt.Errorf("IP option length exceeds remaining IP header size, option type %v length %v", opt.OptionType, opt.OptionLength)
- }
- if opt.OptionLength <= 2 {
- return fmt.Errorf("Invalid IP option type %v length %d. Must be greater than 2", opt.OptionType, opt.OptionLength)
- }
- opt.OptionData = data[2:opt.OptionLength]
- data = data[opt.OptionLength:]
- ip.Options = append(ip.Options, opt)
- }
- }
- return nil
- }
- func (i *IPv4) CanDecode() gopacket.LayerClass {
- return LayerTypeIPv4
- }
- func (i *IPv4) NextLayerType() gopacket.LayerType {
- if i.Flags&IPv4MoreFragments != 0 || i.FragOffset != 0 {
- return gopacket.LayerTypeFragment
- }
- return i.Protocol.LayerType()
- }
- func decodeIPv4(data []byte, p gopacket.PacketBuilder) error {
- ip := &IPv4{}
- err := ip.DecodeFromBytes(data, p)
- p.AddLayer(ip)
- p.SetNetworkLayer(ip)
- if err != nil {
- return err
- }
- return p.NextDecoder(ip.NextLayerType())
- }
- func checkIPv4Address(addr net.IP) (net.IP, error) {
- if c := addr.To4(); c != nil {
- return c, nil
- }
- if len(addr) == net.IPv6len {
- return nil, errors.New("address is IPv6")
- }
- return nil, fmt.Errorf("wrong length of %d bytes instead of %d", len(addr), net.IPv4len)
- }
- func (ip *IPv4) AddressTo4() error {
- var src, dst net.IP
- if addr, err := checkIPv4Address(ip.SrcIP); err != nil {
- return fmt.Errorf("Invalid source IPv4 address (%s)", err)
- } else {
- src = addr
- }
- if addr, err := checkIPv4Address(ip.DstIP); err != nil {
- return fmt.Errorf("Invalid destination IPv4 address (%s)", err)
- } else {
- dst = addr
- }
- ip.SrcIP = src
- ip.DstIP = dst
- return nil
- }
|