github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/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/MerlinKodo/gvisor/pkg/sync" 23 "github.com/MerlinKodo/gvisor/pkg/tcpip" 24 "github.com/MerlinKodo/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, _ *multicastGroupState) { 394 // Send a report immediately to announce us leaving the group. 395 v2ReportBuilder.AddRecord( 396 MulticastGroupProtocolV2ReportRecordChangeToIncludeMode, 397 groupAddress, 398 ) 399 } 400 case protocolModeV1Compatibility: 401 g.mode = protocolModeV2 402 fallthrough 403 case protocolModeV1: 404 handler = g.transitionToNonMemberLocked 405 default: 406 panic(fmt.Sprintf("unrecognized mode = %d", g.mode)) 407 } 408 409 for groupAddress, info := range g.memberships { 410 if !g.shouldPerformForGroup(groupAddress) { 411 continue 412 } 413 414 handler(groupAddress, &info) 415 416 if info.deleteScheduled { 417 delete(g.memberships, groupAddress) 418 } else { 419 info.transmissionLeft = 0 420 g.memberships[groupAddress] = info 421 } 422 } 423 424 if v2ReportBuilder != nil { 425 // Nothing meaningful we can do with the error here - this method may be 426 // called when an interface is being disabled when we expect sends to 427 // fail. 428 _, _ = v2ReportBuilder.Send() 429 } 430 } 431 432 // InitializeGroupsLocked initializes each group, as if they were newly joined 433 // but without affecting the groups' join count. 434 // 435 // Must only be called after calling MakeAllNonMember as a group should not be 436 // initialized while it is not in the non-member state. 437 // 438 // Precondition: g.protocolMU must be locked. 439 func (g *GenericMulticastProtocolState) InitializeGroupsLocked() { 440 if !g.opts.Protocol.Enabled() { 441 return 442 } 443 444 var v2ReportBuilder MulticastGroupProtocolV2ReportBuilder 445 switch g.mode { 446 case protocolModeV2: 447 v2ReportBuilder = g.opts.Protocol.NewReportV2Builder() 448 case protocolModeV1Compatibility, protocolModeV1: 449 default: 450 panic(fmt.Sprintf("unrecognized mode = %d", g.mode)) 451 } 452 453 for groupAddress, info := range g.memberships { 454 g.initializeNewMemberLocked(groupAddress, &info, v2ReportBuilder) 455 g.memberships[groupAddress] = info 456 } 457 458 if v2ReportBuilder == nil { 459 return 460 } 461 462 if sent, err := v2ReportBuilder.Send(); sent && err == nil { 463 g.scheduleStateChangedTimer() 464 } else { 465 // Nothing meaningful we could do with the error here - the interface may 466 // not yet have an address. This is okay because we would either schedule a 467 // report to be sent later or we will be notified when an address is added, 468 // at which point we will try to send messages again. 469 for groupAddress, info := range g.memberships { 470 if !g.shouldPerformForGroup(groupAddress) { 471 continue 472 } 473 474 // Revert the transmissions count since we did not successfully send. 475 info.transmissionLeft++ 476 g.memberships[groupAddress] = info 477 } 478 } 479 } 480 481 // SendQueuedReportsLocked attempts to send reports for groups that failed to 482 // send reports during their last attempt. 483 // 484 // Precondition: g.protocolMU must be locked. 485 func (g *GenericMulticastProtocolState) SendQueuedReportsLocked() { 486 if g.stateChangedReportV2TimerSet { 487 return 488 } 489 490 for groupAddress, info := range g.memberships { 491 if info.delayedReportJobFiresAt.IsZero() { 492 switch g.mode { 493 case protocolModeV2: 494 g.sendV2ReportAndMaybeScheduleChangedTimer(groupAddress, &info, MulticastGroupProtocolV2ReportRecordChangeToExcludeMode) 495 case protocolModeV1Compatibility, protocolModeV1: 496 g.maybeSendReportLocked(groupAddress, &info) 497 default: 498 panic(fmt.Sprintf("unrecognized mode = %d", g.mode)) 499 } 500 501 g.memberships[groupAddress] = info 502 } 503 } 504 } 505 506 // JoinGroupLocked handles joining a new group. 507 // 508 // Precondition: g.protocolMU must be locked. 509 func (g *GenericMulticastProtocolState) JoinGroupLocked(groupAddress tcpip.Address) { 510 info, ok := g.memberships[groupAddress] 511 if ok { 512 info.joins++ 513 if info.joins > 1 { 514 // The group has already been joined. 515 g.memberships[groupAddress] = info 516 return 517 } 518 } else { 519 info = multicastGroupState{ 520 // Since we just joined the group, its count is 1. 521 joins: 1, 522 lastToSendReport: false, 523 delayedReportJob: tcpip.NewJob(g.opts.Clock, g.protocolMU, func() { 524 if !g.opts.Protocol.Enabled() { 525 panic(fmt.Sprintf("delayed report job fired for group %s while the multicast group protocol is disabled", groupAddress)) 526 } 527 528 info, ok := g.memberships[groupAddress] 529 if !ok { 530 panic(fmt.Sprintf("expected to find group state for group = %s", groupAddress)) 531 } 532 533 info.delayedReportJobFiresAt = time.Time{} 534 535 switch g.mode { 536 case protocolModeV2: 537 reportBuilder := g.opts.Protocol.NewReportV2Builder() 538 reportBuilder.AddRecord(MulticastGroupProtocolV2ReportRecordModeIsExclude, groupAddress) 539 // Nothing meaningful we can do with the error here - we only try to 540 // send a delayed report once. 541 _, _ = reportBuilder.Send() 542 case protocolModeV1Compatibility, protocolModeV1: 543 g.maybeSendReportLocked(groupAddress, &info) 544 default: 545 panic(fmt.Sprintf("unrecognized mode = %d", g.mode)) 546 } 547 548 info.clearQueriedIncludeSources() 549 g.memberships[groupAddress] = info 550 }), 551 queriedIncludeSources: make(map[tcpip.Address]struct{}), 552 } 553 } 554 555 info.deleteScheduled = false 556 info.clearQueriedIncludeSources() 557 info.delayedReportJobFiresAt = time.Time{} 558 info.lastToSendReport = false 559 g.initializeNewMemberLocked(groupAddress, &info, nil /* callersV2ReportBuilder */) 560 g.memberships[groupAddress] = info 561 } 562 563 // IsLocallyJoinedRLocked returns true if the group is locally joined. 564 // 565 // Precondition: g.protocolMU must be read locked. 566 func (g *GenericMulticastProtocolState) IsLocallyJoinedRLocked(groupAddress tcpip.Address) bool { 567 info, ok := g.memberships[groupAddress] 568 return ok && !info.deleteScheduled 569 } 570 571 func (g *GenericMulticastProtocolState) sendV2ReportAndMaybeScheduleChangedTimer( 572 groupAddress tcpip.Address, 573 info *multicastGroupState, 574 recordType MulticastGroupProtocolV2ReportRecordType, 575 ) bool { 576 if info.transmissionLeft == 0 { 577 return false 578 } 579 580 successfullySentAndHasMore := false 581 582 // Send a report immediately to announce us leaving the group. 583 reportBuilder := g.opts.Protocol.NewReportV2Builder() 584 reportBuilder.AddRecord(recordType, groupAddress) 585 if sent, err := reportBuilder.Send(); sent && err == nil { 586 info.transmissionLeft-- 587 588 successfullySentAndHasMore = info.transmissionLeft != 0 589 590 // Use the interface-wide state changed report for further transmissions. 591 if successfullySentAndHasMore { 592 g.scheduleStateChangedTimer() 593 } 594 } 595 596 return successfullySentAndHasMore 597 } 598 599 func (g *GenericMulticastProtocolState) scheduleStateChangedTimer() { 600 if g.stateChangedReportV2TimerSet { 601 return 602 } 603 604 delay := g.calculateDelayTimerDuration(g.opts.MaxUnsolicitedReportDelay) 605 if g.stateChangedReportV2Timer == nil { 606 // TODO(https://issuetracker.google.com/264799098): Create timer on 607 // initialization instead of lazily creating the timer since the timer 608 // does not change after being created. 609 g.stateChangedReportV2Timer = g.opts.Clock.AfterFunc(delay, func() { 610 g.protocolMU.Lock() 611 defer g.protocolMU.Unlock() 612 613 reportBuilder := g.opts.Protocol.NewReportV2Builder() 614 nonEmptyReport := false 615 for groupAddress, info := range g.memberships { 616 if info.transmissionLeft == 0 || !g.shouldPerformForGroup(groupAddress) { 617 continue 618 } 619 620 info.transmissionLeft-- 621 nonEmptyReport = true 622 623 mode := MulticastGroupProtocolV2ReportRecordChangeToExcludeMode 624 if info.deleteScheduled { 625 mode = MulticastGroupProtocolV2ReportRecordChangeToIncludeMode 626 } 627 reportBuilder.AddRecord(mode, groupAddress) 628 629 if info.deleteScheduled && info.transmissionLeft == 0 { 630 // No more transmissions left so we can actually delete the 631 // membership. 632 delete(g.memberships, groupAddress) 633 } else { 634 g.memberships[groupAddress] = info 635 } 636 } 637 638 // Nothing meaningful we can do with the error here. We will retry 639 // sending a state changed report again anyways. 640 _, _ = reportBuilder.Send() 641 642 if nonEmptyReport { 643 g.stateChangedReportV2Timer.Reset(g.calculateDelayTimerDuration(g.opts.MaxUnsolicitedReportDelay)) 644 } else { 645 g.stateChangedReportV2TimerSet = false 646 } 647 }) 648 } else { 649 g.stateChangedReportV2Timer.Reset(delay) 650 } 651 g.stateChangedReportV2TimerSet = true 652 } 653 654 // LeaveGroupLocked handles leaving the group. 655 // 656 // Returns false if the group is not currently joined. 657 // 658 // Precondition: g.protocolMU must be locked. 659 func (g *GenericMulticastProtocolState) LeaveGroupLocked(groupAddress tcpip.Address) bool { 660 info, ok := g.memberships[groupAddress] 661 if !ok || info.joins == 0 { 662 return false 663 } 664 665 info.joins-- 666 if info.joins != 0 { 667 // If we still have outstanding joins, then do nothing further. 668 g.memberships[groupAddress] = info 669 return true 670 } 671 672 info.deleteScheduled = true 673 info.cancelDelayedReportJob() 674 675 if !g.shouldPerformForGroup(groupAddress) { 676 delete(g.memberships, groupAddress) 677 return true 678 } 679 680 switch g.mode { 681 case protocolModeV2: 682 info.transmissionLeft = g.robustnessVariable 683 if g.sendV2ReportAndMaybeScheduleChangedTimer(groupAddress, &info, MulticastGroupProtocolV2ReportRecordChangeToIncludeMode) { 684 g.memberships[groupAddress] = info 685 } else { 686 delete(g.memberships, groupAddress) 687 } 688 case protocolModeV1Compatibility, protocolModeV1: 689 g.transitionToNonMemberLocked(groupAddress, &info) 690 delete(g.memberships, groupAddress) 691 default: 692 panic(fmt.Sprintf("unrecognized mode = %d", g.mode)) 693 } 694 695 return true 696 } 697 698 // HandleQueryV2Locked handles a V2 query. 699 // 700 // Precondition: g.protocolMU must be locked. 701 func (g *GenericMulticastProtocolState) HandleQueryV2Locked(groupAddress tcpip.Address, maxResponseCode uint16, sources header.AddressIterator, robustnessVariable uint8, queryInterval time.Duration) { 702 if !g.opts.Protocol.Enabled() { 703 return 704 } 705 706 switch g.mode { 707 case protocolModeV1Compatibility, protocolModeV1: 708 g.handleQueryInnerLocked(groupAddress, g.opts.Protocol.V2QueryMaxRespCodeToV1Delay(maxResponseCode)) 709 return 710 case protocolModeV2: 711 default: 712 panic(fmt.Sprintf("unrecognized mode = %d", g.mode)) 713 } 714 715 if robustnessVariable != 0 { 716 g.robustnessVariable = robustnessVariable 717 } 718 719 if queryInterval != 0 { 720 g.queryInterval = queryInterval 721 } 722 723 maxResponseTime := g.calculateDelayTimerDuration(g.opts.Protocol.V2QueryMaxRespCodeToV2Delay(maxResponseCode)) 724 725 // As per RFC 3376 section 5.2, 726 // 727 // 1. If there is a pending response to a previous General Query 728 // scheduled sooner than the selected delay, no additional response 729 // needs to be scheduled. 730 // 731 // 2. If the received Query is a General Query, the interface timer is 732 // used to schedule a response to the General Query after the 733 // selected delay. Any previously pending response to a General 734 // Query is canceled. 735 // 736 // 3. If the received Query is a Group-Specific Query or a Group-and- 737 // Source-Specific Query and there is no pending response to a 738 // previous Query for this group, then the group timer is used to 739 // schedule a report. If the received Query is a Group-and-Source- 740 // Specific Query, the list of queried sources is recorded to be used 741 // when generating a response. 742 // 743 // 4. If there already is a pending response to a previous Query 744 // scheduled for this group, and either the new Query is a Group- 745 // Specific Query or the recorded source-list associated with the 746 // group is empty, then the group source-list is cleared and a single 747 // response is scheduled using the group timer. The new response is 748 // scheduled to be sent at the earliest of the remaining time for the 749 // pending report and the selected delay. 750 // 751 // 5. If the received Query is a Group-and-Source-Specific Query and 752 // there is a pending response for this group with a non-empty 753 // source-list, then the group source list is augmented to contain 754 // the list of sources in the new Query and a single response is 755 // scheduled using the group timer. The new response is scheduled to 756 // be sent at the earliest of the remaining time for the pending 757 // report and the selected delay. 758 // 759 // As per RFC 3810 section 6.2, 760 // 761 // 1. If there is a pending response to a previous General Query 762 // scheduled sooner than the selected delay, no additional response 763 // needs to be scheduled. 764 // 765 // 2. If the received Query is a General Query, the Interface Timer is 766 // used to schedule a response to the General Query after the 767 // selected delay. Any previously pending response to a General 768 // Query is canceled. 769 // 770 // 3. If the received Query is a Multicast Address Specific Query or a 771 // Multicast Address and Source Specific Query and there is no 772 // pending response to a previous Query for this multicast address, 773 // then the Multicast Address Timer is used to schedule a report. If 774 // the received Query is a Multicast Address and Source Specific 775 // Query, the list of queried sources is recorded to be used when 776 // generating a response. 777 // 778 // 4. If there is already a pending response to a previous Query 779 // scheduled for this multicast address, and either the new Query is 780 // a Multicast Address Specific Query or the recorded source list 781 // associated with the multicast address is empty, then the multicast 782 // address source list is cleared and a single response is scheduled, 783 // using the Multicast Address Timer. The new response is scheduled 784 // to be sent at the earliest of the remaining time for the pending 785 // report and the selected delay. 786 // 787 // 5. If the received Query is a Multicast Address and Source Specific 788 // Query and there is a pending response for this multicast address 789 // with a non-empty source list, then the multicast address source 790 // list is augmented to contain the list of sources in the new Query, 791 // and a single response is scheduled using the Multicast Address 792 // Timer. The new response is scheduled to be sent at the earliest 793 // of the remaining time for the pending report and the selected 794 // delay. 795 now := g.opts.Clock.Now() 796 if !g.generalQueryV2TimerFiresAt.IsZero() && g.generalQueryV2TimerFiresAt.Sub(now) <= maxResponseTime { 797 return 798 } 799 800 if groupAddress.Unspecified() { 801 if g.generalQueryV2Timer == nil { 802 // TODO(https://issuetracker.google.com/264799098): Create timer on 803 // initialization instead of lazily creating the timer since the timer 804 // does not change after being created. 805 g.generalQueryV2Timer = g.opts.Clock.AfterFunc(maxResponseTime, func() { 806 g.protocolMU.Lock() 807 defer g.protocolMU.Unlock() 808 809 g.generalQueryV2TimerFiresAt = time.Time{} 810 811 // As per RFC 3810 section 6.3, 812 // 813 // If the expired timer is the Interface Timer (i.e., there is a 814 // pending response to a General Query), then one Current State 815 // Record is sent for each multicast address for which the specified 816 // interface has listening state, as described in section 4.2. The 817 // Current State Record carries the multicast address and its 818 // associated filter mode (MODE_IS_INCLUDE or MODE_IS_EXCLUDE) and 819 // Source list. Multiple Current State Records are packed into 820 // individual Report messages, to the extent possible. 821 // 822 // As per RFC 3376 section 5.2, 823 // 824 // If the expired timer is the interface timer (i.e., it is a pending 825 // response to a General Query), then one Current-State Record is 826 // sent for each multicast address for which the specified interface 827 // has reception state, as described in section 3.2. The Current- 828 // State Record carries the multicast address and its associated 829 // filter mode (MODE_IS_INCLUDE or MODE_IS_EXCLUDE) and source list. 830 // Multiple Current-State Records are packed into individual Report 831 // messages, to the extent possible. 832 reportBuilder := g.opts.Protocol.NewReportV2Builder() 833 for groupAddress, info := range g.memberships { 834 if info.deleteScheduled || !g.shouldPerformForGroup(groupAddress) { 835 continue 836 } 837 838 // A MODE_IS_EXCLUDE record without any sources indicates that we are 839 // interested in traffic from all sources for the group. 840 // 841 // We currently only hold groups if we have an active interest in the 842 // group. 843 reportBuilder.AddRecord( 844 MulticastGroupProtocolV2ReportRecordModeIsExclude, 845 groupAddress, 846 ) 847 } 848 849 _, _ = reportBuilder.Send() 850 }) 851 } else { 852 g.generalQueryV2Timer.Reset(maxResponseTime) 853 } 854 g.generalQueryV2TimerFiresAt = now.Add(maxResponseTime) 855 return 856 } 857 858 if info, ok := g.memberships[groupAddress]; ok && !info.deleteScheduled && g.shouldPerformForGroup(groupAddress) { 859 if info.delayedReportJobFiresAt.IsZero() || (!sources.Done() && len(info.queriedIncludeSources) != 0) { 860 for { 861 source, ok := sources.Next() 862 if !ok { 863 break 864 } 865 866 info.queriedIncludeSources[source] = struct{}{} 867 } 868 } else { 869 info.clearQueriedIncludeSources() 870 } 871 g.setDelayTimerForAddressLocked(groupAddress, &info, maxResponseTime) 872 g.memberships[groupAddress] = info 873 } 874 } 875 876 // HandleQueryLocked handles a query message with the specified maximum response 877 // time. 878 // 879 // If the group address is unspecified, then reports will be scheduled for all 880 // joined groups. 881 // 882 // Report(s) will be scheduled to be sent after a random duration between 0 and 883 // the maximum response time. 884 // 885 // Precondition: g.protocolMU must be locked. 886 func (g *GenericMulticastProtocolState) HandleQueryLocked(groupAddress tcpip.Address, maxResponseTime time.Duration) { 887 if !g.opts.Protocol.Enabled() { 888 return 889 } 890 891 switch g.mode { 892 case protocolModeV2, protocolModeV1Compatibility: 893 // As per 3376 section 8.12 (for IGMPv3), 894 // 895 // The Older Version Querier Interval is the time-out for transitioning 896 // a host back to IGMPv3 mode once an older version query is heard. 897 // When an older version query is received, hosts set their Older 898 // Version Querier Present Timer to Older Version Querier Interval. 899 // 900 // This value MUST be ((the Robustness Variable) times (the Query 901 // Interval in the last Query received)) plus (one Query Response 902 // Interval). 903 // 904 // As per RFC 3810 section 9.12 (for MLDv2), 905 // 906 // The Older Version Querier Present Timeout is the time-out for 907 // transitioning a host back to MLDv2 Host Compatibility Mode. When an 908 // MLDv1 query is received, MLDv2 hosts set their Older Version Querier 909 // Present Timer to [Older Version Querier Present Timeout]. 910 // 911 // This value MUST be ([Robustness Variable] times (the [Query Interval] 912 // in the last Query received)) plus ([Query Response Interval]). 913 modeRevertDelay := time.Duration(g.robustnessVariable) * g.queryInterval 914 if g.modeTimer == nil { 915 // TODO(https://issuetracker.google.com/264799098): Create timer on 916 // initialization instead of lazily creating the timer since the timer 917 // does not change after being created. 918 g.modeTimer = g.opts.Clock.AfterFunc(modeRevertDelay, func() { 919 g.protocolMU.Lock() 920 defer g.protocolMU.Unlock() 921 g.mode = protocolModeV2 922 }) 923 } else { 924 g.modeTimer.Reset(modeRevertDelay) 925 } 926 g.mode = protocolModeV1Compatibility 927 g.cancelV2ReportTimers() 928 case protocolModeV1: 929 default: 930 panic(fmt.Sprintf("unrecognized mode = %d", g.mode)) 931 } 932 g.handleQueryInnerLocked(groupAddress, maxResponseTime) 933 } 934 935 func (g *GenericMulticastProtocolState) handleQueryInnerLocked(groupAddress tcpip.Address, maxResponseTime time.Duration) { 936 maxResponseTime = g.calculateDelayTimerDuration(maxResponseTime) 937 938 // As per RFC 2236 section 2.4 (for IGMPv2), 939 // 940 // In a Membership Query message, the group address field is set to zero 941 // when sending a General Query, and set to the group address being 942 // queried when sending a Group-Specific Query. 943 // 944 // As per RFC 2710 section 3.6 (for MLDv1), 945 // 946 // In a Query message, the Multicast Address field is set to zero when 947 // sending a General Query, and set to a specific IPv6 multicast address 948 // when sending a Multicast-Address-Specific Query. 949 if groupAddress.Unspecified() { 950 // This is a general query as the group address is unspecified. 951 for groupAddress, info := range g.memberships { 952 g.setDelayTimerForAddressLocked(groupAddress, &info, maxResponseTime) 953 g.memberships[groupAddress] = info 954 } 955 } else if info, ok := g.memberships[groupAddress]; ok && !info.deleteScheduled { 956 g.setDelayTimerForAddressLocked(groupAddress, &info, maxResponseTime) 957 g.memberships[groupAddress] = info 958 } 959 } 960 961 // HandleReportLocked handles a report message. 962 // 963 // If the report is for a joined group, any active delayed report will be 964 // cancelled and the host state for the group transitions to idle. 965 // 966 // Precondition: g.protocolMU must be locked. 967 func (g *GenericMulticastProtocolState) HandleReportLocked(groupAddress tcpip.Address) { 968 if !g.opts.Protocol.Enabled() { 969 return 970 } 971 972 // As per RFC 2236 section 3 pages 3-4 (for IGMPv2), 973 // 974 // If the host receives another host's Report (version 1 or 2) while it has 975 // a timer running, it stops its timer for the specified group and does not 976 // send a Report 977 // 978 // As per RFC 2710 section 4 page 6 (for MLDv1), 979 // 980 // If a node receives another node's Report from an interface for a 981 // multicast address while it has a timer running for that same address 982 // on that interface, it stops its timer and does not send a Report for 983 // that address, thus suppressing duplicate reports on the link. 984 if info, ok := g.memberships[groupAddress]; ok { 985 info.cancelDelayedReportJob() 986 info.lastToSendReport = false 987 g.memberships[groupAddress] = info 988 } 989 } 990 991 // initializeNewMemberLocked initializes a new group membership. 992 // 993 // Precondition: g.protocolMU must be locked. 994 func (g *GenericMulticastProtocolState) initializeNewMemberLocked(groupAddress tcpip.Address, info *multicastGroupState, callersV2ReportBuilder MulticastGroupProtocolV2ReportBuilder) { 995 if !g.shouldPerformForGroup(groupAddress) { 996 return 997 } 998 999 info.lastToSendReport = false 1000 1001 switch g.mode { 1002 case protocolModeV2: 1003 info.transmissionLeft = g.robustnessVariable 1004 if callersV2ReportBuilder == nil { 1005 g.sendV2ReportAndMaybeScheduleChangedTimer(groupAddress, info, MulticastGroupProtocolV2ReportRecordChangeToExcludeMode) 1006 } else { 1007 callersV2ReportBuilder.AddRecord(MulticastGroupProtocolV2ReportRecordChangeToExcludeMode, groupAddress) 1008 info.transmissionLeft-- 1009 } 1010 case protocolModeV1Compatibility, protocolModeV1: 1011 info.transmissionLeft = unsolicitedTransmissionCount 1012 g.maybeSendReportLocked(groupAddress, info) 1013 default: 1014 panic(fmt.Sprintf("unrecognized mode = %d", g.mode)) 1015 } 1016 } 1017 1018 func (g *GenericMulticastProtocolState) shouldPerformForGroup(groupAddress tcpip.Address) bool { 1019 return g.opts.Protocol.ShouldPerformProtocol(groupAddress) && g.opts.Protocol.Enabled() 1020 } 1021 1022 // maybeSendReportLocked attempts to send a report for a group. 1023 // 1024 // Precondition: g.protocolMU must be locked. 1025 func (g *GenericMulticastProtocolState) maybeSendReportLocked(groupAddress tcpip.Address, info *multicastGroupState) { 1026 if info.transmissionLeft == 0 { 1027 return 1028 } 1029 1030 // As per RFC 2236 section 3 page 5 (for IGMPv2), 1031 // 1032 // When a host joins a multicast group, it should immediately transmit an 1033 // unsolicited Version 2 Membership Report for that group" ... "it is 1034 // recommended that it be repeated". 1035 // 1036 // As per RFC 2710 section 4 page 6 (for MLDv1), 1037 // 1038 // When a node starts listening to a multicast address on an interface, 1039 // it should immediately transmit an unsolicited Report for that address 1040 // on that interface, in case it is the first listener on the link. To 1041 // cover the possibility of the initial Report being lost or damaged, it 1042 // is recommended that it be repeated once or twice after short delays 1043 // [Unsolicited Report Interval]. 1044 // 1045 // TODO(gvisor.dev/issue/4901): Support a configurable number of initial 1046 // unsolicited reports. 1047 sent, err := g.opts.Protocol.SendReport(groupAddress) 1048 if err == nil && sent { 1049 info.lastToSendReport = true 1050 1051 info.transmissionLeft-- 1052 if info.transmissionLeft > 0 { 1053 g.setDelayTimerForAddressLocked( 1054 groupAddress, 1055 info, 1056 g.calculateDelayTimerDuration(g.opts.MaxUnsolicitedReportDelay), 1057 ) 1058 } 1059 } 1060 } 1061 1062 // maybeSendLeave attempts to send a leave message. 1063 func (g *GenericMulticastProtocolState) maybeSendLeave(groupAddress tcpip.Address, lastToSendReport bool) { 1064 if !g.shouldPerformForGroup(groupAddress) || !lastToSendReport { 1065 return 1066 } 1067 1068 // Okay to ignore the error here as if packet write failed, the multicast 1069 // routers will eventually drop our membership anyways. If the interface is 1070 // being disabled or removed, the generic multicast protocol's should be 1071 // cleared eventually. 1072 // 1073 // As per RFC 2236 section 3 page 5 (for IGMPv2), 1074 // 1075 // When a router receives a Report, it adds the group being reported to 1076 // the list of multicast group memberships on the network on which it 1077 // received the Report and sets the timer for the membership to the 1078 // [Group Membership Interval]. Repeated Reports refresh the timer. If 1079 // no Reports are received for a particular group before this timer has 1080 // expired, the router assumes that the group has no local members and 1081 // that it need not forward remotely-originated multicasts for that 1082 // group onto the attached network. 1083 // 1084 // As per RFC 2710 section 4 page 5 (for MLDv1), 1085 // 1086 // When a router receives a Report from a link, if the reported address 1087 // is not already present in the router's list of multicast address 1088 // having listeners on that link, the reported address is added to the 1089 // list, its timer is set to [Multicast Listener Interval], and its 1090 // appearance is made known to the router's multicast routing component. 1091 // If a Report is received for a multicast address that is already 1092 // present in the router's list, the timer for that address is reset to 1093 // [Multicast Listener Interval]. If an address's timer expires, it is 1094 // assumed that there are no longer any listeners for that address 1095 // present on the link, so it is deleted from the list and its 1096 // disappearance is made known to the multicast routing component. 1097 // 1098 // The requirement to send a leave message is also optional (it MAY be 1099 // skipped): 1100 // 1101 // As per RFC 2236 section 6 page 8 (for IGMPv2), 1102 // 1103 // "send leave" for the group on the interface. If the interface 1104 // state says the Querier is running IGMPv1, this action SHOULD be 1105 // skipped. If the flag saying we were the last host to report is 1106 // cleared, this action MAY be skipped. The Leave Message is sent to 1107 // the ALL-ROUTERS group (224.0.0.2). 1108 // 1109 // As per RFC 2710 section 5 page 8 (for MLDv1), 1110 // 1111 // "send done" for the address on the interface. If the flag saying 1112 // we were the last node to report is cleared, this action MAY be 1113 // skipped. The Done message is sent to the link-scope all-routers 1114 // address (FF02::2). 1115 _ = g.opts.Protocol.SendLeave(groupAddress) 1116 } 1117 1118 // transitionToNonMemberLocked transitions the given multicast group the the 1119 // non-member/listener state. 1120 // 1121 // Precondition: g.protocolMU must be locked. 1122 func (g *GenericMulticastProtocolState) transitionToNonMemberLocked(groupAddress tcpip.Address, info *multicastGroupState) { 1123 info.cancelDelayedReportJob() 1124 g.maybeSendLeave(groupAddress, info.lastToSendReport) 1125 info.lastToSendReport = false 1126 } 1127 1128 // setDelayTimerForAddressLocked sets timer to send a delayed report. 1129 // 1130 // Precondition: g.protocolMU MUST be locked. 1131 func (g *GenericMulticastProtocolState) setDelayTimerForAddressLocked(groupAddress tcpip.Address, info *multicastGroupState, maxResponseTime time.Duration) { 1132 if !g.shouldPerformForGroup(groupAddress) { 1133 return 1134 } 1135 1136 if info.transmissionLeft < minQueryResponseTransmissionCount { 1137 info.transmissionLeft = minQueryResponseTransmissionCount 1138 } 1139 1140 // As per RFC 2236 section 3 page 3 (for IGMPv2), 1141 // 1142 // If a timer for the group is already running, it is reset to the random 1143 // value only if the requested Max Response Time is less than the remaining 1144 // value of the running timer. 1145 // 1146 // As per RFC 2710 section 4 page 5 (for MLDv1), 1147 // 1148 // If a timer for any address is already running, it is reset to the new 1149 // random value only if the requested Maximum Response Delay is less than 1150 // the remaining value of the running timer. 1151 now := g.opts.Clock.Now() 1152 if !info.delayedReportJobFiresAt.IsZero() && info.delayedReportJobFiresAt.Sub(now) <= maxResponseTime { 1153 // The timer is scheduled to fire before the maximum response time so we 1154 // leave our timer as is. 1155 return 1156 } 1157 1158 info.delayedReportJob.Cancel() 1159 info.delayedReportJob.Schedule(maxResponseTime) 1160 info.delayedReportJobFiresAt = now.Add(maxResponseTime) 1161 } 1162 1163 // calculateDelayTimerDuration returns a random time between (0, maxRespTime]. 1164 func (g *GenericMulticastProtocolState) calculateDelayTimerDuration(maxRespTime time.Duration) time.Duration { 1165 // As per RFC 2236 section 3 page 3 (for IGMPv2), 1166 // 1167 // When a host receives a Group-Specific Query, it sets a delay timer to a 1168 // random value selected from the range (0, Max Response Time]... 1169 // 1170 // As per RFC 2710 section 4 page 6 (for MLDv1), 1171 // 1172 // When a node receives a Multicast-Address-Specific Query, if it is 1173 // listening to the queried Multicast Address on the interface from 1174 // which the Query was received, it sets a delay timer for that address 1175 // to a random value selected from the range [0, Maximum Response Delay], 1176 // as above. 1177 if maxRespTime == 0 { 1178 return 0 1179 } 1180 return time.Duration(g.opts.Rand.Int63n(int64(maxRespTime))) 1181 }