igmpv3.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 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 auxiliary 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 }
503