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
29impl<T: AsRef<[u8]>> Packet<T> {
34 #[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 #[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 #[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 #[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 #[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
70impl<T: AsRef<[u8]>> Packet<T> {
77 #[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
85impl<T: AsRef<[u8]>> Packet<T> {
90 #[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
98impl<T: AsRef<[u8]>> Packet<T> {
103 #[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
111impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
116 #[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 #[inline]
125 pub fn set_router_flags(&mut self, flags: RouterFlags) {
126 self.buffer.as_mut()[field::ROUTER_FLAGS] = flags.bits();
127 }
128
129 #[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 #[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 #[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
151impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
158 #[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
166impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
171 #[inline]
173 pub fn set_neighbor_flags(&mut self, flags: NeighborFlags) {
174 self.buffer.as_mut()[field::NEIGH_FLAGS] = flags.bits();
175 }
176}
177
178impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
183 #[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#[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 #[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 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}