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 if *ctx == 0 {
160 return Ok(ParsedRecord::Done);
161 }
162 let header = data
163 .take_obj_front::<Mldv2ReportRecordHeader>()
164 .ok_or_else(debug_err_fn!(ParseError::Format, "Can't take multicast record header"))?;
165 let sources = data
166 .take_slice_front::<Ipv6Addr>(header.number_of_sources().into())
167 .ok_or_else(debug_err_fn!(ParseError::Format, "Can't take multicast record sources"))?;
168 let _ = data
171 .take_front(usize::from(header.aux_data_len) * 4)
172 .ok_or_else(debug_err_fn!(ParseError::Format, "Can't skip auxiliary data"))?;
173
174 *ctx -= 1;
175 Ok(ParsedRecord::Parsed(Self::Record { header, sources }))
176 }
177}
178
179#[repr(C)]
183#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
184pub struct Mldv2ReportHeader {
185 _reserved: [u8; 2],
187 num_mcast_addr_records: U16,
189}
190
191impl Mldv2ReportHeader {
192 pub fn new(num_mcast_addr_records: u16) -> Self {
194 Mldv2ReportHeader {
195 _reserved: [0, 0],
196 num_mcast_addr_records: U16::from(num_mcast_addr_records),
197 }
198 }
199 pub fn num_mcast_addr_records(&self) -> u16 {
201 self.num_mcast_addr_records.get()
202 }
203}
204
205#[derive(Debug)]
210pub struct Mldv2ReportBody<B: SplitByteSlice> {
211 header: Ref<B, Mldv2ReportHeader>,
212 records: Records<B, Mldv2ReportRecords>,
213}
214
215impl<B: SplitByteSlice> Mldv2ReportBody<B> {
216 pub fn header(&self) -> &Mldv2ReportHeader {
218 self.header.deref()
219 }
220
221 pub fn iter_multicast_records(&self) -> impl Iterator<Item = MulticastRecord<&'_ [u8]>> {
223 self.records.iter()
224 }
225}
226
227impl<B: SplitByteSlice> MessageBody for Mldv2ReportBody<B> {
228 type B = B;
229 fn parse(bytes: B) -> ParseResult<Self> {
230 let (header, bytes) =
231 Ref::<_, Mldv2ReportHeader>::from_prefix(bytes).map_err(|_| ParseError::Format)?;
232 let records = Records::parse_with_context(bytes, header.num_mcast_addr_records().into())?;
233 Ok(Mldv2ReportBody { header, records })
234 }
235
236 fn len(&self) -> usize {
237 let (inner_header, inner_body) = self.bytes();
238 inner_header.len() + inner_body.unwrap().len()
241 }
242
243 fn bytes(&self) -> (&[u8], Option<&[u8]>) {
244 (Ref::bytes(&self.header), Some(self.records.bytes()))
245 }
246}
247
248#[repr(C)]
250#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
251pub struct MulticastListenerReportV2;
252
253impl_icmp_message!(
254 Ipv6,
255 MulticastListenerReportV2,
256 MulticastListenerReportV2,
257 IcmpSenderZeroCode,
258 Mldv2ReportBody<B>
259);
260
261#[repr(C)]
263#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
264pub struct MulticastListenerQuery;
265
266#[repr(C)]
268#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
269pub struct MulticastListenerReport;
270
271#[repr(C)]
273#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
274pub struct MulticastListenerDone;
275
276pub trait Mldv1MessageType {
278 type MaxRespDelay: MaxCode<U16> + Debug + Copy;
283 type GroupAddr: Into<Ipv6Addr> + Debug + Copy;
290}
291
292pub trait IcmpMldv1MessageType:
294 Mldv1MessageType + IcmpMessage<Ipv6, Code = IcmpSenderZeroCode>
295{
296}
297
298pub trait MaxCode<T: Default + Debug + FromBytes + IntoBytes> {
303 #[allow(clippy::wrong_self_convention)]
305 fn as_code(self) -> T;
306
307 fn from_code(code: T) -> Self;
309}
310
311impl<T: Default + Debug + FromBytes + IntoBytes> MaxCode<T> for () {
312 fn as_code(self) -> T {
313 T::default()
314 }
315
316 fn from_code(_: T) -> Self {}
317}
318
319#[derive(PartialEq, Eq, Debug, Clone, Copy)]
321pub struct Mldv1ResponseDelay(u16);
322
323impl MaxCode<U16> for Mldv1ResponseDelay {
324 fn as_code(self) -> U16 {
325 U16::new(self.0)
326 }
327
328 fn from_code(code: U16) -> Self {
329 Mldv1ResponseDelay(code.get())
330 }
331}
332
333impl From<Mldv1ResponseDelay> for Duration {
334 fn from(code: Mldv1ResponseDelay) -> Self {
335 Duration::from_millis(code.0.into())
336 }
337}
338
339impl TryFrom<Duration> for Mldv1ResponseDelay {
340 type Error = OverflowError;
341 fn try_from(period: Duration) -> Result<Self, Self::Error> {
342 Ok(Mldv1ResponseDelay(u16::try_from(period.as_millis()).map_err(|_| OverflowError)?))
343 }
344}
345
346#[repr(C)]
348#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
349pub struct Mldv1Message {
350 pub max_response_delay: U16,
352 _reserved: U16,
354 pub group_addr: Ipv6Addr,
362}
363
364impl Mldv1Message {
365 pub fn max_response_delay(&self) -> Duration {
367 Mldv1ResponseDelay(self.max_response_delay.get()).into()
368 }
369}
370
371#[derive(Debug)]
373pub struct Mldv1Body<B: SplitByteSlice>(Ref<B, Mldv1Message>);
374
375impl<B: SplitByteSlice> Deref for Mldv1Body<B> {
376 type Target = Mldv1Message;
377
378 fn deref(&self) -> &Self::Target {
379 &*self.0
380 }
381}
382
383impl<B: SplitByteSlice> MessageBody for Mldv1Body<B> {
384 type B = B;
385 fn parse(bytes: B) -> ParseResult<Self> {
386 Ref::from_bytes(bytes).map_or(Err(ParseError::Format), |body| Ok(Mldv1Body(body)))
387 }
388
389 fn len(&self) -> usize {
390 let (inner_header, _inner_body) = self.bytes();
391 debug_assert!(_inner_body.is_none());
392 inner_header.len()
393 }
394
395 fn bytes(&self) -> (&[u8], Option<&[u8]>) {
396 (Ref::bytes(&self.0), None)
397 }
398}
399
400macro_rules! impl_mldv1_message {
401 ($msg:ident, $resp_code:ty, $group_addr:ty) => {
402 impl_icmp_message!(Ipv6, $msg, $msg, IcmpSenderZeroCode, Mldv1Body<B>);
403 impl Mldv1MessageType for $msg {
404 type MaxRespDelay = $resp_code;
405 type GroupAddr = $group_addr;
406 }
407 impl IcmpMldv1MessageType for $msg {}
408 };
409}
410
411impl_mldv1_message!(MulticastListenerQuery, Mldv1ResponseDelay, Ipv6Addr);
412impl_mldv1_message!(MulticastListenerReport, (), MulticastAddr<Ipv6Addr>);
413impl_mldv1_message!(MulticastListenerDone, (), MulticastAddr<Ipv6Addr>);
414
415#[derive(Debug)]
417pub struct Mldv1MessageBuilder<M: Mldv1MessageType> {
418 max_resp_delay: M::MaxRespDelay,
419 group_addr: M::GroupAddr,
420}
421
422impl<M: Mldv1MessageType<MaxRespDelay = ()>> Mldv1MessageBuilder<M> {
423 pub fn new(group_addr: M::GroupAddr) -> Self {
426 Mldv1MessageBuilder { max_resp_delay: (), group_addr }
427 }
428}
429
430impl<M: Mldv1MessageType> Mldv1MessageBuilder<M> {
431 pub fn new_with_max_resp_delay(
434 group_addr: M::GroupAddr,
435 max_resp_delay: M::MaxRespDelay,
436 ) -> Self {
437 Mldv1MessageBuilder { max_resp_delay, group_addr }
438 }
439
440 fn serialize_message(&self, mut buf: &mut [u8]) {
441 use packet::BufferViewMut;
442 let mut bytes = &mut buf;
443 bytes
444 .write_obj_front(&Mldv1Message {
445 max_response_delay: self.max_resp_delay.as_code(),
446 _reserved: U16::ZERO,
447 group_addr: self.group_addr.into(),
448 })
449 .expect("too few bytes for MLDv1 message");
450 }
451}
452
453impl<M: Mldv1MessageType> InnerPacketBuilder for Mldv1MessageBuilder<M> {
454 fn bytes_len(&self) -> usize {
455 size_of::<Mldv1Message>()
456 }
457
458 fn serialize(&self, buf: &mut [u8]) {
459 self.serialize_message(buf);
460 }
461}
462
463#[derive(Debug)]
465pub struct Mldv2QueryMessageBuilder<I> {
466 max_response_delay: Mldv2ResponseDelay,
467 group_addr: Option<MulticastAddr<Ipv6Addr>>,
468 s_flag: bool,
469 qrv: Mldv2QRV,
470 qqic: Mldv2QQIC,
471 sources: I,
472}
473
474impl<I> Mldv2QueryMessageBuilder<I> {
475 pub fn new(
477 max_response_delay: Mldv2ResponseDelay,
478 group_addr: Option<MulticastAddr<Ipv6Addr>>,
479 s_flag: bool,
480 qrv: Mldv2QRV,
481 qqic: Mldv2QQIC,
482 sources: I,
483 ) -> Self {
484 Self { max_response_delay, group_addr, s_flag, qrv, qqic, sources }
485 }
486}
487
488impl<I> InnerPacketBuilder for Mldv2QueryMessageBuilder<I>
489where
490 I: Iterator<Item: Borrow<Ipv6Addr>> + Clone,
491{
492 fn bytes_len(&self) -> usize {
493 core::mem::size_of::<Mldv2QueryMessageHeader>()
494 + self.sources.clone().count() * core::mem::size_of::<Ipv6Addr>()
495 }
496
497 fn serialize(&self, mut buf: &mut [u8]) {
498 use packet::BufferViewMut;
499 let mut bytes = &mut buf;
500 let mut header = bytes
501 .take_obj_front_zero::<Mldv2QueryMessageHeader>()
502 .expect("too few bytes for header");
503 let Mldv2QueryMessageHeader {
504 max_response_code,
505 _reserved,
506 group_addr,
507 sqrv,
508 qqic,
509 number_of_sources,
510 } = &mut *header;
511 let Self {
512 max_response_delay,
513 group_addr: wr_group_addr,
514 s_flag,
515 qrv,
516 qqic: wr_qqic,
517 sources,
518 } = self;
519 *max_response_code = max_response_delay.as_code();
520 *group_addr =
521 wr_group_addr.as_ref().map(|addr| addr.get()).unwrap_or(Ipv6::UNSPECIFIED_ADDRESS);
522 *sqrv = (u8::from(*s_flag) << 3) | (Mldv2QueryMessageHeader::QRV_MASK & u8::from(*qrv));
525 *qqic = wr_qqic.as_code();
526 let mut count: u16 = 0;
527 for src in sources.clone() {
528 count = count.checked_add(1).expect("overflowed number of sources");
529 bytes.write_obj_front(src.borrow()).expect("too few bytes for source");
530 }
531 *number_of_sources = count.into();
532 }
533}
534
535#[derive(Debug)]
537pub struct Mldv2ReportMessageBuilder<I> {
538 groups: I,
539}
540
541impl<I> Mldv2ReportMessageBuilder<I> {
542 pub fn new(groups: I) -> Self {
544 Self { groups }
545 }
546}
547
548impl<I> Mldv2ReportMessageBuilder<I>
549where
550 I: Iterator<Item: GmpReportGroupRecord<Ipv6Addr> + Clone> + Clone,
551{
552 pub fn with_len_limits(
563 self,
564 max_len: usize,
565 ) -> Result<
566 impl Iterator<
567 Item = Mldv2ReportMessageBuilder<
568 impl Iterator<Item: GmpReportGroupRecord<Ipv6Addr>> + Clone,
569 >,
570 >,
571 InvalidConstraintsError,
572 > {
573 let Self { groups } = self;
574 crate::gmp::group_record_split_iterator(
575 max_len.saturating_sub(core::mem::size_of::<Mldv2ReportHeader>()),
576 core::mem::size_of::<Mldv2ReportRecordHeader>(),
577 groups,
578 )
579 .map(|iter| iter.map(|groups| Mldv2ReportMessageBuilder { groups }))
580 }
581}
582
583impl<I> InnerPacketBuilder for Mldv2ReportMessageBuilder<I>
584where
585 I: Iterator<Item: GmpReportGroupRecord<Ipv6Addr>> + Clone,
586{
587 fn bytes_len(&self) -> usize {
588 core::mem::size_of::<Mldv2ReportHeader>()
589 + self
590 .groups
591 .clone()
592 .map(|g| {
593 core::mem::size_of::<Mldv2ReportRecordHeader>()
594 + g.sources().count() * core::mem::size_of::<Ipv6Addr>()
595 })
596 .sum::<usize>()
597 }
598
599 fn serialize(&self, mut buf: &mut [u8]) {
600 use packet::BufferViewMut;
601 let mut bytes = &mut buf;
602 let mut header =
603 bytes.take_obj_front_zero::<Mldv2ReportHeader>().expect("too few bytes for header");
604 let Mldv2ReportHeader { _reserved, num_mcast_addr_records } = &mut *header;
605 let mut mcast_count: u16 = 0;
606 for group in self.groups.clone() {
607 mcast_count = mcast_count.checked_add(1).expect("multicast groups count overflows");
608 let mut header = bytes
609 .take_obj_front_zero::<Mldv2ReportRecordHeader>()
610 .expect("too few bytes for record header");
611 let Mldv2ReportRecordHeader {
612 record_type,
613 aux_data_len,
614 number_of_sources,
615 multicast_address,
616 } = &mut *header;
617 *record_type = group.record_type().into();
618 *aux_data_len = 0;
619 *multicast_address = group.group().into();
620 let mut source_count: u16 = 0;
621 for src in group.sources() {
622 source_count = source_count.checked_add(1).expect("sources count overflows");
623 bytes.write_obj_front(src.borrow()).expect("too few bytes for source");
624 }
625 *number_of_sources = source_count.into();
626 }
627 *num_mcast_addr_records = mcast_count.into();
628 }
629}
630
631#[derive(PartialEq, Eq, Debug, Clone, Copy, Default)]
637pub struct Mldv2ResponseDelay(u16);
638
639impl LinExpConversion<Duration> for Mldv2ResponseDelay {
640 const NUM_MANT_BITS: u8 = 12;
641 const NUM_EXP_BITS: u8 = 3;
642
643 fn lossy_try_from(value: Duration) -> Result<Self, OverflowError> {
644 let millis: u32 = value.as_millis().try_into().map_err(|_| OverflowError)?;
645 Self::lossy_try_from_expanded(millis).map(Self)
646 }
647}
648
649impl MaxCode<U16> for Mldv2ResponseDelay {
650 fn as_code(self) -> U16 {
651 U16::new(self.0)
652 }
653
654 fn from_code(code: U16) -> Self {
655 Mldv2ResponseDelay(code.get())
656 }
657}
658
659impl From<Mldv2ResponseDelay> for Duration {
660 fn from(code: Mldv2ResponseDelay) -> Self {
661 Duration::from_millis(Mldv2ResponseDelay::to_expanded(code.0).into())
662 }
663}
664
665pub type Mldv2QRV = crate::gmp::QRV;
673
674pub type Mldv2QQIC = crate::gmp::QQIC;
682
683impl MaxCode<u8> for Mldv2QQIC {
684 fn as_code(self) -> u8 {
685 self.into()
686 }
687
688 fn from_code(code: u8) -> Self {
689 code.into()
690 }
691}
692
693#[repr(C)]
715#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
716pub struct Mldv2QueryMessageHeader {
717 max_response_code: U16,
719 _reserved: U16,
721 group_addr: Ipv6Addr,
725
726 sqrv: u8,
734 qqic: u8,
736 number_of_sources: U16,
739}
740
741impl Mldv2QueryMessageHeader {
742 const S_FLAG_MASK: u8 = (1 << 3);
743 const QRV_MASK: u8 = 0x07;
744
745 pub fn max_response_delay(&self) -> Mldv2ResponseDelay {
747 Mldv2ResponseDelay(self.max_response_code.get())
748 }
749
750 pub fn group_address(&self) -> Ipv6Addr {
752 self.group_addr
753 }
754
755 pub fn number_of_sources(&self) -> u16 {
757 self.number_of_sources.get()
758 }
759
760 pub fn suppress_router_side_processing(&self) -> bool {
762 (self.sqrv & Self::S_FLAG_MASK) != 0
763 }
764
765 pub fn querier_robustness_variable(&self) -> u8 {
767 self.sqrv & Self::QRV_MASK
768 }
769
770 pub fn querier_query_interval(&self) -> Duration {
772 Mldv2QQIC::from(self.qqic).into()
773 }
774}
775
776#[derive(Debug)]
781pub struct Mldv2QueryBody<B: SplitByteSlice> {
782 header: Ref<B, Mldv2QueryMessageHeader>,
783 sources: Ref<B, [Ipv6Addr]>,
784}
785
786impl<B: SplitByteSlice> Mldv2QueryBody<B> {
787 pub fn header(&self) -> &Mldv2QueryMessageHeader {
789 self.header.deref()
790 }
791
792 pub fn sources(&self) -> &[Ipv6Addr] {
794 self.sources.deref()
795 }
796
797 pub fn as_v1_query(&self) -> Mldv1Body<&[u8]> {
811 let Self { header, sources: _ } = self;
812 let (msg, _rest) = Ref::from_prefix(header.as_bytes()).unwrap();
815 Mldv1Body(msg)
816 }
817}
818
819impl<B: SplitByteSlice> MessageBody for Mldv2QueryBody<B> {
820 type B = B;
821 fn parse(bytes: B) -> ParseResult<Self> {
822 let (header, bytes) = Ref::<_, Mldv2QueryMessageHeader>::from_prefix(bytes)
823 .map_err(|_| ParseError::Format)?;
824 let num_sources_bytes =
825 usize::from(Ipv6Addr::BYTES) * usize::from(header.number_of_sources());
826 let (sources_bytes, _rest) =
832 bytes.split_at(num_sources_bytes).map_err(|_| ParseError::Format)?;
833 let sources =
834 Ref::<B, [Ipv6Addr]>::from_bytes(sources_bytes).map_err(|_| ParseError::Format)?;
835 Ok(Mldv2QueryBody { header, sources })
836 }
837
838 fn len(&self) -> usize {
839 let (inner_header, inner_body) = self.bytes();
840 inner_header.len() + inner_body.unwrap().len()
843 }
844
845 fn bytes(&self) -> (&[u8], Option<&[u8]>) {
846 (Ref::bytes(&self.header), Some(Ref::bytes(&self.sources)))
847 }
848}
849
850#[repr(C)]
852#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
853pub struct MulticastListenerQueryV2;
854
855impl_icmp_message!(
856 Ipv6,
857 MulticastListenerQueryV2,
858 MulticastListenerQuery,
859 IcmpSenderZeroCode,
860 Mldv2QueryBody<B>
861);
862
863#[cfg(test)]
864mod tests {
865 use packet::{NestableSerializer as _, NoOpSerializationContext, ParseBuffer, Serializer};
866 use test_case::test_case;
867
868 use super::*;
869 use crate::gmp::{ExactConversionError, GroupRecordType};
870 use crate::icmp::{IcmpPacketBuilder, IcmpParseArgs};
871 use crate::ip::Ipv6Proto;
872 use crate::ipv6::ext_hdrs::{
873 ExtensionHeaderOptionAction, HopByHopOption, HopByHopOptionData, Ipv6ExtensionHeader,
874 };
875 use crate::ipv6::{Ipv6Header, Ipv6Packet, Ipv6PacketBuilder, Ipv6PacketBuilderWithHbhOptions};
876 use net_declare::net_ip_v6;
877
878 fn serialize_to_bytes<B: SplitByteSlice + Debug, M: IcmpMessage<Ipv6> + Debug>(
879 src_ip: Ipv6Addr,
880 dst_ip: Ipv6Addr,
881 icmp: &IcmpPacket<Ipv6, B, M>,
882 ) -> Vec<u8> {
883 let ip = Ipv6PacketBuilder::new(src_ip, dst_ip, 1, Ipv6Proto::Icmpv6);
884 let with_options = Ipv6PacketBuilderWithHbhOptions::new(
885 ip,
886 &[HopByHopOption {
887 action: ExtensionHeaderOptionAction::SkipAndContinue,
888 mutable: false,
889 data: HopByHopOptionData::RouterAlert { data: 0 },
890 }],
891 )
892 .unwrap();
893 let (header, body) = icmp.message_body.bytes();
894 let body = if let Some(b) = body { b } else { &[] };
895 let complete_msg = &[header, body].concat();
896 complete_msg
897 .into_serializer()
898 .wrap_in(icmp.builder(src_ip, dst_ip))
899 .wrap_in(with_options)
900 .serialize_vec_outer(&mut NoOpSerializationContext)
901 .unwrap()
902 .as_ref()
903 .to_vec()
904 }
905
906 fn test_parse_and_serialize<
907 M: IcmpMessage<Ipv6> + Debug,
908 F: FnOnce(&Ipv6Packet<&[u8]>),
909 G: for<'a> FnOnce(&IcmpPacket<Ipv6, &'a [u8], M>),
910 >(
911 src_ip: Ipv6Addr,
912 dst_ip: Ipv6Addr,
913 mut req: &[u8],
914 check_ip: F,
915 check_icmp: G,
916 ) {
917 let orig_req = req;
918
919 let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
920 check_ip(&ip);
921 let icmp =
922 req.parse_with::<_, IcmpPacket<_, _, M>>(IcmpParseArgs::new(src_ip, dst_ip)).unwrap();
923 check_icmp(&icmp);
924
925 let data = serialize_to_bytes(src_ip, dst_ip, &icmp);
926 assert_eq!(&data[..], orig_req);
927 }
928
929 fn serialize_to_bytes_with_builder<M: IcmpMldv1MessageType + Debug>(
930 src_ip: Ipv6Addr,
931 dst_ip: Ipv6Addr,
932 msg: M,
933 group_addr: M::GroupAddr,
934 max_resp_delay: M::MaxRespDelay,
935 ) -> Vec<u8> {
936 let ip = Ipv6PacketBuilder::new(src_ip, dst_ip, 1, Ipv6Proto::Icmpv6);
937 let with_options = Ipv6PacketBuilderWithHbhOptions::new(
938 ip,
939 &[HopByHopOption {
940 action: ExtensionHeaderOptionAction::SkipAndContinue,
941 mutable: false,
942 data: HopByHopOptionData::RouterAlert { data: 0 },
943 }],
944 )
945 .unwrap();
946 Mldv1MessageBuilder::<M>::new_with_max_resp_delay(group_addr, max_resp_delay)
948 .into_serializer()
949 .wrap_in(IcmpPacketBuilder::new(src_ip, dst_ip, IcmpSenderZeroCode, msg))
950 .wrap_in(with_options)
951 .serialize_vec_outer(&mut NoOpSerializationContext)
952 .unwrap()
953 .as_ref()
954 .to_vec()
955 }
956
957 fn serialize_to_bytes_with_builder_v2<
958 M: IcmpMessage<Ipv6, Code = IcmpSenderZeroCode> + Debug,
959 B: InnerPacketBuilder + Debug,
960 >(
961 src_ip: Ipv6Addr,
962 dst_ip: Ipv6Addr,
963 msg: M,
964 builder: B,
965 ) -> Vec<u8> {
966 let ip = Ipv6PacketBuilder::new(src_ip, dst_ip, 1, Ipv6Proto::Icmpv6);
967 let with_options = Ipv6PacketBuilderWithHbhOptions::new(
968 ip,
969 &[HopByHopOption {
970 action: ExtensionHeaderOptionAction::SkipAndContinue,
971 mutable: false,
972 data: HopByHopOptionData::RouterAlert { data: 0 },
973 }],
974 )
975 .unwrap();
976 builder
979 .into_serializer()
980 .wrap_in(IcmpPacketBuilder::new(src_ip, dst_ip, IcmpSenderZeroCode, msg))
981 .wrap_in(with_options)
982 .serialize_vec_outer(&mut NoOpSerializationContext)
983 .unwrap()
984 .as_ref()
985 .to_vec()
986 }
987
988 fn check_ip<B: SplitByteSlice>(ip: &Ipv6Packet<B>, src_ip: Ipv6Addr, dst_ip: Ipv6Addr) {
989 assert_eq!(ip.src_ip(), src_ip);
990 assert_eq!(ip.dst_ip(), dst_ip);
991 assert_eq!(ip.iter_extension_hdrs().count(), 1);
992 let hbh = ip.iter_extension_hdrs().next().unwrap();
993 match hbh {
994 Ipv6ExtensionHeader::HopByHopOptions { options } => {
995 assert_eq!(options.iter().count(), 1);
996 assert_eq!(
997 options.iter().next().unwrap(),
998 HopByHopOption {
999 action: ExtensionHeaderOptionAction::SkipAndContinue,
1000 mutable: false,
1001 data: HopByHopOptionData::RouterAlert { data: 0 },
1002 }
1003 );
1004 }
1005 _ => panic!("Wrong extension header"),
1006 }
1007 }
1008
1009 fn check_mld_v1<
1010 B: SplitByteSlice,
1011 M: IcmpMessage<Ipv6, Body<B> = Mldv1Body<B>> + Mldv1MessageType + Debug,
1012 >(
1013 icmp: &IcmpPacket<Ipv6, B, M>,
1014 max_resp_code: u16,
1015 group_addr: Ipv6Addr,
1016 ) {
1017 assert_eq!(icmp.message_body._reserved.get(), 0);
1018 assert_eq!(icmp.message_body.max_response_delay.get(), max_resp_code);
1019 assert_eq!(icmp.message_body.group_addr, group_addr);
1020 }
1021
1022 fn check_mld_query_v2<
1023 'a,
1024 B: SplitByteSlice,
1025 M: IcmpMessage<Ipv6, Body<B> = Mldv2QueryBody<B>> + Debug,
1026 >(
1027 icmp: &IcmpPacket<Ipv6, B, M>,
1028 max_resp_code: u16,
1029 group_addr: Ipv6Addr,
1030 sources: &[Ipv6Addr],
1031 ) {
1032 assert_eq!(icmp.message_body.header._reserved.get(), 0);
1033 assert_eq!(icmp.message_body.header.max_response_code.get(), max_resp_code);
1034 assert_eq!(icmp.message_body.header.group_addr, group_addr);
1035 assert_eq!(icmp.message_body.sources.len(), sources.len());
1036 for (expected, actual) in sources.iter().zip(icmp.message_body.sources.iter()) {
1037 assert_eq!(actual, expected);
1038 }
1039
1040 let Mldv1Body(v1) = icmp.message_body.as_v1_query();
1042 assert_eq!(v1.max_response_delay.get(), max_resp_code);
1043 assert_eq!(v1.group_addr, group_addr);
1044 }
1045
1046 fn check_mld_report_v2<
1047 'a,
1048 B: SplitByteSlice,
1049 M: IcmpMessage<Ipv6, Body<B> = Mldv2ReportBody<B>> + Debug,
1050 >(
1051 icmp: &IcmpPacket<Ipv6, B, M>,
1052 expected_records_header: &[(Mldv2MulticastRecordType, Ipv6Addr)],
1053 expected_records_sources: &[&[Ipv6Addr]],
1054 ) {
1055 assert_eq!(
1056 icmp.message_body.header.num_mcast_addr_records.get(),
1057 u16::try_from(expected_records_header.len()).unwrap()
1058 );
1059 let expected_records = expected_records_header.iter().zip(expected_records_sources.iter());
1060 for (expected_record, actual_record) in
1061 expected_records.zip(icmp.message_body.iter_multicast_records())
1062 {
1063 let (expected_header, expected_sources) = expected_record;
1064 let (expected_record_type, expected_multicast_addr) = expected_header;
1065 assert_eq!(
1066 expected_record_type,
1067 &actual_record.header.record_type().expect("valid record type")
1068 );
1069
1070 assert_eq!(
1071 u16::try_from(expected_sources.len()).unwrap(),
1072 actual_record.header.number_of_sources()
1073 );
1074 assert_eq!(expected_multicast_addr, actual_record.header.multicast_addr());
1075 assert_eq!(*expected_sources, actual_record.sources());
1076 }
1077 }
1078
1079 #[test]
1080 fn test_mld_parse_and_serialize_query() {
1081 use crate::icmp::mld::MulticastListenerQuery;
1082 use crate::testdata::mld_router_query::*;
1083 test_parse_and_serialize::<MulticastListenerQuery, _, _>(
1084 SRC_IP,
1085 DST_IP,
1086 QUERY,
1087 |ip| {
1088 check_ip(ip, SRC_IP, DST_IP);
1089 },
1090 |icmp| {
1091 check_mld_v1(icmp, MAX_RESP_CODE, HOST_GROUP_ADDRESS);
1092 },
1093 );
1094 }
1095
1096 #[test]
1097 fn test_mld_parse_and_serialize_report() {
1098 use crate::icmp::mld::MulticastListenerReport;
1099 use crate::testdata::mld_router_report::*;
1100 test_parse_and_serialize::<MulticastListenerReport, _, _>(
1101 SRC_IP,
1102 DST_IP,
1103 REPORT,
1104 |ip| {
1105 check_ip(ip, SRC_IP, DST_IP);
1106 },
1107 |icmp| {
1108 check_mld_v1(icmp, 0, HOST_GROUP_ADDRESS);
1109 },
1110 );
1111 }
1112
1113 #[test]
1114 fn test_mld_parse_and_serialize_done() {
1115 use crate::icmp::mld::MulticastListenerDone;
1116 use crate::testdata::mld_router_done::*;
1117 test_parse_and_serialize::<MulticastListenerDone, _, _>(
1118 SRC_IP,
1119 DST_IP,
1120 DONE,
1121 |ip| {
1122 check_ip(ip, SRC_IP, DST_IP);
1123 },
1124 |icmp| {
1125 check_mld_v1(icmp, 0, HOST_GROUP_ADDRESS);
1126 },
1127 );
1128 }
1129
1130 #[test]
1131 fn test_mld_parse_and_serialize_query_v2() {
1132 use crate::icmp::mld::MulticastListenerQueryV2;
1133 use crate::testdata::mld_router_query::*;
1134 test_parse_and_serialize::<MulticastListenerQueryV2, _, _>(
1135 SRC_IP,
1136 DST_IP,
1137 QUERY_V2,
1138 |ip| {
1139 check_ip(ip, SRC_IP, DST_IP);
1140 },
1141 |icmp| {
1142 check_mld_query_v2(icmp, MAX_RESP_CODE, HOST_GROUP_ADDRESS, SOURCES);
1143 },
1144 );
1145 }
1146
1147 #[test]
1148 fn test_mld_parse_and_serialize_report_v2() {
1149 use crate::icmp::mld::MulticastListenerReportV2;
1150 use crate::testdata::mld_router_report_v2::*;
1151 test_parse_and_serialize::<MulticastListenerReportV2, _, _>(
1152 SRC_IP,
1153 DST_IP,
1154 REPORT,
1155 |ip| {
1156 check_ip(ip, SRC_IP, DST_IP);
1157 },
1158 |icmp| {
1159 check_mld_report_v2(icmp, RECORDS_HEADERS, RECORDS_SOURCES);
1160 },
1161 );
1162 }
1163
1164 #[test]
1165 fn test_mld_serialize_and_parse_query() {
1166 use crate::icmp::mld::MulticastListenerQuery;
1167 use crate::testdata::mld_router_query::*;
1168 let bytes = serialize_to_bytes_with_builder::<_>(
1169 SRC_IP,
1170 DST_IP,
1171 MulticastListenerQuery,
1172 HOST_GROUP_ADDRESS,
1173 Duration::from_secs(1).try_into().unwrap(),
1174 );
1175 assert_eq!(&bytes[..], QUERY);
1176 let mut req = &bytes[..];
1177 let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1178 check_ip(&ip, SRC_IP, DST_IP);
1179 let icmp = req
1180 .parse_with::<_, IcmpPacket<_, _, MulticastListenerQuery>>(IcmpParseArgs::new(
1181 SRC_IP, DST_IP,
1182 ))
1183 .unwrap();
1184 check_mld_v1(&icmp, MAX_RESP_CODE, HOST_GROUP_ADDRESS);
1185 }
1186
1187 #[test]
1188 fn test_mld_serialize_and_parse_report() {
1189 use crate::icmp::mld::MulticastListenerReport;
1190 use crate::testdata::mld_router_report::*;
1191 let bytes = serialize_to_bytes_with_builder::<_>(
1192 SRC_IP,
1193 DST_IP,
1194 MulticastListenerReport,
1195 MulticastAddr::new(HOST_GROUP_ADDRESS).unwrap(),
1196 (),
1197 );
1198 assert_eq!(&bytes[..], REPORT);
1199 let mut req = &bytes[..];
1200 let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1201 check_ip(&ip, SRC_IP, DST_IP);
1202 let icmp = req
1203 .parse_with::<_, IcmpPacket<_, _, MulticastListenerReport>>(IcmpParseArgs::new(
1204 SRC_IP, DST_IP,
1205 ))
1206 .unwrap();
1207 check_mld_v1(&icmp, 0, HOST_GROUP_ADDRESS);
1208 }
1209
1210 #[test]
1211 fn test_mld_serialize_and_parse_done() {
1212 use crate::icmp::mld::MulticastListenerDone;
1213 use crate::testdata::mld_router_done::*;
1214 let bytes = serialize_to_bytes_with_builder::<_>(
1215 SRC_IP,
1216 DST_IP,
1217 MulticastListenerDone,
1218 MulticastAddr::new(HOST_GROUP_ADDRESS).unwrap(),
1219 (),
1220 );
1221 assert_eq!(&bytes[..], DONE);
1222 let mut req = &bytes[..];
1223 let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1224 check_ip(&ip, SRC_IP, DST_IP);
1225 let icmp = req
1226 .parse_with::<_, IcmpPacket<_, _, MulticastListenerDone>>(IcmpParseArgs::new(
1227 SRC_IP, DST_IP,
1228 ))
1229 .unwrap();
1230 check_mld_v1(&icmp, 0, HOST_GROUP_ADDRESS);
1231 }
1232
1233 #[test]
1234 fn test_mld_serialize_and_parse_query_v2() {
1235 use crate::icmp::mld::{Mldv2QRV, Mldv2ResponseDelay, MulticastListenerQueryV2};
1236 use crate::testdata::mld_router_query::*;
1237 use core::time::Duration;
1238
1239 let builder = Mldv2QueryMessageBuilder::new(
1240 Mldv2ResponseDelay::lossy_try_from(Duration::from_millis(MAX_RESP_CODE.into()))
1241 .unwrap(),
1242 MulticastAddr::new(HOST_GROUP_ADDRESS),
1243 S_FLAG,
1244 Mldv2QRV::new(QRV),
1245 Mldv2QQIC::lossy_try_from(Duration::from_secs(QQIC.into())).unwrap(),
1246 SOURCES.iter(),
1247 );
1248
1249 let bytes =
1250 serialize_to_bytes_with_builder_v2(SRC_IP, DST_IP, MulticastListenerQueryV2, builder);
1251 assert_eq!(&bytes[..], QUERY_V2);
1252 let mut req = &bytes[..];
1253 let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1254 check_ip(&ip, SRC_IP, DST_IP);
1255 let icmp = req
1256 .parse_with::<_, IcmpPacket<_, _, MulticastListenerQueryV2>>(IcmpParseArgs::new(
1257 SRC_IP, DST_IP,
1258 ))
1259 .unwrap();
1260
1261 check_mld_query_v2(&icmp, MAX_RESP_CODE, HOST_GROUP_ADDRESS, SOURCES);
1262 }
1263
1264 #[test]
1265 fn test_mld_serialize_and_parse_report_v2() {
1266 use crate::icmp::mld::MulticastListenerReportV2;
1267 use crate::testdata::mld_router_report_v2::*;
1268
1269 let builder = Mldv2ReportMessageBuilder::new(
1270 RECORDS_HEADERS.iter().zip(RECORDS_SOURCES.iter()).map(|record| {
1271 let (record_header, record_sources) = record;
1272 let (record_type, multicast_addr) = record_header;
1273 (MulticastAddr::new(*multicast_addr).unwrap(), *record_type, record_sources.iter())
1274 }),
1275 );
1276
1277 let bytes =
1278 serialize_to_bytes_with_builder_v2(SRC_IP, DST_IP, MulticastListenerReportV2, builder);
1279 assert_eq!(&bytes[..], REPORT);
1280 let mut req = &bytes[..];
1281 let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1282 check_ip(&ip, SRC_IP, DST_IP);
1283 let icmp = req
1284 .parse_with::<_, IcmpPacket<_, _, MulticastListenerReportV2>>(IcmpParseArgs::new(
1285 SRC_IP, DST_IP,
1286 ))
1287 .unwrap();
1288
1289 check_mld_report_v2(&icmp, RECORDS_HEADERS, RECORDS_SOURCES);
1290 }
1291
1292 #[test_case(&[0x11, 0x22, 0x33, 0x44]; "extra bytes")]
1293 #[test_case(Ipv6Addr::new([1, 2, 3, 4, 5, 6, 7, 8]).bytes(); "extra addr")]
1294 fn test_mld_query_v2_trailing_bytes_ignored(extend_bytes: &[u8]) {
1295 use crate::testdata::mld_router_query::*;
1296
1297 let mut body_with_trailing = QUERY_V2.to_vec();
1298 body_with_trailing.extend_from_slice(extend_bytes);
1299
1300 let mut req = &body_with_trailing[..];
1301 let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1302 check_ip(&ip, SRC_IP, DST_IP);
1303 let icmp = req
1304 .parse_with::<_, IcmpPacket<_, _, MulticastListenerQueryV2>>(IcmpParseArgs::new(
1305 SRC_IP, DST_IP,
1306 ))
1307 .unwrap();
1308
1309 check_mld_query_v2(&icmp, MAX_RESP_CODE, HOST_GROUP_ADDRESS, SOURCES);
1310 }
1311
1312 #[test]
1325 fn report_v2_split_many_sources() {
1326 use crate::testdata::mld_router_report_v2::*;
1327 use packet::{NestablePacketBuilder as _, NestableSerializer as _};
1328
1329 const ETH_MTU: usize = 1500;
1330 const MAX_SOURCES: usize = 89;
1331
1332 let ip_builder = Ipv6PacketBuilderWithHbhOptions::new(
1333 Ipv6PacketBuilder::new(SRC_IP, DST_IP, 1, Ipv6Proto::Icmpv6),
1334 &[HopByHopOption {
1335 action: ExtensionHeaderOptionAction::SkipAndContinue,
1336 mutable: false,
1337 data: HopByHopOptionData::RouterAlert { data: 0 },
1338 }],
1339 )
1340 .unwrap();
1341 let icmp_builder =
1342 IcmpPacketBuilder::new(SRC_IP, DST_IP, IcmpSenderZeroCode, MulticastListenerReportV2);
1343
1344 let avail_len = ETH_MTU
1345 - ip_builder.constraints().header_len()
1346 - icmp_builder.constraints().header_len();
1347
1348 let src_ip = |i: usize| Ipv6Addr::new([0x2000, 0, 0, 0, 0, 0, 0, i as u16]);
1349 let group_addr = MulticastAddr::new(RECORDS_HEADERS[0].1).unwrap();
1350 let reports = Mldv2ReportMessageBuilder::new(
1351 [(
1352 group_addr,
1353 Mldv2MulticastRecordType::ModeIsInclude,
1354 (0..MAX_SOURCES).into_iter().map(|i| src_ip(i)),
1355 )]
1356 .into_iter(),
1357 )
1358 .with_len_limits(avail_len)
1359 .unwrap();
1360
1361 let mut reports = reports.map(|builder| {
1362 builder
1363 .into_serializer()
1364 .wrap_in(icmp_builder.clone())
1365 .wrap_in(ip_builder.clone())
1366 .serialize_vec_outer(&mut NoOpSerializationContext)
1367 .unwrap_or_else(|(err, _)| panic!("{err:?}"))
1368 .unwrap_b()
1369 .into_inner()
1370 });
1371 let serialized = reports.next().unwrap();
1373 assert_eq!(serialized.len(), ETH_MTU);
1374 let mut buffer = &serialized[..];
1375 let ip = buffer.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1376 check_ip(&ip, SRC_IP, DST_IP);
1377 let icmp = buffer
1378 .parse_with::<_, IcmpPacket<_, _, MulticastListenerReportV2>>(IcmpParseArgs::new(
1379 SRC_IP, DST_IP,
1380 ))
1381 .unwrap();
1382
1383 let mut groups = icmp.body().iter_multicast_records();
1384 let group = groups.next().expect("has group");
1385 assert_eq!(group.header.multicast_address, group_addr.get());
1386 assert_eq!(usize::from(group.header.number_of_sources()), MAX_SOURCES);
1387 assert_eq!(group.sources().len(), MAX_SOURCES);
1388 for (i, addr) in group.sources().iter().enumerate() {
1389 assert_eq!(*addr, src_ip(i));
1390 }
1391 assert_eq!(groups.next().map(|r| r.header.multicast_address), None);
1392 assert_eq!(reports.next(), None);
1394
1395 let reports = Mldv2ReportMessageBuilder::new(
1396 [(
1397 group_addr,
1398 Mldv2MulticastRecordType::ModeIsInclude,
1399 core::iter::repeat(SRC_IP).take(MAX_SOURCES + 1),
1400 )]
1401 .into_iter(),
1402 )
1403 .with_len_limits(avail_len)
1404 .unwrap();
1405 assert_eq!(
1407 reports
1408 .map(|r| r.groups.map(|group| group.sources().count()).collect::<Vec<_>>())
1409 .collect::<Vec<_>>(),
1410 vec![vec![MAX_SOURCES], vec![1]]
1411 );
1412 }
1413
1414 #[test]
1424 fn report_v2_split_many_groups() {
1425 use crate::testdata::mld_router_report_v2::*;
1426 use packet::{NestablePacketBuilder as _, NestableSerializer as _};
1427
1428 const ETH_MTU: usize = 1500;
1429 const EXPECT_SERIALIZED: usize = 1496;
1430 const MAX_GROUPS: usize = 72;
1431
1432 let ip_builder = Ipv6PacketBuilderWithHbhOptions::new(
1433 Ipv6PacketBuilder::new(SRC_IP, DST_IP, 1, Ipv6Proto::Icmpv6),
1434 &[HopByHopOption {
1435 action: ExtensionHeaderOptionAction::SkipAndContinue,
1436 mutable: false,
1437 data: HopByHopOptionData::RouterAlert { data: 0 },
1438 }],
1439 )
1440 .unwrap();
1441 let icmp_builder =
1442 IcmpPacketBuilder::new(SRC_IP, DST_IP, IcmpSenderZeroCode, MulticastListenerReportV2);
1443
1444 let avail_len = ETH_MTU
1445 - ip_builder.constraints().header_len()
1446 - icmp_builder.constraints().header_len();
1447
1448 let group_ip = |i: usize| {
1449 MulticastAddr::new(Ipv6Addr::new([0xff02, 0, 0, 0, 0, 0, 0, i as u16])).unwrap()
1450 };
1451 let reports = Mldv2ReportMessageBuilder::new((0..MAX_GROUPS).into_iter().map(|i| {
1452 (group_ip(i), Mldv2MulticastRecordType::ModeIsExclude, core::iter::empty::<Ipv6Addr>())
1453 }))
1454 .with_len_limits(avail_len)
1455 .unwrap();
1456
1457 let mut reports = reports.map(|builder| {
1458 builder
1459 .into_serializer()
1460 .wrap_in(icmp_builder.clone())
1461 .wrap_in(ip_builder.clone())
1462 .serialize_vec_outer(&mut NoOpSerializationContext)
1463 .unwrap_or_else(|(err, _)| panic!("{err:?}"))
1464 .unwrap_b()
1465 .into_inner()
1466 });
1467 let serialized = reports.next().unwrap();
1469 assert_eq!(serialized.len(), EXPECT_SERIALIZED);
1470 let mut buffer = &serialized[..];
1471 let ip = buffer.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1472 check_ip(&ip, SRC_IP, DST_IP);
1473 let icmp = buffer
1474 .parse_with::<_, IcmpPacket<_, _, MulticastListenerReportV2>>(IcmpParseArgs::new(
1475 SRC_IP, DST_IP,
1476 ))
1477 .unwrap();
1478 assert_eq!(usize::from(icmp.body().header().num_mcast_addr_records()), MAX_GROUPS);
1479 for (i, group) in icmp.body().iter_multicast_records().enumerate() {
1480 assert_eq!(group.header.number_of_sources.get(), 0);
1481 assert_eq!(group.header.multicast_addr(), &group_ip(i).get());
1482 }
1483 assert_eq!(reports.next(), None);
1485
1486 let reports = Mldv2ReportMessageBuilder::new((0..MAX_GROUPS + 1).into_iter().map(|i| {
1487 (group_ip(i), Mldv2MulticastRecordType::ModeIsExclude, core::iter::empty::<Ipv6Addr>())
1488 }))
1489 .with_len_limits(avail_len)
1490 .unwrap();
1491 assert_eq!(reports.map(|r| r.groups.count()).collect::<Vec<_>>(), vec![MAX_GROUPS, 1]);
1493 }
1494
1495 #[test]
1496 fn test_mld_parse_and_serialize_response_delay_v2_linear() {
1497 for code in 0..(Mldv2ResponseDelay::SWITCHPOINT as u16) {
1499 let response_delay = Mldv2ResponseDelay::from_code(U16::from(code));
1500 let duration = Duration::from(response_delay);
1501 assert_eq!(duration.as_millis(), code.into());
1502
1503 let duration = Duration::from_millis(code.into());
1504 let response_delay_code: u16 =
1505 Mldv2ResponseDelay::lossy_try_from(duration).unwrap().as_code().into();
1506 assert_eq!(response_delay_code, code);
1507
1508 let duration = Duration::from_millis(code.into());
1509 let response_delay_code: u16 =
1510 Mldv2ResponseDelay::exact_try_from(duration).unwrap().as_code().into();
1511 assert_eq!(response_delay_code, code);
1512 }
1513 }
1514
1515 #[test_case(Mldv2ResponseDelay::SWITCHPOINT, 0x8000; "min exponential value")]
1516 #[test_case(32784, 0x8002; "exponental value 32784")]
1517 #[test_case(227744, 0xABCD; "exponental value 227744")]
1518 #[test_case(1821184, 0xDBCA; "exponental value 1821184")]
1519 #[test_case(8385536, 0xFFFD; "exponental value 8385536")]
1520 #[test_case(Mldv2ResponseDelay::MAX_VALUE, 0xFFFF; "max exponential value")]
1521 fn test_mld_parse_and_serialize_response_delay_v2_exponential_exact(
1522 duration_millis: u32,
1523 resp_code: u16,
1524 ) {
1525 let response_delay = Mldv2ResponseDelay::from_code(resp_code.into());
1526 let duration = Duration::from(response_delay);
1527 assert_eq!(duration.as_millis(), duration_millis.into());
1528
1529 let response_delay_code: u16 =
1530 Mldv2ResponseDelay::lossy_try_from(duration).unwrap().as_code().into();
1531 assert_eq!(response_delay_code, resp_code);
1532
1533 let response_delay_code: u16 =
1534 Mldv2ResponseDelay::exact_try_from(duration).unwrap().as_code().into();
1535 assert_eq!(response_delay_code, resp_code);
1536 }
1537
1538 #[test]
1539 fn test_mld_parse_and_serialize_response_delay_v2_errors() {
1540 let duration = Duration::from_millis((Mldv2ResponseDelay::MAX_VALUE + 1).into());
1541 assert_eq!(Mldv2ResponseDelay::lossy_try_from(duration), Err(OverflowError));
1542
1543 let duration = Duration::from_millis((Mldv2ResponseDelay::MAX_VALUE + 1).into());
1544 assert_eq!(
1545 Mldv2ResponseDelay::exact_try_from(duration),
1546 Err(ExactConversionError::Overflow)
1547 );
1548
1549 let duration = Duration::from_millis((Mldv2ResponseDelay::MAX_VALUE - 1).into());
1550 assert_eq!(
1551 Mldv2ResponseDelay::exact_try_from(duration),
1552 Err(ExactConversionError::NotExact)
1553 );
1554 }
1555
1556 #[test]
1557 fn test_mld_parse_and_serialize_response_qqic_v2_linear() {
1558 for code in 0..(Mldv2QQIC::SWITCHPOINT as u8) {
1560 let response_delay = Mldv2QQIC::from_code(code);
1561 let duration = Duration::from(response_delay);
1562 assert_eq!(duration.as_secs(), code.into());
1563
1564 let duration = Duration::from_secs(code.into());
1565 let response_delay_code: u8 =
1566 Mldv2QQIC::lossy_try_from(duration).unwrap().as_code().into();
1567 assert_eq!(response_delay_code, code);
1568
1569 let duration = Duration::from_secs(code.into());
1570 let response_delay_code: u8 =
1571 Mldv2QQIC::exact_try_from(duration).unwrap().as_code().into();
1572 assert_eq!(response_delay_code, code);
1573 }
1574 }
1575
1576 #[test_case(Mldv2QQIC::SWITCHPOINT, 0x80; "min exponential value")]
1577 #[test_case(144, 0x82; "exponental value 144")]
1578 #[test_case(928, 0xAD; "exponental value 928")]
1579 #[test_case(6656, 0xDA; "exponental value 6656")]
1580 #[test_case(29696, 0xFD; "exponental value 29696")]
1581 #[test_case(Mldv2QQIC::MAX_VALUE, 0xFF; "max exponential value")]
1582 fn test_mld_parse_and_serialize_response_qqic_v2_exponential_exact(
1583 duration_secs: u32,
1584 resp_code: u8,
1585 ) {
1586 let response_delay = Mldv2QQIC::from_code(resp_code.into());
1587 let duration = Duration::from(response_delay);
1588 assert_eq!(duration.as_secs(), duration_secs.into());
1589
1590 let response_delay_code: u8 = Mldv2QQIC::lossy_try_from(duration).unwrap().as_code().into();
1591 assert_eq!(response_delay_code, resp_code);
1592
1593 let response_delay_code: u8 = Mldv2QQIC::exact_try_from(duration).unwrap().as_code().into();
1594 assert_eq!(response_delay_code, resp_code);
1595 }
1596
1597 #[test]
1598 fn test_mld_parse_and_serialize_response_qqic_v2_errors() {
1599 let duration = Duration::from_secs((Mldv2QQIC::MAX_VALUE + 1).into());
1600 assert_eq!(Mldv2QQIC::lossy_try_from(duration), Err(OverflowError));
1601
1602 let duration = Duration::from_secs((Mldv2QQIC::MAX_VALUE + 1).into());
1603 assert_eq!(Mldv2QQIC::exact_try_from(duration), Err(ExactConversionError::Overflow));
1604
1605 let duration = Duration::from_secs((Mldv2QQIC::MAX_VALUE - 1).into());
1606 assert_eq!(Mldv2QQIC::exact_try_from(duration), Err(ExactConversionError::NotExact));
1607 }
1608
1609 #[test]
1610 fn test_mld_too_few_records() {
1611 let src_ip = net_ip_v6!("::1");
1612 let group_addr = MulticastAddr::new(net_ip_v6!("ff02::2")).unwrap();
1613
1614 let builder = Mldv2ReportMessageBuilder::new(
1616 [(group_addr, GroupRecordType::ModeIsInclude, core::iter::once(src_ip))].into_iter(),
1617 );
1618 let serialized = builder
1619 .into_serializer()
1620 .serialize_vec_outer(&mut NoOpSerializationContext)
1621 .unwrap()
1622 .unwrap_b()
1623 .into_inner();
1624
1625 let header_size = core::mem::size_of::<Mldv2ReportHeader>();
1627 let record_bytes = &serialized[header_size..];
1628
1629 let mut ctx = 2; let res = Records::<_, Mldv2ReportRecords>::parse_with_mut_context(record_bytes, &mut ctx);
1631 assert_eq!(res.unwrap_err(), ParseError::Format);
1632 assert_eq!(ctx, 1); }
1634}