Add RawHardwareAddress, use it in wire ndisc.

This avoids wire needing to know what medium we're on.
This commit is contained in:
Dario Nieuwenhuis 2021-10-07 06:56:32 +02:00
parent fb2d0029d8
commit b4764e4973
10 changed files with 282 additions and 328 deletions

View File

@ -22,7 +22,7 @@ use smoltcp::{
}; };
macro_rules! send_icmp_ping { macro_rules! send_icmp_ping {
(v4, $repr_type:ident, $packet_type:ident, $ident:expr, $seq_no:expr, ( $repr_type:ident, $packet_type:ident, $ident:expr, $seq_no:expr,
$echo_payload:expr, $socket:expr, $remote_addr:expr ) => {{ $echo_payload:expr, $socket:expr, $remote_addr:expr ) => {{
let icmp_repr = $repr_type::EchoRequest { let icmp_repr = $repr_type::EchoRequest {
ident: $ident, ident: $ident,
@ -35,22 +35,6 @@ macro_rules! send_icmp_ping {
let icmp_packet = $packet_type::new_unchecked(icmp_payload); let icmp_packet = $packet_type::new_unchecked(icmp_payload);
(icmp_repr, icmp_packet) (icmp_repr, icmp_packet)
}}; }};
(v6, $repr_type:ident, $packet_type:ident, $ident:expr, $seq_no:expr,
$echo_payload:expr, $socket:expr, $remote_addr:expr ) => {{
let icmp_repr = $repr_type::EchoRequest {
ident: $ident,
seq_no: $seq_no,
data: &$echo_payload,
};
let icmp_payload = $socket
.send(icmp_repr.buffer_len(&Medium::Ethernet), $remote_addr)
.unwrap();
let icmp_packet = $packet_type::new_unchecked(icmp_payload);
(icmp_repr, icmp_packet)
}};
} }
macro_rules! get_icmp_pong { macro_rules! get_icmp_pong {
@ -186,7 +170,6 @@ fn main() {
match remote_addr { match remote_addr {
IpAddress::Ipv4(_) => { IpAddress::Ipv4(_) => {
let (icmp_repr, mut icmp_packet) = send_icmp_ping!( let (icmp_repr, mut icmp_packet) = send_icmp_ping!(
v4,
Icmpv4Repr, Icmpv4Repr,
Icmpv4Packet, Icmpv4Packet,
ident, ident,
@ -199,7 +182,6 @@ fn main() {
} }
IpAddress::Ipv6(_) => { IpAddress::Ipv6(_) => {
let (icmp_repr, mut icmp_packet) = send_icmp_ping!( let (icmp_repr, mut icmp_packet) = send_icmp_ping!(
v6,
Icmpv6Repr, Icmpv6Repr,
Icmpv6Packet, Icmpv6Packet,
ident, ident,
@ -213,7 +195,6 @@ fn main() {
&remote_addr, &remote_addr,
&mut icmp_packet, &mut icmp_packet,
&device_caps.checksum, &device_caps.checksum,
&device_caps.medium,
); );
} }
_ => unimplemented!(), _ => unimplemented!(),
@ -249,7 +230,6 @@ fn main() {
&src_ipv6, &src_ipv6,
&icmp_packet, &icmp_packet,
&device_caps.checksum, &device_caps.checksum,
&device_caps.medium,
) )
.unwrap(); .unwrap();
get_icmp_pong!( get_icmp_pong!(

View File

@ -389,7 +389,6 @@ impl<'a> IpPacket<'a> {
&_ip_repr.dst_addr(), &_ip_repr.dst_addr(),
&mut Icmpv6Packet::new_unchecked(payload), &mut Icmpv6Packet::new_unchecked(payload),
&caps.checksum, &caps.checksum,
&caps.medium,
), ),
#[cfg(feature = "socket-raw")] #[cfg(feature = "socket-raw")]
IpPacket::Raw((_, raw_packet)) => payload.copy_from_slice(raw_packet), IpPacket::Raw((_, raw_packet)) => payload.copy_from_slice(raw_packet),
@ -1198,7 +1197,7 @@ impl<'a> InterfaceInner<'a> {
header: ipv6_repr, header: ipv6_repr,
data: &payload[0..payload_len], data: &payload[0..payload_len],
}; };
Ok(self.icmpv6_reply(cx, ipv6_repr, icmpv6_reply_repr)) Ok(self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr))
} }
IpRepr::Unspecified { .. } => Err(Error::Unaddressable), IpRepr::Unspecified { .. } => Err(Error::Unaddressable),
IpRepr::Sixlowpan(_) => Err(Error::Malformed), // XXX(thvdveld): this is just wrong; IpRepr::Sixlowpan(_) => Err(Error::Malformed), // XXX(thvdveld): this is just wrong;
@ -1394,7 +1393,7 @@ impl<'a> InterfaceInner<'a> {
header: ipv6_repr, header: ipv6_repr,
data: &ip_payload[0..payload_len], data: &ip_payload[0..payload_len],
}; };
Ok(self.icmpv6_reply(cx, ipv6_repr, icmp_reply_repr)) Ok(self.icmpv6_reply(ipv6_repr, icmp_reply_repr))
} }
} }
} }
@ -1604,7 +1603,6 @@ impl<'a> InterfaceInner<'a> {
&ip_repr.dst_addr(), &ip_repr.dst_addr(),
&icmp_packet, &icmp_packet,
&cx.caps.checksum, &cx.caps.checksum,
&cx.caps.medium,
)?; )?;
#[cfg(feature = "socket-icmp")] #[cfg(feature = "socket-icmp")]
@ -1639,7 +1637,7 @@ impl<'a> InterfaceInner<'a> {
seq_no, seq_no,
data, data,
}; };
Ok(self.icmpv6_reply(cx, ipv6_repr, icmp_reply_repr)) Ok(self.icmpv6_reply(ipv6_repr, icmp_reply_repr))
} }
#[cfg(feature = "medium-ieee802154")] #[cfg(feature = "medium-ieee802154")]
IpRepr::Sixlowpan(sixlowpan_repr) => { IpRepr::Sixlowpan(sixlowpan_repr) => {
@ -1649,7 +1647,6 @@ impl<'a> InterfaceInner<'a> {
data, data,
}; };
Ok(self.icmpv6_reply( Ok(self.icmpv6_reply(
cx,
Ipv6Repr { Ipv6Repr {
src_addr: sixlowpan_repr.src_addr, src_addr: sixlowpan_repr.src_addr,
dst_addr: sixlowpan_repr.dst_addr, dst_addr: sixlowpan_repr.dst_addr,
@ -1714,23 +1711,24 @@ impl<'a> InterfaceInner<'a> {
flags, flags,
} => { } => {
let ip_addr = ip_repr.src_addr.into(); let ip_addr = ip_repr.src_addr.into();
match lladdr { if let Some(lladdr) = lladdr {
Some(lladdr) if lladdr.is_unicast() && target_addr.is_unicast() => { let lladdr = lladdr.parse(cx.caps.medium)?;
if flags.contains(NdiscNeighborFlags::OVERRIDE) if !lladdr.is_unicast() || !target_addr.is_unicast() {
|| !self return Err(Error::Malformed);
.neighbor_cache }
.as_mut() if flags.contains(NdiscNeighborFlags::OVERRIDE)
.unwrap() || !self
.lookup(&ip_addr, cx.now) .neighbor_cache
.found() .as_mut()
{ .unwrap()
self.neighbor_cache .lookup(&ip_addr, cx.now)
.as_mut() .found()
.unwrap() {
.fill(ip_addr, lladdr, cx.now) self.neighbor_cache
} .as_mut()
.unwrap()
.fill(ip_addr, lladdr, cx.now)
} }
_ => (),
} }
Ok(None) Ok(None)
} }
@ -1739,27 +1737,31 @@ impl<'a> InterfaceInner<'a> {
lladdr, lladdr,
.. ..
} => { } => {
match lladdr { if let Some(lladdr) = lladdr {
Some(lladdr) if lladdr.is_unicast() && target_addr.is_unicast() => self let lladdr = lladdr.parse(cx.caps.medium)?;
.neighbor_cache if !lladdr.is_unicast() || !target_addr.is_unicast() {
.as_mut() return Err(Error::Malformed);
.unwrap() }
.fill(ip_repr.src_addr.into(), lladdr, cx.now), self.neighbor_cache.as_mut().unwrap().fill(
_ => (), ip_repr.src_addr.into(),
lladdr,
cx.now,
);
} }
if self.has_solicited_node(ip_repr.dst_addr) && self.has_ip_addr(target_addr) { if self.has_solicited_node(ip_repr.dst_addr) && self.has_ip_addr(target_addr) {
let advert = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert { let advert = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert {
flags: NdiscNeighborFlags::SOLICITED, flags: NdiscNeighborFlags::SOLICITED,
target_addr, target_addr,
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
lladdr: Some(self.hardware_addr.unwrap()), lladdr: Some(self.hardware_addr.unwrap().into()),
}); });
let ip_repr = Ipv6Repr { let ip_repr = Ipv6Repr {
src_addr: target_addr, src_addr: target_addr,
dst_addr: ip_repr.src_addr, dst_addr: ip_repr.src_addr,
next_header: IpProtocol::Icmpv6, next_header: IpProtocol::Icmpv6,
hop_limit: 0xff, hop_limit: 0xff,
payload_len: advert.buffer_len(&cx.caps.medium), payload_len: advert.buffer_len(),
}; };
Ok(Some(IpPacket::Icmpv6((ip_repr, advert)))) Ok(Some(IpPacket::Icmpv6((ip_repr, advert))))
} else { } else {
@ -1917,7 +1919,6 @@ impl<'a> InterfaceInner<'a> {
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
fn icmpv6_reply<'frame, 'icmp: 'frame>( fn icmpv6_reply<'frame, 'icmp: 'frame>(
&self, &self,
cx: &Context,
ipv6_repr: Ipv6Repr, ipv6_repr: Ipv6Repr,
icmp_repr: Icmpv6Repr<'icmp>, icmp_repr: Icmpv6Repr<'icmp>,
) -> Option<IpPacket<'frame>> { ) -> Option<IpPacket<'frame>> {
@ -1926,7 +1927,7 @@ impl<'a> InterfaceInner<'a> {
src_addr: ipv6_repr.dst_addr, src_addr: ipv6_repr.dst_addr,
dst_addr: ipv6_repr.src_addr, dst_addr: ipv6_repr.src_addr,
next_header: IpProtocol::Icmpv6, next_header: IpProtocol::Icmpv6,
payload_len: icmp_repr.buffer_len(&cx.caps.medium), payload_len: icmp_repr.buffer_len(),
hop_limit: 64, hop_limit: 64,
}; };
Some(IpPacket::Icmpv6((ipv6_reply_repr, icmp_repr))) Some(IpPacket::Icmpv6((ipv6_reply_repr, icmp_repr)))
@ -1989,7 +1990,7 @@ impl<'a> InterfaceInner<'a> {
header: ipv6_repr, header: ipv6_repr,
data: &ip_payload[0..payload_len], data: &ip_payload[0..payload_len],
}; };
Ok(self.icmpv6_reply(cx, ipv6_repr, icmpv6_reply_repr)) Ok(self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr))
} }
#[cfg(feature = "proto-sixlowpan")] #[cfg(feature = "proto-sixlowpan")]
IpRepr::Sixlowpan(sixlowpan_repr) => { IpRepr::Sixlowpan(sixlowpan_repr) => {
@ -2011,7 +2012,7 @@ impl<'a> InterfaceInner<'a> {
header: ipv6_repr, header: ipv6_repr,
data: &ip_payload[0..payload_len], data: &ip_payload[0..payload_len],
}; };
Ok(self.icmpv6_reply(cx, ipv6_repr, icmpv6_reply_repr)) Ok(self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr))
} }
IpRepr::Unspecified { .. } => Err(Error::Unaddressable), IpRepr::Unspecified { .. } => Err(Error::Unaddressable),
} }
@ -2254,7 +2255,7 @@ impl<'a> InterfaceInner<'a> {
let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit { let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit {
target_addr: dst_addr, target_addr: dst_addr,
lladdr: self.hardware_addr, lladdr: Some(self.hardware_addr.unwrap().into()),
}); });
let packet = IpPacket::Icmpv6(( let packet = IpPacket::Icmpv6((
@ -2262,7 +2263,7 @@ impl<'a> InterfaceInner<'a> {
src_addr, src_addr,
dst_addr: dst_addr.solicited_node(), dst_addr: dst_addr.solicited_node(),
next_header: IpProtocol::Icmpv6, next_header: IpProtocol::Icmpv6,
payload_len: solicit.buffer_len(&cx.caps.medium), payload_len: solicit.buffer_len(),
hop_limit: 0xff, hop_limit: 0xff,
}, },
solicit, solicit,
@ -2422,7 +2423,7 @@ impl<'a> InterfaceInner<'a> {
tx_len += udp_repr.header_len() + payload.len(); tx_len += udp_repr.header_len() + payload.len();
} }
IpPacket::Icmpv6((_, icmp)) => { IpPacket::Icmpv6((_, icmp)) => {
tx_len += icmp.buffer_len(&cx.caps.medium); tx_len += icmp.buffer_len();
} }
_ => return Err(Error::Unrecognized), _ => return Err(Error::Unrecognized),
} }
@ -2466,7 +2467,6 @@ impl<'a> InterfaceInner<'a> {
&iphc_repr.dst_addr.into(), &iphc_repr.dst_addr.into(),
&mut icmp_packet, &mut icmp_packet,
&cx.caps.checksum, &cx.caps.checksum,
&cx.caps.medium,
); );
} }
_ => return Err(Error::Unrecognized), _ => return Err(Error::Unrecognized),
@ -3135,7 +3135,7 @@ mod test {
dst_addr: src_addr, dst_addr: src_addr,
next_header: IpProtocol::Icmpv6, next_header: IpProtocol::Icmpv6,
hop_limit: 64, hop_limit: 64,
payload_len: expected_icmp_repr.buffer_len(&Medium::Ethernet), payload_len: expected_icmp_repr.buffer_len(),
}; };
#[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
let expected_icmp_repr = Icmpv4Repr::DstUnreachable { let expected_icmp_repr = Icmpv4Repr::DstUnreachable {
@ -3157,7 +3157,7 @@ mod test {
// The expected packet does not exceed the IPV4_MIN_MTU // The expected packet does not exceed the IPV4_MIN_MTU
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
assert_eq!( assert_eq!(
expected_ip_repr.buffer_len() + expected_icmp_repr.buffer_len(&Medium::Ethernet), expected_ip_repr.buffer_len() + expected_icmp_repr.buffer_len(),
MIN_MTU MIN_MTU
); );
// The expected packet does not exceed the IPV4_MIN_MTU // The expected packet does not exceed the IPV4_MIN_MTU
@ -3267,7 +3267,7 @@ mod test {
dst_addr: local_ip_addr.solicited_node(), dst_addr: local_ip_addr.solicited_node(),
next_header: IpProtocol::Icmpv6, next_header: IpProtocol::Icmpv6,
hop_limit: 0xff, hop_limit: 0xff,
payload_len: solicit.buffer_len(&Medium::Ethernet), payload_len: solicit.buffer_len(),
}); });
let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes);
@ -3281,7 +3281,6 @@ mod test {
&local_ip_addr.solicited_node().into(), &local_ip_addr.solicited_node().into(),
&mut Icmpv6Packet::new_unchecked(&mut frame.payload_mut()[ip_repr.buffer_len()..]), &mut Icmpv6Packet::new_unchecked(&mut frame.payload_mut()[ip_repr.buffer_len()..]),
&ChecksumCapabilities::default(), &ChecksumCapabilities::default(),
&iface.device().capabilities().medium,
); );
} }
@ -3296,7 +3295,7 @@ mod test {
dst_addr: remote_ip_addr, dst_addr: remote_ip_addr,
next_header: IpProtocol::Icmpv6, next_header: IpProtocol::Icmpv6,
hop_limit: 0xff, hop_limit: 0xff,
payload_len: icmpv6_expected.buffer_len(&Medium::Ethernet), payload_len: icmpv6_expected.buffer_len(),
}; };
let cx = iface.context(Instant::from_secs(0)); let cx = iface.context(Instant::from_secs(0));
@ -3530,7 +3529,7 @@ mod test {
src_addr: Ipv6Address::LOOPBACK, src_addr: Ipv6Address::LOOPBACK,
dst_addr: remote_ip_addr, dst_addr: remote_ip_addr,
next_header: IpProtocol::Icmpv6, next_header: IpProtocol::Icmpv6,
payload_len: reply_icmp_repr.buffer_len(&Medium::Ethernet), payload_len: reply_icmp_repr.buffer_len(),
hop_limit: 0x40, hop_limit: 0x40,
}; };

View File

@ -427,19 +427,18 @@ impl<'a> IcmpSocket<'a> {
IcmpRepr::Ipv6(ref icmp_repr) => { IcmpRepr::Ipv6(ref icmp_repr) => {
let packet_buf = self let packet_buf = self
.rx_buffer .rx_buffer
.enqueue(icmp_repr.buffer_len(&_cx.caps.medium), ip_repr.src_addr())?; .enqueue(icmp_repr.buffer_len(), ip_repr.src_addr())?;
icmp_repr.emit( icmp_repr.emit(
&ip_repr.src_addr(), &ip_repr.src_addr(),
&ip_repr.dst_addr(), &ip_repr.dst_addr(),
&mut Icmpv6Packet::new_unchecked(packet_buf), &mut Icmpv6Packet::new_unchecked(packet_buf),
&ChecksumCapabilities::default(), &ChecksumCapabilities::default(),
&_cx.caps.medium,
); );
net_trace!( net_trace!(
"{}:{}: receiving {} octets", "{}:{}: receiving {} octets",
self.meta.handle, self.meta.handle,
icmp_repr.buffer_len(&_cx.caps.medium), icmp_repr.buffer_len(),
packet_buf.len() packet_buf.len()
); );
} }
@ -487,13 +486,12 @@ impl<'a> IcmpSocket<'a> {
&ipv6_addr.into(), &ipv6_addr.into(),
&packet, &packet,
&ChecksumCapabilities::ignored(), &ChecksumCapabilities::ignored(),
&_cx.caps.medium,
)?; )?;
let ip_repr = IpRepr::Ipv6(Ipv6Repr { let ip_repr = IpRepr::Ipv6(Ipv6Repr {
src_addr: src_addr, src_addr: src_addr,
dst_addr: ipv6_addr, dst_addr: ipv6_addr,
next_header: IpProtocol::Icmpv6, next_header: IpProtocol::Icmpv6,
payload_len: repr.buffer_len(&_cx.caps.medium), payload_len: repr.buffer_len(),
hop_limit: hop_limit, hop_limit: hop_limit,
}); });
emit((ip_repr, IcmpRepr::Ipv6(repr))) emit((ip_repr, IcmpRepr::Ipv6(repr)))
@ -868,7 +866,6 @@ mod test_ipv6 {
&REMOTE_IPV6.into(), &REMOTE_IPV6.into(),
&mut packet, &mut packet,
&checksum, &checksum,
&crate::phy::Medium::Ethernet,
); );
assert_eq!( assert_eq!(
@ -916,7 +913,6 @@ mod test_ipv6 {
&REMOTE_IPV6.into(), &REMOTE_IPV6.into(),
&mut packet, &mut packet,
&checksum, &checksum,
&crate::phy::Medium::Ethernet,
); );
s.set_hop_limit(Some(0x2a)); s.set_hop_limit(Some(0x2a));
@ -933,7 +929,7 @@ mod test_ipv6 {
src_addr: Ipv6Address::UNSPECIFIED, src_addr: Ipv6Address::UNSPECIFIED,
dst_addr: REMOTE_IPV6, dst_addr: REMOTE_IPV6,
next_header: IpProtocol::Icmpv6, next_header: IpProtocol::Icmpv6,
payload_len: ECHOV6_REPR.buffer_len(&crate::phy::Medium::Ethernet), payload_len: ECHOV6_REPR.buffer_len(),
hop_limit: 0x2a, hop_limit: 0x2a,
}) })
); );
@ -960,7 +956,6 @@ mod test_ipv6 {
&REMOTE_IPV6.into(), &REMOTE_IPV6.into(),
&mut packet, &mut packet,
&checksum, &checksum,
&crate::phy::Medium::Ethernet,
); );
let data = &packet.into_inner()[..]; let data = &packet.into_inner()[..];
@ -999,7 +994,6 @@ mod test_ipv6 {
&REMOTE_IPV6.into(), &REMOTE_IPV6.into(),
&mut packet, &mut packet,
&checksum, &checksum,
&crate::phy::Medium::Ethernet,
); );
// Ensure that a packet with an identifier that isn't the bound // Ensure that a packet with an identifier that isn't the bound
@ -1042,7 +1036,7 @@ mod test_ipv6 {
src_addr: REMOTE_IPV6.into(), src_addr: REMOTE_IPV6.into(),
dst_addr: LOCAL_IPV6.into(), dst_addr: LOCAL_IPV6.into(),
protocol: IpProtocol::Icmpv6, protocol: IpProtocol::Icmpv6,
payload_len: icmp_repr.buffer_len(&crate::phy::Medium::Ethernet), payload_len: icmp_repr.buffer_len(),
hop_limit: 0x40, hop_limit: 0x40,
}; };
@ -1064,7 +1058,6 @@ mod test_ipv6 {
&REMOTE_IPV6.into(), &REMOTE_IPV6.into(),
&mut packet, &mut packet,
&checksum, &checksum,
&crate::phy::Medium::Ethernet,
); );
assert_eq!( assert_eq!(
socket.recv(), socket.recv(),

View File

@ -357,15 +357,17 @@ impl fmt::Display for Repr {
source_protocol_addr, source_protocol_addr,
target_hardware_addr, target_hardware_addr,
target_protocol_addr, target_protocol_addr,
} => write!( } => {
f, write!(
"ARP type=Ethernet+IPv4 src={}/{} tgt={}/{} op={:?}", f,
source_hardware_addr, "ARP type=Ethernet+IPv4 src={}/{} tgt={}/{} op={:?}",
source_protocol_addr, source_hardware_addr,
target_hardware_addr, source_protocol_addr,
target_protocol_addr, target_hardware_addr,
operation target_protocol_addr,
), operation
)
}
} }
} }
} }

View File

@ -2,7 +2,6 @@ use byteorder::{ByteOrder, NetworkEndian};
use core::{cmp, fmt}; use core::{cmp, fmt};
use crate::phy::ChecksumCapabilities; use crate::phy::ChecksumCapabilities;
use crate::phy::Medium;
use crate::wire::ip::checksum; use crate::wire::ip::checksum;
use crate::wire::MldRepr; use crate::wire::MldRepr;
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
@ -546,7 +545,6 @@ impl<'a> Repr<'a> {
dst_addr: &IpAddress, dst_addr: &IpAddress,
packet: &Packet<&'a T>, packet: &Packet<&'a T>,
checksum_caps: &ChecksumCapabilities, checksum_caps: &ChecksumCapabilities,
medium: &Medium,
) -> Result<Repr<'a>> ) -> Result<Repr<'a>>
where where
T: AsRef<[u8]> + ?Sized, T: AsRef<[u8]> + ?Sized,
@ -620,16 +618,14 @@ impl<'a> Repr<'a> {
data: packet.payload(), data: packet.payload(),
}), }),
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
(msg_type, 0) if msg_type.is_ndisc() => { (msg_type, 0) if msg_type.is_ndisc() => NdiscRepr::parse(packet).map(Repr::Ndisc),
NdiscRepr::parse(packet, medium).map(Repr::Ndisc)
}
(msg_type, 0) if msg_type.is_mld() => MldRepr::parse(packet).map(Repr::Mld), (msg_type, 0) if msg_type.is_mld() => MldRepr::parse(packet).map(Repr::Mld),
_ => Err(Error::Unrecognized), _ => Err(Error::Unrecognized),
} }
} }
/// Return the length of a packet that will be emitted from this high-level representation. /// Return the length of a packet that will be emitted from this high-level representation.
pub fn buffer_len(&self, medium: &Medium) -> usize { pub fn buffer_len(&self) -> usize {
match self { match self {
&Repr::DstUnreachable { header, data, .. } &Repr::DstUnreachable { header, data, .. }
| &Repr::PktTooBig { header, data, .. } | &Repr::PktTooBig { header, data, .. }
@ -641,7 +637,7 @@ impl<'a> Repr<'a> {
field::ECHO_SEQNO.end + data.len() field::ECHO_SEQNO.end + data.len()
} }
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
&Repr::Ndisc(ndisc) => ndisc.buffer_len(medium), &Repr::Ndisc(ndisc) => ndisc.buffer_len(),
&Repr::Mld(mld) => mld.buffer_len(), &Repr::Mld(mld) => mld.buffer_len(),
} }
} }
@ -654,7 +650,6 @@ impl<'a> Repr<'a> {
dst_addr: &IpAddress, dst_addr: &IpAddress,
packet: &mut Packet<&mut T>, packet: &mut Packet<&mut T>,
checksum_caps: &ChecksumCapabilities, checksum_caps: &ChecksumCapabilities,
medium: &Medium,
) where ) where
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
{ {
@ -736,7 +731,7 @@ impl<'a> Repr<'a> {
} }
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
Repr::Ndisc(ndisc) => ndisc.emit(packet, medium), Repr::Ndisc(ndisc) => ndisc.emit(packet),
Repr::Mld(mld) => mld.emit(packet), Repr::Mld(mld) => mld.emit(packet),
} }
@ -844,7 +839,6 @@ mod test {
&MOCK_IP_ADDR_2, &MOCK_IP_ADDR_2,
&packet, &packet,
&ChecksumCapabilities::default(), &ChecksumCapabilities::default(),
&Medium::Ethernet,
) )
.unwrap(); .unwrap();
assert_eq!(repr, echo_packet_repr()); assert_eq!(repr, echo_packet_repr());
@ -853,14 +847,13 @@ mod test {
#[test] #[test]
fn test_echo_emit() { fn test_echo_emit() {
let repr = echo_packet_repr(); let repr = echo_packet_repr();
let mut bytes = vec![0xa5; repr.buffer_len(&Medium::Ethernet)]; let mut bytes = vec![0xa5; repr.buffer_len()];
let mut packet = Packet::new_unchecked(&mut bytes); let mut packet = Packet::new_unchecked(&mut bytes);
repr.emit( repr.emit(
&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_1,
&MOCK_IP_ADDR_2, &MOCK_IP_ADDR_2,
&mut packet, &mut packet,
&ChecksumCapabilities::default(), &ChecksumCapabilities::default(),
&Medium::Ethernet,
); );
assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]); assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
} }
@ -899,7 +892,6 @@ mod test {
&MOCK_IP_ADDR_2, &MOCK_IP_ADDR_2,
&packet, &packet,
&ChecksumCapabilities::default(), &ChecksumCapabilities::default(),
&Medium::Ethernet,
) )
.unwrap(); .unwrap();
assert_eq!(repr, too_big_packet_repr()); assert_eq!(repr, too_big_packet_repr());
@ -908,14 +900,13 @@ mod test {
#[test] #[test]
fn test_too_big_emit() { fn test_too_big_emit() {
let repr = too_big_packet_repr(); let repr = too_big_packet_repr();
let mut bytes = vec![0xa5; repr.buffer_len(&Medium::Ethernet)]; let mut bytes = vec![0xa5; repr.buffer_len()];
let mut packet = Packet::new_unchecked(&mut bytes); let mut packet = Packet::new_unchecked(&mut bytes);
repr.emit( repr.emit(
&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_1,
&MOCK_IP_ADDR_2, &MOCK_IP_ADDR_2,
&mut packet, &mut packet,
&ChecksumCapabilities::default(), &ChecksumCapabilities::default(),
&Medium::Ethernet,
); );
assert_eq!(&packet.into_inner()[..], &PKT_TOO_BIG_BYTES[..]); assert_eq!(&packet.into_inner()[..], &PKT_TOO_BIG_BYTES[..]);
} }

View File

@ -503,15 +503,17 @@ impl<'a> fmt::Display for Repr<'a> {
length, length,
segments_left, segments_left,
home_address, home_address,
} => write!( } => {
f, write!(
"IPv6 Routing next_hdr={} length={} type={} seg_left={} home_address={}", f,
next_header, "IPv6 Routing next_hdr={} length={} type={} seg_left={} home_address={}",
length, next_header,
Type::Type2, length,
segments_left, Type::Type2,
home_address segments_left,
), home_address
)
}
Repr::Rpl { Repr::Rpl {
next_header, next_header,
length, length,
@ -520,17 +522,10 @@ impl<'a> fmt::Display for Repr<'a> {
cmpr_e, cmpr_e,
pad, pad,
.. ..
} => write!( } => {
f, write!(f, "IPv6 Routing next_hdr={} length={} type={} seg_left={} cmpr_i={} cmpr_e={} pad={}",
"IPv6 Routing next_hdr={} length={} type={} seg_left={} cmpr_i={} cmpr_e={} pad={}", next_header, length, Type::Rpl, segments_left, cmpr_i, cmpr_e, pad)
next_header, }
length,
Type::Rpl,
segments_left,
cmpr_i,
cmpr_e,
pad
),
} }
} }
} }

View File

@ -532,7 +532,6 @@ mod test {
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&packet, &packet,
&ChecksumCapabilities::default(), &ChecksumCapabilities::default(),
&crate::phy::Medium::Ethernet,
); );
assert_eq!(repr, Ok(create_repr(Message::MldQuery))); assert_eq!(repr, Ok(create_repr(Message::MldQuery)));
} }
@ -545,7 +544,6 @@ mod test {
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&packet, &packet,
&ChecksumCapabilities::default(), &ChecksumCapabilities::default(),
&crate::phy::Medium::Ethernet,
); );
assert_eq!(repr, Ok(create_repr(Message::MldReport))); assert_eq!(repr, Ok(create_repr(Message::MldReport)));
} }
@ -560,7 +558,6 @@ mod test {
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&mut packet, &mut packet,
&ChecksumCapabilities::default(), &ChecksumCapabilities::default(),
&crate::phy::Medium::Ethernet,
); );
assert_eq!(&packet.into_inner()[..], &QUERY_PACKET_BYTES[..]); assert_eq!(&packet.into_inner()[..], &QUERY_PACKET_BYTES[..]);
} }
@ -575,7 +572,6 @@ mod test {
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&mut packet, &mut packet,
&ChecksumCapabilities::default(), &ChecksumCapabilities::default(),
&crate::phy::Medium::Ethernet,
); );
assert_eq!(&packet.into_inner()[..], &REPORT_PACKET_BYTES[..]); assert_eq!(&packet.into_inner()[..], &REPORT_PACKET_BYTES[..]);
} }

View File

@ -123,6 +123,8 @@ mod sixlowpan;
mod tcp; mod tcp;
mod udp; mod udp;
use crate::{phy::Medium, Error};
pub use self::pretty_print::PrettyPrinter; pub use self::pretty_print::PrettyPrinter;
#[cfg(feature = "medium-ethernet")] #[cfg(feature = "medium-ethernet")]
@ -310,3 +312,94 @@ impl From<Ieee802154Address> for HardwareAddress {
HardwareAddress::Ieee802154(addr) HardwareAddress::Ieee802154(addr)
} }
} }
#[cfg(not(feature = "medium-ieee802154"))]
pub const MAX_HARDWARE_ADDRESS_LEN: usize = 6;
#[cfg(feature = "medium-ieee802154")]
pub const MAX_HARDWARE_ADDRESS_LEN: usize = 8;
/// Unparsed hardware address.
///
/// Used to make NDISC parsing agnostic of the hardware medium in use.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct RawHardwareAddress {
len: u8,
data: [u8; MAX_HARDWARE_ADDRESS_LEN],
}
impl RawHardwareAddress {
pub fn from_bytes(addr: &[u8]) -> Self {
let mut data = [0u8; MAX_HARDWARE_ADDRESS_LEN];
data[..addr.len()].copy_from_slice(addr);
Self {
len: addr.len() as u8,
data,
}
}
pub fn as_bytes(&self) -> &[u8] {
&self.data[..self.len as usize]
}
pub fn len(&self) -> usize {
self.len as usize
}
pub fn parse(&self, medium: Medium) -> Result<HardwareAddress, Error> {
match medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => {
if self.len() < 6 {
return Err(Error::Malformed);
}
Ok(HardwareAddress::Ethernet(EthernetAddress::from_bytes(
self.as_bytes(),
)))
}
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => {
if self.len() < 8 {
return Err(Error::Malformed);
}
Ok(HardwareAddress::Ieee802154(Ieee802154Address::from_bytes(
self.as_bytes(),
)))
}
#[cfg(feature = "medium-ip")]
Medium::Ip => unreachable!(),
}
}
}
impl core::fmt::Display for RawHardwareAddress {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
for (i, &b) in self.as_bytes().iter().enumerate() {
if i != 0 {
write!(f, ":")?;
}
write!(f, "{:02x}", b)?;
}
Ok(())
}
}
#[cfg(feature = "medium-ethernet")]
impl From<EthernetAddress> for RawHardwareAddress {
fn from(addr: EthernetAddress) -> Self {
Self::from_bytes(addr.as_bytes())
}
}
#[cfg(feature = "medium-ieee802154")]
impl From<Ieee802154Address> for RawHardwareAddress {
fn from(addr: Ieee802154Address) -> Self {
Self::from_bytes(addr.as_bytes())
}
}
impl From<HardwareAddress> for RawHardwareAddress {
fn from(addr: HardwareAddress) -> Self {
Self::from_bytes(addr.as_bytes())
}
}

View File

@ -1,11 +1,10 @@
use bitflags::bitflags; use bitflags::bitflags;
use byteorder::{ByteOrder, NetworkEndian}; use byteorder::{ByteOrder, NetworkEndian};
use crate::phy::Medium;
use crate::time::Duration; use crate::time::Duration;
use crate::wire::icmpv6::{field, Message, Packet}; use crate::wire::icmpv6::{field, Message, Packet};
use crate::wire::HardwareAddress;
use crate::wire::Ipv6Address; use crate::wire::Ipv6Address;
use crate::wire::RawHardwareAddress;
use crate::wire::{Ipv6Packet, Ipv6Repr}; use crate::wire::{Ipv6Packet, Ipv6Repr};
use crate::wire::{NdiscOption, NdiscOptionRepr, NdiscOptionType}; use crate::wire::{NdiscOption, NdiscOptionRepr, NdiscOptionType};
use crate::wire::{NdiscPrefixInformation, NdiscRedirectedHeader}; use crate::wire::{NdiscPrefixInformation, NdiscRedirectedHeader};
@ -195,7 +194,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Repr<'a> { pub enum Repr<'a> {
RouterSolicit { RouterSolicit {
lladdr: Option<HardwareAddress>, lladdr: Option<RawHardwareAddress>,
}, },
RouterAdvert { RouterAdvert {
hop_limit: u8, hop_limit: u8,
@ -203,23 +202,23 @@ pub enum Repr<'a> {
router_lifetime: Duration, router_lifetime: Duration,
reachable_time: Duration, reachable_time: Duration,
retrans_time: Duration, retrans_time: Duration,
lladdr: Option<HardwareAddress>, lladdr: Option<RawHardwareAddress>,
mtu: Option<u32>, mtu: Option<u32>,
prefix_info: Option<NdiscPrefixInformation>, prefix_info: Option<NdiscPrefixInformation>,
}, },
NeighborSolicit { NeighborSolicit {
target_addr: Ipv6Address, target_addr: Ipv6Address,
lladdr: Option<HardwareAddress>, lladdr: Option<RawHardwareAddress>,
}, },
NeighborAdvert { NeighborAdvert {
flags: NeighborFlags, flags: NeighborFlags,
target_addr: Ipv6Address, target_addr: Ipv6Address,
lladdr: Option<HardwareAddress>, lladdr: Option<RawHardwareAddress>,
}, },
Redirect { Redirect {
target_addr: Ipv6Address, target_addr: Ipv6Address,
dest_addr: Ipv6Address, dest_addr: Ipv6Address,
lladdr: Option<HardwareAddress>, lladdr: Option<RawHardwareAddress>,
redirected_hdr: Option<NdiscRedirectedHeader<'a>>, redirected_hdr: Option<NdiscRedirectedHeader<'a>>,
}, },
} }
@ -227,7 +226,7 @@ pub enum Repr<'a> {
impl<'a> Repr<'a> { impl<'a> Repr<'a> {
/// Parse an NDISC packet and return a high-level representation of the /// Parse an NDISC packet and return a high-level representation of the
/// packet. /// packet.
pub fn parse<T>(packet: &Packet<&'a T>, medium: &Medium) -> Result<Repr<'a>> pub fn parse<T>(packet: &Packet<&'a T>) -> Result<Repr<'a>>
where where
T: AsRef<[u8]> + ?Sized, T: AsRef<[u8]> + ?Sized,
{ {
@ -236,7 +235,7 @@ impl<'a> Repr<'a> {
let lladdr = if !packet.payload().is_empty() { let lladdr = if !packet.payload().is_empty() {
let opt = NdiscOption::new_checked(packet.payload())?; let opt = NdiscOption::new_checked(packet.payload())?;
match opt.option_type() { match opt.option_type() {
NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr(medium)), NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()),
_ => { _ => {
return Err(Error::Unrecognized); return Err(Error::Unrecognized);
} }
@ -251,7 +250,7 @@ impl<'a> Repr<'a> {
let (mut lladdr, mut mtu, mut prefix_info) = (None, None, None); let (mut lladdr, mut mtu, mut prefix_info) = (None, None, None);
while packet.payload().len() - offset > 0 { while packet.payload().len() - offset > 0 {
let pkt = NdiscOption::new_checked(&packet.payload()[offset..])?; let pkt = NdiscOption::new_checked(&packet.payload()[offset..])?;
let opt = NdiscOptionRepr::parse(&pkt, medium)?; let opt = NdiscOptionRepr::parse(&pkt)?;
match opt { match opt {
NdiscOptionRepr::SourceLinkLayerAddr(addr) => lladdr = Some(addr), NdiscOptionRepr::SourceLinkLayerAddr(addr) => lladdr = Some(addr),
NdiscOptionRepr::Mtu(val) => mtu = Some(val), NdiscOptionRepr::Mtu(val) => mtu = Some(val),
@ -260,7 +259,7 @@ impl<'a> Repr<'a> {
return Err(Error::Unrecognized); return Err(Error::Unrecognized);
} }
} }
offset += opt.buffer_len(medium); offset += opt.buffer_len();
} }
Ok(Repr::RouterAdvert { Ok(Repr::RouterAdvert {
hop_limit: packet.current_hop_limit(), hop_limit: packet.current_hop_limit(),
@ -277,7 +276,7 @@ impl<'a> Repr<'a> {
let lladdr = if !packet.payload().is_empty() { let lladdr = if !packet.payload().is_empty() {
let opt = NdiscOption::new_checked(packet.payload())?; let opt = NdiscOption::new_checked(packet.payload())?;
match opt.option_type() { match opt.option_type() {
NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr(medium)), NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()),
_ => { _ => {
return Err(Error::Unrecognized); return Err(Error::Unrecognized);
} }
@ -294,7 +293,7 @@ impl<'a> Repr<'a> {
let lladdr = if !packet.payload().is_empty() { let lladdr = if !packet.payload().is_empty() {
let opt = NdiscOption::new_checked(packet.payload())?; let opt = NdiscOption::new_checked(packet.payload())?;
match opt.option_type() { match opt.option_type() {
NdiscOptionType::TargetLinkLayerAddr => Some(opt.link_layer_addr(medium)), NdiscOptionType::TargetLinkLayerAddr => Some(opt.link_layer_addr()),
_ => { _ => {
return Err(Error::Unrecognized); return Err(Error::Unrecognized);
} }
@ -315,7 +314,7 @@ impl<'a> Repr<'a> {
let opt = NdiscOption::new_checked(&packet.payload()[offset..])?; let opt = NdiscOption::new_checked(&packet.payload()[offset..])?;
match opt.option_type() { match opt.option_type() {
NdiscOptionType::SourceLinkLayerAddr => { NdiscOptionType::SourceLinkLayerAddr => {
lladdr = Some(opt.link_layer_addr(medium)); lladdr = Some(opt.link_layer_addr());
offset += 8; offset += 8;
} }
NdiscOptionType::RedirectedHeader => { NdiscOptionType::RedirectedHeader => {
@ -349,7 +348,7 @@ impl<'a> Repr<'a> {
} }
} }
pub fn buffer_len(&self, medium: &Medium) -> usize { pub fn buffer_len(&self) -> usize {
match self { match self {
&Repr::RouterSolicit { lladdr } => match lladdr { &Repr::RouterSolicit { lladdr } => match lladdr {
Some(_) => field::UNUSED.end + 8, Some(_) => field::UNUSED.end + 8,
@ -374,34 +373,11 @@ impl<'a> Repr<'a> {
field::RETRANS_TM.end + offset field::RETRANS_TM.end + offset
} }
&Repr::NeighborSolicit { lladdr, .. } | &Repr::NeighborAdvert { lladdr, .. } => { &Repr::NeighborSolicit { lladdr, .. } | &Repr::NeighborAdvert { lladdr, .. } => {
match lladdr { let mut offset = field::TARGET_ADDR.end;
Some(_addr) => { if let Some(lladdr) = lladdr {
match medium { offset += NdiscOptionRepr::SourceLinkLayerAddr(lladdr).buffer_len();
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => field::TARGET_ADDR.end + 8,
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => {
// XXX: This is for 6LoWPAN
let mut len = field::TARGET_ADDR.end;
len += 2;
len += _addr.as_bytes().len();
// A packet of len == 30 is a packet with an Ethernet address
if len > 30 {
// TODO(thvdveld): find out why this padding is +4 and not +6
// WireShark wants a padding of +6, however, then the packet is not accepted when using ping
// When a padding of +4 is used, then WireShark complains and says that the packet is malformed,
// however, ping accepts this packet.
len += 4; // Padding
}
len
}
#[cfg(feature = "medium-ip")]
Medium::Ip => unreachable!(),
}
}
None => field::TARGET_ADDR.end,
} }
offset
} }
&Repr::Redirect { &Repr::Redirect {
lladdr, lladdr,
@ -420,7 +396,7 @@ impl<'a> Repr<'a> {
} }
} }
pub fn emit<T>(&self, packet: &mut Packet<&mut T>, medium: &Medium) pub fn emit<T>(&self, packet: &mut Packet<&mut T>)
where where
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
{ {
@ -431,7 +407,7 @@ impl<'a> Repr<'a> {
packet.clear_reserved(); packet.clear_reserved();
if let Some(lladdr) = lladdr { if let Some(lladdr) = lladdr {
let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt, medium); NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt);
} }
} }
@ -455,26 +431,20 @@ impl<'a> Repr<'a> {
let mut offset = 0; let mut offset = 0;
if let Some(lladdr) = lladdr { if let Some(lladdr) = lladdr {
let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt, medium); let opt = NdiscOptionRepr::SourceLinkLayerAddr(lladdr);
match medium { opt.emit(&mut opt_pkt);
#[cfg(feature = "medium-ethernet")] offset += opt.buffer_len();
Medium::Ethernet => offset += 6,
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => offset += 8,
#[cfg(feature = "medium-ip")]
_ => unreachable!(),
}
} }
if let Some(mtu) = mtu { if let Some(mtu) = mtu {
let mut opt_pkt = let mut opt_pkt =
NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]); NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]);
NdiscOptionRepr::Mtu(mtu).emit(&mut opt_pkt, medium); NdiscOptionRepr::Mtu(mtu).emit(&mut opt_pkt);
offset += 8; offset += 8;
} }
if let Some(prefix_info) = prefix_info { if let Some(prefix_info) = prefix_info {
let mut opt_pkt = let mut opt_pkt =
NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]); NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]);
NdiscOptionRepr::PrefixInformation(prefix_info).emit(&mut opt_pkt, medium) NdiscOptionRepr::PrefixInformation(prefix_info).emit(&mut opt_pkt)
} }
} }
@ -488,7 +458,7 @@ impl<'a> Repr<'a> {
packet.set_target_addr(target_addr); packet.set_target_addr(target_addr);
if let Some(lladdr) = lladdr { if let Some(lladdr) = lladdr {
let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt, medium); NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt);
} }
} }
@ -504,7 +474,7 @@ impl<'a> Repr<'a> {
packet.set_target_addr(target_addr); packet.set_target_addr(target_addr);
if let Some(lladdr) = lladdr { if let Some(lladdr) = lladdr {
let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt, medium); NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt);
} }
} }
@ -522,7 +492,7 @@ impl<'a> Repr<'a> {
let offset = match lladdr { let offset = match lladdr {
Some(lladdr) => { Some(lladdr) => {
let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut()); let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt, medium); NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt);
8 8
} }
None => 0, None => 0,
@ -530,7 +500,7 @@ impl<'a> Repr<'a> {
if let Some(redirected_hdr) = redirected_hdr { if let Some(redirected_hdr) = redirected_hdr {
let mut opt_pkt = let mut opt_pkt =
NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]); NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]);
NdiscOptionRepr::RedirectedHeader(redirected_hdr).emit(&mut opt_pkt, medium); NdiscOptionRepr::RedirectedHeader(redirected_hdr).emit(&mut opt_pkt);
} }
} }
} }
@ -543,7 +513,6 @@ mod test {
use crate::phy::ChecksumCapabilities; use crate::phy::ChecksumCapabilities;
use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2}; use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2};
use crate::wire::EthernetAddress; use crate::wire::EthernetAddress;
use crate::wire::HardwareAddress;
use crate::wire::Icmpv6Repr; use crate::wire::Icmpv6Repr;
static ROUTER_ADVERT_BYTES: [u8; 24] = [ static ROUTER_ADVERT_BYTES: [u8; 24] = [
@ -559,9 +528,7 @@ mod test {
router_lifetime: Duration::from_secs(900), router_lifetime: Duration::from_secs(900),
reachable_time: Duration::from_millis(900), reachable_time: Duration::from_millis(900),
retrans_time: Duration::from_millis(900), retrans_time: Duration::from_millis(900),
lladdr: Some(HardwareAddress::Ethernet(EthernetAddress([ lladdr: Some(EthernetAddress([0x52, 0x54, 0x00, 0x12, 0x34, 0x56]).into()),
0x52, 0x54, 0x00, 0x12, 0x34, 0x56,
]))),
mtu: None, mtu: None,
prefix_info: None, prefix_info: None,
}) })
@ -606,8 +573,7 @@ mod test {
&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_1,
&MOCK_IP_ADDR_2, &MOCK_IP_ADDR_2,
&packet, &packet,
&ChecksumCapabilities::default(), &ChecksumCapabilities::default()
&Medium::Ethernet,
) )
.unwrap(), .unwrap(),
create_repr() create_repr()
@ -623,7 +589,6 @@ mod test {
&MOCK_IP_ADDR_2, &MOCK_IP_ADDR_2,
&mut packet, &mut packet,
&ChecksumCapabilities::default(), &ChecksumCapabilities::default(),
&Medium::Ethernet,
); );
assert_eq!(&packet.into_inner()[..], &ROUTER_ADVERT_BYTES[..]); assert_eq!(&packet.into_inner()[..], &ROUTER_ADVERT_BYTES[..]);
} }

View File

@ -2,17 +2,11 @@ use bitflags::bitflags;
use byteorder::{ByteOrder, NetworkEndian}; use byteorder::{ByteOrder, NetworkEndian};
use core::fmt; use core::fmt;
use crate::phy::Medium;
use crate::time::Duration; use crate::time::Duration;
use crate::wire::{Ipv6Address, Ipv6Packet, Ipv6Repr}; use crate::wire::{Ipv6Address, Ipv6Packet, Ipv6Repr, MAX_HARDWARE_ADDRESS_LEN};
use crate::{Error, Result}; use crate::{Error, Result};
#[cfg(feature = "medium-ethernet")] use crate::wire::RawHardwareAddress;
use crate::wire::EthernetAddress;
#[cfg(feature = "medium-ieee802154")]
use crate::wire::Ieee802154Address;
use crate::wire::HardwareAddress;
enum_with_unknown! { enum_with_unknown! {
/// NDISC Option Type /// NDISC Option Type
@ -90,12 +84,6 @@ mod field {
// | Type | Length | Link-Layer Address ... // | Type | Length | Link-Layer Address ...
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// Link-Layer Address
#[cfg(feature = "medium-ethernet")]
pub const LL_ADDR_ETHERNET: Field = 2..8;
#[cfg(feature = "medium-ieee802154")]
pub const LL_ADDR_IEEE802154: Field = 2..10;
// Prefix Information Option fields. // Prefix Information Option fields.
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Type | Length | Prefix Length |L|A| Reserved1 | // | Type | Length | Prefix Length |L|A| Reserved1 |
@ -225,23 +213,10 @@ impl<T: AsRef<[u8]>> NdiscOption<T> {
impl<T: AsRef<[u8]>> NdiscOption<T> { impl<T: AsRef<[u8]>> NdiscOption<T> {
/// Return the Source/Target Link-layer Address. /// Return the Source/Target Link-layer Address.
#[inline] #[inline]
pub fn link_layer_addr(&self, medium: &Medium) -> HardwareAddress { pub fn link_layer_addr(&self) -> RawHardwareAddress {
let len = MAX_HARDWARE_ADDRESS_LEN.min(self.data_len() as usize * 8 - 2);
let data = self.buffer.as_ref(); let data = self.buffer.as_ref();
RawHardwareAddress::from_bytes(&data[2..len + 2])
match medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => {
let addr = &data[field::LL_ADDR_ETHERNET];
HardwareAddress::Ethernet(EthernetAddress::from_bytes(addr))
}
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => {
let addr = &data[field::LL_ADDR_IEEE802154];
HardwareAddress::Ieee802154(Ieee802154Address::from_bytes(addr))
}
#[cfg(feature = "medium-ip")]
_ => todo!(), // TODO(thvdveld)
}
} }
} }
@ -322,21 +297,9 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> NdiscOption<T> {
impl<T: AsRef<[u8]> + AsMut<[u8]>> NdiscOption<T> { impl<T: AsRef<[u8]> + AsMut<[u8]>> NdiscOption<T> {
/// Set the Source/Target Link-layer Address. /// Set the Source/Target Link-layer Address.
#[inline] #[inline]
pub fn set_link_layer_addr(&mut self, addr: HardwareAddress) { pub fn set_link_layer_addr(&mut self, addr: RawHardwareAddress) {
let data = self.buffer.as_mut(); let data = self.buffer.as_mut();
match addr { data[2..2 + addr.len()].copy_from_slice(addr.as_bytes())
#[cfg(feature = "medium-ethernet")]
HardwareAddress::Ethernet(addr) => {
let data = &mut data[field::LL_ADDR_ETHERNET];
data.copy_from_slice(addr.as_bytes());
}
#[cfg(feature = "medium-ieee802154")]
HardwareAddress::Ieee802154(addr) => {
let data = &mut data[field::LL_ADDR_IEEE802154];
data.copy_from_slice(addr.as_bytes());
}
_ => todo!(),
}
} }
} }
@ -413,18 +376,17 @@ impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> NdiscOption<&'a mut T> {
} }
} }
//impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for NdiscOption<&'a T> { impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for NdiscOption<&'a T> {
//fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//// XXX(thvdveld): how do we pass the medium? match Repr::parse(self) {
//match Repr::parse(self, &Medium::Ethernet) { Ok(repr) => write!(f, "{}", repr),
//Ok(repr) => write!(f, "{}", repr), Err(err) => {
//Err(err) => { write!(f, "NDISC Option ({})", err)?;
//write!(f, "NDISC Option ({})", err)?; Ok(())
//Ok(()) }
//} }
//} }
//} }
//}
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -447,8 +409,8 @@ pub struct RedirectedHeader<'a> {
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Repr<'a> { pub enum Repr<'a> {
SourceLinkLayerAddr(HardwareAddress), SourceLinkLayerAddr(RawHardwareAddress),
TargetLinkLayerAddr(HardwareAddress), TargetLinkLayerAddr(RawHardwareAddress),
PrefixInformation(PrefixInformation), PrefixInformation(PrefixInformation),
RedirectedHeader(RedirectedHeader<'a>), RedirectedHeader(RedirectedHeader<'a>),
Mtu(u32), Mtu(u32),
@ -461,21 +423,21 @@ pub enum Repr<'a> {
impl<'a> Repr<'a> { impl<'a> Repr<'a> {
/// Parse an NDISC Option and return a high-level representation. /// Parse an NDISC Option and return a high-level representation.
pub fn parse<T>(opt: &'a NdiscOption<&'a T>, medium: &Medium) -> Result<Repr<'a>> pub fn parse<T>(opt: &'a NdiscOption<&'a T>) -> Result<Repr<'a>>
where where
T: AsRef<[u8]> + ?Sized, T: AsRef<[u8]> + ?Sized,
{ {
match opt.option_type() { match opt.option_type() {
Type::SourceLinkLayerAddr => { Type::SourceLinkLayerAddr => {
if opt.data_len() == 1 { if opt.data_len() == 1 {
Ok(Repr::SourceLinkLayerAddr(opt.link_layer_addr(medium))) Ok(Repr::SourceLinkLayerAddr(opt.link_layer_addr()))
} else { } else {
Err(Error::Malformed) Err(Error::Malformed)
} }
} }
Type::TargetLinkLayerAddr => { Type::TargetLinkLayerAddr => {
if opt.data_len() == 1 { if opt.data_len() == 1 {
Ok(Repr::TargetLinkLayerAddr(opt.link_layer_addr(medium))) Ok(Repr::TargetLinkLayerAddr(opt.link_layer_addr()))
} else { } else {
Err(Error::Malformed) Err(Error::Malformed)
} }
@ -524,57 +486,38 @@ impl<'a> Repr<'a> {
} }
/// Return the length of a header that will be emitted from this high-level representation. /// Return the length of a header that will be emitted from this high-level representation.
pub fn buffer_len(&self, medium: &Medium) -> usize { pub fn buffer_len(&self) -> usize {
match (self, medium) { match self {
#[cfg(feature = "medium-ethernet")] &Repr::SourceLinkLayerAddr(addr) | &Repr::TargetLinkLayerAddr(addr) => {
(&Repr::SourceLinkLayerAddr(_) | &Repr::TargetLinkLayerAddr(_), Medium::Ethernet) => { let len = 2 + addr.len();
field::LL_ADDR_ETHERNET.end // Round up to next multiple of 8
(len + 7) / 8 * 8
} }
#[cfg(feature = "medium-ieee802154")] &Repr::PrefixInformation(_) => field::PREFIX.end,
(&Repr::SourceLinkLayerAddr(_) | &Repr::TargetLinkLayerAddr(_), Medium::Ieee802154) => { &Repr::RedirectedHeader(RedirectedHeader { header, data }) => {
field::LL_ADDR_IEEE802154.end
}
#[cfg(feature = "medium-ip")]
(&Repr::SourceLinkLayerAddr(_) | &Repr::TargetLinkLayerAddr(_), _) => {
unreachable!()
}
(&Repr::PrefixInformation(_), _) => field::PREFIX.end,
(&Repr::RedirectedHeader(RedirectedHeader { header, data }), _) => {
field::IP_DATA + header.buffer_len() + data.len() field::IP_DATA + header.buffer_len() + data.len()
} }
(&Repr::Mtu(_), _) => field::MTU.end, &Repr::Mtu(_) => field::MTU.end,
(&Repr::Unknown { length, .. }, _) => field::DATA(length).end, &Repr::Unknown { length, .. } => field::DATA(length).end,
} }
} }
/// Emit a high-level representation into an NDISC Option. /// Emit a high-level representation into an NDISC Option.
pub fn emit<T>(&self, opt: &mut NdiscOption<&'a mut T>, medium: &Medium) pub fn emit<T>(&self, opt: &mut NdiscOption<&'a mut T>)
where where
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
{ {
match *self { match *self {
Repr::SourceLinkLayerAddr(addr) => { Repr::SourceLinkLayerAddr(addr) => {
opt.set_option_type(Type::SourceLinkLayerAddr); opt.set_option_type(Type::SourceLinkLayerAddr);
match medium { let opt_len = addr.len() + 2;
#[cfg(feature = "medium-ethernet")] opt.set_data_len(((opt_len + 7) / 8) as u8); // round to next multiple of 8.
Medium::Ethernet => opt.set_data_len(1),
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => opt.set_data_len(2),
#[cfg(feature = "medium-ip")]
_ => unreachable!(),
}
opt.set_link_layer_addr(addr); opt.set_link_layer_addr(addr);
} }
Repr::TargetLinkLayerAddr(addr) => { Repr::TargetLinkLayerAddr(addr) => {
opt.set_option_type(Type::TargetLinkLayerAddr); opt.set_option_type(Type::TargetLinkLayerAddr);
match medium { let opt_len = addr.len() + 2;
#[cfg(feature = "medium-ethernet")] opt.set_data_len(((opt_len + 7) / 8) as u8); // round to next multiple of 8.
Medium::Ethernet => opt.set_data_len(1),
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => opt.set_data_len(2),
#[cfg(feature = "medium-ip")]
_ => unreachable!(),
}
opt.set_link_layer_addr(addr); opt.set_link_layer_addr(addr);
} }
Repr::PrefixInformation(PrefixInformation { Repr::PrefixInformation(PrefixInformation {
@ -626,42 +569,51 @@ impl<'a> fmt::Display for Repr<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "NDISC Option: ")?; write!(f, "NDISC Option: ")?;
match *self { match *self {
Repr::SourceLinkLayerAddr(addr) => write!(f, "SourceLinkLayer addr={}", addr), Repr::SourceLinkLayerAddr(addr) => {
Repr::TargetLinkLayerAddr(addr) => write!(f, "TargetLinkLayer addr={}", addr), write!(f, "SourceLinkLayer addr={}", addr)
}
Repr::TargetLinkLayerAddr(addr) => {
write!(f, "TargetLinkLayer addr={}", addr)
}
Repr::PrefixInformation(PrefixInformation { Repr::PrefixInformation(PrefixInformation {
prefix, prefix_len, .. prefix, prefix_len, ..
}) => write!(f, "PrefixInformation prefix={}/{}", prefix, prefix_len), }) => {
write!(f, "PrefixInformation prefix={}/{}", prefix, prefix_len)
}
Repr::RedirectedHeader(RedirectedHeader { header, .. }) => { Repr::RedirectedHeader(RedirectedHeader { header, .. }) => {
write!(f, "RedirectedHeader header={}", header) write!(f, "RedirectedHeader header={}", header)
} }
Repr::Mtu(mtu) => write!(f, "MTU mtu={}", mtu), Repr::Mtu(mtu) => {
write!(f, "MTU mtu={}", mtu)
}
Repr::Unknown { Repr::Unknown {
type_: id, length, .. type_: id, length, ..
} => write!(f, "Unknown({}) length={}", id, length), } => {
write!(f, "Unknown({}) length={}", id, length)
}
} }
} }
} }
//use crate::wire::pretty_print::{PrettyIndent, PrettyPrint}; use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
//impl<T: AsRef<[u8]>> PrettyPrint for NdiscOption<T> { impl<T: AsRef<[u8]>> PrettyPrint for NdiscOption<T> {
//fn pretty_print( fn pretty_print(
//buffer: &dyn AsRef<[u8]>, buffer: &dyn AsRef<[u8]>,
//f: &mut fmt::Formatter, f: &mut fmt::Formatter,
//indent: &mut PrettyIndent, indent: &mut PrettyIndent,
//) -> fmt::Result { ) -> fmt::Result {
//// TODO(thvdveld): how do we pass the medium? match NdiscOption::new_checked(buffer) {
//match NdiscOption::new_checked(buffer) { Err(err) => return write!(f, "{}({})", indent, err),
//Err(err) => return write!(f, "{}({})", indent, err), Ok(ndisc) => match Repr::parse(&ndisc) {
//Ok(ndisc) => match Repr::parse(&ndisc, &Medium::Ethernet) { Err(_) => Ok(()),
//Err(_) => Ok(()), Ok(repr) => {
//Ok(repr) => { write!(f, "{}{}", indent, repr)
//write!(f, "{}{}", indent, repr) }
//} },
//}, }
//} }
//} }
//}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
@ -721,20 +673,14 @@ mod test {
let addr = EthernetAddress([0x54, 0x52, 0x00, 0x12, 0x23, 0x34]); let addr = EthernetAddress([0x54, 0x52, 0x00, 0x12, 0x23, 0x34]);
{ {
assert_eq!( assert_eq!(
Repr::parse( Repr::parse(&NdiscOption::new_unchecked(&bytes)),
&NdiscOption::new_unchecked(&bytes),
&crate::phy::Medium::Ethernet
),
Ok(Repr::SourceLinkLayerAddr(addr.into())) Ok(Repr::SourceLinkLayerAddr(addr.into()))
); );
} }
bytes[0] = 0x02; bytes[0] = 0x02;
{ {
assert_eq!( assert_eq!(
Repr::parse( Repr::parse(&NdiscOption::new_unchecked(&bytes)),
&NdiscOption::new_unchecked(&bytes),
&crate::phy::Medium::Ethernet
),
Ok(Repr::TargetLinkLayerAddr(addr.into())) Ok(Repr::TargetLinkLayerAddr(addr.into()))
); );
} }
@ -750,10 +696,7 @@ mod test {
prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
}); });
assert_eq!( assert_eq!(
Repr::parse( Repr::parse(&NdiscOption::new_unchecked(&PREFIX_OPT_BYTES)),
&NdiscOption::new_unchecked(&PREFIX_OPT_BYTES),
&crate::phy::Medium::Ethernet
),
Ok(repr) Ok(repr)
); );
} }
@ -769,7 +712,7 @@ mod test {
prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
}); });
let mut opt = NdiscOption::new_unchecked(&mut bytes); let mut opt = NdiscOption::new_unchecked(&mut bytes);
repr.emit(&mut opt, &crate::phy::Medium::Ethernet); repr.emit(&mut opt);
assert_eq!(&opt.into_inner()[..], &PREFIX_OPT_BYTES[..]); assert_eq!(&opt.into_inner()[..], &PREFIX_OPT_BYTES[..]);
} }
@ -777,10 +720,7 @@ mod test {
fn test_repr_parse_mtu() { fn test_repr_parse_mtu() {
let bytes = [0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0xdc]; let bytes = [0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0xdc];
assert_eq!( assert_eq!(
Repr::parse( Repr::parse(&NdiscOption::new_unchecked(&bytes)),
&NdiscOption::new_unchecked(&bytes),
&crate::phy::Medium::Ethernet
),
Ok(Repr::Mtu(1500)) Ok(Repr::Mtu(1500))
); );
} }