// 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" "encoding/hex" "errors" "fmt" "net" "time" "github.com/google/gopacket" ) // Based on RFC 4861 // ICMPv6Opt indicate how to decode the data associated with each ICMPv6Option. type ICMPv6Opt uint8 const ( _ ICMPv6Opt = iota // ICMPv6OptSourceAddress contains the link-layer address of the sender of // the packet. It is used in the Neighbor Solicitation, Router // Solicitation, and Router Advertisement packets. Must be ignored for other // Neighbor discovery messages. ICMPv6OptSourceAddress // ICMPv6OptTargetAddress contains the link-layer address of the target. It // is used in Neighbor Advertisement and Redirect packets. Must be ignored // for other Neighbor discovery messages. ICMPv6OptTargetAddress // ICMPv6OptPrefixInfo provides hosts with on-link prefixes and prefixes // for Address Autoconfiguration. The Prefix Information option appears in // Router Advertisement packets and MUST be silently ignored for other // messages. ICMPv6OptPrefixInfo // ICMPv6OptRedirectedHeader is used in Redirect messages and contains all // or part of the packet that is being redirected. ICMPv6OptRedirectedHeader // ICMPv6OptMTU is used in Router Advertisement messages to ensure that all // nodes on a link use the same MTU value in those cases where the link MTU // is not well known. This option MUST be silently ignored for other // Neighbor Discovery messages. ICMPv6OptMTU ) // ICMPv6RouterSolicitation is sent by hosts to find routers. type ICMPv6RouterSolicitation struct { BaseLayer Options ICMPv6Options } // ICMPv6RouterAdvertisement is sent by routers in response to Solicitation. type ICMPv6RouterAdvertisement struct { BaseLayer HopLimit uint8 Flags uint8 RouterLifetime uint16 ReachableTime uint32 RetransTimer uint32 Options ICMPv6Options } // ICMPv6NeighborSolicitation is sent to request the link-layer address of a // target node. type ICMPv6NeighborSolicitation struct { BaseLayer TargetAddress net.IP Options ICMPv6Options } // ICMPv6NeighborAdvertisement is sent by nodes in response to Solicitation. type ICMPv6NeighborAdvertisement struct { BaseLayer Flags uint8 TargetAddress net.IP Options ICMPv6Options } // ICMPv6Redirect is sent by routers to inform hosts of a better first-hop node // on the path to a destination. type ICMPv6Redirect struct { BaseLayer TargetAddress net.IP DestinationAddress net.IP Options ICMPv6Options } // ICMPv6Option contains the type and data for a single option. type ICMPv6Option struct { Type ICMPv6Opt Data []byte } // ICMPv6Options is a slice of ICMPv6Option. type ICMPv6Options []ICMPv6Option func (i ICMPv6Opt) String() string { switch i { case ICMPv6OptSourceAddress: return "SourceAddress" case ICMPv6OptTargetAddress: return "TargetAddress" case ICMPv6OptPrefixInfo: return "PrefixInfo" case ICMPv6OptRedirectedHeader: return "RedirectedHeader" case ICMPv6OptMTU: return "MTU" default: return fmt.Sprintf("Unknown(%d)", i) } } // LayerType returns LayerTypeICMPv6. func (i *ICMPv6RouterSolicitation) LayerType() gopacket.LayerType { return LayerTypeICMPv6RouterSolicitation } // NextLayerType returns the layer type contained by this DecodingLayer. func (i *ICMPv6RouterSolicitation) NextLayerType() gopacket.LayerType { return gopacket.LayerTypePayload } // DecodeFromBytes decodes the given bytes into this layer. func (i *ICMPv6RouterSolicitation) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { // first 4 bytes are reserved followed by options if len(data) < 4 { df.SetTruncated() return errors.New("ICMP layer less then 4 bytes for ICMPv6 router solicitation") } // truncate old options i.Options = i.Options[:0] return i.Options.DecodeFromBytes(data[4:], df) } // SerializeTo writes the serialized form of this layer into the // SerializationBuffer, implementing gopacket.SerializableLayer. // See the docs for gopacket.SerializableLayer for more info. func (i *ICMPv6RouterSolicitation) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { if err := i.Options.SerializeTo(b, opts); err != nil { return err } buf, err := b.PrependBytes(4) if err != nil { return err } copy(buf, lotsOfZeros[:4]) return nil } // LayerType returns LayerTypeICMPv6RouterAdvertisement. func (i *ICMPv6RouterAdvertisement) LayerType() gopacket.LayerType { return LayerTypeICMPv6RouterAdvertisement } // NextLayerType returns the layer type contained by this DecodingLayer. func (i *ICMPv6RouterAdvertisement) NextLayerType() gopacket.LayerType { return gopacket.LayerTypePayload } // DecodeFromBytes decodes the given bytes into this layer. func (i *ICMPv6RouterAdvertisement) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { if len(data) < 12 { df.SetTruncated() return errors.New("ICMP layer less then 12 bytes for ICMPv6 router advertisement") } i.HopLimit = uint8(data[0]) // M, O bit followed by 6 reserved bits i.Flags = uint8(data[1]) i.RouterLifetime = binary.BigEndian.Uint16(data[2:4]) i.ReachableTime = binary.BigEndian.Uint32(data[4:8]) i.RetransTimer = binary.BigEndian.Uint32(data[8:12]) i.BaseLayer = BaseLayer{data, nil} // assume no payload // truncate old options i.Options = i.Options[:0] return i.Options.DecodeFromBytes(data[12:], df) } // SerializeTo writes the serialized form of this layer into the // SerializationBuffer, implementing gopacket.SerializableLayer. // See the docs for gopacket.SerializableLayer for more info. func (i *ICMPv6RouterAdvertisement) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { if err := i.Options.SerializeTo(b, opts); err != nil { return err } buf, err := b.PrependBytes(12) if err != nil { return err } buf[0] = byte(i.HopLimit) buf[1] = byte(i.Flags) binary.BigEndian.PutUint16(buf[2:], i.RouterLifetime) binary.BigEndian.PutUint32(buf[4:], i.ReachableTime) binary.BigEndian.PutUint32(buf[8:], i.RetransTimer) return nil } // ManagedAddressConfig is true when addresses are available via DHCPv6. If // set, the OtherConfig flag is redundant. func (i *ICMPv6RouterAdvertisement) ManagedAddressConfig() bool { return i.Flags&0x80 != 1 } // OtherConfig is true when there is other configuration information available // via DHCPv6. For example, DNS-related information. func (i *ICMPv6RouterAdvertisement) OtherConfig() bool { return i.Flags&0x40 != 1 } // LayerType returns LayerTypeICMPv6NeighborSolicitation. func (i *ICMPv6NeighborSolicitation) LayerType() gopacket.LayerType { return LayerTypeICMPv6NeighborSolicitation } // NextLayerType returns the layer type contained by this DecodingLayer. func (i *ICMPv6NeighborSolicitation) NextLayerType() gopacket.LayerType { return gopacket.LayerTypePayload } // DecodeFromBytes decodes the given bytes into this layer. func (i *ICMPv6NeighborSolicitation) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { if len(data) < 20 { df.SetTruncated() return errors.New("ICMP layer less then 20 bytes for ICMPv6 neighbor solicitation") } i.TargetAddress = net.IP(data[4:20]) i.BaseLayer = BaseLayer{data, nil} // assume no payload // truncate old options i.Options = i.Options[:0] return i.Options.DecodeFromBytes(data[20:], df) } // SerializeTo writes the serialized form of this layer into the // SerializationBuffer, implementing gopacket.SerializableLayer. // See the docs for gopacket.SerializableLayer for more info. func (i *ICMPv6NeighborSolicitation) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { if err := i.Options.SerializeTo(b, opts); err != nil { return err } buf, err := b.PrependBytes(20) if err != nil { return err } copy(buf, lotsOfZeros[:4]) copy(buf[4:], i.TargetAddress) return nil } // LayerType returns LayerTypeICMPv6NeighborAdvertisement. func (i *ICMPv6NeighborAdvertisement) LayerType() gopacket.LayerType { return LayerTypeICMPv6NeighborAdvertisement } // NextLayerType returns the layer type contained by this DecodingLayer. func (i *ICMPv6NeighborAdvertisement) NextLayerType() gopacket.LayerType { return gopacket.LayerTypePayload } // DecodeFromBytes decodes the given bytes into this layer. func (i *ICMPv6NeighborAdvertisement) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { if len(data) < 20 { df.SetTruncated() return errors.New("ICMP layer less then 20 bytes for ICMPv6 neighbor advertisement") } i.Flags = uint8(data[0]) i.TargetAddress = net.IP(data[4:20]) i.BaseLayer = BaseLayer{data, nil} // assume no payload // truncate old options i.Options = i.Options[:0] return i.Options.DecodeFromBytes(data[20:], df) } // SerializeTo writes the serialized form of this layer into the // SerializationBuffer, implementing gopacket.SerializableLayer. // See the docs for gopacket.SerializableLayer for more info. func (i *ICMPv6NeighborAdvertisement) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { if err := i.Options.SerializeTo(b, opts); err != nil { return err } buf, err := b.PrependBytes(20) if err != nil { return err } buf[0] = byte(i.Flags) copy(buf[1:], lotsOfZeros[:3]) copy(buf[4:], i.TargetAddress) return nil } // Router indicates whether the sender is a router or not. func (i *ICMPv6NeighborAdvertisement) Router() bool { return i.Flags&0x80 != 0 } // Solicited indicates whether the advertisement was solicited or not. func (i *ICMPv6NeighborAdvertisement) Solicited() bool { return i.Flags&0x40 != 0 } // Override indicates whether the advertisement should Override an existing // cache entry. func (i *ICMPv6NeighborAdvertisement) Override() bool { return i.Flags&0x20 != 0 } // LayerType returns LayerTypeICMPv6Redirect. func (i *ICMPv6Redirect) LayerType() gopacket.LayerType { return LayerTypeICMPv6Redirect } // NextLayerType returns the layer type contained by this DecodingLayer. func (i *ICMPv6Redirect) NextLayerType() gopacket.LayerType { return gopacket.LayerTypePayload } // DecodeFromBytes decodes the given bytes into this layer. func (i *ICMPv6Redirect) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { if len(data) < 36 { df.SetTruncated() return errors.New("ICMP layer less then 36 bytes for ICMPv6 redirect") } i.TargetAddress = net.IP(data[4:20]) i.DestinationAddress = net.IP(data[20:36]) i.BaseLayer = BaseLayer{data, nil} // assume no payload // truncate old options i.Options = i.Options[:0] return i.Options.DecodeFromBytes(data[36:], df) } // SerializeTo writes the serialized form of this layer into the // SerializationBuffer, implementing gopacket.SerializableLayer. // See the docs for gopacket.SerializableLayer for more info. func (i *ICMPv6Redirect) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { if err := i.Options.SerializeTo(b, opts); err != nil { return err } buf, err := b.PrependBytes(36) if err != nil { return err } copy(buf, lotsOfZeros[:4]) copy(buf[4:], i.TargetAddress) copy(buf[20:], i.DestinationAddress) return nil } func (i ICMPv6Option) String() string { hd := hex.EncodeToString(i.Data) if len(hd) > 0 { hd = " 0x" + hd } switch i.Type { case ICMPv6OptSourceAddress, ICMPv6OptTargetAddress: return fmt.Sprintf("ICMPv6Option(%s:%v)", i.Type, net.HardwareAddr(i.Data)) case ICMPv6OptPrefixInfo: if len(i.Data) == 30 { prefixLen := uint8(i.Data[0]) onLink := (i.Data[1]&0x80 != 0) autonomous := (i.Data[1]&0x40 != 0) validLifetime := time.Duration(binary.BigEndian.Uint32(i.Data[2:6])) * time.Second preferredLifetime := time.Duration(binary.BigEndian.Uint32(i.Data[6:10])) * time.Second prefix := net.IP(i.Data[14:]) return fmt.Sprintf("ICMPv6Option(%s:%v/%v:%t:%t:%v:%v)", i.Type, prefix, prefixLen, onLink, autonomous, validLifetime, preferredLifetime) } case ICMPv6OptRedirectedHeader: // could invoke IP decoder on data... probably best not to break case ICMPv6OptMTU: if len(i.Data) == 6 { return fmt.Sprintf("ICMPv6Option(%s:%v)", i.Type, binary.BigEndian.Uint32(i.Data[2:])) } } return fmt.Sprintf("ICMPv6Option(%s:%s)", i.Type, hd) } // DecodeFromBytes decodes the given bytes into this layer. func (i *ICMPv6Options) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error { for len(data) > 0 { if len(data) < 2 { df.SetTruncated() return errors.New("ICMP layer less then 2 bytes for ICMPv6 message option") } // unit is 8 octets, convert to bytes length := int(data[1]) * 8 if len(data) < length { df.SetTruncated() return fmt.Errorf("ICMP layer only %v bytes for ICMPv6 message option with length %v", len(data), length) } o := ICMPv6Option{ Type: ICMPv6Opt(data[0]), Data: data[2:length], } // chop off option we just consumed data = data[length:] *i = append(*i, o) } return nil } // SerializeTo writes the serialized form of this layer into the // SerializationBuffer, implementing gopacket.SerializableLayer. // See the docs for gopacket.SerializableLayer for more info. func (i *ICMPv6Options) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error { for _, opt := range []ICMPv6Option(*i) { length := len(opt.Data) + 2 buf, err := b.PrependBytes(length) if err != nil { return err } buf[0] = byte(opt.Type) buf[1] = byte(length / 8) copy(buf[2:], opt.Data) } return nil } func decodeICMPv6RouterSolicitation(data []byte, p gopacket.PacketBuilder) error { i := &ICMPv6RouterSolicitation{} return decodingLayerDecoder(i, data, p) } func decodeICMPv6RouterAdvertisement(data []byte, p gopacket.PacketBuilder) error { i := &ICMPv6RouterAdvertisement{} return decodingLayerDecoder(i, data, p) } func decodeICMPv6NeighborSolicitation(data []byte, p gopacket.PacketBuilder) error { i := &ICMPv6NeighborSolicitation{} return decodingLayerDecoder(i, data, p) } func decodeICMPv6NeighborAdvertisement(data []byte, p gopacket.PacketBuilder) error { i := &ICMPv6NeighborAdvertisement{} return decodingLayerDecoder(i, data, p) } func decodeICMPv6Redirect(data []byte, p gopacket.PacketBuilder) error { i := &ICMPv6Redirect{} return decodingLayerDecoder(i, data, p) }