@@ -34,6 +34,18 @@ pub(crate) enum IgmpReportState {
34
34
} ,
35
35
}
36
36
37
+ #[ cfg( feature = "proto-ipv6" ) ]
38
+ pub ( crate ) enum MldReportState {
39
+ Inactive ,
40
+ ToGeneralQuery {
41
+ timeout : crate :: time:: Instant ,
42
+ } ,
43
+ ToSpecificQuery {
44
+ group : Ipv6Address ,
45
+ timeout : crate :: time:: Instant ,
46
+ } ,
47
+ }
48
+
37
49
#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
38
50
enum GroupState {
39
51
/// Joining group, we have to send the join packet.
@@ -49,6 +61,7 @@ pub(crate) struct State {
49
61
/// When to report for (all or) the next multicast group membership via IGMP
50
62
#[ cfg( feature = "proto-ipv4" ) ]
51
63
igmp_report_state : IgmpReportState ,
64
+ mld_report_state : MldReportState ,
52
65
}
53
66
54
67
impl State {
@@ -57,6 +70,7 @@ impl State {
57
70
groups : LinearMap :: new ( ) ,
58
71
#[ cfg( feature = "proto-ipv4" ) ]
59
72
igmp_report_state : IgmpReportState :: Inactive ,
73
+ mld_report_state : MldReportState :: Inactive ,
60
74
}
61
75
}
62
76
@@ -306,6 +320,46 @@ impl Interface {
306
320
}
307
321
_ => { }
308
322
}
323
+ #[ cfg( feature = "proto-ipv6" ) ]
324
+ match self . inner . multicast . mld_report_state {
325
+ MldReportState :: ToGeneralQuery { timeout } if self . inner . now >= timeout => {
326
+ let records = self
327
+ . inner
328
+ . multicast
329
+ . groups
330
+ . iter ( )
331
+ . filter_map ( |( addr, _) | match addr {
332
+ IpAddress :: Ipv6 ( addr) => Some ( MldAddressRecordRepr :: new (
333
+ MldRecordType :: ModeIsExclude ,
334
+ * addr,
335
+ ) ) ,
336
+ #[ allow( unreachable_patterns) ]
337
+ _ => None ,
338
+ } )
339
+ . collect :: < Vec < _ > > ( ) ;
340
+ if let Some ( pkt) = self . inner . mldv2_report_packet ( & records) {
341
+ if let Some ( tx_token) = device. transmit ( self . inner . now ) {
342
+ self . inner
343
+ . dispatch_ip ( tx_token, PacketMeta :: default ( ) , pkt, & mut self . fragmenter )
344
+ . unwrap ( ) ;
345
+ } ;
346
+ } ;
347
+ self . inner . multicast . mld_report_state = MldReportState :: Inactive ;
348
+ }
349
+ MldReportState :: ToSpecificQuery { group, timeout } if self . inner . now >= timeout => {
350
+ let record = MldAddressRecordRepr :: new ( MldRecordType :: ModeIsExclude , group) ;
351
+ if let Some ( pkt) = self . inner . mldv2_report_packet ( & [ record] ) {
352
+ if let Some ( tx_token) = device. transmit ( self . inner . now ) {
353
+ // NOTE(unwrap): packet destination is multicast, which is always routable and doesn't require neighbor discovery.
354
+ self . inner
355
+ . dispatch_ip ( tx_token, PacketMeta :: default ( ) , pkt, & mut self . fragmenter )
356
+ . unwrap ( ) ;
357
+ }
358
+ }
359
+ self . inner . multicast . mld_report_state = MldReportState :: Inactive ;
360
+ }
361
+ _ => { }
362
+ }
309
363
}
310
364
}
311
365
@@ -425,4 +479,53 @@ impl InterfaceInner {
425
479
)
426
480
} )
427
481
}
482
+
483
+ /// Host duties of the **MLDv2** protocol.
484
+ ///
485
+ /// Sets up `mld_report_state` for responding to MLD general/specific membership queries.
486
+ /// Membership must not be reported immediately in order to avoid flooding the network
487
+ /// after a query is broadcasted by a router; Currently the delay is fixed and not randomized.
488
+ #[ cfg( feature = "proto-ipv6" ) ]
489
+ pub ( super ) fn process_mldv2 < ' frame > (
490
+ & mut self ,
491
+ ip_repr : Ipv6Repr ,
492
+ repr : MldRepr < ' frame > ,
493
+ ) -> Option < Packet < ' frame > > {
494
+ match repr {
495
+ MldRepr :: Query {
496
+ mcast_addr,
497
+ max_resp_code,
498
+ ..
499
+ } => {
500
+ // Do not respont immediately to the query
501
+ let delay = crate :: time:: Duration :: from_millis ( max_resp_code. into ( ) ) / 3 ;
502
+ // General query
503
+ if mcast_addr. is_unspecified ( )
504
+ && ( ip_repr. dst_addr == IPV6_LINK_LOCAL_ALL_NODES
505
+ || self . has_ip_addr ( ip_repr. dst_addr ) )
506
+ {
507
+ let ipv6_multicast_group_count = self
508
+ . multicast
509
+ . groups
510
+ . keys ( )
511
+ . filter ( |a| matches ! ( a, IpAddress :: Ipv6 ( _) ) )
512
+ . count ( ) ;
513
+ if ipv6_multicast_group_count != 0 {
514
+ self . multicast . mld_report_state = MldReportState :: ToGeneralQuery {
515
+ timeout : self . now + delay,
516
+ } ;
517
+ }
518
+ }
519
+ if self . has_multicast_group ( mcast_addr) && ip_repr. dst_addr == mcast_addr {
520
+ self . multicast . mld_report_state = MldReportState :: ToSpecificQuery {
521
+ group : mcast_addr,
522
+ timeout : self . now + delay,
523
+ } ;
524
+ }
525
+ None
526
+ }
527
+ MldRepr :: Report { .. } => None ,
528
+ MldRepr :: ReportRecordReprs { .. } => None ,
529
+ }
530
+ }
428
531
}
0 commit comments