1 use bitflags::bitflags;
2 use byteorder::{ByteOrder, NetworkEndian};
3 
4 use super::{Error, Result};
5 use crate::time::Duration;
6 use crate::wire::icmpv6::{field, Message, Packet};
7 use crate::wire::Ipv6Address;
8 use crate::wire::RawHardwareAddress;
9 use crate::wire::{NdiscOption, NdiscOptionRepr};
10 use crate::wire::{NdiscPrefixInformation, NdiscRedirectedHeader};
11 
12 bitflags! {
13     #[cfg_attr(feature = "defmt", derive(defmt::Format))]
14     pub struct RouterFlags: u8 {
15         const MANAGED = 0b10000000;
16         const OTHER   = 0b01000000;
17     }
18 }
19 
20 bitflags! {
21     #[cfg_attr(feature = "defmt", derive(defmt::Format))]
22     pub struct NeighborFlags: u8 {
23         const ROUTER    = 0b10000000;
24         const SOLICITED = 0b01000000;
25         const OVERRIDE  = 0b00100000;
26     }
27 }
28 
29 /// Getters for the Router Advertisement message header.
30 /// See [RFC 4861 § 4.2].
31 ///
32 /// [RFC 4861 § 4.2]: https://tools.ietf.org/html/rfc4861#section-4.2
33 impl<T: AsRef<[u8]>> Packet<T> {
34     /// Return the current hop limit field.
35     #[inline]
current_hop_limit(&self) -> u836     pub fn current_hop_limit(&self) -> u8 {
37         let data = self.buffer.as_ref();
38         data[field::CUR_HOP_LIMIT]
39     }
40 
41     /// Return the Router Advertisement flags.
42     #[inline]
router_flags(&self) -> RouterFlags43     pub fn router_flags(&self) -> RouterFlags {
44         let data = self.buffer.as_ref();
45         RouterFlags::from_bits_truncate(data[field::ROUTER_FLAGS])
46     }
47 
48     /// Return the router lifetime field.
49     #[inline]
router_lifetime(&self) -> Duration50     pub fn router_lifetime(&self) -> Duration {
51         let data = self.buffer.as_ref();
52         Duration::from_secs(NetworkEndian::read_u16(&data[field::ROUTER_LT]) as u64)
53     }
54 
55     /// Return the reachable time field.
56     #[inline]
reachable_time(&self) -> Duration57     pub fn reachable_time(&self) -> Duration {
58         let data = self.buffer.as_ref();
59         Duration::from_millis(NetworkEndian::read_u32(&data[field::REACHABLE_TM]) as u64)
60     }
61 
62     /// Return the retransmit time field.
63     #[inline]
retrans_time(&self) -> Duration64     pub fn retrans_time(&self) -> Duration {
65         let data = self.buffer.as_ref();
66         Duration::from_millis(NetworkEndian::read_u32(&data[field::RETRANS_TM]) as u64)
67     }
68 }
69 
70 /// Common getters for the [Neighbor Solicitation], [Neighbor Advertisement], and
71 /// [Redirect] message types.
72 ///
73 /// [Neighbor Solicitation]: https://tools.ietf.org/html/rfc4861#section-4.3
74 /// [Neighbor Advertisement]: https://tools.ietf.org/html/rfc4861#section-4.4
75 /// [Redirect]: https://tools.ietf.org/html/rfc4861#section-4.5
76 impl<T: AsRef<[u8]>> Packet<T> {
77     /// Return the target address field.
78     #[inline]
target_addr(&self) -> Ipv6Address79     pub fn target_addr(&self) -> Ipv6Address {
80         let data = self.buffer.as_ref();
81         Ipv6Address::from_bytes(&data[field::TARGET_ADDR])
82     }
83 }
84 
85 /// Getters for the Neighbor Solicitation message header.
86 /// See [RFC 4861 § 4.3].
87 ///
88 /// [RFC 4861 § 4.3]: https://tools.ietf.org/html/rfc4861#section-4.3
89 impl<T: AsRef<[u8]>> Packet<T> {
90     /// Return the Neighbor Solicitation flags.
91     #[inline]
neighbor_flags(&self) -> NeighborFlags92     pub fn neighbor_flags(&self) -> NeighborFlags {
93         let data = self.buffer.as_ref();
94         NeighborFlags::from_bits_truncate(data[field::NEIGH_FLAGS])
95     }
96 }
97 
98 /// Getters for the Redirect message header.
99 /// See [RFC 4861 § 4.5].
100 ///
101 /// [RFC 4861 § 4.5]: https://tools.ietf.org/html/rfc4861#section-4.5
102 impl<T: AsRef<[u8]>> Packet<T> {
103     /// Return the destination address field.
104     #[inline]
dest_addr(&self) -> Ipv6Address105     pub fn dest_addr(&self) -> Ipv6Address {
106         let data = self.buffer.as_ref();
107         Ipv6Address::from_bytes(&data[field::DEST_ADDR])
108     }
109 }
110 
111 /// Setters for the Router Advertisement message header.
112 /// See [RFC 4861 § 4.2].
113 ///
114 /// [RFC 4861 § 4.2]: https://tools.ietf.org/html/rfc4861#section-4.2
115 impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
116     /// Set the current hop limit field.
117     #[inline]
set_current_hop_limit(&mut self, value: u8)118     pub fn set_current_hop_limit(&mut self, value: u8) {
119         let data = self.buffer.as_mut();
120         data[field::CUR_HOP_LIMIT] = value;
121     }
122 
123     /// Set the Router Advertisement flags.
124     #[inline]
set_router_flags(&mut self, flags: RouterFlags)125     pub fn set_router_flags(&mut self, flags: RouterFlags) {
126         self.buffer.as_mut()[field::ROUTER_FLAGS] = flags.bits();
127     }
128 
129     /// Set the router lifetime field.
130     #[inline]
set_router_lifetime(&mut self, value: Duration)131     pub fn set_router_lifetime(&mut self, value: Duration) {
132         let data = self.buffer.as_mut();
133         NetworkEndian::write_u16(&mut data[field::ROUTER_LT], value.secs() as u16);
134     }
135 
136     /// Set the reachable time field.
137     #[inline]
set_reachable_time(&mut self, value: Duration)138     pub fn set_reachable_time(&mut self, value: Duration) {
139         let data = self.buffer.as_mut();
140         NetworkEndian::write_u32(&mut data[field::REACHABLE_TM], value.total_millis() as u32);
141     }
142 
143     /// Set the retransmit time field.
144     #[inline]
set_retrans_time(&mut self, value: Duration)145     pub fn set_retrans_time(&mut self, value: Duration) {
146         let data = self.buffer.as_mut();
147         NetworkEndian::write_u32(&mut data[field::RETRANS_TM], value.total_millis() as u32);
148     }
149 }
150 
151 /// Common setters for the [Neighbor Solicitation], [Neighbor Advertisement], and
152 /// [Redirect] message types.
153 ///
154 /// [Neighbor Solicitation]: https://tools.ietf.org/html/rfc4861#section-4.3
155 /// [Neighbor Advertisement]: https://tools.ietf.org/html/rfc4861#section-4.4
156 /// [Redirect]: https://tools.ietf.org/html/rfc4861#section-4.5
157 impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
158     /// Set the target address field.
159     #[inline]
set_target_addr(&mut self, value: Ipv6Address)160     pub fn set_target_addr(&mut self, value: Ipv6Address) {
161         let data = self.buffer.as_mut();
162         data[field::TARGET_ADDR].copy_from_slice(value.as_bytes());
163     }
164 }
165 
166 /// Setters for the Neighbor Solicitation message header.
167 /// See [RFC 4861 § 4.3].
168 ///
169 /// [RFC 4861 § 4.3]: https://tools.ietf.org/html/rfc4861#section-4.3
170 impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
171     /// Set the Neighbor Solicitation flags.
172     #[inline]
set_neighbor_flags(&mut self, flags: NeighborFlags)173     pub fn set_neighbor_flags(&mut self, flags: NeighborFlags) {
174         self.buffer.as_mut()[field::NEIGH_FLAGS] = flags.bits();
175     }
176 }
177 
178 /// Setters for the Redirect message header.
179 /// See [RFC 4861 § 4.5].
180 ///
181 /// [RFC 4861 § 4.5]: https://tools.ietf.org/html/rfc4861#section-4.5
182 impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
183     /// Set the destination address field.
184     #[inline]
set_dest_addr(&mut self, value: Ipv6Address)185     pub fn set_dest_addr(&mut self, value: Ipv6Address) {
186         let data = self.buffer.as_mut();
187         data[field::DEST_ADDR].copy_from_slice(value.as_bytes());
188     }
189 }
190 
191 /// A high-level representation of an Neighbor Discovery packet header.
192 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
193 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
194 pub enum Repr<'a> {
195     RouterSolicit {
196         lladdr: Option<RawHardwareAddress>,
197     },
198     RouterAdvert {
199         hop_limit: u8,
200         flags: RouterFlags,
201         router_lifetime: Duration,
202         reachable_time: Duration,
203         retrans_time: Duration,
204         lladdr: Option<RawHardwareAddress>,
205         mtu: Option<u32>,
206         prefix_info: Option<NdiscPrefixInformation>,
207     },
208     NeighborSolicit {
209         target_addr: Ipv6Address,
210         lladdr: Option<RawHardwareAddress>,
211     },
212     NeighborAdvert {
213         flags: NeighborFlags,
214         target_addr: Ipv6Address,
215         lladdr: Option<RawHardwareAddress>,
216     },
217     Redirect {
218         target_addr: Ipv6Address,
219         dest_addr: Ipv6Address,
220         lladdr: Option<RawHardwareAddress>,
221         redirected_hdr: Option<NdiscRedirectedHeader<'a>>,
222     },
223 }
224 
225 impl<'a> Repr<'a> {
226     /// Parse an NDISC packet and return a high-level representation of the
227     /// packet.
228     #[allow(clippy::single_match)]
parse<T>(packet: &Packet<&'a T>) -> Result<Repr<'a>> where T: AsRef<[u8]> + ?Sized,229     pub fn parse<T>(packet: &Packet<&'a T>) -> Result<Repr<'a>>
230     where
231         T: AsRef<[u8]> + ?Sized,
232     {
233         fn foreach_option<'a>(
234             payload: &'a [u8],
235             mut f: impl FnMut(NdiscOptionRepr<'a>) -> Result<()>,
236         ) -> Result<()> {
237             let mut offset = 0;
238             while payload.len() > offset {
239                 let pkt = NdiscOption::new_checked(&payload[offset..])?;
240 
241                 // If an option doesn't parse, ignore it and still parse the others.
242                 if let Ok(opt) = NdiscOptionRepr::parse(&pkt) {
243                     f(opt)?;
244                 }
245 
246                 let len = pkt.data_len() as usize * 8;
247                 if len == 0 {
248                     return Err(Error);
249                 }
250                 offset += len;
251             }
252             Ok(())
253         }
254 
255         match packet.msg_type() {
256             Message::RouterSolicit => {
257                 let mut lladdr = None;
258                 foreach_option(packet.payload(), |opt| {
259                     match opt {
260                         NdiscOptionRepr::SourceLinkLayerAddr(addr) => lladdr = Some(addr),
261                         _ => {}
262                     }
263                     Ok(())
264                 })?;
265                 Ok(Repr::RouterSolicit { lladdr })
266             }
267             Message::RouterAdvert => {
268                 let (mut lladdr, mut mtu, mut prefix_info) = (None, None, None);
269                 foreach_option(packet.payload(), |opt| {
270                     match opt {
271                         NdiscOptionRepr::SourceLinkLayerAddr(addr) => lladdr = Some(addr),
272                         NdiscOptionRepr::Mtu(val) => mtu = Some(val),
273                         NdiscOptionRepr::PrefixInformation(info) => prefix_info = Some(info),
274                         _ => {}
275                     }
276                     Ok(())
277                 })?;
278                 Ok(Repr::RouterAdvert {
279                     hop_limit: packet.current_hop_limit(),
280                     flags: packet.router_flags(),
281                     router_lifetime: packet.router_lifetime(),
282                     reachable_time: packet.reachable_time(),
283                     retrans_time: packet.retrans_time(),
284                     lladdr,
285                     mtu,
286                     prefix_info,
287                 })
288             }
289             Message::NeighborSolicit => {
290                 let mut lladdr = None;
291                 foreach_option(packet.payload(), |opt| {
292                     match opt {
293                         NdiscOptionRepr::SourceLinkLayerAddr(addr) => lladdr = Some(addr),
294                         _ => {}
295                     }
296                     Ok(())
297                 })?;
298                 Ok(Repr::NeighborSolicit {
299                     target_addr: packet.target_addr(),
300                     lladdr,
301                 })
302             }
303             Message::NeighborAdvert => {
304                 let mut lladdr = None;
305                 foreach_option(packet.payload(), |opt| {
306                     match opt {
307                         NdiscOptionRepr::TargetLinkLayerAddr(addr) => lladdr = Some(addr),
308                         _ => {}
309                     }
310                     Ok(())
311                 })?;
312                 Ok(Repr::NeighborAdvert {
313                     flags: packet.neighbor_flags(),
314                     target_addr: packet.target_addr(),
315                     lladdr,
316                 })
317             }
318             Message::Redirect => {
319                 let (mut lladdr, mut redirected_hdr) = (None, None);
320 
321                 foreach_option(packet.payload(), |opt| {
322                     match opt {
323                         NdiscOptionRepr::SourceLinkLayerAddr(addr) => lladdr = Some(addr),
324                         NdiscOptionRepr::RedirectedHeader(rh) => redirected_hdr = Some(rh),
325                         _ => {}
326                     }
327                     Ok(())
328                 })?;
329                 Ok(Repr::Redirect {
330                     target_addr: packet.target_addr(),
331                     dest_addr: packet.dest_addr(),
332                     lladdr,
333                     redirected_hdr,
334                 })
335             }
336             _ => Err(Error),
337         }
338     }
339 
buffer_len(&self) -> usize340     pub const fn buffer_len(&self) -> usize {
341         match self {
342             &Repr::RouterSolicit { lladdr } => match lladdr {
343                 Some(addr) => {
344                     field::UNUSED.end + { NdiscOptionRepr::SourceLinkLayerAddr(addr).buffer_len() }
345                 }
346                 None => field::UNUSED.end,
347             },
348             &Repr::RouterAdvert {
349                 lladdr,
350                 mtu,
351                 prefix_info,
352                 ..
353             } => {
354                 let mut offset = 0;
355                 if let Some(lladdr) = lladdr {
356                     offset += NdiscOptionRepr::TargetLinkLayerAddr(lladdr).buffer_len();
357                 }
358                 if let Some(mtu) = mtu {
359                     offset += NdiscOptionRepr::Mtu(mtu).buffer_len();
360                 }
361                 if let Some(prefix_info) = prefix_info {
362                     offset += NdiscOptionRepr::PrefixInformation(prefix_info).buffer_len();
363                 }
364                 field::RETRANS_TM.end + offset
365             }
366             &Repr::NeighborSolicit { lladdr, .. } | &Repr::NeighborAdvert { lladdr, .. } => {
367                 let mut offset = field::TARGET_ADDR.end;
368                 if let Some(lladdr) = lladdr {
369                     offset += NdiscOptionRepr::SourceLinkLayerAddr(lladdr).buffer_len();
370                 }
371                 offset
372             }
373             &Repr::Redirect {
374                 lladdr,
375                 redirected_hdr,
376                 ..
377             } => {
378                 let mut offset = field::DEST_ADDR.end;
379                 if let Some(lladdr) = lladdr {
380                     offset += NdiscOptionRepr::TargetLinkLayerAddr(lladdr).buffer_len();
381                 }
382                 if let Some(NdiscRedirectedHeader { header, data }) = redirected_hdr {
383                     offset +=
384                         NdiscOptionRepr::RedirectedHeader(NdiscRedirectedHeader { header, data })
385                             .buffer_len();
386                 }
387                 offset
388             }
389         }
390     }
391 
emit<T>(&self, packet: &mut Packet<&mut T>) where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,392     pub fn emit<T>(&self, packet: &mut Packet<&mut T>)
393     where
394         T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
395     {
396         match *self {
397             Repr::RouterSolicit { lladdr } => {
398                 packet.set_msg_type(Message::RouterSolicit);
399                 packet.set_msg_code(0);
400                 packet.clear_reserved();
401                 if let Some(lladdr) = lladdr {
402                     let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
403                     NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt);
404                 }
405             }
406 
407             Repr::RouterAdvert {
408                 hop_limit,
409                 flags,
410                 router_lifetime,
411                 reachable_time,
412                 retrans_time,
413                 lladdr,
414                 mtu,
415                 prefix_info,
416             } => {
417                 packet.set_msg_type(Message::RouterAdvert);
418                 packet.set_msg_code(0);
419                 packet.set_current_hop_limit(hop_limit);
420                 packet.set_router_flags(flags);
421                 packet.set_router_lifetime(router_lifetime);
422                 packet.set_reachable_time(reachable_time);
423                 packet.set_retrans_time(retrans_time);
424                 let mut offset = 0;
425                 if let Some(lladdr) = lladdr {
426                     let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
427                     let opt = NdiscOptionRepr::SourceLinkLayerAddr(lladdr);
428                     opt.emit(&mut opt_pkt);
429                     offset += opt.buffer_len();
430                 }
431                 if let Some(mtu) = mtu {
432                     let mut opt_pkt =
433                         NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]);
434                     NdiscOptionRepr::Mtu(mtu).emit(&mut opt_pkt);
435                     offset += NdiscOptionRepr::Mtu(mtu).buffer_len();
436                 }
437                 if let Some(prefix_info) = prefix_info {
438                     let mut opt_pkt =
439                         NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]);
440                     NdiscOptionRepr::PrefixInformation(prefix_info).emit(&mut opt_pkt)
441                 }
442             }
443 
444             Repr::NeighborSolicit {
445                 target_addr,
446                 lladdr,
447             } => {
448                 packet.set_msg_type(Message::NeighborSolicit);
449                 packet.set_msg_code(0);
450                 packet.clear_reserved();
451                 packet.set_target_addr(target_addr);
452                 if let Some(lladdr) = lladdr {
453                     let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
454                     NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt);
455                 }
456             }
457 
458             Repr::NeighborAdvert {
459                 flags,
460                 target_addr,
461                 lladdr,
462             } => {
463                 packet.set_msg_type(Message::NeighborAdvert);
464                 packet.set_msg_code(0);
465                 packet.clear_reserved();
466                 packet.set_neighbor_flags(flags);
467                 packet.set_target_addr(target_addr);
468                 if let Some(lladdr) = lladdr {
469                     let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
470                     NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt);
471                 }
472             }
473 
474             Repr::Redirect {
475                 target_addr,
476                 dest_addr,
477                 lladdr,
478                 redirected_hdr,
479             } => {
480                 packet.set_msg_type(Message::Redirect);
481                 packet.set_msg_code(0);
482                 packet.clear_reserved();
483                 packet.set_target_addr(target_addr);
484                 packet.set_dest_addr(dest_addr);
485                 let offset = match lladdr {
486                     Some(lladdr) => {
487                         let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
488                         NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt);
489                         NdiscOptionRepr::TargetLinkLayerAddr(lladdr).buffer_len()
490                     }
491                     None => 0,
492                 };
493                 if let Some(redirected_hdr) = redirected_hdr {
494                     let mut opt_pkt =
495                         NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]);
496                     NdiscOptionRepr::RedirectedHeader(redirected_hdr).emit(&mut opt_pkt);
497                 }
498             }
499         }
500     }
501 }
502 
503 #[cfg(feature = "medium-ethernet")]
504 #[cfg(test)]
505 mod test {
506     use super::*;
507     use crate::phy::ChecksumCapabilities;
508     use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2};
509     use crate::wire::EthernetAddress;
510     use crate::wire::Icmpv6Repr;
511 
512     static ROUTER_ADVERT_BYTES: [u8; 24] = [
513         0x86, 0x00, 0xa9, 0xde, 0x40, 0x80, 0x03, 0x84, 0x00, 0x00, 0x03, 0x84, 0x00, 0x00, 0x03,
514         0x84, 0x01, 0x01, 0x52, 0x54, 0x00, 0x12, 0x34, 0x56,
515     ];
516     static SOURCE_LINK_LAYER_OPT: [u8; 8] = [0x01, 0x01, 0x52, 0x54, 0x00, 0x12, 0x34, 0x56];
517 
create_repr<'a>() -> Icmpv6Repr<'a>518     fn create_repr<'a>() -> Icmpv6Repr<'a> {
519         Icmpv6Repr::Ndisc(Repr::RouterAdvert {
520             hop_limit: 64,
521             flags: RouterFlags::MANAGED,
522             router_lifetime: Duration::from_secs(900),
523             reachable_time: Duration::from_millis(900),
524             retrans_time: Duration::from_millis(900),
525             lladdr: Some(EthernetAddress([0x52, 0x54, 0x00, 0x12, 0x34, 0x56]).into()),
526             mtu: None,
527             prefix_info: None,
528         })
529     }
530 
531     #[test]
test_router_advert_deconstruct()532     fn test_router_advert_deconstruct() {
533         let packet = Packet::new_unchecked(&ROUTER_ADVERT_BYTES[..]);
534         assert_eq!(packet.msg_type(), Message::RouterAdvert);
535         assert_eq!(packet.msg_code(), 0);
536         assert_eq!(packet.current_hop_limit(), 64);
537         assert_eq!(packet.router_flags(), RouterFlags::MANAGED);
538         assert_eq!(packet.router_lifetime(), Duration::from_secs(900));
539         assert_eq!(packet.reachable_time(), Duration::from_millis(900));
540         assert_eq!(packet.retrans_time(), Duration::from_millis(900));
541         assert_eq!(packet.payload(), &SOURCE_LINK_LAYER_OPT[..]);
542     }
543 
544     #[test]
test_router_advert_construct()545     fn test_router_advert_construct() {
546         let mut bytes = vec![0x0; 24];
547         let mut packet = Packet::new_unchecked(&mut bytes);
548         packet.set_msg_type(Message::RouterAdvert);
549         packet.set_msg_code(0);
550         packet.set_current_hop_limit(64);
551         packet.set_router_flags(RouterFlags::MANAGED);
552         packet.set_router_lifetime(Duration::from_secs(900));
553         packet.set_reachable_time(Duration::from_millis(900));
554         packet.set_retrans_time(Duration::from_millis(900));
555         packet
556             .payload_mut()
557             .copy_from_slice(&SOURCE_LINK_LAYER_OPT[..]);
558         packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2);
559         assert_eq!(&*packet.into_inner(), &ROUTER_ADVERT_BYTES[..]);
560     }
561 
562     #[test]
test_router_advert_repr_parse()563     fn test_router_advert_repr_parse() {
564         let packet = Packet::new_unchecked(&ROUTER_ADVERT_BYTES[..]);
565         assert_eq!(
566             Icmpv6Repr::parse(
567                 &MOCK_IP_ADDR_1,
568                 &MOCK_IP_ADDR_2,
569                 &packet,
570                 &ChecksumCapabilities::default()
571             )
572             .unwrap(),
573             create_repr()
574         );
575     }
576 
577     #[test]
test_router_advert_repr_emit()578     fn test_router_advert_repr_emit() {
579         let mut bytes = vec![0x2a; 24];
580         let mut packet = Packet::new_unchecked(&mut bytes[..]);
581         create_repr().emit(
582             &MOCK_IP_ADDR_1,
583             &MOCK_IP_ADDR_2,
584             &mut packet,
585             &ChecksumCapabilities::default(),
586         );
587         assert_eq!(&*packet.into_inner(), &ROUTER_ADVERT_BYTES[..]);
588     }
589 }
590