blob: 70e9c8d558a9c0d360e5e32d2780177a911a457c [file] [log] [blame]
// 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"
"github.com/google/gopacket"
)
const (
// IPv6HopByHopOptionJumbogram code as defined in RFC 2675
IPv6HopByHopOptionJumbogram = 0xC2
)
const (
ipv6MaxPayloadLength = 65535
)
// IPv6 is the layer for the IPv6 header.
type IPv6 struct {
// http://www.networksorcery.com/enp/protocol/ipv6.htm
BaseLayer
Version uint8
TrafficClass uint8
FlowLabel uint32
Length uint16
NextHeader IPProtocol
HopLimit uint8
SrcIP net.IP
DstIP net.IP
HopByHop *IPv6HopByHop
// hbh will be pointed to by HopByHop if that layer exists.
hbh IPv6HopByHop
}
// LayerType returns LayerTypeIPv6
func (ipv6 *IPv6) LayerType() gopacket.LayerType { return LayerTypeIPv6 }
// NetworkFlow returns this new Flow (EndpointIPv6, SrcIP, DstIP)
func (ipv6 *IPv6) NetworkFlow() gopacket.Flow {
return gopacket.NewFlow(EndpointIPv6, ipv6.SrcIP, ipv6.DstIP)
}
// Search for Jumbo Payload TLV in IPv6HopByHop and return (length, true) if found
func getIPv6HopByHopJumboLength(hopopts *IPv6HopByHop) (uint32, bool, error) {
var tlv *IPv6HopByHopOption
for _, t := range hopopts.Options {
if t.OptionType == IPv6HopByHopOptionJumbogram {
tlv = t
break
}
}
if tlv == nil {
// Not found
return 0, false, nil
}
if len(tlv.OptionData) != 4 {
return 0, false, errors.New("Jumbo length TLV data must have length 4")
}
l := binary.BigEndian.Uint32(tlv.OptionData)
if l <= ipv6MaxPayloadLength {
return 0, false, fmt.Errorf("Jumbo length cannot be less than %d", ipv6MaxPayloadLength+1)
}
// Found
return l, true, nil
}
// Adds zero-valued Jumbo TLV to IPv6 header if it does not exist
// (if necessary add hop-by-hop header)
func addIPv6JumboOption(ip6 *IPv6) {
var tlv *IPv6HopByHopOption
if ip6.HopByHop == nil {
// Add IPv6 HopByHop
ip6.HopByHop = &IPv6HopByHop{}
ip6.HopByHop.NextHeader = ip6.NextHeader
ip6.HopByHop.HeaderLength = 0
ip6.NextHeader = IPProtocolIPv6HopByHop
}
for _, t := range ip6.HopByHop.Options {
if t.OptionType == IPv6HopByHopOptionJumbogram {
tlv = t
break
}
}
if tlv == nil {
// Add Jumbo TLV
tlv = &IPv6HopByHopOption{}
ip6.HopByHop.Options = append(ip6.HopByHop.Options, tlv)
}
tlv.SetJumboLength(0)
}
// Set jumbo length in serialized IPv6 payload (starting with HopByHop header)
func setIPv6PayloadJumboLength(hbh []byte) error {
pLen := len(hbh)
if pLen < 8 {
//HopByHop is minimum 8 bytes
return fmt.Errorf("Invalid IPv6 payload (length %d)", pLen)
}
hbhLen := int((hbh[1] + 1) * 8)
if hbhLen > pLen {
return fmt.Errorf("Invalid hop-by-hop length (length: %d, payload: %d", hbhLen, pLen)
}
offset := 2 //start with options
for offset < hbhLen {
opt := hbh[offset]
if opt == 0 {
//Pad1
offset++
continue
}
optLen := int(hbh[offset+1])
if opt == IPv6HopByHopOptionJumbogram {
if optLen == 4 {
binary.BigEndian.PutUint32(hbh[offset+2:], uint32(pLen))
return nil
}
return fmt.Errorf("Jumbo TLV too short (%d bytes)", optLen)
}
offset += 2 + optLen
}
return errors.New("Jumbo TLV not found")
}
// SerializeTo writes the serialized form of this layer into the
// SerializationBuffer, implementing gopacket.SerializableLayer.
// See the docs for gopacket.SerializableLayer for more info.
func (ipv6 *IPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
var jumbo bool
var err error
payload := b.Bytes()
pLen := len(payload)
if pLen > ipv6MaxPayloadLength {
jumbo = true
if opts.FixLengths {
// We need to set the length later because the hop-by-hop header may
// not exist or else need padding, so pLen may yet change
addIPv6JumboOption(ipv6)
} else if ipv6.HopByHop == nil {
return fmt.Errorf("Cannot fit payload length of %d into IPv6 packet", pLen)
} else {
_, ok, err := getIPv6HopByHopJumboLength(ipv6.HopByHop)
if err != nil {
return err
}
if !ok {
return errors.New("Missing jumbo length hop-by-hop option")
}
}
}
hbhAlreadySerialized := false
if ipv6.HopByHop != nil {
for _, l := range b.Layers() {
if l == LayerTypeIPv6HopByHop {
hbhAlreadySerialized = true
break
}
}
}
if ipv6.HopByHop != nil && !hbhAlreadySerialized {
if ipv6.NextHeader != IPProtocolIPv6HopByHop {
// Just fix it instead of throwing an error
ipv6.NextHeader = IPProtocolIPv6HopByHop
}
err = ipv6.HopByHop.SerializeTo(b, opts)
if err != nil {
return err
}
payload = b.Bytes()
pLen = len(payload)
if opts.FixLengths && jumbo {
err := setIPv6PayloadJumboLength(payload)
if err != nil {
return err
}
}
}
if !jumbo && pLen > ipv6MaxPayloadLength {
return errors.New("Cannot fit payload into IPv6 header")
}
bytes, err := b.PrependBytes(40)
if err != nil {
return err
}
bytes[0] = (ipv6.Version << 4) | (ipv6.TrafficClass >> 4)
bytes[1] = (ipv6.TrafficClass << 4) | uint8(ipv6.FlowLabel>>16)
binary.BigEndian.PutUint16(bytes[2:], uint16(ipv6.FlowLabel))
if opts.FixLengths {
if jumbo {
ipv6.Length = 0
} else {
ipv6.Length = uint16(pLen)
}
}
binary.BigEndian.PutUint16(bytes[4:], ipv6.Length)
bytes[6] = byte(ipv6.NextHeader)
bytes[7] = byte(ipv6.HopLimit)
if err := ipv6.AddressTo16(); err != nil {
return err
}
copy(bytes[8:], ipv6.SrcIP)
copy(bytes[24:], ipv6.DstIP)
return nil
}
// DecodeFromBytes implementation according to gopacket.DecodingLayer
func (ipv6 *IPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 40 {
df.SetTruncated()
return fmt.Errorf("Invalid ip6 header. Length %d less than 40", len(data))
}
ipv6.Version = uint8(data[0]) >> 4
ipv6.TrafficClass = uint8((binary.BigEndian.Uint16(data[0:2]) >> 4) & 0x00FF)
ipv6.FlowLabel = binary.BigEndian.Uint32(data[0:4]) & 0x000FFFFF
ipv6.Length = binary.BigEndian.Uint16(data[4:6])
ipv6.NextHeader = IPProtocol(data[6])
ipv6.HopLimit = data[7]
ipv6.SrcIP = data[8:24]
ipv6.DstIP = data[24:40]
ipv6.HopByHop = nil
ipv6.BaseLayer = BaseLayer{data[:40], data[40:]}
// We treat a HopByHop IPv6 option as part of the IPv6 packet, since its
// options are crucial for understanding what's actually happening per packet.
if ipv6.NextHeader == IPProtocolIPv6HopByHop {
err := ipv6.hbh.DecodeFromBytes(ipv6.Payload, df)
if err != nil {
return err
}
ipv6.HopByHop = &ipv6.hbh
pEnd, jumbo, err := getIPv6HopByHopJumboLength(ipv6.HopByHop)
if err != nil {
return err
}
if jumbo && ipv6.Length == 0 {
pEnd := int(pEnd)
if pEnd > len(ipv6.Payload) {
df.SetTruncated()
pEnd = len(ipv6.Payload)
}
ipv6.Payload = ipv6.Payload[:pEnd]
return nil
} else if jumbo && ipv6.Length != 0 {
return errors.New("IPv6 has jumbo length and IPv6 length is not 0")
} else if !jumbo && ipv6.Length == 0 {
return errors.New("IPv6 length 0, but HopByHop header does not have jumbogram option")
} else {
ipv6.Payload = ipv6.Payload[ipv6.hbh.ActualLength:]
}
}
if ipv6.Length == 0 {
return fmt.Errorf("IPv6 length 0, but next header is %v, not HopByHop", ipv6.NextHeader)
}
pEnd := int(ipv6.Length)
if pEnd > len(ipv6.Payload) {
df.SetTruncated()
pEnd = len(ipv6.Payload)
}
ipv6.Payload = ipv6.Payload[:pEnd]
return nil
}
// CanDecode implementation according to gopacket.DecodingLayer
func (ipv6 *IPv6) CanDecode() gopacket.LayerClass {
return LayerTypeIPv6
}
// NextLayerType implementation according to gopacket.DecodingLayer
func (ipv6 *IPv6) NextLayerType() gopacket.LayerType {
if ipv6.HopByHop != nil {
return ipv6.HopByHop.NextHeader.LayerType()
}
return ipv6.NextHeader.LayerType()
}
func decodeIPv6(data []byte, p gopacket.PacketBuilder) error {
ip6 := &IPv6{}
err := ip6.DecodeFromBytes(data, p)
p.AddLayer(ip6)
p.SetNetworkLayer(ip6)
if ip6.HopByHop != nil {
p.AddLayer(ip6.HopByHop)
}
if err != nil {
return err
}
return p.NextDecoder(ip6.NextLayerType())
}
type ipv6HeaderTLVOption struct {
OptionType, OptionLength uint8
ActualLength int
OptionData []byte
OptionAlignment [2]uint8 // Xn+Y = [2]uint8{X, Y}
}
func (h *ipv6HeaderTLVOption) serializeTo(data []byte, fixLengths bool, dryrun bool) int {
if fixLengths {
h.OptionLength = uint8(len(h.OptionData))
}
length := int(h.OptionLength) + 2
if !dryrun {
data[0] = h.OptionType
data[1] = h.OptionLength
copy(data[2:], h.OptionData)
}
return length
}
func decodeIPv6HeaderTLVOption(data []byte) (h *ipv6HeaderTLVOption) {
h = &ipv6HeaderTLVOption{}
if data[0] == 0 {
h.ActualLength = 1
return
}
h.OptionType = data[0]
h.OptionLength = data[1]
h.ActualLength = int(h.OptionLength) + 2
h.OptionData = data[2:h.ActualLength]
return
}
func serializeTLVOptionPadding(data []byte, padLength int) {
if padLength <= 0 {
return
}
if padLength == 1 {
data[0] = 0x0
return
}
tlvLength := uint8(padLength) - 2
data[0] = 0x1
data[1] = tlvLength
if tlvLength != 0 {
for k := range data[2:] {
data[k+2] = 0x0
}
}
return
}
// If buf is 'nil' do a serialize dry run
func serializeIPv6HeaderTLVOptions(buf []byte, options []*ipv6HeaderTLVOption, fixLengths bool) int {
var l int
dryrun := buf == nil
length := 2
for _, opt := range options {
if fixLengths {
x := int(opt.OptionAlignment[0])
y := int(opt.OptionAlignment[1])
if x != 0 {
n := length / x
offset := x*n + y
if offset < length {
offset += x
}
if length != offset {
pad := offset - length
if !dryrun {
serializeTLVOptionPadding(buf[length-2:], pad)
}
length += pad
}
}
}
if dryrun {
l = opt.serializeTo(nil, fixLengths, true)
} else {
l = opt.serializeTo(buf[length-2:], fixLengths, false)
}
length += l
}
if fixLengths {
pad := length % 8
if pad != 0 {
if !dryrun {
serializeTLVOptionPadding(buf[length-2:], pad)
}
length += pad
}
}
return length - 2
}
type ipv6ExtensionBase struct {
BaseLayer
NextHeader IPProtocol
HeaderLength uint8
ActualLength int
}
func decodeIPv6ExtensionBase(data []byte, df gopacket.DecodeFeedback) (i ipv6ExtensionBase, returnedErr error) {
if len(data) < 2 {
df.SetTruncated()
return ipv6ExtensionBase{}, fmt.Errorf("Invalid ip6-extension header. Length %d less than 2", len(data))
}
i.NextHeader = IPProtocol(data[0])
i.HeaderLength = data[1]
i.ActualLength = int(i.HeaderLength)*8 + 8
if len(data) < i.ActualLength {
return ipv6ExtensionBase{}, fmt.Errorf("Invalid ip6-extension header. Length %d less than specified length %d", len(data), i.ActualLength)
}
i.Contents = data[:i.ActualLength]
i.Payload = data[i.ActualLength:]
return
}
// IPv6ExtensionSkipper is a DecodingLayer which decodes and ignores v6
// extensions. You can use it with a DecodingLayerParser to handle IPv6 stacks
// which may or may not have extensions.
type IPv6ExtensionSkipper struct {
NextHeader IPProtocol
BaseLayer
}
// DecodeFromBytes implementation according to gopacket.DecodingLayer
func (i *IPv6ExtensionSkipper) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
extension, err := decodeIPv6ExtensionBase(data, df)
if err != nil {
return err
}
i.BaseLayer = BaseLayer{data[:extension.ActualLength], data[extension.ActualLength:]}
i.NextHeader = extension.NextHeader
return nil
}
// CanDecode implementation according to gopacket.DecodingLayer
func (i *IPv6ExtensionSkipper) CanDecode() gopacket.LayerClass {
return LayerClassIPv6Extension
}
// NextLayerType implementation according to gopacket.DecodingLayer
func (i *IPv6ExtensionSkipper) NextLayerType() gopacket.LayerType {
return i.NextHeader.LayerType()
}
// IPv6HopByHopOption is a TLV option present in an IPv6 hop-by-hop extension.
type IPv6HopByHopOption ipv6HeaderTLVOption
// IPv6HopByHop is the IPv6 hop-by-hop extension.
type IPv6HopByHop struct {
ipv6ExtensionBase
Options []*IPv6HopByHopOption
}
// LayerType returns LayerTypeIPv6HopByHop.
func (i *IPv6HopByHop) LayerType() gopacket.LayerType { return LayerTypeIPv6HopByHop }
// SerializeTo implementation according to gopacket.SerializableLayer
func (i *IPv6HopByHop) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
var bytes []byte
var err error
o := make([]*ipv6HeaderTLVOption, 0, len(i.Options))
for _, v := range i.Options {
o = append(o, (*ipv6HeaderTLVOption)(v))
}
l := serializeIPv6HeaderTLVOptions(nil, o, opts.FixLengths)
bytes, err = b.PrependBytes(l)
if err != nil {
return err
}
serializeIPv6HeaderTLVOptions(bytes, o, opts.FixLengths)
length := len(bytes) + 2
if length%8 != 0 {
return errors.New("IPv6HopByHop actual length must be multiple of 8")
}
bytes, err = b.PrependBytes(2)
if err != nil {
return err
}
bytes[0] = uint8(i.NextHeader)
if opts.FixLengths {
i.HeaderLength = uint8((length / 8) - 1)
}
bytes[1] = uint8(i.HeaderLength)
return nil
}
// DecodeFromBytes implementation according to gopacket.DecodingLayer
func (i *IPv6HopByHop) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
var err error
i.ipv6ExtensionBase, err = decodeIPv6ExtensionBase(data, df)
if err != nil {
return err
}
offset := 2
for offset < i.ActualLength {
opt := decodeIPv6HeaderTLVOption(data[offset:])
i.Options = append(i.Options, (*IPv6HopByHopOption)(opt))
offset += opt.ActualLength
}
return nil
}
func decodeIPv6HopByHop(data []byte, p gopacket.PacketBuilder) error {
i := &IPv6HopByHop{}
err := i.DecodeFromBytes(data, p)
p.AddLayer(i)
if err != nil {
return err
}
return p.NextDecoder(i.NextHeader)
}
// SetJumboLength adds the IPv6HopByHopOptionJumbogram with the given length
func (o *IPv6HopByHopOption) SetJumboLength(len uint32) {
o.OptionType = IPv6HopByHopOptionJumbogram
o.OptionLength = 4
o.ActualLength = 6
if o.OptionData == nil {
o.OptionData = make([]byte, 4)
}
binary.BigEndian.PutUint32(o.OptionData, len)
o.OptionAlignment = [2]uint8{4, 2}
}
// IPv6Routing is the IPv6 routing extension.
type IPv6Routing struct {
ipv6ExtensionBase
RoutingType uint8
SegmentsLeft uint8
// This segment is supposed to be zero according to RFC2460, the second set of
// 4 bytes in the extension.
Reserved []byte
// SourceRoutingIPs is the set of IPv6 addresses requested for source routing,
// set only if RoutingType == 0.
SourceRoutingIPs []net.IP
}
// LayerType returns LayerTypeIPv6Routing.
func (i *IPv6Routing) LayerType() gopacket.LayerType { return LayerTypeIPv6Routing }
func decodeIPv6Routing(data []byte, p gopacket.PacketBuilder) error {
base, err := decodeIPv6ExtensionBase(data, p)
if err != nil {
return err
}
i := &IPv6Routing{
ipv6ExtensionBase: base,
RoutingType: data[2],
SegmentsLeft: data[3],
Reserved: data[4:8],
}
switch i.RoutingType {
case 0: // Source routing
if (i.ActualLength-8)%16 != 0 {
return fmt.Errorf("Invalid IPv6 source routing, length of type 0 packet %d", i.ActualLength)
}
for d := i.Contents[8:]; len(d) >= 16; d = d[16:] {
i.SourceRoutingIPs = append(i.SourceRoutingIPs, net.IP(d[:16]))
}
default:
return fmt.Errorf("Unknown IPv6 routing header type %d", i.RoutingType)
}
p.AddLayer(i)
return p.NextDecoder(i.NextHeader)
}
// IPv6Fragment is the IPv6 fragment header, used for packet
// fragmentation/defragmentation.
type IPv6Fragment struct {
BaseLayer
NextHeader IPProtocol
// Reserved1 is bits [8-16), from least to most significant, 0-indexed
Reserved1 uint8
FragmentOffset uint16
// Reserved2 is bits [29-31), from least to most significant, 0-indexed
Reserved2 uint8
MoreFragments bool
Identification uint32
}
// LayerType returns LayerTypeIPv6Fragment.
func (i *IPv6Fragment) LayerType() gopacket.LayerType { return LayerTypeIPv6Fragment }
func decodeIPv6Fragment(data []byte, p gopacket.PacketBuilder) error {
if len(data) < 8 {
p.SetTruncated()
return fmt.Errorf("Invalid ip6-fragment header. Length %d less than 8", len(data))
}
i := &IPv6Fragment{
BaseLayer: BaseLayer{data[:8], data[8:]},
NextHeader: IPProtocol(data[0]),
Reserved1: data[1],
FragmentOffset: binary.BigEndian.Uint16(data[2:4]) >> 3,
Reserved2: data[3] & 0x6 >> 1,
MoreFragments: data[3]&0x1 != 0,
Identification: binary.BigEndian.Uint32(data[4:8]),
}
p.AddLayer(i)
return p.NextDecoder(gopacket.DecodeFragment)
}
// IPv6DestinationOption is a TLV option present in an IPv6 destination options extension.
type IPv6DestinationOption ipv6HeaderTLVOption
// IPv6Destination is the IPv6 destination options header.
type IPv6Destination struct {
ipv6ExtensionBase
Options []*IPv6DestinationOption
}
// LayerType returns LayerTypeIPv6Destination.
func (i *IPv6Destination) LayerType() gopacket.LayerType { return LayerTypeIPv6Destination }
// DecodeFromBytes implementation according to gopacket.DecodingLayer
func (i *IPv6Destination) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
var err error
i.ipv6ExtensionBase, err = decodeIPv6ExtensionBase(data, df)
if err != nil {
return err
}
offset := 2
for offset < i.ActualLength {
opt := decodeIPv6HeaderTLVOption(data[offset:])
i.Options = append(i.Options, (*IPv6DestinationOption)(opt))
offset += opt.ActualLength
}
return nil
}
func decodeIPv6Destination(data []byte, p gopacket.PacketBuilder) error {
i := &IPv6Destination{}
err := i.DecodeFromBytes(data, p)
p.AddLayer(i)
if err != nil {
return err
}
return p.NextDecoder(i.NextHeader)
}
// 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 *IPv6Destination) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
var bytes []byte
var err error
o := make([]*ipv6HeaderTLVOption, 0, len(i.Options))
for _, v := range i.Options {
o = append(o, (*ipv6HeaderTLVOption)(v))
}
l := serializeIPv6HeaderTLVOptions(nil, o, opts.FixLengths)
bytes, err = b.PrependBytes(l)
if err != nil {
return err
}
serializeIPv6HeaderTLVOptions(bytes, o, opts.FixLengths)
length := len(bytes) + 2
if length%8 != 0 {
return errors.New("IPv6Destination actual length must be multiple of 8")
}
bytes, err = b.PrependBytes(2)
if err != nil {
return err
}
bytes[0] = uint8(i.NextHeader)
if opts.FixLengths {
i.HeaderLength = uint8((length / 8) - 1)
}
bytes[1] = uint8(i.HeaderLength)
return nil
}
func checkIPv6Address(addr net.IP) error {
if len(addr) == net.IPv6len {
return nil
}
if len(addr) == net.IPv4len {
return errors.New("address is IPv4")
}
return fmt.Errorf("wrong length of %d bytes instead of %d", len(addr), net.IPv6len)
}
// AddressTo16 ensures IPv6.SrcIP and IPv6.DstIP are actually IPv6 addresses (i.e. 16 byte addresses)
func (ipv6 *IPv6) AddressTo16() error {
if err := checkIPv6Address(ipv6.SrcIP); err != nil {
return fmt.Errorf("Invalid source IPv6 address (%s)", err)
}
if err := checkIPv6Address(ipv6.DstIP); err != nil {
return fmt.Errorf("Invalid destination IPv6 address (%s)", err)
}
return nil
}