packet_formats/icmp/
mld.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Multicast Listener Discovery Protocol.
6//!
7//! Wire serialization and deserialization functions.
8
9use 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// TODO(https://github.com/google/zerocopy/issues/1528): Use std::convert::Infallible.
30/// A record that can never be instantiated. Trying to instantiate this will result in a compile
31/// error.
32///
33/// At time of writing, [std::convert::Infallible] does not implement [Immutable] nor [IntoBytes]
34/// therefore this enum was created.
35#[derive(Debug, Immutable)]
36pub enum UninstantiableRecord {}
37
38// We have to implement `only_derive_is_allowed_to_implement_this_trait` because
39// `#[derive(IntoBytes)]` works only if we can have a `repr` for that type, but since that the type
40// is empty we cannot have `repr`.
41unsafe impl IntoBytes for UninstantiableRecord {
42    fn only_derive_is_allowed_to_implement_this_trait() {
43        panic!("UninstantiableRecord cannot be instantiated");
44    }
45}
46
47/// An ICMPv6 packet with an MLD message.
48#[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/// A raw ICMPv6 packet with an MLD message.
59#[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
69/// Multicast Record Types as defined in [RFC 3810 section 5.2.12].
70///
71/// Aliased to shared GMP implementation for convenience.
72///
73/// [RFC 3810 section 5.2.12]:
74///     https://www.rfc-editor.org/rfc/rfc3810#section-5.2.12
75pub type Mldv2MulticastRecordType = crate::gmp::GroupRecordType;
76
77/// Fixed information for an MLDv2 Report's Multicast Record, per
78/// [RFC 3810 section 5.2].
79///
80/// [RFC 3810 section 5.2]: https://www.rfc-editor.org/rfc/rfc3810#section-5.2
81#[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    /// Create a `Mldv2ReportRecordHeader`.
92    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    /// Returns the number of sources.
107    pub fn number_of_sources(&self) -> u16 {
108        self.number_of_sources.get()
109    }
110
111    /// Returns the type of the record.
112    pub fn record_type(&self) -> Result<Mldv2MulticastRecordType, UnrecognizedProtocolCode<u8>> {
113        Mldv2MulticastRecordType::try_from(self.record_type)
114    }
115
116    /// Returns the multicast address.
117    pub fn multicast_addr(&self) -> &Ipv6Addr {
118        &self.multicast_address
119    }
120}
121
122/// Wire representation of an MLDv2 Report's Multicast Record, per
123/// [RFC 3810 section 5.2].
124///
125/// [RFC 3810 section 5.2]: https://www.rfc-editor.org/rfc/rfc3810#section-5.2
126pub struct MulticastRecord<B> {
127    header: Ref<B, Mldv2ReportRecordHeader>,
128    sources: Ref<B, [Ipv6Addr]>,
129}
130
131impl<B: SplitByteSlice> MulticastRecord<B> {
132    /// Returns the multicast record header.
133    pub fn header(&self) -> &Mldv2ReportRecordHeader {
134        self.header.deref()
135    }
136
137    /// Returns the multicast record's sources.
138    pub fn sources(&self) -> &[Ipv6Addr] {
139        self.sources.deref()
140    }
141}
142
143/// An implementation of MLDv2 report's records parsing.
144#[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        // every record may have aux_data_len 32-bit words at the end.
166        // we need to update our buffer view to reflect that.
167        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/// The layout for an MLDv2 report message header, per [RFC 3810 section 5.2].
176///
177/// [RFC 3810 section 5.2]: https://www.rfc-editor.org/rfc/rfc3810#section-5.2
178#[repr(C)]
179#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
180pub struct Mldv2ReportHeader {
181    /// Initialized to zero by the sender; ignored by receivers.
182    _reserved: [u8; 2],
183    /// The number of multicast address records found in this message.
184    num_mcast_addr_records: U16,
185}
186
187impl Mldv2ReportHeader {
188    /// Create a `Mldv2ReportHeader`.
189    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    /// Returns the number of multicast address records found in this message.
196    pub fn num_mcast_addr_records(&self) -> u16 {
197        self.num_mcast_addr_records.get()
198    }
199}
200
201/// The on-wire structure for the body of an MLDv2 report message, per
202/// [RFC 3910 section 5.2].
203///
204/// [RFC 3810 section 5.2]: https://www.rfc-editor.org/rfc/rfc3810#section-5.2
205#[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    /// Returns the header.
213    pub fn header(&self) -> &Mldv2ReportHeader {
214        self.header.deref()
215    }
216
217    /// Returns an iterator over the multicast address records.
218    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        // We know this is a V2 Report message and that it must have a variable sized body, it's
235        // therefore safe to unwrap.
236        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/// Multicast Listener Report V2 Message.
245#[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/// Multicast Listener Query V1 Message.
258#[repr(C)]
259#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
260pub struct MulticastListenerQuery;
261
262/// Multicast Listener Report V1 Message.
263#[repr(C)]
264#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
265pub struct MulticastListenerReport;
266
267/// Multicast Listener Done V1 Message.
268#[repr(C)]
269#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
270pub struct MulticastListenerDone;
271
272/// The trait for all MLDv1 Messages.
273pub trait Mldv1MessageType {
274    /// The type used to represent maximum response delay.
275    ///
276    /// It should be `()` for Report and Done messages,
277    /// and be `Mldv1ResponseDelay` for Query messages.
278    type MaxRespDelay: MaxCode<U16> + Debug + Copy;
279    /// The type used to represent the group_addr in the message.
280    ///
281    /// For Query Messages, it is just `Ipv6Addr` because
282    /// general queries will have this field to be zero, which
283    /// is not a multicast address, for Report and Done messages,
284    /// this should be `MulticastAddr<Ipv6Addr>`.
285    type GroupAddr: Into<Ipv6Addr> + Debug + Copy;
286}
287
288/// The trait for all ICMPv6 messages holding MLDv1 messages.
289pub trait IcmpMldv1MessageType:
290    Mldv1MessageType + IcmpMessage<Ipv6, Code = IcmpSenderZeroCode>
291{
292}
293
294/// The trait for MLD codes that can be further interpreted using different methods e.g. QQIC.
295///
296/// The type implementing this trait should be able
297/// to convert itself from/to `T`
298pub trait MaxCode<T: Default + Debug + FromBytes + IntoBytes> {
299    /// Convert to `T`
300    #[allow(clippy::wrong_self_convention)]
301    fn as_code(self) -> T;
302
303    /// Convert from `T`
304    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/// Maximum Response Delay used in Query messages.
316#[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/// The layout for an MLDv1 message body.
343#[repr(C)]
344#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
345pub struct Mldv1Message {
346    /// Max Response Delay, in units of milliseconds.
347    pub max_response_delay: U16,
348    /// Initialized to zero by the sender; ignored by receivers.
349    _reserved: U16,
350    /// In a Query message, the Multicast Address field is set to zero when
351    /// sending a General Query, and set to a specific IPv6 multicast address
352    /// when sending a Multicast-Address-Specific Query.
353    ///
354    /// In a Report or Done message, the Multicast Address field holds a
355    /// specific IPv6 multicast address to which the message sender is
356    /// listening or is ceasing to listen, respectively.
357    pub group_addr: Ipv6Addr,
358}
359
360impl Mldv1Message {
361    /// Gets the response delay value.
362    pub fn max_response_delay(&self) -> Duration {
363        Mldv1ResponseDelay(self.max_response_delay.get()).into()
364    }
365}
366
367/// The on-wire structure for the body of an MLDv1 message.
368#[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/// The builder for MLDv1 Messages.
412#[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    /// Create an `Mldv1MessageBuilder` without a `max_resp_delay`
420    /// for Report and Done messages.
421    pub fn new(group_addr: M::GroupAddr) -> Self {
422        Mldv1MessageBuilder { max_resp_delay: (), group_addr }
423    }
424}
425
426impl<M: Mldv1MessageType> Mldv1MessageBuilder<M> {
427    /// Create an `Mldv1MessageBuilder` with a `max_resp_delay`
428    /// for Query messages.
429    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/// The builder for MLDv2 Query Messages.
460#[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    /// Creates a new [`Mldv2QueryMessageBuilder`].
472    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 contains 4 reserved bits, the s_flag and the qrv,
519        // see https://datatracker.ietf.org/doc/html/rfc3810#section-5.1.
520        *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/// The builder for MLDv2 Report Messages.
532#[derive(Debug)]
533pub struct Mldv2ReportMessageBuilder<I> {
534    groups: I,
535}
536
537impl<I> Mldv2ReportMessageBuilder<I> {
538    /// Creates a new [`Mldv2ReportMessageBuilder`].
539    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    /// Transform this builder into an iterator of builders with a given
549    /// `max_len` for each generated packet.
550    ///
551    /// `max_len` is the maximum length each builder yielded by the returned
552    /// iterator can have. The groups used to create this builder are split into
553    /// multiple reports in order to meet this length. Note that this length
554    /// does _not_ account for the IP *or* the shared ICMP header.
555    ///
556    /// Returns `Err` if `max_len` is not large enough to meet minimal
557    /// constraints for each report.
558    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/// Maximum Response Delay used in Queryv2 messages, defined in [RFC 3810
628/// section 5.1.3].
629///
630/// [RFC 3810 section 5.1.3]:
631///     https://datatracker.ietf.org/doc/html/rfc3810#section-5.1.3
632#[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
661/// QRV (Querier's Robustness Variable) used in Queryv2 messages, defined in
662/// [RFC 3810 section 5.1.8].
663///
664/// Aliased to shared GMP implementation for convenience.
665///
666/// [RFC 3810 section 5.1.8]:
667///     https://datatracker.ietf.org/doc/html/rfc3810#section-5.1.8
668pub type Mldv2QRV = crate::gmp::QRV;
669
670/// QQIC (Querier's Query Interval Code) used in Queryv2 messages, defined in
671/// [RFC 3810 section 5.1.9].
672///
673/// Aliased to shared GMP implementation for convenience.
674///
675/// [RFC 3810 section 5.1.9]:
676///     https://datatracker.ietf.org/doc/html/rfc3810#section-5.1.9
677pub 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/// The layout for an MLDv2 Query message header.
690///
691/// It tracks the fixed part of the message as defined in [RFC 3810 section 5.1]:
692///
693///    | ...                                                           |
694///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
695///    |    Maximum Response Code      |           Reserved            |
696///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
697///    |                                                               |
698///    *                                                               *
699///    |                                                               |
700///    *                       Multicast Address                       *
701///    |                                                               |
702///    *                                                               *
703///    |                                                               |
704///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
705///    | Resv  |S| QRV |     QQIC      |     Number of Sources (N)     |
706///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
707///    | ...                                                           |
708///
709/// [RFC 3810 section 5.1]: https://datatracker.ietf.org/doc/html/rfc3810#section-5.1
710#[repr(C)]
711#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
712pub struct Mldv2QueryMessageHeader {
713    /// Max Response Code
714    max_response_code: U16,
715    /// Initialized to zero by the sender; ignored by receivers.
716    _reserved: U16,
717    /// In a Query message, the Multicast Address field is set to zero when
718    /// sending a General Query, and set to a specific IPv6 multicast address
719    /// when sending a Multicast-Address-Specific Query.
720    group_addr: Ipv6Addr,
721
722    /// Tracks 4 reserved bits, the s_flag defined in [RFC 3810 section 5.1.7]
723    /// and the qrv defined in [RFC 3810 section 5.1.8].
724    ///
725    /// [RFC 3810 section 5.1.7]:
726    ///     https://datatracker.ietf.org/doc/html/rfc3810#section-5.1.7
727    /// [RFC 3810 section 5.1.8]:
728    ///     https://datatracker.ietf.org/doc/html/rfc3810#section-5.1.8
729    sqrv: u8,
730    /// Querier's Query Interval Code.
731    qqic: u8,
732    /// Number of Sources i.e. how many source addresses are present in the
733    /// Query.
734    number_of_sources: U16,
735}
736
737impl Mldv2QueryMessageHeader {
738    const S_FLAG_MASK: u8 = (1 << 3);
739    const QRV_MASK: u8 = 0x07;
740
741    /// Gets the response delay for this query.
742    pub fn max_response_delay(&self) -> Mldv2ResponseDelay {
743        Mldv2ResponseDelay(self.max_response_code.get())
744    }
745
746    /// Returns the query's group address.
747    pub fn group_address(&self) -> Ipv6Addr {
748        self.group_addr
749    }
750
751    /// Returns the number of sources.
752    pub fn number_of_sources(&self) -> u16 {
753        self.number_of_sources.get()
754    }
755
756    /// Returns the S Flag (Suppress Router-Side Processing).
757    pub fn suppress_router_side_processing(&self) -> bool {
758        (self.sqrv & Self::S_FLAG_MASK) != 0
759    }
760
761    /// Returns the Querier's Robustness Variable.
762    pub fn querier_robustness_variable(&self) -> u8 {
763        self.sqrv & Self::QRV_MASK
764    }
765
766    /// Returns the Querier's Query Interval Code.
767    pub fn querier_query_interval(&self) -> Duration {
768        Mldv2QQIC::from(self.qqic).into()
769    }
770}
771
772/// The on-wire structure for the body of an MLDv2 report message, per
773/// [RFC 3910 section 5.1].
774///
775/// [RFC 3810 section 5.1]: https://www.rfc-editor.org/rfc/rfc3810#section-5.1
776#[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    /// Returns the header.
784    pub fn header(&self) -> &Mldv2QueryMessageHeader {
785        self.header.deref()
786    }
787
788    /// Returns the sources.
789    pub fn sources(&self) -> &[Ipv6Addr] {
790        self.sources.deref()
791    }
792
793    /// Reinterprets this [`Mldv2QueryBody`] message as an
794    /// [`Mldv1Body`] message in an MLDv1 query.
795    ///
796    /// Given this crate parses the version separately, users desiring to
797    /// operate in MLDv1 mode *SHOULD* reinterpret V2 queries as the
798    /// older version.
799    ///
800    /// See [RFC 3810 section 8.2.1] and [RFC 2236 section 2.5].
801    ///
802    /// [RFC 3810 section 8.2.1]:
803    ///     https://datatracker.ietf.org/doc/html/rfc3810#section-8.2.1
804    /// [RFC 2710 section 3.7]:
805    ///     https://datatracker.ietf.org/doc/html/rfc2710#section-3.7
806    pub fn as_v1_query(&self) -> Mldv1Body<&[u8]> {
807        let Self { header, sources: _ } = self;
808        // This unwrap is okay because we know Mldv1Message is effectively the
809        // prefix within Mldv2QueryBody.
810        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        // We know this is a V2 Query message and that it must have a variable sized body, it's
827        // therefore safe to unwrap.
828        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/// Multicast Query V2 Message.
837#[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        // Serialize an MLD(ICMPv6) packet using the builder.
933        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        //Serialize an MLD(ICMPv6) packet using the builder.
963
964        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        // When interpreted as a v1 body we should get valid results.
1027        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 that our maximum sources accounting matches an equivalent example in
1279    // RFC 3810 section 5.1.10:
1280    //
1281    //  For example, on an Ethernet link with an MTU of 1500 octets, the IPv6
1282    //  header (40 octets) together with the Hop-By-Hop Extension Header (8
1283    //  octets) that includes the Router Alert option consume 48 octets; the MLD
1284    //  fields up to the Number of Sources (N) field consume 28 octets; thus,
1285    //  there are 1424 octets left for source addresses, which limits the number
1286    //  of source addresses to 89 (1424/16)
1287    //
1288    // This example is for queries, but reports have the same prefix length so
1289    // we can use the same numbers.
1290    #[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        // We can generate a report at exactly ETH_MTU.
1338        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        // Only one report is generated.
1359        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        // 2 reports are generated with one extra source.
1372        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    // Like report_v2_split_many_sources but we calculate how many groups with
1381    // no sources specified we can have in the same 1500 Ethernet MTU.
1382    //
1383    // * 48 bytes for IPv6 header + Hop-by-Hop router alert.
1384    // * 8 bytes for ICMPv6 header + report up to number of groups.
1385    // * 20 bytes per group with no sources.
1386    //
1387    // So we should be able to fit (1500 - 48 - 8)/20 = 72.2 groups. 72
1388    // groups result in a a 1496 byte-long message.
1389    #[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        // We can generate a report at exactly ETH_MTU.
1434        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        // Only one report is generated.
1450        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        // 2 reports are generated with one extra group.
1458        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        // Linear code:duration mapping
1464        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        // Linear code:duration mapping
1525        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}