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        if *ctx == 0 {
160            return Ok(ParsedRecord::Done);
161        }
162        let header = data
163            .take_obj_front::<Mldv2ReportRecordHeader>()
164            .ok_or_else(debug_err_fn!(ParseError::Format, "Can't take multicast record header"))?;
165        let sources = data
166            .take_slice_front::<Ipv6Addr>(header.number_of_sources().into())
167            .ok_or_else(debug_err_fn!(ParseError::Format, "Can't take multicast record sources"))?;
168        // every record may have aux_data_len 32-bit words at the end.
169        // we need to update our buffer view to reflect that.
170        let _ = data
171            .take_front(usize::from(header.aux_data_len) * 4)
172            .ok_or_else(debug_err_fn!(ParseError::Format, "Can't skip auxiliary data"))?;
173
174        *ctx -= 1;
175        Ok(ParsedRecord::Parsed(Self::Record { header, sources }))
176    }
177}
178
179/// The layout for an MLDv2 report message header, per [RFC 3810 section 5.2].
180///
181/// [RFC 3810 section 5.2]: https://www.rfc-editor.org/rfc/rfc3810#section-5.2
182#[repr(C)]
183#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
184pub struct Mldv2ReportHeader {
185    /// Initialized to zero by the sender; ignored by receivers.
186    _reserved: [u8; 2],
187    /// The number of multicast address records found in this message.
188    num_mcast_addr_records: U16,
189}
190
191impl Mldv2ReportHeader {
192    /// Create a `Mldv2ReportHeader`.
193    pub fn new(num_mcast_addr_records: u16) -> Self {
194        Mldv2ReportHeader {
195            _reserved: [0, 0],
196            num_mcast_addr_records: U16::from(num_mcast_addr_records),
197        }
198    }
199    /// Returns the number of multicast address records found in this message.
200    pub fn num_mcast_addr_records(&self) -> u16 {
201        self.num_mcast_addr_records.get()
202    }
203}
204
205/// The on-wire structure for the body of an MLDv2 report message, per
206/// [RFC 3910 section 5.2].
207///
208/// [RFC 3810 section 5.2]: https://www.rfc-editor.org/rfc/rfc3810#section-5.2
209#[derive(Debug)]
210pub struct Mldv2ReportBody<B: SplitByteSlice> {
211    header: Ref<B, Mldv2ReportHeader>,
212    records: Records<B, Mldv2ReportRecords>,
213}
214
215impl<B: SplitByteSlice> Mldv2ReportBody<B> {
216    /// Returns the header.
217    pub fn header(&self) -> &Mldv2ReportHeader {
218        self.header.deref()
219    }
220
221    /// Returns an iterator over the multicast address records.
222    pub fn iter_multicast_records(&self) -> impl Iterator<Item = MulticastRecord<&'_ [u8]>> {
223        self.records.iter()
224    }
225}
226
227impl<B: SplitByteSlice> MessageBody for Mldv2ReportBody<B> {
228    type B = B;
229    fn parse(bytes: B) -> ParseResult<Self> {
230        let (header, bytes) =
231            Ref::<_, Mldv2ReportHeader>::from_prefix(bytes).map_err(|_| ParseError::Format)?;
232        let records = Records::parse_with_context(bytes, header.num_mcast_addr_records().into())?;
233        Ok(Mldv2ReportBody { header, records })
234    }
235
236    fn len(&self) -> usize {
237        let (inner_header, inner_body) = self.bytes();
238        // We know this is a V2 Report message and that it must have a variable sized body, it's
239        // therefore safe to unwrap.
240        inner_header.len() + inner_body.unwrap().len()
241    }
242
243    fn bytes(&self) -> (&[u8], Option<&[u8]>) {
244        (Ref::bytes(&self.header), Some(self.records.bytes()))
245    }
246}
247
248/// Multicast Listener Report V2 Message.
249#[repr(C)]
250#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
251pub struct MulticastListenerReportV2;
252
253impl_icmp_message!(
254    Ipv6,
255    MulticastListenerReportV2,
256    MulticastListenerReportV2,
257    IcmpSenderZeroCode,
258    Mldv2ReportBody<B>
259);
260
261/// Multicast Listener Query V1 Message.
262#[repr(C)]
263#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
264pub struct MulticastListenerQuery;
265
266/// Multicast Listener Report V1 Message.
267#[repr(C)]
268#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
269pub struct MulticastListenerReport;
270
271/// Multicast Listener Done V1 Message.
272#[repr(C)]
273#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
274pub struct MulticastListenerDone;
275
276/// The trait for all MLDv1 Messages.
277pub trait Mldv1MessageType {
278    /// The type used to represent maximum response delay.
279    ///
280    /// It should be `()` for Report and Done messages,
281    /// and be `Mldv1ResponseDelay` for Query messages.
282    type MaxRespDelay: MaxCode<U16> + Debug + Copy;
283    /// The type used to represent the group_addr in the message.
284    ///
285    /// For Query Messages, it is just `Ipv6Addr` because
286    /// general queries will have this field to be zero, which
287    /// is not a multicast address, for Report and Done messages,
288    /// this should be `MulticastAddr<Ipv6Addr>`.
289    type GroupAddr: Into<Ipv6Addr> + Debug + Copy;
290}
291
292/// The trait for all ICMPv6 messages holding MLDv1 messages.
293pub trait IcmpMldv1MessageType:
294    Mldv1MessageType + IcmpMessage<Ipv6, Code = IcmpSenderZeroCode>
295{
296}
297
298/// The trait for MLD codes that can be further interpreted using different methods e.g. QQIC.
299///
300/// The type implementing this trait should be able
301/// to convert itself from/to `T`
302pub trait MaxCode<T: Default + Debug + FromBytes + IntoBytes> {
303    /// Convert to `T`
304    #[allow(clippy::wrong_self_convention)]
305    fn as_code(self) -> T;
306
307    /// Convert from `T`
308    fn from_code(code: T) -> Self;
309}
310
311impl<T: Default + Debug + FromBytes + IntoBytes> MaxCode<T> for () {
312    fn as_code(self) -> T {
313        T::default()
314    }
315
316    fn from_code(_: T) -> Self {}
317}
318
319/// Maximum Response Delay used in Query messages.
320#[derive(PartialEq, Eq, Debug, Clone, Copy)]
321pub struct Mldv1ResponseDelay(u16);
322
323impl MaxCode<U16> for Mldv1ResponseDelay {
324    fn as_code(self) -> U16 {
325        U16::new(self.0)
326    }
327
328    fn from_code(code: U16) -> Self {
329        Mldv1ResponseDelay(code.get())
330    }
331}
332
333impl From<Mldv1ResponseDelay> for Duration {
334    fn from(code: Mldv1ResponseDelay) -> Self {
335        Duration::from_millis(code.0.into())
336    }
337}
338
339impl TryFrom<Duration> for Mldv1ResponseDelay {
340    type Error = OverflowError;
341    fn try_from(period: Duration) -> Result<Self, Self::Error> {
342        Ok(Mldv1ResponseDelay(u16::try_from(period.as_millis()).map_err(|_| OverflowError)?))
343    }
344}
345
346/// The layout for an MLDv1 message body.
347#[repr(C)]
348#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
349pub struct Mldv1Message {
350    /// Max Response Delay, in units of milliseconds.
351    pub max_response_delay: U16,
352    /// Initialized to zero by the sender; ignored by receivers.
353    _reserved: U16,
354    /// In a Query message, the Multicast Address field is set to zero when
355    /// sending a General Query, and set to a specific IPv6 multicast address
356    /// when sending a Multicast-Address-Specific Query.
357    ///
358    /// In a Report or Done message, the Multicast Address field holds a
359    /// specific IPv6 multicast address to which the message sender is
360    /// listening or is ceasing to listen, respectively.
361    pub group_addr: Ipv6Addr,
362}
363
364impl Mldv1Message {
365    /// Gets the response delay value.
366    pub fn max_response_delay(&self) -> Duration {
367        Mldv1ResponseDelay(self.max_response_delay.get()).into()
368    }
369}
370
371/// The on-wire structure for the body of an MLDv1 message.
372#[derive(Debug)]
373pub struct Mldv1Body<B: SplitByteSlice>(Ref<B, Mldv1Message>);
374
375impl<B: SplitByteSlice> Deref for Mldv1Body<B> {
376    type Target = Mldv1Message;
377
378    fn deref(&self) -> &Self::Target {
379        &*self.0
380    }
381}
382
383impl<B: SplitByteSlice> MessageBody for Mldv1Body<B> {
384    type B = B;
385    fn parse(bytes: B) -> ParseResult<Self> {
386        Ref::from_bytes(bytes).map_or(Err(ParseError::Format), |body| Ok(Mldv1Body(body)))
387    }
388
389    fn len(&self) -> usize {
390        let (inner_header, _inner_body) = self.bytes();
391        debug_assert!(_inner_body.is_none());
392        inner_header.len()
393    }
394
395    fn bytes(&self) -> (&[u8], Option<&[u8]>) {
396        (Ref::bytes(&self.0), None)
397    }
398}
399
400macro_rules! impl_mldv1_message {
401    ($msg:ident, $resp_code:ty, $group_addr:ty) => {
402        impl_icmp_message!(Ipv6, $msg, $msg, IcmpSenderZeroCode, Mldv1Body<B>);
403        impl Mldv1MessageType for $msg {
404            type MaxRespDelay = $resp_code;
405            type GroupAddr = $group_addr;
406        }
407        impl IcmpMldv1MessageType for $msg {}
408    };
409}
410
411impl_mldv1_message!(MulticastListenerQuery, Mldv1ResponseDelay, Ipv6Addr);
412impl_mldv1_message!(MulticastListenerReport, (), MulticastAddr<Ipv6Addr>);
413impl_mldv1_message!(MulticastListenerDone, (), MulticastAddr<Ipv6Addr>);
414
415/// The builder for MLDv1 Messages.
416#[derive(Debug)]
417pub struct Mldv1MessageBuilder<M: Mldv1MessageType> {
418    max_resp_delay: M::MaxRespDelay,
419    group_addr: M::GroupAddr,
420}
421
422impl<M: Mldv1MessageType<MaxRespDelay = ()>> Mldv1MessageBuilder<M> {
423    /// Create an `Mldv1MessageBuilder` without a `max_resp_delay`
424    /// for Report and Done messages.
425    pub fn new(group_addr: M::GroupAddr) -> Self {
426        Mldv1MessageBuilder { max_resp_delay: (), group_addr }
427    }
428}
429
430impl<M: Mldv1MessageType> Mldv1MessageBuilder<M> {
431    /// Create an `Mldv1MessageBuilder` with a `max_resp_delay`
432    /// for Query messages.
433    pub fn new_with_max_resp_delay(
434        group_addr: M::GroupAddr,
435        max_resp_delay: M::MaxRespDelay,
436    ) -> Self {
437        Mldv1MessageBuilder { max_resp_delay, group_addr }
438    }
439
440    fn serialize_message(&self, mut buf: &mut [u8]) {
441        use packet::BufferViewMut;
442        let mut bytes = &mut buf;
443        bytes
444            .write_obj_front(&Mldv1Message {
445                max_response_delay: self.max_resp_delay.as_code(),
446                _reserved: U16::ZERO,
447                group_addr: self.group_addr.into(),
448            })
449            .expect("too few bytes for MLDv1 message");
450    }
451}
452
453impl<M: Mldv1MessageType> InnerPacketBuilder for Mldv1MessageBuilder<M> {
454    fn bytes_len(&self) -> usize {
455        size_of::<Mldv1Message>()
456    }
457
458    fn serialize(&self, buf: &mut [u8]) {
459        self.serialize_message(buf);
460    }
461}
462
463/// The builder for MLDv2 Query Messages.
464#[derive(Debug)]
465pub struct Mldv2QueryMessageBuilder<I> {
466    max_response_delay: Mldv2ResponseDelay,
467    group_addr: Option<MulticastAddr<Ipv6Addr>>,
468    s_flag: bool,
469    qrv: Mldv2QRV,
470    qqic: Mldv2QQIC,
471    sources: I,
472}
473
474impl<I> Mldv2QueryMessageBuilder<I> {
475    /// Creates a new [`Mldv2QueryMessageBuilder`].
476    pub fn new(
477        max_response_delay: Mldv2ResponseDelay,
478        group_addr: Option<MulticastAddr<Ipv6Addr>>,
479        s_flag: bool,
480        qrv: Mldv2QRV,
481        qqic: Mldv2QQIC,
482        sources: I,
483    ) -> Self {
484        Self { max_response_delay, group_addr, s_flag, qrv, qqic, sources }
485    }
486}
487
488impl<I> InnerPacketBuilder for Mldv2QueryMessageBuilder<I>
489where
490    I: Iterator<Item: Borrow<Ipv6Addr>> + Clone,
491{
492    fn bytes_len(&self) -> usize {
493        core::mem::size_of::<Mldv2QueryMessageHeader>()
494            + self.sources.clone().count() * core::mem::size_of::<Ipv6Addr>()
495    }
496
497    fn serialize(&self, mut buf: &mut [u8]) {
498        use packet::BufferViewMut;
499        let mut bytes = &mut buf;
500        let mut header = bytes
501            .take_obj_front_zero::<Mldv2QueryMessageHeader>()
502            .expect("too few bytes for header");
503        let Mldv2QueryMessageHeader {
504            max_response_code,
505            _reserved,
506            group_addr,
507            sqrv,
508            qqic,
509            number_of_sources,
510        } = &mut *header;
511        let Self {
512            max_response_delay,
513            group_addr: wr_group_addr,
514            s_flag,
515            qrv,
516            qqic: wr_qqic,
517            sources,
518        } = self;
519        *max_response_code = max_response_delay.as_code();
520        *group_addr =
521            wr_group_addr.as_ref().map(|addr| addr.get()).unwrap_or(Ipv6::UNSPECIFIED_ADDRESS);
522        // sqrv contains 4 reserved bits, the s_flag and the qrv,
523        // see https://datatracker.ietf.org/doc/html/rfc3810#section-5.1.
524        *sqrv = (u8::from(*s_flag) << 3) | (Mldv2QueryMessageHeader::QRV_MASK & u8::from(*qrv));
525        *qqic = wr_qqic.as_code();
526        let mut count: u16 = 0;
527        for src in sources.clone() {
528            count = count.checked_add(1).expect("overflowed number of sources");
529            bytes.write_obj_front(src.borrow()).expect("too few bytes for source");
530        }
531        *number_of_sources = count.into();
532    }
533}
534
535/// The builder for MLDv2 Report Messages.
536#[derive(Debug)]
537pub struct Mldv2ReportMessageBuilder<I> {
538    groups: I,
539}
540
541impl<I> Mldv2ReportMessageBuilder<I> {
542    /// Creates a new [`Mldv2ReportMessageBuilder`].
543    pub fn new(groups: I) -> Self {
544        Self { groups }
545    }
546}
547
548impl<I> Mldv2ReportMessageBuilder<I>
549where
550    I: Iterator<Item: GmpReportGroupRecord<Ipv6Addr> + Clone> + Clone,
551{
552    /// Transform this builder into an iterator of builders with a given
553    /// `max_len` for each generated packet.
554    ///
555    /// `max_len` is the maximum length each builder yielded by the returned
556    /// iterator can have. The groups used to create this builder are split into
557    /// multiple reports in order to meet this length. Note that this length
558    /// does _not_ account for the IP *or* the shared ICMP header.
559    ///
560    /// Returns `Err` if `max_len` is not large enough to meet minimal
561    /// constraints for each report.
562    pub fn with_len_limits(
563        self,
564        max_len: usize,
565    ) -> Result<
566        impl Iterator<
567            Item = Mldv2ReportMessageBuilder<
568                impl Iterator<Item: GmpReportGroupRecord<Ipv6Addr>> + Clone,
569            >,
570        >,
571        InvalidConstraintsError,
572    > {
573        let Self { groups } = self;
574        crate::gmp::group_record_split_iterator(
575            max_len.saturating_sub(core::mem::size_of::<Mldv2ReportHeader>()),
576            core::mem::size_of::<Mldv2ReportRecordHeader>(),
577            groups,
578        )
579        .map(|iter| iter.map(|groups| Mldv2ReportMessageBuilder { groups }))
580    }
581}
582
583impl<I> InnerPacketBuilder for Mldv2ReportMessageBuilder<I>
584where
585    I: Iterator<Item: GmpReportGroupRecord<Ipv6Addr>> + Clone,
586{
587    fn bytes_len(&self) -> usize {
588        core::mem::size_of::<Mldv2ReportHeader>()
589            + self
590                .groups
591                .clone()
592                .map(|g| {
593                    core::mem::size_of::<Mldv2ReportRecordHeader>()
594                        + g.sources().count() * core::mem::size_of::<Ipv6Addr>()
595                })
596                .sum::<usize>()
597    }
598
599    fn serialize(&self, mut buf: &mut [u8]) {
600        use packet::BufferViewMut;
601        let mut bytes = &mut buf;
602        let mut header =
603            bytes.take_obj_front_zero::<Mldv2ReportHeader>().expect("too few bytes for header");
604        let Mldv2ReportHeader { _reserved, num_mcast_addr_records } = &mut *header;
605        let mut mcast_count: u16 = 0;
606        for group in self.groups.clone() {
607            mcast_count = mcast_count.checked_add(1).expect("multicast groups count overflows");
608            let mut header = bytes
609                .take_obj_front_zero::<Mldv2ReportRecordHeader>()
610                .expect("too few bytes for record header");
611            let Mldv2ReportRecordHeader {
612                record_type,
613                aux_data_len,
614                number_of_sources,
615                multicast_address,
616            } = &mut *header;
617            *record_type = group.record_type().into();
618            *aux_data_len = 0;
619            *multicast_address = group.group().into();
620            let mut source_count: u16 = 0;
621            for src in group.sources() {
622                source_count = source_count.checked_add(1).expect("sources count overflows");
623                bytes.write_obj_front(src.borrow()).expect("too few bytes for source");
624            }
625            *number_of_sources = source_count.into();
626        }
627        *num_mcast_addr_records = mcast_count.into();
628    }
629}
630
631/// Maximum Response Delay used in Queryv2 messages, defined in [RFC 3810
632/// section 5.1.3].
633///
634/// [RFC 3810 section 5.1.3]:
635///     https://datatracker.ietf.org/doc/html/rfc3810#section-5.1.3
636#[derive(PartialEq, Eq, Debug, Clone, Copy, Default)]
637pub struct Mldv2ResponseDelay(u16);
638
639impl LinExpConversion<Duration> for Mldv2ResponseDelay {
640    const NUM_MANT_BITS: u8 = 12;
641    const NUM_EXP_BITS: u8 = 3;
642
643    fn lossy_try_from(value: Duration) -> Result<Self, OverflowError> {
644        let millis: u32 = value.as_millis().try_into().map_err(|_| OverflowError)?;
645        Self::lossy_try_from_expanded(millis).map(Self)
646    }
647}
648
649impl MaxCode<U16> for Mldv2ResponseDelay {
650    fn as_code(self) -> U16 {
651        U16::new(self.0)
652    }
653
654    fn from_code(code: U16) -> Self {
655        Mldv2ResponseDelay(code.get())
656    }
657}
658
659impl From<Mldv2ResponseDelay> for Duration {
660    fn from(code: Mldv2ResponseDelay) -> Self {
661        Duration::from_millis(Mldv2ResponseDelay::to_expanded(code.0).into())
662    }
663}
664
665/// QRV (Querier's Robustness Variable) used in Queryv2 messages, defined in
666/// [RFC 3810 section 5.1.8].
667///
668/// Aliased to shared GMP implementation for convenience.
669///
670/// [RFC 3810 section 5.1.8]:
671///     https://datatracker.ietf.org/doc/html/rfc3810#section-5.1.8
672pub type Mldv2QRV = crate::gmp::QRV;
673
674/// QQIC (Querier's Query Interval Code) used in Queryv2 messages, defined in
675/// [RFC 3810 section 5.1.9].
676///
677/// Aliased to shared GMP implementation for convenience.
678///
679/// [RFC 3810 section 5.1.9]:
680///     https://datatracker.ietf.org/doc/html/rfc3810#section-5.1.9
681pub type Mldv2QQIC = crate::gmp::QQIC;
682
683impl MaxCode<u8> for Mldv2QQIC {
684    fn as_code(self) -> u8 {
685        self.into()
686    }
687
688    fn from_code(code: u8) -> Self {
689        code.into()
690    }
691}
692
693/// The layout for an MLDv2 Query message header.
694///
695/// It tracks the fixed part of the message as defined in [RFC 3810 section 5.1]:
696///
697///    | ...                                                           |
698///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
699///    |    Maximum Response Code      |           Reserved            |
700///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
701///    |                                                               |
702///    *                                                               *
703///    |                                                               |
704///    *                       Multicast Address                       *
705///    |                                                               |
706///    *                                                               *
707///    |                                                               |
708///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
709///    | Resv  |S| QRV |     QQIC      |     Number of Sources (N)     |
710///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
711///    | ...                                                           |
712///
713/// [RFC 3810 section 5.1]: https://datatracker.ietf.org/doc/html/rfc3810#section-5.1
714#[repr(C)]
715#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
716pub struct Mldv2QueryMessageHeader {
717    /// Max Response Code
718    max_response_code: U16,
719    /// Initialized to zero by the sender; ignored by receivers.
720    _reserved: U16,
721    /// In a Query message, the Multicast Address field is set to zero when
722    /// sending a General Query, and set to a specific IPv6 multicast address
723    /// when sending a Multicast-Address-Specific Query.
724    group_addr: Ipv6Addr,
725
726    /// Tracks 4 reserved bits, the s_flag defined in [RFC 3810 section 5.1.7]
727    /// and the qrv defined in [RFC 3810 section 5.1.8].
728    ///
729    /// [RFC 3810 section 5.1.7]:
730    ///     https://datatracker.ietf.org/doc/html/rfc3810#section-5.1.7
731    /// [RFC 3810 section 5.1.8]:
732    ///     https://datatracker.ietf.org/doc/html/rfc3810#section-5.1.8
733    sqrv: u8,
734    /// Querier's Query Interval Code.
735    qqic: u8,
736    /// Number of Sources i.e. how many source addresses are present in the
737    /// Query.
738    number_of_sources: U16,
739}
740
741impl Mldv2QueryMessageHeader {
742    const S_FLAG_MASK: u8 = (1 << 3);
743    const QRV_MASK: u8 = 0x07;
744
745    /// Gets the response delay for this query.
746    pub fn max_response_delay(&self) -> Mldv2ResponseDelay {
747        Mldv2ResponseDelay(self.max_response_code.get())
748    }
749
750    /// Returns the query's group address.
751    pub fn group_address(&self) -> Ipv6Addr {
752        self.group_addr
753    }
754
755    /// Returns the number of sources.
756    pub fn number_of_sources(&self) -> u16 {
757        self.number_of_sources.get()
758    }
759
760    /// Returns the S Flag (Suppress Router-Side Processing).
761    pub fn suppress_router_side_processing(&self) -> bool {
762        (self.sqrv & Self::S_FLAG_MASK) != 0
763    }
764
765    /// Returns the Querier's Robustness Variable.
766    pub fn querier_robustness_variable(&self) -> u8 {
767        self.sqrv & Self::QRV_MASK
768    }
769
770    /// Returns the Querier's Query Interval Code.
771    pub fn querier_query_interval(&self) -> Duration {
772        Mldv2QQIC::from(self.qqic).into()
773    }
774}
775
776/// The on-wire structure for the body of an MLDv2 report message, per
777/// [RFC 3910 section 5.1].
778///
779/// [RFC 3810 section 5.1]: https://www.rfc-editor.org/rfc/rfc3810#section-5.1
780#[derive(Debug)]
781pub struct Mldv2QueryBody<B: SplitByteSlice> {
782    header: Ref<B, Mldv2QueryMessageHeader>,
783    sources: Ref<B, [Ipv6Addr]>,
784}
785
786impl<B: SplitByteSlice> Mldv2QueryBody<B> {
787    /// Returns the header.
788    pub fn header(&self) -> &Mldv2QueryMessageHeader {
789        self.header.deref()
790    }
791
792    /// Returns the sources.
793    pub fn sources(&self) -> &[Ipv6Addr] {
794        self.sources.deref()
795    }
796
797    /// Reinterprets this [`Mldv2QueryBody`] message as an
798    /// [`Mldv1Body`] message in an MLDv1 query.
799    ///
800    /// Given this crate parses the version separately, users desiring to
801    /// operate in MLDv1 mode *SHOULD* reinterpret V2 queries as the
802    /// older version.
803    ///
804    /// See [RFC 3810 section 8.2.1] and [RFC 2236 section 2.5].
805    ///
806    /// [RFC 3810 section 8.2.1]:
807    ///     https://datatracker.ietf.org/doc/html/rfc3810#section-8.2.1
808    /// [RFC 2710 section 3.7]:
809    ///     https://datatracker.ietf.org/doc/html/rfc2710#section-3.7
810    pub fn as_v1_query(&self) -> Mldv1Body<&[u8]> {
811        let Self { header, sources: _ } = self;
812        // This unwrap is okay because we know Mldv1Message is effectively the
813        // prefix within Mldv2QueryBody.
814        let (msg, _rest) = Ref::from_prefix(header.as_bytes()).unwrap();
815        Mldv1Body(msg)
816    }
817}
818
819impl<B: SplitByteSlice> MessageBody for Mldv2QueryBody<B> {
820    type B = B;
821    fn parse(bytes: B) -> ParseResult<Self> {
822        let (header, bytes) = Ref::<_, Mldv2QueryMessageHeader>::from_prefix(bytes)
823            .map_err(|_| ParseError::Format)?;
824        let num_sources_bytes =
825            usize::from(Ipv6Addr::BYTES) * usize::from(header.number_of_sources());
826        // Read exactly `number_of_sources` addresses from the body. Any
827        // trailing bytes MUST be ignored per [RFC 3810 section 5.1.12].
828        //
829        // [RFC 3810 section 5.1.12]:
830        //     https://datatracker.ietf.org/doc/html/rfc3810#section-5.1.12
831        let (sources_bytes, _rest) =
832            bytes.split_at(num_sources_bytes).map_err(|_| ParseError::Format)?;
833        let sources =
834            Ref::<B, [Ipv6Addr]>::from_bytes(sources_bytes).map_err(|_| ParseError::Format)?;
835        Ok(Mldv2QueryBody { header, sources })
836    }
837
838    fn len(&self) -> usize {
839        let (inner_header, inner_body) = self.bytes();
840        // We know this is a V2 Query message and that it must have a variable sized body, it's
841        // therefore safe to unwrap.
842        inner_header.len() + inner_body.unwrap().len()
843    }
844
845    fn bytes(&self) -> (&[u8], Option<&[u8]>) {
846        (Ref::bytes(&self.header), Some(Ref::bytes(&self.sources)))
847    }
848}
849
850/// Multicast Query V2 Message.
851#[repr(C)]
852#[derive(IntoBytes, KnownLayout, FromBytes, Immutable, Unaligned, Copy, Clone, Debug)]
853pub struct MulticastListenerQueryV2;
854
855impl_icmp_message!(
856    Ipv6,
857    MulticastListenerQueryV2,
858    MulticastListenerQuery,
859    IcmpSenderZeroCode,
860    Mldv2QueryBody<B>
861);
862
863#[cfg(test)]
864mod tests {
865    use packet::{NestableSerializer as _, NoOpSerializationContext, ParseBuffer, Serializer};
866    use test_case::test_case;
867
868    use super::*;
869    use crate::gmp::{ExactConversionError, GroupRecordType};
870    use crate::icmp::{IcmpPacketBuilder, IcmpParseArgs};
871    use crate::ip::Ipv6Proto;
872    use crate::ipv6::ext_hdrs::{
873        ExtensionHeaderOptionAction, HopByHopOption, HopByHopOptionData, Ipv6ExtensionHeader,
874    };
875    use crate::ipv6::{Ipv6Header, Ipv6Packet, Ipv6PacketBuilder, Ipv6PacketBuilderWithHbhOptions};
876    use net_declare::net_ip_v6;
877
878    fn serialize_to_bytes<B: SplitByteSlice + Debug, M: IcmpMessage<Ipv6> + Debug>(
879        src_ip: Ipv6Addr,
880        dst_ip: Ipv6Addr,
881        icmp: &IcmpPacket<Ipv6, B, M>,
882    ) -> Vec<u8> {
883        let ip = Ipv6PacketBuilder::new(src_ip, dst_ip, 1, Ipv6Proto::Icmpv6);
884        let with_options = Ipv6PacketBuilderWithHbhOptions::new(
885            ip,
886            &[HopByHopOption {
887                action: ExtensionHeaderOptionAction::SkipAndContinue,
888                mutable: false,
889                data: HopByHopOptionData::RouterAlert { data: 0 },
890            }],
891        )
892        .unwrap();
893        let (header, body) = icmp.message_body.bytes();
894        let body = if let Some(b) = body { b } else { &[] };
895        let complete_msg = &[header, body].concat();
896        complete_msg
897            .into_serializer()
898            .wrap_in(icmp.builder(src_ip, dst_ip))
899            .wrap_in(with_options)
900            .serialize_vec_outer(&mut NoOpSerializationContext)
901            .unwrap()
902            .as_ref()
903            .to_vec()
904    }
905
906    fn test_parse_and_serialize<
907        M: IcmpMessage<Ipv6> + Debug,
908        F: FnOnce(&Ipv6Packet<&[u8]>),
909        G: for<'a> FnOnce(&IcmpPacket<Ipv6, &'a [u8], M>),
910    >(
911        src_ip: Ipv6Addr,
912        dst_ip: Ipv6Addr,
913        mut req: &[u8],
914        check_ip: F,
915        check_icmp: G,
916    ) {
917        let orig_req = req;
918
919        let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
920        check_ip(&ip);
921        let icmp =
922            req.parse_with::<_, IcmpPacket<_, _, M>>(IcmpParseArgs::new(src_ip, dst_ip)).unwrap();
923        check_icmp(&icmp);
924
925        let data = serialize_to_bytes(src_ip, dst_ip, &icmp);
926        assert_eq!(&data[..], orig_req);
927    }
928
929    fn serialize_to_bytes_with_builder<M: IcmpMldv1MessageType + Debug>(
930        src_ip: Ipv6Addr,
931        dst_ip: Ipv6Addr,
932        msg: M,
933        group_addr: M::GroupAddr,
934        max_resp_delay: M::MaxRespDelay,
935    ) -> Vec<u8> {
936        let ip = Ipv6PacketBuilder::new(src_ip, dst_ip, 1, Ipv6Proto::Icmpv6);
937        let with_options = Ipv6PacketBuilderWithHbhOptions::new(
938            ip,
939            &[HopByHopOption {
940                action: ExtensionHeaderOptionAction::SkipAndContinue,
941                mutable: false,
942                data: HopByHopOptionData::RouterAlert { data: 0 },
943            }],
944        )
945        .unwrap();
946        // Serialize an MLD(ICMPv6) packet using the builder.
947        Mldv1MessageBuilder::<M>::new_with_max_resp_delay(group_addr, max_resp_delay)
948            .into_serializer()
949            .wrap_in(IcmpPacketBuilder::new(src_ip, dst_ip, IcmpSenderZeroCode, msg))
950            .wrap_in(with_options)
951            .serialize_vec_outer(&mut NoOpSerializationContext)
952            .unwrap()
953            .as_ref()
954            .to_vec()
955    }
956
957    fn serialize_to_bytes_with_builder_v2<
958        M: IcmpMessage<Ipv6, Code = IcmpSenderZeroCode> + Debug,
959        B: InnerPacketBuilder + Debug,
960    >(
961        src_ip: Ipv6Addr,
962        dst_ip: Ipv6Addr,
963        msg: M,
964        builder: B,
965    ) -> Vec<u8> {
966        let ip = Ipv6PacketBuilder::new(src_ip, dst_ip, 1, Ipv6Proto::Icmpv6);
967        let with_options = Ipv6PacketBuilderWithHbhOptions::new(
968            ip,
969            &[HopByHopOption {
970                action: ExtensionHeaderOptionAction::SkipAndContinue,
971                mutable: false,
972                data: HopByHopOptionData::RouterAlert { data: 0 },
973            }],
974        )
975        .unwrap();
976        //Serialize an MLD(ICMPv6) packet using the builder.
977
978        builder
979            .into_serializer()
980            .wrap_in(IcmpPacketBuilder::new(src_ip, dst_ip, IcmpSenderZeroCode, msg))
981            .wrap_in(with_options)
982            .serialize_vec_outer(&mut NoOpSerializationContext)
983            .unwrap()
984            .as_ref()
985            .to_vec()
986    }
987
988    fn check_ip<B: SplitByteSlice>(ip: &Ipv6Packet<B>, src_ip: Ipv6Addr, dst_ip: Ipv6Addr) {
989        assert_eq!(ip.src_ip(), src_ip);
990        assert_eq!(ip.dst_ip(), dst_ip);
991        assert_eq!(ip.iter_extension_hdrs().count(), 1);
992        let hbh = ip.iter_extension_hdrs().next().unwrap();
993        match hbh {
994            Ipv6ExtensionHeader::HopByHopOptions { options } => {
995                assert_eq!(options.iter().count(), 1);
996                assert_eq!(
997                    options.iter().next().unwrap(),
998                    HopByHopOption {
999                        action: ExtensionHeaderOptionAction::SkipAndContinue,
1000                        mutable: false,
1001                        data: HopByHopOptionData::RouterAlert { data: 0 },
1002                    }
1003                );
1004            }
1005            _ => panic!("Wrong extension header"),
1006        }
1007    }
1008
1009    fn check_mld_v1<
1010        B: SplitByteSlice,
1011        M: IcmpMessage<Ipv6, Body<B> = Mldv1Body<B>> + Mldv1MessageType + Debug,
1012    >(
1013        icmp: &IcmpPacket<Ipv6, B, M>,
1014        max_resp_code: u16,
1015        group_addr: Ipv6Addr,
1016    ) {
1017        assert_eq!(icmp.message_body._reserved.get(), 0);
1018        assert_eq!(icmp.message_body.max_response_delay.get(), max_resp_code);
1019        assert_eq!(icmp.message_body.group_addr, group_addr);
1020    }
1021
1022    fn check_mld_query_v2<
1023        'a,
1024        B: SplitByteSlice,
1025        M: IcmpMessage<Ipv6, Body<B> = Mldv2QueryBody<B>> + Debug,
1026    >(
1027        icmp: &IcmpPacket<Ipv6, B, M>,
1028        max_resp_code: u16,
1029        group_addr: Ipv6Addr,
1030        sources: &[Ipv6Addr],
1031    ) {
1032        assert_eq!(icmp.message_body.header._reserved.get(), 0);
1033        assert_eq!(icmp.message_body.header.max_response_code.get(), max_resp_code);
1034        assert_eq!(icmp.message_body.header.group_addr, group_addr);
1035        assert_eq!(icmp.message_body.sources.len(), sources.len());
1036        for (expected, actual) in sources.iter().zip(icmp.message_body.sources.iter()) {
1037            assert_eq!(actual, expected);
1038        }
1039
1040        // When interpreted as a v1 body we should get valid results.
1041        let Mldv1Body(v1) = icmp.message_body.as_v1_query();
1042        assert_eq!(v1.max_response_delay.get(), max_resp_code);
1043        assert_eq!(v1.group_addr, group_addr);
1044    }
1045
1046    fn check_mld_report_v2<
1047        'a,
1048        B: SplitByteSlice,
1049        M: IcmpMessage<Ipv6, Body<B> = Mldv2ReportBody<B>> + Debug,
1050    >(
1051        icmp: &IcmpPacket<Ipv6, B, M>,
1052        expected_records_header: &[(Mldv2MulticastRecordType, Ipv6Addr)],
1053        expected_records_sources: &[&[Ipv6Addr]],
1054    ) {
1055        assert_eq!(
1056            icmp.message_body.header.num_mcast_addr_records.get(),
1057            u16::try_from(expected_records_header.len()).unwrap()
1058        );
1059        let expected_records = expected_records_header.iter().zip(expected_records_sources.iter());
1060        for (expected_record, actual_record) in
1061            expected_records.zip(icmp.message_body.iter_multicast_records())
1062        {
1063            let (expected_header, expected_sources) = expected_record;
1064            let (expected_record_type, expected_multicast_addr) = expected_header;
1065            assert_eq!(
1066                expected_record_type,
1067                &actual_record.header.record_type().expect("valid record type")
1068            );
1069
1070            assert_eq!(
1071                u16::try_from(expected_sources.len()).unwrap(),
1072                actual_record.header.number_of_sources()
1073            );
1074            assert_eq!(expected_multicast_addr, actual_record.header.multicast_addr());
1075            assert_eq!(*expected_sources, actual_record.sources());
1076        }
1077    }
1078
1079    #[test]
1080    fn test_mld_parse_and_serialize_query() {
1081        use crate::icmp::mld::MulticastListenerQuery;
1082        use crate::testdata::mld_router_query::*;
1083        test_parse_and_serialize::<MulticastListenerQuery, _, _>(
1084            SRC_IP,
1085            DST_IP,
1086            QUERY,
1087            |ip| {
1088                check_ip(ip, SRC_IP, DST_IP);
1089            },
1090            |icmp| {
1091                check_mld_v1(icmp, MAX_RESP_CODE, HOST_GROUP_ADDRESS);
1092            },
1093        );
1094    }
1095
1096    #[test]
1097    fn test_mld_parse_and_serialize_report() {
1098        use crate::icmp::mld::MulticastListenerReport;
1099        use crate::testdata::mld_router_report::*;
1100        test_parse_and_serialize::<MulticastListenerReport, _, _>(
1101            SRC_IP,
1102            DST_IP,
1103            REPORT,
1104            |ip| {
1105                check_ip(ip, SRC_IP, DST_IP);
1106            },
1107            |icmp| {
1108                check_mld_v1(icmp, 0, HOST_GROUP_ADDRESS);
1109            },
1110        );
1111    }
1112
1113    #[test]
1114    fn test_mld_parse_and_serialize_done() {
1115        use crate::icmp::mld::MulticastListenerDone;
1116        use crate::testdata::mld_router_done::*;
1117        test_parse_and_serialize::<MulticastListenerDone, _, _>(
1118            SRC_IP,
1119            DST_IP,
1120            DONE,
1121            |ip| {
1122                check_ip(ip, SRC_IP, DST_IP);
1123            },
1124            |icmp| {
1125                check_mld_v1(icmp, 0, HOST_GROUP_ADDRESS);
1126            },
1127        );
1128    }
1129
1130    #[test]
1131    fn test_mld_parse_and_serialize_query_v2() {
1132        use crate::icmp::mld::MulticastListenerQueryV2;
1133        use crate::testdata::mld_router_query::*;
1134        test_parse_and_serialize::<MulticastListenerQueryV2, _, _>(
1135            SRC_IP,
1136            DST_IP,
1137            QUERY_V2,
1138            |ip| {
1139                check_ip(ip, SRC_IP, DST_IP);
1140            },
1141            |icmp| {
1142                check_mld_query_v2(icmp, MAX_RESP_CODE, HOST_GROUP_ADDRESS, SOURCES);
1143            },
1144        );
1145    }
1146
1147    #[test]
1148    fn test_mld_parse_and_serialize_report_v2() {
1149        use crate::icmp::mld::MulticastListenerReportV2;
1150        use crate::testdata::mld_router_report_v2::*;
1151        test_parse_and_serialize::<MulticastListenerReportV2, _, _>(
1152            SRC_IP,
1153            DST_IP,
1154            REPORT,
1155            |ip| {
1156                check_ip(ip, SRC_IP, DST_IP);
1157            },
1158            |icmp| {
1159                check_mld_report_v2(icmp, RECORDS_HEADERS, RECORDS_SOURCES);
1160            },
1161        );
1162    }
1163
1164    #[test]
1165    fn test_mld_serialize_and_parse_query() {
1166        use crate::icmp::mld::MulticastListenerQuery;
1167        use crate::testdata::mld_router_query::*;
1168        let bytes = serialize_to_bytes_with_builder::<_>(
1169            SRC_IP,
1170            DST_IP,
1171            MulticastListenerQuery,
1172            HOST_GROUP_ADDRESS,
1173            Duration::from_secs(1).try_into().unwrap(),
1174        );
1175        assert_eq!(&bytes[..], QUERY);
1176        let mut req = &bytes[..];
1177        let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1178        check_ip(&ip, SRC_IP, DST_IP);
1179        let icmp = req
1180            .parse_with::<_, IcmpPacket<_, _, MulticastListenerQuery>>(IcmpParseArgs::new(
1181                SRC_IP, DST_IP,
1182            ))
1183            .unwrap();
1184        check_mld_v1(&icmp, MAX_RESP_CODE, HOST_GROUP_ADDRESS);
1185    }
1186
1187    #[test]
1188    fn test_mld_serialize_and_parse_report() {
1189        use crate::icmp::mld::MulticastListenerReport;
1190        use crate::testdata::mld_router_report::*;
1191        let bytes = serialize_to_bytes_with_builder::<_>(
1192            SRC_IP,
1193            DST_IP,
1194            MulticastListenerReport,
1195            MulticastAddr::new(HOST_GROUP_ADDRESS).unwrap(),
1196            (),
1197        );
1198        assert_eq!(&bytes[..], REPORT);
1199        let mut req = &bytes[..];
1200        let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1201        check_ip(&ip, SRC_IP, DST_IP);
1202        let icmp = req
1203            .parse_with::<_, IcmpPacket<_, _, MulticastListenerReport>>(IcmpParseArgs::new(
1204                SRC_IP, DST_IP,
1205            ))
1206            .unwrap();
1207        check_mld_v1(&icmp, 0, HOST_GROUP_ADDRESS);
1208    }
1209
1210    #[test]
1211    fn test_mld_serialize_and_parse_done() {
1212        use crate::icmp::mld::MulticastListenerDone;
1213        use crate::testdata::mld_router_done::*;
1214        let bytes = serialize_to_bytes_with_builder::<_>(
1215            SRC_IP,
1216            DST_IP,
1217            MulticastListenerDone,
1218            MulticastAddr::new(HOST_GROUP_ADDRESS).unwrap(),
1219            (),
1220        );
1221        assert_eq!(&bytes[..], DONE);
1222        let mut req = &bytes[..];
1223        let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1224        check_ip(&ip, SRC_IP, DST_IP);
1225        let icmp = req
1226            .parse_with::<_, IcmpPacket<_, _, MulticastListenerDone>>(IcmpParseArgs::new(
1227                SRC_IP, DST_IP,
1228            ))
1229            .unwrap();
1230        check_mld_v1(&icmp, 0, HOST_GROUP_ADDRESS);
1231    }
1232
1233    #[test]
1234    fn test_mld_serialize_and_parse_query_v2() {
1235        use crate::icmp::mld::{Mldv2QRV, Mldv2ResponseDelay, MulticastListenerQueryV2};
1236        use crate::testdata::mld_router_query::*;
1237        use core::time::Duration;
1238
1239        let builder = Mldv2QueryMessageBuilder::new(
1240            Mldv2ResponseDelay::lossy_try_from(Duration::from_millis(MAX_RESP_CODE.into()))
1241                .unwrap(),
1242            MulticastAddr::new(HOST_GROUP_ADDRESS),
1243            S_FLAG,
1244            Mldv2QRV::new(QRV),
1245            Mldv2QQIC::lossy_try_from(Duration::from_secs(QQIC.into())).unwrap(),
1246            SOURCES.iter(),
1247        );
1248
1249        let bytes =
1250            serialize_to_bytes_with_builder_v2(SRC_IP, DST_IP, MulticastListenerQueryV2, builder);
1251        assert_eq!(&bytes[..], QUERY_V2);
1252        let mut req = &bytes[..];
1253        let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1254        check_ip(&ip, SRC_IP, DST_IP);
1255        let icmp = req
1256            .parse_with::<_, IcmpPacket<_, _, MulticastListenerQueryV2>>(IcmpParseArgs::new(
1257                SRC_IP, DST_IP,
1258            ))
1259            .unwrap();
1260
1261        check_mld_query_v2(&icmp, MAX_RESP_CODE, HOST_GROUP_ADDRESS, SOURCES);
1262    }
1263
1264    #[test]
1265    fn test_mld_serialize_and_parse_report_v2() {
1266        use crate::icmp::mld::MulticastListenerReportV2;
1267        use crate::testdata::mld_router_report_v2::*;
1268
1269        let builder = Mldv2ReportMessageBuilder::new(
1270            RECORDS_HEADERS.iter().zip(RECORDS_SOURCES.iter()).map(|record| {
1271                let (record_header, record_sources) = record;
1272                let (record_type, multicast_addr) = record_header;
1273                (MulticastAddr::new(*multicast_addr).unwrap(), *record_type, record_sources.iter())
1274            }),
1275        );
1276
1277        let bytes =
1278            serialize_to_bytes_with_builder_v2(SRC_IP, DST_IP, MulticastListenerReportV2, builder);
1279        assert_eq!(&bytes[..], REPORT);
1280        let mut req = &bytes[..];
1281        let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1282        check_ip(&ip, SRC_IP, DST_IP);
1283        let icmp = req
1284            .parse_with::<_, IcmpPacket<_, _, MulticastListenerReportV2>>(IcmpParseArgs::new(
1285                SRC_IP, DST_IP,
1286            ))
1287            .unwrap();
1288
1289        check_mld_report_v2(&icmp, RECORDS_HEADERS, RECORDS_SOURCES);
1290    }
1291
1292    #[test_case(&[0x11, 0x22, 0x33, 0x44]; "extra bytes")]
1293    #[test_case(Ipv6Addr::new([1, 2, 3, 4, 5, 6, 7, 8]).bytes(); "extra addr")]
1294    fn test_mld_query_v2_trailing_bytes_ignored(extend_bytes: &[u8]) {
1295        use crate::testdata::mld_router_query::*;
1296
1297        let mut body_with_trailing = QUERY_V2.to_vec();
1298        body_with_trailing.extend_from_slice(extend_bytes);
1299
1300        let mut req = &body_with_trailing[..];
1301        let ip = req.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1302        check_ip(&ip, SRC_IP, DST_IP);
1303        let icmp = req
1304            .parse_with::<_, IcmpPacket<_, _, MulticastListenerQueryV2>>(IcmpParseArgs::new(
1305                SRC_IP, DST_IP,
1306            ))
1307            .unwrap();
1308
1309        check_mld_query_v2(&icmp, MAX_RESP_CODE, HOST_GROUP_ADDRESS, SOURCES);
1310    }
1311
1312    // Test that our maximum sources accounting matches an equivalent example in
1313    // RFC 3810 section 5.1.10:
1314    //
1315    //  For example, on an Ethernet link with an MTU of 1500 octets, the IPv6
1316    //  header (40 octets) together with the Hop-By-Hop Extension Header (8
1317    //  octets) that includes the Router Alert option consume 48 octets; the MLD
1318    //  fields up to the Number of Sources (N) field consume 28 octets; thus,
1319    //  there are 1424 octets left for source addresses, which limits the number
1320    //  of source addresses to 89 (1424/16)
1321    //
1322    // This example is for queries, but reports have the same prefix length so
1323    // we can use the same numbers.
1324    #[test]
1325    fn report_v2_split_many_sources() {
1326        use crate::testdata::mld_router_report_v2::*;
1327        use packet::{NestablePacketBuilder as _, NestableSerializer as _};
1328
1329        const ETH_MTU: usize = 1500;
1330        const MAX_SOURCES: usize = 89;
1331
1332        let ip_builder = Ipv6PacketBuilderWithHbhOptions::new(
1333            Ipv6PacketBuilder::new(SRC_IP, DST_IP, 1, Ipv6Proto::Icmpv6),
1334            &[HopByHopOption {
1335                action: ExtensionHeaderOptionAction::SkipAndContinue,
1336                mutable: false,
1337                data: HopByHopOptionData::RouterAlert { data: 0 },
1338            }],
1339        )
1340        .unwrap();
1341        let icmp_builder =
1342            IcmpPacketBuilder::new(SRC_IP, DST_IP, IcmpSenderZeroCode, MulticastListenerReportV2);
1343
1344        let avail_len = ETH_MTU
1345            - ip_builder.constraints().header_len()
1346            - icmp_builder.constraints().header_len();
1347
1348        let src_ip = |i: usize| Ipv6Addr::new([0x2000, 0, 0, 0, 0, 0, 0, i as u16]);
1349        let group_addr = MulticastAddr::new(RECORDS_HEADERS[0].1).unwrap();
1350        let reports = Mldv2ReportMessageBuilder::new(
1351            [(
1352                group_addr,
1353                Mldv2MulticastRecordType::ModeIsInclude,
1354                (0..MAX_SOURCES).into_iter().map(|i| src_ip(i)),
1355            )]
1356            .into_iter(),
1357        )
1358        .with_len_limits(avail_len)
1359        .unwrap();
1360
1361        let mut reports = reports.map(|builder| {
1362            builder
1363                .into_serializer()
1364                .wrap_in(icmp_builder.clone())
1365                .wrap_in(ip_builder.clone())
1366                .serialize_vec_outer(&mut NoOpSerializationContext)
1367                .unwrap_or_else(|(err, _)| panic!("{err:?}"))
1368                .unwrap_b()
1369                .into_inner()
1370        });
1371        // We can generate a report at exactly ETH_MTU.
1372        let serialized = reports.next().unwrap();
1373        assert_eq!(serialized.len(), ETH_MTU);
1374        let mut buffer = &serialized[..];
1375        let ip = buffer.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1376        check_ip(&ip, SRC_IP, DST_IP);
1377        let icmp = buffer
1378            .parse_with::<_, IcmpPacket<_, _, MulticastListenerReportV2>>(IcmpParseArgs::new(
1379                SRC_IP, DST_IP,
1380            ))
1381            .unwrap();
1382
1383        let mut groups = icmp.body().iter_multicast_records();
1384        let group = groups.next().expect("has group");
1385        assert_eq!(group.header.multicast_address, group_addr.get());
1386        assert_eq!(usize::from(group.header.number_of_sources()), MAX_SOURCES);
1387        assert_eq!(group.sources().len(), MAX_SOURCES);
1388        for (i, addr) in group.sources().iter().enumerate() {
1389            assert_eq!(*addr, src_ip(i));
1390        }
1391        assert_eq!(groups.next().map(|r| r.header.multicast_address), None);
1392        // Only one report is generated.
1393        assert_eq!(reports.next(), None);
1394
1395        let reports = Mldv2ReportMessageBuilder::new(
1396            [(
1397                group_addr,
1398                Mldv2MulticastRecordType::ModeIsInclude,
1399                core::iter::repeat(SRC_IP).take(MAX_SOURCES + 1),
1400            )]
1401            .into_iter(),
1402        )
1403        .with_len_limits(avail_len)
1404        .unwrap();
1405        // 2 reports are generated with one extra source.
1406        assert_eq!(
1407            reports
1408                .map(|r| r.groups.map(|group| group.sources().count()).collect::<Vec<_>>())
1409                .collect::<Vec<_>>(),
1410            vec![vec![MAX_SOURCES], vec![1]]
1411        );
1412    }
1413
1414    // Like report_v2_split_many_sources but we calculate how many groups with
1415    // no sources specified we can have in the same 1500 Ethernet MTU.
1416    //
1417    // * 48 bytes for IPv6 header + Hop-by-Hop router alert.
1418    // * 8 bytes for ICMPv6 header + report up to number of groups.
1419    // * 20 bytes per group with no sources.
1420    //
1421    // So we should be able to fit (1500 - 48 - 8)/20 = 72.2 groups. 72
1422    // groups result in a a 1496 byte-long message.
1423    #[test]
1424    fn report_v2_split_many_groups() {
1425        use crate::testdata::mld_router_report_v2::*;
1426        use packet::{NestablePacketBuilder as _, NestableSerializer as _};
1427
1428        const ETH_MTU: usize = 1500;
1429        const EXPECT_SERIALIZED: usize = 1496;
1430        const MAX_GROUPS: usize = 72;
1431
1432        let ip_builder = Ipv6PacketBuilderWithHbhOptions::new(
1433            Ipv6PacketBuilder::new(SRC_IP, DST_IP, 1, Ipv6Proto::Icmpv6),
1434            &[HopByHopOption {
1435                action: ExtensionHeaderOptionAction::SkipAndContinue,
1436                mutable: false,
1437                data: HopByHopOptionData::RouterAlert { data: 0 },
1438            }],
1439        )
1440        .unwrap();
1441        let icmp_builder =
1442            IcmpPacketBuilder::new(SRC_IP, DST_IP, IcmpSenderZeroCode, MulticastListenerReportV2);
1443
1444        let avail_len = ETH_MTU
1445            - ip_builder.constraints().header_len()
1446            - icmp_builder.constraints().header_len();
1447
1448        let group_ip = |i: usize| {
1449            MulticastAddr::new(Ipv6Addr::new([0xff02, 0, 0, 0, 0, 0, 0, i as u16])).unwrap()
1450        };
1451        let reports = Mldv2ReportMessageBuilder::new((0..MAX_GROUPS).into_iter().map(|i| {
1452            (group_ip(i), Mldv2MulticastRecordType::ModeIsExclude, core::iter::empty::<Ipv6Addr>())
1453        }))
1454        .with_len_limits(avail_len)
1455        .unwrap();
1456
1457        let mut reports = reports.map(|builder| {
1458            builder
1459                .into_serializer()
1460                .wrap_in(icmp_builder.clone())
1461                .wrap_in(ip_builder.clone())
1462                .serialize_vec_outer(&mut NoOpSerializationContext)
1463                .unwrap_or_else(|(err, _)| panic!("{err:?}"))
1464                .unwrap_b()
1465                .into_inner()
1466        });
1467        // We can generate a report at exactly ETH_MTU.
1468        let serialized = reports.next().unwrap();
1469        assert_eq!(serialized.len(), EXPECT_SERIALIZED);
1470        let mut buffer = &serialized[..];
1471        let ip = buffer.parse_with::<_, Ipv6Packet<_>>(()).unwrap();
1472        check_ip(&ip, SRC_IP, DST_IP);
1473        let icmp = buffer
1474            .parse_with::<_, IcmpPacket<_, _, MulticastListenerReportV2>>(IcmpParseArgs::new(
1475                SRC_IP, DST_IP,
1476            ))
1477            .unwrap();
1478        assert_eq!(usize::from(icmp.body().header().num_mcast_addr_records()), MAX_GROUPS);
1479        for (i, group) in icmp.body().iter_multicast_records().enumerate() {
1480            assert_eq!(group.header.number_of_sources.get(), 0);
1481            assert_eq!(group.header.multicast_addr(), &group_ip(i).get());
1482        }
1483        // Only one report is generated.
1484        assert_eq!(reports.next(), None);
1485
1486        let reports = Mldv2ReportMessageBuilder::new((0..MAX_GROUPS + 1).into_iter().map(|i| {
1487            (group_ip(i), Mldv2MulticastRecordType::ModeIsExclude, core::iter::empty::<Ipv6Addr>())
1488        }))
1489        .with_len_limits(avail_len)
1490        .unwrap();
1491        // 2 reports are generated with one extra group.
1492        assert_eq!(reports.map(|r| r.groups.count()).collect::<Vec<_>>(), vec![MAX_GROUPS, 1]);
1493    }
1494
1495    #[test]
1496    fn test_mld_parse_and_serialize_response_delay_v2_linear() {
1497        // Linear code:duration mapping
1498        for code in 0..(Mldv2ResponseDelay::SWITCHPOINT as u16) {
1499            let response_delay = Mldv2ResponseDelay::from_code(U16::from(code));
1500            let duration = Duration::from(response_delay);
1501            assert_eq!(duration.as_millis(), code.into());
1502
1503            let duration = Duration::from_millis(code.into());
1504            let response_delay_code: u16 =
1505                Mldv2ResponseDelay::lossy_try_from(duration).unwrap().as_code().into();
1506            assert_eq!(response_delay_code, code);
1507
1508            let duration = Duration::from_millis(code.into());
1509            let response_delay_code: u16 =
1510                Mldv2ResponseDelay::exact_try_from(duration).unwrap().as_code().into();
1511            assert_eq!(response_delay_code, code);
1512        }
1513    }
1514
1515    #[test_case(Mldv2ResponseDelay::SWITCHPOINT, 0x8000; "min exponential value")]
1516    #[test_case(32784,                           0x8002; "exponental value 32784")]
1517    #[test_case(227744,                          0xABCD; "exponental value 227744")]
1518    #[test_case(1821184,                         0xDBCA; "exponental value 1821184")]
1519    #[test_case(8385536,                         0xFFFD; "exponental value 8385536")]
1520    #[test_case(Mldv2ResponseDelay::MAX_VALUE,   0xFFFF; "max exponential value")]
1521    fn test_mld_parse_and_serialize_response_delay_v2_exponential_exact(
1522        duration_millis: u32,
1523        resp_code: u16,
1524    ) {
1525        let response_delay = Mldv2ResponseDelay::from_code(resp_code.into());
1526        let duration = Duration::from(response_delay);
1527        assert_eq!(duration.as_millis(), duration_millis.into());
1528
1529        let response_delay_code: u16 =
1530            Mldv2ResponseDelay::lossy_try_from(duration).unwrap().as_code().into();
1531        assert_eq!(response_delay_code, resp_code);
1532
1533        let response_delay_code: u16 =
1534            Mldv2ResponseDelay::exact_try_from(duration).unwrap().as_code().into();
1535        assert_eq!(response_delay_code, resp_code);
1536    }
1537
1538    #[test]
1539    fn test_mld_parse_and_serialize_response_delay_v2_errors() {
1540        let duration = Duration::from_millis((Mldv2ResponseDelay::MAX_VALUE + 1).into());
1541        assert_eq!(Mldv2ResponseDelay::lossy_try_from(duration), Err(OverflowError));
1542
1543        let duration = Duration::from_millis((Mldv2ResponseDelay::MAX_VALUE + 1).into());
1544        assert_eq!(
1545            Mldv2ResponseDelay::exact_try_from(duration),
1546            Err(ExactConversionError::Overflow)
1547        );
1548
1549        let duration = Duration::from_millis((Mldv2ResponseDelay::MAX_VALUE - 1).into());
1550        assert_eq!(
1551            Mldv2ResponseDelay::exact_try_from(duration),
1552            Err(ExactConversionError::NotExact)
1553        );
1554    }
1555
1556    #[test]
1557    fn test_mld_parse_and_serialize_response_qqic_v2_linear() {
1558        // Linear code:duration mapping
1559        for code in 0..(Mldv2QQIC::SWITCHPOINT as u8) {
1560            let response_delay = Mldv2QQIC::from_code(code);
1561            let duration = Duration::from(response_delay);
1562            assert_eq!(duration.as_secs(), code.into());
1563
1564            let duration = Duration::from_secs(code.into());
1565            let response_delay_code: u8 =
1566                Mldv2QQIC::lossy_try_from(duration).unwrap().as_code().into();
1567            assert_eq!(response_delay_code, code);
1568
1569            let duration = Duration::from_secs(code.into());
1570            let response_delay_code: u8 =
1571                Mldv2QQIC::exact_try_from(duration).unwrap().as_code().into();
1572            assert_eq!(response_delay_code, code);
1573        }
1574    }
1575
1576    #[test_case(Mldv2QQIC::SWITCHPOINT, 0x80; "min exponential value")]
1577    #[test_case(144,                    0x82; "exponental value 144")]
1578    #[test_case(928,                    0xAD; "exponental value 928")]
1579    #[test_case(6656,                   0xDA; "exponental value 6656")]
1580    #[test_case(29696,                  0xFD; "exponental value 29696")]
1581    #[test_case(Mldv2QQIC::MAX_VALUE,   0xFF; "max exponential value")]
1582    fn test_mld_parse_and_serialize_response_qqic_v2_exponential_exact(
1583        duration_secs: u32,
1584        resp_code: u8,
1585    ) {
1586        let response_delay = Mldv2QQIC::from_code(resp_code.into());
1587        let duration = Duration::from(response_delay);
1588        assert_eq!(duration.as_secs(), duration_secs.into());
1589
1590        let response_delay_code: u8 = Mldv2QQIC::lossy_try_from(duration).unwrap().as_code().into();
1591        assert_eq!(response_delay_code, resp_code);
1592
1593        let response_delay_code: u8 = Mldv2QQIC::exact_try_from(duration).unwrap().as_code().into();
1594        assert_eq!(response_delay_code, resp_code);
1595    }
1596
1597    #[test]
1598    fn test_mld_parse_and_serialize_response_qqic_v2_errors() {
1599        let duration = Duration::from_secs((Mldv2QQIC::MAX_VALUE + 1).into());
1600        assert_eq!(Mldv2QQIC::lossy_try_from(duration), Err(OverflowError));
1601
1602        let duration = Duration::from_secs((Mldv2QQIC::MAX_VALUE + 1).into());
1603        assert_eq!(Mldv2QQIC::exact_try_from(duration), Err(ExactConversionError::Overflow));
1604
1605        let duration = Duration::from_secs((Mldv2QQIC::MAX_VALUE - 1).into());
1606        assert_eq!(Mldv2QQIC::exact_try_from(duration), Err(ExactConversionError::NotExact));
1607    }
1608
1609    #[test]
1610    fn test_mld_too_few_records() {
1611        let src_ip = net_ip_v6!("::1");
1612        let group_addr = MulticastAddr::new(net_ip_v6!("ff02::2")).unwrap();
1613
1614        // Serialize 1 record.
1615        let builder = Mldv2ReportMessageBuilder::new(
1616            [(group_addr, GroupRecordType::ModeIsInclude, core::iter::once(src_ip))].into_iter(),
1617        );
1618        let serialized = builder
1619            .into_serializer()
1620            .serialize_vec_outer(&mut NoOpSerializationContext)
1621            .unwrap()
1622            .unwrap_b()
1623            .into_inner();
1624
1625        // Skip MLDv2 report header to get only the record bytes.
1626        let header_size = core::mem::size_of::<Mldv2ReportHeader>();
1627        let record_bytes = &serialized[header_size..];
1628
1629        let mut ctx = 2; // Expect 2 records, but only 1 is present in record_bytes
1630        let res = Records::<_, Mldv2ReportRecords>::parse_with_mut_context(record_bytes, &mut ctx);
1631        assert_eq!(res.unwrap_err(), ParseError::Format);
1632        assert_eq!(ctx, 1); // Parsed one record, so counter decremented to 1
1633    }
1634}