blob: de2c3e988eb656b3d70433b70a4667e683e1ec0e [file] [log] [blame]
use super::*;
use crate::phy::ChecksumCapabilities;
use crate::wire::*;
// Max len of non-fragmented packets after decompression (including ipv6 header and payload)
// TODO: lower. Should be (6lowpan mtu) - (min 6lowpan header size) + (max ipv6 header size)
pub(crate) const MAX_DECOMPRESSED_LEN: usize = 1500;
impl InterfaceInner {
pub(super) fn process_sixlowpan<'output, 'payload: 'output>(
&mut self,
sockets: &mut SocketSet,
meta: PacketMeta,
ieee802154_repr: &Ieee802154Repr,
payload: &'payload [u8],
f: &'output mut FragmentsBuffer,
) -> Option<Packet<'output>> {
let payload = match check!(SixlowpanPacket::dispatch(payload)) {
#[cfg(not(feature = "proto-sixlowpan-fragmentation"))]
SixlowpanPacket::FragmentHeader => {
net_debug!(
"Fragmentation is not supported, \
use the `proto-sixlowpan-fragmentation` feature to add support."
);
return None;
}
#[cfg(feature = "proto-sixlowpan-fragmentation")]
SixlowpanPacket::FragmentHeader => {
match self.process_sixlowpan_fragment(ieee802154_repr, payload, f) {
Some(payload) => payload,
None => return None,
}
}
SixlowpanPacket::IphcHeader => {
match Self::sixlowpan_to_ipv6(
&self.sixlowpan_address_context,
ieee802154_repr,
payload,
None,
&mut f.decompress_buf,
) {
Ok(len) => &f.decompress_buf[..len],
Err(e) => {
net_debug!("sixlowpan decompress failed: {:?}", e);
return None;
}
}
}
};
self.process_ipv6(sockets, meta, &check!(Ipv6Packet::new_checked(payload)))
}
#[cfg(feature = "proto-sixlowpan-fragmentation")]
fn process_sixlowpan_fragment<'output, 'payload: 'output>(
&mut self,
ieee802154_repr: &Ieee802154Repr,
payload: &'payload [u8],
f: &'output mut FragmentsBuffer,
) -> Option<&'output [u8]> {
use crate::iface::fragmentation::{AssemblerError, AssemblerFullError};
// We have a fragment header, which means we cannot process the 6LoWPAN packet,
// unless we have a complete one after processing this fragment.
let frag = check!(SixlowpanFragPacket::new_checked(payload));
// The key specifies to which 6LoWPAN fragment it belongs too.
// It is based on the link layer addresses, the tag and the size.
let key = FragKey::Sixlowpan(frag.get_key(ieee802154_repr));
// The offset of this fragment in increments of 8 octets.
let offset = frag.datagram_offset() as usize * 8;
// We reserve a spot in the packet assembler set and add the required
// information to the packet assembler.
// This information is the total size of the packet when it is fully assmbled.
// We also pass the header size, since this is needed when other fragments
// (other than the first one) are added.
let frag_slot = match f.assembler.get(&key, self.now + f.reassembly_timeout) {
Ok(frag) => frag,
Err(AssemblerFullError) => {
net_debug!("No available packet assembler for fragmented packet");
return None;
}
};
if frag.is_first_fragment() {
// The first fragment contains the total size of the IPv6 packet.
// However, we received a packet that is compressed following the 6LoWPAN
// standard. This means we need to convert the IPv6 packet size to a 6LoWPAN
// packet size. The packet size can be different because of first the
// compression of the IP header and when UDP is used (because the UDP header
// can also be compressed). Other headers are not compressed by 6LoWPAN.
// First segment tells us the total size.
let total_size = frag.datagram_size() as usize;
if frag_slot.set_total_size(total_size).is_err() {
net_debug!("No available packet assembler for fragmented packet");
return None;
}
// Decompress headers+payload into the assembler.
if let Err(e) = frag_slot.add_with(0, |buffer| {
Self::sixlowpan_to_ipv6(
&self.sixlowpan_address_context,
ieee802154_repr,
frag.payload(),
Some(total_size),
buffer,
)
.map_err(|_| AssemblerError)
}) {
net_debug!("fragmentation error: {:?}", e);
return None;
}
} else {
// Add the fragment to the packet assembler.
if let Err(e) = frag_slot.add(frag.payload(), offset) {
net_debug!("fragmentation error: {:?}", e);
return None;
}
}
match frag_slot.assemble() {
Some(payload) => {
net_trace!("6LoWPAN: fragmented packet now complete");
Some(payload)
}
None => None,
}
}
fn sixlowpan_to_ipv6(
address_context: &[SixlowpanAddressContext],
ieee802154_repr: &Ieee802154Repr,
iphc_payload: &[u8],
total_size: Option<usize>,
buffer: &mut [u8],
) -> core::result::Result<usize, crate::wire::Error> {
let iphc = SixlowpanIphcPacket::new_checked(iphc_payload)?;
let iphc_repr = SixlowpanIphcRepr::parse(
&iphc,
ieee802154_repr.src_addr,
ieee802154_repr.dst_addr,
address_context,
)?;
let first_next_header = match iphc_repr.next_header {
SixlowpanNextHeader::Compressed => {
match SixlowpanNhcPacket::dispatch(iphc.payload())? {
SixlowpanNhcPacket::ExtHeader => {
SixlowpanExtHeaderPacket::new_checked(iphc.payload())?
.extension_header_id()
.into()
}
SixlowpanNhcPacket::UdpHeader => IpProtocol::Udp,
}
}
SixlowpanNextHeader::Uncompressed(proto) => proto,
};
let mut decompressed_size = 40 + iphc.payload().len();
let mut next_header = Some(iphc_repr.next_header);
let mut data = iphc.payload();
while let Some(nh) = next_header {
match nh {
SixlowpanNextHeader::Compressed => match SixlowpanNhcPacket::dispatch(data)? {
SixlowpanNhcPacket::ExtHeader => {
let ext_hdr = SixlowpanExtHeaderPacket::new_checked(data)?;
let ext_repr = SixlowpanExtHeaderRepr::parse(&ext_hdr)?;
decompressed_size += 2;
decompressed_size -= ext_repr.buffer_len();
next_header = Some(ext_repr.next_header);
if ext_repr.buffer_len() + ext_repr.length as usize > data.len() {
return Err(Error);
}
data = &data[ext_repr.buffer_len() + ext_repr.length as usize..];
}
SixlowpanNhcPacket::UdpHeader => {
let udp_packet = SixlowpanUdpNhcPacket::new_checked(data)?;
let udp_repr = SixlowpanUdpNhcRepr::parse(
&udp_packet,
&iphc_repr.src_addr,
&iphc_repr.dst_addr,
&crate::phy::ChecksumCapabilities::ignored(),
)?;
decompressed_size += 8;
decompressed_size -= udp_repr.header_len();
break;
}
},
SixlowpanNextHeader::Uncompressed(proto) => match proto {
IpProtocol::Tcp => break,
IpProtocol::Udp => break,
IpProtocol::Icmpv6 => break,
proto => {
net_debug!("unable to decompress Uncompressed({})", proto);
return Err(Error);
}
},
}
}
if buffer.len() < decompressed_size {
net_debug!("sixlowpan decompress: buffer too short");
return Err(crate::wire::Error);
}
let buffer = &mut buffer[..decompressed_size];
let total_size = if let Some(size) = total_size {
size
} else {
decompressed_size
};
let mut rest_size = total_size;
let ipv6_repr = Ipv6Repr {
src_addr: iphc_repr.src_addr,
dst_addr: iphc_repr.dst_addr,
next_header: first_next_header,
payload_len: total_size - 40,
hop_limit: iphc_repr.hop_limit,
};
rest_size -= 40;
// Emit the decompressed IPHC header (decompressed to an IPv6 header).
let mut ipv6_packet = Ipv6Packet::new_unchecked(&mut buffer[..ipv6_repr.buffer_len()]);
ipv6_repr.emit(&mut ipv6_packet);
let mut buffer = &mut buffer[ipv6_repr.buffer_len()..];
let mut next_header = Some(iphc_repr.next_header);
let mut data = iphc.payload();
while let Some(nh) = next_header {
match nh {
SixlowpanNextHeader::Compressed => match SixlowpanNhcPacket::dispatch(data)? {
SixlowpanNhcPacket::ExtHeader => {
let ext_hdr = SixlowpanExtHeaderPacket::new_checked(data)?;
let ext_repr = SixlowpanExtHeaderRepr::parse(&ext_hdr)?;
let nh = match ext_repr.next_header {
SixlowpanNextHeader::Compressed => {
let d = &data[ext_repr.length as usize + ext_repr.buffer_len()..];
match SixlowpanNhcPacket::dispatch(d)? {
SixlowpanNhcPacket::ExtHeader => {
SixlowpanExtHeaderPacket::new_checked(d)?
.extension_header_id()
.into()
}
SixlowpanNhcPacket::UdpHeader => IpProtocol::Udp,
}
}
SixlowpanNextHeader::Uncompressed(proto) => proto,
};
next_header = Some(ext_repr.next_header);
let ipv6_ext_hdr = Ipv6ExtHeaderRepr {
next_header: nh,
length: ext_repr.length / 8,
data: &ext_hdr.payload()[..ext_repr.length as usize],
};
ipv6_ext_hdr.emit(&mut Ipv6ExtHeader::new_unchecked(
&mut buffer[..ipv6_ext_hdr.header_len()],
));
buffer[ipv6_ext_hdr.header_len()..][..ipv6_ext_hdr.data.len()]
.copy_from_slice(ipv6_ext_hdr.data);
buffer = &mut buffer[ipv6_ext_hdr.header_len() + ipv6_ext_hdr.data.len()..];
rest_size -= ipv6_ext_hdr.header_len() + ipv6_ext_hdr.data.len();
data = &data[ext_repr.buffer_len() + ext_repr.length as usize..];
}
SixlowpanNhcPacket::UdpHeader => {
let udp_packet = SixlowpanUdpNhcPacket::new_checked(data)?;
let payload = udp_packet.payload();
let udp_repr = SixlowpanUdpNhcRepr::parse(
&udp_packet,
&iphc_repr.src_addr,
&iphc_repr.dst_addr,
&ChecksumCapabilities::ignored(),
)?;
if payload.len() + 8 > buffer.len() {
return Err(Error);
}
let mut udp = UdpPacket::new_unchecked(&mut buffer[..payload.len() + 8]);
udp_repr
.0
.emit_header(&mut udp, rest_size - udp_repr.0.header_len());
buffer[8..][..payload.len()].copy_from_slice(payload);
break;
}
},
SixlowpanNextHeader::Uncompressed(proto) => match proto {
IpProtocol::HopByHop => unreachable!(),
IpProtocol::Tcp => {
buffer.copy_from_slice(data);
break;
}
IpProtocol::Udp => {
buffer.copy_from_slice(data);
break;
}
IpProtocol::Icmpv6 => {
buffer.copy_from_slice(data);
break;
}
_ => unreachable!(),
},
}
}
Ok(decompressed_size)
}
pub(super) fn dispatch_sixlowpan<Tx: TxToken>(
&mut self,
mut tx_token: Tx,
meta: PacketMeta,
packet: Packet,
ieee_repr: Ieee802154Repr,
frag: &mut Fragmenter,
) {
let packet = match packet {
#[cfg(feature = "proto-ipv4")]
Packet::Ipv4(_) => unreachable!(),
Packet::Ipv6(packet) => packet,
};
// First we calculate the size we are going to need. If the size is bigger than the MTU,
// then we use fragmentation.
let (total_size, compressed_size, uncompressed_size) =
Self::compressed_packet_size(&packet, &ieee_repr);
let ieee_len = ieee_repr.buffer_len();
// TODO(thvdveld): use the MTU of the device.
if total_size + ieee_len > 125 {
#[cfg(feature = "proto-sixlowpan-fragmentation")]
{
// The packet does not fit in one Ieee802154 frame, so we need fragmentation.
// We do this by emitting everything in the `frag.buffer` from the interface.
// After emitting everything into that buffer, we send the first fragment heere.
// When `poll` is called again, we check if frag was fully sent, otherwise we
// call `dispatch_ieee802154_frag`, which will transmit the other fragments.
// `dispatch_ieee802154_frag` requires some information about the total packet size,
// the link local source and destination address...
let pkt = frag;
if pkt.buffer.len() < total_size {
net_debug!(
"dispatch_ieee802154: dropping, \
fragmentation buffer is too small, at least {} needed",
total_size
);
return;
}
let payload_length = packet.header.payload_len;
Self::ipv6_to_sixlowpan(
&self.checksum_caps(),
packet,
&ieee_repr,
&mut pkt.buffer[..],
);
pkt.sixlowpan.ll_dst_addr = ieee_repr.dst_addr.unwrap();
pkt.sixlowpan.ll_src_addr = ieee_repr.src_addr.unwrap();
pkt.packet_len = total_size;
// The datagram size that we need to set in the first fragment header is equal to the
// IPv6 payload length + 40.
pkt.sixlowpan.datagram_size = (payload_length + 40) as u16;
let tag = self.get_sixlowpan_fragment_tag();
// We save the tag for the other fragments that will be created when calling `poll`
// multiple times.
pkt.sixlowpan.datagram_tag = tag;
let frag1 = SixlowpanFragRepr::FirstFragment {
size: pkt.sixlowpan.datagram_size,
tag,
};
let fragn = SixlowpanFragRepr::Fragment {
size: pkt.sixlowpan.datagram_size,
tag,
offset: 0,
};
// We calculate how much data we can send in the first fragment and the other
// fragments. The eventual IPv6 sizes of these fragments need to be a multiple of eight
// (except for the last fragment) since the offset field in the fragment is an offset
// in multiples of 8 octets. This is explained in [RFC 4944 § 5.3].
//
// [RFC 4944 § 5.3]: https://datatracker.ietf.org/doc/html/rfc4944#section-5.3
let header_diff = uncompressed_size - compressed_size;
let frag1_size =
(125 - ieee_len - frag1.buffer_len() + header_diff) / 8 * 8 - header_diff;
pkt.sixlowpan.fragn_size = (125 - ieee_len - fragn.buffer_len()) / 8 * 8;
pkt.sent_bytes = frag1_size;
pkt.sixlowpan.datagram_offset = frag1_size + header_diff;
tx_token.set_meta(meta);
tx_token.consume(ieee_len + frag1.buffer_len() + frag1_size, |mut tx_buf| {
// Add the IEEE header.
let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]);
ieee_repr.emit(&mut ieee_packet);
tx_buf = &mut tx_buf[ieee_len..];
// Add the first fragment header
let mut frag1_packet = SixlowpanFragPacket::new_unchecked(&mut tx_buf);
frag1.emit(&mut frag1_packet);
tx_buf = &mut tx_buf[frag1.buffer_len()..];
// Add the buffer part.
tx_buf[..frag1_size].copy_from_slice(&pkt.buffer[..frag1_size]);
});
}
#[cfg(not(feature = "proto-sixlowpan-fragmentation"))]
{
net_debug!(
"Enable the `proto-sixlowpan-fragmentation` feature for fragmentation support."
);
return;
}
} else {
tx_token.set_meta(meta);
// We don't need fragmentation, so we emit everything to the TX token.
tx_token.consume(total_size + ieee_len, |mut tx_buf| {
let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]);
ieee_repr.emit(&mut ieee_packet);
tx_buf = &mut tx_buf[ieee_len..];
Self::ipv6_to_sixlowpan(&self.checksum_caps(), packet, &ieee_repr, tx_buf);
});
}
}
fn ipv6_to_sixlowpan(
checksum_caps: &ChecksumCapabilities,
mut packet: PacketV6,
ieee_repr: &Ieee802154Repr,
mut buffer: &mut [u8],
) {
let last_header = packet.payload.as_sixlowpan_next_header();
let next_header = last_header;
#[cfg(feature = "proto-ipv6-hbh")]
let next_header = if packet.hop_by_hop.is_some() {
SixlowpanNextHeader::Compressed
} else {
next_header
};
#[cfg(feature = "proto-ipv6-routing")]
let next_header = if packet.routing.is_some() {
SixlowpanNextHeader::Compressed
} else {
next_header
};
let iphc_repr = SixlowpanIphcRepr {
src_addr: packet.header.src_addr,
ll_src_addr: ieee_repr.src_addr,
dst_addr: packet.header.dst_addr,
ll_dst_addr: ieee_repr.dst_addr,
next_header,
hop_limit: packet.header.hop_limit,
ecn: None,
dscp: None,
flow_label: None,
};
iphc_repr.emit(&mut SixlowpanIphcPacket::new_unchecked(
&mut buffer[..iphc_repr.buffer_len()],
));
buffer = &mut buffer[iphc_repr.buffer_len()..];
// Emit the Hop-by-Hop header
#[cfg(feature = "proto-ipv6-hbh")]
if let Some(hbh) = packet.hop_by_hop {
#[allow(unused)]
let next_header = last_header;
#[cfg(feature = "proto-ipv6-routing")]
let next_header = if packet.routing.is_some() {
SixlowpanNextHeader::Compressed
} else {
last_header
};
let ext_hdr = SixlowpanExtHeaderRepr {
ext_header_id: SixlowpanExtHeaderId::HopByHopHeader,
next_header,
length: hbh.options.iter().map(|o| o.buffer_len()).sum::<usize>() as u8,
};
ext_hdr.emit(&mut SixlowpanExtHeaderPacket::new_unchecked(
&mut buffer[..ext_hdr.buffer_len()],
));
buffer = &mut buffer[ext_hdr.buffer_len()..];
for opt in &hbh.options {
opt.emit(&mut Ipv6Option::new_unchecked(
&mut buffer[..opt.buffer_len()],
));
buffer = &mut buffer[opt.buffer_len()..];
}
}
// Emit the Routing header
#[cfg(feature = "proto-ipv6-routing")]
if let Some(routing) = &packet.routing {
let ext_hdr = SixlowpanExtHeaderRepr {
ext_header_id: SixlowpanExtHeaderId::RoutingHeader,
next_header,
length: routing.buffer_len() as u8,
};
ext_hdr.emit(&mut SixlowpanExtHeaderPacket::new_unchecked(
&mut buffer[..ext_hdr.buffer_len()],
));
buffer = &mut buffer[ext_hdr.buffer_len()..];
routing.emit(&mut Ipv6RoutingHeader::new_unchecked(
&mut buffer[..routing.buffer_len()],
));
buffer = &mut buffer[routing.buffer_len()..];
}
match &mut packet.payload {
IpPayload::Icmpv6(icmp_repr) => {
icmp_repr.emit(
&packet.header.src_addr.into(),
&packet.header.dst_addr.into(),
&mut Icmpv6Packet::new_unchecked(&mut buffer[..icmp_repr.buffer_len()]),
checksum_caps,
);
}
#[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
IpPayload::Udp(udp_repr, payload) => {
let udp_repr = SixlowpanUdpNhcRepr(*udp_repr);
udp_repr.emit(
&mut SixlowpanUdpNhcPacket::new_unchecked(
&mut buffer[..udp_repr.header_len() + payload.len()],
),
&iphc_repr.src_addr,
&iphc_repr.dst_addr,
payload.len(),
|buf| buf.copy_from_slice(payload),
checksum_caps,
);
}
#[cfg(feature = "socket-tcp")]
IpPayload::Tcp(tcp_repr) => {
tcp_repr.emit(
&mut TcpPacket::new_unchecked(&mut buffer[..tcp_repr.buffer_len()]),
&packet.header.src_addr.into(),
&packet.header.dst_addr.into(),
checksum_caps,
);
}
#[cfg(feature = "socket-raw")]
IpPayload::Raw(_raw) => todo!(),
#[allow(unreachable_patterns)]
_ => unreachable!(),
}
}
/// Calculates three sizes:
/// - total size: the size of a compressed IPv6 packet
/// - compressed header size: the size of the compressed headers
/// - uncompressed header size: the size of the headers that are not compressed
/// They are returned as a tuple in the same order.
fn compressed_packet_size(
packet: &PacketV6,
ieee_repr: &Ieee802154Repr,
) -> (usize, usize, usize) {
let last_header = packet.payload.as_sixlowpan_next_header();
let next_header = last_header;
#[cfg(feature = "proto-ipv6-hbh")]
let next_header = if packet.hop_by_hop.is_some() {
SixlowpanNextHeader::Compressed
} else {
next_header
};
#[cfg(feature = "proto-ipv6-routing")]
let next_header = if packet.routing.is_some() {
SixlowpanNextHeader::Compressed
} else {
next_header
};
let iphc = SixlowpanIphcRepr {
src_addr: packet.header.src_addr,
ll_src_addr: ieee_repr.src_addr,
dst_addr: packet.header.dst_addr,
ll_dst_addr: ieee_repr.dst_addr,
next_header,
hop_limit: packet.header.hop_limit,
ecn: None,
dscp: None,
flow_label: None,
};
let mut total_size = iphc.buffer_len();
let mut compressed_hdr_size = iphc.buffer_len();
let mut uncompressed_hdr_size = packet.header.buffer_len();
// Add the hop-by-hop to the sizes.
#[cfg(feature = "proto-ipv6-hbh")]
if let Some(hbh) = &packet.hop_by_hop {
#[allow(unused)]
let next_header = last_header;
#[cfg(feature = "proto-ipv6-routing")]
let next_header = if packet.routing.is_some() {
SixlowpanNextHeader::Compressed
} else {
last_header
};
let options_size = hbh.options.iter().map(|o| o.buffer_len()).sum::<usize>();
let ext_hdr = SixlowpanExtHeaderRepr {
ext_header_id: SixlowpanExtHeaderId::HopByHopHeader,
next_header,
length: hbh.buffer_len() as u8 + options_size as u8,
};
total_size += ext_hdr.buffer_len() + options_size;
compressed_hdr_size += ext_hdr.buffer_len() + options_size;
uncompressed_hdr_size += hbh.buffer_len() + options_size;
}
// Add the routing header to the sizes.
#[cfg(feature = "proto-ipv6-routing")]
if let Some(routing) = &packet.routing {
let ext_hdr = SixlowpanExtHeaderRepr {
ext_header_id: SixlowpanExtHeaderId::RoutingHeader,
next_header,
length: routing.buffer_len() as u8,
};
total_size += ext_hdr.buffer_len() + routing.buffer_len();
compressed_hdr_size += ext_hdr.buffer_len() + routing.buffer_len();
uncompressed_hdr_size += routing.buffer_len();
}
match packet.payload {
#[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
IpPayload::Udp(udp_hdr, payload) => {
uncompressed_hdr_size += udp_hdr.header_len();
let udp_hdr = SixlowpanUdpNhcRepr(udp_hdr);
compressed_hdr_size += udp_hdr.header_len();
total_size += udp_hdr.header_len() + payload.len();
}
_ => {
total_size += packet.header.payload_len;
}
}
(total_size, compressed_hdr_size, uncompressed_hdr_size)
}
#[cfg(feature = "proto-sixlowpan-fragmentation")]
pub(super) fn dispatch_sixlowpan_frag<Tx: TxToken>(
&mut self,
tx_token: Tx,
ieee_repr: Ieee802154Repr,
frag: &mut Fragmenter,
) {
// Create the FRAG_N header.
let fragn = SixlowpanFragRepr::Fragment {
size: frag.sixlowpan.datagram_size,
tag: frag.sixlowpan.datagram_tag,
offset: (frag.sixlowpan.datagram_offset / 8) as u8,
};
let ieee_len = ieee_repr.buffer_len();
let frag_size = (frag.packet_len - frag.sent_bytes).min(frag.sixlowpan.fragn_size);
tx_token.consume(
ieee_repr.buffer_len() + fragn.buffer_len() + frag_size,
|mut tx_buf| {
let mut ieee_packet = Ieee802154Frame::new_unchecked(&mut tx_buf[..ieee_len]);
ieee_repr.emit(&mut ieee_packet);
tx_buf = &mut tx_buf[ieee_len..];
let mut frag_packet =
SixlowpanFragPacket::new_unchecked(&mut tx_buf[..fragn.buffer_len()]);
fragn.emit(&mut frag_packet);
tx_buf = &mut tx_buf[fragn.buffer_len()..];
// Add the buffer part
tx_buf[..frag_size].copy_from_slice(&frag.buffer[frag.sent_bytes..][..frag_size]);
frag.sent_bytes += frag_size;
frag.sixlowpan.datagram_offset += frag_size;
},
);
}
}
#[cfg(test)]
#[cfg(all(feature = "proto-rpl", feature = "proto-ipv6-hbh"))]
mod tests {
use super::*;
static SIXLOWPAN_COMPRESSED_RPL_DAO: [u8; 99] = [
0x61, 0xdc, 0x45, 0xcd, 0xab, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03, 0x00,
0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x7e, 0xf7, 0x00, 0xe0, 0x3a, 0x06, 0x63, 0x04, 0x00,
0x1e, 0x08, 0x00, 0x9b, 0x02, 0x3e, 0x63, 0x1e, 0x40, 0x00, 0xf1, 0xfd, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x05, 0x12, 0x00,
0x80, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x03, 0x00, 0x03,
0x00, 0x03, 0x06, 0x14, 0x00, 0x00, 0x00, 0x1e, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
];
static SIXLOWPAN_UNCOMPRESSED_RPL_DAO: [u8; 114] = [
0x60, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x40, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x3a, 0x00, 0x63, 0x04, 0x00,
0x1e, 0x08, 0x00, 0x9b, 0x02, 0x3e, 0x63, 0x1e, 0x40, 0x00, 0xf1, 0xfd, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x05, 0x12, 0x00,
0x80, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x03, 0x00, 0x03,
0x00, 0x03, 0x06, 0x14, 0x00, 0x00, 0x00, 0x1e, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01,
];
#[test]
fn test_sixlowpan_decompress_hop_by_hop_with_icmpv6() {
let address_context = [SixlowpanAddressContext([
0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
])];
let ieee_frame = Ieee802154Frame::new_checked(&SIXLOWPAN_COMPRESSED_RPL_DAO).unwrap();
let ieee_repr = Ieee802154Repr::parse(&ieee_frame).unwrap();
let mut buffer = [0u8; 256];
let len = InterfaceInner::sixlowpan_to_ipv6(
&address_context,
&ieee_repr,
ieee_frame.payload().unwrap(),
None,
&mut buffer[..],
)
.unwrap();
assert_eq!(&buffer[..len], &SIXLOWPAN_UNCOMPRESSED_RPL_DAO);
}
#[test]
fn test_sixlowpan_compress_hop_by_hop_with_icmpv6() {
let ieee_repr = Ieee802154Repr {
frame_type: Ieee802154FrameType::Data,
security_enabled: false,
frame_pending: false,
ack_request: true,
sequence_number: Some(69),
pan_id_compression: true,
frame_version: Ieee802154FrameVersion::Ieee802154_2006,
dst_pan_id: Some(Ieee802154Pan(43981)),
dst_addr: Some(Ieee802154Address::Extended([0, 1, 0, 1, 0, 1, 0, 1])),
src_pan_id: None,
src_addr: Some(Ieee802154Address::Extended([0, 3, 0, 3, 0, 3, 0, 3])),
};
let mut ip_packet = PacketV6 {
header: Ipv6Repr {
src_addr: Ipv6Address::from_bytes(&[
253, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 3, 0, 3, 0, 3,
]),
dst_addr: Ipv6Address::from_bytes(&[
253, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 1, 0, 1,
]),
next_header: IpProtocol::Icmpv6,
payload_len: 66,
hop_limit: 64,
},
#[cfg(feature = "proto-ipv6-hbh")]
hop_by_hop: None,
#[cfg(feature = "proto-ipv6-fragmentation")]
fragment: None,
#[cfg(feature = "proto-ipv6-routing")]
routing: None,
payload: IpPayload::Icmpv6(Icmpv6Repr::Rpl(RplRepr::DestinationAdvertisementObject {
rpl_instance_id: RplInstanceId::Global(30),
expect_ack: false,
sequence: 241,
dodag_id: Some(Ipv6Address::from_bytes(&[
253, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 1, 0, 1,
])),
options: &[],
})),
};
let (total_size, _, _) = InterfaceInner::compressed_packet_size(&mut ip_packet, &ieee_repr);
let mut buffer = vec![0u8; total_size];
InterfaceInner::ipv6_to_sixlowpan(
&ChecksumCapabilities::default(),
ip_packet,
&ieee_repr,
&mut buffer[..total_size],
);
let result = [
0x7e, 0x0, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x3, 0x0, 0x3, 0x0, 0x3, 0x0,
0x3, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1,
0xe0, 0x3a, 0x6, 0x63, 0x4, 0x0, 0x1e, 0x3, 0x0, 0x9b, 0x2, 0x3e, 0x63, 0x1e, 0x40,
0x0, 0xf1, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0,
0x1, 0x5, 0x12, 0x0, 0x80, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x3, 0x0, 0x3,
0x0, 0x3, 0x0, 0x3, 0x6, 0x14, 0x0, 0x0, 0x0, 0x1e, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x2, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1,
];
assert_eq!(&result, &result);
}
#[test]
fn test_sixlowpan_compress_hop_by_hop_with_udp() {
let ieee_repr = Ieee802154Repr {
frame_type: Ieee802154FrameType::Data,
security_enabled: false,
frame_pending: false,
ack_request: true,
sequence_number: Some(69),
pan_id_compression: true,
frame_version: Ieee802154FrameVersion::Ieee802154_2006,
dst_pan_id: Some(Ieee802154Pan(43981)),
dst_addr: Some(Ieee802154Address::Extended([0, 1, 0, 1, 0, 1, 0, 1])),
src_pan_id: None,
src_addr: Some(Ieee802154Address::Extended([0, 3, 0, 3, 0, 3, 0, 3])),
};
let addr = Ipv6Address::from_bytes(&[253, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 3, 0, 3, 0, 3]);
let parent_address =
Ipv6Address::from_bytes(&[253, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 1, 0, 1]);
let mut hbh_options = heapless::Vec::new();
hbh_options
.push(Ipv6OptionRepr::Rpl(RplHopByHopRepr {
down: false,
rank_error: false,
forwarding_error: false,
instance_id: RplInstanceId::from(0x1e),
sender_rank: 0x300,
}))
.unwrap();
let mut ip_packet = PacketV6 {
header: Ipv6Repr {
src_addr: addr,
dst_addr: parent_address,
next_header: IpProtocol::Icmpv6,
payload_len: 66,
hop_limit: 64,
},
#[cfg(feature = "proto-ipv6-hbh")]
hop_by_hop: Some(Ipv6HopByHopRepr {
options: hbh_options,
}),
#[cfg(feature = "proto-ipv6-fragmentation")]
fragment: None,
#[cfg(feature = "proto-ipv6-routing")]
routing: None,
payload: IpPayload::Icmpv6(Icmpv6Repr::Rpl(RplRepr::DestinationAdvertisementObject {
rpl_instance_id: RplInstanceId::Global(30),
expect_ack: false,
sequence: 241,
dodag_id: Some(Ipv6Address::from_bytes(&[
253, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 1, 0, 1,
])),
options: &[
5, 18, 0, 128, 253, 0, 0, 0, 0, 0, 0, 0, 2, 3, 0, 3, 0, 3, 0, 3, 6, 20, 0, 0,
0, 30, 253, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 1, 0, 1,
],
})),
};
let (total_size, _, _) = InterfaceInner::compressed_packet_size(&mut ip_packet, &ieee_repr);
let mut buffer = vec![0u8; total_size];
InterfaceInner::ipv6_to_sixlowpan(
&ChecksumCapabilities::default(),
ip_packet,
&ieee_repr,
&mut buffer[..total_size],
);
let result = [
0x7e, 0x0, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x3, 0x0, 0x3, 0x0, 0x3, 0x0,
0x3, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1,
0xe0, 0x3a, 0x6, 0x63, 0x4, 0x0, 0x1e, 0x3, 0x0, 0x9b, 0x2, 0x3e, 0x63, 0x1e, 0x40,
0x0, 0xf1, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0,
0x1, 0x5, 0x12, 0x0, 0x80, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x3, 0x0, 0x3,
0x0, 0x3, 0x0, 0x3, 0x6, 0x14, 0x0, 0x0, 0x0, 0x1e, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x2, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1,
];
assert_eq!(&buffer[..total_size], &result);
}
}