Skip to main content

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