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, IpAddress as _, Ipv6, Ipv6Addr};
16use net_types::{MulticastAddr, Witness as _};
17use packet::BufferView;
18use packet::records::{ParsedRecord, RecordParseResult, Records, RecordsImpl, RecordsImplLayout};
19use packet::serialize::InnerPacketBuilder;
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 num_sources_bytes =
821 usize::from(Ipv6Addr::BYTES) * usize::from(header.number_of_sources());
822 let (sources_bytes, _rest) =
828 bytes.split_at(num_sources_bytes).map_err(|_| ParseError::Format)?;
829 let sources =
830 Ref::<B, [Ipv6Addr]>::from_bytes(sources_bytes).map_err(|_| ParseError::Format)?;
831 Ok(Mldv2QueryBody { header, sources })
832 }
833
834 fn len(&self) -> usize {
835 let (inner_header, inner_body) = self.bytes();
836 inner_header.len() + inner_body.unwrap().len()
839 }
840
841 fn bytes(&self) -> (&[u8], Option<&[u8]>) {
842 (Ref::bytes(&self.header), Some(Ref::bytes(&self.sources)))
843 }
844}
845
846#[repr(C)]
848#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
849pub struct MulticastListenerQueryV2;
850
851impl_icmp_message!(
852 Ipv6,
853 MulticastListenerQueryV2,
854 MulticastListenerQuery,
855 IcmpSenderZeroCode,
856 Mldv2QueryBody<B>
857);
858
859#[cfg(test)]
860mod tests {
861 use packet::{NestableSerializer as _, NoOpSerializationContext, ParseBuffer, Serializer};
862 use test_case::test_case;
863
864 use super::*;
865 use crate::gmp::ExactConversionError;
866 use crate::icmp::{IcmpPacketBuilder, IcmpParseArgs};
867 use crate::ip::Ipv6Proto;
868 use crate::ipv6::ext_hdrs::{
869 ExtensionHeaderOptionAction, HopByHopOption, HopByHopOptionData, Ipv6ExtensionHeaderData,
870 };
871 use crate::ipv6::{Ipv6Header, Ipv6Packet, Ipv6PacketBuilder, Ipv6PacketBuilderWithHbhOptions};
872
873 fn serialize_to_bytes<B: SplitByteSlice + Debug, M: IcmpMessage<Ipv6> + Debug>(
874 src_ip: Ipv6Addr,
875 dst_ip: Ipv6Addr,
876 icmp: &IcmpPacket<Ipv6, B, M>,
877 ) -> Vec<u8> {
878 let ip = Ipv6PacketBuilder::new(src_ip, dst_ip, 1, Ipv6Proto::Icmpv6);
879 let with_options = Ipv6PacketBuilderWithHbhOptions::new(
880 ip,
881 &[HopByHopOption {
882 action: ExtensionHeaderOptionAction::SkipAndContinue,
883 mutable: false,
884 data: HopByHopOptionData::RouterAlert { data: 0 },
885 }],
886 )
887 .unwrap();
888 let (header, body) = icmp.message_body.bytes();
889 let body = if let Some(b) = body { b } else { &[] };
890 let complete_msg = &[header, body].concat();
891 complete_msg
892 .into_serializer()
893 .wrap_in(icmp.builder(src_ip, dst_ip))
894 .wrap_in(with_options)
895 .serialize_vec_outer(&mut NoOpSerializationContext)
896 .unwrap()
897 .as_ref()
898 .to_vec()
899 }
900
901 fn test_parse_and_serialize<
902 M: IcmpMessage<Ipv6> + Debug,
903 F: FnOnce(&Ipv6Packet<&[u8]>),
904 G: for<'a> FnOnce(&IcmpPacket<Ipv6, &'a [u8], M>),
905 >(
906 src_ip: Ipv6Addr,
907 dst_ip: Ipv6Addr,
908 mut req: &[u8],
909 check_ip: F,
910 check_icmp: G,
911 ) {
912 let orig_req = req;
913
914 let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
915 check_ip(&ip);
916 let icmp =
917 req.parse_with::<_, IcmpPacket<_, _, M>>(IcmpParseArgs::new(src_ip, dst_ip)).unwrap();
918 check_icmp(&icmp);
919
920 let data = serialize_to_bytes(src_ip, dst_ip, &icmp);
921 assert_eq!(&data[..], orig_req);
922 }
923
924 fn serialize_to_bytes_with_builder<M: IcmpMldv1MessageType + Debug>(
925 src_ip: Ipv6Addr,
926 dst_ip: Ipv6Addr,
927 msg: M,
928 group_addr: M::GroupAddr,
929 max_resp_delay: M::MaxRespDelay,
930 ) -> Vec<u8> {
931 let ip = Ipv6PacketBuilder::new(src_ip, dst_ip, 1, Ipv6Proto::Icmpv6);
932 let with_options = Ipv6PacketBuilderWithHbhOptions::new(
933 ip,
934 &[HopByHopOption {
935 action: ExtensionHeaderOptionAction::SkipAndContinue,
936 mutable: false,
937 data: HopByHopOptionData::RouterAlert { data: 0 },
938 }],
939 )
940 .unwrap();
941 Mldv1MessageBuilder::<M>::new_with_max_resp_delay(group_addr, max_resp_delay)
943 .into_serializer()
944 .wrap_in(IcmpPacketBuilder::new(src_ip, dst_ip, IcmpSenderZeroCode, msg))
945 .wrap_in(with_options)
946 .serialize_vec_outer(&mut NoOpSerializationContext)
947 .unwrap()
948 .as_ref()
949 .to_vec()
950 }
951
952 fn serialize_to_bytes_with_builder_v2<
953 M: IcmpMessage<Ipv6, Code = IcmpSenderZeroCode> + Debug,
954 B: InnerPacketBuilder + Debug,
955 >(
956 src_ip: Ipv6Addr,
957 dst_ip: Ipv6Addr,
958 msg: M,
959 builder: B,
960 ) -> Vec<u8> {
961 let ip = Ipv6PacketBuilder::new(src_ip, dst_ip, 1, Ipv6Proto::Icmpv6);
962 let with_options = Ipv6PacketBuilderWithHbhOptions::new(
963 ip,
964 &[HopByHopOption {
965 action: ExtensionHeaderOptionAction::SkipAndContinue,
966 mutable: false,
967 data: HopByHopOptionData::RouterAlert { data: 0 },
968 }],
969 )
970 .unwrap();
971 builder
974 .into_serializer()
975 .wrap_in(IcmpPacketBuilder::new(src_ip, dst_ip, IcmpSenderZeroCode, msg))
976 .wrap_in(with_options)
977 .serialize_vec_outer(&mut NoOpSerializationContext)
978 .unwrap()
979 .as_ref()
980 .to_vec()
981 }
982
983 fn check_ip<B: SplitByteSlice>(ip: &Ipv6Packet<B>, src_ip: Ipv6Addr, dst_ip: Ipv6Addr) {
984 assert_eq!(ip.src_ip(), src_ip);
985 assert_eq!(ip.dst_ip(), dst_ip);
986 assert_eq!(ip.iter_extension_hdrs().count(), 1);
987 let hbh = ip.iter_extension_hdrs().next().unwrap();
988 match hbh.data() {
989 Ipv6ExtensionHeaderData::HopByHopOptions { options } => {
990 assert_eq!(options.iter().count(), 1);
991 assert_eq!(
992 options.iter().next().unwrap(),
993 HopByHopOption {
994 action: ExtensionHeaderOptionAction::SkipAndContinue,
995 mutable: false,
996 data: HopByHopOptionData::RouterAlert { data: 0 },
997 }
998 );
999 }
1000 _ => panic!("Wrong extension header"),
1001 }
1002 }
1003
1004 fn check_mld_v1<
1005 B: SplitByteSlice,
1006 M: IcmpMessage<Ipv6, Body<B> = Mldv1Body<B>> + Mldv1MessageType + Debug,
1007 >(
1008 icmp: &IcmpPacket<Ipv6, B, M>,
1009 max_resp_code: u16,
1010 group_addr: Ipv6Addr,
1011 ) {
1012 assert_eq!(icmp.message_body._reserved.get(), 0);
1013 assert_eq!(icmp.message_body.max_response_delay.get(), max_resp_code);
1014 assert_eq!(icmp.message_body.group_addr, group_addr);
1015 }
1016
1017 fn check_mld_query_v2<
1018 'a,
1019 B: SplitByteSlice,
1020 M: IcmpMessage<Ipv6, Body<B> = Mldv2QueryBody<B>> + Debug,
1021 >(
1022 icmp: &IcmpPacket<Ipv6, B, M>,
1023 max_resp_code: u16,
1024 group_addr: Ipv6Addr,
1025 sources: &[Ipv6Addr],
1026 ) {
1027 assert_eq!(icmp.message_body.header._reserved.get(), 0);
1028 assert_eq!(icmp.message_body.header.max_response_code.get(), max_resp_code);
1029 assert_eq!(icmp.message_body.header.group_addr, group_addr);
1030 assert_eq!(icmp.message_body.sources.len(), sources.len());
1031 for (expected, actual) in sources.iter().zip(icmp.message_body.sources.iter()) {
1032 assert_eq!(actual, expected);
1033 }
1034
1035 let Mldv1Body(v1) = icmp.message_body.as_v1_query();
1037 assert_eq!(v1.max_response_delay.get(), max_resp_code);
1038 assert_eq!(v1.group_addr, group_addr);
1039 }
1040
1041 fn check_mld_report_v2<
1042 'a,
1043 B: SplitByteSlice,
1044 M: IcmpMessage<Ipv6, Body<B> = Mldv2ReportBody<B>> + Debug,
1045 >(
1046 icmp: &IcmpPacket<Ipv6, B, M>,
1047 expected_records_header: &[(Mldv2MulticastRecordType, Ipv6Addr)],
1048 expected_records_sources: &[&[Ipv6Addr]],
1049 ) {
1050 assert_eq!(
1051 icmp.message_body.header.num_mcast_addr_records.get(),
1052 u16::try_from(expected_records_header.len()).unwrap()
1053 );
1054 let expected_records = expected_records_header.iter().zip(expected_records_sources.iter());
1055 for (expected_record, actual_record) in
1056 expected_records.zip(icmp.message_body.iter_multicast_records())
1057 {
1058 let (expected_header, expected_sources) = expected_record;
1059 let (expected_record_type, expected_multicast_addr) = expected_header;
1060 assert_eq!(
1061 expected_record_type,
1062 &actual_record.header.record_type().expect("valid record type")
1063 );
1064
1065 assert_eq!(
1066 u16::try_from(expected_sources.len()).unwrap(),
1067 actual_record.header.number_of_sources()
1068 );
1069 assert_eq!(expected_multicast_addr, actual_record.header.multicast_addr());
1070 assert_eq!(*expected_sources, actual_record.sources());
1071 }
1072 }
1073
1074 #[test]
1075 fn test_mld_parse_and_serialize_query() {
1076 use crate::icmp::mld::MulticastListenerQuery;
1077 use crate::testdata::mld_router_query::*;
1078 test_parse_and_serialize::<MulticastListenerQuery, _, _>(
1079 SRC_IP,
1080 DST_IP,
1081 QUERY,
1082 |ip| {
1083 check_ip(ip, SRC_IP, DST_IP);
1084 },
1085 |icmp| {
1086 check_mld_v1(icmp, MAX_RESP_CODE, HOST_GROUP_ADDRESS);
1087 },
1088 );
1089 }
1090
1091 #[test]
1092 fn test_mld_parse_and_serialize_report() {
1093 use crate::icmp::mld::MulticastListenerReport;
1094 use crate::testdata::mld_router_report::*;
1095 test_parse_and_serialize::<MulticastListenerReport, _, _>(
1096 SRC_IP,
1097 DST_IP,
1098 REPORT,
1099 |ip| {
1100 check_ip(ip, SRC_IP, DST_IP);
1101 },
1102 |icmp| {
1103 check_mld_v1(icmp, 0, HOST_GROUP_ADDRESS);
1104 },
1105 );
1106 }
1107
1108 #[test]
1109 fn test_mld_parse_and_serialize_done() {
1110 use crate::icmp::mld::MulticastListenerDone;
1111 use crate::testdata::mld_router_done::*;
1112 test_parse_and_serialize::<MulticastListenerDone, _, _>(
1113 SRC_IP,
1114 DST_IP,
1115 DONE,
1116 |ip| {
1117 check_ip(ip, SRC_IP, DST_IP);
1118 },
1119 |icmp| {
1120 check_mld_v1(icmp, 0, HOST_GROUP_ADDRESS);
1121 },
1122 );
1123 }
1124
1125 #[test]
1126 fn test_mld_parse_and_serialize_query_v2() {
1127 use crate::icmp::mld::MulticastListenerQueryV2;
1128 use crate::testdata::mld_router_query::*;
1129 test_parse_and_serialize::<MulticastListenerQueryV2, _, _>(
1130 SRC_IP,
1131 DST_IP,
1132 QUERY_V2,
1133 |ip| {
1134 check_ip(ip, SRC_IP, DST_IP);
1135 },
1136 |icmp| {
1137 check_mld_query_v2(icmp, MAX_RESP_CODE, HOST_GROUP_ADDRESS, SOURCES);
1138 },
1139 );
1140 }
1141
1142 #[test]
1143 fn test_mld_parse_and_serialize_report_v2() {
1144 use crate::icmp::mld::MulticastListenerReportV2;
1145 use crate::testdata::mld_router_report_v2::*;
1146 test_parse_and_serialize::<MulticastListenerReportV2, _, _>(
1147 SRC_IP,
1148 DST_IP,
1149 REPORT,
1150 |ip| {
1151 check_ip(ip, SRC_IP, DST_IP);
1152 },
1153 |icmp| {
1154 check_mld_report_v2(icmp, RECORDS_HEADERS, RECORDS_SOURCES);
1155 },
1156 );
1157 }
1158
1159 #[test]
1160 fn test_mld_serialize_and_parse_query() {
1161 use crate::icmp::mld::MulticastListenerQuery;
1162 use crate::testdata::mld_router_query::*;
1163 let bytes = serialize_to_bytes_with_builder::<_>(
1164 SRC_IP,
1165 DST_IP,
1166 MulticastListenerQuery,
1167 HOST_GROUP_ADDRESS,
1168 Duration::from_secs(1).try_into().unwrap(),
1169 );
1170 assert_eq!(&bytes[..], QUERY);
1171 let mut req = &bytes[..];
1172 let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1173 check_ip(&ip, SRC_IP, DST_IP);
1174 let icmp = req
1175 .parse_with::<_, IcmpPacket<_, _, MulticastListenerQuery>>(IcmpParseArgs::new(
1176 SRC_IP, DST_IP,
1177 ))
1178 .unwrap();
1179 check_mld_v1(&icmp, MAX_RESP_CODE, HOST_GROUP_ADDRESS);
1180 }
1181
1182 #[test]
1183 fn test_mld_serialize_and_parse_report() {
1184 use crate::icmp::mld::MulticastListenerReport;
1185 use crate::testdata::mld_router_report::*;
1186 let bytes = serialize_to_bytes_with_builder::<_>(
1187 SRC_IP,
1188 DST_IP,
1189 MulticastListenerReport,
1190 MulticastAddr::new(HOST_GROUP_ADDRESS).unwrap(),
1191 (),
1192 );
1193 assert_eq!(&bytes[..], REPORT);
1194 let mut req = &bytes[..];
1195 let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1196 check_ip(&ip, SRC_IP, DST_IP);
1197 let icmp = req
1198 .parse_with::<_, IcmpPacket<_, _, MulticastListenerReport>>(IcmpParseArgs::new(
1199 SRC_IP, DST_IP,
1200 ))
1201 .unwrap();
1202 check_mld_v1(&icmp, 0, HOST_GROUP_ADDRESS);
1203 }
1204
1205 #[test]
1206 fn test_mld_serialize_and_parse_done() {
1207 use crate::icmp::mld::MulticastListenerDone;
1208 use crate::testdata::mld_router_done::*;
1209 let bytes = serialize_to_bytes_with_builder::<_>(
1210 SRC_IP,
1211 DST_IP,
1212 MulticastListenerDone,
1213 MulticastAddr::new(HOST_GROUP_ADDRESS).unwrap(),
1214 (),
1215 );
1216 assert_eq!(&bytes[..], DONE);
1217 let mut req = &bytes[..];
1218 let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1219 check_ip(&ip, SRC_IP, DST_IP);
1220 let icmp = req
1221 .parse_with::<_, IcmpPacket<_, _, MulticastListenerDone>>(IcmpParseArgs::new(
1222 SRC_IP, DST_IP,
1223 ))
1224 .unwrap();
1225 check_mld_v1(&icmp, 0, HOST_GROUP_ADDRESS);
1226 }
1227
1228 #[test]
1229 fn test_mld_serialize_and_parse_query_v2() {
1230 use crate::icmp::mld::{Mldv2QRV, Mldv2ResponseDelay, MulticastListenerQueryV2};
1231 use crate::testdata::mld_router_query::*;
1232 use core::time::Duration;
1233
1234 let builder = Mldv2QueryMessageBuilder::new(
1235 Mldv2ResponseDelay::lossy_try_from(Duration::from_millis(MAX_RESP_CODE.into()))
1236 .unwrap(),
1237 MulticastAddr::new(HOST_GROUP_ADDRESS),
1238 S_FLAG,
1239 Mldv2QRV::new(QRV),
1240 Mldv2QQIC::lossy_try_from(Duration::from_secs(QQIC.into())).unwrap(),
1241 SOURCES.iter(),
1242 );
1243
1244 let bytes =
1245 serialize_to_bytes_with_builder_v2(SRC_IP, DST_IP, MulticastListenerQueryV2, builder);
1246 assert_eq!(&bytes[..], QUERY_V2);
1247 let mut req = &bytes[..];
1248 let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1249 check_ip(&ip, SRC_IP, DST_IP);
1250 let icmp = req
1251 .parse_with::<_, IcmpPacket<_, _, MulticastListenerQueryV2>>(IcmpParseArgs::new(
1252 SRC_IP, DST_IP,
1253 ))
1254 .unwrap();
1255
1256 check_mld_query_v2(&icmp, MAX_RESP_CODE, HOST_GROUP_ADDRESS, SOURCES);
1257 }
1258
1259 #[test]
1260 fn test_mld_serialize_and_parse_report_v2() {
1261 use crate::icmp::mld::MulticastListenerReportV2;
1262 use crate::testdata::mld_router_report_v2::*;
1263
1264 let builder = Mldv2ReportMessageBuilder::new(
1265 RECORDS_HEADERS.iter().zip(RECORDS_SOURCES.iter()).map(|record| {
1266 let (record_header, record_sources) = record;
1267 let (record_type, multicast_addr) = record_header;
1268 (MulticastAddr::new(*multicast_addr).unwrap(), *record_type, record_sources.iter())
1269 }),
1270 );
1271
1272 let bytes =
1273 serialize_to_bytes_with_builder_v2(SRC_IP, DST_IP, MulticastListenerReportV2, builder);
1274 assert_eq!(&bytes[..], REPORT);
1275 let mut req = &bytes[..];
1276 let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1277 check_ip(&ip, SRC_IP, DST_IP);
1278 let icmp = req
1279 .parse_with::<_, IcmpPacket<_, _, MulticastListenerReportV2>>(IcmpParseArgs::new(
1280 SRC_IP, DST_IP,
1281 ))
1282 .unwrap();
1283
1284 check_mld_report_v2(&icmp, RECORDS_HEADERS, RECORDS_SOURCES);
1285 }
1286
1287 #[test_case(&[0x11, 0x22, 0x33, 0x44]; "extra bytes")]
1288 #[test_case(Ipv6Addr::new([1, 2, 3, 4, 5, 6, 7, 8]).bytes(); "extra addr")]
1289 fn test_mld_query_v2_trailing_bytes_ignored(extend_bytes: &[u8]) {
1290 use crate::testdata::mld_router_query::*;
1291
1292 let mut body_with_trailing = QUERY_V2.to_vec();
1293 body_with_trailing.extend_from_slice(extend_bytes);
1294
1295 let mut req = &body_with_trailing[..];
1296 let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1297 check_ip(&ip, SRC_IP, DST_IP);
1298 let icmp = req
1299 .parse_with::<_, IcmpPacket<_, _, MulticastListenerQueryV2>>(IcmpParseArgs::new(
1300 SRC_IP, DST_IP,
1301 ))
1302 .unwrap();
1303
1304 check_mld_query_v2(&icmp, MAX_RESP_CODE, HOST_GROUP_ADDRESS, SOURCES);
1305 }
1306
1307 #[test]
1320 fn report_v2_split_many_sources() {
1321 use crate::testdata::mld_router_report_v2::*;
1322 use packet::{NestablePacketBuilder as _, NestableSerializer as _};
1323
1324 const ETH_MTU: usize = 1500;
1325 const MAX_SOURCES: usize = 89;
1326
1327 let ip_builder = Ipv6PacketBuilderWithHbhOptions::new(
1328 Ipv6PacketBuilder::new(SRC_IP, DST_IP, 1, Ipv6Proto::Icmpv6),
1329 &[HopByHopOption {
1330 action: ExtensionHeaderOptionAction::SkipAndContinue,
1331 mutable: false,
1332 data: HopByHopOptionData::RouterAlert { data: 0 },
1333 }],
1334 )
1335 .unwrap();
1336 let icmp_builder =
1337 IcmpPacketBuilder::new(SRC_IP, DST_IP, IcmpSenderZeroCode, MulticastListenerReportV2);
1338
1339 let avail_len = ETH_MTU
1340 - ip_builder.constraints().header_len()
1341 - icmp_builder.constraints().header_len();
1342
1343 let src_ip = |i: usize| Ipv6Addr::new([0x2000, 0, 0, 0, 0, 0, 0, i as u16]);
1344 let group_addr = MulticastAddr::new(RECORDS_HEADERS[0].1).unwrap();
1345 let reports = Mldv2ReportMessageBuilder::new(
1346 [(
1347 group_addr,
1348 Mldv2MulticastRecordType::ModeIsInclude,
1349 (0..MAX_SOURCES).into_iter().map(|i| src_ip(i)),
1350 )]
1351 .into_iter(),
1352 )
1353 .with_len_limits(avail_len)
1354 .unwrap();
1355
1356 let mut reports = reports.map(|builder| {
1357 builder
1358 .into_serializer()
1359 .wrap_in(icmp_builder.clone())
1360 .wrap_in(ip_builder.clone())
1361 .serialize_vec_outer(&mut NoOpSerializationContext)
1362 .unwrap_or_else(|(err, _)| panic!("{err:?}"))
1363 .unwrap_b()
1364 .into_inner()
1365 });
1366 let serialized = reports.next().unwrap();
1368 assert_eq!(serialized.len(), ETH_MTU);
1369 let mut buffer = &serialized[..];
1370 let ip = buffer.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1371 check_ip(&ip, SRC_IP, DST_IP);
1372 let icmp = buffer
1373 .parse_with::<_, IcmpPacket<_, _, MulticastListenerReportV2>>(IcmpParseArgs::new(
1374 SRC_IP, DST_IP,
1375 ))
1376 .unwrap();
1377
1378 let mut groups = icmp.body().iter_multicast_records();
1379 let group = groups.next().expect("has group");
1380 assert_eq!(group.header.multicast_address, group_addr.get());
1381 assert_eq!(usize::from(group.header.number_of_sources()), MAX_SOURCES);
1382 assert_eq!(group.sources().len(), MAX_SOURCES);
1383 for (i, addr) in group.sources().iter().enumerate() {
1384 assert_eq!(*addr, src_ip(i));
1385 }
1386 assert_eq!(groups.next().map(|r| r.header.multicast_address), None);
1387 assert_eq!(reports.next(), None);
1389
1390 let reports = Mldv2ReportMessageBuilder::new(
1391 [(
1392 group_addr,
1393 Mldv2MulticastRecordType::ModeIsInclude,
1394 core::iter::repeat(SRC_IP).take(MAX_SOURCES + 1),
1395 )]
1396 .into_iter(),
1397 )
1398 .with_len_limits(avail_len)
1399 .unwrap();
1400 assert_eq!(
1402 reports
1403 .map(|r| r.groups.map(|group| group.sources().count()).collect::<Vec<_>>())
1404 .collect::<Vec<_>>(),
1405 vec![vec![MAX_SOURCES], vec![1]]
1406 );
1407 }
1408
1409 #[test]
1419 fn report_v2_split_many_groups() {
1420 use crate::testdata::mld_router_report_v2::*;
1421 use packet::{NestablePacketBuilder as _, NestableSerializer as _};
1422
1423 const ETH_MTU: usize = 1500;
1424 const EXPECT_SERIALIZED: usize = 1496;
1425 const MAX_GROUPS: usize = 72;
1426
1427 let ip_builder = Ipv6PacketBuilderWithHbhOptions::new(
1428 Ipv6PacketBuilder::new(SRC_IP, DST_IP, 1, Ipv6Proto::Icmpv6),
1429 &[HopByHopOption {
1430 action: ExtensionHeaderOptionAction::SkipAndContinue,
1431 mutable: false,
1432 data: HopByHopOptionData::RouterAlert { data: 0 },
1433 }],
1434 )
1435 .unwrap();
1436 let icmp_builder =
1437 IcmpPacketBuilder::new(SRC_IP, DST_IP, IcmpSenderZeroCode, MulticastListenerReportV2);
1438
1439 let avail_len = ETH_MTU
1440 - ip_builder.constraints().header_len()
1441 - icmp_builder.constraints().header_len();
1442
1443 let group_ip = |i: usize| {
1444 MulticastAddr::new(Ipv6Addr::new([0xff02, 0, 0, 0, 0, 0, 0, i as u16])).unwrap()
1445 };
1446 let reports = Mldv2ReportMessageBuilder::new((0..MAX_GROUPS).into_iter().map(|i| {
1447 (group_ip(i), Mldv2MulticastRecordType::ModeIsExclude, core::iter::empty::<Ipv6Addr>())
1448 }))
1449 .with_len_limits(avail_len)
1450 .unwrap();
1451
1452 let mut reports = reports.map(|builder| {
1453 builder
1454 .into_serializer()
1455 .wrap_in(icmp_builder.clone())
1456 .wrap_in(ip_builder.clone())
1457 .serialize_vec_outer(&mut NoOpSerializationContext)
1458 .unwrap_or_else(|(err, _)| panic!("{err:?}"))
1459 .unwrap_b()
1460 .into_inner()
1461 });
1462 let serialized = reports.next().unwrap();
1464 assert_eq!(serialized.len(), EXPECT_SERIALIZED);
1465 let mut buffer = &serialized[..];
1466 let ip = buffer.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1467 check_ip(&ip, SRC_IP, DST_IP);
1468 let icmp = buffer
1469 .parse_with::<_, IcmpPacket<_, _, MulticastListenerReportV2>>(IcmpParseArgs::new(
1470 SRC_IP, DST_IP,
1471 ))
1472 .unwrap();
1473 assert_eq!(usize::from(icmp.body().header().num_mcast_addr_records()), MAX_GROUPS);
1474 for (i, group) in icmp.body().iter_multicast_records().enumerate() {
1475 assert_eq!(group.header.number_of_sources.get(), 0);
1476 assert_eq!(group.header.multicast_addr(), &group_ip(i).get());
1477 }
1478 assert_eq!(reports.next(), None);
1480
1481 let reports = Mldv2ReportMessageBuilder::new((0..MAX_GROUPS + 1).into_iter().map(|i| {
1482 (group_ip(i), Mldv2MulticastRecordType::ModeIsExclude, core::iter::empty::<Ipv6Addr>())
1483 }))
1484 .with_len_limits(avail_len)
1485 .unwrap();
1486 assert_eq!(reports.map(|r| r.groups.count()).collect::<Vec<_>>(), vec![MAX_GROUPS, 1]);
1488 }
1489
1490 #[test]
1491 fn test_mld_parse_and_serialize_response_delay_v2_linear() {
1492 for code in 0..(Mldv2ResponseDelay::SWITCHPOINT as u16) {
1494 let response_delay = Mldv2ResponseDelay::from_code(U16::from(code));
1495 let duration = Duration::from(response_delay);
1496 assert_eq!(duration.as_millis(), code.into());
1497
1498 let duration = Duration::from_millis(code.into());
1499 let response_delay_code: u16 =
1500 Mldv2ResponseDelay::lossy_try_from(duration).unwrap().as_code().into();
1501 assert_eq!(response_delay_code, code);
1502
1503 let duration = Duration::from_millis(code.into());
1504 let response_delay_code: u16 =
1505 Mldv2ResponseDelay::exact_try_from(duration).unwrap().as_code().into();
1506 assert_eq!(response_delay_code, code);
1507 }
1508 }
1509
1510 #[test_case(Mldv2ResponseDelay::SWITCHPOINT, 0x8000; "min exponential value")]
1511 #[test_case(32784, 0x8002; "exponental value 32784")]
1512 #[test_case(227744, 0xABCD; "exponental value 227744")]
1513 #[test_case(1821184, 0xDBCA; "exponental value 1821184")]
1514 #[test_case(8385536, 0xFFFD; "exponental value 8385536")]
1515 #[test_case(Mldv2ResponseDelay::MAX_VALUE, 0xFFFF; "max exponential value")]
1516 fn test_mld_parse_and_serialize_response_delay_v2_exponential_exact(
1517 duration_millis: u32,
1518 resp_code: u16,
1519 ) {
1520 let response_delay = Mldv2ResponseDelay::from_code(resp_code.into());
1521 let duration = Duration::from(response_delay);
1522 assert_eq!(duration.as_millis(), duration_millis.into());
1523
1524 let response_delay_code: u16 =
1525 Mldv2ResponseDelay::lossy_try_from(duration).unwrap().as_code().into();
1526 assert_eq!(response_delay_code, resp_code);
1527
1528 let response_delay_code: u16 =
1529 Mldv2ResponseDelay::exact_try_from(duration).unwrap().as_code().into();
1530 assert_eq!(response_delay_code, resp_code);
1531 }
1532
1533 #[test]
1534 fn test_mld_parse_and_serialize_response_delay_v2_errors() {
1535 let duration = Duration::from_millis((Mldv2ResponseDelay::MAX_VALUE + 1).into());
1536 assert_eq!(Mldv2ResponseDelay::lossy_try_from(duration), Err(OverflowError));
1537
1538 let duration = Duration::from_millis((Mldv2ResponseDelay::MAX_VALUE + 1).into());
1539 assert_eq!(
1540 Mldv2ResponseDelay::exact_try_from(duration),
1541 Err(ExactConversionError::Overflow)
1542 );
1543
1544 let duration = Duration::from_millis((Mldv2ResponseDelay::MAX_VALUE - 1).into());
1545 assert_eq!(
1546 Mldv2ResponseDelay::exact_try_from(duration),
1547 Err(ExactConversionError::NotExact)
1548 );
1549 }
1550
1551 #[test]
1552 fn test_mld_parse_and_serialize_response_qqic_v2_linear() {
1553 for code in 0..(Mldv2QQIC::SWITCHPOINT as u8) {
1555 let response_delay = Mldv2QQIC::from_code(code);
1556 let duration = Duration::from(response_delay);
1557 assert_eq!(duration.as_secs(), code.into());
1558
1559 let duration = Duration::from_secs(code.into());
1560 let response_delay_code: u8 =
1561 Mldv2QQIC::lossy_try_from(duration).unwrap().as_code().into();
1562 assert_eq!(response_delay_code, code);
1563
1564 let duration = Duration::from_secs(code.into());
1565 let response_delay_code: u8 =
1566 Mldv2QQIC::exact_try_from(duration).unwrap().as_code().into();
1567 assert_eq!(response_delay_code, code);
1568 }
1569 }
1570
1571 #[test_case(Mldv2QQIC::SWITCHPOINT, 0x80; "min exponential value")]
1572 #[test_case(144, 0x82; "exponental value 144")]
1573 #[test_case(928, 0xAD; "exponental value 928")]
1574 #[test_case(6656, 0xDA; "exponental value 6656")]
1575 #[test_case(29696, 0xFD; "exponental value 29696")]
1576 #[test_case(Mldv2QQIC::MAX_VALUE, 0xFF; "max exponential value")]
1577 fn test_mld_parse_and_serialize_response_qqic_v2_exponential_exact(
1578 duration_secs: u32,
1579 resp_code: u8,
1580 ) {
1581 let response_delay = Mldv2QQIC::from_code(resp_code.into());
1582 let duration = Duration::from(response_delay);
1583 assert_eq!(duration.as_secs(), duration_secs.into());
1584
1585 let response_delay_code: u8 = Mldv2QQIC::lossy_try_from(duration).unwrap().as_code().into();
1586 assert_eq!(response_delay_code, resp_code);
1587
1588 let response_delay_code: u8 = Mldv2QQIC::exact_try_from(duration).unwrap().as_code().into();
1589 assert_eq!(response_delay_code, resp_code);
1590 }
1591
1592 #[test]
1593 fn test_mld_parse_and_serialize_response_qqic_v2_errors() {
1594 let duration = Duration::from_secs((Mldv2QQIC::MAX_VALUE + 1).into());
1595 assert_eq!(Mldv2QQIC::lossy_try_from(duration), Err(OverflowError));
1596
1597 let duration = Duration::from_secs((Mldv2QQIC::MAX_VALUE + 1).into());
1598 assert_eq!(Mldv2QQIC::exact_try_from(duration), Err(ExactConversionError::Overflow));
1599
1600 let duration = Duration::from_secs((Mldv2QQIC::MAX_VALUE - 1).into());
1601 assert_eq!(Mldv2QQIC::exact_try_from(duration), Err(ExactConversionError::NotExact));
1602 }
1603}