smoltcp/wire/
mld.rs

1// Packet implementation for the Multicast Listener Discovery
2// protocol. See [RFC 3810] and [RFC 2710].
3//
4// [RFC 3810]: https://tools.ietf.org/html/rfc3810
5// [RFC 2710]: https://tools.ietf.org/html/rfc2710
6
7use byteorder::{ByteOrder, NetworkEndian};
8
9use super::{Error, Result};
10use crate::wire::icmpv6::{field, Message, Packet};
11use crate::wire::{Ipv6Address, Ipv6AddressExt};
12
13enum_with_unknown! {
14    /// MLDv2 Multicast Listener Report Record Type. See [RFC 3810 § 5.2.12] for
15    /// more details.
16    ///
17    /// [RFC 3810 § 5.2.12]: https://tools.ietf.org/html/rfc3010#section-5.2.12
18    pub enum RecordType(u8) {
19        /// Interface has a filter mode of INCLUDE for the specified multicast address.
20        ModeIsInclude   = 0x01,
21        /// Interface has a filter mode of EXCLUDE for the specified multicast address.
22        ModeIsExclude   = 0x02,
23        /// Interface has changed to a filter mode of INCLUDE for the specified
24        /// multicast address.
25        ChangeToInclude = 0x03,
26        /// Interface has changed to a filter mode of EXCLUDE for the specified
27        /// multicast address.
28        ChangeToExclude = 0x04,
29        /// Interface wishes to listen to the sources in the specified list.
30        AllowNewSources = 0x05,
31        /// Interface no longer wishes to listen to the sources in the specified list.
32        BlockOldSources = 0x06
33    }
34}
35
36/// Getters for the Multicast Listener Query message header.
37/// See [RFC 3810 § 5.1].
38///
39/// [RFC 3810 § 5.1]: https://tools.ietf.org/html/rfc3010#section-5.1
40impl<T: AsRef<[u8]>> Packet<T> {
41    /// Return the maximum response code field.
42    #[inline]
43    pub fn max_resp_code(&self) -> u16 {
44        let data = self.buffer.as_ref();
45        NetworkEndian::read_u16(&data[field::MAX_RESP_CODE])
46    }
47
48    /// Return the address being queried.
49    #[inline]
50    pub fn mcast_addr(&self) -> Ipv6Address {
51        let data = self.buffer.as_ref();
52        Ipv6Address::from_bytes(&data[field::QUERY_MCAST_ADDR])
53    }
54
55    /// Return the Suppress Router-Side Processing flag.
56    #[inline]
57    pub fn s_flag(&self) -> bool {
58        let data = self.buffer.as_ref();
59        (data[field::SQRV] & 0x08) != 0
60    }
61
62    /// Return the Querier's Robustness Variable.
63    #[inline]
64    pub fn qrv(&self) -> u8 {
65        let data = self.buffer.as_ref();
66        data[field::SQRV] & 0x7
67    }
68
69    /// Return the Querier's Query Interval Code.
70    #[inline]
71    pub fn qqic(&self) -> u8 {
72        let data = self.buffer.as_ref();
73        data[field::QQIC]
74    }
75
76    /// Return number of sources.
77    #[inline]
78    pub fn num_srcs(&self) -> u16 {
79        let data = self.buffer.as_ref();
80        NetworkEndian::read_u16(&data[field::QUERY_NUM_SRCS])
81    }
82}
83
84/// Getters for the Multicast Listener Report message header.
85/// See [RFC 3810 § 5.2].
86///
87/// [RFC 3810 § 5.2]: https://tools.ietf.org/html/rfc3010#section-5.2
88impl<T: AsRef<[u8]>> Packet<T> {
89    /// Return the number of Multicast Address Records.
90    #[inline]
91    pub fn nr_mcast_addr_rcrds(&self) -> u16 {
92        let data = self.buffer.as_ref();
93        NetworkEndian::read_u16(&data[field::NR_MCAST_RCRDS])
94    }
95}
96
97/// Setters for the Multicast Listener Query message header.
98/// See [RFC 3810 § 5.1].
99///
100/// [RFC 3810 § 5.1]: https://tools.ietf.org/html/rfc3010#section-5.1
101impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
102    /// Set the maximum response code field.
103    #[inline]
104    pub fn set_max_resp_code(&mut self, code: u16) {
105        let data = self.buffer.as_mut();
106        NetworkEndian::write_u16(&mut data[field::MAX_RESP_CODE], code);
107    }
108
109    /// Set the address being queried.
110    #[inline]
111    pub fn set_mcast_addr(&mut self, addr: Ipv6Address) {
112        let data = self.buffer.as_mut();
113        data[field::QUERY_MCAST_ADDR].copy_from_slice(&addr.octets());
114    }
115
116    /// Set the Suppress Router-Side Processing flag.
117    #[inline]
118    pub fn set_s_flag(&mut self) {
119        let data = self.buffer.as_mut();
120        let current = data[field::SQRV];
121        data[field::SQRV] = 0x8 | (current & 0x7);
122    }
123
124    /// Clear the Suppress Router-Side Processing flag.
125    #[inline]
126    pub fn clear_s_flag(&mut self) {
127        let data = self.buffer.as_mut();
128        data[field::SQRV] &= 0x7;
129    }
130
131    /// Set the Querier's Robustness Variable.
132    #[inline]
133    pub fn set_qrv(&mut self, value: u8) {
134        assert!(value < 8);
135        let data = self.buffer.as_mut();
136        data[field::SQRV] = (data[field::SQRV] & 0x8) | value & 0x7;
137    }
138
139    /// Set the Querier's Query Interval Code.
140    #[inline]
141    pub fn set_qqic(&mut self, value: u8) {
142        let data = self.buffer.as_mut();
143        data[field::QQIC] = value;
144    }
145
146    /// Set number of sources.
147    #[inline]
148    pub fn set_num_srcs(&mut self, value: u16) {
149        let data = self.buffer.as_mut();
150        NetworkEndian::write_u16(&mut data[field::QUERY_NUM_SRCS], value);
151    }
152}
153
154/// Setters for the Multicast Listener Report message header.
155/// See [RFC 3810 § 5.2].
156///
157/// [RFC 3810 § 5.2]: https://tools.ietf.org/html/rfc3010#section-5.2
158impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
159    /// Set the number of Multicast Address Records.
160    #[inline]
161    pub fn set_nr_mcast_addr_rcrds(&mut self, value: u16) {
162        let data = self.buffer.as_mut();
163        NetworkEndian::write_u16(&mut data[field::NR_MCAST_RCRDS], value)
164    }
165}
166
167/// A read/write wrapper around an MLDv2 Listener Report Message Address Record.
168#[derive(Debug, PartialEq, Eq, Clone)]
169#[cfg_attr(feature = "defmt", derive(defmt::Format))]
170pub struct AddressRecord<T: AsRef<[u8]>> {
171    buffer: T,
172}
173
174impl<T: AsRef<[u8]>> AddressRecord<T> {
175    /// Imbue a raw octet buffer with a Address Record structure.
176    pub const fn new_unchecked(buffer: T) -> Self {
177        Self { buffer }
178    }
179
180    /// Shorthand for a combination of [new_unchecked] and [check_len].
181    ///
182    /// [new_unchecked]: #method.new_unchecked
183    /// [check_len]: #method.check_len
184    pub fn new_checked(buffer: T) -> Result<Self> {
185        let packet = Self::new_unchecked(buffer);
186        packet.check_len()?;
187        Ok(packet)
188    }
189
190    /// Ensure that no accessor method will panic if called.
191    /// Returns `Err(Error::Truncated)` if the buffer is too short.
192    pub fn check_len(&self) -> Result<()> {
193        let len = self.buffer.as_ref().len();
194        if len < field::RECORD_MCAST_ADDR.end {
195            Err(Error)
196        } else {
197            Ok(())
198        }
199    }
200
201    /// Consume the packet, returning the underlying buffer.
202    pub fn into_inner(self) -> T {
203        self.buffer
204    }
205}
206
207/// Getters for a MLDv2 Listener Report Message Address Record.
208/// See [RFC 3810 § 5.2].
209///
210/// [RFC 3810 § 5.2]: https://tools.ietf.org/html/rfc3010#section-5.2
211impl<T: AsRef<[u8]>> AddressRecord<T> {
212    /// Return the record type for the given sources.
213    #[inline]
214    pub fn record_type(&self) -> RecordType {
215        let data = self.buffer.as_ref();
216        RecordType::from(data[field::RECORD_TYPE])
217    }
218
219    /// Return the length of the auxiliary data.
220    #[inline]
221    pub fn aux_data_len(&self) -> u8 {
222        let data = self.buffer.as_ref();
223        data[field::AUX_DATA_LEN]
224    }
225
226    /// Return the number of sources field.
227    #[inline]
228    pub fn num_srcs(&self) -> u16 {
229        let data = self.buffer.as_ref();
230        NetworkEndian::read_u16(&data[field::RECORD_NUM_SRCS])
231    }
232
233    /// Return the multicast address field.
234    #[inline]
235    pub fn mcast_addr(&self) -> Ipv6Address {
236        let data = self.buffer.as_ref();
237        Ipv6Address::from_bytes(&data[field::RECORD_MCAST_ADDR])
238    }
239}
240
241impl<'a, T: AsRef<[u8]> + ?Sized> AddressRecord<&'a T> {
242    /// Return a pointer to the address records.
243    #[inline]
244    pub fn payload(&self) -> &'a [u8] {
245        let data = self.buffer.as_ref();
246        &data[field::RECORD_MCAST_ADDR.end..]
247    }
248}
249
250/// Setters for a MLDv2 Listener Report Message Address Record.
251/// See [RFC 3810 § 5.2].
252///
253/// [RFC 3810 § 5.2]: https://tools.ietf.org/html/rfc3010#section-5.2
254impl<T: AsMut<[u8]> + AsRef<[u8]>> AddressRecord<T> {
255    /// Return the record type for the given sources.
256    #[inline]
257    pub fn set_record_type(&mut self, rty: RecordType) {
258        let data = self.buffer.as_mut();
259        data[field::RECORD_TYPE] = rty.into();
260    }
261
262    /// Return the length of the auxiliary data.
263    #[inline]
264    pub fn set_aux_data_len(&mut self, len: u8) {
265        let data = self.buffer.as_mut();
266        data[field::AUX_DATA_LEN] = len;
267    }
268
269    /// Return the number of sources field.
270    #[inline]
271    pub fn set_num_srcs(&mut self, num_srcs: u16) {
272        let data = self.buffer.as_mut();
273        NetworkEndian::write_u16(&mut data[field::RECORD_NUM_SRCS], num_srcs);
274    }
275
276    /// Return the multicast address field.
277    ///
278    /// # Panics
279    /// This function panics if the given address is not a multicast address.
280    #[inline]
281    pub fn set_mcast_addr(&mut self, addr: Ipv6Address) {
282        assert!(addr.is_multicast());
283        let data = self.buffer.as_mut();
284        data[field::RECORD_MCAST_ADDR].copy_from_slice(&addr.octets());
285    }
286}
287
288impl<T: AsRef<[u8]> + AsMut<[u8]>> AddressRecord<T> {
289    /// Return a pointer to the address records.
290    #[inline]
291    pub fn payload_mut(&mut self) -> &mut [u8] {
292        let data = self.buffer.as_mut();
293        &mut data[field::RECORD_MCAST_ADDR.end..]
294    }
295}
296
297/// A high level representation of an MLDv2 Listener Report Message Address Record.
298#[derive(Debug, PartialEq, Eq, Clone, Copy)]
299#[cfg_attr(feature = "defmt", derive(defmt::Format))]
300pub struct AddressRecordRepr<'a> {
301    pub record_type: RecordType,
302    pub aux_data_len: u8,
303    pub num_srcs: u16,
304    pub mcast_addr: Ipv6Address,
305    pub payload: &'a [u8],
306}
307
308impl<'a> AddressRecordRepr<'a> {
309    /// Create a new MLDv2 address record representation with an empty payload.
310    pub const fn new(record_type: RecordType, mcast_addr: Ipv6Address) -> Self {
311        Self {
312            record_type,
313            aux_data_len: 0,
314            num_srcs: 0,
315            mcast_addr,
316            payload: &[],
317        }
318    }
319
320    /// Parse an MLDv2 address record and return a high-level representation.
321    pub fn parse<T>(record: &AddressRecord<&'a T>) -> Result<Self>
322    where
323        T: AsRef<[u8]> + ?Sized,
324    {
325        Ok(Self {
326            num_srcs: record.num_srcs(),
327            mcast_addr: record.mcast_addr(),
328            record_type: record.record_type(),
329            aux_data_len: record.aux_data_len(),
330            payload: record.payload(),
331        })
332    }
333
334    /// Return the length of a record that will be emitted from this high-level
335    /// representation, not including any payload data.
336    pub fn buffer_len(&self) -> usize {
337        field::RECORD_MCAST_ADDR.end
338    }
339
340    /// Emit a high-level representation into an MLDv2 address record.
341    pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, record: &mut AddressRecord<T>) {
342        record.set_record_type(self.record_type);
343        record.set_aux_data_len(self.aux_data_len);
344        record.set_num_srcs(self.num_srcs);
345        record.set_mcast_addr(self.mcast_addr);
346    }
347}
348
349/// A high-level representation of an MLDv2 packet header.
350#[derive(Debug, PartialEq, Eq, Clone, Copy)]
351#[cfg_attr(feature = "defmt", derive(defmt::Format))]
352pub enum Repr<'a> {
353    Query {
354        max_resp_code: u16,
355        mcast_addr: Ipv6Address,
356        s_flag: bool,
357        qrv: u8,
358        qqic: u8,
359        num_srcs: u16,
360        data: &'a [u8],
361    },
362    Report {
363        nr_mcast_addr_rcrds: u16,
364        data: &'a [u8],
365    },
366    ReportRecordReprs(&'a [AddressRecordRepr<'a>]),
367}
368
369impl<'a> Repr<'a> {
370    /// Parse an MLDv2 packet and return a high-level representation.
371    pub fn parse<T>(packet: &Packet<&'a T>) -> Result<Repr<'a>>
372    where
373        T: AsRef<[u8]> + ?Sized,
374    {
375        packet.check_len()?;
376        match packet.msg_type() {
377            Message::MldQuery => Ok(Repr::Query {
378                max_resp_code: packet.max_resp_code(),
379                mcast_addr: packet.mcast_addr(),
380                s_flag: packet.s_flag(),
381                qrv: packet.qrv(),
382                qqic: packet.qqic(),
383                num_srcs: packet.num_srcs(),
384                data: packet.payload(),
385            }),
386            Message::MldReport => Ok(Repr::Report {
387                nr_mcast_addr_rcrds: packet.nr_mcast_addr_rcrds(),
388                data: packet.payload(),
389            }),
390            _ => Err(Error),
391        }
392    }
393
394    /// Return the length of a packet that will be emitted from this high-level representation.
395    pub const fn buffer_len(&self) -> usize {
396        match self {
397            Repr::Query { data, .. } => field::QUERY_NUM_SRCS.end + data.len(),
398            Repr::Report { data, .. } => field::NR_MCAST_RCRDS.end + data.len(),
399            Repr::ReportRecordReprs(_data) => field::NR_MCAST_RCRDS.end,
400        }
401    }
402
403    /// Emit a high-level representation into an MLDv2 packet.
404    pub fn emit<T>(&self, packet: &mut Packet<&mut T>)
405    where
406        T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
407    {
408        match self {
409            Repr::Query {
410                max_resp_code,
411                mcast_addr,
412                s_flag,
413                qrv,
414                qqic,
415                num_srcs,
416                data,
417            } => {
418                packet.set_msg_type(Message::MldQuery);
419                packet.set_msg_code(0);
420                packet.clear_reserved();
421                packet.set_max_resp_code(*max_resp_code);
422                packet.set_mcast_addr(*mcast_addr);
423                if *s_flag {
424                    packet.set_s_flag();
425                } else {
426                    packet.clear_s_flag();
427                }
428                packet.set_qrv(*qrv);
429                packet.set_qqic(*qqic);
430                packet.set_num_srcs(*num_srcs);
431                packet.payload_mut().copy_from_slice(&data[..]);
432            }
433            Repr::Report {
434                nr_mcast_addr_rcrds,
435                data,
436            } => {
437                packet.set_msg_type(Message::MldReport);
438                packet.set_msg_code(0);
439                packet.clear_reserved();
440                packet.set_nr_mcast_addr_rcrds(*nr_mcast_addr_rcrds);
441                packet.payload_mut().copy_from_slice(&data[..]);
442            }
443            Repr::ReportRecordReprs(records) => {
444                packet.set_msg_type(Message::MldReport);
445                packet.set_msg_code(0);
446                packet.clear_reserved();
447                packet.set_nr_mcast_addr_rcrds(records.len() as u16);
448                let mut payload = packet.payload_mut();
449                for record in *records {
450                    record.emit(&mut AddressRecord::new_unchecked(&mut *payload));
451                    payload = &mut payload[record.buffer_len()..];
452                }
453            }
454        }
455    }
456}
457
458#[cfg(test)]
459mod test {
460    use super::*;
461    use crate::phy::ChecksumCapabilities;
462    use crate::wire::icmpv6::Message;
463    use crate::wire::{Icmpv6Repr, IPV6_LINK_LOCAL_ALL_NODES, IPV6_LINK_LOCAL_ALL_ROUTERS};
464
465    static QUERY_PACKET_BYTES: [u8; 44] = [
466        0x82, 0x00, 0x73, 0x74, 0x04, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
467        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x12, 0x00, 0x01, 0xff, 0x02,
468        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
469    ];
470
471    static QUERY_PACKET_PAYLOAD: [u8; 16] = [
472        0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
473        0x02,
474    ];
475
476    static REPORT_PACKET_BYTES: [u8; 44] = [
477        0x8f, 0x00, 0x73, 0x85, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00,
478        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02,
479        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
480    ];
481
482    static REPORT_PACKET_PAYLOAD: [u8; 36] = [
483        0x01, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
484        0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
485        0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
486    ];
487
488    fn create_repr<'a>(ty: Message) -> Icmpv6Repr<'a> {
489        match ty {
490            Message::MldQuery => Icmpv6Repr::Mld(Repr::Query {
491                max_resp_code: 0x400,
492                mcast_addr: IPV6_LINK_LOCAL_ALL_NODES,
493                s_flag: true,
494                qrv: 0x02,
495                qqic: 0x12,
496                num_srcs: 0x01,
497                data: &QUERY_PACKET_PAYLOAD,
498            }),
499            Message::MldReport => Icmpv6Repr::Mld(Repr::Report {
500                nr_mcast_addr_rcrds: 1,
501                data: &REPORT_PACKET_PAYLOAD,
502            }),
503            _ => {
504                panic!("Message type must be a MLDv2 message type");
505            }
506        }
507    }
508
509    #[test]
510    fn test_query_deconstruct() {
511        let packet = Packet::new_unchecked(&QUERY_PACKET_BYTES[..]);
512        assert_eq!(packet.msg_type(), Message::MldQuery);
513        assert_eq!(packet.msg_code(), 0);
514        assert_eq!(packet.checksum(), 0x7374);
515        assert_eq!(packet.max_resp_code(), 0x0400);
516        assert_eq!(packet.mcast_addr(), IPV6_LINK_LOCAL_ALL_NODES);
517        assert!(packet.s_flag());
518        assert_eq!(packet.qrv(), 0x02);
519        assert_eq!(packet.qqic(), 0x12);
520        assert_eq!(packet.num_srcs(), 0x01);
521        assert_eq!(
522            Ipv6Address::from_bytes(packet.payload()),
523            IPV6_LINK_LOCAL_ALL_ROUTERS
524        );
525    }
526
527    #[test]
528    fn test_query_construct() {
529        let mut bytes = [0xff; 44];
530        let mut packet = Packet::new_unchecked(&mut bytes[..]);
531        packet.set_msg_type(Message::MldQuery);
532        packet.set_msg_code(0);
533        packet.set_max_resp_code(0x0400);
534        packet.set_mcast_addr(IPV6_LINK_LOCAL_ALL_NODES);
535        packet.set_s_flag();
536        packet.set_qrv(0x02);
537        packet.set_qqic(0x12);
538        packet.set_num_srcs(0x01);
539        packet
540            .payload_mut()
541            .copy_from_slice(&IPV6_LINK_LOCAL_ALL_ROUTERS.octets());
542        packet.clear_reserved();
543        packet.fill_checksum(&IPV6_LINK_LOCAL_ALL_NODES, &IPV6_LINK_LOCAL_ALL_ROUTERS);
544        assert_eq!(&*packet.into_inner(), &QUERY_PACKET_BYTES[..]);
545    }
546
547    #[test]
548    fn test_record_deconstruct() {
549        let packet = Packet::new_unchecked(&REPORT_PACKET_BYTES[..]);
550        assert_eq!(packet.msg_type(), Message::MldReport);
551        assert_eq!(packet.msg_code(), 0);
552        assert_eq!(packet.checksum(), 0x7385);
553        assert_eq!(packet.nr_mcast_addr_rcrds(), 0x01);
554        let addr_rcrd = AddressRecord::new_unchecked(packet.payload());
555        assert_eq!(addr_rcrd.record_type(), RecordType::ModeIsInclude);
556        assert_eq!(addr_rcrd.aux_data_len(), 0x00);
557        assert_eq!(addr_rcrd.num_srcs(), 0x01);
558        assert_eq!(addr_rcrd.mcast_addr(), IPV6_LINK_LOCAL_ALL_NODES);
559        assert_eq!(
560            Ipv6Address::from_bytes(addr_rcrd.payload()),
561            IPV6_LINK_LOCAL_ALL_ROUTERS
562        );
563    }
564
565    #[test]
566    fn test_record_construct() {
567        let mut bytes = [0xff; 44];
568        let mut packet = Packet::new_unchecked(&mut bytes[..]);
569        packet.set_msg_type(Message::MldReport);
570        packet.set_msg_code(0);
571        packet.clear_reserved();
572        packet.set_nr_mcast_addr_rcrds(1);
573        {
574            let mut addr_rcrd = AddressRecord::new_unchecked(packet.payload_mut());
575            addr_rcrd.set_record_type(RecordType::ModeIsInclude);
576            addr_rcrd.set_aux_data_len(0);
577            addr_rcrd.set_num_srcs(1);
578            addr_rcrd.set_mcast_addr(IPV6_LINK_LOCAL_ALL_NODES);
579            addr_rcrd
580                .payload_mut()
581                .copy_from_slice(&IPV6_LINK_LOCAL_ALL_ROUTERS.octets());
582        }
583        packet.fill_checksum(&IPV6_LINK_LOCAL_ALL_NODES, &IPV6_LINK_LOCAL_ALL_ROUTERS);
584        assert_eq!(&*packet.into_inner(), &REPORT_PACKET_BYTES[..]);
585    }
586
587    #[test]
588    fn test_query_repr_parse() {
589        let packet = Packet::new_unchecked(&QUERY_PACKET_BYTES[..]);
590        let repr = Icmpv6Repr::parse(
591            &IPV6_LINK_LOCAL_ALL_NODES,
592            &IPV6_LINK_LOCAL_ALL_ROUTERS,
593            &packet,
594            &ChecksumCapabilities::default(),
595        );
596        assert_eq!(repr, Ok(create_repr(Message::MldQuery)));
597    }
598
599    #[test]
600    fn test_report_repr_parse() {
601        let packet = Packet::new_unchecked(&REPORT_PACKET_BYTES[..]);
602        let repr = Icmpv6Repr::parse(
603            &IPV6_LINK_LOCAL_ALL_NODES,
604            &IPV6_LINK_LOCAL_ALL_ROUTERS,
605            &packet,
606            &ChecksumCapabilities::default(),
607        );
608        assert_eq!(repr, Ok(create_repr(Message::MldReport)));
609    }
610
611    #[test]
612    fn test_query_repr_emit() {
613        let mut bytes = [0x2a; 44];
614        let mut packet = Packet::new_unchecked(&mut bytes[..]);
615        let repr = create_repr(Message::MldQuery);
616        repr.emit(
617            &IPV6_LINK_LOCAL_ALL_NODES,
618            &IPV6_LINK_LOCAL_ALL_ROUTERS,
619            &mut packet,
620            &ChecksumCapabilities::default(),
621        );
622        assert_eq!(&*packet.into_inner(), &QUERY_PACKET_BYTES[..]);
623    }
624
625    #[test]
626    fn test_report_repr_emit() {
627        let mut bytes = [0x2a; 44];
628        let mut packet = Packet::new_unchecked(&mut bytes[..]);
629        let repr = create_repr(Message::MldReport);
630        repr.emit(
631            &IPV6_LINK_LOCAL_ALL_NODES,
632            &IPV6_LINK_LOCAL_ALL_ROUTERS,
633            &mut packet,
634            &ChecksumCapabilities::default(),
635        );
636        assert_eq!(&*packet.into_inner(), &REPORT_PACKET_BYTES[..]);
637    }
638}