github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/tcpip/network/internal/ip/generic_multicast_protocol.go (about) 1 // Copyright 2020 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 ip 16 17 import ( 18 "fmt" 19 "math/rand" 20 "time" 21 22 "github.com/metacubex/gvisor/pkg/sync" 23 "github.com/metacubex/gvisor/pkg/tcpip" 24 "github.com/metacubex/gvisor/pkg/tcpip/header" 25 ) 26 27 const ( 28 // As per RFC 2236 section 3, 29 // 30 // When a host joins a multicast group, it should immediately transmit 31 // an unsolicited Version 2 Membership Report for that group, in case it 32 // is the first member of that group on the network. To cover the 33 // possibility of the initial Membership Report being lost or damaged, 34 // it is recommended that it be repeated once or twice after short 35 // delays [Unsolicited Report Interval]. (A simple way to accomplish 36 // this is to send the initial Version 2 Membership Report and then act 37 // as if a Group-Specific Query was received for that group, and set a 38 // timer appropriately). 39 // 40 // As per RFC 2710 section 4, 41 // 42 // When a node starts listening to a multicast address on an interface, 43 // it should immediately transmit an unsolicited Report for that address 44 // on that interface, in case it is the first listener on the link. To 45 // cover the possibility of the initial Report being lost or damaged, it 46 // is recommended that it be repeated once or twice after short delays 47 // [Unsolicited Report Interval]. (A simple way to accomplish this is 48 // to send the initial Report and then act as if a Multicast-Address- 49 // Specific Query was received for that address, and set a timer 50 // appropriately). 51 unsolicitedTransmissionCount = 2 52 53 // Responses to queries may be delayed, but we only send a response to a 54 // query once. A response to a query can be handled by any pending 55 // unsolicited transmission count, but we should send at least one report 56 // after sending a query. 57 // 58 // As per RFC 2236 section 3, 59 // 60 // When a host receives a General Query, it sets delay timers for each 61 // group (excluding the all-systems group) of which it is a member on 62 // the interface from which it received the query. 63 // 64 // As per RFC 2710 section 4, 65 // 66 // When a node receives a General Query, it sets a delay timer for each 67 // multicast address to which it is listening on the interface from 68 // which it received the Query, EXCLUDING the link-scope all-nodes 69 // address and any multicast addresses of scope 0 (reserved) or 1 70 // (node-local). 71 minQueryResponseTransmissionCount = 1 72 73 // DefaultRobustnessVariable is the default robustness variable 74 // 75 // As per RFC 3810 section 9.1 (for MLDv2), 76 // 77 // The Robustness Variable allows tuning for the expected packet loss on 78 // a link. If a link is expected to be lossy, the value of the 79 // Robustness Variable may be increased. MLD is robust to [Robustness 80 // Variable] - 1 packet losses. The value of the Robustness Variable 81 // MUST NOT be zero, and SHOULD NOT be one. Default value: 2. 82 // 83 // As per RFC 3376 section 8.1 (for IGMPv3), 84 // 85 // The Robustness Variable allows tuning for the expected packet loss on 86 // a network. If a network is expected to be lossy, the Robustness 87 // Variable may be increased. IGMP is robust to (Robustness Variable - 88 // 1) packet losses. The Robustness Variable MUST NOT be zero, and 89 // SHOULD NOT be one. Default: 2 90 DefaultRobustnessVariable = 2 91 92 // DefaultQueryInterval is the default query interval. 93 // 94 // As per RFC 3810 section 9.2 (for MLDv2), 95 // 96 // The Query Interval variable denotes the interval between General 97 // Queries sent by the Querier. Default value: 125 seconds. 98 // 99 // As per RFC 3376 section 8.2 (for IGMPv3), 100 // 101 // The Query Interval is the interval between General Queries sent by 102 // the Querier. Default: 125 seconds. 103 DefaultQueryInterval = 125 * time.Second 104 ) 105 106 // multicastGroupState holds the Generic Multicast Protocol state for a 107 // multicast group. 108 type multicastGroupState struct { 109 // joins is the number of times the group has been joined. 110 joins uint64 111 112 // transmissionLeft is the number of transmissions left to send. 113 transmissionLeft uint8 114 115 // lastToSendReport is true if we sent the last report for the group. It is 116 // used to track whether there are other hosts on the subnet that are also 117 // members of the group. 118 // 119 // Defined in RFC 2236 section 6 page 9 for IGMPv2 and RFC 2710 section 5 page 120 // 8 for MLDv1. 121 lastToSendReport bool 122 123 // delayedReportJob is used to delay sending responses to membership report 124 // messages in order to reduce duplicate reports from multiple hosts on the 125 // interface. 126 // 127 // Must not be nil. 128 delayedReportJob *tcpip.Job 129 130 // delyedReportJobFiresAt is the time when the delayed report job will fire. 131 // 132 // A zero value indicates that the job is not scheduled. 133 delayedReportJobFiresAt time.Time 134 135 // queriedIncludeSources holds sources that were queried for. 136 // 137 // Indicates that there is a pending source-specific query response for the 138 // multicast address. 139 queriedIncludeSources map[tcpip.Address]struct{} 140 141 deleteScheduled bool 142 } 143 144 func (m *multicastGroupState) cancelDelayedReportJob() { 145 m.delayedReportJob.Cancel() 146 m.delayedReportJobFiresAt = time.Time{} 147 m.transmissionLeft = 0 148 } 149 150 func (m *multicastGroupState) clearQueriedIncludeSources() { 151 for source := range m.queriedIncludeSources { 152 delete(m.queriedIncludeSources, source) 153 } 154 } 155 156 // GenericMulticastProtocolOptions holds options for the generic multicast 157 // protocol. 158 type GenericMulticastProtocolOptions struct { 159 // Rand is the source of random numbers. 160 Rand *rand.Rand 161 162 // Clock is the clock used to create timers. 163 Clock tcpip.Clock 164 165 // Protocol is the implementation of the variant of multicast group protocol 166 // in use. 167 Protocol MulticastGroupProtocol 168 169 // MaxUnsolicitedReportDelay is the maximum amount of time to wait between 170 // transmitting unsolicited reports. 171 // 172 // Unsolicited reports are transmitted when a group is newly joined. 173 MaxUnsolicitedReportDelay time.Duration 174 } 175 176 // MulticastGroupProtocolV2ReportRecordType is the type of a 177 // MulticastGroupProtocolv2 multicast address record. 178 type MulticastGroupProtocolV2ReportRecordType int 179 180 // MulticastGroupProtocolv2 multicast address record types. 181 const ( 182 _ MulticastGroupProtocolV2ReportRecordType = iota 183 MulticastGroupProtocolV2ReportRecordModeIsInclude 184 MulticastGroupProtocolV2ReportRecordModeIsExclude 185 MulticastGroupProtocolV2ReportRecordChangeToIncludeMode 186 MulticastGroupProtocolV2ReportRecordChangeToExcludeMode 187 MulticastGroupProtocolV2ReportRecordAllowNewSources 188 MulticastGroupProtocolV2ReportRecordBlockOldSources 189 ) 190 191 // MulticastGroupProtocolV2ReportBuilder is a builder for a V2 report. 192 type MulticastGroupProtocolV2ReportBuilder interface { 193 // AddRecord adds a record to the report. 194 AddRecord(recordType MulticastGroupProtocolV2ReportRecordType, groupAddress tcpip.Address) 195 196 // Send sends the report. 197 // 198 // Does nothing if no records were added. 199 // 200 // It is invalid to use this builder after this method is called. 201 Send() (sent bool, err tcpip.Error) 202 } 203 204 // MulticastGroupProtocol is a multicast group protocol whose core state machine 205 // can be represented by GenericMulticastProtocolState. 206 type MulticastGroupProtocol interface { 207 // Enabled indicates whether the generic multicast protocol will be 208 // performed. 209 // 210 // When enabled, the protocol may transmit report and leave messages when 211 // joining and leaving multicast groups respectively, and handle incoming 212 // packets. 213 // 214 // When disabled, the protocol will still keep track of locally joined groups, 215 // it just won't transmit and handle packets, or update groups' state. 216 Enabled() bool 217 218 // SendReport sends a multicast report for the specified group address. 219 // 220 // Returns false if the caller should queue the report to be sent later. Note, 221 // returning false does not mean that the receiver hit an error. 222 SendReport(groupAddress tcpip.Address) (sent bool, err tcpip.Error) 223 224 // SendLeave sends a multicast leave for the specified group address. 225 SendLeave(groupAddress tcpip.Address) tcpip.Error 226 227 // ShouldPerformProtocol returns true iff the protocol should be performed for 228 // the specified group. 229 ShouldPerformProtocol(tcpip.Address) bool 230 231 // NewReportV2Builder creates a new V2 builder. 232 NewReportV2Builder() MulticastGroupProtocolV2ReportBuilder 233 234 // V2QueryMaxRespCodeToV2Delay takes a V2 query's maximum response code and 235 // returns the V2 delay. 236 V2QueryMaxRespCodeToV2Delay(code uint16) time.Duration 237 238 // V2QueryMaxRespCodeToV1Delay takes a V2 query's maximum response code and 239 // returns the V1 delay. 240 V2QueryMaxRespCodeToV1Delay(code uint16) time.Duration 241 } 242 243 type protocolMode int 244 245 const ( 246 protocolModeV2 protocolMode = iota 247 protocolModeV1 248 protocolModeV1Compatibility 249 ) 250 251 // GenericMulticastProtocolState is the per interface generic multicast protocol 252 // state. 253 // 254 // There is actually no protocol named "Generic Multicast Protocol". Instead, 255 // the term used to refer to a generic multicast protocol that applies to both 256 // IPv4 and IPv6. Specifically, Generic Multicast Protocol is the core state 257 // machine of IGMPv2 as defined by RFC 2236 and MLDv1 as defined by RFC 2710. 258 // 259 // Callers must synchronize accesses to the generic multicast protocol state; 260 // GenericMulticastProtocolState obtains no locks in any of its methods. The 261 // only exception to this is GenericMulticastProtocolState's timer/job callbacks 262 // which will obtain the lock provided to the GenericMulticastProtocolState when 263 // it is initialized. 264 // 265 // GenericMulticastProtocolState.Init MUST be called before calling any of 266 // the methods on GenericMulticastProtocolState. 267 // 268 // GenericMulticastProtocolState.MakeAllNonMemberLocked MUST be called when the 269 // multicast group protocol is disabled so that leave messages may be sent. 270 type GenericMulticastProtocolState struct { 271 // Do not allow overwriting this state. 272 _ sync.NoCopy 273 274 opts GenericMulticastProtocolOptions 275 276 // memberships holds group addresses and their associated state. 277 memberships map[tcpip.Address]multicastGroupState 278 279 // protocolMU is the mutex used to protect the protocol. 280 protocolMU *sync.RWMutex 281 282 // V2 state. 283 robustnessVariable uint8 284 queryInterval time.Duration 285 mode protocolMode 286 modeTimer tcpip.Timer 287 288 generalQueryV2Timer tcpip.Timer 289 generalQueryV2TimerFiresAt time.Time 290 291 stateChangedReportV2Timer tcpip.Timer 292 stateChangedReportV2TimerSet bool 293 } 294 295 // GetV1ModeLocked returns the V1 configuration. 296 // 297 // Precondition: g.protocolMU must be read locked. 298 func (g *GenericMulticastProtocolState) GetV1ModeLocked() bool { 299 switch g.mode { 300 case protocolModeV2, protocolModeV1Compatibility: 301 return false 302 case protocolModeV1: 303 return true 304 default: 305 panic(fmt.Sprintf("unrecognized mode = %d", g.mode)) 306 } 307 } 308 309 func (g *GenericMulticastProtocolState) stopModeTimer() { 310 if g.modeTimer != nil { 311 g.modeTimer.Stop() 312 } 313 } 314 315 // SetV1ModeLocked sets the V1 configuration. 316 // 317 // Returns the previous configuration. 318 // 319 // Precondition: g.protocolMU must be locked. 320 func (g *GenericMulticastProtocolState) SetV1ModeLocked(v bool) bool { 321 if g.GetV1ModeLocked() == v { 322 return v 323 } 324 325 if v { 326 g.stopModeTimer() 327 g.cancelV2ReportTimers() 328 g.mode = protocolModeV1 329 return false 330 } 331 332 g.mode = protocolModeV2 333 return true 334 } 335 336 func (g *GenericMulticastProtocolState) cancelV2ReportTimers() { 337 if g.generalQueryV2Timer != nil { 338 g.generalQueryV2Timer.Stop() 339 g.generalQueryV2TimerFiresAt = time.Time{} 340 } 341 342 if g.stateChangedReportV2Timer != nil { 343 g.stateChangedReportV2Timer.Stop() 344 g.stateChangedReportV2TimerSet = false 345 } 346 } 347 348 // Init initializes the Generic Multicast Protocol state. 349 // 350 // Must only be called once for the lifetime of g; Init will panic if it is 351 // called twice. 352 // 353 // The GenericMulticastProtocolState will only grab the lock when timers/jobs 354 // fire. 355 // 356 // Note: the methods on opts.Protocol will always be called while protocolMU is 357 // held. 358 func (g *GenericMulticastProtocolState) Init(protocolMU *sync.RWMutex, opts GenericMulticastProtocolOptions) { 359 if g.memberships != nil { 360 panic("attempted to initialize generic membership protocol state twice") 361 } 362 363 *g = GenericMulticastProtocolState{ 364 opts: opts, 365 memberships: make(map[tcpip.Address]multicastGroupState), 366 protocolMU: protocolMU, 367 robustnessVariable: DefaultRobustnessVariable, 368 queryInterval: DefaultQueryInterval, 369 mode: protocolModeV2, 370 } 371 } 372 373 // MakeAllNonMemberLocked transitions all groups to the non-member state. 374 // 375 // The groups will still be considered joined locally. 376 // 377 // MUST be called when the multicast group protocol is disabled. 378 // 379 // Precondition: g.protocolMU must be locked. 380 func (g *GenericMulticastProtocolState) MakeAllNonMemberLocked() { 381 if !g.opts.Protocol.Enabled() { 382 return 383 } 384 385 g.stopModeTimer() 386 g.cancelV2ReportTimers() 387 388 var v2ReportBuilder MulticastGroupProtocolV2ReportBuilder 389 var handler func(tcpip.Address, *multicastGroupState) 390 switch g.mode { 391 case protocolModeV2: 392 v2ReportBuilder = g.opts.Protocol.NewReportV2Builder() 393 handler = func(groupAddress tcpip.Address, info *multicastGroupState) { 394 info.cancelDelayedReportJob() 395 396 // Send a report immediately to announce us leaving the group. 397 v2ReportBuilder.AddRecord( 398 MulticastGroupProtocolV2ReportRecordChangeToIncludeMode, 399 groupAddress, 400 ) 401 } 402 case protocolModeV1Compatibility: 403 g.mode = protocolModeV2 404 fallthrough 405 case protocolModeV1: 406 handler = g.transitionToNonMemberLocked 407 default: 408 panic(fmt.Sprintf("unrecognized mode = %d", g.mode)) 409 } 410 411 for groupAddress, info := range g.memberships { 412 if !g.shouldPerformForGroup(groupAddress) { 413 continue 414 } 415 416 handler(groupAddress, &info) 417 418 if info.deleteScheduled { 419 delete(g.memberships, groupAddress) 420 } else { 421 info.transmissionLeft = 0 422 g.memberships[groupAddress] = info 423 } 424 } 425 426 if v2ReportBuilder != nil { 427 // Nothing meaningful we can do with the error here - this method may be 428 // called when an interface is being disabled when we expect sends to 429 // fail. 430 _, _ = v2ReportBuilder.Send() 431 } 432 } 433 434 // InitializeGroupsLocked initializes each group, as if they were newly joined 435 // but without affecting the groups' join count. 436 // 437 // Must only be called after calling MakeAllNonMember as a group should not be 438 // initialized while it is not in the non-member state. 439 // 440 // Precondition: g.protocolMU must be locked. 441 func (g *GenericMulticastProtocolState) InitializeGroupsLocked() { 442 if !g.opts.Protocol.Enabled() { 443 return 444 } 445 446 var v2ReportBuilder MulticastGroupProtocolV2ReportBuilder 447 switch g.mode { 448 case protocolModeV2: 449 v2ReportBuilder = g.opts.Protocol.NewReportV2Builder() 450 case protocolModeV1Compatibility, protocolModeV1: 451 default: 452 panic(fmt.Sprintf("unrecognized mode = %d", g.mode)) 453 } 454 455 for groupAddress, info := range g.memberships { 456 g.initializeNewMemberLocked(groupAddress, &info, v2ReportBuilder) 457 g.memberships[groupAddress] = info 458 } 459 460 if v2ReportBuilder == nil { 461 return 462 } 463 464 if sent, err := v2ReportBuilder.Send(); sent && err == nil { 465 g.scheduleStateChangedTimer() 466 } else { 467 // Nothing meaningful we could do with the error here - the interface may 468 // not yet have an address. This is okay because we would either schedule a 469 // report to be sent later or we will be notified when an address is added, 470 // at which point we will try to send messages again. 471 for groupAddress, info := range g.memberships { 472 if !g.shouldPerformForGroup(groupAddress) { 473 continue 474 } 475 476 // Revert the transmissions count since we did not successfully send. 477 info.transmissionLeft++ 478 g.memberships[groupAddress] = info 479 } 480 } 481 } 482 483 // SendQueuedReportsLocked attempts to send reports for groups that failed to 484 // send reports during their last attempt. 485 // 486 // Precondition: g.protocolMU must be locked. 487 func (g *GenericMulticastProtocolState) SendQueuedReportsLocked() { 488 if g.stateChangedReportV2TimerSet { 489 return 490 } 491 492 for groupAddress, info := range g.memberships { 493 if info.delayedReportJobFiresAt.IsZero() { 494 switch g.mode { 495 case protocolModeV2: 496 g.sendV2ReportAndMaybeScheduleChangedTimer(groupAddress, &info, MulticastGroupProtocolV2ReportRecordChangeToExcludeMode) 497 case protocolModeV1Compatibility, protocolModeV1: 498 g.maybeSendReportLocked(groupAddress, &info) 499 default: 500 panic(fmt.Sprintf("unrecognized mode = %d", g.mode)) 501 } 502 503 g.memberships[groupAddress] = info 504 } 505 } 506 } 507 508 // JoinGroupLocked handles joining a new group. 509 // 510 // Precondition: g.protocolMU must be locked. 511 func (g *GenericMulticastProtocolState) JoinGroupLocked(groupAddress tcpip.Address) { 512 info, ok := g.memberships[groupAddress] 513 if ok { 514 info.joins++ 515 if info.joins > 1 { 516 // The group has already been joined. 517 g.memberships[groupAddress] = info 518 return 519 } 520 } else { 521 info = multicastGroupState{ 522 // Since we just joined the group, its count is 1. 523 joins: 1, 524 lastToSendReport: false, 525 delayedReportJob: tcpip.NewJob(g.opts.Clock, g.protocolMU, func() { 526 if !g.opts.Protocol.Enabled() { 527 panic(fmt.Sprintf("delayed report job fired for group %s while the multicast group protocol is disabled", groupAddress)) 528 } 529 530 info, ok := g.memberships[groupAddress] 531 if !ok { 532 panic(fmt.Sprintf("expected to find group state for group = %s", groupAddress)) 533 } 534 535 info.delayedReportJobFiresAt = time.Time{} 536 537 switch g.mode { 538 case protocolModeV2: 539 reportBuilder := g.opts.Protocol.NewReportV2Builder() 540 reportBuilder.AddRecord(MulticastGroupProtocolV2ReportRecordModeIsExclude, groupAddress) 541 // Nothing meaningful we can do with the error here - we only try to 542 // send a delayed report once. 543 _, _ = reportBuilder.Send() 544 case protocolModeV1Compatibility, protocolModeV1: 545 g.maybeSendReportLocked(groupAddress, &info) 546 default: 547 panic(fmt.Sprintf("unrecognized mode = %d", g.mode)) 548 } 549 550 info.clearQueriedIncludeSources() 551 g.memberships[groupAddress] = info 552 }), 553 queriedIncludeSources: make(map[tcpip.Address]struct{}), 554 } 555 } 556 557 info.deleteScheduled = false 558 info.clearQueriedIncludeSources() 559 info.delayedReportJobFiresAt = time.Time{} 560 info.lastToSendReport = false 561 g.initializeNewMemberLocked(groupAddress, &info, nil /* callersV2ReportBuilder */) 562 g.memberships[groupAddress] = info 563 } 564 565 // IsLocallyJoinedRLocked returns true if the group is locally joined. 566 // 567 // Precondition: g.protocolMU must be read locked. 568 func (g *GenericMulticastProtocolState) IsLocallyJoinedRLocked(groupAddress tcpip.Address) bool { 569 info, ok := g.memberships[groupAddress] 570 return ok && !info.deleteScheduled 571 } 572 573 func (g *GenericMulticastProtocolState) sendV2ReportAndMaybeScheduleChangedTimer( 574 groupAddress tcpip.Address, 575 info *multicastGroupState, 576 recordType MulticastGroupProtocolV2ReportRecordType, 577 ) bool { 578 if info.transmissionLeft == 0 { 579 return false 580 } 581 582 successfullySentAndHasMore := false 583 584 // Send a report immediately to announce us leaving the group. 585 reportBuilder := g.opts.Protocol.NewReportV2Builder() 586 reportBuilder.AddRecord(recordType, groupAddress) 587 if sent, err := reportBuilder.Send(); sent && err == nil { 588 info.transmissionLeft-- 589 590 successfullySentAndHasMore = info.transmissionLeft != 0 591 592 // Use the interface-wide state changed report for further transmissions. 593 if successfullySentAndHasMore { 594 g.scheduleStateChangedTimer() 595 } 596 } 597 598 return successfullySentAndHasMore 599 } 600 601 func (g *GenericMulticastProtocolState) scheduleStateChangedTimer() { 602 if g.stateChangedReportV2TimerSet { 603 return 604 } 605 606 delay := g.calculateDelayTimerDuration(g.opts.MaxUnsolicitedReportDelay) 607 if g.stateChangedReportV2Timer == nil { 608 // TODO(https://issuetracker.google.com/264799098): Create timer on 609 // initialization instead of lazily creating the timer since the timer 610 // does not change after being created. 611 g.stateChangedReportV2Timer = g.opts.Clock.AfterFunc(delay, func() { 612 g.protocolMU.Lock() 613 defer g.protocolMU.Unlock() 614 615 reportBuilder := g.opts.Protocol.NewReportV2Builder() 616 nonEmptyReport := false 617 for groupAddress, info := range g.memberships { 618 if info.transmissionLeft == 0 || !g.shouldPerformForGroup(groupAddress) { 619 continue 620 } 621 622 info.transmissionLeft-- 623 nonEmptyReport = true 624 625 mode := MulticastGroupProtocolV2ReportRecordChangeToExcludeMode 626 if info.deleteScheduled { 627 mode = MulticastGroupProtocolV2ReportRecordChangeToIncludeMode 628 } 629 reportBuilder.AddRecord(mode, groupAddress) 630 631 if info.deleteScheduled && info.transmissionLeft == 0 { 632 // No more transmissions left so we can actually delete the 633 // membership. 634 delete(g.memberships, groupAddress) 635 } else { 636 g.memberships[groupAddress] = info 637 } 638 } 639 640 // Nothing meaningful we can do with the error here. We will retry 641 // sending a state changed report again anyways. 642 _, _ = reportBuilder.Send() 643 644 if nonEmptyReport { 645 g.stateChangedReportV2Timer.Reset(g.calculateDelayTimerDuration(g.opts.MaxUnsolicitedReportDelay)) 646 } else { 647 g.stateChangedReportV2TimerSet = false 648 } 649 }) 650 } else { 651 g.stateChangedReportV2Timer.Reset(delay) 652 } 653 g.stateChangedReportV2TimerSet = true 654 } 655 656 // LeaveGroupLocked handles leaving the group. 657 // 658 // Returns false if the group is not currently joined. 659 // 660 // Precondition: g.protocolMU must be locked. 661 func (g *GenericMulticastProtocolState) LeaveGroupLocked(groupAddress tcpip.Address) bool { 662 info, ok := g.memberships[groupAddress] 663 if !ok || info.joins == 0 { 664 return false 665 } 666 667 info.joins-- 668 if info.joins != 0 { 669 // If we still have outstanding joins, then do nothing further. 670 g.memberships[groupAddress] = info 671 return true 672 } 673 674 info.deleteScheduled = true 675 info.cancelDelayedReportJob() 676 677 if !g.shouldPerformForGroup(groupAddress) { 678 delete(g.memberships, groupAddress) 679 return true 680 } 681 682 switch g.mode { 683 case protocolModeV2: 684 info.transmissionLeft = g.robustnessVariable 685 if g.sendV2ReportAndMaybeScheduleChangedTimer(groupAddress, &info, MulticastGroupProtocolV2ReportRecordChangeToIncludeMode) { 686 g.memberships[groupAddress] = info 687 } else { 688 delete(g.memberships, groupAddress) 689 } 690 case protocolModeV1Compatibility, protocolModeV1: 691 g.transitionToNonMemberLocked(groupAddress, &info) 692 delete(g.memberships, groupAddress) 693 default: 694 panic(fmt.Sprintf("unrecognized mode = %d", g.mode)) 695 } 696 697 return true 698 } 699 700 // HandleQueryV2Locked handles a V2 query. 701 // 702 // Precondition: g.protocolMU must be locked. 703 func (g *GenericMulticastProtocolState) HandleQueryV2Locked(groupAddress tcpip.Address, maxResponseCode uint16, sources header.AddressIterator, robustnessVariable uint8, queryInterval time.Duration) { 704 if !g.opts.Protocol.Enabled() { 705 return 706 } 707 708 switch g.mode { 709 case protocolModeV1Compatibility, protocolModeV1: 710 g.handleQueryInnerLocked(groupAddress, g.opts.Protocol.V2QueryMaxRespCodeToV1Delay(maxResponseCode)) 711 return 712 case protocolModeV2: 713 default: 714 panic(fmt.Sprintf("unrecognized mode = %d", g.mode)) 715 } 716 717 if robustnessVariable != 0 { 718 g.robustnessVariable = robustnessVariable 719 } 720 721 if queryInterval != 0 { 722 g.queryInterval = queryInterval 723 } 724 725 maxResponseTime := g.calculateDelayTimerDuration(g.opts.Protocol.V2QueryMaxRespCodeToV2Delay(maxResponseCode)) 726 727 // As per RFC 3376 section 5.2, 728 // 729 // 1. If there is a pending response to a previous General Query 730 // scheduled sooner than the selected delay, no additional response 731 // needs to be scheduled. 732 // 733 // 2. If the received Query is a General Query, the interface timer is 734 // used to schedule a response to the General Query after the 735 // selected delay. Any previously pending response to a General 736 // Query is canceled. 737 // 738 // 3. If the received Query is a Group-Specific Query or a Group-and- 739 // Source-Specific Query and there is no pending response to a 740 // previous Query for this group, then the group timer is used to 741 // schedule a report. If the received Query is a Group-and-Source- 742 // Specific Query, the list of queried sources is recorded to be used 743 // when generating a response. 744 // 745 // 4. If there already is a pending response to a previous Query 746 // scheduled for this group, and either the new Query is a Group- 747 // Specific Query or the recorded source-list associated with the 748 // group is empty, then the group source-list is cleared and a single 749 // response is scheduled using the group timer. The new response is 750 // scheduled to be sent at the earliest of the remaining time for the 751 // pending report and the selected delay. 752 // 753 // 5. If the received Query is a Group-and-Source-Specific Query and 754 // there is a pending response for this group with a non-empty 755 // source-list, then the group source list is augmented to contain 756 // the list of sources in the new Query and a single response is 757 // scheduled using the group timer. The new response is scheduled to 758 // be sent at the earliest of the remaining time for the pending 759 // report and the selected delay. 760 // 761 // As per RFC 3810 section 6.2, 762 // 763 // 1. If there is a pending response to a previous General Query 764 // scheduled sooner than the selected delay, no additional response 765 // needs to be scheduled. 766 // 767 // 2. If the received Query is a General Query, the Interface Timer is 768 // used to schedule a response to the General Query after the 769 // selected delay. Any previously pending response to a General 770 // Query is canceled. 771 // 772 // 3. If the received Query is a Multicast Address Specific Query or a 773 // Multicast Address and Source Specific Query and there is no 774 // pending response to a previous Query for this multicast address, 775 // then the Multicast Address Timer is used to schedule a report. If 776 // the received Query is a Multicast Address and Source Specific 777 // Query, the list of queried sources is recorded to be used when 778 // generating a response. 779 // 780 // 4. If there is already a pending response to a previous Query 781 // scheduled for this multicast address, and either the new Query is 782 // a Multicast Address Specific Query or the recorded source list 783 // associated with the multicast address is empty, then the multicast 784 // address source list is cleared and a single response is scheduled, 785 // using the Multicast Address Timer. The new response is scheduled 786 // to be sent at the earliest of the remaining time for the pending 787 // report and the selected delay. 788 // 789 // 5. If the received Query is a Multicast Address and Source Specific 790 // Query and there is a pending response for this multicast address 791 // with a non-empty source list, then the multicast address source 792 // list is augmented to contain the list of sources in the new Query, 793 // and a single response is scheduled using the Multicast Address 794 // Timer. The new response is scheduled to be sent at the earliest 795 // of the remaining time for the pending report and the selected 796 // delay. 797 now := g.opts.Clock.Now() 798 if !g.generalQueryV2TimerFiresAt.IsZero() && g.generalQueryV2TimerFiresAt.Sub(now) <= maxResponseTime { 799 return 800 } 801 802 if groupAddress.Unspecified() { 803 if g.generalQueryV2Timer == nil { 804 // TODO(https://issuetracker.google.com/264799098): Create timer on 805 // initialization instead of lazily creating the timer since the timer 806 // does not change after being created. 807 g.generalQueryV2Timer = g.opts.Clock.AfterFunc(maxResponseTime, func() { 808 g.protocolMU.Lock() 809 defer g.protocolMU.Unlock() 810 811 g.generalQueryV2TimerFiresAt = time.Time{} 812 813 // As per RFC 3810 section 6.3, 814 // 815 // If the expired timer is the Interface Timer (i.e., there is a 816 // pending response to a General Query), then one Current State 817 // Record is sent for each multicast address for which the specified 818 // interface has listening state, as described in section 4.2. The 819 // Current State Record carries the multicast address and its 820 // associated filter mode (MODE_IS_INCLUDE or MODE_IS_EXCLUDE) and 821 // Source list. Multiple Current State Records are packed into 822 // individual Report messages, to the extent possible. 823 // 824 // As per RFC 3376 section 5.2, 825 // 826 // If the expired timer is the interface timer (i.e., it is a pending 827 // response to a General Query), then one Current-State Record is 828 // sent for each multicast address for which the specified interface 829 // has reception state, as described in section 3.2. The Current- 830 // State Record carries the multicast address and its associated 831 // filter mode (MODE_IS_INCLUDE or MODE_IS_EXCLUDE) and source list. 832 // Multiple Current-State Records are packed into individual Report 833 // messages, to the extent possible. 834 reportBuilder := g.opts.Protocol.NewReportV2Builder() 835 for groupAddress, info := range g.memberships { 836 if info.deleteScheduled || !g.shouldPerformForGroup(groupAddress) { 837 continue 838 } 839 840 // A MODE_IS_EXCLUDE record without any sources indicates that we are 841 // interested in traffic from all sources for the group. 842 // 843 // We currently only hold groups if we have an active interest in the 844 // group. 845 reportBuilder.AddRecord( 846 MulticastGroupProtocolV2ReportRecordModeIsExclude, 847 groupAddress, 848 ) 849 } 850 851 _, _ = reportBuilder.Send() 852 }) 853 } else { 854 g.generalQueryV2Timer.Reset(maxResponseTime) 855 } 856 g.generalQueryV2TimerFiresAt = now.Add(maxResponseTime) 857 return 858 } 859 860 if info, ok := g.memberships[groupAddress]; ok && !info.deleteScheduled && g.shouldPerformForGroup(groupAddress) { 861 if info.delayedReportJobFiresAt.IsZero() || (!sources.Done() && len(info.queriedIncludeSources) != 0) { 862 for { 863 source, ok := sources.Next() 864 if !ok { 865 break 866 } 867 868 info.queriedIncludeSources[source] = struct{}{} 869 } 870 } else { 871 info.clearQueriedIncludeSources() 872 } 873 g.setDelayTimerForAddressLocked(groupAddress, &info, maxResponseTime) 874 g.memberships[groupAddress] = info 875 } 876 } 877 878 // HandleQueryLocked handles a query message with the specified maximum response 879 // time. 880 // 881 // If the group address is unspecified, then reports will be scheduled for all 882 // joined groups. 883 // 884 // Report(s) will be scheduled to be sent after a random duration between 0 and 885 // the maximum response time. 886 // 887 // Precondition: g.protocolMU must be locked. 888 func (g *GenericMulticastProtocolState) HandleQueryLocked(groupAddress tcpip.Address, maxResponseTime time.Duration) { 889 if !g.opts.Protocol.Enabled() { 890 return 891 } 892 893 switch g.mode { 894 case protocolModeV2, protocolModeV1Compatibility: 895 // As per 3376 section 8.12 (for IGMPv3), 896 // 897 // The Older Version Querier Interval is the time-out for transitioning 898 // a host back to IGMPv3 mode once an older version query is heard. 899 // When an older version query is received, hosts set their Older 900 // Version Querier Present Timer to Older Version Querier Interval. 901 // 902 // This value MUST be ((the Robustness Variable) times (the Query 903 // Interval in the last Query received)) plus (one Query Response 904 // Interval). 905 // 906 // As per RFC 3810 section 9.12 (for MLDv2), 907 // 908 // The Older Version Querier Present Timeout is the time-out for 909 // transitioning a host back to MLDv2 Host Compatibility Mode. When an 910 // MLDv1 query is received, MLDv2 hosts set their Older Version Querier 911 // Present Timer to [Older Version Querier Present Timeout]. 912 // 913 // This value MUST be ([Robustness Variable] times (the [Query Interval] 914 // in the last Query received)) plus ([Query Response Interval]). 915 modeRevertDelay := time.Duration(g.robustnessVariable) * g.queryInterval 916 if g.modeTimer == nil { 917 // TODO(https://issuetracker.google.com/264799098): Create timer on 918 // initialization instead of lazily creating the timer since the timer 919 // does not change after being created. 920 g.modeTimer = g.opts.Clock.AfterFunc(modeRevertDelay, func() { 921 g.protocolMU.Lock() 922 defer g.protocolMU.Unlock() 923 g.mode = protocolModeV2 924 }) 925 } else { 926 g.modeTimer.Reset(modeRevertDelay) 927 } 928 g.mode = protocolModeV1Compatibility 929 g.cancelV2ReportTimers() 930 case protocolModeV1: 931 default: 932 panic(fmt.Sprintf("unrecognized mode = %d", g.mode)) 933 } 934 g.handleQueryInnerLocked(groupAddress, maxResponseTime) 935 } 936 937 func (g *GenericMulticastProtocolState) handleQueryInnerLocked(groupAddress tcpip.Address, maxResponseTime time.Duration) { 938 maxResponseTime = g.calculateDelayTimerDuration(maxResponseTime) 939 940 // As per RFC 2236 section 2.4 (for IGMPv2), 941 // 942 // In a Membership Query message, the group address field is set to zero 943 // when sending a General Query, and set to the group address being 944 // queried when sending a Group-Specific Query. 945 // 946 // As per RFC 2710 section 3.6 (for MLDv1), 947 // 948 // In a Query message, the Multicast Address field is set to zero when 949 // sending a General Query, and set to a specific IPv6 multicast address 950 // when sending a Multicast-Address-Specific Query. 951 if groupAddress.Unspecified() { 952 // This is a general query as the group address is unspecified. 953 for groupAddress, info := range g.memberships { 954 g.setDelayTimerForAddressLocked(groupAddress, &info, maxResponseTime) 955 g.memberships[groupAddress] = info 956 } 957 } else if info, ok := g.memberships[groupAddress]; ok && !info.deleteScheduled { 958 g.setDelayTimerForAddressLocked(groupAddress, &info, maxResponseTime) 959 g.memberships[groupAddress] = info 960 } 961 } 962 963 // HandleReportLocked handles a report message. 964 // 965 // If the report is for a joined group, any active delayed report will be 966 // cancelled and the host state for the group transitions to idle. 967 // 968 // Precondition: g.protocolMU must be locked. 969 func (g *GenericMulticastProtocolState) HandleReportLocked(groupAddress tcpip.Address) { 970 if !g.opts.Protocol.Enabled() { 971 return 972 } 973 974 // As per RFC 2236 section 3 pages 3-4 (for IGMPv2), 975 // 976 // If the host receives another host's Report (version 1 or 2) while it has 977 // a timer running, it stops its timer for the specified group and does not 978 // send a Report 979 // 980 // As per RFC 2710 section 4 page 6 (for MLDv1), 981 // 982 // If a node receives another node's Report from an interface for a 983 // multicast address while it has a timer running for that same address 984 // on that interface, it stops its timer and does not send a Report for 985 // that address, thus suppressing duplicate reports on the link. 986 if info, ok := g.memberships[groupAddress]; ok { 987 info.cancelDelayedReportJob() 988 info.lastToSendReport = false 989 g.memberships[groupAddress] = info 990 } 991 } 992 993 // initializeNewMemberLocked initializes a new group membership. 994 // 995 // Precondition: g.protocolMU must be locked. 996 func (g *GenericMulticastProtocolState) initializeNewMemberLocked(groupAddress tcpip.Address, info *multicastGroupState, callersV2ReportBuilder MulticastGroupProtocolV2ReportBuilder) { 997 if !g.shouldPerformForGroup(groupAddress) { 998 return 999 } 1000 1001 info.lastToSendReport = false 1002 1003 switch g.mode { 1004 case protocolModeV2: 1005 info.transmissionLeft = g.robustnessVariable 1006 if callersV2ReportBuilder == nil { 1007 g.sendV2ReportAndMaybeScheduleChangedTimer(groupAddress, info, MulticastGroupProtocolV2ReportRecordChangeToExcludeMode) 1008 } else { 1009 callersV2ReportBuilder.AddRecord(MulticastGroupProtocolV2ReportRecordChangeToExcludeMode, groupAddress) 1010 info.transmissionLeft-- 1011 } 1012 case protocolModeV1Compatibility, protocolModeV1: 1013 info.transmissionLeft = unsolicitedTransmissionCount 1014 g.maybeSendReportLocked(groupAddress, info) 1015 default: 1016 panic(fmt.Sprintf("unrecognized mode = %d", g.mode)) 1017 } 1018 } 1019 1020 func (g *GenericMulticastProtocolState) shouldPerformForGroup(groupAddress tcpip.Address) bool { 1021 return g.opts.Protocol.ShouldPerformProtocol(groupAddress) && g.opts.Protocol.Enabled() 1022 } 1023 1024 // maybeSendReportLocked attempts to send a report for a group. 1025 // 1026 // Precondition: g.protocolMU must be locked. 1027 func (g *GenericMulticastProtocolState) maybeSendReportLocked(groupAddress tcpip.Address, info *multicastGroupState) { 1028 if info.transmissionLeft == 0 { 1029 return 1030 } 1031 1032 // As per RFC 2236 section 3 page 5 (for IGMPv2), 1033 // 1034 // When a host joins a multicast group, it should immediately transmit an 1035 // unsolicited Version 2 Membership Report for that group" ... "it is 1036 // recommended that it be repeated". 1037 // 1038 // As per RFC 2710 section 4 page 6 (for MLDv1), 1039 // 1040 // When a node starts listening to a multicast address on an interface, 1041 // it should immediately transmit an unsolicited Report for that address 1042 // on that interface, in case it is the first listener on the link. To 1043 // cover the possibility of the initial Report being lost or damaged, it 1044 // is recommended that it be repeated once or twice after short delays 1045 // [Unsolicited Report Interval]. 1046 // 1047 // TODO(gvisor.dev/issue/4901): Support a configurable number of initial 1048 // unsolicited reports. 1049 sent, err := g.opts.Protocol.SendReport(groupAddress) 1050 if err == nil && sent { 1051 info.lastToSendReport = true 1052 1053 info.transmissionLeft-- 1054 if info.transmissionLeft > 0 { 1055 g.setDelayTimerForAddressLocked( 1056 groupAddress, 1057 info, 1058 g.calculateDelayTimerDuration(g.opts.MaxUnsolicitedReportDelay), 1059 ) 1060 } 1061 } 1062 } 1063 1064 // maybeSendLeave attempts to send a leave message. 1065 func (g *GenericMulticastProtocolState) maybeSendLeave(groupAddress tcpip.Address, lastToSendReport bool) { 1066 if !g.shouldPerformForGroup(groupAddress) || !lastToSendReport { 1067 return 1068 } 1069 1070 // Okay to ignore the error here as if packet write failed, the multicast 1071 // routers will eventually drop our membership anyways. If the interface is 1072 // being disabled or removed, the generic multicast protocol's should be 1073 // cleared eventually. 1074 // 1075 // As per RFC 2236 section 3 page 5 (for IGMPv2), 1076 // 1077 // When a router receives a Report, it adds the group being reported to 1078 // the list of multicast group memberships on the network on which it 1079 // received the Report and sets the timer for the membership to the 1080 // [Group Membership Interval]. Repeated Reports refresh the timer. If 1081 // no Reports are received for a particular group before this timer has 1082 // expired, the router assumes that the group has no local members and 1083 // that it need not forward remotely-originated multicasts for that 1084 // group onto the attached network. 1085 // 1086 // As per RFC 2710 section 4 page 5 (for MLDv1), 1087 // 1088 // When a router receives a Report from a link, if the reported address 1089 // is not already present in the router's list of multicast address 1090 // having listeners on that link, the reported address is added to the 1091 // list, its timer is set to [Multicast Listener Interval], and its 1092 // appearance is made known to the router's multicast routing component. 1093 // If a Report is received for a multicast address that is already 1094 // present in the router's list, the timer for that address is reset to 1095 // [Multicast Listener Interval]. If an address's timer expires, it is 1096 // assumed that there are no longer any listeners for that address 1097 // present on the link, so it is deleted from the list and its 1098 // disappearance is made known to the multicast routing component. 1099 // 1100 // The requirement to send a leave message is also optional (it MAY be 1101 // skipped): 1102 // 1103 // As per RFC 2236 section 6 page 8 (for IGMPv2), 1104 // 1105 // "send leave" for the group on the interface. If the interface 1106 // state says the Querier is running IGMPv1, this action SHOULD be 1107 // skipped. If the flag saying we were the last host to report is 1108 // cleared, this action MAY be skipped. The Leave Message is sent to 1109 // the ALL-ROUTERS group (224.0.0.2). 1110 // 1111 // As per RFC 2710 section 5 page 8 (for MLDv1), 1112 // 1113 // "send done" for the address on the interface. If the flag saying 1114 // we were the last node to report is cleared, this action MAY be 1115 // skipped. The Done message is sent to the link-scope all-routers 1116 // address (FF02::2). 1117 _ = g.opts.Protocol.SendLeave(groupAddress) 1118 } 1119 1120 // transitionToNonMemberLocked transitions the given multicast group the the 1121 // non-member/listener state. 1122 // 1123 // Precondition: g.protocolMU must be locked. 1124 func (g *GenericMulticastProtocolState) transitionToNonMemberLocked(groupAddress tcpip.Address, info *multicastGroupState) { 1125 info.cancelDelayedReportJob() 1126 g.maybeSendLeave(groupAddress, info.lastToSendReport) 1127 info.lastToSendReport = false 1128 } 1129 1130 // setDelayTimerForAddressLocked sets timer to send a delayed report. 1131 // 1132 // Precondition: g.protocolMU MUST be locked. 1133 func (g *GenericMulticastProtocolState) setDelayTimerForAddressLocked(groupAddress tcpip.Address, info *multicastGroupState, maxResponseTime time.Duration) { 1134 if !g.shouldPerformForGroup(groupAddress) { 1135 return 1136 } 1137 1138 if info.transmissionLeft < minQueryResponseTransmissionCount { 1139 info.transmissionLeft = minQueryResponseTransmissionCount 1140 } 1141 1142 // As per RFC 2236 section 3 page 3 (for IGMPv2), 1143 // 1144 // If a timer for the group is already running, it is reset to the random 1145 // value only if the requested Max Response Time is less than the remaining 1146 // value of the running timer. 1147 // 1148 // As per RFC 2710 section 4 page 5 (for MLDv1), 1149 // 1150 // If a timer for any address is already running, it is reset to the new 1151 // random value only if the requested Maximum Response Delay is less than 1152 // the remaining value of the running timer. 1153 now := g.opts.Clock.Now() 1154 if !info.delayedReportJobFiresAt.IsZero() && info.delayedReportJobFiresAt.Sub(now) <= maxResponseTime { 1155 // The timer is scheduled to fire before the maximum response time so we 1156 // leave our timer as is. 1157 return 1158 } 1159 1160 info.delayedReportJob.Cancel() 1161 info.delayedReportJob.Schedule(maxResponseTime) 1162 info.delayedReportJobFiresAt = now.Add(maxResponseTime) 1163 } 1164 1165 // calculateDelayTimerDuration returns a random time between (0, maxRespTime]. 1166 func (g *GenericMulticastProtocolState) calculateDelayTimerDuration(maxRespTime time.Duration) time.Duration { 1167 // As per RFC 2236 section 3 page 3 (for IGMPv2), 1168 // 1169 // When a host receives a Group-Specific Query, it sets a delay timer to a 1170 // random value selected from the range (0, Max Response Time]... 1171 // 1172 // As per RFC 2710 section 4 page 6 (for MLDv1), 1173 // 1174 // When a node receives a Multicast-Address-Specific Query, if it is 1175 // listening to the queried Multicast Address on the interface from 1176 // which the Query was received, it sets a delay timer for that address 1177 // to a random value selected from the range [0, Maximum Response Delay], 1178 // as above. 1179 if maxRespTime == 0 { 1180 return 0 1181 } 1182 return time.Duration(g.opts.Rand.Int63n(int64(maxRespTime))) 1183 }