blob: 248cf749efbc02bba289f88aa681ef68f2cb4dc5 [file] [log] [blame]
Takahiro Suzuki241c10e2020-12-17 20:17:57 +09001// Copyright 2018 GoPacket Authors. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style license
4// that can be found in the LICENSE file in the root of the source
5// tree.
6
7package layers
8
9import (
10 "encoding/binary"
11 "errors"
12 "fmt"
13 "math"
14 "net"
15 "time"
16
17 "github.com/google/gopacket"
18)
19
20const (
21 // S Flag bit is 1
22 mldv2STrue uint8 = 0x8
23
24 // S Flag value mask
25 // mldv2STrue & mldv2SMask == mldv2STrue // true
26 // 0x1 & mldv2SMask == mldv2STrue // true
27 // 0x0 & mldv2SMask == mldv2STrue // false
28 mldv2SMask uint8 = 0x8
29
30 // QRV value mask
31 mldv2QRVMask uint8 = 0x7
32)
33
34// MLDv2MulticastListenerQueryMessage are sent by multicast routers to query the
35// multicast listening state of neighboring interfaces.
36// https://tools.ietf.org/html/rfc3810#section-5.1
37//
38// Some information, like Maximum Response Code and Multicast Address are in the
39// previous layer LayerTypeMLDv1MulticastListenerQuery
40type MLDv2MulticastListenerQueryMessage struct {
41 BaseLayer
42 // 5.1.3. Maximum Response Delay COde
43 MaximumResponseCode uint16
44 // 5.1.5. Multicast Address
45 // Zero in general query
46 // Specific IPv6 multicast address otherwise
47 MulticastAddress net.IP
48 // 5.1.7. S Flag (Suppress Router-Side Processing)
49 SuppressRoutersideProcessing bool
50 // 5.1.8. QRV (Querier's Robustness Variable)
51 QueriersRobustnessVariable uint8
52 // 5.1.9. QQIC (Querier's Query Interval Code)
53 QueriersQueryIntervalCode uint8
54 // 5.1.10. Number of Sources (N)
55 NumberOfSources uint16
56 // 5.1.11 Source Address [i]
57 SourceAddresses []net.IP
58}
59
60// DecodeFromBytes decodes the given bytes into this layer.
61func (m *MLDv2MulticastListenerQueryMessage) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
62 if len(data) < 24 {
63 df.SetTruncated()
64 return errors.New("ICMP layer less than 24 bytes for Multicast Listener Query Message V2")
65 }
66
67 m.MaximumResponseCode = binary.BigEndian.Uint16(data[0:2])
68 // ignore data[2:4] as per https://tools.ietf.org/html/rfc3810#section-5.1.4
69 m.MulticastAddress = data[4:20]
70 m.SuppressRoutersideProcessing = (data[20] & mldv2SMask) == mldv2STrue
71 m.QueriersRobustnessVariable = data[20] & mldv2QRVMask
72 m.QueriersQueryIntervalCode = data[21]
73
74 m.NumberOfSources = binary.BigEndian.Uint16(data[22:24])
75
76 var end int
77 for i := uint16(0); i < m.NumberOfSources; i++ {
78 begin := 24 + (int(i) * 16)
79 end = begin + 16
80
81 if end > len(data) {
82 df.SetTruncated()
83 return fmt.Errorf("ICMP layer less than %d bytes for Multicast Listener Query Message V2", end)
84 }
85
86 m.SourceAddresses = append(m.SourceAddresses, data[begin:end])
87 }
88
89 return nil
90}
91
92// NextLayerType returns the layer type contained by this DecodingLayer.
93func (*MLDv2MulticastListenerQueryMessage) NextLayerType() gopacket.LayerType {
94 return gopacket.LayerTypeZero
95}
96
97// SerializeTo writes the serialized form of this layer into the
98// SerializationBuffer, implementing gopacket.SerializableLayer.
99// See the docs for gopacket.SerializableLayer for more info.
100func (m *MLDv2MulticastListenerQueryMessage) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
101 if err := m.serializeSourceAddressesTo(b, opts); err != nil {
102 return err
103 }
104
105 buf, err := b.PrependBytes(24)
106 if err != nil {
107 return err
108 }
109
110 binary.BigEndian.PutUint16(buf[0:2], m.MaximumResponseCode)
111 copy(buf[2:4], []byte{0x00, 0x00}) // set reserved bytes to zero
112
113 ma16 := m.MulticastAddress.To16()
114 if ma16 == nil {
115 return fmt.Errorf("invalid MulticastAddress '%s'", m.MulticastAddress)
116 }
117 copy(buf[4:20], ma16)
118
119 byte20 := m.QueriersRobustnessVariable & mldv2QRVMask
120 if m.SuppressRoutersideProcessing {
121 byte20 |= mldv2STrue
122 } else {
123 byte20 &= ^mldv2STrue // the complement of mldv2STrue
124 }
125 byte20 &= 0x0F // set reserved bits to zero
126 buf[20] = byte20
127
128 binary.BigEndian.PutUint16(buf[22:24], m.NumberOfSources)
129 buf[21] = m.QueriersQueryIntervalCode
130
131 return nil
132}
133
134// writes each source address to the buffer preserving the order
135func (m *MLDv2MulticastListenerQueryMessage) serializeSourceAddressesTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
136 numberOfSourceAddresses := len(m.SourceAddresses)
137 if numberOfSourceAddresses > math.MaxUint16 {
138 return fmt.Errorf(
139 "there are more than %d source addresses, but 65535 is the maximum number of supported addresses",
140 numberOfSourceAddresses)
141 }
142
143 if opts.FixLengths {
144 m.NumberOfSources = uint16(numberOfSourceAddresses)
145 }
146
147 lastSAIdx := numberOfSourceAddresses - 1
148 for k := range m.SourceAddresses {
149 i := lastSAIdx - k // reverse order
150
151 buf, err := b.PrependBytes(16)
152 if err != nil {
153 return err
154 }
155
156 sa16 := m.SourceAddresses[i].To16()
157 if sa16 == nil {
158 return fmt.Errorf("invalid source address [%d] '%s'", i, m.SourceAddresses[i])
159 }
160 copy(buf[0:16], sa16)
161 }
162
163 return nil
164}
165
166// String sums this layer up nicely formatted
167func (m *MLDv2MulticastListenerQueryMessage) String() string {
168 return fmt.Sprintf(
169 "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",
170 m.MaximumResponseCode,
171 m.MaximumResponseDelay(),
172 m.MulticastAddress,
173 m.SuppressRoutersideProcessing,
174 m.QueriersRobustnessVariable,
175 m.QueriersQueryIntervalCode,
176 m.QQI()/time.Second,
177 m.NumberOfSources,
178 len(m.SourceAddresses),
179 m.SourceAddresses)
180}
181
182// LayerType returns LayerTypeMLDv2MulticastListenerQuery.
183func (*MLDv2MulticastListenerQueryMessage) LayerType() gopacket.LayerType {
184 return LayerTypeMLDv2MulticastListenerQuery
185}
186
187// CanDecode returns the set of layer types that this DecodingLayer can decode.
188func (*MLDv2MulticastListenerQueryMessage) CanDecode() gopacket.LayerClass {
189 return LayerTypeMLDv2MulticastListenerQuery
190}
191
192// QQI calculates the Querier's Query Interval based on the QQIC
193// according to https://tools.ietf.org/html/rfc3810#section-5.1.9
194func (m *MLDv2MulticastListenerQueryMessage) QQI() time.Duration {
195 data := m.QueriersQueryIntervalCode
196 if data < 128 {
197 return time.Second * time.Duration(data)
198 }
199
200 exp := uint16(data) & 0x70 >> 4
201 mant := uint16(data) & 0x0F
202 return time.Second * time.Duration(mant|0x1000<<(exp+3))
203}
204
205// SetQQI calculates and updates the Querier's Query Interval Code (QQIC)
206// according to https://tools.ietf.org/html/rfc3810#section-5.1.9
207func (m *MLDv2MulticastListenerQueryMessage) SetQQI(d time.Duration) error {
208 if d < 0 {
209 m.QueriersQueryIntervalCode = 0
210 return errors.New("QQI duration is negative")
211 }
212
213 if d == 0 {
214 m.QueriersQueryIntervalCode = 0
215 return nil
216 }
217
218 dms := d / time.Second
219 if dms < 128 {
220 m.QueriersQueryIntervalCode = uint8(dms)
221 }
222
223 if dms > 31744 { // mant=0xF, exp=0x7
224 m.QueriersQueryIntervalCode = 0xFF
225 return fmt.Errorf("QQI duration %ds is, maximum allowed is 31744s", dms)
226 }
227
228 value := uint16(dms) // ok, because 31744 < math.MaxUint16
229 exp := uint8(7)
230 for mask := uint16(0x4000); exp > 0; exp-- {
231 if mask&value != 0 {
232 break
233 }
234
235 mask >>= 1
236 }
237
238 mant := uint8(0x000F & (value >> (exp + 3)))
239 sig := uint8(0x10)
240 m.QueriersQueryIntervalCode = sig | exp<<4 | mant
241
242 return nil
243}
244
245// MaximumResponseDelay returns the Maximum Response Delay based on the
246// Maximum Response Code according to
247// https://tools.ietf.org/html/rfc3810#section-5.1.3
248func (m *MLDv2MulticastListenerQueryMessage) MaximumResponseDelay() time.Duration {
249 if m.MaximumResponseCode < 0x8000 {
250 return time.Duration(m.MaximumResponseCode)
251 }
252
253 exp := m.MaximumResponseCode & 0x7000 >> 12
254 mant := m.MaximumResponseCode & 0x0FFF
255
256 return time.Millisecond * time.Duration(mant|0x1000<<(exp+3))
257}
258
259// SetMLDv2MaximumResponseDelay updates the Maximum Response Code according to
260// https://tools.ietf.org/html/rfc3810#section-5.1.3
261func (m *MLDv2MulticastListenerQueryMessage) SetMLDv2MaximumResponseDelay(d time.Duration) error {
262 if d == 0 {
263 m.MaximumResponseCode = 0
264 return nil
265 }
266
267 if d < 0 {
268 return errors.New("maximum response delay must not be negative")
269 }
270
271 dms := d / time.Millisecond
272
273 if dms < 32768 {
274 m.MaximumResponseCode = uint16(dms)
275 }
276
277 if dms > 4193280 { // mant=0xFFF, exp=0x7
278 return fmt.Errorf("maximum response delay %dms is bigger the than maximum of 4193280ms", dms)
279 }
280
281 value := uint32(dms) // ok, because 4193280 < math.MaxUint32
282 exp := uint8(7)
283 for mask := uint32(0x40000000); exp > 0; exp-- {
284 if mask&value != 0 {
285 break
286 }
287
288 mask >>= 1
289 }
290
291 mant := uint16(0x00000FFF & (value >> (exp + 3)))
292 sig := uint16(0x1000)
293 m.MaximumResponseCode = sig | uint16(exp)<<12 | mant
294 return nil
295}
296
297// MLDv2MulticastListenerReportMessage is sent by an IP node to report the
298// current multicast listening state, or changes therein.
299// https://tools.ietf.org/html/rfc3810#section-5.2
300type MLDv2MulticastListenerReportMessage struct {
301 BaseLayer
302 // 5.2.3. Nr of Mcast Address Records
303 NumberOfMulticastAddressRecords uint16
304 // 5.2.4. Multicast Address Record [i]
305 MulticastAddressRecords []MLDv2MulticastAddressRecord
306}
307
308// DecodeFromBytes decodes the given bytes into this layer.
309func (m *MLDv2MulticastListenerReportMessage) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
310 if len(data) < 4 {
311 df.SetTruncated()
312 return errors.New("ICMP layer less than 4 bytes for Multicast Listener Report Message V2")
313 }
314
315 // ignore data[0:2] as per RFC
316 // https://tools.ietf.org/html/rfc3810#section-5.2.1
317 m.NumberOfMulticastAddressRecords = binary.BigEndian.Uint16(data[2:4])
318
319 begin := 4
320 for i := uint16(0); i < m.NumberOfMulticastAddressRecords; i++ {
321 mar := MLDv2MulticastAddressRecord{}
322 read, err := mar.decode(data[begin:], df)
323 if err != nil {
324 return err
325 }
326
327 m.MulticastAddressRecords = append(m.MulticastAddressRecords, mar)
328
329 begin += read
330 }
331
332 return nil
333}
334
335// SerializeTo writes the serialized form of this layer into the
336// SerializationBuffer, implementing gopacket.SerializableLayer.
337// See the docs for gopacket.SerializableLayer for more info.
338func (m *MLDv2MulticastListenerReportMessage) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
339 lastItemIdx := len(m.MulticastAddressRecords) - 1
340 for k := range m.MulticastAddressRecords {
341 i := lastItemIdx - k // reverse order
342
343 err := m.MulticastAddressRecords[i].serializeTo(b, opts)
344 if err != nil {
345 return err
346 }
347 }
348
349 if opts.FixLengths {
350 numberOfMAR := len(m.MulticastAddressRecords)
351 if numberOfMAR > math.MaxUint16 {
352 return fmt.Errorf(
353 "%d multicast address records added, but the maximum is 65535",
354 numberOfMAR)
355 }
356
357 m.NumberOfMulticastAddressRecords = uint16(numberOfMAR)
358 }
359
360 buf, err := b.PrependBytes(4)
361 if err != nil {
362 return err
363 }
364
365 copy(buf[0:2], []byte{0x0, 0x0})
366 binary.BigEndian.PutUint16(buf[2:4], m.NumberOfMulticastAddressRecords)
367 return nil
368}
369
370// Sums this layer up nicely formatted
371func (m *MLDv2MulticastListenerReportMessage) String() string {
372 return fmt.Sprintf(
373 "Number of Mcast Addr Records: %d (actual %d), Multicast Address Records: %+v",
374 m.NumberOfMulticastAddressRecords,
375 len(m.MulticastAddressRecords),
376 m.MulticastAddressRecords)
377}
378
379// LayerType returns LayerTypeMLDv2MulticastListenerQuery.
380func (*MLDv2MulticastListenerReportMessage) LayerType() gopacket.LayerType {
381 return LayerTypeMLDv2MulticastListenerReport
382}
383
384// CanDecode returns the set of layer types that this DecodingLayer can decode.
385func (*MLDv2MulticastListenerReportMessage) CanDecode() gopacket.LayerClass {
386 return LayerTypeMLDv2MulticastListenerReport
387}
388
389// NextLayerType returns the layer type contained by this DecodingLayer.
390func (*MLDv2MulticastListenerReportMessage) NextLayerType() gopacket.LayerType {
391 return gopacket.LayerTypePayload
392}
393
394// MLDv2MulticastAddressRecordType holds the type of a
395// Multicast Address Record, according to
396// https://tools.ietf.org/html/rfc3810#section-5.2.5 and
397// https://tools.ietf.org/html/rfc3810#section-5.2.12
398type MLDv2MulticastAddressRecordType uint8
399
400const (
401 // MLDv2MulticastAddressRecordTypeModeIsIncluded stands for
402 // MODE_IS_INCLUDE - indicates that the interface has a filter
403 // mode of INCLUDE for the specified multicast address.
404 MLDv2MulticastAddressRecordTypeModeIsIncluded MLDv2MulticastAddressRecordType = 1
405 // MLDv2MulticastAddressRecordTypeModeIsExcluded stands for
406 // MODE_IS_EXCLUDE - indicates that the interface has a filter
407 // mode of EXCLUDE for the specified multicast address.
408 MLDv2MulticastAddressRecordTypeModeIsExcluded MLDv2MulticastAddressRecordType = 2
409 // MLDv2MulticastAddressRecordTypeChangeToIncludeMode stands for
410 // CHANGE_TO_INCLUDE_MODE - indicates that the interface has
411 // changed to INCLUDE filter mode for the specified multicast
412 // address.
413 MLDv2MulticastAddressRecordTypeChangeToIncludeMode MLDv2MulticastAddressRecordType = 3
414 // MLDv2MulticastAddressRecordTypeChangeToExcludeMode stands for
415 // CHANGE_TO_EXCLUDE_MODE - indicates that the interface has
416 // changed to EXCLUDE filter mode for the specified multicast
417 // address
418 MLDv2MulticastAddressRecordTypeChangeToExcludeMode MLDv2MulticastAddressRecordType = 4
419 // MLDv2MulticastAddressRecordTypeAllowNewSources stands for
420 // ALLOW_NEW_SOURCES - indicates that the Source Address [i]
421 // fields in this Multicast Address Record contain a list of
422 // the additional sources that the node wishes to listen to,
423 // for packets sent to the specified multicast address.
424 MLDv2MulticastAddressRecordTypeAllowNewSources MLDv2MulticastAddressRecordType = 5
425 // MLDv2MulticastAddressRecordTypeBlockOldSources stands for
426 // BLOCK_OLD_SOURCES - indicates that the Source Address [i]
427 // fields in this Multicast Address Record contain a list of
428 // the sources that the node no longer wishes to listen to,
429 // for packets sent to the specified multicast address.
430 MLDv2MulticastAddressRecordTypeBlockOldSources MLDv2MulticastAddressRecordType = 6
431)
432
433// Human readable record types
434// Naming follows https://tools.ietf.org/html/rfc3810#section-5.2.12
435func (m MLDv2MulticastAddressRecordType) String() string {
436 switch m {
437 case MLDv2MulticastAddressRecordTypeModeIsIncluded:
438 return "MODE_IS_INCLUDE"
439 case MLDv2MulticastAddressRecordTypeModeIsExcluded:
440 return "MODE_IS_EXCLUDE"
441 case MLDv2MulticastAddressRecordTypeChangeToIncludeMode:
442 return "CHANGE_TO_INCLUDE_MODE"
443 case MLDv2MulticastAddressRecordTypeChangeToExcludeMode:
444 return "CHANGE_TO_EXCLUDE_MODE"
445 case MLDv2MulticastAddressRecordTypeAllowNewSources:
446 return "ALLOW_NEW_SOURCES"
447 case MLDv2MulticastAddressRecordTypeBlockOldSources:
448 return "BLOCK_OLD_SOURCES"
449 default:
450 return fmt.Sprintf("UNKNOWN(%d)", m)
451 }
452}
453
454// MLDv2MulticastAddressRecord contains information on the sender listening to a
455// single multicast address on the interface the report is sent.
456// https://tools.ietf.org/html/rfc3810#section-5.2.4
457type MLDv2MulticastAddressRecord struct {
458 // 5.2.5. Record Type
459 RecordType MLDv2MulticastAddressRecordType
460 // 5.2.6. Auxiliary Data Length (number of 32-bit words)
461 AuxDataLen uint8
462 // 5.2.7. Number Of Sources (N)
463 N uint16
464 // 5.2.8. Multicast Address
465 MulticastAddress net.IP
466 // 5.2.9 Source Address [i]
467 SourceAddresses []net.IP
468 // 5.2.10 Auxiliary Data
469 AuxiliaryData []byte
470}
471
472// decodes a multicast address record from bytes
473func (m *MLDv2MulticastAddressRecord) decode(data []byte, df gopacket.DecodeFeedback) (int, error) {
474 if len(data) < 4 {
475 df.SetTruncated()
476 return 0, errors.New(
477 "Multicast Listener Report Message V2 layer less than 4 bytes for Multicast Address Record")
478 }
479
480 m.RecordType = MLDv2MulticastAddressRecordType(data[0])
481 m.AuxDataLen = data[1]
482 m.N = binary.BigEndian.Uint16(data[2:4])
483 m.MulticastAddress = data[4:20]
484
485 for i := uint16(0); i < m.N; i++ {
486 begin := 20 + (int(i) * 16)
487 end := begin + 16
488
489 if len(data) < end {
490 df.SetTruncated()
491 return begin, fmt.Errorf(
492 "Multicast Listener Report Message V2 layer less than %d bytes for Multicast Address Record", end)
493 }
494
495 m.SourceAddresses = append(m.SourceAddresses, data[begin:end])
496 }
497
498 expectedLengthWithouAuxData := 20 + (int(m.N) * 16)
499 expectedTotalLength := (int(m.AuxDataLen) * 4) + expectedLengthWithouAuxData // *4 because AuxDataLen are 32bit words
500 if len(data) < expectedTotalLength {
501 return expectedLengthWithouAuxData, fmt.Errorf(
502 "Multicast Listener Report Message V2 layer less than %d bytes for Multicast Address Record",
503 expectedLengthWithouAuxData)
504 }
505
506 m.AuxiliaryData = data[expectedLengthWithouAuxData:expectedTotalLength]
507
508 return expectedTotalLength, nil
509}
510
511// String sums this layer up nicely formatted
512func (m *MLDv2MulticastAddressRecord) String() string {
513 return fmt.Sprintf(
514 "RecordType: %d (%s), AuxDataLen: %d [32-bit words], N: %d, Multicast Address: %s, SourceAddresses: %s, Auxiliary Data: %#x",
515 m.RecordType,
516 m.RecordType.String(),
517 m.AuxDataLen,
518 m.N,
519 m.MulticastAddress.To16(),
520 m.SourceAddresses,
521 m.AuxiliaryData)
522}
523
524// serializes a multicast address record
525func (m *MLDv2MulticastAddressRecord) serializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
526 if err := m.serializeAuxiliaryDataTo(b, opts); err != nil {
527 return err
528 }
529
530 if err := m.serializeSourceAddressesTo(b, opts); err != nil {
531 return err
532 }
533
534 buf, err := b.PrependBytes(20)
535 if err != nil {
536 return err
537 }
538
539 buf[0] = uint8(m.RecordType)
540 buf[1] = m.AuxDataLen
541 binary.BigEndian.PutUint16(buf[2:4], m.N)
542
543 ma16 := m.MulticastAddress.To16()
544 if ma16 == nil {
545 return fmt.Errorf("invalid multicast address '%s'", m.MulticastAddress)
546 }
547 copy(buf[4:20], ma16)
548
549 return nil
550}
551
552// serializes the auxiliary data of a multicast address record
553func (m *MLDv2MulticastAddressRecord) serializeAuxiliaryDataTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
554 if remainder := len(m.AuxiliaryData) % 4; remainder != 0 {
555 zeroWord := []byte{0x0, 0x0, 0x0, 0x0}
556 m.AuxiliaryData = append(m.AuxiliaryData, zeroWord[:remainder]...)
557 }
558
559 if opts.FixLengths {
560 auxDataLen := len(m.AuxiliaryData) / 4
561
562 if auxDataLen > math.MaxUint8 {
563 return fmt.Errorf("auxilary data is %d 32-bit words, but the maximum is 255 32-bit words", auxDataLen)
564 }
565
566 m.AuxDataLen = uint8(auxDataLen)
567 }
568
569 buf, err := b.PrependBytes(len(m.AuxiliaryData))
570 if err != nil {
571 return err
572 }
573
574 copy(buf, m.AuxiliaryData)
575 return nil
576}
577
578// serializes the source addresses of a multicast address record preserving the order
579func (m *MLDv2MulticastAddressRecord) serializeSourceAddressesTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
580 if opts.FixLengths {
581 numberOfSourceAddresses := len(m.SourceAddresses)
582
583 if numberOfSourceAddresses > math.MaxUint16 {
584 return fmt.Errorf(
585 "%d source addresses added, but the maximum is 65535",
586 numberOfSourceAddresses)
587 }
588
589 m.N = uint16(numberOfSourceAddresses)
590 }
591
592 lastItemIdx := len(m.SourceAddresses) - 1
593 for k := range m.SourceAddresses {
594 i := lastItemIdx - k // reverse order
595
596 buf, err := b.PrependBytes(16)
597 if err != nil {
598 return err
599 }
600
601 sa16 := m.SourceAddresses[i].To16()
602 if sa16 == nil {
603 return fmt.Errorf("invalid source address [%d] '%s'", i, m.SourceAddresses[i])
604 }
605 copy(buf, sa16)
606 }
607
608 return nil
609}
610
611func decodeMLDv2MulticastListenerReport(data []byte, p gopacket.PacketBuilder) error {
612 m := &MLDv2MulticastListenerReportMessage{}
613 return decodingLayerDecoder(m, data, p)
614}
615
616func decodeMLDv2MulticastListenerQuery(data []byte, p gopacket.PacketBuilder) error {
617 m := &MLDv2MulticastListenerQueryMessage{}
618 return decodingLayerDecoder(m, data, p)
619}