smoltcp/wire/
ndisc.rs

1use bitflags::bitflags;
2use byteorder::{ByteOrder, NetworkEndian};
3
4use super::{Error, Result};
5use crate::time::Duration;
6use crate::wire::icmpv6::{field, Message, Packet};
7use crate::wire::RawHardwareAddress;
8use crate::wire::{Ipv6Address, Ipv6AddressExt};
9use crate::wire::{NdiscOption, NdiscOptionRepr};
10use crate::wire::{NdiscPrefixInformation, NdiscRedirectedHeader};
11
12bitflags! {
13    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
14    pub struct RouterFlags: u8 {
15        const MANAGED = 0b10000000;
16        const OTHER   = 0b01000000;
17    }
18}
19
20bitflags! {
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
33impl<T: AsRef<[u8]>> Packet<T> {
34    /// Return the current hop limit field.
35    #[inline]
36    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]
43    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]
50    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]
57    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]
64    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
76impl<T: AsRef<[u8]>> Packet<T> {
77    /// Return the target address field.
78    #[inline]
79    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
89impl<T: AsRef<[u8]>> Packet<T> {
90    /// Return the Neighbor Solicitation flags.
91    #[inline]
92    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
102impl<T: AsRef<[u8]>> Packet<T> {
103    /// Return the destination address field.
104    #[inline]
105    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
115impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
116    /// Set the current hop limit field.
117    #[inline]
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]
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]
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]
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]
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
157impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
158    /// Set the target address field.
159    #[inline]
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.octets());
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
170impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
171    /// Set the Neighbor Solicitation flags.
172    #[inline]
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
182impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
183    /// Set the destination address field.
184    #[inline]
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.octets());
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))]
194pub 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
225impl<'a> Repr<'a> {
226    /// Parse an NDISC packet and return a high-level representation of the
227    /// packet.
228    #[allow(clippy::single_match)]
229    pub fn parse<T>(packet: &Packet<&'a T>) -> Result<Repr<'a>>
230    where
231        T: AsRef<[u8]> + ?Sized,
232    {
233        packet.check_len()?;
234
235        let (mut src_ll_addr, mut mtu, mut prefix_info, mut target_ll_addr, mut redirected_hdr) =
236            (None, None, None, None, None);
237
238        let mut offset = 0;
239        while packet.payload().len() > offset {
240            let pkt = NdiscOption::new_checked(&packet.payload()[offset..])?;
241
242            // If an option doesn't parse, ignore it and still parse the others.
243            if let Ok(opt) = NdiscOptionRepr::parse(&pkt) {
244                match opt {
245                    NdiscOptionRepr::SourceLinkLayerAddr(addr) => src_ll_addr = Some(addr),
246                    NdiscOptionRepr::TargetLinkLayerAddr(addr) => target_ll_addr = Some(addr),
247                    NdiscOptionRepr::PrefixInformation(prefix) => prefix_info = Some(prefix),
248                    NdiscOptionRepr::RedirectedHeader(redirect) => redirected_hdr = Some(redirect),
249                    NdiscOptionRepr::Mtu(m) => mtu = Some(m),
250                    _ => {}
251                }
252            }
253
254            let len = pkt.data_len() as usize * 8;
255            if len == 0 {
256                return Err(Error);
257            }
258            offset += len;
259        }
260
261        match packet.msg_type() {
262            Message::RouterSolicit => Ok(Repr::RouterSolicit {
263                lladdr: src_ll_addr,
264            }),
265            Message::RouterAdvert => Ok(Repr::RouterAdvert {
266                hop_limit: packet.current_hop_limit(),
267                flags: packet.router_flags(),
268                router_lifetime: packet.router_lifetime(),
269                reachable_time: packet.reachable_time(),
270                retrans_time: packet.retrans_time(),
271                lladdr: src_ll_addr,
272                mtu,
273                prefix_info,
274            }),
275            Message::NeighborSolicit => Ok(Repr::NeighborSolicit {
276                target_addr: packet.target_addr(),
277                lladdr: src_ll_addr,
278            }),
279            Message::NeighborAdvert => Ok(Repr::NeighborAdvert {
280                flags: packet.neighbor_flags(),
281                target_addr: packet.target_addr(),
282                lladdr: target_ll_addr,
283            }),
284            Message::Redirect => Ok(Repr::Redirect {
285                target_addr: packet.target_addr(),
286                dest_addr: packet.dest_addr(),
287                lladdr: src_ll_addr,
288                redirected_hdr,
289            }),
290            _ => Err(Error),
291        }
292    }
293
294    pub const fn buffer_len(&self) -> usize {
295        match self {
296            &Repr::RouterSolicit { lladdr } => match lladdr {
297                Some(addr) => {
298                    field::UNUSED.end + { NdiscOptionRepr::SourceLinkLayerAddr(addr).buffer_len() }
299                }
300                None => field::UNUSED.end,
301            },
302            &Repr::RouterAdvert {
303                lladdr,
304                mtu,
305                prefix_info,
306                ..
307            } => {
308                let mut offset = 0;
309                if let Some(lladdr) = lladdr {
310                    offset += NdiscOptionRepr::TargetLinkLayerAddr(lladdr).buffer_len();
311                }
312                if let Some(mtu) = mtu {
313                    offset += NdiscOptionRepr::Mtu(mtu).buffer_len();
314                }
315                if let Some(prefix_info) = prefix_info {
316                    offset += NdiscOptionRepr::PrefixInformation(prefix_info).buffer_len();
317                }
318                field::RETRANS_TM.end + offset
319            }
320            &Repr::NeighborSolicit { lladdr, .. } | &Repr::NeighborAdvert { lladdr, .. } => {
321                let mut offset = field::TARGET_ADDR.end;
322                if let Some(lladdr) = lladdr {
323                    offset += NdiscOptionRepr::SourceLinkLayerAddr(lladdr).buffer_len();
324                }
325                offset
326            }
327            &Repr::Redirect {
328                lladdr,
329                redirected_hdr,
330                ..
331            } => {
332                let mut offset = field::DEST_ADDR.end;
333                if let Some(lladdr) = lladdr {
334                    offset += NdiscOptionRepr::TargetLinkLayerAddr(lladdr).buffer_len();
335                }
336                if let Some(NdiscRedirectedHeader { header, data }) = redirected_hdr {
337                    offset +=
338                        NdiscOptionRepr::RedirectedHeader(NdiscRedirectedHeader { header, data })
339                            .buffer_len();
340                }
341                offset
342            }
343        }
344    }
345
346    pub fn emit<T>(&self, packet: &mut Packet<&mut T>)
347    where
348        T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
349    {
350        match *self {
351            Repr::RouterSolicit { lladdr } => {
352                packet.set_msg_type(Message::RouterSolicit);
353                packet.set_msg_code(0);
354                packet.clear_reserved();
355                if let Some(lladdr) = lladdr {
356                    let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
357                    NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt);
358                }
359            }
360
361            Repr::RouterAdvert {
362                hop_limit,
363                flags,
364                router_lifetime,
365                reachable_time,
366                retrans_time,
367                lladdr,
368                mtu,
369                prefix_info,
370            } => {
371                packet.set_msg_type(Message::RouterAdvert);
372                packet.set_msg_code(0);
373                packet.set_current_hop_limit(hop_limit);
374                packet.set_router_flags(flags);
375                packet.set_router_lifetime(router_lifetime);
376                packet.set_reachable_time(reachable_time);
377                packet.set_retrans_time(retrans_time);
378                let mut offset = 0;
379                if let Some(lladdr) = lladdr {
380                    let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
381                    let opt = NdiscOptionRepr::SourceLinkLayerAddr(lladdr);
382                    opt.emit(&mut opt_pkt);
383                    offset += opt.buffer_len();
384                }
385                if let Some(mtu) = mtu {
386                    let mut opt_pkt =
387                        NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]);
388                    NdiscOptionRepr::Mtu(mtu).emit(&mut opt_pkt);
389                    offset += NdiscOptionRepr::Mtu(mtu).buffer_len();
390                }
391                if let Some(prefix_info) = prefix_info {
392                    let mut opt_pkt =
393                        NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]);
394                    NdiscOptionRepr::PrefixInformation(prefix_info).emit(&mut opt_pkt)
395                }
396            }
397
398            Repr::NeighborSolicit {
399                target_addr,
400                lladdr,
401            } => {
402                packet.set_msg_type(Message::NeighborSolicit);
403                packet.set_msg_code(0);
404                packet.clear_reserved();
405                packet.set_target_addr(target_addr);
406                if let Some(lladdr) = lladdr {
407                    let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
408                    NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt);
409                }
410            }
411
412            Repr::NeighborAdvert {
413                flags,
414                target_addr,
415                lladdr,
416            } => {
417                packet.set_msg_type(Message::NeighborAdvert);
418                packet.set_msg_code(0);
419                packet.clear_reserved();
420                packet.set_neighbor_flags(flags);
421                packet.set_target_addr(target_addr);
422                if let Some(lladdr) = lladdr {
423                    let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
424                    NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt);
425                }
426            }
427
428            Repr::Redirect {
429                target_addr,
430                dest_addr,
431                lladdr,
432                redirected_hdr,
433            } => {
434                packet.set_msg_type(Message::Redirect);
435                packet.set_msg_code(0);
436                packet.clear_reserved();
437                packet.set_target_addr(target_addr);
438                packet.set_dest_addr(dest_addr);
439                let offset = match lladdr {
440                    Some(lladdr) => {
441                        let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
442                        NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt);
443                        NdiscOptionRepr::TargetLinkLayerAddr(lladdr).buffer_len()
444                    }
445                    None => 0,
446                };
447                if let Some(redirected_hdr) = redirected_hdr {
448                    let mut opt_pkt =
449                        NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]);
450                    NdiscOptionRepr::RedirectedHeader(redirected_hdr).emit(&mut opt_pkt);
451                }
452            }
453        }
454    }
455}
456
457#[cfg(feature = "medium-ethernet")]
458#[cfg(test)]
459mod test {
460    use super::*;
461    use crate::phy::ChecksumCapabilities;
462    use crate::wire::EthernetAddress;
463    use crate::wire::Icmpv6Repr;
464
465    const MOCK_IP_ADDR_1: Ipv6Address = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
466    const MOCK_IP_ADDR_2: Ipv6Address = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 2);
467
468    static ROUTER_ADVERT_BYTES: [u8; 24] = [
469        0x86, 0x00, 0xa9, 0xde, 0x40, 0x80, 0x03, 0x84, 0x00, 0x00, 0x03, 0x84, 0x00, 0x00, 0x03,
470        0x84, 0x01, 0x01, 0x52, 0x54, 0x00, 0x12, 0x34, 0x56,
471    ];
472    static SOURCE_LINK_LAYER_OPT: [u8; 8] = [0x01, 0x01, 0x52, 0x54, 0x00, 0x12, 0x34, 0x56];
473
474    fn create_repr<'a>() -> Icmpv6Repr<'a> {
475        Icmpv6Repr::Ndisc(Repr::RouterAdvert {
476            hop_limit: 64,
477            flags: RouterFlags::MANAGED,
478            router_lifetime: Duration::from_secs(900),
479            reachable_time: Duration::from_millis(900),
480            retrans_time: Duration::from_millis(900),
481            lladdr: Some(EthernetAddress([0x52, 0x54, 0x00, 0x12, 0x34, 0x56]).into()),
482            mtu: None,
483            prefix_info: None,
484        })
485    }
486
487    #[test]
488    fn test_router_advert_deconstruct() {
489        let packet = Packet::new_unchecked(&ROUTER_ADVERT_BYTES[..]);
490        assert_eq!(packet.msg_type(), Message::RouterAdvert);
491        assert_eq!(packet.msg_code(), 0);
492        assert_eq!(packet.current_hop_limit(), 64);
493        assert_eq!(packet.router_flags(), RouterFlags::MANAGED);
494        assert_eq!(packet.router_lifetime(), Duration::from_secs(900));
495        assert_eq!(packet.reachable_time(), Duration::from_millis(900));
496        assert_eq!(packet.retrans_time(), Duration::from_millis(900));
497        assert_eq!(packet.payload(), &SOURCE_LINK_LAYER_OPT[..]);
498    }
499
500    #[test]
501    fn test_router_advert_construct() {
502        let mut bytes = vec![0x0; 24];
503        let mut packet = Packet::new_unchecked(&mut bytes);
504        packet.set_msg_type(Message::RouterAdvert);
505        packet.set_msg_code(0);
506        packet.set_current_hop_limit(64);
507        packet.set_router_flags(RouterFlags::MANAGED);
508        packet.set_router_lifetime(Duration::from_secs(900));
509        packet.set_reachable_time(Duration::from_millis(900));
510        packet.set_retrans_time(Duration::from_millis(900));
511        packet
512            .payload_mut()
513            .copy_from_slice(&SOURCE_LINK_LAYER_OPT[..]);
514        packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2);
515        assert_eq!(&*packet.into_inner(), &ROUTER_ADVERT_BYTES[..]);
516    }
517
518    #[test]
519    fn test_router_advert_repr_parse() {
520        let packet = Packet::new_unchecked(&ROUTER_ADVERT_BYTES[..]);
521        assert_eq!(
522            Icmpv6Repr::parse(
523                &MOCK_IP_ADDR_1,
524                &MOCK_IP_ADDR_2,
525                &packet,
526                &ChecksumCapabilities::default()
527            )
528            .unwrap(),
529            create_repr()
530        );
531    }
532
533    #[test]
534    fn test_router_advert_repr_emit() {
535        let mut bytes = [0x2a; 24];
536        let mut packet = Packet::new_unchecked(&mut bytes[..]);
537        create_repr().emit(
538            &MOCK_IP_ADDR_1,
539            &MOCK_IP_ADDR_2,
540            &mut packet,
541            &ChecksumCapabilities::default(),
542        );
543        assert_eq!(&*packet.into_inner(), &ROUTER_ADVERT_BYTES[..]);
544    }
545}