mldv2.go raw
1 // Copyright 2022 The gVisor Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package header
16
17 import (
18 "bytes"
19 "encoding/binary"
20 "fmt"
21 "time"
22
23 "gvisor.dev/gvisor/pkg/tcpip"
24 )
25
26 const (
27 // MLDv2QueryMinimumSize is the minimum size for an MLDv2 message.
28 MLDv2QueryMinimumSize = 24
29
30 mldv2QueryMaximumResponseCodeOffset = 0
31 mldv2QueryResvSQRVOffset = 20
32 mldv2QueryQRVMask = 0b111
33 mldv2QueryQQICOffset = 21
34 // mldv2QueryNumberOfSourcesOffset is the offset to the Number of Sources
35 // field within MLDv2Query.
36 mldv2QueryNumberOfSourcesOffset = 22
37
38 // MLDv2ReportMinimumSize is the minimum size of an MLDv2 report.
39 MLDv2ReportMinimumSize = 24
40
41 // mldv2QuerySourcesOffset is the offset to the Sources field within
42 // MLDv2Query.
43 mldv2QuerySourcesOffset = 24
44 )
45
46 var (
47 // MLDv2RoutersAddress is the address to send MLDv2 reports to.
48 //
49 // As per RFC 3810 section 5.2.14,
50 //
51 // Version 2 Multicast Listener Reports are sent with an IP destination
52 // address of FF02:0:0:0:0:0:0:16, to which all MLDv2-capable multicast
53 // routers listen (see section 11 for IANA considerations related to
54 // this special destination address).
55 MLDv2RoutersAddress = tcpip.AddrFrom16([16]byte{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16})
56 )
57
58 // MLDv2Query is a Multicast Listener Discovery Version 2 Query message in an
59 // ICMPv6 packet.
60 //
61 // MLDv2Query will only contain the body of an ICMPv6 packet.
62 //
63 // As per RFC 3810 section 5.1, MLDv2 Query messages have the following format
64 // (MLDv2Query only holds the bytes after the first four bytes in the diagram
65 // below):
66 //
67 // 0 1 2 3
68 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
69 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
70 // | Type = 130 | Code | Checksum |
71 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
72 // | Maximum Response Code | Reserved |
73 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
74 // | |
75 // * *
76 // | |
77 // * Multicast Address *
78 // | |
79 // * *
80 // | |
81 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
82 // | Resv |S| QRV | QQIC | Number of Sources (N) |
83 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
84 // | |
85 // * *
86 // | |
87 // * Source Address [1] *
88 // | |
89 // * *
90 // | |
91 // +- -+
92 // | |
93 // * *
94 // | |
95 // * Source Address [2] *
96 // | |
97 // * *
98 // | |
99 // +- . -+
100 // . . .
101 // . . .
102 // +- -+
103 // | |
104 // * *
105 // | |
106 // * Source Address [N] *
107 // | |
108 // * *
109 // | |
110 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
111 type MLDv2Query MLD
112
113 // MaximumResponseCode returns the Maximum Response Code
114 func (m MLDv2Query) MaximumResponseCode() uint16 {
115 return binary.BigEndian.Uint16(m[mldv2QueryMaximumResponseCodeOffset:])
116 }
117
118 // MLDv2MaximumResponseDelay returns the Maximum Response Delay in an MLDv2
119 // Maximum Response Code.
120 //
121 // As per RFC 3810 section 5.1.3,
122 //
123 // The Maximum Response Code field specifies the maximum time allowed
124 // before sending a responding Report. The actual time allowed, called
125 // the Maximum Response Delay, is represented in units of milliseconds,
126 // and is derived from the Maximum Response Code as follows:
127 //
128 // If Maximum Response Code < 32768,
129 // Maximum Response Delay = Maximum Response Code
130 //
131 // If Maximum Response Code >=32768, Maximum Response Code represents a
132 // floating-point value as follows:
133 //
134 // 0 1 2 3 4 5 6 7 8 9 A B C D E F
135 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
136 // |1| exp | mant |
137 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
138 //
139 // Maximum Response Delay = (mant | 0x1000) << (exp+3)
140 //
141 // Small values of Maximum Response Delay allow MLDv2 routers to tune
142 // the "leave latency" (the time between the moment the last node on a
143 // link ceases to listen to a specific multicast address and the moment
144 // the routing protocol is notified that there are no more listeners for
145 // that address). Larger values, especially in the exponential range,
146 // allow the tuning of the burstiness of MLD traffic on a link.
147 func MLDv2MaximumResponseDelay(codeRaw uint16) time.Duration {
148 code := time.Duration(codeRaw)
149 if code < 32768 {
150 return code * time.Millisecond
151 }
152
153 const mantBits = 12
154 const expMask = 0b111
155 exp := (code >> mantBits) & expMask
156 mant := code & ((1 << mantBits) - 1)
157 return (mant | 0x1000) << (exp + 3) * time.Millisecond
158 }
159
160 // MulticastAddress returns the Multicast Address.
161 func (m MLDv2Query) MulticastAddress() tcpip.Address {
162 // As per RFC 2710 section 3.5:
163 //
164 // In a Query message, the Multicast Address field is set to zero when
165 // sending a General Query, and set to a specific IPv6 multicast address
166 // when sending a Multicast-Address-Specific Query.
167 //
168 // In a Report or Done message, the Multicast Address field holds a
169 // specific IPv6 multicast address to which the message sender is
170 // listening or is ceasing to listen, respectively.
171 return tcpip.AddrFrom16([16]byte(m[mldMulticastAddressOffset:][:IPv6AddressSize]))
172 }
173
174 // QuerierRobustnessVariable returns the querier's robustness variable.
175 func (m MLDv2Query) QuerierRobustnessVariable() uint8 {
176 return m[mldv2QueryResvSQRVOffset] & mldv2QueryQRVMask
177 }
178
179 // QuerierQueryInterval returns the querier's query interval.
180 func (m MLDv2Query) QuerierQueryInterval() time.Duration {
181 return mldv2AndIGMPv3QuerierQueryCodeToInterval(m[mldv2QueryQQICOffset])
182 }
183
184 // Sources returns an iterator over source addresses in the query.
185 //
186 // Returns false if the message cannot hold the expected number of sources.
187 func (m MLDv2Query) Sources() (AddressIterator, bool) {
188 return makeAddressIterator(
189 m[mldv2QuerySourcesOffset:],
190 binary.BigEndian.Uint16(m[mldv2QueryNumberOfSourcesOffset:]),
191 IPv6AddressSize,
192 )
193 }
194
195 // MLDv2ReportRecordType is the type of an MLDv2 multicast address record
196 // found in an MLDv2 report, as per RFC 3810 section 5.2.12.
197 type MLDv2ReportRecordType int
198
199 // MLDv2 multicast address record types, as per RFC 3810 section 5.2.12.
200 const (
201 MLDv2ReportRecordModeIsInclude MLDv2ReportRecordType = 1
202 MLDv2ReportRecordModeIsExclude MLDv2ReportRecordType = 2
203 MLDv2ReportRecordChangeToIncludeMode MLDv2ReportRecordType = 3
204 MLDv2ReportRecordChangeToExcludeMode MLDv2ReportRecordType = 4
205 MLDv2ReportRecordAllowNewSources MLDv2ReportRecordType = 5
206 MLDv2ReportRecordBlockOldSources MLDv2ReportRecordType = 6
207 )
208
209 const (
210 mldv2ReportMulticastAddressRecordMinimumSize = 20
211 mldv2ReportMulticastAddressRecordTypeOffset = 0
212 mldv2ReportMulticastAddressRecordAuxDataLenOffset = 1
213 mldv2ReportMulticastAddressRecordAuxDataLenUnits = 4
214 mldv2ReportMulticastAddressRecordNumberOfSourcesOffset = 2
215 mldv2ReportMulticastAddressRecordMulticastAddressOffset = 4
216 mldv2ReportMulticastAddressRecordSourcesOffset = 20
217 )
218
219 // MLDv2ReportMulticastAddressRecordSerializer is an MLDv2 Multicast Address
220 // Record serializer.
221 //
222 // As per RFC 3810 section 5.2, a Multicast Address Record has the following
223 // internal format:
224 //
225 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
226 // | Record Type | Aux Data Len | Number of Sources (N) |
227 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
228 // | |
229 // * *
230 // | |
231 // * Multicast Address *
232 // | |
233 // * *
234 // | |
235 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
236 // | |
237 // * *
238 // | |
239 // * Source Address [1] *
240 // | |
241 // * *
242 // | |
243 // +- -+
244 // | |
245 // * *
246 // | |
247 // * Source Address [2] *
248 // | |
249 // * *
250 // | |
251 // +- -+
252 // . . .
253 // . . .
254 // . . .
255 // +- -+
256 // | |
257 // * *
258 // | |
259 // * Source Address [N] *
260 // | |
261 // * *
262 // | |
263 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
264 // | |
265 // . .
266 // . Auxiliary Data .
267 // . .
268 // | |
269 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
270 type MLDv2ReportMulticastAddressRecordSerializer struct {
271 RecordType MLDv2ReportRecordType
272 MulticastAddress tcpip.Address
273 Sources []tcpip.Address
274 }
275
276 // Length returns the number of bytes this serializer would occupy.
277 func (s *MLDv2ReportMulticastAddressRecordSerializer) Length() int {
278 return mldv2ReportMulticastAddressRecordSourcesOffset + len(s.Sources)*IPv6AddressSize
279 }
280
281 func copyIPv6Address(dst []byte, src tcpip.Address) {
282 if n := copy(dst, src.AsSlice()); n != IPv6AddressSize {
283 panic(fmt.Sprintf("got copy(...) = %d, want = %d", n, IPv6AddressSize))
284 }
285 }
286
287 // SerializeInto serializes the record into the buffer.
288 //
289 // Panics if the buffer does not have enough space to fit the record.
290 func (s *MLDv2ReportMulticastAddressRecordSerializer) SerializeInto(b []byte) {
291 b[mldv2ReportMulticastAddressRecordTypeOffset] = byte(s.RecordType)
292 b[mldv2ReportMulticastAddressRecordAuxDataLenOffset] = 0
293 binary.BigEndian.PutUint16(b[mldv2ReportMulticastAddressRecordNumberOfSourcesOffset:], uint16(len(s.Sources)))
294 copyIPv6Address(b[mldv2ReportMulticastAddressRecordMulticastAddressOffset:], s.MulticastAddress)
295 b = b[mldv2ReportMulticastAddressRecordSourcesOffset:]
296 for _, source := range s.Sources {
297 copyIPv6Address(b, source)
298 b = b[IPv6AddressSize:]
299 }
300 }
301
302 const (
303 mldv2ReportReservedOffset = 0
304 mldv2ReportNumberOfMulticastAddressRecordsOffset = 2
305 mldv2ReportMulticastAddressRecordsOffset = 4
306 )
307
308 // MLDv2ReportSerializer is an MLD Version 2 Report serializer.
309 //
310 // As per RFC 3810 section 5.2,
311 //
312 // 0 1 2 3
313 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
314 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
315 // | Type = 143 | Reserved | Checksum |
316 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
317 // | Reserved |Nr of Mcast Address Records (M)|
318 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
319 // | |
320 // . .
321 // . Multicast Address Record [1] .
322 // . .
323 // | |
324 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
325 // | |
326 // . .
327 // . Multicast Address Record [2] .
328 // . .
329 // | |
330 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
331 // | . |
332 // . . .
333 // | . |
334 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
335 // | |
336 // . .
337 // . Multicast Address Record [M] .
338 // . .
339 // | |
340 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
341 type MLDv2ReportSerializer struct {
342 Records []MLDv2ReportMulticastAddressRecordSerializer
343 }
344
345 // Length returns the number of bytes this serializer would occupy.
346 func (s *MLDv2ReportSerializer) Length() int {
347 ret := mldv2ReportMulticastAddressRecordsOffset
348 for _, record := range s.Records {
349 ret += record.Length()
350 }
351 return ret
352 }
353
354 // SerializeInto serializes the report into the buffer.
355 //
356 // Panics if the buffer does not have enough space to fit the report.
357 func (s *MLDv2ReportSerializer) SerializeInto(b []byte) {
358 binary.BigEndian.PutUint16(b[mldv2ReportReservedOffset:], 0)
359 binary.BigEndian.PutUint16(b[mldv2ReportNumberOfMulticastAddressRecordsOffset:], uint16(len(s.Records)))
360 b = b[mldv2ReportMulticastAddressRecordsOffset:]
361 for _, record := range s.Records {
362 len := record.Length()
363 record.SerializeInto(b[:len])
364 b = b[len:]
365 }
366 }
367
368 // MLDv2ReportMulticastAddressRecord is an MLDv2 record.
369 //
370 // As per RFC 3810 section 5.2, a Multicast Address Record has the following
371 // internal format:
372 //
373 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
374 // | Record Type | Aux Data Len | Number of Sources (N) |
375 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
376 // | |
377 // * *
378 // | |
379 // * Multicast Address *
380 // | |
381 // * *
382 // | |
383 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
384 // | |
385 // * *
386 // | |
387 // * Source Address [1] *
388 // | |
389 // * *
390 // | |
391 // +- -+
392 // | |
393 // * *
394 // | |
395 // * Source Address [2] *
396 // | |
397 // * *
398 // | |
399 // +- -+
400 // . . .
401 // . . .
402 // . . .
403 // +- -+
404 // | |
405 // * *
406 // | |
407 // * Source Address [N] *
408 // | |
409 // * *
410 // | |
411 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
412 // | |
413 // . .
414 // . Auxiliary Data .
415 // . .
416 // | |
417 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
418 type MLDv2ReportMulticastAddressRecord []byte
419
420 // RecordType returns the type of this record.
421 func (r MLDv2ReportMulticastAddressRecord) RecordType() MLDv2ReportRecordType {
422 return MLDv2ReportRecordType(r[mldv2ReportMulticastAddressRecordTypeOffset])
423 }
424
425 // AuxDataLen returns the length of the auxiliary data in this record.
426 func (r MLDv2ReportMulticastAddressRecord) AuxDataLen() int {
427 return int(r[mldv2ReportMulticastAddressRecordAuxDataLenOffset]) * mldv2ReportMulticastAddressRecordAuxDataLenUnits
428 }
429
430 // numberOfSources returns the number of sources in this record.
431 func (r MLDv2ReportMulticastAddressRecord) numberOfSources() uint16 {
432 return binary.BigEndian.Uint16(r[mldv2ReportMulticastAddressRecordNumberOfSourcesOffset:])
433 }
434
435 // MulticastAddress returns the multicast address this record targets.
436 func (r MLDv2ReportMulticastAddressRecord) MulticastAddress() tcpip.Address {
437 return tcpip.AddrFrom16([16]byte(r[mldv2ReportMulticastAddressRecordMulticastAddressOffset:][:IPv6AddressSize]))
438 }
439
440 // Sources returns an iterator over source addresses in the query.
441 //
442 // Returns false if the message cannot hold the expected number of sources.
443 func (r MLDv2ReportMulticastAddressRecord) Sources() (AddressIterator, bool) {
444 expectedLen := int(r.numberOfSources()) * IPv6AddressSize
445 b := r[mldv2ReportMulticastAddressRecordSourcesOffset:]
446 if len(b) < expectedLen {
447 return AddressIterator{}, false
448 }
449 return AddressIterator{addressSize: IPv6AddressSize, buf: bytes.NewBuffer(b[:expectedLen])}, true
450 }
451
452 // MLDv2Report is an MLDv2 Report.
453 //
454 // As per RFC 3810 section 5.2,
455 //
456 // 0 1 2 3
457 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
458 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
459 // | Type = 143 | Reserved | Checksum |
460 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
461 // | Reserved |Nr of Mcast Address Records (M)|
462 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
463 // | |
464 // . .
465 // . Multicast Address Record [1] .
466 // . .
467 // | |
468 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
469 // | |
470 // . .
471 // . Multicast Address Record [2] .
472 // . .
473 // | |
474 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
475 // | . |
476 // . . .
477 // | . |
478 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
479 // | |
480 // . .
481 // . Multicast Address Record [M] .
482 // . .
483 // | |
484 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
485 type MLDv2Report []byte
486
487 // MLDv2ReportMulticastAddressRecordIterator is an iterator over MLDv2 Multicast
488 // Address Records.
489 type MLDv2ReportMulticastAddressRecordIterator struct {
490 recordsLeft uint16
491 buf *bytes.Buffer
492 }
493
494 // MLDv2ReportMulticastAddressRecordIteratorNextDisposition is the possible
495 // return values from MLDv2ReportMulticastAddressRecordIterator.Next.
496 type MLDv2ReportMulticastAddressRecordIteratorNextDisposition int
497
498 const (
499 // MLDv2ReportMulticastAddressRecordIteratorNextOk indicates that a multicast
500 // address record was yielded.
501 MLDv2ReportMulticastAddressRecordIteratorNextOk MLDv2ReportMulticastAddressRecordIteratorNextDisposition = iota
502
503 // MLDv2ReportMulticastAddressRecordIteratorNextDone indicates that the iterator
504 // has been exhausted.
505 MLDv2ReportMulticastAddressRecordIteratorNextDone
506
507 // MLDv2ReportMulticastAddressRecordIteratorNextErrBufferTooShort indicates
508 // that the iterator expected another record, but the buffer ended
509 // prematurely.
510 MLDv2ReportMulticastAddressRecordIteratorNextErrBufferTooShort
511 )
512
513 // Next returns the next MLDv2 Multicast Address Record.
514 func (it *MLDv2ReportMulticastAddressRecordIterator) Next() (MLDv2ReportMulticastAddressRecord, MLDv2ReportMulticastAddressRecordIteratorNextDisposition) {
515 if it.recordsLeft == 0 {
516 return MLDv2ReportMulticastAddressRecord{}, MLDv2ReportMulticastAddressRecordIteratorNextDone
517 }
518 if it.buf.Len() < mldv2ReportMulticastAddressRecordMinimumSize {
519 return MLDv2ReportMulticastAddressRecord{}, MLDv2ReportMulticastAddressRecordIteratorNextErrBufferTooShort
520 }
521
522 hdr := MLDv2ReportMulticastAddressRecord(it.buf.Bytes())
523 expectedLen := mldv2ReportMulticastAddressRecordMinimumSize +
524 int(hdr.AuxDataLen()) + int(hdr.numberOfSources())*IPv6AddressSize
525
526 bytes := it.buf.Next(expectedLen)
527 if len(bytes) < expectedLen {
528 return MLDv2ReportMulticastAddressRecord{}, MLDv2ReportMulticastAddressRecordIteratorNextErrBufferTooShort
529 }
530 it.recordsLeft--
531 return MLDv2ReportMulticastAddressRecord(bytes), MLDv2ReportMulticastAddressRecordIteratorNextOk
532 }
533
534 // MulticastAddressRecords returns an iterator of MLDv2 Multicast Address
535 // Records.
536 func (m MLDv2Report) MulticastAddressRecords() MLDv2ReportMulticastAddressRecordIterator {
537 return MLDv2ReportMulticastAddressRecordIterator{
538 recordsLeft: binary.BigEndian.Uint16(m[mldv2ReportNumberOfMulticastAddressRecordsOffset:]),
539 buf: bytes.NewBuffer(m[mldv2ReportMulticastAddressRecordsOffset:]),
540 }
541 }
542