Add support for 802.15.4 and 6LoWPAN

master
Thibaut Vandervelden 2021-04-29 12:04:46 +02:00 committed by Dario Nieuwenhuis
parent d66944c4ac
commit fb2d0029d8
38 changed files with 3952 additions and 339 deletions

View File

@ -37,18 +37,24 @@ verbose = []
rand-custom-impl = []
"medium-ethernet" = ["socket"]
"medium-ip" = ["socket"]
"phy-raw_socket" = ["std", "libc", "medium-ethernet"]
"medium-ieee802154" = ["socket"]
"phy-raw_socket" = ["std", "libc"]
"phy-tuntap_interface" = ["std", "libc", "medium-ethernet"]
"proto-ipv4" = []
"proto-igmp" = ["proto-ipv4"]
"proto-dhcpv4" = ["proto-ipv4"]
"proto-ipv6" = []
"proto-sixlowpan" = ["proto-ipv6"]
"socket" = []
"socket-raw" = ["socket"]
"socket-udp" = ["socket"]
"socket-tcp" = ["socket"]
"socket-icmp" = ["socket"]
"socket-dhcpv4" = ["socket", "medium-ethernet", "proto-dhcpv4"]
"async" = []
defmt-trace = []
@ -59,9 +65,9 @@ defmt-error = []
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",
"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",
"async"
]
@ -110,5 +116,9 @@ required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interfac
name = "dhcp_client"
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]
debug = 2

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

115
examples/sixlowpan.rs Normal file
View File

@ -0,0 +1,115 @@
/*
# Setup
modprobe mac802154_hwsim
ip link set wpan0 down
iwpan dev wpan0 set pan_id 0xbeef
ip link add link wpan0 name lowpan0 type lowpan
ip link set wpan0 up
ip link set lowpan0 up
iwpan dev wpan1 del
iwpan phy phy1 interface add monitor%d type monitor
# Running
sudo ./target/debug/examples/sixlowpan
# 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::{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("monitor0", 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);
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");
}
}

View File

@ -7,7 +7,7 @@ use std::os::unix::io::AsRawFd;
fn main() {
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 {
phy_wait(socket.as_raw_fd(), None).unwrap();
let (rx_token, _) = socket.receive().unwrap();

View File

@ -12,7 +12,6 @@ use std::process;
use std::str::{self, FromStr};
use std::time::{SystemTime, UNIX_EPOCH};
use smoltcp::phy::RawSocket;
#[cfg(feature = "phy-tuntap_interface")]
use smoltcp::phy::TunTapInterface;
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>) {
opts.optopt("", "pcap", "Write a packet capture file", "FILE");
opts.optopt(

File diff suppressed because it is too large Load Diff

View File

@ -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.
*/
#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))]
#[cfg(any(
feature = "medium-ethernet",
feature = "medium-ip",
feature = "medium-ieee802154"
))]
mod interface;
#[cfg(feature = "medium-ethernet")]
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
mod neighbor;
mod route;
#[cfg(feature = "medium-ethernet")]
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
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;
#[cfg(feature = "medium-ethernet")]
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
pub use self::neighbor::Neighbor;
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};

View File

@ -4,7 +4,7 @@
use managed::ManagedMap;
use crate::time::{Duration, Instant};
use crate::wire::{EthernetAddress, IpAddress};
use crate::wire::{HardwareAddress, IpAddress};
/// A cached neighbor.
///
@ -13,7 +13,7 @@ use crate::wire::{EthernetAddress, IpAddress};
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Neighbor {
hardware_addr: EthernetAddress,
hardware_addr: HardwareAddress,
expires_at: Instant,
}
@ -22,7 +22,7 @@ pub struct Neighbor {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) enum Answer {
/// 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.
NotFound,
/// The neighbor address is not in the cache, or has expired,
@ -115,7 +115,7 @@ impl<'a> Cache<'a> {
pub fn fill(
&mut self,
protocol_addr: IpAddress,
hardware_addr: EthernetAddress,
hardware_addr: HardwareAddress,
timestamp: Instant,
) {
debug_assert!(protocol_addr.is_unicast());
@ -197,7 +197,7 @@ impl<'a> Cache<'a> {
pub(crate) fn lookup(&self, protocol_addr: &IpAddress, timestamp: Instant) -> Answer {
if protocol_addr.is_broadcast() {
return Answer::Found(EthernetAddress::BROADCAST);
return Answer::Found(HardwareAddress::BROADCAST);
}
if let Some(&Neighbor {
@ -228,10 +228,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 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]
fn test_fill() {

View File

@ -97,8 +97,12 @@ compile_error!("at least one socket needs to be enabled"); */
#[cfg(any(feature = "std", feature = "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(
feature = "socket",
@ -113,9 +117,13 @@ compile_error!("If you enable the socket feature, you must enable at least one o
#[cfg(all(
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"))]
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.
/// E.g. a TCP packet addressed to a socket that doesn't exist.
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")]
@ -195,6 +207,7 @@ impl fmt::Display for Error {
Error::Fragmented => write!(f, "fragmented packet"),
Error::Malformed => write!(f, "malformed packet"),
Error::Dropped => write!(f, "dropped by socket"),
Error::NotSupported => write!(f, "not supported by smoltcp"),
}
}
}

View File

@ -254,6 +254,8 @@ impl DeviceCapabilities {
}
#[cfg(feature = "medium-ip")]
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.
#[cfg(feature = "medium-ip")]
Ip,
#[cfg(feature = "medium-ieee802154")]
Ieee802154,
}
impl Default for Medium {
fn default() -> Medium {
#[cfg(feature = "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;
#[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");
}
}

View File

@ -15,6 +15,8 @@ enum_with_unknown! {
Ethernet = 1,
/// IPv4 or IPv6 packets (depending on the version field)
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,
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => PcapLinkType::Ethernet,
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => PcapLinkType::Ieee802154WithFcs,
};
sink.global_header(link_type);
PcapWriter {

View File

@ -26,10 +26,19 @@ impl RawSocket {
///
/// This requires superuser privileges or a corresponding capability bit
/// set on the executable.
pub fn new(name: &str) -> io::Result<RawSocket> {
pub fn new(name: &str, medium: Medium) -> io::Result<RawSocket> {
let mut lower = sys::RawSocketDesc::new(name)?;
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 {
lower: Rc::new(RefCell::new(lower)),
mtu: mtu,

View File

@ -3,6 +3,7 @@
pub const SIOCGIFMTU: libc::c_ulong = 0x8921;
pub const SIOCGIFINDEX: libc::c_ulong = 0x8933;
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 IFF_TUN: libc::c_int = 0x0001;

View File

@ -18,10 +18,17 @@ impl AsRawFd for RawSocketDesc {
impl RawSocketDesc {
pub fn new(name: &str) -> io::Result<RawSocketDesc> {
let lower = unsafe {
// TODO(thvdveld)
//#[cfg(feature = "medium-ieee802154")]
//let protocol = imp::ETH_P_IEEE802154;
#[cfg(feature = "medium-ethernet")]
let protocol = imp::ETH_P_ALL;
let lower = libc::socket(
libc::AF_PACKET,
libc::SOCK_RAW | libc::SOCK_NONBLOCK,
imp::ETH_P_ALL.to_be() as i32,
protocol.to_be() as i32,
);
if lower == -1 {
return Err(io::Error::last_os_error());
@ -44,9 +51,16 @@ impl RawSocketDesc {
}
pub fn bind_interface(&mut self) -> io::Result<()> {
// TODO(thvdveld)
//#[cfg(feature = "medium-ieee802154")]
//let protocol = imp::ETH_P_IEEE802154;
#[cfg(feature = "medium-ethernet")]
let protocol = imp::ETH_P_ALL;
let sockaddr = libc::sockaddr_ll {
sll_family: libc::AF_PACKET as u16,
sll_protocol: imp::ETH_P_ALL.to_be() as u16,
sll_protocol: protocol.to_be() as u16,
sll_ifindex: ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFINDEX)?,
sll_hatype: 1,
sll_pkttype: 0,

View File

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

View File

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

View File

@ -2,6 +2,7 @@ use crate::socket::SocketHandle;
use crate::socket::{Context, SocketMeta};
use crate::time::{Duration, Instant};
use crate::wire::dhcpv4::field as dhcpv4_field;
use crate::wire::HardwareAddress;
use crate::wire::{
DhcpMessageType, DhcpPacket, DhcpRepr, IpAddress, IpProtocol, Ipv4Address, Ipv4Cidr, Ipv4Repr,
UdpRepr, DHCP_CLIENT_PORT, DHCP_MAX_DNS_SERVER_COUNT, DHCP_SERVER_PORT, UDP_HEADER_LEN,
@ -218,7 +219,13 @@ impl Dhcpv4Socket {
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(());
}
if dhcp_repr.transaction_id != self.transaction_id {
@ -381,7 +388,11 @@ impl Dhcpv4Socket {
{
// note: Dhcpv4Socket is only usable in ethernet mediums, so the
// 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.
// 0x0f * 4 = 60 bytes.

View File

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

View File

@ -187,8 +187,15 @@ from_socket!(Dhcpv4Socket, Dhcpv4);
#[derive(Clone, Debug)]
pub(crate) struct Context {
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 src_pan_id: Option<crate::wire::Ieee802154Pan>,
#[cfg(feature = "medium-ieee802154")]
pub dst_pan_id: Option<crate::wire::Ieee802154Pan>,
pub caps: DeviceCapabilities,
}
@ -215,10 +222,18 @@ impl Context {
#[cfg(not(feature = "medium-ethernet"))]
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),
#[cfg(feature = "medium-ieee802154")]
src_pan_id: Some(crate::wire::Ieee802154Pan(0xabcd)),
#[cfg(feature = "medium-ieee802154")]
dst_pan_id: Some(crate::wire::Ieee802154Pan(0xabcd)),
};
}

View File

@ -69,9 +69,7 @@ impl<'a> Set<'a> {
}
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"))]
ManagedSlice::Owned(ref mut sockets) => {
sockets.push(None);

View File

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

View File

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

924
src/wire/ieee802154.rs Normal file
View File

@ -0,0 +1,924 @@
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 {
/// 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;
}
todo!();
}
/// Return the IE field (the header as well as the payload IE)
#[inline]
pub fn ie_field(&self) -> Option<&[u8]> {
match self.frame_type() {
FrameType::Data | FrameType::MacCommand | FrameType::Multipurpose => (),
FrameType::Beacon | FrameType::Acknowledgement
if self.frame_version() == FrameVersion::Ieee802154 => {}
FrameType::Beacon
| FrameType::Acknowledgement
| FrameType::Extended
| FrameType::FragmentOrFrak
| FrameType::Unknown(_) => return None,
}
if !self.ie_present() {
return None;
}
todo!();
}
/// Return the FCS fields
#[inline]
pub fn fcs(&self) -> &[u8] {
&self.buffer.as_ref()[self.buffer.as_ref().len() - 2..]
}
}
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..data.len() - 2]) // Remove the FCS field of the IEEE80.15.4 frame.
}
_ => 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 => todo!("{}", self.dst_addressing_mode()),
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 => todo!("{}", self.dst_addressing_mode()),
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?} fcs={:x?}",
self.frame_type(),
self.sequence_number(),
self.dst_pan_id(),
self.dst_addr(),
self.src_pan_id(),
self.src_addr(),
self.fcs(),
)
}
}
/// 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
0xb3, 0x0d // FSM
];
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]),
fcs -> [0xb3, 0x0d],
}
}

View File

@ -337,23 +337,19 @@ impl<'a> fmt::Display for Repr {
max_resp_time,
group_addr,
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 {
group_addr,
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 } => {
write!(f, "IGMP leave group group_addr={})", group_addr)
}

View File

@ -6,6 +6,8 @@ use crate::phy::ChecksumCapabilities;
use crate::wire::{Ipv4Address, Ipv4Cidr, Ipv4Packet, Ipv4Repr};
#[cfg(feature = "proto-ipv6")]
use crate::wire::{Ipv6Address, Ipv6Cidr, Ipv6Packet, Ipv6Repr};
#[cfg(feature = "proto-sixlowpan")]
use crate::wire::{SixlowpanIphcPacket, SixlowpanIphcRepr};
use crate::{Error, Result};
/// Internet protocol version.
@ -513,6 +515,8 @@ pub enum Repr {
Ipv4(Ipv4Repr),
#[cfg(feature = "proto-ipv6")]
Ipv6(Ipv6Repr),
#[cfg(feature = "proto-sixlowpan")]
Sixlowpan(SixlowpanIphcRepr),
}
#[cfg(feature = "proto-ipv4")]
@ -538,6 +542,8 @@ impl Repr {
Repr::Ipv4(_) => Version::Ipv4,
#[cfg(feature = "proto-ipv6")]
Repr::Ipv6(_) => Version::Ipv6,
#[cfg(feature = "proto-sixlowpan")]
Repr::Sixlowpan(_) => Version::Ipv6,
}
}
@ -549,6 +555,8 @@ impl Repr {
Repr::Ipv4(repr) => Address::Ipv4(repr.src_addr),
#[cfg(feature = "proto-ipv6")]
Repr::Ipv6(repr) => Address::Ipv6(repr.src_addr),
#[cfg(feature = "proto-sixlowpan")]
Repr::Sixlowpan(repr) => Address::Ipv6(repr.src_addr),
}
}
@ -560,6 +568,8 @@ impl Repr {
Repr::Ipv4(repr) => Address::Ipv4(repr.dst_addr),
#[cfg(feature = "proto-ipv6")]
Repr::Ipv6(repr) => Address::Ipv6(repr.dst_addr),
#[cfg(feature = "proto-sixlowpan")]
Repr::Sixlowpan(repr) => Address::Ipv6(repr.dst_addr),
}
}
@ -571,6 +581,8 @@ impl Repr {
Repr::Ipv4(repr) => repr.protocol,
#[cfg(feature = "proto-ipv6")]
Repr::Ipv6(repr) => repr.next_header,
#[cfg(feature = "proto-sixlowpan")]
Repr::Sixlowpan(repr) => todo!("{:?}", repr),
}
}
@ -582,6 +594,8 @@ impl Repr {
Repr::Ipv4(repr) => repr.payload_len,
#[cfg(feature = "proto-ipv6")]
Repr::Ipv6(repr) => repr.payload_len,
#[cfg(feature = "proto-sixlowpan")]
Repr::Sixlowpan(repr) => todo!("{:?}", repr),
}
}
@ -602,6 +616,8 @@ impl Repr {
ref mut payload_len,
..
}) => *payload_len = length,
#[cfg(feature = "proto-sixlowpan")]
Repr::Sixlowpan(_) => todo!(),
}
}
@ -613,6 +629,8 @@ impl Repr {
Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit,
#[cfg(feature = "proto-ipv6")]
Repr::Ipv6(Ipv6Repr { hop_limit, .. }) => hop_limit,
#[cfg(feature = "proto-sixlowpan")]
Repr::Sixlowpan(SixlowpanIphcRepr { hop_limit, .. }) => hop_limit,
}
}
@ -753,6 +771,9 @@ impl Repr {
resolve_unspecified!(Repr::Ipv6, Address::Ipv6, repr, fallback_src_addrs)
}
#[cfg(feature = "proto-sixlowpan")]
&Repr::Sixlowpan(_) => todo!(), // TODO(thvdveld): what do we need to do here?
&Repr::Unspecified { .. } => {
panic!("source and destination IP address families do not match")
}
@ -770,6 +791,8 @@ impl Repr {
Repr::Ipv4(repr) => repr.buffer_len(),
#[cfg(feature = "proto-ipv6")]
Repr::Ipv6(repr) => repr.buffer_len(),
#[cfg(feature = "proto-sixlowpan")]
Repr::Sixlowpan(repr) => repr.buffer_len(),
}
}
@ -788,6 +811,8 @@ impl Repr {
Repr::Ipv4(repr) => repr.emit(&mut Ipv4Packet::new_unchecked(buffer), _checksum_caps),
#[cfg(feature = "proto-ipv6")]
Repr::Ipv6(repr) => repr.emit(&mut Ipv6Packet::new_unchecked(buffer)),
#[cfg(feature = "proto-sixlowpan")]
Repr::Sixlowpan(repr) => repr.emit(&mut SixlowpanIphcPacket::new_unchecked(buffer)),
}
}

View File

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

View File

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

View File

@ -89,6 +89,8 @@ mod icmp;
mod icmpv4;
#[cfg(feature = "proto-ipv6")]
mod icmpv6;
#[cfg(feature = "medium-ieee802154")]
pub mod ieee802154;
#[cfg(feature = "proto-igmp")]
mod igmp;
pub(crate) mod ip;
@ -106,10 +108,18 @@ mod ipv6option;
mod ipv6routing;
#[cfg(feature = "proto-ipv6")]
mod mld;
#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
#[cfg(all(
feature = "proto-ipv6",
any(feature = "medium-ethernet", feature = "medium-ieee802154")
))]
mod ndisc;
#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
#[cfg(all(
feature = "proto-ipv6",
any(feature = "medium-ethernet", feature = "medium-ieee802154")
))]
mod ndiscoption;
#[cfg(all(feature = "proto-sixlowpan", feature = "medium-ieee802154"))]
mod sixlowpan;
mod tcp;
mod udp;
@ -126,6 +136,24 @@ pub use self::arp::{
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::{
Address as IpAddress, Cidr as IpCidr, Endpoint as IpEndpoint, Protocol as IpProtocol,
Repr as IpRepr, Version as IpVersion,
@ -177,12 +205,18 @@ pub use self::icmpv6::{
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
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::{
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::{
NdiscOption, PrefixInfoFlags as NdiscPrefixInfoFlags,
PrefixInformation as NdiscPrefixInformation, RedirectedHeader as NdiscRedirectedHeader,
@ -205,3 +239,74 @@ pub use self::dhcpv4::{
CLIENT_PORT as DHCP_CLIENT_PORT, MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT,
SERVER_PORT as DHCP_SERVER_PORT,
};
/// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum HardwareAddress {
BROADCAST,
#[cfg(feature = "medium-ethernet")]
Ethernet(EthernetAddress),
#[cfg(feature = "medium-ieee802154")]
Ieee802154(Ieee802154Address),
}
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(),
_ => todo!(),
}
}
/// 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(),
_ => todo!(),
}
}
/// 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(),
_ => todo!(),
}
}
}
impl core::fmt::Display for HardwareAddress {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
HardwareAddress::BROADCAST => write!(f, "BROADCAST"),
#[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)
}
}

View File

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

View File

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

1704
src/wire/sixlowpan.rs Normal file

File diff suppressed because it is too large Load Diff