| // 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) |
| } |