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