github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/pkg/tcpip/header/igmpv3.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/ttpreport/gvisor-ligolo/pkg/tcpip"
    24  )
    25  
    26  var (
    27  	// IGMPv3RoutersAddress is the address to send IGMPv3 reports to.
    28  	//
    29  	// As per RFC 3376 section 4.2.14,
    30  	//
    31  	//   Version 3 Reports are sent with an IP destination address of
    32  	//   224.0.0.22, to which all IGMPv3-capable multicast routers listen.
    33  	IGMPv3RoutersAddress = tcpip.AddrFrom4([4]byte{0xe0, 0x00, 0x00, 0x16})
    34  )
    35  
    36  const (
    37  	// IGMPv3QueryMinimumSize is the mimum size of a valid IGMPv3 query,
    38  	// as per RFC 3376 section 4.1.
    39  	IGMPv3QueryMinimumSize = 12
    40  
    41  	igmpv3QueryMaxRespCodeOffset     = 1
    42  	igmpv3QueryGroupAddressOffset    = 4
    43  	igmpv3QueryResvSQRVOffset        = 8
    44  	igmpv3QueryQRVMask               = 0b111
    45  	igmpv3QueryQQICOffset            = 9
    46  	igmpv3QueryNumberOfSourcesOffset = 10
    47  	igmpv3QuerySourcesOffset         = 12
    48  )
    49  
    50  // IGMPv3Query is an IGMPv3 query message.
    51  //
    52  // As per RFC 3376 section 4.1,
    53  //
    54  //	 0                   1                   2                   3
    55  //	 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
    56  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    57  //	|  Type = 0x11  | Max Resp Code |           Checksum            |
    58  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    59  //	|                         Group Address                         |
    60  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    61  //	| Resv  |S| QRV |     QQIC      |     Number of Sources (N)     |
    62  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    63  //	|                       Source Address [1]                      |
    64  //	+-                                                             -+
    65  //	|                       Source Address [2]                      |
    66  //	+-                              .                              -+
    67  //	.                               .                               .
    68  //	.                               .                               .
    69  //	+-                                                             -+
    70  //	|                       Source Address [N]                      |
    71  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    72  type IGMPv3Query IGMP
    73  
    74  // MaximumResponseCode returns the Maximum Response Code.
    75  func (i IGMPv3Query) MaximumResponseCode() uint8 {
    76  	return i[igmpv3QueryMaxRespCodeOffset]
    77  }
    78  
    79  // IGMPv3MaximumResponseDelay returns the Maximum Response Delay in an IGMPv3
    80  // Maximum Response Code.
    81  //
    82  // As per RFC 3376 section 4.1.1,
    83  //
    84  //	The Max Resp Code field specifies the maximum time allowed before
    85  //	sending a responding report.  The actual time allowed, called the Max
    86  //	Resp Time, is represented in units of 1/10 second and is derived from
    87  //	the Max Resp Code as follows:
    88  //
    89  //	If Max Resp Code < 128, Max Resp Time = Max Resp Code
    90  //
    91  //	If Max Resp Code >= 128, Max Resp Code represents a floating-point
    92  //	value as follows:
    93  //
    94  //	    0 1 2 3 4 5 6 7
    95  //	  +-+-+-+-+-+-+-+-+
    96  //	   |1| exp | mant  |
    97  //	   +-+-+-+-+-+-+-+-+
    98  //
    99  //	Max Resp Time = (mant | 0x10) << (exp + 3)
   100  //
   101  //	Small values of Max Resp Time allow IGMPv3 routers to tune the "leave
   102  //	latency" (the time between the moment the last host leaves a group
   103  //	and the moment the routing protocol is notified that there are no
   104  //	more members).  Larger values, especially in the exponential range,
   105  //	allow tuning of the burstiness of IGMP traffic on a network.
   106  func IGMPv3MaximumResponseDelay(codeRaw uint8) time.Duration {
   107  	code := uint16(codeRaw)
   108  	if code < 128 {
   109  		return DecisecondToDuration(code)
   110  	}
   111  
   112  	const mantBits = 4
   113  	const expMask = 0b111
   114  	exp := (code >> mantBits) & expMask
   115  	mant := code & ((1 << mantBits) - 1)
   116  	return DecisecondToDuration((mant | 0x10) << (exp + 3))
   117  }
   118  
   119  // GroupAddress returns the group address.
   120  func (i IGMPv3Query) GroupAddress() tcpip.Address {
   121  	return tcpip.AddrFrom4([4]byte(i[igmpv3QueryGroupAddressOffset:][:IPv4AddressSize]))
   122  }
   123  
   124  // QuerierRobustnessVariable returns the querier's robustness variable.
   125  func (i IGMPv3Query) QuerierRobustnessVariable() uint8 {
   126  	return i[igmpv3QueryResvSQRVOffset] & igmpv3QueryQRVMask
   127  }
   128  
   129  // QuerierQueryInterval returns the querier's query interval.
   130  func (i IGMPv3Query) QuerierQueryInterval() time.Duration {
   131  	return mldv2AndIGMPv3QuerierQueryCodeToInterval(i[igmpv3QueryQQICOffset])
   132  }
   133  
   134  // Sources returns an iterator over source addresses in the query.
   135  //
   136  // Returns false if the message cannot hold the expected number of sources.
   137  func (i IGMPv3Query) Sources() (AddressIterator, bool) {
   138  	return makeAddressIterator(
   139  		i[igmpv3QuerySourcesOffset:],
   140  		binary.BigEndian.Uint16(i[igmpv3QueryNumberOfSourcesOffset:]),
   141  		IPv4AddressSize,
   142  	)
   143  }
   144  
   145  // IGMPv3ReportRecordType is the type of an IGMPv3 multicast address record
   146  // found in an IGMPv3 report, as per RFC 3810 section 5.2.12.
   147  type IGMPv3ReportRecordType int
   148  
   149  // IGMPv3 multicast address record types, as per RFC 3810 section 5.2.12.
   150  const (
   151  	IGMPv3ReportRecordModeIsInclude       IGMPv3ReportRecordType = 1
   152  	IGMPv3ReportRecordModeIsExclude       IGMPv3ReportRecordType = 2
   153  	IGMPv3ReportRecordChangeToIncludeMode IGMPv3ReportRecordType = 3
   154  	IGMPv3ReportRecordChangeToExcludeMode IGMPv3ReportRecordType = 4
   155  	IGMPv3ReportRecordAllowNewSources     IGMPv3ReportRecordType = 5
   156  	IGMPv3ReportRecordBlockOldSources     IGMPv3ReportRecordType = 6
   157  )
   158  
   159  const (
   160  	igmpv3ReportGroupAddressRecordMinimumSize           = 8
   161  	igmpv3ReportGroupAddressRecordTypeOffset            = 0
   162  	igmpv3ReportGroupAddressRecordAuxDataLenOffset      = 1
   163  	igmpv3ReportGroupAddressRecordAuxDataLenUnits       = 4
   164  	igmpv3ReportGroupAddressRecordNumberOfSourcesOffset = 2
   165  	igmpv3ReportGroupAddressRecordGroupAddressOffset    = 4
   166  	igmpv3ReportGroupAddressRecordSourcesOffset         = 8
   167  )
   168  
   169  // IGMPv3ReportGroupAddressRecordSerializer is an IGMPv3 Multicast Address
   170  // Record serializer.
   171  //
   172  // As per RFC 3810 section 5.2, a Multicast Address Record has the following
   173  // internal format:
   174  //
   175  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   176  //	|  Record Type  |  Aux Data Len |     Number of Sources (N)     |
   177  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   178  //	|                                                               |
   179  //	*                                                               *
   180  //	|                                                               |
   181  //	*                       Multicast Address                       *
   182  //	|                                                               |
   183  //	*                                                               *
   184  //	|                                                               |
   185  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   186  //	|                                                               |
   187  //	*                                                               *
   188  //	|                                                               |
   189  //	*                       Source Address [1]                      *
   190  //	|                                                               |
   191  //	*                                                               *
   192  //	|                                                               |
   193  //	+-                                                             -+
   194  //	|                                                               |
   195  //	*                                                               *
   196  //	|                                                               |
   197  //	*                       Source Address [2]                      *
   198  //	|                                                               |
   199  //	*                                                               *
   200  //	|                                                               |
   201  //	+-                                                             -+
   202  //	.                               .                               .
   203  //	.                               .                               .
   204  //	.                               .                               .
   205  //	+-                                                             -+
   206  //	|                                                               |
   207  //	*                                                               *
   208  //	|                                                               |
   209  //	*                       Source Address [N]                      *
   210  //	|                                                               |
   211  //	*                                                               *
   212  //	|                                                               |
   213  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   214  //	|                                                               |
   215  //	.                                                               .
   216  //	.                         Auxiliary Data                        .
   217  //	.                                                               .
   218  //	|                                                               |
   219  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   220  type IGMPv3ReportGroupAddressRecordSerializer struct {
   221  	RecordType   IGMPv3ReportRecordType
   222  	GroupAddress tcpip.Address
   223  	Sources      []tcpip.Address
   224  }
   225  
   226  // Length returns the number of bytes this serializer would occupy.
   227  func (s *IGMPv3ReportGroupAddressRecordSerializer) Length() int {
   228  	return igmpv3ReportGroupAddressRecordSourcesOffset + len(s.Sources)*IPv4AddressSize
   229  }
   230  
   231  func copyIPv4Address(dst []byte, src tcpip.Address) {
   232  	srcBytes := src.As4()
   233  	if n := copy(dst, srcBytes[:]); n != IPv4AddressSize {
   234  		panic(fmt.Sprintf("got copy(...) = %d, want = %d", n, IPv4AddressSize))
   235  	}
   236  }
   237  
   238  // SerializeInto serializes the record into the buffer.
   239  //
   240  // Panics if the buffer does not have enough space to fit the record.
   241  func (s *IGMPv3ReportGroupAddressRecordSerializer) SerializeInto(b []byte) {
   242  	b[igmpv3ReportGroupAddressRecordTypeOffset] = byte(s.RecordType)
   243  	b[igmpv3ReportGroupAddressRecordAuxDataLenOffset] = 0
   244  	binary.BigEndian.PutUint16(b[igmpv3ReportGroupAddressRecordNumberOfSourcesOffset:], uint16(len(s.Sources)))
   245  	copyIPv4Address(b[igmpv3ReportGroupAddressRecordGroupAddressOffset:], s.GroupAddress)
   246  	b = b[igmpv3ReportGroupAddressRecordSourcesOffset:]
   247  	for _, source := range s.Sources {
   248  		copyIPv4Address(b, source)
   249  		b = b[IPv4AddressSize:]
   250  	}
   251  }
   252  
   253  const (
   254  	igmpv3ReportTypeOffset                        = 0
   255  	igmpv3ReportReserved1Offset                   = 1
   256  	igmpv3ReportReserved2Offset                   = 4
   257  	igmpv3ReportNumberOfGroupAddressRecordsOffset = 6
   258  	igmpv3ReportGroupAddressRecordsOffset         = 8
   259  )
   260  
   261  // IGMPv3ReportSerializer is an MLD Version 2 Report serializer.
   262  //
   263  // As per RFC 3810 section 5.2,
   264  //
   265  //	 0                   1                   2                   3
   266  //	 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
   267  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   268  //	|  Type = 143   |    Reserved   |           Checksum            |
   269  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   270  //	|           Reserved            |Nr of Mcast Address Records (M)|
   271  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   272  //	|                                                               |
   273  //	.                                                               .
   274  //	.                  Multicast Address Record [1]                 .
   275  //	.                                                               .
   276  //	|                                                               |
   277  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   278  //	|                                                               |
   279  //	.                                                               .
   280  //	.                  Multicast Address Record [2]                 .
   281  //	.                                                               .
   282  //	|                                                               |
   283  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   284  //	|                               .                               |
   285  //	.                               .                               .
   286  //	|                               .                               |
   287  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   288  //	|                                                               |
   289  //	.                                                               .
   290  //	.                  Multicast Address Record [M]                 .
   291  //	.                                                               .
   292  //	|                                                               |
   293  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   294  type IGMPv3ReportSerializer struct {
   295  	Records []IGMPv3ReportGroupAddressRecordSerializer
   296  }
   297  
   298  // Length returns the number of bytes this serializer would occupy.
   299  func (s *IGMPv3ReportSerializer) Length() int {
   300  	ret := igmpv3ReportGroupAddressRecordsOffset
   301  	for _, record := range s.Records {
   302  		ret += record.Length()
   303  	}
   304  	return ret
   305  }
   306  
   307  // SerializeInto serializes the report into the buffer.
   308  //
   309  // Panics if the buffer does not have enough space to fit the report.
   310  func (s *IGMPv3ReportSerializer) SerializeInto(b []byte) {
   311  	b[igmpv3ReportTypeOffset] = byte(IGMPv3MembershipReport)
   312  	b[igmpv3ReportReserved1Offset] = 0
   313  	binary.BigEndian.PutUint16(b[igmpv3ReportReserved2Offset:], 0)
   314  	binary.BigEndian.PutUint16(b[igmpv3ReportNumberOfGroupAddressRecordsOffset:], uint16(len(s.Records)))
   315  	recordsBytes := b[igmpv3ReportGroupAddressRecordsOffset:]
   316  	for _, record := range s.Records {
   317  		len := record.Length()
   318  		record.SerializeInto(recordsBytes[:len])
   319  		recordsBytes = recordsBytes[len:]
   320  	}
   321  	binary.BigEndian.PutUint16(b[igmpChecksumOffset:], IGMPCalculateChecksum(b))
   322  }
   323  
   324  // IGMPv3ReportGroupAddressRecord is an IGMPv3 record.
   325  //
   326  // As per RFC 3810 section 5.2, a Multicast Address Record has the following
   327  // internal format:
   328  //
   329  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   330  //	|  Record Type  |  Aux Data Len |     Number of Sources (N)     |
   331  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   332  //	|                                                               |
   333  //	*                                                               *
   334  //	|                                                               |
   335  //	*                       Multicast Address                       *
   336  //	|                                                               |
   337  //	*                                                               *
   338  //	|                                                               |
   339  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   340  //	|                                                               |
   341  //	*                                                               *
   342  //	|                                                               |
   343  //	*                       Source Address [1]                      *
   344  //	|                                                               |
   345  //	*                                                               *
   346  //	|                                                               |
   347  //	+-                                                             -+
   348  //	|                                                               |
   349  //	*                                                               *
   350  //	|                                                               |
   351  //	*                       Source Address [2]                      *
   352  //	|                                                               |
   353  //	*                                                               *
   354  //	|                                                               |
   355  //	+-                                                             -+
   356  //	.                               .                               .
   357  //	.                               .                               .
   358  //	.                               .                               .
   359  //	+-                                                             -+
   360  //	|                                                               |
   361  //	*                                                               *
   362  //	|                                                               |
   363  //	*                       Source Address [N]                      *
   364  //	|                                                               |
   365  //	*                                                               *
   366  //	|                                                               |
   367  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   368  //	|                                                               |
   369  //	.                                                               .
   370  //	.                         Auxiliary Data                        .
   371  //	.                                                               .
   372  //	|                                                               |
   373  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   374  type IGMPv3ReportGroupAddressRecord []byte
   375  
   376  // RecordType returns the type of this record.
   377  func (r IGMPv3ReportGroupAddressRecord) RecordType() IGMPv3ReportRecordType {
   378  	return IGMPv3ReportRecordType(r[igmpv3ReportGroupAddressRecordTypeOffset])
   379  }
   380  
   381  // AuxDataLen returns the length of the auxillary data in this record.
   382  func (r IGMPv3ReportGroupAddressRecord) AuxDataLen() int {
   383  	return int(r[igmpv3ReportGroupAddressRecordAuxDataLenOffset]) * igmpv3ReportGroupAddressRecordAuxDataLenUnits
   384  }
   385  
   386  // numberOfSources returns the number of sources in this record.
   387  func (r IGMPv3ReportGroupAddressRecord) numberOfSources() uint16 {
   388  	return binary.BigEndian.Uint16(r[igmpv3ReportGroupAddressRecordNumberOfSourcesOffset:])
   389  }
   390  
   391  // GroupAddress returns the multicast address this record targets.
   392  func (r IGMPv3ReportGroupAddressRecord) GroupAddress() tcpip.Address {
   393  	return tcpip.AddrFrom4([4]byte(r[igmpv3ReportGroupAddressRecordGroupAddressOffset:][:IPv4AddressSize]))
   394  }
   395  
   396  // Sources returns an iterator over source addresses in the query.
   397  //
   398  // Returns false if the message cannot hold the expected number of sources.
   399  func (r IGMPv3ReportGroupAddressRecord) Sources() (AddressIterator, bool) {
   400  	expectedLen := int(r.numberOfSources()) * IPv4AddressSize
   401  	b := r[igmpv3ReportGroupAddressRecordSourcesOffset:]
   402  	if len(b) < expectedLen {
   403  		return AddressIterator{}, false
   404  	}
   405  	return AddressIterator{addressSize: IPv4AddressSize, buf: bytes.NewBuffer(b[:expectedLen])}, true
   406  }
   407  
   408  // IGMPv3Report is an IGMPv3 Report.
   409  //
   410  // As per RFC 3810 section 5.2,
   411  //
   412  //	 0                   1                   2                   3
   413  //	 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
   414  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   415  //	|  Type = 143   |    Reserved   |           Checksum            |
   416  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   417  //	|           Reserved            |Nr of Mcast Address Records (M)|
   418  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   419  //	|                                                               |
   420  //	.                                                               .
   421  //	.                  Multicast Address Record [1]                 .
   422  //	.                                                               .
   423  //	|                                                               |
   424  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   425  //	|                                                               |
   426  //	.                                                               .
   427  //	.                  Multicast Address Record [2]                 .
   428  //	.                                                               .
   429  //	|                                                               |
   430  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   431  //	|                               .                               |
   432  //	.                               .                               .
   433  //	|                               .                               |
   434  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   435  //	|                                                               |
   436  //	.                                                               .
   437  //	.                  Multicast Address Record [M]                 .
   438  //	.                                                               .
   439  //	|                                                               |
   440  //	+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   441  type IGMPv3Report []byte
   442  
   443  // Checksum returns the checksum.
   444  func (i IGMPv3Report) Checksum() uint16 {
   445  	return binary.BigEndian.Uint16(i[igmpChecksumOffset:])
   446  }
   447  
   448  // IGMPv3ReportGroupAddressRecordIterator is an iterator over IGMPv3 Multicast
   449  // Address Records.
   450  type IGMPv3ReportGroupAddressRecordIterator struct {
   451  	recordsLeft uint16
   452  	buf         *bytes.Buffer
   453  }
   454  
   455  // IGMPv3ReportGroupAddressRecordIteratorNextDisposition is the possible
   456  // return values from IGMPv3ReportGroupAddressRecordIterator.Next.
   457  type IGMPv3ReportGroupAddressRecordIteratorNextDisposition int
   458  
   459  const (
   460  	// IGMPv3ReportGroupAddressRecordIteratorNextOk indicates that a multicast
   461  	// address record was yielded.
   462  	IGMPv3ReportGroupAddressRecordIteratorNextOk IGMPv3ReportGroupAddressRecordIteratorNextDisposition = iota
   463  
   464  	// IGMPv3ReportGroupAddressRecordIteratorNextDone indicates that the iterator
   465  	// has been exhausted.
   466  	IGMPv3ReportGroupAddressRecordIteratorNextDone
   467  
   468  	// IGMPv3ReportGroupAddressRecordIteratorNextErrBufferTooShort indicates
   469  	// that the iterator expected another record, but the buffer ended
   470  	// prematurely.
   471  	IGMPv3ReportGroupAddressRecordIteratorNextErrBufferTooShort
   472  )
   473  
   474  // Next returns the next IGMPv3 Multicast Address Record.
   475  func (it *IGMPv3ReportGroupAddressRecordIterator) Next() (IGMPv3ReportGroupAddressRecord, IGMPv3ReportGroupAddressRecordIteratorNextDisposition) {
   476  	if it.recordsLeft == 0 {
   477  		return IGMPv3ReportGroupAddressRecord{}, IGMPv3ReportGroupAddressRecordIteratorNextDone
   478  	}
   479  	if it.buf.Len() < igmpv3ReportGroupAddressRecordMinimumSize {
   480  		return IGMPv3ReportGroupAddressRecord{}, IGMPv3ReportGroupAddressRecordIteratorNextErrBufferTooShort
   481  	}
   482  
   483  	hdr := IGMPv3ReportGroupAddressRecord(it.buf.Bytes())
   484  	expectedLen := igmpv3ReportGroupAddressRecordMinimumSize +
   485  		int(hdr.AuxDataLen()) + int(hdr.numberOfSources())*IPv4AddressSize
   486  
   487  	bytes := it.buf.Next(expectedLen)
   488  	if len(bytes) < expectedLen {
   489  		return IGMPv3ReportGroupAddressRecord{}, IGMPv3ReportGroupAddressRecordIteratorNextErrBufferTooShort
   490  	}
   491  	it.recordsLeft--
   492  	return IGMPv3ReportGroupAddressRecord(bytes), IGMPv3ReportGroupAddressRecordIteratorNextOk
   493  }
   494  
   495  // GroupAddressRecords returns an iterator of IGMPv3 Multicast Address
   496  // Records.
   497  func (i IGMPv3Report) GroupAddressRecords() IGMPv3ReportGroupAddressRecordIterator {
   498  	return IGMPv3ReportGroupAddressRecordIterator{
   499  		recordsLeft: binary.BigEndian.Uint16(i[igmpv3ReportNumberOfGroupAddressRecordsOffset:]),
   500  		buf:         bytes.NewBuffer(i[igmpv3ReportGroupAddressRecordsOffset:]),
   501  	}
   502  }