github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/tcpip/header/mldv2.go (about) 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 "github.com/metacubex/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.AddrFrom16Slice(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.AddrFrom16Slice(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 }