github.com/noisysockets/netstack@v0.6.0/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/noisysockets/netstack/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 }