313 lines
11 KiB
Rust
313 lines
11 KiB
Rust
/*! Low-level packet access and construction.
|
|
|
|
The `wire` module deals with the packet *representation*. It provides two levels
|
|
of functionality.
|
|
|
|
* First, it provides functions to extract fields from sequences of octets,
|
|
and to insert fields into sequences of octets. This happens `Packet` family of
|
|
structures, e.g. [EthernetFrame] or [Ipv4Packet].
|
|
* Second, in cases where the space of valid field values is much smaller than the space
|
|
of possible field values, it provides a compact, high-level representation
|
|
of packet data that can be parsed from and emitted into a sequence of octets.
|
|
This happens through the `Repr` family of structs and enums, e.g. [ArpRepr] or [Ipv4Repr].
|
|
|
|
[EthernetFrame]: struct.EthernetFrame.html
|
|
[Ipv4Packet]: struct.Ipv4Packet.html
|
|
[ArpRepr]: enum.ArpRepr.html
|
|
[Ipv4Repr]: struct.Ipv4Repr.html
|
|
|
|
The functions in the `wire` module are designed for use together with `-Cpanic=abort`.
|
|
|
|
The `Packet` family of data structures guarantees that, if the `Packet::check_len()` method
|
|
returned `Ok(())`, then no accessor or setter method will panic; however, the guarantee
|
|
provided by `Packet::check_len()` may no longer hold after changing certain fields,
|
|
which are listed in the documentation for the specific packet.
|
|
|
|
The `Packet::new_checked` method is a shorthand for a combination of `Packet::new_unchecked`
|
|
and `Packet::check_len`.
|
|
When parsing untrusted input, it is *necessary* to use `Packet::new_checked()`;
|
|
so long as the buffer is not modified, no accessor will fail.
|
|
When emitting output, though, it is *incorrect* to use `Packet::new_checked()`;
|
|
the length check is likely to succeed on a zeroed buffer, but fail on a buffer
|
|
filled with data from a previous packet, such as when reusing buffers, resulting
|
|
in nondeterministic panics with some network devices but not others.
|
|
The buffer length for emission is not calculated by the `Packet` layer.
|
|
|
|
In the `Repr` family of data structures, the `Repr::parse()` method never panics
|
|
as long as `Packet::new_checked()` (or `Packet::check_len()`) has succeeded, and
|
|
the `Repr::emit()` method never panics as long as the underlying buffer is exactly
|
|
`Repr::buffer_len()` octets long.
|
|
|
|
# Examples
|
|
|
|
To emit an IP packet header into an octet buffer, and then parse it back:
|
|
|
|
```rust
|
|
# #[cfg(feature = "proto-ipv4")]
|
|
# {
|
|
use smoltcp::phy::ChecksumCapabilities;
|
|
use smoltcp::wire::*;
|
|
let repr = Ipv4Repr {
|
|
src_addr: Ipv4Address::new(10, 0, 0, 1),
|
|
dst_addr: Ipv4Address::new(10, 0, 0, 2),
|
|
protocol: IpProtocol::Tcp,
|
|
payload_len: 10,
|
|
hop_limit: 64
|
|
};
|
|
let mut buffer = vec![0; repr.buffer_len() + repr.payload_len];
|
|
{ // emission
|
|
let mut packet = Ipv4Packet::new_unchecked(&mut buffer);
|
|
repr.emit(&mut packet, &ChecksumCapabilities::default());
|
|
}
|
|
{ // parsing
|
|
let packet = Ipv4Packet::new_checked(&buffer)
|
|
.expect("truncated packet");
|
|
let parsed = Ipv4Repr::parse(&packet, &ChecksumCapabilities::default())
|
|
.expect("malformed packet");
|
|
assert_eq!(repr, parsed);
|
|
}
|
|
# }
|
|
```
|
|
*/
|
|
|
|
mod field {
|
|
pub type Field = ::core::ops::Range<usize>;
|
|
pub type Rest = ::core::ops::RangeFrom<usize>;
|
|
}
|
|
|
|
pub mod pretty_print;
|
|
|
|
#[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
|
|
mod arp;
|
|
#[cfg(feature = "proto-dhcpv4")]
|
|
pub(crate) mod dhcpv4;
|
|
#[cfg(feature = "medium-ethernet")]
|
|
mod ethernet;
|
|
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
|
|
mod icmp;
|
|
#[cfg(feature = "proto-ipv4")]
|
|
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;
|
|
#[cfg(feature = "proto-ipv4")]
|
|
mod ipv4;
|
|
#[cfg(feature = "proto-ipv6")]
|
|
mod ipv6;
|
|
#[cfg(feature = "proto-ipv6")]
|
|
mod ipv6fragment;
|
|
#[cfg(feature = "proto-ipv6")]
|
|
mod ipv6hopbyhop;
|
|
#[cfg(feature = "proto-ipv6")]
|
|
mod ipv6option;
|
|
#[cfg(feature = "proto-ipv6")]
|
|
mod ipv6routing;
|
|
#[cfg(feature = "proto-ipv6")]
|
|
mod mld;
|
|
#[cfg(all(
|
|
feature = "proto-ipv6",
|
|
any(feature = "medium-ethernet", feature = "medium-ieee802154")
|
|
))]
|
|
mod ndisc;
|
|
#[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;
|
|
|
|
pub use self::pretty_print::PrettyPrinter;
|
|
|
|
#[cfg(feature = "medium-ethernet")]
|
|
pub use self::ethernet::{
|
|
Address as EthernetAddress, EtherType as EthernetProtocol, Frame as EthernetFrame,
|
|
Repr as EthernetRepr, HEADER_LEN as ETHERNET_HEADER_LEN,
|
|
};
|
|
|
|
#[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
|
|
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,
|
|
};
|
|
|
|
#[cfg(feature = "proto-ipv4")]
|
|
pub use self::ipv4::{
|
|
Address as Ipv4Address, Cidr as Ipv4Cidr, Packet as Ipv4Packet, Repr as Ipv4Repr,
|
|
HEADER_LEN as IPV4_HEADER_LEN, MIN_MTU as IPV4_MIN_MTU,
|
|
};
|
|
|
|
#[cfg(feature = "proto-ipv6")]
|
|
pub use self::ipv6::{
|
|
Address as Ipv6Address, Cidr as Ipv6Cidr, Packet as Ipv6Packet, Repr as Ipv6Repr,
|
|
HEADER_LEN as IPV6_HEADER_LEN, MIN_MTU as IPV6_MIN_MTU,
|
|
};
|
|
|
|
#[cfg(feature = "proto-ipv6")]
|
|
pub use self::ipv6option::{
|
|
FailureType as Ipv6OptionFailureType, Ipv6Option, Repr as Ipv6OptionRepr,
|
|
Type as Ipv6OptionType,
|
|
};
|
|
|
|
#[cfg(feature = "proto-ipv6")]
|
|
pub use self::ipv6hopbyhop::{Header as Ipv6HopByHopHeader, Repr as Ipv6HopByHopRepr};
|
|
|
|
#[cfg(feature = "proto-ipv6")]
|
|
pub use self::ipv6fragment::{Header as Ipv6FragmentHeader, Repr as Ipv6FragmentRepr};
|
|
|
|
#[cfg(feature = "proto-ipv6")]
|
|
pub use self::ipv6routing::{Header as Ipv6RoutingHeader, Repr as Ipv6RoutingRepr};
|
|
|
|
#[cfg(feature = "proto-ipv4")]
|
|
pub use self::icmpv4::{
|
|
DstUnreachable as Icmpv4DstUnreachable, Message as Icmpv4Message, Packet as Icmpv4Packet,
|
|
ParamProblem as Icmpv4ParamProblem, Redirect as Icmpv4Redirect, Repr as Icmpv4Repr,
|
|
TimeExceeded as Icmpv4TimeExceeded,
|
|
};
|
|
|
|
#[cfg(feature = "proto-igmp")]
|
|
pub use self::igmp::{IgmpVersion, Packet as IgmpPacket, Repr as IgmpRepr};
|
|
|
|
#[cfg(feature = "proto-ipv6")]
|
|
pub use self::icmpv6::{
|
|
DstUnreachable as Icmpv6DstUnreachable, Message as Icmpv6Message, Packet as Icmpv6Packet,
|
|
ParamProblem as Icmpv6ParamProblem, Repr as Icmpv6Repr, TimeExceeded as Icmpv6TimeExceeded,
|
|
};
|
|
|
|
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
|
|
pub use self::icmp::Repr as IcmpRepr;
|
|
|
|
#[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",
|
|
any(feature = "medium-ethernet", feature = "medium-ieee802154")
|
|
))]
|
|
pub use self::ndiscoption::{
|
|
NdiscOption, PrefixInfoFlags as NdiscPrefixInfoFlags,
|
|
PrefixInformation as NdiscPrefixInformation, RedirectedHeader as NdiscRedirectedHeader,
|
|
Repr as NdiscOptionRepr, Type as NdiscOptionType,
|
|
};
|
|
|
|
#[cfg(feature = "proto-ipv6")]
|
|
pub use self::mld::{AddressRecord as MldAddressRecord, Repr as MldRepr};
|
|
|
|
pub use self::udp::{Packet as UdpPacket, Repr as UdpRepr, HEADER_LEN as UDP_HEADER_LEN};
|
|
|
|
pub use self::tcp::{
|
|
Control as TcpControl, Packet as TcpPacket, Repr as TcpRepr, SeqNumber as TcpSeqNumber,
|
|
TcpOption, HEADER_LEN as TCP_HEADER_LEN,
|
|
};
|
|
|
|
#[cfg(feature = "proto-dhcpv4")]
|
|
pub use self::dhcpv4::{
|
|
MessageType as DhcpMessageType, Packet as DhcpPacket, Repr as DhcpRepr,
|
|
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)
|
|
}
|
|
}
|