1use core::borrow::Borrow;
10use core::fmt::Debug;
11use core::mem::size_of;
12use core::ops::Deref;
13use core::time::Duration;
14
15use net_types::ip::{Ip, Ipv6, Ipv6Addr};
16use net_types::{MulticastAddr, Witness as _};
17use packet::records::{ParsedRecord, RecordParseResult, Records, RecordsImpl, RecordsImplLayout};
18use packet::serialize::InnerPacketBuilder;
19use packet::BufferView;
20use zerocopy::byteorder::network_endian::U16;
21use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Ref, SplitByteSlice, Unaligned};
22
23use crate::error::{ParseError, ParseResult, UnrecognizedProtocolCode};
24use crate::gmp::{GmpReportGroupRecord, InvalidConstraintsError, LinExpConversion, OverflowError};
25use crate::icmp::{
26 IcmpIpExt, IcmpMessage, IcmpPacket, IcmpPacketRaw, IcmpSenderZeroCode, MessageBody,
27};
28
29#[derive(Debug, Immutable)]
36pub enum UninstantiableRecord {}
37
38unsafe impl IntoBytes for UninstantiableRecord {
42 fn only_derive_is_allowed_to_implement_this_trait() {
43 panic!("UninstantiableRecord cannot be instantiated");
44 }
45}
46
47#[allow(missing_docs)]
49#[derive(Debug)]
50pub enum MldPacket<B: SplitByteSlice> {
51 MulticastListenerQuery(IcmpPacket<Ipv6, B, MulticastListenerQuery>),
52 MulticastListenerReport(IcmpPacket<Ipv6, B, MulticastListenerReport>),
53 MulticastListenerDone(IcmpPacket<Ipv6, B, MulticastListenerDone>),
54 MulticastListenerQueryV2(IcmpPacket<Ipv6, B, MulticastListenerQueryV2>),
55 MulticastListenerReportV2(IcmpPacket<Ipv6, B, MulticastListenerReportV2>),
56}
57
58#[allow(missing_docs)]
60#[derive(Debug)]
61pub enum MldPacketRaw<B: SplitByteSlice> {
62 MulticastListenerQuery(IcmpPacketRaw<Ipv6, B, MulticastListenerQuery>),
63 MulticastListenerReport(IcmpPacketRaw<Ipv6, B, MulticastListenerReport>),
64 MulticastListenerDone(IcmpPacketRaw<Ipv6, B, MulticastListenerDone>),
65 MulticastListenerQueryV2(IcmpPacketRaw<Ipv6, B, MulticastListenerQueryV2>),
66 MulticastListenerReportV2(IcmpPacketRaw<Ipv6, B, MulticastListenerReportV2>),
67}
68
69pub type Mldv2MulticastRecordType = crate::gmp::GroupRecordType;
76
77#[derive(Copy, Clone, Debug, IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned)]
82#[repr(C)]
83pub struct Mldv2ReportRecordHeader {
84 record_type: u8,
85 aux_data_len: u8,
86 number_of_sources: U16,
87 multicast_address: Ipv6Addr,
88}
89
90impl Mldv2ReportRecordHeader {
91 pub fn new(
93 record_type: Mldv2MulticastRecordType,
94 aux_data_len: u8,
95 number_of_sources: u16,
96 multicast_address: Ipv6Addr,
97 ) -> Self {
98 Mldv2ReportRecordHeader {
99 record_type: record_type.into(),
100 aux_data_len,
101 number_of_sources: number_of_sources.into(),
102 multicast_address,
103 }
104 }
105
106 pub fn number_of_sources(&self) -> u16 {
108 self.number_of_sources.get()
109 }
110
111 pub fn record_type(&self) -> Result<Mldv2MulticastRecordType, UnrecognizedProtocolCode<u8>> {
113 Mldv2MulticastRecordType::try_from(self.record_type)
114 }
115
116 pub fn multicast_addr(&self) -> &Ipv6Addr {
118 &self.multicast_address
119 }
120}
121
122pub struct MulticastRecord<B> {
127 header: Ref<B, Mldv2ReportRecordHeader>,
128 sources: Ref<B, [Ipv6Addr]>,
129}
130
131impl<B: SplitByteSlice> MulticastRecord<B> {
132 pub fn header(&self) -> &Mldv2ReportRecordHeader {
134 self.header.deref()
135 }
136
137 pub fn sources(&self) -> &[Ipv6Addr] {
139 self.sources.deref()
140 }
141}
142
143#[derive(Copy, Clone, Debug)]
145pub enum Mldv2ReportRecords {}
146
147impl RecordsImplLayout for Mldv2ReportRecords {
148 type Context = usize;
149 type Error = ParseError;
150}
151
152impl RecordsImpl for Mldv2ReportRecords {
153 type Record<'a> = MulticastRecord<&'a [u8]>;
154
155 fn parse_with_context<'a, BV: BufferView<&'a [u8]>>(
156 data: &mut BV,
157 _ctx: &mut usize,
158 ) -> RecordParseResult<MulticastRecord<&'a [u8]>, ParseError> {
159 let header = data
160 .take_obj_front::<Mldv2ReportRecordHeader>()
161 .ok_or_else(debug_err_fn!(ParseError::Format, "Can't take multicast record header"))?;
162 let sources = data
163 .take_slice_front::<Ipv6Addr>(header.number_of_sources().into())
164 .ok_or_else(debug_err_fn!(ParseError::Format, "Can't take multicast record sources"))?;
165 let _ = data
168 .take_front(usize::from(header.aux_data_len) * 4)
169 .ok_or_else(debug_err_fn!(ParseError::Format, "Can't skip auxiliary data"))?;
170
171 Ok(ParsedRecord::Parsed(Self::Record { header, sources }))
172 }
173}
174
175#[repr(C)]
179#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
180pub struct Mldv2ReportHeader {
181 _reserved: [u8; 2],
183 num_mcast_addr_records: U16,
185}
186
187impl Mldv2ReportHeader {
188 pub fn new(num_mcast_addr_records: u16) -> Self {
190 Mldv2ReportHeader {
191 _reserved: [0, 0],
192 num_mcast_addr_records: U16::from(num_mcast_addr_records),
193 }
194 }
195 pub fn num_mcast_addr_records(&self) -> u16 {
197 self.num_mcast_addr_records.get()
198 }
199}
200
201#[derive(Debug)]
206pub struct Mldv2ReportBody<B: SplitByteSlice> {
207 header: Ref<B, Mldv2ReportHeader>,
208 records: Records<B, Mldv2ReportRecords>,
209}
210
211impl<B: SplitByteSlice> Mldv2ReportBody<B> {
212 pub fn header(&self) -> &Mldv2ReportHeader {
214 self.header.deref()
215 }
216
217 pub fn iter_multicast_records(&self) -> impl Iterator<Item = MulticastRecord<&'_ [u8]>> {
219 self.records.iter()
220 }
221}
222
223impl<B: SplitByteSlice> MessageBody for Mldv2ReportBody<B> {
224 type B = B;
225 fn parse(bytes: B) -> ParseResult<Self> {
226 let (header, bytes) =
227 Ref::<_, Mldv2ReportHeader>::from_prefix(bytes).map_err(|_| ParseError::Format)?;
228 let records = Records::parse_with_context(bytes, header.num_mcast_addr_records().into())?;
229 Ok(Mldv2ReportBody { header, records })
230 }
231
232 fn len(&self) -> usize {
233 let (inner_header, inner_body) = self.bytes();
234 inner_header.len() + inner_body.unwrap().len()
237 }
238
239 fn bytes(&self) -> (&[u8], Option<&[u8]>) {
240 (Ref::bytes(&self.header), Some(self.records.bytes()))
241 }
242}
243
244#[repr(C)]
246#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
247pub struct MulticastListenerReportV2;
248
249impl_icmp_message!(
250 Ipv6,
251 MulticastListenerReportV2,
252 MulticastListenerReportV2,
253 IcmpSenderZeroCode,
254 Mldv2ReportBody<B>
255);
256
257#[repr(C)]
259#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
260pub struct MulticastListenerQuery;
261
262#[repr(C)]
264#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
265pub struct MulticastListenerReport;
266
267#[repr(C)]
269#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
270pub struct MulticastListenerDone;
271
272pub trait Mldv1MessageType {
274 type MaxRespDelay: MaxCode<U16> + Debug + Copy;
279 type GroupAddr: Into<Ipv6Addr> + Debug + Copy;
286}
287
288pub trait IcmpMldv1MessageType:
290 Mldv1MessageType + IcmpMessage<Ipv6, Code = IcmpSenderZeroCode>
291{
292}
293
294pub trait MaxCode<T: Default + Debug + FromBytes + IntoBytes> {
299 #[allow(clippy::wrong_self_convention)]
301 fn as_code(self) -> T;
302
303 fn from_code(code: T) -> Self;
305}
306
307impl<T: Default + Debug + FromBytes + IntoBytes> MaxCode<T> for () {
308 fn as_code(self) -> T {
309 T::default()
310 }
311
312 fn from_code(_: T) -> Self {}
313}
314
315#[derive(PartialEq, Eq, Debug, Clone, Copy)]
317pub struct Mldv1ResponseDelay(u16);
318
319impl MaxCode<U16> for Mldv1ResponseDelay {
320 fn as_code(self) -> U16 {
321 U16::new(self.0)
322 }
323
324 fn from_code(code: U16) -> Self {
325 Mldv1ResponseDelay(code.get())
326 }
327}
328
329impl From<Mldv1ResponseDelay> for Duration {
330 fn from(code: Mldv1ResponseDelay) -> Self {
331 Duration::from_millis(code.0.into())
332 }
333}
334
335impl TryFrom<Duration> for Mldv1ResponseDelay {
336 type Error = OverflowError;
337 fn try_from(period: Duration) -> Result<Self, Self::Error> {
338 Ok(Mldv1ResponseDelay(u16::try_from(period.as_millis()).map_err(|_| OverflowError)?))
339 }
340}
341
342#[repr(C)]
344#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
345pub struct Mldv1Message {
346 pub max_response_delay: U16,
348 _reserved: U16,
350 pub group_addr: Ipv6Addr,
358}
359
360impl Mldv1Message {
361 pub fn max_response_delay(&self) -> Duration {
363 Mldv1ResponseDelay(self.max_response_delay.get()).into()
364 }
365}
366
367#[derive(Debug)]
369pub struct Mldv1Body<B: SplitByteSlice>(Ref<B, Mldv1Message>);
370
371impl<B: SplitByteSlice> Deref for Mldv1Body<B> {
372 type Target = Mldv1Message;
373
374 fn deref(&self) -> &Self::Target {
375 &*self.0
376 }
377}
378
379impl<B: SplitByteSlice> MessageBody for Mldv1Body<B> {
380 type B = B;
381 fn parse(bytes: B) -> ParseResult<Self> {
382 Ref::from_bytes(bytes).map_or(Err(ParseError::Format), |body| Ok(Mldv1Body(body)))
383 }
384
385 fn len(&self) -> usize {
386 let (inner_header, _inner_body) = self.bytes();
387 debug_assert!(_inner_body.is_none());
388 inner_header.len()
389 }
390
391 fn bytes(&self) -> (&[u8], Option<&[u8]>) {
392 (Ref::bytes(&self.0), None)
393 }
394}
395
396macro_rules! impl_mldv1_message {
397 ($msg:ident, $resp_code:ty, $group_addr:ty) => {
398 impl_icmp_message!(Ipv6, $msg, $msg, IcmpSenderZeroCode, Mldv1Body<B>);
399 impl Mldv1MessageType for $msg {
400 type MaxRespDelay = $resp_code;
401 type GroupAddr = $group_addr;
402 }
403 impl IcmpMldv1MessageType for $msg {}
404 };
405}
406
407impl_mldv1_message!(MulticastListenerQuery, Mldv1ResponseDelay, Ipv6Addr);
408impl_mldv1_message!(MulticastListenerReport, (), MulticastAddr<Ipv6Addr>);
409impl_mldv1_message!(MulticastListenerDone, (), MulticastAddr<Ipv6Addr>);
410
411#[derive(Debug)]
413pub struct Mldv1MessageBuilder<M: Mldv1MessageType> {
414 max_resp_delay: M::MaxRespDelay,
415 group_addr: M::GroupAddr,
416}
417
418impl<M: Mldv1MessageType<MaxRespDelay = ()>> Mldv1MessageBuilder<M> {
419 pub fn new(group_addr: M::GroupAddr) -> Self {
422 Mldv1MessageBuilder { max_resp_delay: (), group_addr }
423 }
424}
425
426impl<M: Mldv1MessageType> Mldv1MessageBuilder<M> {
427 pub fn new_with_max_resp_delay(
430 group_addr: M::GroupAddr,
431 max_resp_delay: M::MaxRespDelay,
432 ) -> Self {
433 Mldv1MessageBuilder { max_resp_delay, group_addr }
434 }
435
436 fn serialize_message(&self, mut buf: &mut [u8]) {
437 use packet::BufferViewMut;
438 let mut bytes = &mut buf;
439 bytes
440 .write_obj_front(&Mldv1Message {
441 max_response_delay: self.max_resp_delay.as_code(),
442 _reserved: U16::ZERO,
443 group_addr: self.group_addr.into(),
444 })
445 .expect("too few bytes for MLDv1 message");
446 }
447}
448
449impl<M: Mldv1MessageType> InnerPacketBuilder for Mldv1MessageBuilder<M> {
450 fn bytes_len(&self) -> usize {
451 size_of::<Mldv1Message>()
452 }
453
454 fn serialize(&self, buf: &mut [u8]) {
455 self.serialize_message(buf);
456 }
457}
458
459#[derive(Debug)]
461pub struct Mldv2QueryMessageBuilder<I> {
462 max_response_delay: Mldv2ResponseDelay,
463 group_addr: Option<MulticastAddr<Ipv6Addr>>,
464 s_flag: bool,
465 qrv: Mldv2QRV,
466 qqic: Mldv2QQIC,
467 sources: I,
468}
469
470impl<I> Mldv2QueryMessageBuilder<I> {
471 pub fn new(
473 max_response_delay: Mldv2ResponseDelay,
474 group_addr: Option<MulticastAddr<Ipv6Addr>>,
475 s_flag: bool,
476 qrv: Mldv2QRV,
477 qqic: Mldv2QQIC,
478 sources: I,
479 ) -> Self {
480 Self { max_response_delay, group_addr, s_flag, qrv, qqic, sources }
481 }
482}
483
484impl<I> InnerPacketBuilder for Mldv2QueryMessageBuilder<I>
485where
486 I: Iterator<Item: Borrow<Ipv6Addr>> + Clone,
487{
488 fn bytes_len(&self) -> usize {
489 core::mem::size_of::<Mldv2QueryMessageHeader>()
490 + self.sources.clone().count() * core::mem::size_of::<Ipv6Addr>()
491 }
492
493 fn serialize(&self, mut buf: &mut [u8]) {
494 use packet::BufferViewMut;
495 let mut bytes = &mut buf;
496 let mut header = bytes
497 .take_obj_front_zero::<Mldv2QueryMessageHeader>()
498 .expect("too few bytes for header");
499 let Mldv2QueryMessageHeader {
500 max_response_code,
501 _reserved,
502 group_addr,
503 sqrv,
504 qqic,
505 number_of_sources,
506 } = &mut *header;
507 let Self {
508 max_response_delay,
509 group_addr: wr_group_addr,
510 s_flag,
511 qrv,
512 qqic: wr_qqic,
513 sources,
514 } = self;
515 *max_response_code = max_response_delay.as_code();
516 *group_addr =
517 wr_group_addr.as_ref().map(|addr| addr.get()).unwrap_or(Ipv6::UNSPECIFIED_ADDRESS);
518 *sqrv = (u8::from(*s_flag) << 3) | (Mldv2QueryMessageHeader::QRV_MASK & u8::from(*qrv));
521 *qqic = wr_qqic.as_code();
522 let mut count: u16 = 0;
523 for src in sources.clone() {
524 count = count.checked_add(1).expect("overflowed number of sources");
525 bytes.write_obj_front(src.borrow()).expect("too few bytes for source");
526 }
527 *number_of_sources = count.into();
528 }
529}
530
531#[derive(Debug)]
533pub struct Mldv2ReportMessageBuilder<I> {
534 groups: I,
535}
536
537impl<I> Mldv2ReportMessageBuilder<I> {
538 pub fn new(groups: I) -> Self {
540 Self { groups }
541 }
542}
543
544impl<I> Mldv2ReportMessageBuilder<I>
545where
546 I: Iterator<Item: GmpReportGroupRecord<Ipv6Addr> + Clone> + Clone,
547{
548 pub fn with_len_limits(
559 self,
560 max_len: usize,
561 ) -> Result<
562 impl Iterator<
563 Item = Mldv2ReportMessageBuilder<
564 impl Iterator<Item: GmpReportGroupRecord<Ipv6Addr>> + Clone,
565 >,
566 >,
567 InvalidConstraintsError,
568 > {
569 let Self { groups } = self;
570 crate::gmp::group_record_split_iterator(
571 max_len.saturating_sub(core::mem::size_of::<Mldv2ReportHeader>()),
572 core::mem::size_of::<Mldv2ReportRecordHeader>(),
573 groups,
574 )
575 .map(|iter| iter.map(|groups| Mldv2ReportMessageBuilder { groups }))
576 }
577}
578
579impl<I> InnerPacketBuilder for Mldv2ReportMessageBuilder<I>
580where
581 I: Iterator<Item: GmpReportGroupRecord<Ipv6Addr>> + Clone,
582{
583 fn bytes_len(&self) -> usize {
584 core::mem::size_of::<Mldv2ReportHeader>()
585 + self
586 .groups
587 .clone()
588 .map(|g| {
589 core::mem::size_of::<Mldv2ReportRecordHeader>()
590 + g.sources().count() * core::mem::size_of::<Ipv6Addr>()
591 })
592 .sum::<usize>()
593 }
594
595 fn serialize(&self, mut buf: &mut [u8]) {
596 use packet::BufferViewMut;
597 let mut bytes = &mut buf;
598 let mut header =
599 bytes.take_obj_front_zero::<Mldv2ReportHeader>().expect("too few bytes for header");
600 let Mldv2ReportHeader { _reserved, num_mcast_addr_records } = &mut *header;
601 let mut mcast_count: u16 = 0;
602 for group in self.groups.clone() {
603 mcast_count = mcast_count.checked_add(1).expect("multicast groups count overflows");
604 let mut header = bytes
605 .take_obj_front_zero::<Mldv2ReportRecordHeader>()
606 .expect("too few bytes for record header");
607 let Mldv2ReportRecordHeader {
608 record_type,
609 aux_data_len,
610 number_of_sources,
611 multicast_address,
612 } = &mut *header;
613 *record_type = group.record_type().into();
614 *aux_data_len = 0;
615 *multicast_address = group.group().into();
616 let mut source_count: u16 = 0;
617 for src in group.sources() {
618 source_count = source_count.checked_add(1).expect("sources count overflows");
619 bytes.write_obj_front(src.borrow()).expect("too few bytes for source");
620 }
621 *number_of_sources = source_count.into();
622 }
623 *num_mcast_addr_records = mcast_count.into();
624 }
625}
626
627#[derive(PartialEq, Eq, Debug, Clone, Copy, Default)]
633pub struct Mldv2ResponseDelay(u16);
634
635impl LinExpConversion<Duration> for Mldv2ResponseDelay {
636 const NUM_MANT_BITS: u8 = 12;
637 const NUM_EXP_BITS: u8 = 3;
638
639 fn lossy_try_from(value: Duration) -> Result<Self, OverflowError> {
640 let millis: u32 = value.as_millis().try_into().map_err(|_| OverflowError)?;
641 Self::lossy_try_from_expanded(millis).map(Self)
642 }
643}
644
645impl MaxCode<U16> for Mldv2ResponseDelay {
646 fn as_code(self) -> U16 {
647 U16::new(self.0)
648 }
649
650 fn from_code(code: U16) -> Self {
651 Mldv2ResponseDelay(code.get())
652 }
653}
654
655impl From<Mldv2ResponseDelay> for Duration {
656 fn from(code: Mldv2ResponseDelay) -> Self {
657 Duration::from_millis(Mldv2ResponseDelay::to_expanded(code.0).into())
658 }
659}
660
661pub type Mldv2QRV = crate::gmp::QRV;
669
670pub type Mldv2QQIC = crate::gmp::QQIC;
678
679impl MaxCode<u8> for Mldv2QQIC {
680 fn as_code(self) -> u8 {
681 self.into()
682 }
683
684 fn from_code(code: u8) -> Self {
685 code.into()
686 }
687}
688
689#[repr(C)]
711#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
712pub struct Mldv2QueryMessageHeader {
713 max_response_code: U16,
715 _reserved: U16,
717 group_addr: Ipv6Addr,
721
722 sqrv: u8,
730 qqic: u8,
732 number_of_sources: U16,
735}
736
737impl Mldv2QueryMessageHeader {
738 const S_FLAG_MASK: u8 = (1 << 3);
739 const QRV_MASK: u8 = 0x07;
740
741 pub fn max_response_delay(&self) -> Mldv2ResponseDelay {
743 Mldv2ResponseDelay(self.max_response_code.get())
744 }
745
746 pub fn group_address(&self) -> Ipv6Addr {
748 self.group_addr
749 }
750
751 pub fn number_of_sources(&self) -> u16 {
753 self.number_of_sources.get()
754 }
755
756 pub fn suppress_router_side_processing(&self) -> bool {
758 (self.sqrv & Self::S_FLAG_MASK) != 0
759 }
760
761 pub fn querier_robustness_variable(&self) -> u8 {
763 self.sqrv & Self::QRV_MASK
764 }
765
766 pub fn querier_query_interval(&self) -> Duration {
768 Mldv2QQIC::from(self.qqic).into()
769 }
770}
771
772#[derive(Debug)]
777pub struct Mldv2QueryBody<B: SplitByteSlice> {
778 header: Ref<B, Mldv2QueryMessageHeader>,
779 sources: Ref<B, [Ipv6Addr]>,
780}
781
782impl<B: SplitByteSlice> Mldv2QueryBody<B> {
783 pub fn header(&self) -> &Mldv2QueryMessageHeader {
785 self.header.deref()
786 }
787
788 pub fn sources(&self) -> &[Ipv6Addr] {
790 self.sources.deref()
791 }
792
793 pub fn as_v1_query(&self) -> Mldv1Body<&[u8]> {
807 let Self { header, sources: _ } = self;
808 let (msg, _rest) = Ref::from_prefix(header.as_bytes()).unwrap();
811 Mldv1Body(msg)
812 }
813}
814
815impl<B: SplitByteSlice> MessageBody for Mldv2QueryBody<B> {
816 type B = B;
817 fn parse(bytes: B) -> ParseResult<Self> {
818 let (header, bytes) = Ref::<_, Mldv2QueryMessageHeader>::from_prefix(bytes)
819 .map_err(|_| ParseError::Format)?;
820 let sources = Ref::<B, [Ipv6Addr]>::from_bytes(bytes).map_err(|_| ParseError::Format)?;
821 Ok(Mldv2QueryBody { header, sources })
822 }
823
824 fn len(&self) -> usize {
825 let (inner_header, inner_body) = self.bytes();
826 inner_header.len() + inner_body.unwrap().len()
829 }
830
831 fn bytes(&self) -> (&[u8], Option<&[u8]>) {
832 (Ref::bytes(&self.header), Some(Ref::bytes(&self.sources)))
833 }
834}
835
836#[repr(C)]
838#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
839pub struct MulticastListenerQueryV2;
840
841impl_icmp_message!(
842 Ipv6,
843 MulticastListenerQueryV2,
844 MulticastListenerQuery,
845 IcmpSenderZeroCode,
846 Mldv2QueryBody<B>
847);
848
849#[cfg(test)]
850mod tests {
851
852 use packet::{ParseBuffer, Serializer};
853 use test_case::test_case;
854
855 use super::*;
856 use crate::gmp::ExactConversionError;
857 use crate::icmp::{IcmpPacketBuilder, IcmpParseArgs};
858 use crate::ip::Ipv6Proto;
859 use crate::ipv6::ext_hdrs::{
860 ExtensionHeaderOptionAction, HopByHopOption, HopByHopOptionData, Ipv6ExtensionHeaderData,
861 };
862 use crate::ipv6::{Ipv6Header, Ipv6Packet, Ipv6PacketBuilder, Ipv6PacketBuilderWithHbhOptions};
863
864 fn serialize_to_bytes<B: SplitByteSlice + Debug, M: IcmpMessage<Ipv6> + Debug>(
865 src_ip: Ipv6Addr,
866 dst_ip: Ipv6Addr,
867 icmp: &IcmpPacket<Ipv6, B, M>,
868 ) -> Vec<u8> {
869 let ip = Ipv6PacketBuilder::new(src_ip, dst_ip, 1, Ipv6Proto::Icmpv6);
870 let with_options = Ipv6PacketBuilderWithHbhOptions::new(
871 ip,
872 &[HopByHopOption {
873 action: ExtensionHeaderOptionAction::SkipAndContinue,
874 mutable: false,
875 data: HopByHopOptionData::RouterAlert { data: 0 },
876 }],
877 )
878 .unwrap();
879 let (header, body) = icmp.message_body.bytes();
880 let body = if let Some(b) = body { b } else { &[] };
881 let complete_msg = &[header, body].concat();
882 complete_msg
883 .into_serializer()
884 .encapsulate(icmp.builder(src_ip, dst_ip))
885 .encapsulate(with_options)
886 .serialize_vec_outer()
887 .unwrap()
888 .as_ref()
889 .to_vec()
890 }
891
892 fn test_parse_and_serialize<
893 M: IcmpMessage<Ipv6> + Debug,
894 F: FnOnce(&Ipv6Packet<&[u8]>),
895 G: for<'a> FnOnce(&IcmpPacket<Ipv6, &'a [u8], M>),
896 >(
897 src_ip: Ipv6Addr,
898 dst_ip: Ipv6Addr,
899 mut req: &[u8],
900 check_ip: F,
901 check_icmp: G,
902 ) {
903 let orig_req = req;
904
905 let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
906 check_ip(&ip);
907 let icmp =
908 req.parse_with::<_, IcmpPacket<_, _, M>>(IcmpParseArgs::new(src_ip, dst_ip)).unwrap();
909 check_icmp(&icmp);
910
911 let data = serialize_to_bytes(src_ip, dst_ip, &icmp);
912 assert_eq!(&data[..], orig_req);
913 }
914
915 fn serialize_to_bytes_with_builder<M: IcmpMldv1MessageType + Debug>(
916 src_ip: Ipv6Addr,
917 dst_ip: Ipv6Addr,
918 msg: M,
919 group_addr: M::GroupAddr,
920 max_resp_delay: M::MaxRespDelay,
921 ) -> Vec<u8> {
922 let ip = Ipv6PacketBuilder::new(src_ip, dst_ip, 1, Ipv6Proto::Icmpv6);
923 let with_options = Ipv6PacketBuilderWithHbhOptions::new(
924 ip,
925 &[HopByHopOption {
926 action: ExtensionHeaderOptionAction::SkipAndContinue,
927 mutable: false,
928 data: HopByHopOptionData::RouterAlert { data: 0 },
929 }],
930 )
931 .unwrap();
932 Mldv1MessageBuilder::<M>::new_with_max_resp_delay(group_addr, max_resp_delay)
934 .into_serializer()
935 .encapsulate(IcmpPacketBuilder::new(src_ip, dst_ip, IcmpSenderZeroCode, msg))
936 .encapsulate(with_options)
937 .serialize_vec_outer()
938 .unwrap()
939 .as_ref()
940 .to_vec()
941 }
942
943 fn serialize_to_bytes_with_builder_v2<
944 M: IcmpMessage<Ipv6, Code = IcmpSenderZeroCode> + Debug,
945 B: InnerPacketBuilder + Debug,
946 >(
947 src_ip: Ipv6Addr,
948 dst_ip: Ipv6Addr,
949 msg: M,
950 builder: B,
951 ) -> Vec<u8> {
952 let ip = Ipv6PacketBuilder::new(src_ip, dst_ip, 1, Ipv6Proto::Icmpv6);
953 let with_options = Ipv6PacketBuilderWithHbhOptions::new(
954 ip,
955 &[HopByHopOption {
956 action: ExtensionHeaderOptionAction::SkipAndContinue,
957 mutable: false,
958 data: HopByHopOptionData::RouterAlert { data: 0 },
959 }],
960 )
961 .unwrap();
962 builder
965 .into_serializer()
966 .encapsulate(IcmpPacketBuilder::new(src_ip, dst_ip, IcmpSenderZeroCode, msg))
967 .encapsulate(with_options)
968 .serialize_vec_outer()
969 .unwrap()
970 .as_ref()
971 .to_vec()
972 }
973
974 fn check_ip<B: SplitByteSlice>(ip: &Ipv6Packet<B>, src_ip: Ipv6Addr, dst_ip: Ipv6Addr) {
975 assert_eq!(ip.src_ip(), src_ip);
976 assert_eq!(ip.dst_ip(), dst_ip);
977 assert_eq!(ip.iter_extension_hdrs().count(), 1);
978 let hbh = ip.iter_extension_hdrs().next().unwrap();
979 match hbh.data() {
980 Ipv6ExtensionHeaderData::HopByHopOptions { options } => {
981 assert_eq!(options.iter().count(), 1);
982 assert_eq!(
983 options.iter().next().unwrap(),
984 HopByHopOption {
985 action: ExtensionHeaderOptionAction::SkipAndContinue,
986 mutable: false,
987 data: HopByHopOptionData::RouterAlert { data: 0 },
988 }
989 );
990 }
991 _ => panic!("Wrong extension header"),
992 }
993 }
994
995 fn check_mld_v1<
996 B: SplitByteSlice,
997 M: IcmpMessage<Ipv6, Body<B> = Mldv1Body<B>> + Mldv1MessageType + Debug,
998 >(
999 icmp: &IcmpPacket<Ipv6, B, M>,
1000 max_resp_code: u16,
1001 group_addr: Ipv6Addr,
1002 ) {
1003 assert_eq!(icmp.message_body._reserved.get(), 0);
1004 assert_eq!(icmp.message_body.max_response_delay.get(), max_resp_code);
1005 assert_eq!(icmp.message_body.group_addr, group_addr);
1006 }
1007
1008 fn check_mld_query_v2<
1009 'a,
1010 B: SplitByteSlice,
1011 M: IcmpMessage<Ipv6, Body<B> = Mldv2QueryBody<B>> + Debug,
1012 >(
1013 icmp: &IcmpPacket<Ipv6, B, M>,
1014 max_resp_code: u16,
1015 group_addr: Ipv6Addr,
1016 sources: &[Ipv6Addr],
1017 ) {
1018 assert_eq!(icmp.message_body.header._reserved.get(), 0);
1019 assert_eq!(icmp.message_body.header.max_response_code.get(), max_resp_code);
1020 assert_eq!(icmp.message_body.header.group_addr, group_addr);
1021 assert_eq!(icmp.message_body.sources.len(), sources.len());
1022 for (expected, actual) in sources.iter().zip(icmp.message_body.sources.into_iter()) {
1023 assert_eq!(actual, expected);
1024 }
1025
1026 let Mldv1Body(v1) = icmp.message_body.as_v1_query();
1028 assert_eq!(v1.max_response_delay.get(), max_resp_code);
1029 assert_eq!(v1.group_addr, group_addr);
1030 }
1031
1032 fn check_mld_report_v2<
1033 'a,
1034 B: SplitByteSlice,
1035 M: IcmpMessage<Ipv6, Body<B> = Mldv2ReportBody<B>> + Debug,
1036 >(
1037 icmp: &IcmpPacket<Ipv6, B, M>,
1038 expected_records_header: &[(Mldv2MulticastRecordType, Ipv6Addr)],
1039 expected_records_sources: &[&[Ipv6Addr]],
1040 ) {
1041 assert_eq!(
1042 icmp.message_body.header.num_mcast_addr_records.get(),
1043 u16::try_from(expected_records_header.len()).unwrap()
1044 );
1045 let expected_records = expected_records_header.iter().zip(expected_records_sources.iter());
1046 for (expected_record, actual_record) in
1047 expected_records.zip(icmp.message_body.iter_multicast_records())
1048 {
1049 let (expected_header, expected_sources) = expected_record;
1050 let (expected_record_type, expected_multicast_addr) = expected_header;
1051 assert_eq!(
1052 expected_record_type,
1053 &actual_record.header.record_type().expect("valid record type")
1054 );
1055
1056 assert_eq!(
1057 u16::try_from(expected_sources.len()).unwrap(),
1058 actual_record.header.number_of_sources()
1059 );
1060 assert_eq!(expected_multicast_addr, actual_record.header.multicast_addr());
1061 assert_eq!(*expected_sources, actual_record.sources());
1062 }
1063 }
1064
1065 #[test]
1066 fn test_mld_parse_and_serialize_query() {
1067 use crate::icmp::mld::MulticastListenerQuery;
1068 use crate::testdata::mld_router_query::*;
1069 test_parse_and_serialize::<MulticastListenerQuery, _, _>(
1070 SRC_IP,
1071 DST_IP,
1072 QUERY,
1073 |ip| {
1074 check_ip(ip, SRC_IP, DST_IP);
1075 },
1076 |icmp| {
1077 check_mld_v1(icmp, MAX_RESP_CODE, HOST_GROUP_ADDRESS);
1078 },
1079 );
1080 }
1081
1082 #[test]
1083 fn test_mld_parse_and_serialize_report() {
1084 use crate::icmp::mld::MulticastListenerReport;
1085 use crate::testdata::mld_router_report::*;
1086 test_parse_and_serialize::<MulticastListenerReport, _, _>(
1087 SRC_IP,
1088 DST_IP,
1089 REPORT,
1090 |ip| {
1091 check_ip(ip, SRC_IP, DST_IP);
1092 },
1093 |icmp| {
1094 check_mld_v1(icmp, 0, HOST_GROUP_ADDRESS);
1095 },
1096 );
1097 }
1098
1099 #[test]
1100 fn test_mld_parse_and_serialize_done() {
1101 use crate::icmp::mld::MulticastListenerDone;
1102 use crate::testdata::mld_router_done::*;
1103 test_parse_and_serialize::<MulticastListenerDone, _, _>(
1104 SRC_IP,
1105 DST_IP,
1106 DONE,
1107 |ip| {
1108 check_ip(ip, SRC_IP, DST_IP);
1109 },
1110 |icmp| {
1111 check_mld_v1(icmp, 0, HOST_GROUP_ADDRESS);
1112 },
1113 );
1114 }
1115
1116 #[test]
1117 fn test_mld_parse_and_serialize_query_v2() {
1118 use crate::icmp::mld::MulticastListenerQueryV2;
1119 use crate::testdata::mld_router_query::*;
1120 test_parse_and_serialize::<MulticastListenerQueryV2, _, _>(
1121 SRC_IP,
1122 DST_IP,
1123 QUERY_V2,
1124 |ip| {
1125 check_ip(ip, SRC_IP, DST_IP);
1126 },
1127 |icmp| {
1128 check_mld_query_v2(icmp, MAX_RESP_CODE, HOST_GROUP_ADDRESS, SOURCES);
1129 },
1130 );
1131 }
1132
1133 #[test]
1134 fn test_mld_parse_and_serialize_report_v2() {
1135 use crate::icmp::mld::MulticastListenerReportV2;
1136 use crate::testdata::mld_router_report_v2::*;
1137 test_parse_and_serialize::<MulticastListenerReportV2, _, _>(
1138 SRC_IP,
1139 DST_IP,
1140 REPORT,
1141 |ip| {
1142 check_ip(ip, SRC_IP, DST_IP);
1143 },
1144 |icmp| {
1145 check_mld_report_v2(icmp, RECORDS_HEADERS, RECORDS_SOURCES);
1146 },
1147 );
1148 }
1149
1150 #[test]
1151 fn test_mld_serialize_and_parse_query() {
1152 use crate::icmp::mld::MulticastListenerQuery;
1153 use crate::testdata::mld_router_query::*;
1154 let bytes = serialize_to_bytes_with_builder::<_>(
1155 SRC_IP,
1156 DST_IP,
1157 MulticastListenerQuery,
1158 HOST_GROUP_ADDRESS,
1159 Duration::from_secs(1).try_into().unwrap(),
1160 );
1161 assert_eq!(&bytes[..], QUERY);
1162 let mut req = &bytes[..];
1163 let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1164 check_ip(&ip, SRC_IP, DST_IP);
1165 let icmp = req
1166 .parse_with::<_, IcmpPacket<_, _, MulticastListenerQuery>>(IcmpParseArgs::new(
1167 SRC_IP, DST_IP,
1168 ))
1169 .unwrap();
1170 check_mld_v1(&icmp, MAX_RESP_CODE, HOST_GROUP_ADDRESS);
1171 }
1172
1173 #[test]
1174 fn test_mld_serialize_and_parse_report() {
1175 use crate::icmp::mld::MulticastListenerReport;
1176 use crate::testdata::mld_router_report::*;
1177 let bytes = serialize_to_bytes_with_builder::<_>(
1178 SRC_IP,
1179 DST_IP,
1180 MulticastListenerReport,
1181 MulticastAddr::new(HOST_GROUP_ADDRESS).unwrap(),
1182 (),
1183 );
1184 assert_eq!(&bytes[..], REPORT);
1185 let mut req = &bytes[..];
1186 let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1187 check_ip(&ip, SRC_IP, DST_IP);
1188 let icmp = req
1189 .parse_with::<_, IcmpPacket<_, _, MulticastListenerReport>>(IcmpParseArgs::new(
1190 SRC_IP, DST_IP,
1191 ))
1192 .unwrap();
1193 check_mld_v1(&icmp, 0, HOST_GROUP_ADDRESS);
1194 }
1195
1196 #[test]
1197 fn test_mld_serialize_and_parse_done() {
1198 use crate::icmp::mld::MulticastListenerDone;
1199 use crate::testdata::mld_router_done::*;
1200 let bytes = serialize_to_bytes_with_builder::<_>(
1201 SRC_IP,
1202 DST_IP,
1203 MulticastListenerDone,
1204 MulticastAddr::new(HOST_GROUP_ADDRESS).unwrap(),
1205 (),
1206 );
1207 assert_eq!(&bytes[..], DONE);
1208 let mut req = &bytes[..];
1209 let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1210 check_ip(&ip, SRC_IP, DST_IP);
1211 let icmp = req
1212 .parse_with::<_, IcmpPacket<_, _, MulticastListenerDone>>(IcmpParseArgs::new(
1213 SRC_IP, DST_IP,
1214 ))
1215 .unwrap();
1216 check_mld_v1(&icmp, 0, HOST_GROUP_ADDRESS);
1217 }
1218
1219 #[test]
1220 fn test_mld_serialize_and_parse_query_v2() {
1221 use crate::icmp::mld::{Mldv2QRV, Mldv2ResponseDelay, MulticastListenerQueryV2};
1222 use crate::testdata::mld_router_query::*;
1223 use core::time::Duration;
1224
1225 let builder = Mldv2QueryMessageBuilder::new(
1226 Mldv2ResponseDelay::lossy_try_from(Duration::from_millis(MAX_RESP_CODE.into()))
1227 .unwrap(),
1228 MulticastAddr::new(HOST_GROUP_ADDRESS),
1229 S_FLAG,
1230 Mldv2QRV::new(QRV),
1231 Mldv2QQIC::lossy_try_from(Duration::from_secs(QQIC.into())).unwrap(),
1232 SOURCES.into_iter(),
1233 );
1234
1235 let bytes =
1236 serialize_to_bytes_with_builder_v2(SRC_IP, DST_IP, MulticastListenerQueryV2, builder);
1237 assert_eq!(&bytes[..], QUERY_V2);
1238 let mut req = &bytes[..];
1239 let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1240 check_ip(&ip, SRC_IP, DST_IP);
1241 let icmp = req
1242 .parse_with::<_, IcmpPacket<_, _, MulticastListenerQueryV2>>(IcmpParseArgs::new(
1243 SRC_IP, DST_IP,
1244 ))
1245 .unwrap();
1246
1247 check_mld_query_v2(&icmp, MAX_RESP_CODE, HOST_GROUP_ADDRESS, SOURCES);
1248 }
1249
1250 #[test]
1251 fn test_mld_serialize_and_parse_report_v2() {
1252 use crate::icmp::mld::MulticastListenerReportV2;
1253 use crate::testdata::mld_router_report_v2::*;
1254
1255 let builder = Mldv2ReportMessageBuilder::new(
1256 RECORDS_HEADERS.into_iter().zip(RECORDS_SOURCES.into_iter()).map(|record| {
1257 let (record_header, record_sources) = record;
1258 let (record_type, multicast_addr) = record_header;
1259 (MulticastAddr::new(*multicast_addr).unwrap(), *record_type, record_sources.iter())
1260 }),
1261 );
1262
1263 let bytes =
1264 serialize_to_bytes_with_builder_v2(SRC_IP, DST_IP, MulticastListenerReportV2, builder);
1265 assert_eq!(&bytes[..], REPORT);
1266 let mut req = &bytes[..];
1267 let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1268 check_ip(&ip, SRC_IP, DST_IP);
1269 let icmp = req
1270 .parse_with::<_, IcmpPacket<_, _, MulticastListenerReportV2>>(IcmpParseArgs::new(
1271 SRC_IP, DST_IP,
1272 ))
1273 .unwrap();
1274
1275 check_mld_report_v2(&icmp, RECORDS_HEADERS, RECORDS_SOURCES);
1276 }
1277
1278 #[test]
1291 fn report_v2_split_many_sources() {
1292 use crate::testdata::mld_router_report_v2::*;
1293 use packet::PacketBuilder;
1294
1295 const ETH_MTU: usize = 1500;
1296 const MAX_SOURCES: usize = 89;
1297
1298 let ip_builder = Ipv6PacketBuilderWithHbhOptions::new(
1299 Ipv6PacketBuilder::new(SRC_IP, DST_IP, 1, Ipv6Proto::Icmpv6),
1300 &[HopByHopOption {
1301 action: ExtensionHeaderOptionAction::SkipAndContinue,
1302 mutable: false,
1303 data: HopByHopOptionData::RouterAlert { data: 0 },
1304 }],
1305 )
1306 .unwrap();
1307 let icmp_builder =
1308 IcmpPacketBuilder::new(SRC_IP, DST_IP, IcmpSenderZeroCode, MulticastListenerReportV2);
1309
1310 let avail_len = ETH_MTU
1311 - ip_builder.constraints().header_len()
1312 - icmp_builder.constraints().header_len();
1313
1314 let src_ip = |i: usize| Ipv6Addr::new([0x2000, 0, 0, 0, 0, 0, 0, i as u16]);
1315 let group_addr = MulticastAddr::new(RECORDS_HEADERS[0].1).unwrap();
1316 let reports = Mldv2ReportMessageBuilder::new(
1317 [(
1318 group_addr,
1319 Mldv2MulticastRecordType::ModeIsInclude,
1320 (0..MAX_SOURCES).into_iter().map(|i| src_ip(i)),
1321 )]
1322 .into_iter(),
1323 )
1324 .with_len_limits(avail_len)
1325 .unwrap();
1326
1327 let mut reports = reports.map(|builder| {
1328 builder
1329 .into_serializer()
1330 .encapsulate(icmp_builder.clone())
1331 .encapsulate(ip_builder.clone())
1332 .serialize_vec_outer()
1333 .unwrap_or_else(|(err, _)| panic!("{err:?}"))
1334 .unwrap_b()
1335 .into_inner()
1336 });
1337 let serialized = reports.next().unwrap();
1339 assert_eq!(serialized.len(), ETH_MTU);
1340 let mut buffer = &serialized[..];
1341 let ip = buffer.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1342 check_ip(&ip, SRC_IP, DST_IP);
1343 let icmp = buffer
1344 .parse_with::<_, IcmpPacket<_, _, MulticastListenerReportV2>>(IcmpParseArgs::new(
1345 SRC_IP, DST_IP,
1346 ))
1347 .unwrap();
1348
1349 let mut groups = icmp.body().iter_multicast_records();
1350 let group = groups.next().expect("has group");
1351 assert_eq!(group.header.multicast_address, group_addr.get());
1352 assert_eq!(usize::from(group.header.number_of_sources()), MAX_SOURCES);
1353 assert_eq!(group.sources().len(), MAX_SOURCES);
1354 for (i, addr) in group.sources().iter().enumerate() {
1355 assert_eq!(*addr, src_ip(i));
1356 }
1357 assert_eq!(groups.next().map(|r| r.header.multicast_address), None);
1358 assert_eq!(reports.next(), None);
1360
1361 let reports = Mldv2ReportMessageBuilder::new(
1362 [(
1363 group_addr,
1364 Mldv2MulticastRecordType::ModeIsInclude,
1365 core::iter::repeat(SRC_IP).take(MAX_SOURCES + 1),
1366 )]
1367 .into_iter(),
1368 )
1369 .with_len_limits(avail_len)
1370 .unwrap();
1371 assert_eq!(
1373 reports
1374 .map(|r| r.groups.map(|group| group.sources().count()).collect::<Vec<_>>())
1375 .collect::<Vec<_>>(),
1376 vec![vec![MAX_SOURCES], vec![1]]
1377 );
1378 }
1379
1380 #[test]
1390 fn report_v2_split_many_groups() {
1391 use crate::testdata::mld_router_report_v2::*;
1392 use packet::PacketBuilder;
1393
1394 const ETH_MTU: usize = 1500;
1395 const EXPECT_SERIALIZED: usize = 1496;
1396 const MAX_GROUPS: usize = 72;
1397
1398 let ip_builder = Ipv6PacketBuilderWithHbhOptions::new(
1399 Ipv6PacketBuilder::new(SRC_IP, DST_IP, 1, Ipv6Proto::Icmpv6),
1400 &[HopByHopOption {
1401 action: ExtensionHeaderOptionAction::SkipAndContinue,
1402 mutable: false,
1403 data: HopByHopOptionData::RouterAlert { data: 0 },
1404 }],
1405 )
1406 .unwrap();
1407 let icmp_builder =
1408 IcmpPacketBuilder::new(SRC_IP, DST_IP, IcmpSenderZeroCode, MulticastListenerReportV2);
1409
1410 let avail_len = ETH_MTU
1411 - ip_builder.constraints().header_len()
1412 - icmp_builder.constraints().header_len();
1413
1414 let group_ip = |i: usize| {
1415 MulticastAddr::new(Ipv6Addr::new([0xff02, 0, 0, 0, 0, 0, 0, i as u16])).unwrap()
1416 };
1417 let reports = Mldv2ReportMessageBuilder::new((0..MAX_GROUPS).into_iter().map(|i| {
1418 (group_ip(i), Mldv2MulticastRecordType::ModeIsExclude, core::iter::empty::<Ipv6Addr>())
1419 }))
1420 .with_len_limits(avail_len)
1421 .unwrap();
1422
1423 let mut reports = reports.map(|builder| {
1424 builder
1425 .into_serializer()
1426 .encapsulate(icmp_builder.clone())
1427 .encapsulate(ip_builder.clone())
1428 .serialize_vec_outer()
1429 .unwrap_or_else(|(err, _)| panic!("{err:?}"))
1430 .unwrap_b()
1431 .into_inner()
1432 });
1433 let serialized = reports.next().unwrap();
1435 assert_eq!(serialized.len(), EXPECT_SERIALIZED);
1436 let mut buffer = &serialized[..];
1437 let ip = buffer.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1438 check_ip(&ip, SRC_IP, DST_IP);
1439 let icmp = buffer
1440 .parse_with::<_, IcmpPacket<_, _, MulticastListenerReportV2>>(IcmpParseArgs::new(
1441 SRC_IP, DST_IP,
1442 ))
1443 .unwrap();
1444 assert_eq!(usize::from(icmp.body().header().num_mcast_addr_records()), MAX_GROUPS);
1445 for (i, group) in icmp.body().iter_multicast_records().enumerate() {
1446 assert_eq!(group.header.number_of_sources.get(), 0);
1447 assert_eq!(group.header.multicast_addr(), &group_ip(i).get());
1448 }
1449 assert_eq!(reports.next(), None);
1451
1452 let reports = Mldv2ReportMessageBuilder::new((0..MAX_GROUPS + 1).into_iter().map(|i| {
1453 (group_ip(i), Mldv2MulticastRecordType::ModeIsExclude, core::iter::empty::<Ipv6Addr>())
1454 }))
1455 .with_len_limits(avail_len)
1456 .unwrap();
1457 assert_eq!(reports.map(|r| r.groups.count()).collect::<Vec<_>>(), vec![MAX_GROUPS, 1]);
1459 }
1460
1461 #[test]
1462 fn test_mld_parse_and_serialize_response_delay_v2_linear() {
1463 for code in 0..(Mldv2ResponseDelay::SWITCHPOINT as u16) {
1465 let response_delay = Mldv2ResponseDelay::from_code(U16::from(code));
1466 let duration = Duration::from(response_delay);
1467 assert_eq!(duration.as_millis(), code.into());
1468
1469 let duration = Duration::from_millis(code.into());
1470 let response_delay_code: u16 =
1471 Mldv2ResponseDelay::lossy_try_from(duration).unwrap().as_code().into();
1472 assert_eq!(response_delay_code, code);
1473
1474 let duration = Duration::from_millis(code.into());
1475 let response_delay_code: u16 =
1476 Mldv2ResponseDelay::exact_try_from(duration).unwrap().as_code().into();
1477 assert_eq!(response_delay_code, code);
1478 }
1479 }
1480
1481 #[test_case(Mldv2ResponseDelay::SWITCHPOINT, 0x8000; "min exponential value")]
1482 #[test_case(32784, 0x8002; "exponental value 32784")]
1483 #[test_case(227744, 0xABCD; "exponental value 227744")]
1484 #[test_case(1821184, 0xDBCA; "exponental value 1821184")]
1485 #[test_case(8385536, 0xFFFD; "exponental value 8385536")]
1486 #[test_case(Mldv2ResponseDelay::MAX_VALUE, 0xFFFF; "max exponential value")]
1487 fn test_mld_parse_and_serialize_response_delay_v2_exponential_exact(
1488 duration_millis: u32,
1489 resp_code: u16,
1490 ) {
1491 let response_delay = Mldv2ResponseDelay::from_code(resp_code.into());
1492 let duration = Duration::from(response_delay);
1493 assert_eq!(duration.as_millis(), duration_millis.into());
1494
1495 let response_delay_code: u16 =
1496 Mldv2ResponseDelay::lossy_try_from(duration).unwrap().as_code().into();
1497 assert_eq!(response_delay_code, resp_code);
1498
1499 let response_delay_code: u16 =
1500 Mldv2ResponseDelay::exact_try_from(duration).unwrap().as_code().into();
1501 assert_eq!(response_delay_code, resp_code);
1502 }
1503
1504 #[test]
1505 fn test_mld_parse_and_serialize_response_delay_v2_errors() {
1506 let duration = Duration::from_millis((Mldv2ResponseDelay::MAX_VALUE + 1).into());
1507 assert_eq!(Mldv2ResponseDelay::lossy_try_from(duration), Err(OverflowError));
1508
1509 let duration = Duration::from_millis((Mldv2ResponseDelay::MAX_VALUE + 1).into());
1510 assert_eq!(
1511 Mldv2ResponseDelay::exact_try_from(duration),
1512 Err(ExactConversionError::Overflow)
1513 );
1514
1515 let duration = Duration::from_millis((Mldv2ResponseDelay::MAX_VALUE - 1).into());
1516 assert_eq!(
1517 Mldv2ResponseDelay::exact_try_from(duration),
1518 Err(ExactConversionError::NotExact)
1519 );
1520 }
1521
1522 #[test]
1523 fn test_mld_parse_and_serialize_response_qqic_v2_linear() {
1524 for code in 0..(Mldv2QQIC::SWITCHPOINT as u8) {
1526 let response_delay = Mldv2QQIC::from_code(code);
1527 let duration = Duration::from(response_delay);
1528 assert_eq!(duration.as_secs(), code.into());
1529
1530 let duration = Duration::from_secs(code.into());
1531 let response_delay_code: u8 =
1532 Mldv2QQIC::lossy_try_from(duration).unwrap().as_code().into();
1533 assert_eq!(response_delay_code, code);
1534
1535 let duration = Duration::from_secs(code.into());
1536 let response_delay_code: u8 =
1537 Mldv2QQIC::exact_try_from(duration).unwrap().as_code().into();
1538 assert_eq!(response_delay_code, code);
1539 }
1540 }
1541
1542 #[test_case(Mldv2QQIC::SWITCHPOINT, 0x80; "min exponential value")]
1543 #[test_case(144, 0x82; "exponental value 144")]
1544 #[test_case(928, 0xAD; "exponental value 928")]
1545 #[test_case(6656, 0xDA; "exponental value 6656")]
1546 #[test_case(29696, 0xFD; "exponental value 29696")]
1547 #[test_case(Mldv2QQIC::MAX_VALUE, 0xFF; "max exponential value")]
1548 fn test_mld_parse_and_serialize_response_qqic_v2_exponential_exact(
1549 duration_secs: u32,
1550 resp_code: u8,
1551 ) {
1552 let response_delay = Mldv2QQIC::from_code(resp_code.into());
1553 let duration = Duration::from(response_delay);
1554 assert_eq!(duration.as_secs(), duration_secs.into());
1555
1556 let response_delay_code: u8 = Mldv2QQIC::lossy_try_from(duration).unwrap().as_code().into();
1557 assert_eq!(response_delay_code, resp_code);
1558
1559 let response_delay_code: u8 = Mldv2QQIC::exact_try_from(duration).unwrap().as_code().into();
1560 assert_eq!(response_delay_code, resp_code);
1561 }
1562
1563 #[test]
1564 fn test_mld_parse_and_serialize_response_qqic_v2_errors() {
1565 let duration = Duration::from_secs((Mldv2QQIC::MAX_VALUE + 1).into());
1566 assert_eq!(Mldv2QQIC::lossy_try_from(duration), Err(OverflowError));
1567
1568 let duration = Duration::from_secs((Mldv2QQIC::MAX_VALUE + 1).into());
1569 assert_eq!(Mldv2QQIC::exact_try_from(duration), Err(ExactConversionError::Overflow));
1570
1571 let duration = Duration::from_secs((Mldv2QQIC::MAX_VALUE - 1).into());
1572 assert_eq!(Mldv2QQIC::exact_try_from(duration), Err(ExactConversionError::NotExact));
1573 }
1574}