620 lines
19 KiB
Go
620 lines
19 KiB
Go
// Copyright 2018 GoPacket Authors. 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"
|
|
"math"
|
|
"net"
|
|
"time"
|
|
|
|
"github.com/google/gopacket"
|
|
)
|
|
|
|
const (
|
|
// S Flag bit is 1
|
|
mldv2STrue uint8 = 0x8
|
|
|
|
// S Flag value mask
|
|
// mldv2STrue & mldv2SMask == mldv2STrue // true
|
|
// 0x1 & mldv2SMask == mldv2STrue // true
|
|
// 0x0 & mldv2SMask == mldv2STrue // false
|
|
mldv2SMask uint8 = 0x8
|
|
|
|
// QRV value mask
|
|
mldv2QRVMask uint8 = 0x7
|
|
)
|
|
|
|
// MLDv2MulticastListenerQueryMessage are sent by multicast routers to query the
|
|
// multicast listening state of neighboring interfaces.
|
|
// https://tools.ietf.org/html/rfc3810#section-5.1
|
|
//
|
|
// Some information, like Maximum Response Code and Multicast Address are in the
|
|
// previous layer LayerTypeMLDv1MulticastListenerQuery
|
|
type MLDv2MulticastListenerQueryMessage struct {
|
|
BaseLayer
|
|
// 5.1.3. Maximum Response Delay COde
|
|
MaximumResponseCode uint16
|
|
// 5.1.5. Multicast Address
|
|
// Zero in general query
|
|
// Specific IPv6 multicast address otherwise
|
|
MulticastAddress net.IP
|
|
// 5.1.7. S Flag (Suppress Router-Side Processing)
|
|
SuppressRoutersideProcessing bool
|
|
// 5.1.8. QRV (Querier's Robustness Variable)
|
|
QueriersRobustnessVariable uint8
|
|
// 5.1.9. QQIC (Querier's Query Interval Code)
|
|
QueriersQueryIntervalCode uint8
|
|
// 5.1.10. Number of Sources (N)
|
|
NumberOfSources uint16
|
|
// 5.1.11 Source Address [i]
|
|
SourceAddresses []net.IP
|
|
}
|
|
|
|
// DecodeFromBytes decodes the given bytes into this layer.
|
|
func (m *MLDv2MulticastListenerQueryMessage) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
|
if len(data) < 24 {
|
|
df.SetTruncated()
|
|
return errors.New("ICMP layer less than 24 bytes for Multicast Listener Query Message V2")
|
|
}
|
|
|
|
m.MaximumResponseCode = binary.BigEndian.Uint16(data[0:2])
|
|
// ignore data[2:4] as per https://tools.ietf.org/html/rfc3810#section-5.1.4
|
|
m.MulticastAddress = data[4:20]
|
|
m.SuppressRoutersideProcessing = (data[20] & mldv2SMask) == mldv2STrue
|
|
m.QueriersRobustnessVariable = data[20] & mldv2QRVMask
|
|
m.QueriersQueryIntervalCode = data[21]
|
|
|
|
m.NumberOfSources = binary.BigEndian.Uint16(data[22:24])
|
|
|
|
var end int
|
|
for i := uint16(0); i < m.NumberOfSources; i++ {
|
|
begin := 24 + (int(i) * 16)
|
|
end = begin + 16
|
|
|
|
if end > len(data) {
|
|
df.SetTruncated()
|
|
return fmt.Errorf("ICMP layer less than %d bytes for Multicast Listener Query Message V2", end)
|
|
}
|
|
|
|
m.SourceAddresses = append(m.SourceAddresses, data[begin:end])
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// NextLayerType returns the layer type contained by this DecodingLayer.
|
|
func (*MLDv2MulticastListenerQueryMessage) NextLayerType() gopacket.LayerType {
|
|
return gopacket.LayerTypeZero
|
|
}
|
|
|
|
// SerializeTo writes the serialized form of this layer into the
|
|
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
|
// See the docs for gopacket.SerializableLayer for more info.
|
|
func (m *MLDv2MulticastListenerQueryMessage) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
|
if err := m.serializeSourceAddressesTo(b, opts); err != nil {
|
|
return err
|
|
}
|
|
|
|
buf, err := b.PrependBytes(24)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
binary.BigEndian.PutUint16(buf[0:2], m.MaximumResponseCode)
|
|
copy(buf[2:4], []byte{0x00, 0x00}) // set reserved bytes to zero
|
|
|
|
ma16 := m.MulticastAddress.To16()
|
|
if ma16 == nil {
|
|
return fmt.Errorf("invalid MulticastAddress '%s'", m.MulticastAddress)
|
|
}
|
|
copy(buf[4:20], ma16)
|
|
|
|
byte20 := m.QueriersRobustnessVariable & mldv2QRVMask
|
|
if m.SuppressRoutersideProcessing {
|
|
byte20 |= mldv2STrue
|
|
} else {
|
|
byte20 &= ^mldv2STrue // the complement of mldv2STrue
|
|
}
|
|
byte20 &= 0x0F // set reserved bits to zero
|
|
buf[20] = byte20
|
|
|
|
binary.BigEndian.PutUint16(buf[22:24], m.NumberOfSources)
|
|
buf[21] = m.QueriersQueryIntervalCode
|
|
|
|
return nil
|
|
}
|
|
|
|
// writes each source address to the buffer preserving the order
|
|
func (m *MLDv2MulticastListenerQueryMessage) serializeSourceAddressesTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
|
numberOfSourceAddresses := len(m.SourceAddresses)
|
|
if numberOfSourceAddresses > math.MaxUint16 {
|
|
return fmt.Errorf(
|
|
"there are more than %d source addresses, but 65535 is the maximum number of supported addresses",
|
|
numberOfSourceAddresses)
|
|
}
|
|
|
|
if opts.FixLengths {
|
|
m.NumberOfSources = uint16(numberOfSourceAddresses)
|
|
}
|
|
|
|
lastSAIdx := numberOfSourceAddresses - 1
|
|
for k := range m.SourceAddresses {
|
|
i := lastSAIdx - k // reverse order
|
|
|
|
buf, err := b.PrependBytes(16)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
sa16 := m.SourceAddresses[i].To16()
|
|
if sa16 == nil {
|
|
return fmt.Errorf("invalid source address [%d] '%s'", i, m.SourceAddresses[i])
|
|
}
|
|
copy(buf[0:16], sa16)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// String sums this layer up nicely formatted
|
|
func (m *MLDv2MulticastListenerQueryMessage) String() string {
|
|
return fmt.Sprintf(
|
|
"Maximum Response Code: %#x (%dms), Multicast Address: %s, Suppress Routerside Processing: %t, QRV: %#x, QQIC: %#x (%ds), Number of Source Address: %d (actual: %d), Source Addresses: %s",
|
|
m.MaximumResponseCode,
|
|
m.MaximumResponseDelay(),
|
|
m.MulticastAddress,
|
|
m.SuppressRoutersideProcessing,
|
|
m.QueriersRobustnessVariable,
|
|
m.QueriersQueryIntervalCode,
|
|
m.QQI()/time.Second,
|
|
m.NumberOfSources,
|
|
len(m.SourceAddresses),
|
|
m.SourceAddresses)
|
|
}
|
|
|
|
// LayerType returns LayerTypeMLDv2MulticastListenerQuery.
|
|
func (*MLDv2MulticastListenerQueryMessage) LayerType() gopacket.LayerType {
|
|
return LayerTypeMLDv2MulticastListenerQuery
|
|
}
|
|
|
|
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
|
func (*MLDv2MulticastListenerQueryMessage) CanDecode() gopacket.LayerClass {
|
|
return LayerTypeMLDv2MulticastListenerQuery
|
|
}
|
|
|
|
// QQI calculates the Querier's Query Interval based on the QQIC
|
|
// according to https://tools.ietf.org/html/rfc3810#section-5.1.9
|
|
func (m *MLDv2MulticastListenerQueryMessage) QQI() time.Duration {
|
|
data := m.QueriersQueryIntervalCode
|
|
if data < 128 {
|
|
return time.Second * time.Duration(data)
|
|
}
|
|
|
|
exp := uint16(data) & 0x70 >> 4
|
|
mant := uint16(data) & 0x0F
|
|
return time.Second * time.Duration(mant|0x1000<<(exp+3))
|
|
}
|
|
|
|
// SetQQI calculates and updates the Querier's Query Interval Code (QQIC)
|
|
// according to https://tools.ietf.org/html/rfc3810#section-5.1.9
|
|
func (m *MLDv2MulticastListenerQueryMessage) SetQQI(d time.Duration) error {
|
|
if d < 0 {
|
|
m.QueriersQueryIntervalCode = 0
|
|
return errors.New("QQI duration is negative")
|
|
}
|
|
|
|
if d == 0 {
|
|
m.QueriersQueryIntervalCode = 0
|
|
return nil
|
|
}
|
|
|
|
dms := d / time.Second
|
|
if dms < 128 {
|
|
m.QueriersQueryIntervalCode = uint8(dms)
|
|
}
|
|
|
|
if dms > 31744 { // mant=0xF, exp=0x7
|
|
m.QueriersQueryIntervalCode = 0xFF
|
|
return fmt.Errorf("QQI duration %ds is, maximum allowed is 31744s", dms)
|
|
}
|
|
|
|
value := uint16(dms) // ok, because 31744 < math.MaxUint16
|
|
exp := uint8(7)
|
|
for mask := uint16(0x4000); exp > 0; exp-- {
|
|
if mask&value != 0 {
|
|
break
|
|
}
|
|
|
|
mask >>= 1
|
|
}
|
|
|
|
mant := uint8(0x000F & (value >> (exp + 3)))
|
|
sig := uint8(0x10)
|
|
m.QueriersQueryIntervalCode = sig | exp<<4 | mant
|
|
|
|
return nil
|
|
}
|
|
|
|
// MaximumResponseDelay returns the Maximum Response Delay based on the
|
|
// Maximum Response Code according to
|
|
// https://tools.ietf.org/html/rfc3810#section-5.1.3
|
|
func (m *MLDv2MulticastListenerQueryMessage) MaximumResponseDelay() time.Duration {
|
|
if m.MaximumResponseCode < 0x8000 {
|
|
return time.Duration(m.MaximumResponseCode)
|
|
}
|
|
|
|
exp := m.MaximumResponseCode & 0x7000 >> 12
|
|
mant := m.MaximumResponseCode & 0x0FFF
|
|
|
|
return time.Millisecond * time.Duration(mant|0x1000<<(exp+3))
|
|
}
|
|
|
|
// SetMLDv2MaximumResponseDelay updates the Maximum Response Code according to
|
|
// https://tools.ietf.org/html/rfc3810#section-5.1.3
|
|
func (m *MLDv2MulticastListenerQueryMessage) SetMLDv2MaximumResponseDelay(d time.Duration) error {
|
|
if d == 0 {
|
|
m.MaximumResponseCode = 0
|
|
return nil
|
|
}
|
|
|
|
if d < 0 {
|
|
return errors.New("maximum response delay must not be negative")
|
|
}
|
|
|
|
dms := d / time.Millisecond
|
|
|
|
if dms < 32768 {
|
|
m.MaximumResponseCode = uint16(dms)
|
|
}
|
|
|
|
if dms > 4193280 { // mant=0xFFF, exp=0x7
|
|
return fmt.Errorf("maximum response delay %dms is bigger the than maximum of 4193280ms", dms)
|
|
}
|
|
|
|
value := uint32(dms) // ok, because 4193280 < math.MaxUint32
|
|
exp := uint8(7)
|
|
for mask := uint32(0x40000000); exp > 0; exp-- {
|
|
if mask&value != 0 {
|
|
break
|
|
}
|
|
|
|
mask >>= 1
|
|
}
|
|
|
|
mant := uint16(0x00000FFF & (value >> (exp + 3)))
|
|
sig := uint16(0x1000)
|
|
m.MaximumResponseCode = sig | uint16(exp)<<12 | mant
|
|
return nil
|
|
}
|
|
|
|
// MLDv2MulticastListenerReportMessage is sent by an IP node to report the
|
|
// current multicast listening state, or changes therein.
|
|
// https://tools.ietf.org/html/rfc3810#section-5.2
|
|
type MLDv2MulticastListenerReportMessage struct {
|
|
BaseLayer
|
|
// 5.2.3. Nr of Mcast Address Records
|
|
NumberOfMulticastAddressRecords uint16
|
|
// 5.2.4. Multicast Address Record [i]
|
|
MulticastAddressRecords []MLDv2MulticastAddressRecord
|
|
}
|
|
|
|
// DecodeFromBytes decodes the given bytes into this layer.
|
|
func (m *MLDv2MulticastListenerReportMessage) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
|
if len(data) < 4 {
|
|
df.SetTruncated()
|
|
return errors.New("ICMP layer less than 4 bytes for Multicast Listener Report Message V2")
|
|
}
|
|
|
|
// ignore data[0:2] as per RFC
|
|
// https://tools.ietf.org/html/rfc3810#section-5.2.1
|
|
m.NumberOfMulticastAddressRecords = binary.BigEndian.Uint16(data[2:4])
|
|
|
|
begin := 4
|
|
for i := uint16(0); i < m.NumberOfMulticastAddressRecords; i++ {
|
|
mar := MLDv2MulticastAddressRecord{}
|
|
read, err := mar.decode(data[begin:], df)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
m.MulticastAddressRecords = append(m.MulticastAddressRecords, mar)
|
|
|
|
begin += read
|
|
}
|
|
|
|
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 (m *MLDv2MulticastListenerReportMessage) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
|
lastItemIdx := len(m.MulticastAddressRecords) - 1
|
|
for k := range m.MulticastAddressRecords {
|
|
i := lastItemIdx - k // reverse order
|
|
|
|
err := m.MulticastAddressRecords[i].serializeTo(b, opts)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if opts.FixLengths {
|
|
numberOfMAR := len(m.MulticastAddressRecords)
|
|
if numberOfMAR > math.MaxUint16 {
|
|
return fmt.Errorf(
|
|
"%d multicast address records added, but the maximum is 65535",
|
|
numberOfMAR)
|
|
}
|
|
|
|
m.NumberOfMulticastAddressRecords = uint16(numberOfMAR)
|
|
}
|
|
|
|
buf, err := b.PrependBytes(4)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
copy(buf[0:2], []byte{0x0, 0x0})
|
|
binary.BigEndian.PutUint16(buf[2:4], m.NumberOfMulticastAddressRecords)
|
|
return nil
|
|
}
|
|
|
|
// Sums this layer up nicely formatted
|
|
func (m *MLDv2MulticastListenerReportMessage) String() string {
|
|
return fmt.Sprintf(
|
|
"Number of Mcast Addr Records: %d (actual %d), Multicast Address Records: %+v",
|
|
m.NumberOfMulticastAddressRecords,
|
|
len(m.MulticastAddressRecords),
|
|
m.MulticastAddressRecords)
|
|
}
|
|
|
|
// LayerType returns LayerTypeMLDv2MulticastListenerQuery.
|
|
func (*MLDv2MulticastListenerReportMessage) LayerType() gopacket.LayerType {
|
|
return LayerTypeMLDv2MulticastListenerReport
|
|
}
|
|
|
|
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
|
func (*MLDv2MulticastListenerReportMessage) CanDecode() gopacket.LayerClass {
|
|
return LayerTypeMLDv2MulticastListenerReport
|
|
}
|
|
|
|
// NextLayerType returns the layer type contained by this DecodingLayer.
|
|
func (*MLDv2MulticastListenerReportMessage) NextLayerType() gopacket.LayerType {
|
|
return gopacket.LayerTypePayload
|
|
}
|
|
|
|
// MLDv2MulticastAddressRecordType holds the type of a
|
|
// Multicast Address Record, according to
|
|
// https://tools.ietf.org/html/rfc3810#section-5.2.5 and
|
|
// https://tools.ietf.org/html/rfc3810#section-5.2.12
|
|
type MLDv2MulticastAddressRecordType uint8
|
|
|
|
const (
|
|
// MLDv2MulticastAddressRecordTypeModeIsIncluded stands for
|
|
// MODE_IS_INCLUDE - indicates that the interface has a filter
|
|
// mode of INCLUDE for the specified multicast address.
|
|
MLDv2MulticastAddressRecordTypeModeIsIncluded MLDv2MulticastAddressRecordType = 1
|
|
// MLDv2MulticastAddressRecordTypeModeIsExcluded stands for
|
|
// MODE_IS_EXCLUDE - indicates that the interface has a filter
|
|
// mode of EXCLUDE for the specified multicast address.
|
|
MLDv2MulticastAddressRecordTypeModeIsExcluded MLDv2MulticastAddressRecordType = 2
|
|
// MLDv2MulticastAddressRecordTypeChangeToIncludeMode stands for
|
|
// CHANGE_TO_INCLUDE_MODE - indicates that the interface has
|
|
// changed to INCLUDE filter mode for the specified multicast
|
|
// address.
|
|
MLDv2MulticastAddressRecordTypeChangeToIncludeMode MLDv2MulticastAddressRecordType = 3
|
|
// MLDv2MulticastAddressRecordTypeChangeToExcludeMode stands for
|
|
// CHANGE_TO_EXCLUDE_MODE - indicates that the interface has
|
|
// changed to EXCLUDE filter mode for the specified multicast
|
|
// address
|
|
MLDv2MulticastAddressRecordTypeChangeToExcludeMode MLDv2MulticastAddressRecordType = 4
|
|
// MLDv2MulticastAddressRecordTypeAllowNewSources stands for
|
|
// ALLOW_NEW_SOURCES - indicates that the Source Address [i]
|
|
// fields in this Multicast Address Record contain a list of
|
|
// the additional sources that the node wishes to listen to,
|
|
// for packets sent to the specified multicast address.
|
|
MLDv2MulticastAddressRecordTypeAllowNewSources MLDv2MulticastAddressRecordType = 5
|
|
// MLDv2MulticastAddressRecordTypeBlockOldSources stands for
|
|
// BLOCK_OLD_SOURCES - indicates that the Source Address [i]
|
|
// fields in this Multicast Address Record contain a list of
|
|
// the sources that the node no longer wishes to listen to,
|
|
// for packets sent to the specified multicast address.
|
|
MLDv2MulticastAddressRecordTypeBlockOldSources MLDv2MulticastAddressRecordType = 6
|
|
)
|
|
|
|
// Human readable record types
|
|
// Naming follows https://tools.ietf.org/html/rfc3810#section-5.2.12
|
|
func (m MLDv2MulticastAddressRecordType) String() string {
|
|
switch m {
|
|
case MLDv2MulticastAddressRecordTypeModeIsIncluded:
|
|
return "MODE_IS_INCLUDE"
|
|
case MLDv2MulticastAddressRecordTypeModeIsExcluded:
|
|
return "MODE_IS_EXCLUDE"
|
|
case MLDv2MulticastAddressRecordTypeChangeToIncludeMode:
|
|
return "CHANGE_TO_INCLUDE_MODE"
|
|
case MLDv2MulticastAddressRecordTypeChangeToExcludeMode:
|
|
return "CHANGE_TO_EXCLUDE_MODE"
|
|
case MLDv2MulticastAddressRecordTypeAllowNewSources:
|
|
return "ALLOW_NEW_SOURCES"
|
|
case MLDv2MulticastAddressRecordTypeBlockOldSources:
|
|
return "BLOCK_OLD_SOURCES"
|
|
default:
|
|
return fmt.Sprintf("UNKNOWN(%d)", m)
|
|
}
|
|
}
|
|
|
|
// MLDv2MulticastAddressRecord contains information on the sender listening to a
|
|
// single multicast address on the interface the report is sent.
|
|
// https://tools.ietf.org/html/rfc3810#section-5.2.4
|
|
type MLDv2MulticastAddressRecord struct {
|
|
// 5.2.5. Record Type
|
|
RecordType MLDv2MulticastAddressRecordType
|
|
// 5.2.6. Auxiliary Data Length (number of 32-bit words)
|
|
AuxDataLen uint8
|
|
// 5.2.7. Number Of Sources (N)
|
|
N uint16
|
|
// 5.2.8. Multicast Address
|
|
MulticastAddress net.IP
|
|
// 5.2.9 Source Address [i]
|
|
SourceAddresses []net.IP
|
|
// 5.2.10 Auxiliary Data
|
|
AuxiliaryData []byte
|
|
}
|
|
|
|
// decodes a multicast address record from bytes
|
|
func (m *MLDv2MulticastAddressRecord) decode(data []byte, df gopacket.DecodeFeedback) (int, error) {
|
|
if len(data) < 20 {
|
|
df.SetTruncated()
|
|
return 0, errors.New(
|
|
"Multicast Listener Report Message V2 layer less than 4 bytes for Multicast Address Record")
|
|
}
|
|
|
|
m.RecordType = MLDv2MulticastAddressRecordType(data[0])
|
|
m.AuxDataLen = data[1]
|
|
m.N = binary.BigEndian.Uint16(data[2:4])
|
|
m.MulticastAddress = data[4:20]
|
|
|
|
for i := uint16(0); i < m.N; i++ {
|
|
begin := 20 + (int(i) * 16)
|
|
end := begin + 16
|
|
|
|
if len(data) < end {
|
|
df.SetTruncated()
|
|
return begin, fmt.Errorf(
|
|
"Multicast Listener Report Message V2 layer less than %d bytes for Multicast Address Record", end)
|
|
}
|
|
|
|
m.SourceAddresses = append(m.SourceAddresses, data[begin:end])
|
|
}
|
|
|
|
expectedLengthWithouAuxData := 20 + (int(m.N) * 16)
|
|
expectedTotalLength := (int(m.AuxDataLen) * 4) + expectedLengthWithouAuxData // *4 because AuxDataLen are 32bit words
|
|
if len(data) < expectedTotalLength {
|
|
return expectedLengthWithouAuxData, fmt.Errorf(
|
|
"Multicast Listener Report Message V2 layer less than %d bytes for Multicast Address Record",
|
|
expectedLengthWithouAuxData)
|
|
}
|
|
|
|
m.AuxiliaryData = data[expectedLengthWithouAuxData:expectedTotalLength]
|
|
|
|
return expectedTotalLength, nil
|
|
}
|
|
|
|
// String sums this layer up nicely formatted
|
|
func (m *MLDv2MulticastAddressRecord) String() string {
|
|
return fmt.Sprintf(
|
|
"RecordType: %d (%s), AuxDataLen: %d [32-bit words], N: %d, Multicast Address: %s, SourceAddresses: %s, Auxiliary Data: %#x",
|
|
m.RecordType,
|
|
m.RecordType.String(),
|
|
m.AuxDataLen,
|
|
m.N,
|
|
m.MulticastAddress.To16(),
|
|
m.SourceAddresses,
|
|
m.AuxiliaryData)
|
|
}
|
|
|
|
// serializes a multicast address record
|
|
func (m *MLDv2MulticastAddressRecord) serializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
|
if err := m.serializeAuxiliaryDataTo(b, opts); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := m.serializeSourceAddressesTo(b, opts); err != nil {
|
|
return err
|
|
}
|
|
|
|
buf, err := b.PrependBytes(20)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
buf[0] = uint8(m.RecordType)
|
|
buf[1] = m.AuxDataLen
|
|
binary.BigEndian.PutUint16(buf[2:4], m.N)
|
|
|
|
ma16 := m.MulticastAddress.To16()
|
|
if ma16 == nil {
|
|
return fmt.Errorf("invalid multicast address '%s'", m.MulticastAddress)
|
|
}
|
|
copy(buf[4:20], ma16)
|
|
|
|
return nil
|
|
}
|
|
|
|
// serializes the auxiliary data of a multicast address record
|
|
func (m *MLDv2MulticastAddressRecord) serializeAuxiliaryDataTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
|
if remainder := len(m.AuxiliaryData) % 4; remainder != 0 {
|
|
zeroWord := []byte{0x0, 0x0, 0x0, 0x0}
|
|
m.AuxiliaryData = append(m.AuxiliaryData, zeroWord[:remainder]...)
|
|
}
|
|
|
|
if opts.FixLengths {
|
|
auxDataLen := len(m.AuxiliaryData) / 4
|
|
|
|
if auxDataLen > math.MaxUint8 {
|
|
return fmt.Errorf("auxilary data is %d 32-bit words, but the maximum is 255 32-bit words", auxDataLen)
|
|
}
|
|
|
|
m.AuxDataLen = uint8(auxDataLen)
|
|
}
|
|
|
|
buf, err := b.PrependBytes(len(m.AuxiliaryData))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
copy(buf, m.AuxiliaryData)
|
|
return nil
|
|
}
|
|
|
|
// serializes the source addresses of a multicast address record preserving the order
|
|
func (m *MLDv2MulticastAddressRecord) serializeSourceAddressesTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
|
if opts.FixLengths {
|
|
numberOfSourceAddresses := len(m.SourceAddresses)
|
|
|
|
if numberOfSourceAddresses > math.MaxUint16 {
|
|
return fmt.Errorf(
|
|
"%d source addresses added, but the maximum is 65535",
|
|
numberOfSourceAddresses)
|
|
}
|
|
|
|
m.N = uint16(numberOfSourceAddresses)
|
|
}
|
|
|
|
lastItemIdx := len(m.SourceAddresses) - 1
|
|
for k := range m.SourceAddresses {
|
|
i := lastItemIdx - k // reverse order
|
|
|
|
buf, err := b.PrependBytes(16)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
sa16 := m.SourceAddresses[i].To16()
|
|
if sa16 == nil {
|
|
return fmt.Errorf("invalid source address [%d] '%s'", i, m.SourceAddresses[i])
|
|
}
|
|
copy(buf, sa16)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func decodeMLDv2MulticastListenerReport(data []byte, p gopacket.PacketBuilder) error {
|
|
m := &MLDv2MulticastListenerReportMessage{}
|
|
return decodingLayerDecoder(m, data, p)
|
|
}
|
|
|
|
func decodeMLDv2MulticastListenerQuery(data []byte, p gopacket.PacketBuilder) error {
|
|
m := &MLDv2MulticastListenerQueryMessage{}
|
|
return decodingLayerDecoder(m, data, p)
|
|
}
|