[VOL-1349] EPON OLT adapter (package B)

Change-Id: I634ef62c53813dcf4456f54948f13e06358e263c
diff --git a/vendor/github.com/google/gopacket/layers/ip6.go b/vendor/github.com/google/gopacket/layers/ip6.go
new file mode 100644
index 0000000..70e9c8d
--- /dev/null
+++ b/vendor/github.com/google/gopacket/layers/ip6.go
@@ -0,0 +1,707 @@
+// 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
+}