Эх сурвалжийг харах

Merge #469

469: Add IEEE 802.15.4/6LoWPAN support r=Dirbaio a=thibautvdv

This adds the IEEE 802.15.4 frame representation.
Still a work in progress and interested to know what you think about this.

I really would like to add 6LowPAN as well, however I'm not sure where to put this in smoltcp, since 6LowPAN is kind of weird.

Co-authored-by: Thibaut Vandervelden <thvdveld@vub.be>
Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
bors[bot] 3 жил өмнө
parent
commit
c8141bbe69

+ 1 - 1
.github/workflows/test.yml

@@ -73,7 +73,7 @@ jobs:
 
 
         features:
         features:
           # These feature sets cannot run tests, so we only check they build.
           # These feature sets cannot run tests, so we only check they build.
-          - rand-custom-impl medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async
+          - rand-custom-impl medium-ip medium-ethernet medium-ieee802154 proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async
           - rand-custom-impl defmt defmt-trace medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async
           - rand-custom-impl defmt defmt-trace medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async
 
 
     steps:
     steps:

+ 13 - 3
Cargo.toml

@@ -37,18 +37,24 @@ verbose = []
 rand-custom-impl = []
 rand-custom-impl = []
 "medium-ethernet" = ["socket"]
 "medium-ethernet" = ["socket"]
 "medium-ip" = ["socket"]
 "medium-ip" = ["socket"]
-"phy-raw_socket" = ["std", "libc", "medium-ethernet"]
+"medium-ieee802154" = ["socket", "proto-sixlowpan"]
+
+"phy-raw_socket" = ["std", "libc"]
 "phy-tuntap_interface" = ["std", "libc", "medium-ethernet"]
 "phy-tuntap_interface" = ["std", "libc", "medium-ethernet"]
+
 "proto-ipv4" = []
 "proto-ipv4" = []
 "proto-igmp" = ["proto-ipv4"]
 "proto-igmp" = ["proto-ipv4"]
 "proto-dhcpv4" = ["proto-ipv4"]
 "proto-dhcpv4" = ["proto-ipv4"]
 "proto-ipv6" = []
 "proto-ipv6" = []
+"proto-sixlowpan" = ["proto-ipv6"]
+
 "socket" = []
 "socket" = []
 "socket-raw" = ["socket"]
 "socket-raw" = ["socket"]
 "socket-udp" = ["socket"]
 "socket-udp" = ["socket"]
 "socket-tcp" = ["socket"]
 "socket-tcp" = ["socket"]
 "socket-icmp" = ["socket"]
 "socket-icmp" = ["socket"]
 "socket-dhcpv4" = ["socket", "medium-ethernet", "proto-dhcpv4"]
 "socket-dhcpv4" = ["socket", "medium-ethernet", "proto-dhcpv4"]
+
 "async" = []
 "async" = []
 
 
 defmt-trace = []
 defmt-trace = []
@@ -59,9 +65,9 @@ defmt-error = []
 
 
 default = [
 default = [
   "std", "log", # needed for `cargo test --no-default-features --features default` :/
   "std", "log", # needed for `cargo test --no-default-features --features default` :/
-  "medium-ethernet", "medium-ip",
+  "medium-ethernet", "medium-ip", "medium-ieee802154",
   "phy-raw_socket", "phy-tuntap_interface",
   "phy-raw_socket", "phy-tuntap_interface",
-  "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6",
+  "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "proto-sixlowpan",
   "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-dhcpv4",
   "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-dhcpv4",
   "async"
   "async"
 ]
 ]
@@ -110,5 +116,9 @@ required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interfac
 name = "dhcp_client"
 name = "dhcp_client"
 required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-dhcpv4", "socket-raw"]
 required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-dhcpv4", "socket-raw"]
 
 
+[[example]]
+name = "sixlowpan"
+required-features = ["std", "medium-ieee802154", "phy-raw_socket", "proto-sixlowpan", "socket-udp"]
+
 [profile.release]
 [profile.release]
 debug = 2
 debug = 2

+ 1 - 1
examples/benchmark.rs

@@ -100,7 +100,7 @@ fn main() {
     let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs);
     let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs);
     if medium == Medium::Ethernet {
     if medium == Medium::Ethernet {
         builder = builder
         builder = builder
-            .ethernet_addr(ethernet_addr)
+            .hardware_addr(ethernet_addr.into())
             .neighbor_cache(neighbor_cache);
             .neighbor_cache(neighbor_cache);
     }
     }
     let mut iface = builder.finalize();
     let mut iface = builder.finalize();

+ 1 - 1
examples/client.rs

@@ -47,7 +47,7 @@ fn main() {
         .routes(routes);
         .routes(routes);
     if medium == Medium::Ethernet {
     if medium == Medium::Ethernet {
         builder = builder
         builder = builder
-            .ethernet_addr(ethernet_addr)
+            .hardware_addr(ethernet_addr.into())
             .neighbor_cache(neighbor_cache);
             .neighbor_cache(neighbor_cache);
     }
     }
     let mut iface = builder.finalize();
     let mut iface = builder.finalize();

+ 1 - 1
examples/dhcp_client.rs

@@ -39,7 +39,7 @@ fn main() {
         .routes(routes);
         .routes(routes);
     if medium == Medium::Ethernet {
     if medium == Medium::Ethernet {
         builder = builder
         builder = builder
-            .ethernet_addr(ethernet_addr)
+            .hardware_addr(ethernet_addr.into())
             .neighbor_cache(neighbor_cache);
             .neighbor_cache(neighbor_cache);
     }
     }
     let mut iface = builder.finalize();
     let mut iface = builder.finalize();

+ 1 - 1
examples/httpclient.rs

@@ -53,7 +53,7 @@ fn main() {
         .routes(routes);
         .routes(routes);
     if medium == Medium::Ethernet {
     if medium == Medium::Ethernet {
         builder = builder
         builder = builder
-            .ethernet_addr(ethernet_addr)
+            .hardware_addr(ethernet_addr.into())
             .neighbor_cache(neighbor_cache);
             .neighbor_cache(neighbor_cache);
     }
     }
     let mut iface = builder.finalize();
     let mut iface = builder.finalize();

+ 1 - 1
examples/loopback.rs

@@ -95,7 +95,7 @@ fn main() {
 
 
     let mut ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)];
     let mut ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)];
     let mut iface = InterfaceBuilder::new(device)
     let mut iface = InterfaceBuilder::new(device)
-        .ethernet_addr(EthernetAddress::default())
+        .hardware_addr(EthernetAddress::default().into())
         .neighbor_cache(neighbor_cache)
         .neighbor_cache(neighbor_cache)
         .ip_addrs(ip_addrs)
         .ip_addrs(ip_addrs)
         .finalize();
         .finalize();

+ 1 - 1
examples/multicast.rs

@@ -38,7 +38,7 @@ fn main() {
     let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24);
     let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24);
     let mut ipv4_multicast_storage = [None; 1];
     let mut ipv4_multicast_storage = [None; 1];
     let mut iface = InterfaceBuilder::new(device)
     let mut iface = InterfaceBuilder::new(device)
-        .ethernet_addr(ethernet_addr)
+        .hardware_addr(ethernet_addr.into())
         .neighbor_cache(neighbor_cache)
         .neighbor_cache(neighbor_cache)
         .ip_addrs([ip_addr])
         .ip_addrs([ip_addr])
         .ipv4_multicast_groups(&mut ipv4_multicast_storage[..])
         .ipv4_multicast_groups(&mut ipv4_multicast_storage[..])

+ 1 - 1
examples/ping.rs

@@ -132,7 +132,7 @@ fn main() {
         .routes(routes);
         .routes(routes);
     if medium == Medium::Ethernet {
     if medium == Medium::Ethernet {
         builder = builder
         builder = builder
-            .ethernet_addr(ethernet_addr)
+            .hardware_addr(ethernet_addr.into())
             .neighbor_cache(neighbor_cache);
             .neighbor_cache(neighbor_cache);
     }
     }
     let mut iface = builder.finalize();
     let mut iface = builder.finalize();

+ 1 - 1
examples/server.rs

@@ -59,7 +59,7 @@ fn main() {
     let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs);
     let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs);
     if medium == Medium::Ethernet {
     if medium == Medium::Ethernet {
         builder = builder
         builder = builder
-            .ethernet_addr(ethernet_addr)
+            .hardware_addr(ethernet_addr.into())
             .neighbor_cache(neighbor_cache);
             .neighbor_cache(neighbor_cache);
     }
     }
     let mut iface = builder.finalize();
     let mut iface = builder.finalize();

+ 134 - 0
examples/sixlowpan.rs

@@ -0,0 +1,134 @@
+//! 6lowpan exmaple
+//!
+//! This example is designed to run using the Linux ieee802154/6lowpan support,
+//! using mac802154_hwsim.
+//!
+//! mac802154_hwsim allows you to create multiple "virtual" radios and specify
+//! which is in range with which. This is very useful for testing without
+//! needing real hardware. By default it creates two interfaces `wpan0` and
+//! `wpan1` that are in range with each other. You can customize this with
+//! the `wpan-hwsim` tool.
+//!
+//! We'll configure Linux to speak 6lowpan on `wpan0`, and leave `wpan1`
+//! unconfigured so smoltcp can use it with a raw socket.
+//!
+//! # Setup
+//!
+//!     modprobe mac802154_hwsim
+//!
+//!     ip link set wpan0 down
+//!     ip link set wpan1 down
+//!     iwpan dev wpan0 set pan_id 0xbeef
+//!     iwpan dev wpan1 set pan_id 0xbeef
+//!     ip link add link wpan0 name lowpan0 type lowpan
+//!     ip link set wpan0 up
+//!     ip link set wpan1 up
+//!     ip link set lowpan0 up
+//!
+//! # Running
+//!
+//! Run it with `sudo ./target/debug/examples/sixlowpan`.
+//!
+//! You can set wireshark to sniff on interface `wpan0` to see the packets.
+//!
+//! Ping it with `ping fe80::180b:4242:4242:4242%lowpan0`.
+//!
+//! Speak UDP with `nc -uv fe80::180b:4242:4242:4242%lowpan0 6969`.
+//!
+//! # Teardown
+//!
+//!     rmmod mac802154_hwsim
+//!
+
+mod utils;
+
+use log::debug;
+use std::collections::BTreeMap;
+use std::os::unix::io::AsRawFd;
+use std::str;
+
+use smoltcp::iface::{InterfaceBuilder, NeighborCache};
+use smoltcp::phy::{wait as phy_wait, Medium, RawSocket};
+use smoltcp::socket::SocketSet;
+use smoltcp::socket::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer};
+use smoltcp::time::Instant;
+use smoltcp::wire::{Ieee802154Pan, IpAddress, IpCidr};
+
+fn main() {
+    utils::setup_logging("");
+
+    let (mut opts, mut free) = utils::create_options();
+    utils::add_middleware_options(&mut opts, &mut free);
+
+    let mut matches = utils::parse_options(&opts, free);
+
+    let device = RawSocket::new("wpan1", Medium::Ieee802154).unwrap();
+
+    let fd = device.as_raw_fd();
+    let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
+
+    let neighbor_cache = NeighborCache::new(BTreeMap::new());
+
+    let udp_rx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 64]);
+    let udp_tx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 128]);
+    let udp_socket = UdpSocket::new(udp_rx_buffer, udp_tx_buffer);
+
+    let ieee802154_addr = smoltcp::wire::Ieee802154Address::Extended([
+        0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
+    ]);
+    let ip_addrs = [IpCidr::new(
+        IpAddress::v6(0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242),
+        64,
+    )];
+
+    let mut builder = InterfaceBuilder::new(device)
+        .ip_addrs(ip_addrs)
+        .pan_id(Ieee802154Pan(0xbeef));
+    builder = builder
+        .hardware_addr(ieee802154_addr.into())
+        .neighbor_cache(neighbor_cache);
+    let mut iface = builder.finalize();
+
+    let mut sockets = SocketSet::new(vec![]);
+    let udp_handle = sockets.add(udp_socket);
+
+    loop {
+        let timestamp = Instant::now();
+        match iface.poll(&mut sockets, timestamp) {
+            Ok(_) => {}
+            Err(e) => {
+                debug!("poll error: {}", e);
+            }
+        }
+
+        // udp:6969: respond "hello"
+        {
+            let mut socket = sockets.get::<UdpSocket>(udp_handle);
+            if !socket.is_open() {
+                socket.bind(6969).unwrap()
+            }
+
+            let client = match socket.recv() {
+                Ok((data, endpoint)) => {
+                    debug!(
+                        "udp:6969 recv data: {:?} from {}",
+                        str::from_utf8(data).unwrap(),
+                        endpoint
+                    );
+                    Some(endpoint)
+                }
+                Err(_) => None,
+            };
+            if let Some(endpoint) = client {
+                let data = b"hello\n";
+                debug!(
+                    "udp:6969 send data: {:?}",
+                    str::from_utf8(data.as_ref()).unwrap()
+                );
+                socket.send_slice(data, endpoint).unwrap();
+            }
+        }
+
+        phy_wait(fd, iface.poll_delay(&sockets, timestamp)).expect("wait error");
+    }
+}

+ 1 - 1
examples/tcpdump.rs

@@ -7,7 +7,7 @@ use std::os::unix::io::AsRawFd;
 
 
 fn main() {
 fn main() {
     let ifname = env::args().nth(1).unwrap();
     let ifname = env::args().nth(1).unwrap();
-    let mut socket = RawSocket::new(ifname.as_ref()).unwrap();
+    let mut socket = RawSocket::new(ifname.as_ref(), smoltcp::phy::Medium::Ethernet).unwrap();
     loop {
     loop {
         phy_wait(socket.as_raw_fd(), None).unwrap();
         phy_wait(socket.as_raw_fd(), None).unwrap();
         let (rx_token, _) = socket.receive().unwrap();
         let (rx_token, _) = socket.receive().unwrap();

+ 0 - 6
examples/utils.rs

@@ -12,7 +12,6 @@ use std::process;
 use std::str::{self, FromStr};
 use std::str::{self, FromStr};
 use std::time::{SystemTime, UNIX_EPOCH};
 use std::time::{SystemTime, UNIX_EPOCH};
 
 
-use smoltcp::phy::RawSocket;
 #[cfg(feature = "phy-tuntap_interface")]
 #[cfg(feature = "phy-tuntap_interface")]
 use smoltcp::phy::TunTapInterface;
 use smoltcp::phy::TunTapInterface;
 use smoltcp::phy::{Device, FaultInjector, Medium, Tracer};
 use smoltcp::phy::{Device, FaultInjector, Medium, Tracer};
@@ -112,11 +111,6 @@ pub fn parse_tuntap_options(matches: &mut Matches) -> TunTapInterface {
     }
     }
 }
 }
 
 
-pub fn parse_raw_socket_options(matches: &mut Matches) -> RawSocket {
-    let interface = matches.free.remove(0);
-    RawSocket::new(&interface).unwrap()
-}
-
 pub fn add_middleware_options(opts: &mut Options, _free: &mut Vec<&str>) {
 pub fn add_middleware_options(opts: &mut Options, _free: &mut Vec<&str>) {
     opts.optopt("", "pcap", "Write a packet capture file", "FILE");
     opts.optopt("", "pcap", "Write a packet capture file", "FILE");
     opts.optopt(
     opts.optopt(

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 559 - 136
src/iface/interface.rs


+ 14 - 6
src/iface/mod.rs

@@ -4,19 +4,27 @@ The `iface` module deals with the *network interfaces*. It filters incoming fram
 provides lookup and caching of hardware addresses, and handles management packets.
 provides lookup and caching of hardware addresses, and handles management packets.
 */
 */
 
 
-#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))]
+#[cfg(any(
+    feature = "medium-ethernet",
+    feature = "medium-ip",
+    feature = "medium-ieee802154"
+))]
 mod interface;
 mod interface;
-#[cfg(feature = "medium-ethernet")]
+#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
 mod neighbor;
 mod neighbor;
 mod route;
 mod route;
 
 
-#[cfg(feature = "medium-ethernet")]
+#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
 pub(crate) use self::neighbor::Answer as NeighborAnswer;
 pub(crate) use self::neighbor::Answer as NeighborAnswer;
-#[cfg(feature = "medium-ethernet")]
+#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
 pub use self::neighbor::Cache as NeighborCache;
 pub use self::neighbor::Cache as NeighborCache;
-#[cfg(feature = "medium-ethernet")]
+#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
 pub use self::neighbor::Neighbor;
 pub use self::neighbor::Neighbor;
 pub use self::route::{Route, Routes};
 pub use self::route::{Route, Routes};
 
 
-#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))]
+#[cfg(any(
+    feature = "medium-ethernet",
+    feature = "medium-ip",
+    feature = "medium-ieee802154"
+))]
 pub use self::interface::{Interface, InterfaceBuilder};
 pub use self::interface::{Interface, InterfaceBuilder};

+ 11 - 11
src/iface/neighbor.rs

@@ -4,7 +4,7 @@
 use managed::ManagedMap;
 use managed::ManagedMap;
 
 
 use crate::time::{Duration, Instant};
 use crate::time::{Duration, Instant};
-use crate::wire::{EthernetAddress, IpAddress};
+use crate::wire::{HardwareAddress, IpAddress};
 
 
 /// A cached neighbor.
 /// A cached neighbor.
 ///
 ///
@@ -13,7 +13,7 @@ use crate::wire::{EthernetAddress, IpAddress};
 #[derive(Debug, Clone, Copy)]
 #[derive(Debug, Clone, Copy)]
 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Neighbor {
 pub struct Neighbor {
-    hardware_addr: EthernetAddress,
+    hardware_addr: HardwareAddress,
     expires_at: Instant,
     expires_at: Instant,
 }
 }
 
 
@@ -22,7 +22,7 @@ pub struct Neighbor {
 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub(crate) enum Answer {
 pub(crate) enum Answer {
     /// The neighbor address is in the cache and not expired.
     /// The neighbor address is in the cache and not expired.
-    Found(EthernetAddress),
+    Found(HardwareAddress),
     /// The neighbor address is not in the cache, or has expired.
     /// The neighbor address is not in the cache, or has expired.
     NotFound,
     NotFound,
     /// The neighbor address is not in the cache, or has expired,
     /// The neighbor address is not in the cache, or has expired,
@@ -115,7 +115,7 @@ impl<'a> Cache<'a> {
     pub fn fill(
     pub fn fill(
         &mut self,
         &mut self,
         protocol_addr: IpAddress,
         protocol_addr: IpAddress,
-        hardware_addr: EthernetAddress,
+        hardware_addr: HardwareAddress,
         timestamp: Instant,
         timestamp: Instant,
     ) {
     ) {
         debug_assert!(protocol_addr.is_unicast());
         debug_assert!(protocol_addr.is_unicast());
@@ -196,9 +196,7 @@ impl<'a> Cache<'a> {
     }
     }
 
 
     pub(crate) fn lookup(&self, protocol_addr: &IpAddress, timestamp: Instant) -> Answer {
     pub(crate) fn lookup(&self, protocol_addr: &IpAddress, timestamp: Instant) -> Answer {
-        if protocol_addr.is_broadcast() {
-            return Answer::Found(EthernetAddress::BROADCAST);
-        }
+        assert!(protocol_addr.is_unicast());
 
 
         if let Some(&Neighbor {
         if let Some(&Neighbor {
             expires_at,
             expires_at,
@@ -228,10 +226,12 @@ mod test {
     use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_IP_ADDR_4};
     use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_IP_ADDR_4};
     use std::collections::BTreeMap;
     use std::collections::BTreeMap;
 
 
-    const HADDR_A: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 1]);
-    const HADDR_B: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 2]);
-    const HADDR_C: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 3]);
-    const HADDR_D: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 4]);
+    use crate::wire::EthernetAddress;
+
+    const HADDR_A: HardwareAddress = HardwareAddress::Ethernet(EthernetAddress([0, 0, 0, 0, 0, 1]));
+    const HADDR_B: HardwareAddress = HardwareAddress::Ethernet(EthernetAddress([0, 0, 0, 0, 0, 2]));
+    const HADDR_C: HardwareAddress = HardwareAddress::Ethernet(EthernetAddress([0, 0, 0, 0, 0, 3]));
+    const HADDR_D: HardwareAddress = HardwareAddress::Ethernet(EthernetAddress([0, 0, 0, 0, 0, 4]));
 
 
     #[test]
     #[test]
     fn test_fill() {
     fn test_fill() {

+ 17 - 4
src/lib.rs

@@ -97,8 +97,12 @@ compile_error!("at least one socket needs to be enabled"); */
 #[cfg(any(feature = "std", feature = "alloc"))]
 #[cfg(any(feature = "std", feature = "alloc"))]
 extern crate alloc;
 extern crate alloc;
 
 
-#[cfg(not(any(feature = "proto-ipv4", feature = "proto-ipv6")))]
-compile_error!("You must enable at least one of the following features: proto-ipv4, proto-ipv6");
+#[cfg(not(any(
+    feature = "proto-ipv4",
+    feature = "proto-ipv6",
+    feature = "proto-sixlowpan"
+)))]
+compile_error!("You must enable at least one of the following features: proto-ipv4, proto-ipv6, proto-sixlowpan");
 
 
 #[cfg(all(
 #[cfg(all(
     feature = "socket",
     feature = "socket",
@@ -113,9 +117,13 @@ compile_error!("If you enable the socket feature, you must enable at least one o
 
 
 #[cfg(all(
 #[cfg(all(
     feature = "socket",
     feature = "socket",
-    not(any(feature = "medium-ethernet", feature = "medium-ip",))
+    not(any(
+        feature = "medium-ethernet",
+        feature = "medium-ip",
+        feature = "medium-ieee802154",
+    ))
 ))]
 ))]
-compile_error!("If you enable the socket feature, you must enable at least one of the following features: medium-ip, medium-ethernet");
+compile_error!("If you enable the socket feature, you must enable at least one of the following features: medium-ip, medium-ethernet, medium-ieee802154");
 
 
 #[cfg(all(feature = "defmt", feature = "log"))]
 #[cfg(all(feature = "defmt", feature = "log"))]
 compile_error!("You must enable at most one of the following features: defmt, log");
 compile_error!("You must enable at most one of the following features: defmt, log");
@@ -174,6 +182,10 @@ pub enum Error {
     /// An incoming packet was recognized but contradicted internal state.
     /// An incoming packet was recognized but contradicted internal state.
     /// E.g. a TCP packet addressed to a socket that doesn't exist.
     /// E.g. a TCP packet addressed to a socket that doesn't exist.
     Dropped,
     Dropped,
+
+    /// An incoming packet was recognized but some parts are not supported by smoltcp.
+    /// E.g. some bit configuration in a packet header is not supported, but is defined in an RFC.
+    NotSupported,
 }
 }
 
 
 #[cfg(feature = "std")]
 #[cfg(feature = "std")]
@@ -195,6 +207,7 @@ impl fmt::Display for Error {
             Error::Fragmented => write!(f, "fragmented packet"),
             Error::Fragmented => write!(f, "fragmented packet"),
             Error::Malformed => write!(f, "malformed packet"),
             Error::Malformed => write!(f, "malformed packet"),
             Error::Dropped => write!(f, "dropped by socket"),
             Error::Dropped => write!(f, "dropped by socket"),
+            Error::NotSupported => write!(f, "not supported by smoltcp"),
         }
         }
     }
     }
 }
 }

+ 21 - 2
src/phy/mod.rs

@@ -254,6 +254,8 @@ impl DeviceCapabilities {
             }
             }
             #[cfg(feature = "medium-ip")]
             #[cfg(feature = "medium-ip")]
             Medium::Ip => self.max_transmission_unit,
             Medium::Ip => self.max_transmission_unit,
+            #[cfg(feature = "medium-ieee802154")]
+            Medium::Ieee802154 => self.max_transmission_unit, // TODO(thvdveld): what is the MTU for Medium::IEEE802
         }
         }
     }
     }
 }
 }
@@ -275,15 +277,32 @@ pub enum Medium {
     /// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
     /// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
     #[cfg(feature = "medium-ip")]
     #[cfg(feature = "medium-ip")]
     Ip,
     Ip,
+
+    #[cfg(feature = "medium-ieee802154")]
+    Ieee802154,
 }
 }
 
 
 impl Default for Medium {
 impl Default for Medium {
     fn default() -> Medium {
     fn default() -> Medium {
         #[cfg(feature = "medium-ethernet")]
         #[cfg(feature = "medium-ethernet")]
         return Medium::Ethernet;
         return Medium::Ethernet;
-        #[cfg(all(feature = "medium-ip", not(feature = "medium-ethernet")))]
+        #[cfg(all(
+            feature = "medium-ip",
+            not(feature = "medium-ethernet"),
+            not(feature = "medium-ieee802154")
+        ))]
         return Medium::Ip;
         return Medium::Ip;
-        #[cfg(all(not(feature = "medium-ip"), not(feature = "medium-ethernet")))]
+        #[cfg(all(
+            feature = "medium-ieee802154",
+            not(feature = "medium-ip"),
+            not(feature = "medium-ethernet")
+        ))]
+        return Medium::Ieee802154;
+        #[cfg(all(
+            not(feature = "medium-ip"),
+            not(feature = "medium-ethernet"),
+            not(feature = "medium-ieee802154")
+        ))]
         panic!("No medium enabled");
         panic!("No medium enabled");
     }
     }
 }
 }

+ 4 - 0
src/phy/pcap_writer.rs

@@ -15,6 +15,8 @@ enum_with_unknown! {
         Ethernet =   1,
         Ethernet =   1,
         /// IPv4 or IPv6 packets (depending on the version field)
         /// IPv4 or IPv6 packets (depending on the version field)
         Ip       = 101,
         Ip       = 101,
+        /// IEEE 802.15.4 packets with FCS included.
+        Ieee802154WithFcs = 195,
     }
     }
 }
 }
 
 
@@ -133,6 +135,8 @@ impl<D: for<'a> Device<'a>, S: PcapSink> PcapWriter<D, S> {
             Medium::Ip => PcapLinkType::Ip,
             Medium::Ip => PcapLinkType::Ip,
             #[cfg(feature = "medium-ethernet")]
             #[cfg(feature = "medium-ethernet")]
             Medium::Ethernet => PcapLinkType::Ethernet,
             Medium::Ethernet => PcapLinkType::Ethernet,
+            #[cfg(feature = "medium-ieee802154")]
+            Medium::Ieee802154 => PcapLinkType::Ieee802154WithFcs,
         };
         };
         sink.global_header(link_type);
         sink.global_header(link_type);
         PcapWriter {
         PcapWriter {

+ 15 - 4
src/phy/raw_socket.rs

@@ -11,6 +11,7 @@ use crate::Result;
 /// A socket that captures or transmits the complete frame.
 /// A socket that captures or transmits the complete frame.
 #[derive(Debug)]
 #[derive(Debug)]
 pub struct RawSocket {
 pub struct RawSocket {
+    medium: Medium,
     lower: Rc<RefCell<sys::RawSocketDesc>>,
     lower: Rc<RefCell<sys::RawSocketDesc>>,
     mtu: usize,
     mtu: usize,
 }
 }
@@ -26,11 +27,21 @@ impl RawSocket {
     ///
     ///
     /// This requires superuser privileges or a corresponding capability bit
     /// This requires superuser privileges or a corresponding capability bit
     /// set on the executable.
     /// set on the executable.
-    pub fn new(name: &str) -> io::Result<RawSocket> {
-        let mut lower = sys::RawSocketDesc::new(name)?;
+    pub fn new(name: &str, medium: Medium) -> io::Result<RawSocket> {
+        let mut lower = sys::RawSocketDesc::new(name, medium)?;
         lower.bind_interface()?;
         lower.bind_interface()?;
-        let mtu = lower.interface_mtu()?;
+
+        let mut mtu = lower.interface_mtu()?;
+
+        #[cfg(feature = "medium-ethernet")]
+        if medium == Medium::Ethernet {
+            // SIOCGIFMTU returns the IP MTU (typically 1500 bytes.)
+            // smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it.
+            mtu += crate::wire::EthernetFrame::<&[u8]>::header_len()
+        }
+
         Ok(RawSocket {
         Ok(RawSocket {
+            medium,
             lower: Rc::new(RefCell::new(lower)),
             lower: Rc::new(RefCell::new(lower)),
             mtu: mtu,
             mtu: mtu,
         })
         })
@@ -44,7 +55,7 @@ impl<'a> Device<'a> for RawSocket {
     fn capabilities(&self) -> DeviceCapabilities {
     fn capabilities(&self) -> DeviceCapabilities {
         DeviceCapabilities {
         DeviceCapabilities {
             max_transmission_unit: self.mtu,
             max_transmission_unit: self.mtu,
-            medium: Medium::Ethernet,
+            medium: self.medium,
             ..DeviceCapabilities::default()
             ..DeviceCapabilities::default()
         }
         }
     }
     }

+ 2 - 1
src/phy/sys/bpf.rs

@@ -5,6 +5,7 @@ use std::os::unix::io::{AsRawFd, RawFd};
 use libc;
 use libc;
 
 
 use super::{ifreq, ifreq_for};
 use super::{ifreq, ifreq_for};
+use crate::phy::Medium;
 use crate::wire::ETHERNET_HEADER_LEN;
 use crate::wire::ETHERNET_HEADER_LEN;
 
 
 /// set interface
 /// set interface
@@ -67,7 +68,7 @@ fn open_device() -> io::Result<libc::c_int> {
 }
 }
 
 
 impl BpfDevice {
 impl BpfDevice {
-    pub fn new(name: &str) -> io::Result<BpfDevice> {
+    pub fn new(name: &str, _medium: Medium) -> io::Result<BpfDevice> {
         Ok(BpfDevice {
         Ok(BpfDevice {
             fd: open_device()?,
             fd: open_device()?,
             ifreq: ifreq_for(name),
             ifreq: ifreq_for(name),

+ 1 - 0
src/phy/sys/linux.rs

@@ -3,6 +3,7 @@
 pub const SIOCGIFMTU: libc::c_ulong = 0x8921;
 pub const SIOCGIFMTU: libc::c_ulong = 0x8921;
 pub const SIOCGIFINDEX: libc::c_ulong = 0x8933;
 pub const SIOCGIFINDEX: libc::c_ulong = 0x8933;
 pub const ETH_P_ALL: libc::c_short = 0x0003;
 pub const ETH_P_ALL: libc::c_short = 0x0003;
+pub const ETH_P_IEEE802154: libc::c_short = 0x00F6;
 
 
 pub const TUNSETIFF: libc::c_ulong = 0x400454CA;
 pub const TUNSETIFF: libc::c_ulong = 0x400454CA;
 pub const IFF_TUN: libc::c_int = 0x0001;
 pub const IFF_TUN: libc::c_int = 0x0001;

+ 16 - 4
src/phy/sys/raw_socket.rs

@@ -1,10 +1,12 @@
 use super::*;
 use super::*;
+use crate::phy::Medium;
 use crate::wire::EthernetFrame;
 use crate::wire::EthernetFrame;
 use std::os::unix::io::{AsRawFd, RawFd};
 use std::os::unix::io::{AsRawFd, RawFd};
 use std::{io, mem};
 use std::{io, mem};
 
 
 #[derive(Debug)]
 #[derive(Debug)]
 pub struct RawSocketDesc {
 pub struct RawSocketDesc {
+    protocol: libc::c_short,
     lower: libc::c_int,
     lower: libc::c_int,
     ifreq: ifreq,
     ifreq: ifreq,
 }
 }
@@ -16,12 +18,21 @@ impl AsRawFd for RawSocketDesc {
 }
 }
 
 
 impl RawSocketDesc {
 impl RawSocketDesc {
-    pub fn new(name: &str) -> io::Result<RawSocketDesc> {
+    pub fn new(name: &str, medium: Medium) -> io::Result<RawSocketDesc> {
+        let protocol = match medium {
+            #[cfg(feature = "medium-ethernet")]
+            Medium::Ethernet => imp::ETH_P_ALL,
+            #[cfg(feature = "medium-ip")]
+            Medium::Ip => imp::ETH_P_ALL,
+            #[cfg(feature = "medium-ieee802154")]
+            Medium::Ieee802154 => imp::ETH_P_IEEE802154,
+        };
+
         let lower = unsafe {
         let lower = unsafe {
             let lower = libc::socket(
             let lower = libc::socket(
                 libc::AF_PACKET,
                 libc::AF_PACKET,
                 libc::SOCK_RAW | libc::SOCK_NONBLOCK,
                 libc::SOCK_RAW | libc::SOCK_NONBLOCK,
-                imp::ETH_P_ALL.to_be() as i32,
+                protocol.to_be() as i32,
             );
             );
             if lower == -1 {
             if lower == -1 {
                 return Err(io::Error::last_os_error());
                 return Err(io::Error::last_os_error());
@@ -30,7 +41,8 @@ impl RawSocketDesc {
         };
         };
 
 
         Ok(RawSocketDesc {
         Ok(RawSocketDesc {
-            lower: lower,
+            protocol,
+            lower,
             ifreq: ifreq_for(name),
             ifreq: ifreq_for(name),
         })
         })
     }
     }
@@ -46,7 +58,7 @@ impl RawSocketDesc {
     pub fn bind_interface(&mut self) -> io::Result<()> {
     pub fn bind_interface(&mut self) -> io::Result<()> {
         let sockaddr = libc::sockaddr_ll {
         let sockaddr = libc::sockaddr_ll {
             sll_family: libc::AF_PACKET as u16,
             sll_family: libc::AF_PACKET as u16,
-            sll_protocol: imp::ETH_P_ALL.to_be() as u16,
+            sll_protocol: self.protocol.to_be() as u16,
             sll_ifindex: ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFINDEX)?,
             sll_ifindex: ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFINDEX)?,
             sll_hatype: 1,
             sll_hatype: 1,
             sll_pkttype: 0,
             sll_pkttype: 0,

+ 4 - 0
src/phy/sys/tuntap_interface.rs

@@ -42,6 +42,8 @@ impl TunTapInterfaceDesc {
             Medium::Ip => imp::IFF_TUN,
             Medium::Ip => imp::IFF_TUN,
             #[cfg(feature = "medium-ethernet")]
             #[cfg(feature = "medium-ethernet")]
             Medium::Ethernet => imp::IFF_TAP,
             Medium::Ethernet => imp::IFF_TAP,
+            #[cfg(feature = "medium-ieee802154")]
+            Medium::Ieee802154 => todo!(),
         };
         };
         self.ifreq.ifr_data = mode | imp::IFF_NO_PI;
         self.ifreq.ifr_data = mode | imp::IFF_NO_PI;
         ifreq_ioctl(self.lower, &mut self.ifreq, imp::TUNSETIFF).map(|_| ())
         ifreq_ioctl(self.lower, &mut self.ifreq, imp::TUNSETIFF).map(|_| ())
@@ -72,6 +74,8 @@ impl TunTapInterfaceDesc {
             Medium::Ip => ip_mtu,
             Medium::Ip => ip_mtu,
             #[cfg(feature = "medium-ethernet")]
             #[cfg(feature = "medium-ethernet")]
             Medium::Ethernet => ip_mtu + EthernetFrame::<&[u8]>::header_len(),
             Medium::Ethernet => ip_mtu + EthernetFrame::<&[u8]>::header_len(),
+            #[cfg(feature = "medium-ieee802154")]
+            Medium::Ieee802154 => todo!(),
         };
         };
 
 
         Ok(mtu)
         Ok(mtu)

+ 2 - 0
src/phy/tracer.rs

@@ -190,6 +190,8 @@ impl<'a> fmt::Display for Packet<'a> {
                 }
                 }
                 _ => f.write_str("unrecognized IP version"),
                 _ => f.write_str("unrecognized IP version"),
             },
             },
+            #[cfg(feature = "medium-ieee802154")]
+            Medium::Ieee802154 => Ok(()), // XXX
         }
         }
     }
     }
 }
 }

+ 13 - 2
src/socket/dhcpv4.rs

@@ -2,6 +2,7 @@ use crate::socket::SocketHandle;
 use crate::socket::{Context, SocketMeta};
 use crate::socket::{Context, SocketMeta};
 use crate::time::{Duration, Instant};
 use crate::time::{Duration, Instant};
 use crate::wire::dhcpv4::field as dhcpv4_field;
 use crate::wire::dhcpv4::field as dhcpv4_field;
+use crate::wire::HardwareAddress;
 use crate::wire::{
 use crate::wire::{
     DhcpMessageType, DhcpPacket, DhcpRepr, IpAddress, IpProtocol, Ipv4Address, Ipv4Cidr, Ipv4Repr,
     DhcpMessageType, DhcpPacket, DhcpRepr, IpAddress, IpProtocol, Ipv4Address, Ipv4Cidr, Ipv4Repr,
     UdpRepr, DHCP_CLIENT_PORT, DHCP_MAX_DNS_SERVER_COUNT, DHCP_SERVER_PORT, UDP_HEADER_LEN,
     UdpRepr, DHCP_CLIENT_PORT, DHCP_MAX_DNS_SERVER_COUNT, DHCP_SERVER_PORT, UDP_HEADER_LEN,
@@ -218,7 +219,13 @@ impl Dhcpv4Socket {
                 return Ok(());
                 return Ok(());
             }
             }
         };
         };
-        if dhcp_repr.client_hardware_address != cx.ethernet_address.unwrap() {
+        let hardware_addr = if let Some(HardwareAddress::Ethernet(addr)) = cx.hardware_addr {
+            addr
+        } else {
+            return Err(Error::Malformed);
+        };
+
+        if dhcp_repr.client_hardware_address != hardware_addr {
             return Ok(());
             return Ok(());
         }
         }
         if dhcp_repr.transaction_id != self.transaction_id {
         if dhcp_repr.transaction_id != self.transaction_id {
@@ -381,7 +388,11 @@ impl Dhcpv4Socket {
     {
     {
         // note: Dhcpv4Socket is only usable in ethernet mediums, so the
         // note: Dhcpv4Socket is only usable in ethernet mediums, so the
         // unwrap can never fail.
         // unwrap can never fail.
-        let ethernet_addr = cx.ethernet_address.unwrap();
+        let ethernet_addr = if let Some(HardwareAddress::Ethernet(addr)) = cx.hardware_addr {
+            addr
+        } else {
+            return Err(Error::Malformed);
+        };
 
 
         // Worst case biggest IPv4 header length.
         // Worst case biggest IPv4 header length.
         // 0x0f * 4 = 60 bytes.
         // 0x0f * 4 = 60 bytes.

+ 17 - 6
src/socket/mod.rs

@@ -187,8 +187,13 @@ from_socket!(Dhcpv4Socket, Dhcpv4);
 #[derive(Clone, Debug)]
 #[derive(Clone, Debug)]
 pub(crate) struct Context {
 pub(crate) struct Context {
     pub now: Instant,
     pub now: Instant,
-    #[cfg(all(feature = "medium-ethernet", feature = "socket-dhcpv4"))]
-    pub ethernet_address: Option<crate::wire::EthernetAddress>,
+    #[cfg(all(
+        any(feature = "medium-ethernet", feature = "medium-ieee802154"),
+        feature = "socket-dhcpv4"
+    ))]
+    pub hardware_addr: Option<crate::wire::HardwareAddress>,
+    #[cfg(feature = "medium-ieee802154")]
+    pub pan_id: Option<crate::wire::Ieee802154Pan>,
     pub caps: DeviceCapabilities,
     pub caps: DeviceCapabilities,
 }
 }
 
 
@@ -215,10 +220,16 @@ impl Context {
             #[cfg(not(feature = "medium-ethernet"))]
             #[cfg(not(feature = "medium-ethernet"))]
             max_transmission_unit: 1500,
             max_transmission_unit: 1500,
         },
         },
-        #[cfg(all(feature = "medium-ethernet", feature = "socket-dhcpv4"))]
-        ethernet_address: Some(crate::wire::EthernetAddress([
-            0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
-        ])),
+        #[cfg(all(
+            any(feature = "medium-ethernet", feature = "medium-ieee802154"),
+            feature = "socket-dhcpv4"
+        ))]
+        hardware_addr: Some(crate::wire::HardwareAddress::Ethernet(
+            crate::wire::EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]),
+        )),
         now: Instant::from_millis_const(0),
         now: Instant::from_millis_const(0),
+
+        #[cfg(feature = "medium-ieee802154")]
+        pan_id: Some(crate::wire::Ieee802154Pan(0xabcd)),
     };
     };
 }
 }

+ 1 - 3
src/socket/set.rs

@@ -69,9 +69,7 @@ impl<'a> Set<'a> {
         }
         }
 
 
         match self.sockets {
         match self.sockets {
-            ManagedSlice::Borrowed(_) => {
-                panic!("adding a socket to a full SocketSet")
-            }
+            ManagedSlice::Borrowed(_) => panic!("adding a socket to a full SocketSet"),
             #[cfg(any(feature = "std", feature = "alloc"))]
             #[cfg(any(feature = "std", feature = "alloc"))]
             ManagedSlice::Owned(ref mut sockets) => {
             ManagedSlice::Owned(ref mut sockets) => {
                 sockets.push(None);
                 sockets.push(None);

+ 5 - 5
src/wire/icmpv6.rs

@@ -4,7 +4,7 @@ use core::{cmp, fmt};
 use crate::phy::ChecksumCapabilities;
 use crate::phy::ChecksumCapabilities;
 use crate::wire::ip::checksum;
 use crate::wire::ip::checksum;
 use crate::wire::MldRepr;
 use crate::wire::MldRepr;
-#[cfg(feature = "medium-ethernet")]
+#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
 use crate::wire::NdiscRepr;
 use crate::wire::NdiscRepr;
 use crate::wire::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr};
 use crate::wire::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr};
 use crate::{Error, Result};
 use crate::{Error, Result};
@@ -532,7 +532,7 @@ pub enum Repr<'a> {
         seq_no: u16,
         seq_no: u16,
         data: &'a [u8],
         data: &'a [u8],
     },
     },
-    #[cfg(feature = "medium-ethernet")]
+    #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
     Ndisc(NdiscRepr<'a>),
     Ndisc(NdiscRepr<'a>),
     Mld(MldRepr<'a>),
     Mld(MldRepr<'a>),
 }
 }
@@ -617,7 +617,7 @@ impl<'a> Repr<'a> {
                 seq_no: packet.echo_seq_no(),
                 seq_no: packet.echo_seq_no(),
                 data: packet.payload(),
                 data: packet.payload(),
             }),
             }),
-            #[cfg(feature = "medium-ethernet")]
+            #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
             (msg_type, 0) if msg_type.is_ndisc() => NdiscRepr::parse(packet).map(Repr::Ndisc),
             (msg_type, 0) if msg_type.is_ndisc() => NdiscRepr::parse(packet).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),
@@ -636,7 +636,7 @@ impl<'a> Repr<'a> {
             &Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => {
             &Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => {
                 field::ECHO_SEQNO.end + data.len()
                 field::ECHO_SEQNO.end + data.len()
             }
             }
-            #[cfg(feature = "medium-ethernet")]
+            #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
             &Repr::Ndisc(ndisc) => ndisc.buffer_len(),
             &Repr::Ndisc(ndisc) => ndisc.buffer_len(),
             &Repr::Mld(mld) => mld.buffer_len(),
             &Repr::Mld(mld) => mld.buffer_len(),
         }
         }
@@ -730,7 +730,7 @@ impl<'a> Repr<'a> {
                 packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len])
                 packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len])
             }
             }
 
 
-            #[cfg(feature = "medium-ethernet")]
+            #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
             Repr::Ndisc(ndisc) => ndisc.emit(packet),
             Repr::Ndisc(ndisc) => ndisc.emit(packet),
 
 
             Repr::Mld(mld) => mld.emit(packet),
             Repr::Mld(mld) => mld.emit(packet),

+ 897 - 0
src/wire/ieee802154.rs

@@ -0,0 +1,897 @@
+use core::fmt;
+
+use byteorder::{ByteOrder, LittleEndian};
+
+use crate::wire::ipv6::Address as Ipv6Address;
+use crate::Error;
+use crate::Result;
+
+const CRC_TABLE: [u16; 256] = [
+    0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3,
+    0xca6c, 0xdbe5, 0xe97e, 0xf8f7, 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+    0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, 0x2102, 0x308b, 0x0210, 0x1399,
+    0x6726, 0x76af, 0x4434, 0x55bd, 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+    0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, 0xbdcb, 0xac42, 0x9ed9, 0x8f50,
+    0xfbef, 0xea66, 0xd8fd, 0xc974, 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+    0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, 0x5285, 0x430c, 0x7197, 0x601e,
+    0x14a1, 0x0528, 0x37b3, 0x263a, 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+    0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, 0xef4e, 0xfec7, 0xcc5c, 0xddd5,
+    0xa96a, 0xb8e3, 0x8a78, 0x9bf1, 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+    0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, 0x8408, 0x9581, 0xa71a, 0xb693,
+    0xc22c, 0xd3a5, 0xe13e, 0xf0b7, 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+    0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, 0x18c1, 0x0948, 0x3bd3, 0x2a5a,
+    0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+    0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, 0xb58b, 0xa402, 0x9699, 0x8710,
+    0xf3af, 0xe226, 0xd0bd, 0xc134, 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+    0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, 0x4a44, 0x5bcd, 0x6956, 0x78df,
+    0x0c60, 0x1de9, 0x2f72, 0x3efb, 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+    0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, 0xe70e, 0xf687, 0xc41c, 0xd595,
+    0xa12a, 0xb0a3, 0x8238, 0x93b1, 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+    0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, 0x7bc7, 0x6a4e, 0x58d5, 0x495c,
+    0x3de3, 0x2c6a, 0x1ef1, 0x0f78,
+];
+
+pub fn calculate_crc(buffer: &[u8]) -> u16 {
+    fn crc_byte(crc: u16, c: u8) -> u16 {
+        (crc >> 8) ^ CRC_TABLE[((crc ^ (c as u16)) & 0xff) as usize]
+    }
+
+    let mut crc = 0;
+
+    for b in buffer {
+        crc = crc_byte(crc, *b);
+    }
+
+    crc
+}
+
+enum_with_unknown! {
+    /// IEEE 802.15.4 frame type.
+    pub enum FrameType(u8) {
+        Beacon = 0b000,
+        Data = 0b001,
+        Acknowledgement = 0b010,
+        MacCommand = 0b011,
+        Multipurpose = 0b101,
+        FragmentOrFrak = 0b110,
+        Extended = 0b111,
+    }
+}
+
+impl fmt::Display for FrameType {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            FrameType::Beacon => write!(f, "Beacon"),
+            FrameType::Data => write!(f, "Data"),
+            FrameType::Acknowledgement => write!(f, "Ack"),
+            FrameType::MacCommand => write!(f, "MAC command"),
+            FrameType::Multipurpose => write!(f, "Multipurpose"),
+            FrameType::FragmentOrFrak => write!(f, "FragmentOrFrak"),
+            FrameType::Extended => write!(f, "Extended"),
+            FrameType::Unknown(id) => write!(f, "0b{:04b}", id),
+        }
+    }
+}
+enum_with_unknown! {
+    /// IEEE 802.15.4 addressing mode for destination and source addresses.
+    pub enum AddressingMode(u8) {
+        Absent    = 0b00,
+        Short     = 0b10,
+        Extended  = 0b11,
+    }
+}
+
+impl AddressingMode {
+    /// Return the size in octets of the address.
+    fn size(&self) -> usize {
+        match self {
+            AddressingMode::Absent => 0,
+            AddressingMode::Short => 2,
+            AddressingMode::Extended => 8,
+            AddressingMode::Unknown(_) => 0, // TODO(thvdveld): what do we need to here?
+        }
+    }
+}
+
+impl fmt::Display for AddressingMode {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            AddressingMode::Absent => write!(f, "Absent"),
+            AddressingMode::Short => write!(f, "Short"),
+            AddressingMode::Extended => write!(f, "Extended"),
+            AddressingMode::Unknown(id) => write!(f, "0b{:04b}", id),
+        }
+    }
+}
+
+/// A IEEE 802.15.4 PAN.
+#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+pub struct Pan(pub u16);
+
+impl Pan {
+    pub const BROADCAST: Self = Self(0xffff);
+
+    /// Return the PAN ID as bytes.
+    pub fn as_bytes(&self) -> [u8; 2] {
+        let mut pan = [0u8; 2];
+        LittleEndian::write_u16(&mut pan, self.0);
+        pan
+    }
+}
+
+/// A IEEE 802.15.4 address.
+#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+pub enum Address {
+    Absent,
+    Short([u8; 2]),
+    Extended([u8; 8]),
+}
+
+impl Address {
+    /// The broadcast address.
+    pub const BROADCAST: Address = Address::Short([0xff; 2]);
+
+    /// Query whether the address is an unicast address.
+    pub fn is_unicast(&self) -> bool {
+        !self.is_broadcast()
+    }
+
+    /// Query whether this address is the broadcast address.
+    pub fn is_broadcast(&self) -> bool {
+        *self == Self::BROADCAST
+    }
+
+    fn short_from_bytes(a: [u8; 2]) -> Self {
+        Self::Short(a)
+    }
+
+    fn extended_from_bytes(a: [u8; 8]) -> Self {
+        Self::Extended(a)
+    }
+
+    pub fn from_bytes(a: &[u8]) -> Self {
+        if a.len() == 2 {
+            let mut b = [0u8; 2];
+            b.copy_from_slice(a);
+            Address::Short(b)
+        } else if a.len() == 8 {
+            let mut b = [0u8; 8];
+            b.copy_from_slice(a);
+            Address::Extended(b)
+        } else {
+            panic!("Not an IEEE802.15.4 address");
+        }
+    }
+
+    pub fn as_bytes(&self) -> &[u8] {
+        match self {
+            Address::Absent => &[],
+            Address::Short(value) => value,
+            Address::Extended(value) => value,
+        }
+    }
+
+    /// Convert the extended address to an Extended Unique Identifier (EUI-64)
+    pub fn as_eui_64(&self) -> Option<[u8; 8]> {
+        match self {
+            Address::Absent | Address::Short(_) => None,
+            Address::Extended(value) => {
+                let mut bytes = [0; 8];
+                bytes.copy_from_slice(&value[..]);
+
+                bytes[0] ^= 1 << 1;
+
+                Some(bytes)
+            }
+        }
+    }
+
+    /// Convert an extended address to a link-local IPv6 address using the EUI-64 format from
+    /// RFC2464.
+    pub fn as_link_local_address(&self) -> Option<Ipv6Address> {
+        let mut bytes = [0; 16];
+        bytes[0] = 0xfe;
+        bytes[1] = 0x80;
+        bytes[8..].copy_from_slice(&self.as_eui_64()?);
+
+        Some(Ipv6Address::from_bytes(&bytes))
+    }
+}
+
+impl fmt::Display for Address {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            Self::Absent => write!(f, "not-present"),
+            Self::Short(bytes) => write!(f, "{:02x}-{:02x}", bytes[0], bytes[1]),
+            Self::Extended(bytes) => write!(
+                f,
+                "{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}",
+                bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]
+            ),
+        }
+    }
+}
+
+enum_with_unknown! {
+    /// IEEE 802.15.4 addressing mode for destination and source addresses.
+    pub enum FrameVersion(u8) {
+        Ieee802154_2003 = 0b00,
+        Ieee802154_2006 = 0b01,
+        Ieee802154 = 0b10,
+    }
+}
+
+/// A read/write wrapper around an IEEE 802.15.4 frame buffer.
+#[derive(Debug, Clone)]
+pub struct Frame<T: AsRef<[u8]>> {
+    buffer: T,
+}
+
+mod field {
+    use crate::wire::field::*;
+
+    pub const FRAMECONTROL: Field = 0..2;
+    pub const SEQUENCE_NUMBER: usize = 2;
+    pub const ADDRESSING: Rest = 3..;
+}
+
+macro_rules! fc_bit_field {
+    ($field:ident, $bit:literal) => {
+        #[inline]
+        pub fn $field(&self) -> bool {
+            let data = self.buffer.as_ref();
+            let raw = LittleEndian::read_u16(&data[field::FRAMECONTROL]);
+
+            ((raw >> $bit) & 0b1) == 0b1
+        }
+    };
+}
+
+macro_rules! set_fc_bit_field {
+    ($field:ident, $bit:literal) => {
+        #[inline]
+        pub fn $field(&mut self, val: bool) {
+            let data = &mut self.buffer.as_mut()[field::FRAMECONTROL];
+            let mut raw = LittleEndian::read_u16(data);
+            raw |= ((val as u16) << $bit);
+
+            data.copy_from_slice(&raw.to_le_bytes());
+        }
+    };
+}
+
+impl<T: AsRef<[u8]>> Frame<T> {
+    /// Input a raw octet buffer with Ethernet frame structure.
+    pub fn new_unchecked(buffer: T) -> Frame<T> {
+        Frame { buffer }
+    }
+
+    /// Shorthand for a combination of [new_unchecked] and [check_len].
+    ///
+    /// [new_unchecked]: #method.new_unchecked
+    /// [check_len]: #method.check_len
+    pub fn new_checked(buffer: T) -> Result<Frame<T>> {
+        let packet = Self::new_unchecked(buffer);
+        packet.check_len()?;
+
+        if matches!(packet.dst_addressing_mode(), AddressingMode::Unknown(_)) {
+            return Err(Error::Malformed);
+        }
+
+        if matches!(packet.src_addressing_mode(), AddressingMode::Unknown(_)) {
+            return Err(Error::Malformed);
+        }
+
+        Ok(packet)
+    }
+
+    /// Ensure that no accessor method will panic if called.
+    /// Returns `Err(Error::Truncated)` if the buffer is too short.
+    pub fn check_len(&self) -> Result<()> {
+        if self.buffer.as_ref().is_empty() {
+            Err(Error::Truncated)
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Consumes the frame, returning the underlying buffer.
+    pub fn into_inner(self) -> T {
+        self.buffer
+    }
+
+    /// Return the FrameType field.
+    #[inline]
+    pub fn frame_type(&self) -> FrameType {
+        let data = self.buffer.as_ref();
+        let raw = LittleEndian::read_u16(&data[field::FRAMECONTROL]);
+        let ft = (raw & 0b11) as u8;
+        FrameType::from(ft)
+    }
+
+    fc_bit_field!(security_enabled, 3);
+    fc_bit_field!(frame_pending, 4);
+    fc_bit_field!(ack_request, 5);
+    fc_bit_field!(pan_id_compression, 6);
+
+    fc_bit_field!(sequence_number_suppression, 8);
+    fc_bit_field!(ie_present, 9);
+
+    /// Return the destination addressing mode.
+    #[inline]
+    pub fn dst_addressing_mode(&self) -> AddressingMode {
+        let data = self.buffer.as_ref();
+        let raw = LittleEndian::read_u16(&data[field::FRAMECONTROL]);
+        let am = ((raw >> 10) & 0b11) as u8;
+        AddressingMode::from(am)
+    }
+
+    /// Return the frame version.
+    #[inline]
+    pub fn frame_version(&self) -> FrameVersion {
+        let data = self.buffer.as_ref();
+        let raw = LittleEndian::read_u16(&data[field::FRAMECONTROL]);
+        let fv = ((raw >> 12) & 0b11) as u8;
+        FrameVersion::from(fv)
+    }
+
+    /// Return the source addressing mode.
+    #[inline]
+    pub fn src_addressing_mode(&self) -> AddressingMode {
+        let data = self.buffer.as_ref();
+        let raw = LittleEndian::read_u16(&data[field::FRAMECONTROL]);
+        let am = ((raw >> 14) & 0b11) as u8;
+        AddressingMode::from(am)
+    }
+
+    /// Return the sequence number of the frame.
+    #[inline]
+    pub fn sequence_number(&self) -> Option<u8> {
+        match self.frame_type() {
+            FrameType::Beacon
+            | FrameType::Data
+            | FrameType::Acknowledgement
+            | FrameType::MacCommand
+            | FrameType::Multipurpose => {
+                let data = self.buffer.as_ref();
+                let raw = data[field::SEQUENCE_NUMBER];
+                Some(raw)
+            }
+            FrameType::Extended | FrameType::FragmentOrFrak | FrameType::Unknown(_) => None,
+        }
+    }
+
+    /// Return the addressing fields.
+    #[inline]
+    fn addressing_fields(&self) -> Option<&[u8]> {
+        match self.frame_type() {
+            FrameType::Beacon
+            | FrameType::Data
+            | FrameType::MacCommand
+            | FrameType::Multipurpose => (),
+            FrameType::Acknowledgement if self.frame_version() == FrameVersion::Ieee802154 => (),
+            FrameType::Acknowledgement
+            | FrameType::Extended
+            | FrameType::FragmentOrFrak
+            | FrameType::Unknown(_) => return None,
+        }
+
+        let mut offset = 2;
+
+        // Calculate the size of the addressing field.
+        offset += self.dst_addressing_mode().size();
+        offset += self.src_addressing_mode().size();
+
+        if !self.pan_id_compression() {
+            offset += 2;
+        }
+
+        Some(&self.buffer.as_ref()[field::ADDRESSING][..offset])
+    }
+
+    /// Return the destination PAN field.
+    #[inline]
+    pub fn dst_pan_id(&self) -> Option<Pan> {
+        let addressing_fields = self.addressing_fields()?;
+        match self.dst_addressing_mode() {
+            AddressingMode::Absent => None,
+            AddressingMode::Short | AddressingMode::Extended => {
+                Some(Pan(LittleEndian::read_u16(&addressing_fields[0..2])))
+            }
+            AddressingMode::Unknown(_) => None,
+        }
+    }
+
+    /// Return the destination address field.
+    #[inline]
+    pub fn dst_addr(&self) -> Option<Address> {
+        let addressing_fields = self.addressing_fields()?;
+        match self.dst_addressing_mode() {
+            AddressingMode::Absent => Some(Address::Absent),
+            AddressingMode::Short => {
+                let mut raw = [0u8; 2];
+                raw.clone_from_slice(&addressing_fields[2..4]);
+                raw.reverse();
+                Some(Address::short_from_bytes(raw))
+            }
+            AddressingMode::Extended => {
+                let mut raw = [0u8; 8];
+                raw.clone_from_slice(&addressing_fields[2..10]);
+                raw.reverse();
+                Some(Address::extended_from_bytes(raw))
+            }
+            AddressingMode::Unknown(_) => None,
+        }
+    }
+
+    /// Return the destination PAN field.
+    #[inline]
+    pub fn src_pan_id(&self) -> Option<Pan> {
+        if self.pan_id_compression() {
+            return None;
+        }
+
+        let addressing_fields = self.addressing_fields()?;
+        let offset = self.dst_addressing_mode().size() + 2;
+
+        match self.src_addressing_mode() {
+            AddressingMode::Absent => None,
+            AddressingMode::Short | AddressingMode::Extended => Some(Pan(LittleEndian::read_u16(
+                &addressing_fields[offset..offset + 2],
+            ))),
+            AddressingMode::Unknown(_) => None,
+        }
+    }
+
+    /// Return the source address field.
+    #[inline]
+    pub fn src_addr(&self) -> Option<Address> {
+        let addressing_fields = self.addressing_fields()?;
+        let mut offset = match self.dst_addressing_mode() {
+            AddressingMode::Absent => 0,
+            AddressingMode::Short => 2,
+            AddressingMode::Extended => 8,
+            _ => return None, // TODO(thvdveld): what do we do here?
+        } + 2;
+
+        if !self.pan_id_compression() {
+            offset += 2;
+        }
+
+        match self.src_addressing_mode() {
+            AddressingMode::Absent => Some(Address::Absent),
+            AddressingMode::Short => {
+                let mut raw = [0u8; 2];
+                raw.clone_from_slice(&addressing_fields[offset..offset + 2]);
+                raw.reverse();
+                Some(Address::short_from_bytes(raw))
+            }
+            AddressingMode::Extended => {
+                let mut raw = [0u8; 8];
+                raw.clone_from_slice(&addressing_fields[offset..offset + 8]);
+                raw.reverse();
+                Some(Address::extended_from_bytes(raw))
+            }
+            AddressingMode::Unknown(_) => None,
+        }
+    }
+
+    /// Return the Auxilliary Security Header Field
+    #[inline]
+    pub fn aux_security_header(&self) -> Option<&[u8]> {
+        match self.frame_type() {
+            FrameType::Beacon
+            | FrameType::Data
+            | FrameType::MacCommand
+            | FrameType::Multipurpose => (),
+            FrameType::Acknowledgement if self.frame_version() == FrameVersion::Ieee802154 => (),
+            FrameType::Acknowledgement
+            | FrameType::Extended
+            | FrameType::FragmentOrFrak
+            | FrameType::Unknown(_) => return None,
+        }
+
+        if !self.security_enabled() {
+            return None;
+        }
+
+        net_debug!("Auxilliary security header is currently not supported.");
+        None
+    }
+}
+
+impl<'a, T: AsRef<[u8]> + ?Sized> Frame<&'a T> {
+    /// Return a pointer to the payload.
+    #[inline]
+    pub fn payload(&self) -> Option<&'a [u8]> {
+        match self.frame_type() {
+            FrameType::Data => {
+                let data = &self.buffer.as_ref()[field::ADDRESSING];
+                let offset = self.addressing_fields().unwrap().len();
+
+                Some(&data[offset..])
+            }
+            _ => None,
+        }
+    }
+}
+
+impl<T: AsRef<[u8]> + AsMut<[u8]>> Frame<T> {
+    /// Set the frame type.
+    #[inline]
+    pub fn set_frame_type(&mut self, frame_type: FrameType) {
+        let data = &mut self.buffer.as_mut()[field::FRAMECONTROL];
+        let mut raw = LittleEndian::read_u16(data);
+
+        raw = (raw & !(0b111)) | (u8::from(frame_type) as u16 & 0b111);
+        data.copy_from_slice(&raw.to_le_bytes());
+    }
+
+    set_fc_bit_field!(set_security_enabled, 3);
+    set_fc_bit_field!(set_frame_pending, 4);
+    set_fc_bit_field!(set_ack_request, 5);
+    set_fc_bit_field!(set_pan_id_compression, 6);
+
+    /// Set the frame version.
+    #[inline]
+    pub fn set_frame_version(&mut self, version: FrameVersion) {
+        let data = &mut self.buffer.as_mut()[field::FRAMECONTROL];
+        let mut raw = LittleEndian::read_u16(data);
+
+        raw = (raw & !(0b11 << 12)) | ((u8::from(version) as u16 & 0b11) << 12);
+        data.copy_from_slice(&raw.to_le_bytes());
+    }
+
+    /// Set the frame sequence number.
+    #[inline]
+    pub fn set_sequence_number(&mut self, value: u8) {
+        let data = self.buffer.as_mut();
+        data[field::SEQUENCE_NUMBER] = value;
+    }
+
+    /// Set the destination PAN ID.
+    #[inline]
+    pub fn set_dst_pan_id(&mut self, value: Pan) {
+        // NOTE the destination addressing mode must be different than Absent.
+        // This is the reason why we set it to Extended.
+        self.set_dst_addressing_mode(AddressingMode::Extended);
+
+        let data = self.buffer.as_mut();
+        data[field::ADDRESSING][..2].copy_from_slice(&value.as_bytes());
+    }
+
+    /// Set the destination address.
+    #[inline]
+    pub fn set_dst_addr(&mut self, mut value: Address) {
+        match value {
+            Address::Absent => self.set_dst_addressing_mode(AddressingMode::Absent),
+            Address::Short(ref mut value) => {
+                value.reverse();
+                self.set_dst_addressing_mode(AddressingMode::Short);
+                let data = self.buffer.as_mut();
+                data[field::ADDRESSING][2..2 + 2].copy_from_slice(value);
+                value.reverse();
+            }
+            Address::Extended(ref mut value) => {
+                value.reverse();
+                self.set_dst_addressing_mode(AddressingMode::Extended);
+                let data = &mut self.buffer.as_mut()[field::ADDRESSING];
+                data[2..2 + 8].copy_from_slice(value);
+                value.reverse();
+            }
+        }
+    }
+
+    /// Set the destination addressing mode.
+    #[inline]
+    fn set_dst_addressing_mode(&mut self, value: AddressingMode) {
+        let data = &mut self.buffer.as_mut()[field::FRAMECONTROL];
+        let mut raw = LittleEndian::read_u16(data);
+
+        raw = (raw & !(0b11 << 10)) | ((u8::from(value) as u16 & 0b11) << 10);
+        data.copy_from_slice(&raw.to_le_bytes());
+    }
+
+    /// Set the source PAN ID.
+    #[inline]
+    pub fn set_src_pan_id(&mut self, value: Pan) {
+        let offset = match self.dst_addressing_mode() {
+            AddressingMode::Absent => 0,
+            AddressingMode::Short => 2,
+            AddressingMode::Extended => 8,
+            _ => unreachable!(),
+        } + 2;
+
+        let data = &mut self.buffer.as_mut()[field::ADDRESSING];
+        data[offset..offset + 2].copy_from_slice(&value.as_bytes());
+    }
+
+    /// Set the source address.
+    #[inline]
+    pub fn set_src_addr(&mut self, mut value: Address) {
+        let offset = match self.dst_addressing_mode() {
+            AddressingMode::Absent => 0,
+            AddressingMode::Short => 2,
+            AddressingMode::Extended => 8,
+            _ => unreachable!(),
+        } + 2;
+
+        let offset = offset + if self.pan_id_compression() { 0 } else { 2 };
+
+        match value {
+            Address::Absent => self.set_src_addressing_mode(AddressingMode::Absent),
+            Address::Short(ref mut value) => {
+                value.reverse();
+                self.set_src_addressing_mode(AddressingMode::Short);
+                let data = &mut self.buffer.as_mut()[field::ADDRESSING];
+                data[offset..offset + 2].copy_from_slice(value);
+                value.reverse();
+            }
+            Address::Extended(ref mut value) => {
+                value.reverse();
+                self.set_src_addressing_mode(AddressingMode::Extended);
+                let data = &mut self.buffer.as_mut()[field::ADDRESSING];
+                data[offset..offset + 8].copy_from_slice(value);
+                value.reverse();
+            }
+        }
+    }
+
+    /// Set the source addressing mode.
+    #[inline]
+    fn set_src_addressing_mode(&mut self, value: AddressingMode) {
+        let data = &mut self.buffer.as_mut()[field::FRAMECONTROL];
+        let mut raw = LittleEndian::read_u16(data);
+
+        raw = (raw & !(0b11 << 14)) | ((u8::from(value) as u16 & 0b11) << 14);
+        data.copy_from_slice(&raw.to_le_bytes());
+    }
+
+    /// Return a mutable pointer to the payload.
+    #[inline]
+    pub fn payload_mut(&mut self) -> Option<&mut [u8]> {
+        match self.frame_type() {
+            FrameType::Data => {
+                let mut start_offset = 3;
+                start_offset += self.addressing_fields().unwrap().len();
+
+                let data = self.buffer.as_mut();
+                let end_offset = start_offset + data.len() - 2;
+                Some(&mut data[start_offset..end_offset])
+            }
+            _ => None,
+        }
+    }
+}
+
+impl<T: AsRef<[u8]>> fmt::Display for Frame<T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(
+            f,
+            "IEEE802.15.4 frame type={} seq={:2x?} dst_pan={:x?} dest={:x?} src_pan={:?} src={:x?}",
+            self.frame_type(),
+            self.sequence_number(),
+            self.dst_pan_id(),
+            self.dst_addr(),
+            self.src_pan_id(),
+            self.src_addr(),
+        )
+    }
+}
+
+/// A high-level representation of an IEEE802.15.4 frame.
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Repr {
+    pub frame_type: FrameType,
+    pub security_enabled: bool,
+    pub frame_pending: bool,
+    pub ack_request: bool,
+    pub sequence_number: Option<u8>,
+    pub pan_id_compression: bool,
+    pub frame_version: FrameVersion,
+    pub dst_pan_id: Option<Pan>,
+    pub dst_addr: Option<Address>,
+    pub src_pan_id: Option<Pan>,
+    pub src_addr: Option<Address>,
+}
+
+impl Repr {
+    /// Parse an IEEE 802.15.4 frame and return a high-level representation.
+    pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Frame<&T>) -> Result<Repr> {
+        // Ensure the basic accessors will work.
+        packet.check_len()?;
+
+        Ok(Repr {
+            frame_type: packet.frame_type(),
+            security_enabled: packet.security_enabled(),
+            frame_pending: packet.frame_pending(),
+            ack_request: packet.ack_request(),
+            sequence_number: packet.sequence_number(),
+            pan_id_compression: packet.pan_id_compression(),
+            frame_version: packet.frame_version(),
+            dst_pan_id: packet.dst_pan_id(),
+            dst_addr: packet.dst_addr(),
+            src_pan_id: packet.src_pan_id(),
+            src_addr: packet.src_addr(),
+        })
+    }
+
+    /// Return the length of a buffer required to hold a packet with the payload of a given length.
+    #[inline]
+    pub fn buffer_len(&self) -> usize {
+        3 + 2
+            + match self.dst_addr {
+                Some(Address::Absent) | None => 0,
+                Some(Address::Short(_)) => 2,
+                Some(Address::Extended(_)) => 8,
+            }
+            + if !self.pan_id_compression { 2 } else { 0 }
+            + match self.src_addr {
+                Some(Address::Absent) | None => 0,
+                Some(Address::Short(_)) => 2,
+                Some(Address::Extended(_)) => 8,
+            }
+    }
+
+    /// Emit a high-level representation into an IEEE802.15.4 frame.
+    pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, frame: &mut Frame<T>) {
+        frame.set_frame_type(self.frame_type);
+        frame.set_security_enabled(self.security_enabled);
+        frame.set_frame_pending(self.frame_pending);
+        frame.set_ack_request(self.ack_request);
+        frame.set_pan_id_compression(self.pan_id_compression);
+        frame.set_frame_version(self.frame_version);
+
+        if let Some(sequence_number) = self.sequence_number {
+            frame.set_sequence_number(sequence_number);
+        }
+
+        if let Some(dst_pan_id) = self.dst_pan_id {
+            frame.set_dst_pan_id(dst_pan_id);
+        }
+        if let Some(dst_addr) = self.dst_addr {
+            frame.set_dst_addr(dst_addr);
+        }
+
+        if !self.pan_id_compression && self.src_pan_id.is_some() {
+            frame.set_src_pan_id(self.src_pan_id.unwrap());
+        }
+
+        if let Some(src_addr) = self.src_addr {
+            frame.set_src_addr(src_addr);
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::Result;
+
+    #[test]
+    fn test_broadcast() {
+        assert!(Address::BROADCAST.is_broadcast());
+        assert!(!Address::BROADCAST.is_unicast());
+    }
+
+    #[test]
+    fn prepare_frame() {
+        let mut buffer = [0u8; 128];
+
+        let repr = Repr {
+            frame_type: FrameType::Data,
+            security_enabled: false,
+            frame_pending: false,
+            ack_request: true,
+            pan_id_compression: true,
+            frame_version: FrameVersion::Ieee802154,
+            sequence_number: Some(1),
+            dst_pan_id: Some(Pan(0xabcd)),
+            dst_addr: Some(Address::BROADCAST),
+            src_pan_id: None,
+            src_addr: Some(Address::Extended([
+                0xc7, 0xd9, 0xb5, 0x14, 0x00, 0x4b, 0x12, 0x00,
+            ])),
+        };
+
+        let buffer_len = repr.buffer_len();
+
+        let mut frame = Frame::new_unchecked(&mut buffer[..buffer_len]);
+        repr.emit(&mut frame);
+
+        println!("{:2x?}", frame);
+
+        assert_eq!(frame.frame_type(), FrameType::Data);
+        assert!(!frame.security_enabled());
+        assert!(!frame.frame_pending());
+        assert!(frame.ack_request());
+        assert!(frame.pan_id_compression());
+        assert_eq!(frame.frame_version(), FrameVersion::Ieee802154);
+        assert_eq!(frame.sequence_number(), Some(1));
+        assert_eq!(frame.dst_pan_id(), Some(Pan(0xabcd)));
+        assert_eq!(frame.dst_addr(), Some(Address::BROADCAST));
+        assert_eq!(frame.src_pan_id(), None);
+        assert_eq!(
+            frame.src_addr(),
+            Some(Address::Extended([
+                0xc7, 0xd9, 0xb5, 0x14, 0x00, 0x4b, 0x12, 0x00
+            ]))
+        );
+    }
+
+    macro_rules! vector_test {
+        ($name:ident $bytes:expr ; $($test_method:ident -> $expected:expr,)*) => {
+            #[test]
+            #[allow(clippy::bool_assert_comparison)]
+            fn $name() -> Result<()> {
+                let frame = &$bytes;
+                let frame = Frame::new_checked(frame)?;
+
+                $(
+                    assert_eq!(frame.$test_method(), $expected, stringify!($test_method));
+                )*
+
+                Ok(())
+            }
+        }
+    }
+
+    vector_test! {
+        extended_addr
+        [
+            0b0000_0001, 0b1100_1100, // frame control
+            0b0, // seq
+            0xcd, 0xab, // pan id
+            0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, // dst addr
+            0x03, 0x04, // pan id
+            0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, // src addr
+        ];
+        frame_type -> FrameType::Data,
+        dst_addr -> Some(Address::Extended([0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00])),
+        src_addr -> Some(Address::Extended([0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00])),
+        dst_pan_id -> Some(Pan(0xabcd)),
+    }
+
+    vector_test! {
+        short_addr
+        [
+            0x01, 0x98,             // frame control
+            0x00,                   // sequence number
+            0x34, 0x12, 0x78, 0x56, // PAN identifier and address of destination
+            0x34, 0x12, 0xbc, 0x9a, // PAN identifier and address of source
+        ];
+        frame_type -> FrameType::Data,
+        security_enabled -> false,
+        frame_pending -> false,
+        ack_request -> false,
+        pan_id_compression -> false,
+        dst_addressing_mode -> AddressingMode::Short,
+        frame_version -> FrameVersion::Ieee802154_2006,
+        src_addressing_mode -> AddressingMode::Short,
+        dst_pan_id -> Some(Pan(0x1234)),
+        dst_addr -> Some(Address::Short([0x56, 0x78])),
+        src_pan_id -> Some(Pan(0x1234)),
+        src_addr -> Some(Address::Short([0x9a, 0xbc])),
+    }
+
+    vector_test! {
+        zolertia_remote
+        [
+            0x41, 0xd8, // frame control
+            0x01, // sequence number
+            0xcd, 0xab, // Destination PAN id
+            0xff, 0xff, // Short destination address
+            0xc7, 0xd9, 0xb5, 0x14, 0x00, 0x4b, 0x12, 0x00, // Extended source address
+            0x2b, 0x00, 0x00, 0x00, // payload
+        ];
+        frame_type -> FrameType::Data,
+        security_enabled -> false,
+        frame_pending -> false,
+        ack_request -> false,
+        pan_id_compression -> true,
+        dst_addressing_mode -> AddressingMode::Short,
+        frame_version -> FrameVersion::Ieee802154_2006,
+        src_addressing_mode -> AddressingMode::Extended,
+        //payload -> Some(&[0x2b, 0x00, 0x00, 0x00]),
+    }
+}

+ 10 - 14
src/wire/igmp.rs

@@ -337,23 +337,19 @@ impl<'a> fmt::Display for Repr {
                 max_resp_time,
                 max_resp_time,
                 group_addr,
                 group_addr,
                 version,
                 version,
-            } => {
-                write!(
-                    f,
-                    "IGMP membership query max_resp_time={} group_addr={} version={:?}",
-                    max_resp_time, group_addr, version
-                )
-            }
+            } => write!(
+                f,
+                "IGMP membership query max_resp_time={} group_addr={} version={:?}",
+                max_resp_time, group_addr, version
+            ),
             Repr::MembershipReport {
             Repr::MembershipReport {
                 group_addr,
                 group_addr,
                 version,
                 version,
-            } => {
-                write!(
-                    f,
-                    "IGMP membership report group_addr={} version={:?}",
-                    group_addr, version
-                )
-            }
+            } => write!(
+                f,
+                "IGMP membership report group_addr={} version={:?}",
+                group_addr, version
+            ),
             Repr::LeaveGroup { group_addr } => {
             Repr::LeaveGroup { group_addr } => {
                 write!(f, "IGMP leave group group_addr={})", group_addr)
                 write!(f, "IGMP leave group group_addr={})", group_addr)
             }
             }

+ 4 - 6
src/wire/ipv6.rs

@@ -183,12 +183,10 @@ impl Address {
     /// unicast.
     /// unicast.
     pub fn solicited_node(&self) -> Address {
     pub fn solicited_node(&self) -> Address {
         assert!(self.is_unicast());
         assert!(self.is_unicast());
-        let mut bytes = [
-            0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-            0x00, 0x00,
-        ];
-        bytes[14..].copy_from_slice(&self.0[14..]);
-        Address(bytes)
+        Address([
+            0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF,
+            self.0[13], self.0[14], self.0[15],
+        ])
     }
     }
 }
 }
 
 

+ 209 - 4
src/wire/mod.rs

@@ -89,6 +89,8 @@ mod icmp;
 mod icmpv4;
 mod icmpv4;
 #[cfg(feature = "proto-ipv6")]
 #[cfg(feature = "proto-ipv6")]
 mod icmpv6;
 mod icmpv6;
+#[cfg(feature = "medium-ieee802154")]
+pub mod ieee802154;
 #[cfg(feature = "proto-igmp")]
 #[cfg(feature = "proto-igmp")]
 mod igmp;
 mod igmp;
 pub(crate) mod ip;
 pub(crate) mod ip;
@@ -106,13 +108,23 @@ mod ipv6option;
 mod ipv6routing;
 mod ipv6routing;
 #[cfg(feature = "proto-ipv6")]
 #[cfg(feature = "proto-ipv6")]
 mod mld;
 mod mld;
-#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
+#[cfg(all(
+    feature = "proto-ipv6",
+    any(feature = "medium-ethernet", feature = "medium-ieee802154")
+))]
 mod ndisc;
 mod ndisc;
-#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
+#[cfg(all(
+    feature = "proto-ipv6",
+    any(feature = "medium-ethernet", feature = "medium-ieee802154")
+))]
 mod ndiscoption;
 mod ndiscoption;
+#[cfg(all(feature = "proto-sixlowpan", feature = "medium-ieee802154"))]
+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")]
@@ -126,6 +138,24 @@ pub use self::arp::{
     Hardware as ArpHardware, Operation as ArpOperation, Packet as ArpPacket, Repr as ArpRepr,
     Hardware as ArpHardware, Operation as ArpOperation, Packet as ArpPacket, Repr as ArpRepr,
 };
 };
 
 
+#[cfg(all(feature = "proto-sixlowpan", feature = "medium-ieee802154"))]
+pub use self::sixlowpan::{
+    iphc::{Packet as SixlowpanIphcPacket, Repr as SixlowpanIphcRepr},
+    nhc::{
+        ExtensionHeaderPacket as SixlowpanExtHeaderPacket,
+        ExtensionHeaderRepr as SixlowpanExtHeaderRepr, Packet as SixlowpanNhcPacket,
+        UdpNhcRepr as SixlowpanUdpRepr, UdpPacket as SixlowpanUdpPacket,
+    },
+    NextHeader as SixlowpanNextHeader,
+};
+
+#[cfg(feature = "medium-ieee802154")]
+pub use self::ieee802154::{
+    Address as Ieee802154Address, AddressingMode as Ieee802154AddressingMode,
+    Frame as Ieee802154Frame, FrameType as Ieee802154FrameType,
+    FrameVersion as Ieee802154FrameVersion, Pan as Ieee802154Pan, Repr as Ieee802154Repr,
+};
+
 pub use self::ip::{
 pub use self::ip::{
     Address as IpAddress, Cidr as IpCidr, Endpoint as IpEndpoint, Protocol as IpProtocol,
     Address as IpAddress, Cidr as IpCidr, Endpoint as IpEndpoint, Protocol as IpProtocol,
     Repr as IpRepr, Version as IpVersion,
     Repr as IpRepr, Version as IpVersion,
@@ -177,12 +207,18 @@ pub use self::icmpv6::{
 #[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
 #[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
 pub use self::icmp::Repr as IcmpRepr;
 pub use self::icmp::Repr as IcmpRepr;
 
 
-#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
+#[cfg(all(
+    feature = "proto-ipv6",
+    any(feature = "medium-ethernet", feature = "medium-ieee802154")
+))]
 pub use self::ndisc::{
 pub use self::ndisc::{
     NeighborFlags as NdiscNeighborFlags, Repr as NdiscRepr, RouterFlags as NdiscRouterFlags,
     NeighborFlags as NdiscNeighborFlags, Repr as NdiscRepr, RouterFlags as NdiscRouterFlags,
 };
 };
 
 
-#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
+#[cfg(all(
+    feature = "proto-ipv6",
+    any(feature = "medium-ethernet", feature = "medium-ieee802154")
+))]
 pub use self::ndiscoption::{
 pub use self::ndiscoption::{
     NdiscOption, PrefixInfoFlags as NdiscPrefixInfoFlags,
     NdiscOption, PrefixInfoFlags as NdiscPrefixInfoFlags,
     PrefixInformation as NdiscPrefixInformation, RedirectedHeader as NdiscRedirectedHeader,
     PrefixInformation as NdiscPrefixInformation, RedirectedHeader as NdiscRedirectedHeader,
@@ -205,3 +241,172 @@ pub use self::dhcpv4::{
     CLIENT_PORT as DHCP_CLIENT_PORT, MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT,
     CLIENT_PORT as DHCP_CLIENT_PORT, MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT,
     SERVER_PORT as DHCP_SERVER_PORT,
     SERVER_PORT as DHCP_SERVER_PORT,
 };
 };
+
+/// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address.
+#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum HardwareAddress {
+    #[cfg(feature = "medium-ethernet")]
+    Ethernet(EthernetAddress),
+    #[cfg(feature = "medium-ieee802154")]
+    Ieee802154(Ieee802154Address),
+}
+
+#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
+impl HardwareAddress {
+    pub fn as_bytes(&self) -> &[u8] {
+        match self {
+            #[cfg(feature = "medium-ethernet")]
+            HardwareAddress::Ethernet(addr) => addr.as_bytes(),
+            #[cfg(feature = "medium-ieee802154")]
+            HardwareAddress::Ieee802154(addr) => addr.as_bytes(),
+        }
+    }
+
+    /// Query wether the address is an unicast address.
+    pub fn is_unicast(&self) -> bool {
+        match self {
+            #[cfg(feature = "medium-ethernet")]
+            HardwareAddress::Ethernet(addr) => addr.is_unicast(),
+            #[cfg(feature = "medium-ieee802154")]
+            HardwareAddress::Ieee802154(addr) => addr.is_unicast(),
+        }
+    }
+
+    /// Query wether the address is a broadcast address.
+    pub fn is_broadcast(&self) -> bool {
+        match self {
+            #[cfg(feature = "medium-ethernet")]
+            HardwareAddress::Ethernet(addr) => addr.is_broadcast(),
+            #[cfg(feature = "medium-ieee802154")]
+            HardwareAddress::Ieee802154(addr) => addr.is_broadcast(),
+        }
+    }
+}
+
+#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
+impl core::fmt::Display for HardwareAddress {
+    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+        match self {
+            #[cfg(feature = "medium-ethernet")]
+            HardwareAddress::Ethernet(addr) => write!(f, "{}", addr),
+            #[cfg(feature = "medium-ieee802154")]
+            HardwareAddress::Ieee802154(addr) => write!(f, "{}", addr),
+        }
+    }
+}
+
+#[cfg(feature = "medium-ethernet")]
+impl From<EthernetAddress> for HardwareAddress {
+    fn from(addr: EthernetAddress) -> Self {
+        HardwareAddress::Ethernet(addr)
+    }
+}
+
+#[cfg(feature = "medium-ieee802154")]
+impl From<Ieee802154Address> for HardwareAddress {
+    fn from(addr: Ieee802154Address) -> Self {
+        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.
+#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub struct RawHardwareAddress {
+    len: u8,
+    data: [u8; MAX_HARDWARE_ADDRESS_LEN],
+}
+
+#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
+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 is_empty(&self) -> bool {
+        self.len == 0
+    }
+
+    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!(),
+        }
+    }
+}
+
+#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
+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())
+    }
+}
+
+#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
+impl From<HardwareAddress> for RawHardwareAddress {
+    fn from(addr: HardwareAddress) -> Self {
+        Self::from_bytes(addr.as_bytes())
+    }
+}

+ 16 - 12
src/wire/ndisc.rs

@@ -4,7 +4,8 @@ use byteorder::{ByteOrder, NetworkEndian};
 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::Ipv6Address;
 use crate::wire::Ipv6Address;
-use crate::wire::{EthernetAddress, Ipv6Packet, Ipv6Repr};
+use crate::wire::RawHardwareAddress;
+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};
 use crate::{Error, Result};
 use crate::{Error, Result};
@@ -193,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<EthernetAddress>,
+        lladdr: Option<RawHardwareAddress>,
     },
     },
     RouterAdvert {
     RouterAdvert {
         hop_limit: u8,
         hop_limit: u8,
@@ -201,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<EthernetAddress>,
+        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<EthernetAddress>,
+        lladdr: Option<RawHardwareAddress>,
     },
     },
     NeighborAdvert {
     NeighborAdvert {
         flags: NeighborFlags,
         flags: NeighborFlags,
         target_addr: Ipv6Address,
         target_addr: Ipv6Address,
-        lladdr: Option<EthernetAddress>,
+        lladdr: Option<RawHardwareAddress>,
     },
     },
     Redirect {
     Redirect {
         target_addr: Ipv6Address,
         target_addr: Ipv6Address,
         dest_addr: Ipv6Address,
         dest_addr: Ipv6Address,
-        lladdr: Option<EthernetAddress>,
+        lladdr: Option<RawHardwareAddress>,
         redirected_hdr: Option<NdiscRedirectedHeader<'a>>,
         redirected_hdr: Option<NdiscRedirectedHeader<'a>>,
     },
     },
 }
 }
@@ -372,10 +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 {
-                    Some(_) => field::TARGET_ADDR.end + 8,
-                    None => field::TARGET_ADDR.end,
+                let mut offset = field::TARGET_ADDR.end;
+                if let Some(lladdr) = lladdr {
+                    offset += NdiscOptionRepr::SourceLinkLayerAddr(lladdr).buffer_len();
                 }
                 }
+                offset
             }
             }
             &Repr::Redirect {
             &Repr::Redirect {
                 lladdr,
                 lladdr,
@@ -429,8 +431,9 @@ 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);
-                    offset += 8;
+                    let opt = NdiscOptionRepr::SourceLinkLayerAddr(lladdr);
+                    opt.emit(&mut opt_pkt);
+                    offset += opt.buffer_len();
                 }
                 }
                 if let Some(mtu) = mtu {
                 if let Some(mtu) = mtu {
                     let mut opt_pkt =
                     let mut opt_pkt =
@@ -509,6 +512,7 @@ mod test {
     use super::*;
     use super::*;
     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::Icmpv6Repr;
     use crate::wire::Icmpv6Repr;
 
 
     static ROUTER_ADVERT_BYTES: [u8; 24] = [
     static ROUTER_ADVERT_BYTES: [u8; 24] = [
@@ -524,7 +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(EthernetAddress([0x52, 0x54, 0x00, 0x12, 0x34, 0x56])),
+            lladdr: Some(EthernetAddress([0x52, 0x54, 0x00, 0x12, 0x34, 0x56]).into()),
             mtu: None,
             mtu: None,
             prefix_info: None,
             prefix_info: None,
         })
         })

+ 21 - 15
src/wire/ndiscoption.rs

@@ -3,9 +3,11 @@ use byteorder::{ByteOrder, NetworkEndian};
 use core::fmt;
 use core::fmt;
 
 
 use crate::time::Duration;
 use crate::time::Duration;
-use crate::wire::{EthernetAddress, Ipv6Address, Ipv6Packet, Ipv6Repr};
+use crate::wire::{Ipv6Address, Ipv6Packet, Ipv6Repr, MAX_HARDWARE_ADDRESS_LEN};
 use crate::{Error, Result};
 use crate::{Error, Result};
 
 
+use crate::wire::RawHardwareAddress;
+
 enum_with_unknown! {
 enum_with_unknown! {
     /// NDISC Option Type
     /// NDISC Option Type
     pub enum Type(u8) {
     pub enum Type(u8) {
@@ -82,9 +84,6 @@ mod field {
     // |     Type      |    Length     |    Link-Layer Address ...
     // |     Type      |    Length     |    Link-Layer Address ...
     // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
 
-    // Link-Layer Address
-    pub const LL_ADDR: Field = 2..8;
-
     // Prefix Information Option fields.
     // Prefix Information Option fields.
     //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     //  |     Type      |    Length     | Prefix Length |L|A| Reserved1 |
     //  |     Type      |    Length     | Prefix Length |L|A| Reserved1 |
@@ -214,9 +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) -> EthernetAddress {
+    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();
-        EthernetAddress::from_bytes(&data[field::LL_ADDR])
+        RawHardwareAddress::from_bytes(&data[2..len + 2])
     }
     }
 }
 }
 
 
@@ -297,9 +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: EthernetAddress) {
+    pub fn set_link_layer_addr(&mut self, addr: RawHardwareAddress) {
         let data = self.buffer.as_mut();
         let data = self.buffer.as_mut();
-        data[field::LL_ADDR].copy_from_slice(addr.as_bytes())
+        data[2..2 + addr.len()].copy_from_slice(addr.as_bytes())
     }
     }
 }
 }
 
 
@@ -409,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(EthernetAddress),
-    TargetLinkLayerAddr(EthernetAddress),
+    SourceLinkLayerAddr(RawHardwareAddress),
+    TargetLinkLayerAddr(RawHardwareAddress),
     PrefixInformation(PrefixInformation),
     PrefixInformation(PrefixInformation),
     RedirectedHeader(RedirectedHeader<'a>),
     RedirectedHeader(RedirectedHeader<'a>),
     Mtu(u32),
     Mtu(u32),
@@ -488,7 +488,11 @@ 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) -> usize {
     pub fn buffer_len(&self) -> usize {
         match self {
         match self {
-            &Repr::SourceLinkLayerAddr(_) | &Repr::TargetLinkLayerAddr(_) => field::LL_ADDR.end,
+            &Repr::SourceLinkLayerAddr(addr) | &Repr::TargetLinkLayerAddr(addr) => {
+                let len = 2 + addr.len();
+                // Round up to next multiple of 8
+                (len + 7) / 8 * 8
+            }
             &Repr::PrefixInformation(_) => field::PREFIX.end,
             &Repr::PrefixInformation(_) => field::PREFIX.end,
             &Repr::RedirectedHeader(RedirectedHeader { header, data }) => {
             &Repr::RedirectedHeader(RedirectedHeader { header, data }) => {
                 field::IP_DATA + header.buffer_len() + data.len()
                 field::IP_DATA + header.buffer_len() + data.len()
@@ -506,12 +510,14 @@ impl<'a> Repr<'a> {
         match *self {
         match *self {
             Repr::SourceLinkLayerAddr(addr) => {
             Repr::SourceLinkLayerAddr(addr) => {
                 opt.set_option_type(Type::SourceLinkLayerAddr);
                 opt.set_option_type(Type::SourceLinkLayerAddr);
-                opt.set_data_len(1);
+                let opt_len = addr.len() + 2;
+                opt.set_data_len(((opt_len + 7) / 8) as u8); // round to next multiple of 8.
                 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);
-                opt.set_data_len(1);
+                let opt_len = addr.len() + 2;
+                opt.set_data_len(((opt_len + 7) / 8) as u8); // round to next multiple of 8.
                 opt.set_link_layer_addr(addr);
                 opt.set_link_layer_addr(addr);
             }
             }
             Repr::PrefixInformation(PrefixInformation {
             Repr::PrefixInformation(PrefixInformation {
@@ -668,14 +674,14 @@ mod test {
         {
         {
             assert_eq!(
             assert_eq!(
                 Repr::parse(&NdiscOption::new_unchecked(&bytes)),
                 Repr::parse(&NdiscOption::new_unchecked(&bytes)),
-                Ok(Repr::SourceLinkLayerAddr(addr))
+                Ok(Repr::SourceLinkLayerAddr(addr.into()))
             );
             );
         }
         }
         bytes[0] = 0x02;
         bytes[0] = 0x02;
         {
         {
             assert_eq!(
             assert_eq!(
                 Repr::parse(&NdiscOption::new_unchecked(&bytes)),
                 Repr::parse(&NdiscOption::new_unchecked(&bytes)),
-                Ok(Repr::TargetLinkLayerAddr(addr))
+                Ok(Repr::TargetLinkLayerAddr(addr.into()))
             );
             );
         }
         }
     }
     }

+ 1738 - 0
src/wire/sixlowpan.rs

@@ -0,0 +1,1738 @@
+/// Implementation of [RFC 6282] which specifies a compression format for IPv6 datagrams over
+/// IEEE802.154-based networks.
+///
+/// [RFC 6282]: https://datatracker.ietf.org/doc/html/rfc6282
+use crate::wire::ieee802154::Address as LlAddress;
+use crate::wire::ipv6;
+use crate::wire::IpProtocol;
+use crate::Error;
+use crate::Result;
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum NextHeader {
+    Compressed,
+    Uncompressed(IpProtocol),
+}
+
+/// A wrapper around the address provided in the 6LoWPAN_IPHC header.
+/// This requires some context to convert it the an IPv6 address in some cases.
+/// For 802.15.4 the context are the short/extended addresses.
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub enum Address<'a> {
+    Complete(ipv6::Address),
+    WithContext(&'a [u8]),
+    Elided,
+    Reserved,
+}
+
+impl<'a> Address<'a> {
+    /// Resolve the address provided by the IPHC encoding.
+    pub(crate) fn resolve(self, ll_addr: Option<LlAddress>) -> Result<ipv6::Address> {
+        match self {
+            Address::Complete(addr) => Ok(addr),
+            Address::Elided => {
+                let mut bytes = [0; 16];
+                bytes[0] = 0xfe;
+                bytes[1] = 0x80;
+
+                match ll_addr {
+                    Some(LlAddress::Short(ll)) => {
+                        bytes[11] = 0xff;
+                        bytes[12] = 0xfe;
+                        bytes[14..].copy_from_slice(&ll);
+                    }
+                    Some(LlAddress::Extended(ll)) => {
+                        bytes[8..].copy_from_slice(&LlAddress::Extended(ll).as_eui_64().unwrap());
+                    }
+                    _ => return Err(Error::Malformed),
+                }
+
+                Ok(ipv6::Address::from_bytes(&bytes))
+            }
+            Address::WithContext(_) => Err(Error::NotSupported),
+            Address::Reserved => Err(Error::Malformed),
+        }
+    }
+}
+
+pub mod iphc {
+    use crate::wire::ieee802154::Address as LlAddress;
+    use crate::wire::ipv6;
+    use crate::wire::IpProtocol;
+    use crate::Error;
+    use crate::Result;
+    use byteorder::{ByteOrder, NetworkEndian};
+
+    use super::Address;
+    use super::NextHeader;
+
+    mod field {
+        #![allow(non_snake_case)]
+
+        use crate::wire::field::*;
+
+        pub const IPHC_FIELD: Field = 0..2;
+    }
+
+    const DISPATCH: u8 = 0b011;
+
+    macro_rules! get_field {
+        ($name:ident, $mask:expr, $shift:expr) => {
+            fn $name(&self) -> u8 {
+                let data = self.buffer.as_ref();
+                let raw = NetworkEndian::read_u16(&data[field::IPHC_FIELD]);
+                ((raw >> $shift) & $mask) as u8
+            }
+        };
+    }
+
+    macro_rules! set_field {
+        ($name:ident, $mask:expr, $shift:expr) => {
+            fn $name(&mut self, val: u8) {
+                let data = &mut self.buffer.as_mut()[field::IPHC_FIELD];
+                let mut raw = NetworkEndian::read_u16(data);
+
+                raw = (raw & !($mask << $shift)) | ((val as u16) << $shift);
+                NetworkEndian::write_u16(data, raw);
+            }
+        };
+    }
+
+    /// A read/write wrapper around a LOWPAN_IPHC frame buffer.
+    #[derive(Debug, Clone)]
+    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+    pub struct Packet<T: AsRef<[u8]>> {
+        buffer: T,
+    }
+
+    impl<T: AsRef<[u8]>> Packet<T> {
+        /// Input a raw octet buffer with a 6LoWPAN_IPHC frame structure.
+        pub fn new_unchecked(buffer: T) -> Packet<T> {
+            Packet { buffer }
+        }
+
+        /// Shorthand for a combination of [new_unchecked] and [check_len].
+        ///
+        /// [new_unchecked]: #method.new_unchecked
+        /// [check_len]: #method.check_len
+        pub fn new_checked(buffer: T) -> Result<Packet<T>> {
+            let packet = Self::new_unchecked(buffer);
+            packet.check_len()?;
+            Ok(packet)
+        }
+
+        /// Ensure that no accessor method will panic if called.
+        /// Returns `Err(Error::Truncated)` if the buffer is too short.
+        pub fn check_len(&self) -> Result<()> {
+            let buffer = self.buffer.as_ref();
+            if buffer.len() < 2 {
+                Err(Error::Truncated)
+            } else {
+                Ok(())
+            }
+        }
+
+        /// Consumes the frame, returning the underlying buffer.
+        pub fn into_inner(self) -> T {
+            self.buffer
+        }
+
+        /// Return the Next Header field of this IPHC packet.
+        pub fn next_header(&self) -> NextHeader {
+            let nh = self.nh_field();
+
+            if nh == 1 {
+                // The next header field is compressed.
+                // It is also encoded using LOWPAN_NHC.
+                NextHeader::Compressed
+            } else {
+                // The full 8 bits for Next Header are carried in-line.
+                let start = (self.ip_fields_start() + self.traffic_class_size()) as usize;
+
+                let data = self.buffer.as_ref();
+                let nh = data[start..start + 1][0];
+                NextHeader::Uncompressed(IpProtocol::from(nh))
+            }
+        }
+
+        /// Return the Hop Limit of this IPHC packet.
+        pub fn hop_limit(&self) -> u8 {
+            match self.hlim_field() {
+                0b00 => {
+                    let start = (self.ip_fields_start()
+                        + self.traffic_class_size()
+                        + self.next_header_size()) as usize;
+
+                    let data = self.buffer.as_ref();
+                    data[start..start + 1][0]
+                }
+                0b01 => 1,
+                0b10 => 64,
+                0b11 => 255,
+                _ => unreachable!(),
+            }
+        }
+
+        /// Return the Source Context Identifier of this IPHC packet.
+        pub fn src_context_id(&self) -> Option<u8> {
+            if self.cid_field() == 1 {
+                let data = self.buffer.as_ref();
+                Some(data[1] >> 4)
+            } else {
+                None
+            }
+        }
+
+        /// Return the Destination Context Identifier of this IPHC packet.
+        pub fn dst_context_id(&self) -> Option<u8> {
+            if self.cid_field() == 1 {
+                let data = self.buffer.as_ref();
+                Some(data[1] & 0x0f)
+            } else {
+                None
+            }
+        }
+
+        /// Return the Source Address of this IPHC packet.
+        pub fn src_addr(&self) -> Result<Address> {
+            let start = (self.ip_fields_start()
+                + self.traffic_class_size()
+                + self.next_header_size()
+                + self.hop_limit_size()) as usize;
+
+            match (self.sac_field(), self.sam_field()) {
+                (0, 0b00) => {
+                    // The full address is carried in-line.
+                    let data = self.buffer.as_ref();
+                    Ok(Address::Complete(ipv6::Address::from_bytes(
+                        &data[start..start + 16],
+                    )))
+                }
+                (0, 0b01) => {
+                    // The first 64-bits of the address is elided.
+                    // The value of those bits is the link-local prefix padded with zeros.
+                    // The remaining 64-bits are carried in-line.
+                    let data = self.buffer.as_ref();
+                    let mut bytes = [0u8; 16];
+
+                    // Link-local prefix
+                    bytes[0] = 0xfe;
+                    bytes[1] = 0x80;
+
+                    bytes[8..].copy_from_slice(&data[start..start + 8]);
+
+                    Ok(Address::Complete(ipv6::Address::from_bytes(&bytes)))
+                }
+                (0, 0b10) => {
+                    // The first 112 bits of the address are elided.
+                    // The value of the 64 bits is the link-local prefix padded with zeros.
+                    // The following 64 bits are 0000:00ff:fe00:XXXX,
+                    // where XXXX are the bits carried in-line.
+                    let data = self.buffer.as_ref();
+                    let mut bytes = [0u8; 16];
+
+                    // Link-local prefix
+                    bytes[0] = 0xfe;
+                    bytes[1] = 0x80;
+
+                    bytes[11] = 0xff;
+                    bytes[12] = 0xfe;
+
+                    bytes[14..].copy_from_slice(&data[start..start + 2]);
+
+                    Ok(Address::Complete(ipv6::Address::from_bytes(&bytes)))
+                }
+                (0, 0b11) => {
+                    // The address is fully elided.
+                    // The first 64 bits of the address are the link-local prefix padded with zeros.
+                    // The remaining 64 bits are computed from the encapsulating header.
+                    Ok(Address::Elided)
+                }
+                (1, 0b00) => Ok(Address::Complete(ipv6::Address::UNSPECIFIED)),
+                (1, 0b01) => {
+                    // The address is derived using context information and the 64 bits carried in-line.
+                    // Bits covered by context information are always used.
+                    // Any IID bits not covered by context information are directly from the corresponding bits carried in-line.
+                    // Any remaining bits are zero.
+                    let data = self.buffer.as_ref();
+                    let bytes = &data[start..start + 8];
+
+                    Ok(Address::WithContext(bytes))
+                }
+                (1, 0b10) => {
+                    // The address is derived using context information and the 16 bits carried in-line.
+                    // Bits covered by context information are always used.
+                    // Any IID bits not covered by context information are directly from the corresponding bits carried in-line.
+                    // Any remaining bits are zero.
+                    let data = self.buffer.as_ref();
+                    let bytes = &data[start..start + 2];
+
+                    Ok(Address::WithContext(bytes))
+                }
+                (1, 0b11) => {
+                    // The address is fully elided and is derived using context information and the encapsulating header.
+                    // Bits covered by context information are always used.
+                    // Any IID bits not covered by context information are always used.
+                    // Any IID bits not covered by context information are directly from the corresponding bits carried in-line.
+                    // Any remaining bits are zero.
+                    Ok(Address::WithContext(&[]))
+                }
+                _ => Err(Error::Malformed),
+            }
+        }
+
+        /// Return the Destination Address of this IPHC packet.
+        pub fn dst_addr(&self) -> Result<Address> {
+            let start = (self.ip_fields_start()
+                + self.traffic_class_size()
+                + self.next_header_size()
+                + self.hop_limit_size()
+                + self.src_address_size()) as usize;
+
+            match (self.m_field(), self.dac_field(), self.dam_field()) {
+                (0, 0, 0b00) => {
+                    // The full address is carried in-line.
+                    let data = self.buffer.as_ref();
+                    Ok(Address::Complete(ipv6::Address::from_bytes(
+                        &data[start..start + 16],
+                    )))
+                }
+                (0, 0, 0b01) => {
+                    // The first 64-bits of the address is elided.
+                    // The value of those bits is the link-local prefix padded with zeros.
+                    // The remaining 64-bits are carried in-line.
+                    let data = self.buffer.as_ref();
+                    let mut bytes = [0u8; 16];
+
+                    // Link-local prefix
+                    bytes[0] = 0xfe;
+                    bytes[1] = 0x80;
+
+                    bytes[8..].copy_from_slice(&data[start..start + 8]);
+
+                    Ok(Address::Complete(ipv6::Address::from_bytes(&bytes)))
+                }
+                (0, 0, 0b10) => {
+                    // The first 112 bits of the address are elided.
+                    // The value of the 64 bits is the link-local prefix padded with zeros.
+                    // The following 64 bits are 0000:00ff:fe00:XXXX,
+                    // where XXXX are the bits carried in-line.
+                    let data = self.buffer.as_ref();
+                    let mut bytes = [0u8; 16];
+
+                    // Link-local prefix
+                    bytes[0] = 0xfe;
+                    bytes[1] = 0x80;
+
+                    bytes[11] = 0xff;
+                    bytes[12] = 0xfe;
+
+                    bytes[14..].copy_from_slice(&data[start..start + 2]);
+
+                    Ok(Address::Complete(ipv6::Address::from_bytes(&bytes)))
+                }
+                (0, 0, 0b11) => {
+                    // The address is fully elided.
+                    // The first 64 bits of the address are the link-local prefix padded with zeros.
+                    // The remaining 64 bits are computed from the encapsulating header.
+                    Ok(Address::Elided)
+                }
+                (0, 1, 0b00) => Ok(Address::Reserved),
+                (0, 1, 0b01) => {
+                    // The address is derived using context information and the 64 bits carried in-line.
+                    // Bits covered by context information are always used.
+                    // Any IID bits not covered by context information are directly from the corresponding bits carried in-line.
+                    // Any remaining bits are zero.
+                    let data = self.buffer.as_ref();
+                    let bytes = &data[start..start + 8];
+
+                    Ok(Address::WithContext(bytes))
+                }
+                (0, 1, 0b10) => {
+                    // The address is derived using context information and the 16 bits carried in-line.
+                    // Bits covered by context information are always used.
+                    // Any IID bits not covered by context information are directly from the corresponding bits carried in-line.
+                    // Any remaining bits are zero.
+                    let data = self.buffer.as_ref();
+                    let bytes = &data[start..start + 2];
+                    Ok(Address::WithContext(bytes))
+                }
+                (0, 1, 0b11) => {
+                    // The address is fully elided and is derived using context information and the encapsulating header.
+                    // Bits covered by context information are always used.
+                    // Any IID bits not covered by context information are always used.
+                    // Any IID bits not covered by context information are directly from the corresponding bits carried in-line.
+                    // Any remaining bits are zero.
+                    Ok(Address::WithContext(&[]))
+                }
+                (1, 0, 0b00) => {
+                    // The full address is carried in-line.
+                    let data = self.buffer.as_ref();
+                    Ok(Address::Complete(ipv6::Address::from_bytes(
+                        &data[start..start + 16],
+                    )))
+                }
+                (1, 0, 0b01) => {
+                    // The address takes the form ffXX::00XX:XXXX:XXXX
+                    let data = self.buffer.as_ref();
+                    let mut bytes = [0u8; 16];
+
+                    bytes[0] = 0xff;
+                    bytes[1] = data[start];
+
+                    bytes[11..].copy_from_slice(&data[start + 1..start + 6]);
+
+                    Ok(Address::Complete(ipv6::Address::from_bytes(&bytes)))
+                }
+                (1, 0, 0b10) => {
+                    // The address takes the form ffXX::00XX:XXXX
+                    let data = self.buffer.as_ref();
+                    let mut bytes = [0u8; 16];
+
+                    bytes[0] = 0xff;
+                    bytes[1] = data[start];
+
+                    bytes[13..].copy_from_slice(&data[start + 1..start + 4]);
+
+                    Ok(Address::Complete(ipv6::Address::from_bytes(&bytes)))
+                }
+                (1, 0, 0b11) => {
+                    // The address takes the form ff02::00XX
+                    let data = self.buffer.as_ref();
+                    let mut bytes = [0u8; 16];
+
+                    bytes[0] = 0xff;
+                    bytes[1] = 0x02;
+
+                    bytes[15] = data[start];
+
+                    Ok(Address::Complete(ipv6::Address::from_bytes(&bytes)))
+                }
+                (1, 1, 0b00) => {
+                    // This format is designed to match Unicast-Prefix-based IPv6 Multicast Addresses.
+                    // The multicast takes the form ffXX:XXLL:PPPP:PPPP:PPPP:PPPP:XXXX:XXXX.
+                    // X are octets that are carried in-line, in the order in which they appear.
+                    // P are octets used to encode the prefix itself.
+                    // L are octets used to encode the prefix length.
+                    // The prefix information P and L is taken from the specified context.
+                    Err(Error::NotSupported)
+                }
+                (1, 1, 0b01 | 0b10 | 0b11) => Ok(Address::Reserved),
+                _ => Err(Error::Malformed),
+            }
+        }
+
+        get_field!(dispatch_field, 0b111, 13);
+        get_field!(tf_field, 0b11, 11);
+        get_field!(nh_field, 0b1, 10);
+        get_field!(hlim_field, 0b11, 8);
+        get_field!(cid_field, 0b1, 7);
+        get_field!(sac_field, 0b1, 6);
+        get_field!(sam_field, 0b11, 4);
+        get_field!(m_field, 0b1, 3);
+        get_field!(dac_field, 0b1, 2);
+        get_field!(dam_field, 0b11, 0);
+
+        /// Return the start for the IP fields.
+        fn ip_fields_start(&self) -> u8 {
+            2 + self.cid_size()
+        }
+
+        /// Get the size in octets of the traffic class field.
+        fn traffic_class_size(&self) -> u8 {
+            match self.tf_field() {
+                0b00 => 4,
+                0b01 => 3,
+                0b10 => 1,
+                0b11 => 0,
+                _ => unreachable!(),
+            }
+        }
+
+        /// Get the size in octets of the next header field.
+        fn next_header_size(&self) -> u8 {
+            (self.nh_field() != 1) as u8
+        }
+
+        /// Get the size in octets of the hop limit field.
+        fn hop_limit_size(&self) -> u8 {
+            (self.hlim_field() == 0b00) as u8
+        }
+
+        /// Get the size in octets of the CID field.
+        fn cid_size(&self) -> u8 {
+            (self.cid_field() == 1) as u8
+        }
+
+        /// Get the size in octets of the source address.
+        fn src_address_size(&self) -> u8 {
+            match (self.sac_field(), self.sam_field()) {
+                (0, 0b00) => 16, // The full address is carried in-line.
+                (0, 0b01) => 8,  // The first 64 bits are elided.
+                (0, 0b10) => 2,  // The first 112 bits are elided.
+                (0, 0b11) => 0,  // The address is fully elided.
+                (1, 0b00) => 0,  // The UNSPECIFIED address.
+                (1, 0b01) => 8,  // Address derived using context information.
+                (1, 0b10) => 2,  // Address derived using context information.
+                (1, 0b11) => 0,  // Address derived using context information.
+                _ => unreachable!(),
+            }
+        }
+
+        /// Get the size in octets of the address address.
+        fn dst_address_size(&self) -> u8 {
+            match (self.m_field(), self.dac_field(), self.dam_field()) {
+                (0, 0, 0b00) => 16, // The full address is carried in-line.
+                (0, 0, 0b01) => 8,  // The first 64 bits are elided.
+                (0, 0, 0b10) => 2,  // The first 112 bits are elided.
+                (0, 0, 0b11) => 0,  // The address is fully elided.
+                (0, 1, 0b00) => 0,  // Reserved.
+                (0, 1, 0b01) => 8,  // Address derived using context information.
+                (0, 1, 0b10) => 2,  // Address derived using context information.
+                (0, 1, 0b11) => 0,  // Address derived using context information.
+                (1, 0, 0b00) => 16, // The full address is carried in-line.
+                (1, 0, 0b01) => 6,  // The address takes the form ffXX::00XX:XXXX:XXXX.
+                (1, 0, 0b10) => 4,  // The address takes the form ffXX::00XX:XXXX.
+                (1, 0, 0b11) => 1,  // The address takes the form ff02::00XX.
+                (1, 1, 0b00) => 6,  // Match Unicast-Prefix-based IPv6.
+                (1, 1, 0b01) => 0,  // Reserved.
+                (1, 1, 0b10) => 0,  // Reserved.
+                (1, 1, 0b11) => 0,  // Reserved.
+                _ => unreachable!(),
+            }
+        }
+    }
+
+    impl<'a, T: AsRef<[u8]> + ?Sized> Packet<&'a T> {
+        /// Return a pointer to the payload.
+        pub fn payload(&self) -> &'a [u8] {
+            let mut len = self.ip_fields_start();
+            len += self.traffic_class_size();
+            len += self.next_header_size();
+            len += self.hop_limit_size();
+            len += self.src_address_size();
+            len += self.dst_address_size();
+
+            let len = len as usize;
+
+            let data = self.buffer.as_ref();
+            &data[len..]
+        }
+    }
+
+    impl<'a, T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
+        /// Set the dispatch field to `0b011`.
+        fn set_dispatch_field(&mut self) {
+            let data = &mut self.buffer.as_mut()[field::IPHC_FIELD];
+            let mut raw = NetworkEndian::read_u16(data);
+
+            raw = (raw & !(0b111 << 13)) | (0b11 << 13);
+            NetworkEndian::write_u16(data, raw);
+        }
+
+        set_field!(set_tf_field, 0b11, 11);
+        set_field!(set_nh_field, 0b1, 10);
+        set_field!(set_hlim_field, 0b11, 8);
+        set_field!(set_cid_field, 0b1, 7);
+        set_field!(set_sac_field, 0b1, 6);
+        set_field!(set_sam_field, 0b11, 4);
+        set_field!(set_m_field, 0b1, 3);
+        set_field!(set_dac_field, 0b1, 2);
+        set_field!(set_dam_field, 0b11, 0);
+
+        fn set_field(&mut self, idx: usize, value: &[u8]) {
+            let raw = self.buffer.as_mut();
+            raw[idx..idx + value.len()].copy_from_slice(value);
+        }
+
+        /// Set the Next Header of this IPHC packet.
+        ///
+        /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to.
+        fn set_next_header(&mut self, nh: NextHeader, mut idx: usize) -> usize {
+            match nh {
+                NextHeader::Uncompressed(nh) => {
+                    self.set_nh_field(0);
+                    self.set_field(idx, &[nh.into()]);
+                    idx += 1;
+                }
+                NextHeader::Compressed => self.set_nh_field(1),
+            }
+
+            idx
+        }
+
+        /// Set the Hop Limit of this IPHC packet.
+        ///
+        /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to.
+        fn set_hop_limit(&mut self, hl: u8, mut idx: usize) -> usize {
+            match hl {
+                255 => self.set_hlim_field(0b11),
+                64 => self.set_hlim_field(0b10),
+                1 => self.set_hlim_field(0b01),
+                _ => {
+                    self.set_hlim_field(0b00);
+                    self.set_field(idx, &[hl]);
+                    idx += 1;
+                }
+            }
+
+            idx
+        }
+
+        /// Set the Source Address based on the IPv6 address and the Link-Local address.
+        ///
+        /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to.
+        fn set_src_address(
+            &mut self,
+            src_addr: ipv6::Address,
+            ll_src_addr: Option<LlAddress>,
+            mut idx: usize,
+        ) -> usize {
+            self.set_cid_field(0);
+            self.set_sac_field(0);
+            self.set_sam_field(0b11);
+            let src = src_addr.as_bytes();
+            if src_addr == ipv6::Address::UNSPECIFIED {
+                self.set_sac_field(1);
+                self.set_sam_field(0b00);
+            } else if src_addr.is_link_local() {
+                // We have a link local address.
+                // The remainder of the address can be elided when the context contains
+                // a 802.15.4 short address or a 802.15.4 extended address which can be
+                // converted to a eui64 address.
+                let is_eui_64 = ll_src_addr
+                    .map(|addr| {
+                        addr.as_eui_64()
+                            .map(|addr| addr[..] == src[8..])
+                            .unwrap_or(false)
+                    })
+                    .unwrap_or(false);
+
+                if src[8..14] == [0, 0, 0, 0xff, 0xfe, 0] {
+                    let ll = [src[14], src[15]];
+
+                    if ll_src_addr == Some(LlAddress::Short(ll)) {
+                        // We have the context from the 802.15.4 frame.
+                        // The context contains the short address.
+                        // We can elide the source address.
+                        self.set_sam_field(0b11);
+                    } else {
+                        // We don't have the context from the 802.15.4 frame.
+                        // We cannot elide the source address, however we can elide 112 bits.
+                        self.set_sam_field(0b10);
+
+                        self.set_field(idx, &src[14..]);
+                        idx += 2;
+                    }
+                } else if is_eui_64 {
+                    // We have the context from the 802.15.4 frame.
+                    // The context contains the extended address.
+                    // We can elide the source address.
+                    self.set_sam_field(0b11);
+                } else {
+                    // We cannot elide the source address, however we can elide 64 bits.
+                    self.set_sam_field(0b01);
+
+                    self.set_field(idx, &src[8..]);
+                    idx += 8;
+                }
+            } else {
+                // We cannot elide anything.
+                self.set_field(idx, src);
+                idx += 16;
+            }
+
+            idx
+        }
+
+        /// Set the Destination Address based on the IPv6 address and the Link-Local address.
+        ///
+        /// **NOTE**: `idx` is the offset at which the Next Header needs to be written to.
+        fn set_dst_address(
+            &mut self,
+            dst_addr: ipv6::Address,
+            ll_dst_addr: Option<LlAddress>,
+            mut idx: usize,
+        ) -> usize {
+            self.set_dac_field(0);
+            self.set_dam_field(0);
+            self.set_m_field(0);
+            let dst = dst_addr.as_bytes();
+            if dst_addr.is_multicast() {
+                self.set_m_field(1);
+
+                if dst[1] == 0x02 && dst[2..15] == [0; 13] {
+                    self.set_dam_field(0b11);
+
+                    self.set_field(idx, &[dst[15]]);
+                    idx += 1;
+                } else if dst[2..13] == [0; 11] {
+                    self.set_dam_field(0b10);
+
+                    self.set_field(idx, &[dst[1]]);
+                    idx += 1;
+                    self.set_field(idx, &dst[13..]);
+                    idx += 3;
+                } else if dst[2..11] == [0; 9] {
+                    self.set_dam_field(0b01);
+
+                    self.set_field(idx, &[dst[1]]);
+                    idx += 1;
+                    self.set_field(idx, &dst[11..]);
+                    idx += 5;
+                } else {
+                    self.set_dam_field(0b11);
+
+                    self.set_field(idx, dst);
+                    idx += 16;
+                }
+            } else if dst_addr.is_link_local() {
+                let is_eui_64 = ll_dst_addr
+                    .map(|addr| {
+                        addr.as_eui_64()
+                            .map(|addr| addr[..] == dst[8..])
+                            .unwrap_or(false)
+                    })
+                    .unwrap_or(false);
+
+                if dst[8..14] == [0, 0, 0, 0xff, 0xfe, 0] {
+                    let ll = [dst[14], dst[15]];
+
+                    if ll_dst_addr == Some(LlAddress::Short(ll)) {
+                        self.set_dam_field(0b11);
+                    } else {
+                        self.set_dam_field(0b10);
+
+                        self.set_field(idx, &dst[14..]);
+                        idx += 2;
+                    }
+                } else if is_eui_64 {
+                    self.set_dam_field(0b11);
+                } else {
+                    self.set_dam_field(0b01);
+
+                    self.set_field(idx, &dst[8..]);
+                    idx += 8;
+                }
+            } else {
+                self.set_dam_field(0b00);
+
+                self.set_field(idx, dst);
+                idx += 16;
+            }
+
+            idx
+        }
+
+        /// Return a mutable pointer to the payload.
+        pub fn payload_mut(&mut self) -> &mut [u8] {
+            let mut len = self.ip_fields_start();
+
+            len += self.traffic_class_size();
+            len += self.next_header_size();
+            len += self.hop_limit_size();
+            len += self.src_address_size();
+            len += self.dst_address_size();
+
+            let len = len as usize;
+
+            let data = self.buffer.as_mut();
+            &mut data[len..]
+        }
+    }
+
+    /// A high-level representation of a LOWPAN_IPHC header.
+    #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+    pub struct Repr {
+        pub src_addr: ipv6::Address,
+        pub ll_src_addr: Option<LlAddress>,
+        pub dst_addr: ipv6::Address,
+        pub ll_dst_addr: Option<LlAddress>,
+        pub next_header: NextHeader,
+        pub hop_limit: u8,
+    }
+
+    impl Repr {
+        /// Parse a LOWPAN_IPHC packet and return a high-level representation.
+        ///
+        /// The `ll_src_addr` and `ll_dst_addr` are the link-local addresses used for resolving the
+        /// IPv6 packets.
+        pub fn parse<T: AsRef<[u8]> + ?Sized>(
+            packet: &Packet<&T>,
+            ll_src_addr: Option<LlAddress>,
+            ll_dst_addr: Option<LlAddress>,
+        ) -> Result<Repr> {
+            // Ensure basic accessors will work.
+            packet.check_len()?;
+
+            if packet.dispatch_field() != DISPATCH {
+                // This is not an LOWPAN_IPHC packet.
+                return Err(Error::Malformed);
+            }
+
+            let src_addr = packet.src_addr()?.resolve(ll_src_addr)?;
+            let dst_addr = packet.dst_addr()?.resolve(ll_dst_addr)?;
+
+            Ok(Repr {
+                src_addr,
+                ll_src_addr,
+                dst_addr,
+                ll_dst_addr,
+                next_header: packet.next_header(),
+                hop_limit: packet.hop_limit(),
+            })
+        }
+
+        /// Return the length of a header that will be emitted from this high-level representation.
+        pub fn buffer_len(&self) -> usize {
+            let mut len = 0;
+            len += 2; // The minimal header length
+
+            len += if self.next_header == NextHeader::Compressed {
+                0 // The next header is compressed (we don't need to inline what the next header is)
+            } else {
+                1 // The next header field is inlined
+            };
+
+            // Add the lenght of the source address
+            len += if self.src_addr == ipv6::Address::UNSPECIFIED {
+                0
+            } else if self.src_addr.is_link_local() {
+                let src = self.src_addr.as_bytes();
+                let ll = [src[14], src[15]];
+
+                let is_eui_64 = self
+                    .ll_src_addr
+                    .map(|addr| {
+                        addr.as_eui_64()
+                            .map(|addr| addr[..] == src[8..])
+                            .unwrap_or(false)
+                    })
+                    .unwrap_or(false);
+
+                if src[8..14] == [0, 0, 0, 0xff, 0xfe, 0] {
+                    if self.ll_src_addr == Some(LlAddress::Short(ll)) {
+                        0
+                    } else {
+                        2
+                    }
+                } else if is_eui_64 {
+                    0
+                } else {
+                    8
+                }
+            } else {
+                16
+            };
+
+            // Add the size of the destination header
+            let dst = self.dst_addr.as_bytes();
+            len += if self.dst_addr.is_multicast() {
+                if dst[1] == 0x02 && dst[2..15] == [0; 13] {
+                    1
+                } else if dst[2..13] == [0; 11] {
+                    4
+                } else if dst[2..11] == [0; 9] {
+                    6
+                } else {
+                    16
+                }
+            } else if self.dst_addr.is_link_local() {
+                let is_eui_64 = self
+                    .ll_dst_addr
+                    .map(|addr| {
+                        addr.as_eui_64()
+                            .map(|addr| addr[..] == dst[8..])
+                            .unwrap_or(false)
+                    })
+                    .unwrap_or(false);
+
+                if dst[8..14] == [0, 0, 0, 0xff, 0xfe, 0] {
+                    let ll = [dst[14], dst[15]];
+
+                    if self.ll_dst_addr == Some(LlAddress::Short(ll)) {
+                        0
+                    } else {
+                        2
+                    }
+                } else if is_eui_64 {
+                    0
+                } else {
+                    8
+                }
+            } else {
+                16
+            };
+
+            // Add the size of the traffic flow.
+            // TODO(thvdveld): implement traffic flow for sixlowpan
+            len += 0;
+
+            len
+        }
+
+        /// Emit a high-level representation into a LOWPAN_IPHC packet.
+        pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>) {
+            let idx = 2;
+
+            packet.set_dispatch_field();
+
+            // SETTING THE TRAFIC FLOW
+            // TODO(thvdveld): needs more work.
+            packet.set_tf_field(0b11);
+
+            let idx = packet.set_next_header(self.next_header, idx);
+            let idx = packet.set_hop_limit(self.hop_limit, idx);
+            let idx = packet.set_src_address(self.src_addr, self.ll_src_addr, idx);
+            packet.set_dst_address(self.dst_addr, self.ll_dst_addr, idx);
+        }
+    }
+
+    #[cfg(test)]
+    mod test {
+        use super::*;
+
+        #[test]
+        fn iphc_fields() {
+            let bytes = [
+                0x7a, 0x33, // IPHC
+                0x3a, // Next header
+            ];
+
+            let packet = Packet::new_unchecked(bytes);
+
+            assert_eq!(packet.dispatch_field(), 0b011);
+            assert_eq!(packet.tf_field(), 0b11);
+            assert_eq!(packet.nh_field(), 0b0);
+            assert_eq!(packet.hlim_field(), 0b10);
+            assert_eq!(packet.cid_field(), 0b0);
+            assert_eq!(packet.sac_field(), 0b0);
+            assert_eq!(packet.sam_field(), 0b11);
+            assert_eq!(packet.m_field(), 0b0);
+            assert_eq!(packet.dac_field(), 0b0);
+            assert_eq!(packet.dam_field(), 0b11);
+
+            assert_eq!(
+                packet.next_header(),
+                NextHeader::Uncompressed(IpProtocol::Icmpv6)
+            );
+
+            assert_eq!(packet.src_address_size(), 0);
+            assert_eq!(packet.dst_address_size(), 0);
+            assert_eq!(packet.hop_limit(), 64);
+
+            assert_eq!(packet.src_addr(), Ok(Address::Elided));
+            assert_eq!(packet.dst_addr(), Ok(Address::Elided));
+
+            let bytes = [
+                0x7e, 0xf7, // IPHC,
+                0x00, // CID
+            ];
+
+            let packet = Packet::new_unchecked(bytes);
+
+            assert_eq!(packet.dispatch_field(), 0b011);
+            assert_eq!(packet.tf_field(), 0b11);
+            assert_eq!(packet.nh_field(), 0b1);
+            assert_eq!(packet.hlim_field(), 0b10);
+            assert_eq!(packet.cid_field(), 0b1);
+            assert_eq!(packet.sac_field(), 0b1);
+            assert_eq!(packet.sam_field(), 0b11);
+            assert_eq!(packet.m_field(), 0b0);
+            assert_eq!(packet.dac_field(), 0b1);
+            assert_eq!(packet.dam_field(), 0b11);
+
+            assert_eq!(packet.next_header(), NextHeader::Compressed);
+
+            assert_eq!(packet.src_address_size(), 0);
+            assert_eq!(packet.dst_address_size(), 0);
+            assert_eq!(packet.hop_limit(), 64);
+
+            assert_eq!(packet.src_addr(), Ok(Address::WithContext(&[])));
+            assert_eq!(packet.dst_addr(), Ok(Address::WithContext(&[])));
+        }
+    }
+}
+
+pub mod nhc {
+    use crate::wire::ip::checksum;
+    use crate::wire::ip::Address as IpAddress;
+    use crate::wire::ipv6;
+    use crate::wire::udp::Repr as UdpRepr;
+    use crate::wire::IpProtocol;
+    use crate::Error;
+    use crate::Result;
+    use byteorder::{ByteOrder, NetworkEndian};
+    use ipv6::Address;
+
+    use super::NextHeader;
+
+    macro_rules! get_field {
+        ($name:ident, $mask:expr, $shift:expr) => {
+            fn $name(&self) -> u8 {
+                let data = self.buffer.as_ref();
+                let raw = &data[0];
+                ((raw >> $shift) & $mask) as u8
+            }
+        };
+    }
+
+    macro_rules! set_field {
+        ($name:ident, $mask:expr, $shift:expr) => {
+            fn $name(&mut self, val: u8) {
+                let data = self.buffer.as_mut();
+                let mut raw = data[0];
+                raw = (raw & !($mask << $shift)) | (val << $shift);
+                data[0] = raw;
+            }
+        };
+    }
+
+    /// A read/write wrapper around a LOWPAN_NHC frame buffer.
+    #[derive(Debug, Clone)]
+    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+    pub enum Packet<T: AsRef<[u8]>> {
+        ExtensionHeader(ExtensionHeaderPacket<T>),
+        UdpHeader(UdpPacket<T>),
+    }
+
+    impl<T: AsRef<[u8]>> Packet<T> {
+        pub fn dispatch(buffer: T) -> Result<Packet<T>> {
+            let raw = buffer.as_ref();
+
+            #[cfg(feature = "std")]
+            println!("{:02x?}", raw[0]);
+
+            if raw[0] >> 4 == 0b1110 {
+                // We have a compressed IPv6 Extension Header.
+                Ok(Packet::ExtensionHeader(ExtensionHeaderPacket::new_checked(
+                    buffer,
+                )?))
+            } else if raw[0] >> 3 == 0b11110 {
+                // We have a compressed UDP header.
+                Ok(Packet::UdpHeader(UdpPacket::new_checked(buffer)?))
+            } else {
+                Err(Error::Unrecognized)
+            }
+        }
+    }
+
+    #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+    pub enum ExtensionHeaderId {
+        HopByHopHeader,
+        RoutingHeader,
+        FragmentHeader,
+        DestinationOptionsHeader,
+        MobilityHeader,
+        Header,
+        Reserved,
+    }
+
+    impl From<ExtensionHeaderId> for IpProtocol {
+        fn from(val: ExtensionHeaderId) -> Self {
+            match val {
+                ExtensionHeaderId::HopByHopHeader => IpProtocol::HopByHop,
+                ExtensionHeaderId::RoutingHeader => IpProtocol::Ipv6Route,
+                ExtensionHeaderId::FragmentHeader => IpProtocol::Ipv6Frag,
+                ExtensionHeaderId::DestinationOptionsHeader => IpProtocol::Ipv6Opts,
+                ExtensionHeaderId::MobilityHeader => IpProtocol::Unknown(0),
+                ExtensionHeaderId::Header => IpProtocol::Unknown(0),
+                ExtensionHeaderId::Reserved => IpProtocol::Unknown(0),
+            }
+        }
+    }
+
+    pub(crate) const EXT_HEADER_DISPATCH: u8 = 0b1110;
+
+    /// A read/write wrapper around a LOWPAN_NHC Next Header frame buffer.
+    #[derive(Debug, Clone)]
+    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+    pub struct ExtensionHeaderPacket<T: AsRef<[u8]>> {
+        buffer: T,
+    }
+
+    impl<T: AsRef<[u8]>> ExtensionHeaderPacket<T> {
+        /// Input a raw octet buffer with a LOWPAN_NHC Extension Header frame structure.
+        pub fn new_unchecked(buffer: T) -> ExtensionHeaderPacket<T> {
+            ExtensionHeaderPacket { buffer }
+        }
+
+        /// Shorthand for a combination of [new_unchecked] and [check_len].
+        ///
+        /// [new_unchecked]: #method.new_unchecked
+        /// [check_len]: #method.check_len
+        pub fn new_checked(buffer: T) -> Result<ExtensionHeaderPacket<T>> {
+            let packet = Self::new_unchecked(buffer);
+            packet.check_len()?;
+            Ok(packet)
+        }
+
+        /// Ensure that no accessor method will panic if called.
+        /// Returns `Err(Error::Truncated)` if the buffer is too short.
+        pub fn check_len(&self) -> Result<()> {
+            let buffer = self.buffer.as_ref();
+            if buffer.is_empty() {
+                Err(Error::Truncated)
+            } else {
+                Ok(())
+            }
+        }
+
+        /// Consumes the frame, returning the underlying buffer.
+        pub fn into_inner(self) -> T {
+            self.buffer
+        }
+
+        get_field!(dispatch_field, 0b1111, 4);
+        get_field!(eid_field, 0b111, 1);
+        get_field!(nh_field, 0b1, 0);
+
+        /// Return the Extension Header ID.
+        pub fn extension_header_id(&self) -> ExtensionHeaderId {
+            match self.eid_field() {
+                0 => ExtensionHeaderId::HopByHopHeader,
+                1 => ExtensionHeaderId::RoutingHeader,
+                2 => ExtensionHeaderId::FragmentHeader,
+                3 => ExtensionHeaderId::DestinationOptionsHeader,
+                4 => ExtensionHeaderId::MobilityHeader,
+                5 | 6 => ExtensionHeaderId::Reserved,
+                7 => ExtensionHeaderId::Header,
+                _ => unreachable!(),
+            }
+        }
+
+        /// Return the length field.
+        pub fn length_field(&self) -> u8 {
+            let start = 1 + self.next_header_size();
+
+            let data = self.buffer.as_ref();
+            data[start]
+        }
+
+        /// Parse the next header field.
+        pub fn next_header(&self) -> NextHeader {
+            if self.nh_field() == 1 {
+                NextHeader::Compressed
+            } else {
+                // The full 8 bits for Next Header are carried in-line.
+                let start = 1;
+
+                let data = self.buffer.as_ref();
+                let nh = data[start];
+                NextHeader::Uncompressed(IpProtocol::from(nh))
+            }
+        }
+
+        /// Return the size of the Next Header field.
+        fn next_header_size(&self) -> usize {
+            // If nh is set, then the Next Header is compressed using LOWPAN_NHC
+            if self.nh_field() == 1 {
+                0
+            } else {
+                1
+            }
+        }
+    }
+
+    impl<'a, T: AsRef<[u8]> + ?Sized> ExtensionHeaderPacket<&'a T> {
+        /// Return a pointer to the payload.
+        pub fn payload(&self) -> &'a [u8] {
+            let start = 2 + self.next_header_size();
+            &self.buffer.as_ref()[start..]
+        }
+    }
+
+    impl<'a, T: AsRef<[u8]> + AsMut<[u8]>> ExtensionHeaderPacket<T> {
+        /// Return a mutable pointer to the payload.
+        pub fn payload_mut(&mut self) -> &mut [u8] {
+            let start = 2 + self.next_header_size();
+            &mut self.buffer.as_mut()[start..]
+        }
+
+        /// Set the dispatch field to `0b1110`.
+        fn set_dispatch_field(&mut self) {
+            let data = self.buffer.as_mut();
+            data[0] = (data[0] & !(0b1111 << 4)) | (EXT_HEADER_DISPATCH << 4);
+        }
+
+        set_field!(set_eid_field, 0b111, 1);
+        set_field!(set_nh_field, 0b1, 0);
+
+        /// Set the Extension Header ID field.
+        fn set_extension_header_id(&mut self, ext_header_id: ExtensionHeaderId) {
+            let id = match ext_header_id {
+                ExtensionHeaderId::HopByHopHeader => 0,
+                ExtensionHeaderId::RoutingHeader => 1,
+                ExtensionHeaderId::FragmentHeader => 2,
+                ExtensionHeaderId::DestinationOptionsHeader => 3,
+                ExtensionHeaderId::MobilityHeader => 4,
+                ExtensionHeaderId::Header => 7,
+                _ => unreachable!(),
+            };
+
+            self.set_eid_field(id);
+        }
+
+        /// Set the Next Header.
+        fn set_next_header(&mut self, next_header: NextHeader) {
+            match next_header {
+                NextHeader::Compressed => self.set_nh_field(0b1),
+                NextHeader::Uncompressed(nh) => {
+                    self.set_nh_field(0b0);
+
+                    let start = 1;
+                    let data = self.buffer.as_mut();
+                    data[start] = nh.into();
+                }
+            }
+        }
+
+        /// Set the length.
+        fn set_length(&mut self, length: u8) {
+            let start = 1 + self.next_header_size();
+
+            let data = self.buffer.as_mut();
+            data[start] = length;
+        }
+    }
+
+    /// A high-level representation of an LOWPAN_NHC Extension Header header.
+    #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+    pub struct ExtensionHeaderRepr {
+        ext_header_id: ExtensionHeaderId,
+        next_header: NextHeader,
+        length: u8,
+    }
+
+    impl ExtensionHeaderRepr {
+        /// Parse a LOWPAN_NHC Extension Header packet and return a high-level representation.
+        pub fn parse<T: AsRef<[u8]> + ?Sized>(
+            packet: &ExtensionHeaderPacket<&T>,
+        ) -> Result<ExtensionHeaderRepr> {
+            // Ensure basic accessors will work.
+            packet.check_len()?;
+
+            if packet.dispatch_field() != EXT_HEADER_DISPATCH {
+                return Err(Error::Malformed);
+            }
+
+            Ok(ExtensionHeaderRepr {
+                ext_header_id: packet.extension_header_id(),
+                next_header: packet.next_header(),
+                length: packet.payload().len() as u8,
+            })
+        }
+
+        /// Return the length of a header that will be emitted from this high-level representation.
+        pub fn buffer_len(&self) -> usize {
+            let mut len = 1; // The minimal header size
+
+            if self.next_header != NextHeader::Compressed {
+                len += 1;
+            }
+
+            len += 1; // The length
+
+            len
+        }
+
+        /// Emit a high-level representaiton into a LOWPAN_NHC Extension Header packet.
+        pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut ExtensionHeaderPacket<T>) {
+            packet.set_dispatch_field();
+            packet.set_extension_header_id(self.ext_header_id);
+            packet.set_next_header(self.next_header);
+            packet.set_length(self.length);
+        }
+    }
+
+    pub(crate) const UDP_DISPATCH: u8 = 0b11110;
+
+    /// A read/write wrapper around a 6LoWPAN_NHC_UDP frame buffer.
+    #[derive(Debug, Clone)]
+    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+    pub struct UdpPacket<T: AsRef<[u8]>> {
+        buffer: T,
+    }
+
+    impl<T: AsRef<[u8]>> UdpPacket<T> {
+        /// Input a raw octet buffer with a LOWPAN_NHC frame structure for UDP.
+        pub fn new_unchecked(buffer: T) -> UdpPacket<T> {
+            UdpPacket { buffer }
+        }
+
+        /// Shorthand for a combination of [new_unchecked] and [check_len].
+        ///
+        /// [new_unchecked]: #method.new_unchecked
+        /// [check_len]: #method.check_len
+        pub fn new_checked(buffer: T) -> Result<UdpPacket<T>> {
+            let packet = Self::new_unchecked(buffer);
+            packet.check_len()?;
+            Ok(packet)
+        }
+
+        /// Ensure that no accessor method will panic if called.
+        /// Returns `Err(Error::Truncated)` if the buffer is too short.
+        pub fn check_len(&self) -> Result<()> {
+            let buffer = self.buffer.as_ref();
+            if buffer.is_empty() {
+                Err(Error::Truncated)
+            } else {
+                Ok(())
+            }
+        }
+
+        /// Consumes the frame, returning the underlying buffer.
+        pub fn into_inner(self) -> T {
+            self.buffer
+        }
+
+        get_field!(dispatch_field, 0b11111, 3);
+        get_field!(checksum_field, 0b1, 2);
+        get_field!(ports_field, 0b11, 0);
+
+        /// Returns the index of the start of the next header compressed fields.
+        fn nhc_fields_start(&self) -> usize {
+            1
+        }
+
+        /// Return the source port number.
+        pub fn src_port(&self) -> u16 {
+            match self.ports_field() {
+                0b00 | 0b01 => {
+                    // The full 16 bits are carried in-line.
+                    let data = self.buffer.as_ref();
+                    let start = self.nhc_fields_start();
+
+                    NetworkEndian::read_u16(&data[start..start + 2])
+                }
+                0b10 => {
+                    // The first 8 bits are elided.
+                    let data = self.buffer.as_ref();
+                    let start = self.nhc_fields_start();
+
+                    0xf000 + data[start] as u16
+                }
+                0b11 => {
+                    // The first 12 bits are elided.
+                    let data = self.buffer.as_ref();
+                    let start = self.nhc_fields_start();
+
+                    0xf0b0 + (data[start] >> 4) as u16
+                }
+                _ => unreachable!(),
+            }
+        }
+
+        /// Return the destination port number.
+        pub fn dst_port(&self) -> u16 {
+            match self.ports_field() {
+                0b00 => {
+                    // The full 16 bits are carried in-line.
+                    let data = self.buffer.as_ref();
+                    let idx = self.nhc_fields_start();
+
+                    NetworkEndian::read_u16(&data[idx + 2..idx + 4])
+                }
+                0b01 => {
+                    // The first 8 bits are elided.
+                    let data = self.buffer.as_ref();
+                    let idx = self.nhc_fields_start();
+
+                    0xf000 + data[idx] as u16
+                }
+                0b10 => {
+                    // The full 16 bits are carried in-line.
+                    let data = self.buffer.as_ref();
+                    let idx = self.nhc_fields_start();
+
+                    NetworkEndian::read_u16(&data[idx + 1..idx + 1 + 2])
+                }
+                0b11 => {
+                    // The first 12 bits are elided.
+                    let data = self.buffer.as_ref();
+                    let start = self.nhc_fields_start();
+
+                    0xf0b0 + (NetworkEndian::read_u16(&data[start..start + 1]) & 0xff)
+                }
+                _ => unreachable!(),
+            }
+        }
+
+        /// Return the checksum.
+        pub fn checksum(&self) -> Option<u16> {
+            if self.checksum_field() == 0b0 {
+                // The first 12 bits are elided.
+                let data = self.buffer.as_ref();
+                let start = self.nhc_fields_start() + self.ports_size();
+                Some(NetworkEndian::read_u16(&data[start..start + 2]))
+            } else {
+                // The checksum is ellided and needs to be recomputed on the 6LoWPAN termination point.
+                None
+            }
+        }
+
+        // Return the size of the checksum field.
+        fn checksum_size(&self) -> usize {
+            match self.checksum_field() {
+                0b0 => 2,
+                0b1 => 0,
+                _ => unreachable!(),
+            }
+        }
+
+        /// Returns the total size of both port numbers.
+        fn ports_size(&self) -> usize {
+            match self.ports_field() {
+                0b00 => 4, // 16 bits + 16 bits
+                0b01 => 3, // 16 bits + 8 bits
+                0b10 => 3, // 8 bits + 16 bits
+                0b11 => 1, // 4 bits + 4 bits
+                _ => unreachable!(),
+            }
+        }
+    }
+
+    impl<'a, T: AsRef<[u8]> + ?Sized> UdpPacket<&'a T> {
+        /// Return a pointer to the payload.
+        pub fn payload(&self) -> &'a [u8] {
+            let start = 1 + self.ports_size() + self.checksum_size();
+            &self.buffer.as_ref()[start..]
+        }
+    }
+
+    impl<'a, T: AsRef<[u8]> + AsMut<[u8]>> UdpPacket<T> {
+        /// Return a mutable pointer to the payload.
+        pub fn payload_mut(&mut self) -> &mut [u8] {
+            let start = 1 + self.ports_size() + 2; // XXX(thvdveld): we assume we put the checksum inlined.
+            &mut self.buffer.as_mut()[start..]
+        }
+
+        /// Set the dispatch field to `0b11110`.
+        fn set_dispatch_field(&mut self) {
+            let data = self.buffer.as_mut();
+            data[0] = (data[0] & !(0b11111 << 3)) | (UDP_DISPATCH << 3);
+        }
+
+        set_field!(set_checksum_field, 0b1, 2);
+        set_field!(set_ports_field, 0b11, 0);
+
+        fn set_ports(&mut self, src_port: u16, dst_port: u16) {
+            let mut idx = 1;
+
+            match (src_port, dst_port) {
+                (0xf0b0..=0xf0bf, 0xf0b0..=0xf0bf) => {
+                    // We can compress both the source and destination ports.
+                    self.set_ports_field(0b11);
+                    let data = self.buffer.as_mut();
+                    data[idx] = (((src_port - 0xf0b0) as u8) << 4) & ((dst_port - 0xf0b0) as u8);
+                }
+                (0xf000..=0xf0ff, _) => {
+                    // We can compress the source port, but not the destination port.
+                    self.set_ports_field(0b10);
+                    let data = self.buffer.as_mut();
+                    data[idx] = (src_port - 0xf000) as u8;
+                    idx += 1;
+
+                    NetworkEndian::write_u16(&mut data[idx..idx + 2], dst_port);
+                }
+                (_, 0xf000..=0xf0ff) => {
+                    // We can compress the destination port, but not the source port.
+                    self.set_ports_field(0b01);
+                    let data = self.buffer.as_mut();
+                    NetworkEndian::write_u16(&mut data[idx..idx + 2], src_port);
+                    idx += 2;
+                    data[idx] = (dst_port - 0xf000) as u8;
+                }
+                (_, _) => {
+                    // We cannot compress any port.
+                    self.set_ports_field(0b00);
+                    let data = self.buffer.as_mut();
+                    NetworkEndian::write_u16(&mut data[idx..idx + 2], src_port);
+                    idx += 2;
+                    NetworkEndian::write_u16(&mut data[idx..idx + 2], dst_port);
+                }
+            };
+        }
+
+        fn set_checksum(&mut self, checksum: u16) {
+            self.set_checksum_field(0b0);
+            let idx = 1 + self.ports_size();
+            let data = self.buffer.as_mut();
+            NetworkEndian::write_u16(&mut data[idx..idx + 2], checksum);
+        }
+    }
+
+    /// A high-level representation of a LOWPAN_NHC UDP header.
+    #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+    pub struct UdpNhcRepr(pub UdpRepr);
+
+    impl<'a> UdpNhcRepr {
+        /// Parse a LOWWPAN_NHC UDP packet and return a high-level representation.
+        pub fn parse<T: AsRef<[u8]> + ?Sized>(
+            packet: &UdpPacket<&'a T>,
+            src_addr: &ipv6::Address,
+            dst_addr: &ipv6::Address,
+            _checksum: Option<u16>,
+        ) -> Result<UdpNhcRepr> {
+            // Ensure basic accessors will work.
+            packet.check_len()?;
+
+            if packet.dispatch_field() != UDP_DISPATCH {
+                return Err(Error::Malformed);
+            }
+
+            let payload_len = packet.payload().len();
+            let chk_sum = !checksum::combine(&[
+                checksum::pseudo_header(
+                    &IpAddress::Ipv6(*src_addr),
+                    &IpAddress::Ipv6(*dst_addr),
+                    crate::wire::ip::Protocol::Udp,
+                    payload_len as u32 + 8,
+                ),
+                packet.src_port(),
+                packet.dst_port(),
+                payload_len as u16 + 8,
+                checksum::data(packet.payload()),
+            ]);
+
+            // TODO(thvdveld): remove the unwrap
+            if chk_sum != packet.checksum().unwrap() {
+                return Err(Error::Checksum);
+            }
+
+            Ok(UdpNhcRepr(UdpRepr {
+                src_port: packet.src_port(),
+                dst_port: packet.dst_port(),
+            }))
+        }
+
+        /// Return the length of a packet that will be emitted from this high-level representation.
+        pub fn header_len(&self) -> usize {
+            let mut len = 1; // The minimal header size
+
+            len += 2; // XXX We assume we will add the checksum at the end
+
+            // Check if we can compress the source and destination ports
+            match (self.src_port, self.dst_port) {
+                (0xf0b0..=0xf0bf, 0xf0b0..=0xf0bf) => len + 1,
+                (0xf000..=0xf0ff, _) | (_, 0xf000..=0xf0ff) => len + 3,
+                (_, _) => len + 4,
+            }
+        }
+
+        /// Emit a high-level representation into a LOWPAN_NHC UDP header.
+        pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(
+            &self,
+            packet: &mut UdpPacket<T>,
+            src_addr: &Address,
+            dst_addr: &Address,
+            payload_len: usize,
+            emit_payload: impl FnOnce(&mut [u8]),
+        ) {
+            packet.set_dispatch_field();
+            packet.set_ports(self.src_port, self.dst_port);
+            emit_payload(packet.payload_mut());
+
+            let chk_sum = !checksum::combine(&[
+                checksum::pseudo_header(
+                    &IpAddress::Ipv6(*src_addr),
+                    &IpAddress::Ipv6(*dst_addr),
+                    crate::wire::ip::Protocol::Udp,
+                    payload_len as u32 + 8,
+                ),
+                self.src_port,
+                self.dst_port,
+                payload_len as u16 + 8,
+                checksum::data(packet.payload_mut()),
+            ]);
+
+            packet.set_checksum(chk_sum);
+        }
+    }
+
+    impl core::ops::Deref for UdpNhcRepr {
+        type Target = UdpRepr;
+
+        fn deref(&self) -> &Self::Target {
+            &self.0
+        }
+    }
+
+    impl core::ops::DerefMut for UdpNhcRepr {
+        fn deref_mut(&mut self) -> &mut Self::Target {
+            &mut self.0
+        }
+    }
+
+    #[cfg(test)]
+    mod test {
+        use super::*;
+
+        #[test]
+        fn ext_header_nhc_fields() {
+            let bytes = [0xe3, 0x06, 0x03, 0x00, 0xff, 0x00, 0x00, 0x00];
+
+            let packet = ExtensionHeaderPacket::new_checked(&bytes[..]).unwrap();
+            assert_eq!(packet.dispatch_field(), EXT_HEADER_DISPATCH);
+            assert_eq!(packet.length_field(), 6);
+            assert_eq!(
+                packet.extension_header_id(),
+                ExtensionHeaderId::RoutingHeader
+            );
+
+            assert_eq!(packet.payload(), [0x03, 0x00, 0xff, 0x00, 0x00, 0x00]);
+        }
+
+        #[test]
+        fn ext_header_emit() {
+            let ext_header = ExtensionHeaderRepr {
+                ext_header_id: ExtensionHeaderId::RoutingHeader,
+                next_header: NextHeader::Compressed,
+                length: 6,
+            };
+
+            let len = ext_header.buffer_len();
+            let mut buffer = [0u8; 127];
+            let mut packet = ExtensionHeaderPacket::new_unchecked(&mut buffer[..len]);
+            ext_header.emit(&mut packet);
+
+            assert_eq!(packet.dispatch_field(), EXT_HEADER_DISPATCH);
+            assert_eq!(packet.next_header(), NextHeader::Compressed);
+            assert_eq!(packet.length_field(), 6);
+            assert_eq!(
+                packet.extension_header_id(),
+                ExtensionHeaderId::RoutingHeader
+            );
+        }
+
+        #[test]
+        fn udp_nhc_fields() {
+            let bytes = [0xf0, 0x16, 0x2e, 0x22, 0x3d, 0x28, 0xc4];
+
+            let packet = UdpPacket::new_checked(&bytes[..]).unwrap();
+            assert_eq!(packet.dispatch_field(), UDP_DISPATCH);
+            assert_eq!(packet.checksum(), Some(0x28c4));
+            assert_eq!(packet.src_port(), 5678);
+            assert_eq!(packet.dst_port(), 8765);
+        }
+
+        #[test]
+        fn udp_emit() {
+            let udp = UdpNhcRepr(UdpRepr {
+                src_port: 0xf0b1,
+                dst_port: 0xf001,
+            });
+
+            let payload = b"Hello World!";
+
+            let src_addr = ipv6::Address::default();
+            let dst_addr = ipv6::Address::default();
+
+            let len = udp.header_len() + payload.len();
+            let mut buffer = [0u8; 127];
+            let mut packet = UdpPacket::new_unchecked(&mut buffer[..len]);
+            udp.emit(&mut packet, &src_addr, &dst_addr, payload.len(), |buf| {
+                buf.copy_from_slice(&payload[..])
+            });
+
+            assert_eq!(packet.dispatch_field(), UDP_DISPATCH);
+            assert_eq!(packet.src_port(), 0xf0b1);
+            assert_eq!(packet.dst_port(), 0xf001);
+            assert_eq!(packet.payload_mut(), b"Hello World!");
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    //use super::*;
+
+    //#[test]
+    //fn ieee802154_udp() {
+    //use crate::wire::ieee802154::Frame as Ieee802154Frame;
+    //use crate::wire::ieee802154::Repr as Ieee802154Repr;
+    //use crate::wire::ipv6routing;
+
+    //// This data is captured using Wireshark from the communication between a RPL 6LoWPAN server
+    //// and a RPL 6LoWPAN client.
+    //// The frame is thus an IEEE802.15.4 frame, containing a 6LoWPAN packet,
+    //// containing a RPL extension header and an UDP header.
+    //let bytes: &[u8] = &[
+    //0x61, 0xdc, 0xdd, 0xcd, 0xab, 0xc7, 0xd9, 0xb5, 0x14, 0x00, 0x4b, 0x12, 0x00, 0xbf,
+    //0x9b, 0x15, 0x06, 0x00, 0x4b, 0x12, 0x00, 0x7e, 0xf7, 0x00, 0xe3, 0x06, 0x03, 0x00,
+    //0xff, 0x00, 0x00, 0x00, 0xf0, 0x16, 0x2e, 0x22, 0x3d, 0x28, 0xc4, 0x68, 0x65, 0x6c,
+    //0x6c, 0x6f, 0x20, 0x36, 0x35, 0x18, 0xb9,
+    //];
+
+    //let ieee802154_frame = Ieee802154Frame::new_checked(bytes).unwrap();
+    //let ieee802154_repr = Ieee802154Repr::parse(&ieee802154_frame).unwrap();
+
+    //let iphc_frame = iphc::Packet::new_checked(ieee802154_frame.payload().unwrap()).unwrap();
+    //let iphc_repr = iphc::Repr::parse(
+    //&iphc_frame,
+    //ieee802154_repr.src_addr,
+    //ieee802154_repr.dst_addr,
+    //)
+    //.unwrap();
+
+    //// The next header is compressed.
+    //assert_eq!(iphc_repr.next_header, NextHeader::Compressed);
+
+    //// We dispatch the NHC packet.
+    //let nhc_packet = nhc::Packet::dispatch(iphc_frame.payload()).unwrap();
+
+    //let udp_payload = match nhc_packet {
+    //nhc::Packet::ExtensionHeader(ext_packet) => {
+    //// The next header is compressed (it is the UDP NHC compressed header).
+    //assert_eq!(ext_packet.next_header(), NextHeader::Compressed);
+    //assert_eq!(ext_packet.length_field(), 6);
+    //let payload = ext_packet.payload();
+
+    //let length = ext_packet.length_field() as usize;
+    //let ext_packet_payload = &payload[..length];
+
+    //match ext_packet.extension_header_id() {
+    //nhc::ExtensionHeaderId::RoutingHeader => {
+    //// We are not intersted in the Next Header protocol.
+    //let proto = ipv6::Protocol::Unknown(0);
+    //let mut new_payload = [0; 8];
+
+    //new_payload[0] = proto.into();
+    //new_payload[1] = (2 + length - 8) as u8;
+    //new_payload[2..].copy_from_slice(ext_packet_payload);
+
+    //let routing = ipv6routing::Header::new_checked(new_payload).unwrap();
+
+    //assert_eq!(routing.routing_type(), ipv6routing::Type::Rpl);
+    //assert_eq!(routing.segments_left(), 0);
+    //assert_eq!(routing.cmpr_e(), 0xf);
+    //assert_eq!(routing.cmpr_i(), 0xf);
+    //}
+    //_ => unreachable!(),
+    //}
+
+    //&payload[length..]
+    //}
+    //_ => unreachable!(),
+    //};
+
+    //let udp_nhc_frame = nhc::UdpPacket::new_checked(udp_payload).unwrap();
+    //let udp_repr = nhc::UdpNhcRepr::parse(
+    //&udp_nhc_frame,
+    //&iphc_repr.src_addr,
+    //&iphc_repr.dst_addr,
+    //None,
+    //)
+    //.unwrap();
+
+    //assert_eq!(udp_repr.src_port, 5678);
+    //assert_eq!(udp_repr.dst_port, 8765);
+    //assert_eq!(udp_nhc_frame.checksum(), Some(0x28c4));
+    //}
+}

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно