1 /*! Low-level packet access and construction. 2 3 The `wire` module deals with the packet *representation*. It provides two levels 4 of functionality. 5 6 * First, it provides functions to extract fields from sequences of octets, 7 and to insert fields into sequences of octets. This happens `Packet` family of 8 structures, e.g. [EthernetFrame] or [Ipv4Packet]. 9 * Second, in cases where the space of valid field values is much smaller than the space 10 of possible field values, it provides a compact, high-level representation 11 of packet data that can be parsed from and emitted into a sequence of octets. 12 This happens through the `Repr` family of structs and enums, e.g. [ArpRepr] or [Ipv4Repr]. 13 14 [EthernetFrame]: struct.EthernetFrame.html 15 [Ipv4Packet]: struct.Ipv4Packet.html 16 [ArpRepr]: enum.ArpRepr.html 17 [Ipv4Repr]: struct.Ipv4Repr.html 18 19 The functions in the `wire` module are designed for use together with `-Cpanic=abort`. 20 21 The `Packet` family of data structures guarantees that, if the `Packet::check_len()` method 22 returned `Ok(())`, then no accessor or setter method will panic; however, the guarantee 23 provided by `Packet::check_len()` may no longer hold after changing certain fields, 24 which are listed in the documentation for the specific packet. 25 26 The `Packet::new_checked` method is a shorthand for a combination of `Packet::new_unchecked` 27 and `Packet::check_len`. 28 When parsing untrusted input, it is *necessary* to use `Packet::new_checked()`; 29 so long as the buffer is not modified, no accessor will fail. 30 When emitting output, though, it is *incorrect* to use `Packet::new_checked()`; 31 the length check is likely to succeed on a zeroed buffer, but fail on a buffer 32 filled with data from a previous packet, such as when reusing buffers, resulting 33 in nondeterministic panics with some network devices but not others. 34 The buffer length for emission is not calculated by the `Packet` layer. 35 36 In the `Repr` family of data structures, the `Repr::parse()` method never panics 37 as long as `Packet::new_checked()` (or `Packet::check_len()`) has succeeded, and 38 the `Repr::emit()` method never panics as long as the underlying buffer is exactly 39 `Repr::buffer_len()` octets long. 40 41 # Examples 42 43 To emit an IP packet header into an octet buffer, and then parse it back: 44 45 ```rust 46 # #[cfg(feature = "proto-ipv4")] 47 # { 48 use smoltcp::phy::ChecksumCapabilities; 49 use smoltcp::wire::*; 50 let repr = Ipv4Repr { 51 src_addr: Ipv4Address::new(10, 0, 0, 1), 52 dst_addr: Ipv4Address::new(10, 0, 0, 2), 53 next_header: IpProtocol::Tcp, 54 payload_len: 10, 55 hop_limit: 64, 56 }; 57 let mut buffer = vec![0; repr.buffer_len() + repr.payload_len]; 58 { // emission 59 let mut packet = Ipv4Packet::new_unchecked(&mut buffer); 60 repr.emit(&mut packet, &ChecksumCapabilities::default()); 61 } 62 { // parsing 63 let packet = Ipv4Packet::new_checked(&buffer) 64 .expect("truncated packet"); 65 let parsed = Ipv4Repr::parse(&packet, &ChecksumCapabilities::default()) 66 .expect("malformed packet"); 67 assert_eq!(repr, parsed); 68 } 69 # } 70 ``` 71 */ 72 73 mod field { 74 pub type Field = ::core::ops::Range<usize>; 75 pub type Rest = ::core::ops::RangeFrom<usize>; 76 } 77 78 pub mod pretty_print; 79 80 #[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))] 81 mod arp; 82 #[cfg(feature = "proto-dhcpv4")] 83 pub(crate) mod dhcpv4; 84 #[cfg(feature = "proto-dns")] 85 pub(crate) mod dns; 86 #[cfg(feature = "medium-ethernet")] 87 mod ethernet; 88 #[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))] 89 mod icmp; 90 #[cfg(feature = "proto-ipv4")] 91 mod icmpv4; 92 #[cfg(feature = "proto-ipv6")] 93 mod icmpv6; 94 #[cfg(feature = "medium-ieee802154")] 95 pub mod ieee802154; 96 #[cfg(feature = "proto-igmp")] 97 mod igmp; 98 pub(crate) mod ip; 99 #[cfg(feature = "proto-ipv4")] 100 mod ipv4; 101 #[cfg(feature = "proto-ipv6")] 102 mod ipv6; 103 #[cfg(feature = "proto-ipv6")] 104 mod ipv6fragment; 105 #[cfg(feature = "proto-ipv6")] 106 mod ipv6hopbyhop; 107 #[cfg(feature = "proto-ipv6")] 108 mod ipv6option; 109 #[cfg(feature = "proto-ipv6")] 110 mod ipv6routing; 111 #[cfg(feature = "proto-ipv6")] 112 mod mld; 113 #[cfg(all( 114 feature = "proto-ipv6", 115 any(feature = "medium-ethernet", feature = "medium-ieee802154") 116 ))] 117 mod ndisc; 118 #[cfg(all( 119 feature = "proto-ipv6", 120 any(feature = "medium-ethernet", feature = "medium-ieee802154") 121 ))] 122 mod ndiscoption; 123 #[cfg(all(feature = "proto-sixlowpan", feature = "medium-ieee802154"))] 124 mod sixlowpan; 125 mod tcp; 126 mod udp; 127 128 use core::fmt; 129 130 use crate::phy::Medium; 131 132 pub use self::pretty_print::PrettyPrinter; 133 134 #[cfg(feature = "medium-ethernet")] 135 pub use self::ethernet::{ 136 Address as EthernetAddress, EtherType as EthernetProtocol, Frame as EthernetFrame, 137 Repr as EthernetRepr, HEADER_LEN as ETHERNET_HEADER_LEN, 138 }; 139 140 #[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))] 141 pub use self::arp::{ 142 Hardware as ArpHardware, Operation as ArpOperation, Packet as ArpPacket, Repr as ArpRepr, 143 }; 144 145 #[cfg(all(feature = "proto-sixlowpan", feature = "medium-ieee802154"))] 146 pub use self::sixlowpan::{ 147 frag::{Key as SixlowpanFragKey, Packet as SixlowpanFragPacket, Repr as SixlowpanFragRepr}, 148 iphc::{Packet as SixlowpanIphcPacket, Repr as SixlowpanIphcRepr}, 149 nhc::{ 150 ExtHeaderPacket as SixlowpanExtHeaderPacket, ExtHeaderRepr as SixlowpanExtHeaderRepr, 151 NhcPacket as SixlowpanNhcPacket, UdpNhcPacket as SixlowpanUdpNhcPacket, 152 UdpNhcRepr as SixlowpanUdpNhcRepr, 153 }, 154 AddressContext as SixlowpanAddressContext, NextHeader as SixlowpanNextHeader, SixlowpanPacket, 155 }; 156 157 #[cfg(feature = "medium-ieee802154")] 158 pub use self::ieee802154::{ 159 Address as Ieee802154Address, AddressingMode as Ieee802154AddressingMode, 160 Frame as Ieee802154Frame, FrameType as Ieee802154FrameType, 161 FrameVersion as Ieee802154FrameVersion, Pan as Ieee802154Pan, Repr as Ieee802154Repr, 162 }; 163 164 pub use self::ip::{ 165 Address as IpAddress, Cidr as IpCidr, Endpoint as IpEndpoint, 166 ListenEndpoint as IpListenEndpoint, Protocol as IpProtocol, Repr as IpRepr, 167 Version as IpVersion, 168 }; 169 170 #[cfg(feature = "proto-ipv4")] 171 pub use self::ipv4::{ 172 Address as Ipv4Address, Cidr as Ipv4Cidr, Key as Ipv4FragKey, Packet as Ipv4Packet, 173 Repr as Ipv4Repr, HEADER_LEN as IPV4_HEADER_LEN, MIN_MTU as IPV4_MIN_MTU, 174 }; 175 176 #[cfg(feature = "proto-ipv6")] 177 pub use self::ipv6::{ 178 Address as Ipv6Address, Cidr as Ipv6Cidr, Packet as Ipv6Packet, Repr as Ipv6Repr, 179 HEADER_LEN as IPV6_HEADER_LEN, MIN_MTU as IPV6_MIN_MTU, 180 }; 181 182 #[cfg(feature = "proto-ipv6")] 183 pub use self::ipv6option::{ 184 FailureType as Ipv6OptionFailureType, Ipv6Option, Repr as Ipv6OptionRepr, 185 Type as Ipv6OptionType, 186 }; 187 188 #[cfg(feature = "proto-ipv6")] 189 pub use self::ipv6hopbyhop::{Header as Ipv6HopByHopHeader, Repr as Ipv6HopByHopRepr}; 190 191 #[cfg(feature = "proto-ipv6")] 192 pub use self::ipv6fragment::{Header as Ipv6FragmentHeader, Repr as Ipv6FragmentRepr}; 193 194 #[cfg(feature = "proto-ipv6")] 195 pub use self::ipv6routing::{ 196 Header as Ipv6RoutingHeader, Repr as Ipv6RoutingRepr, Type as Ipv6RoutingType, 197 }; 198 199 #[cfg(feature = "proto-ipv4")] 200 pub use self::icmpv4::{ 201 DstUnreachable as Icmpv4DstUnreachable, Message as Icmpv4Message, Packet as Icmpv4Packet, 202 ParamProblem as Icmpv4ParamProblem, Redirect as Icmpv4Redirect, Repr as Icmpv4Repr, 203 TimeExceeded as Icmpv4TimeExceeded, 204 }; 205 206 #[cfg(feature = "proto-igmp")] 207 pub use self::igmp::{IgmpVersion, Packet as IgmpPacket, Repr as IgmpRepr}; 208 209 #[cfg(feature = "proto-ipv6")] 210 pub use self::icmpv6::{ 211 DstUnreachable as Icmpv6DstUnreachable, Message as Icmpv6Message, Packet as Icmpv6Packet, 212 ParamProblem as Icmpv6ParamProblem, Repr as Icmpv6Repr, TimeExceeded as Icmpv6TimeExceeded, 213 }; 214 215 #[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))] 216 pub use self::icmp::Repr as IcmpRepr; 217 218 #[cfg(all( 219 feature = "proto-ipv6", 220 any(feature = "medium-ethernet", feature = "medium-ieee802154") 221 ))] 222 pub use self::ndisc::{ 223 NeighborFlags as NdiscNeighborFlags, Repr as NdiscRepr, RouterFlags as NdiscRouterFlags, 224 }; 225 226 #[cfg(all( 227 feature = "proto-ipv6", 228 any(feature = "medium-ethernet", feature = "medium-ieee802154") 229 ))] 230 pub use self::ndiscoption::{ 231 NdiscOption, PrefixInfoFlags as NdiscPrefixInfoFlags, 232 PrefixInformation as NdiscPrefixInformation, RedirectedHeader as NdiscRedirectedHeader, 233 Repr as NdiscOptionRepr, Type as NdiscOptionType, 234 }; 235 236 #[cfg(feature = "proto-ipv6")] 237 pub use self::mld::{AddressRecord as MldAddressRecord, Repr as MldRepr}; 238 239 pub use self::udp::{Packet as UdpPacket, Repr as UdpRepr, HEADER_LEN as UDP_HEADER_LEN}; 240 241 pub use self::tcp::{ 242 Control as TcpControl, Packet as TcpPacket, Repr as TcpRepr, SeqNumber as TcpSeqNumber, 243 TcpOption, HEADER_LEN as TCP_HEADER_LEN, 244 }; 245 246 #[cfg(feature = "proto-dhcpv4")] 247 pub use self::dhcpv4::{ 248 DhcpOption, DhcpOptionWriter, MessageType as DhcpMessageType, Packet as DhcpPacket, 249 Repr as DhcpRepr, CLIENT_PORT as DHCP_CLIENT_PORT, 250 MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT, SERVER_PORT as DHCP_SERVER_PORT, 251 }; 252 253 #[cfg(feature = "proto-dns")] 254 pub use self::dns::{Packet as DnsPacket, Repr as DnsRepr, Type as DnsQueryType}; 255 256 /// Parsing a packet failed. 257 /// 258 /// Either it is malformed, or it is not supported by smoltcp. 259 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 260 #[cfg_attr(feature = "defmt", derive(defmt::Format))] 261 pub struct Error; 262 263 #[cfg(feature = "std")] 264 impl std::error::Error for Error {} 265 266 impl fmt::Display for Error { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result267 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 268 write!(f, "wire::Error") 269 } 270 } 271 272 pub type Result<T> = core::result::Result<T, Error>; 273 274 /// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address. 275 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] 276 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 277 #[cfg_attr(feature = "defmt", derive(defmt::Format))] 278 pub enum HardwareAddress { 279 #[cfg(feature = "medium-ethernet")] 280 Ethernet(EthernetAddress), 281 #[cfg(feature = "medium-ieee802154")] 282 Ieee802154(Ieee802154Address), 283 } 284 285 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] 286 impl HardwareAddress { as_bytes(&self) -> &[u8]287 pub const fn as_bytes(&self) -> &[u8] { 288 match self { 289 #[cfg(feature = "medium-ethernet")] 290 HardwareAddress::Ethernet(addr) => addr.as_bytes(), 291 #[cfg(feature = "medium-ieee802154")] 292 HardwareAddress::Ieee802154(addr) => addr.as_bytes(), 293 } 294 } 295 296 /// Query wether the address is an unicast address. is_unicast(&self) -> bool297 pub fn is_unicast(&self) -> bool { 298 match self { 299 #[cfg(feature = "medium-ethernet")] 300 HardwareAddress::Ethernet(addr) => addr.is_unicast(), 301 #[cfg(feature = "medium-ieee802154")] 302 HardwareAddress::Ieee802154(addr) => addr.is_unicast(), 303 } 304 } 305 306 /// Query wether the address is a broadcast address. is_broadcast(&self) -> bool307 pub fn is_broadcast(&self) -> bool { 308 match self { 309 #[cfg(feature = "medium-ethernet")] 310 HardwareAddress::Ethernet(addr) => addr.is_broadcast(), 311 #[cfg(feature = "medium-ieee802154")] 312 HardwareAddress::Ieee802154(addr) => addr.is_broadcast(), 313 } 314 } 315 316 #[cfg(feature = "medium-ethernet")] ethernet_or_panic(&self) -> EthernetAddress317 pub(crate) fn ethernet_or_panic(&self) -> EthernetAddress { 318 match self { 319 HardwareAddress::Ethernet(addr) => *addr, 320 #[allow(unreachable_patterns)] 321 _ => panic!("HardwareAddress is not Ethernet."), 322 } 323 } 324 325 #[cfg(feature = "medium-ieee802154")] ieee802154_or_panic(&self) -> Ieee802154Address326 pub(crate) fn ieee802154_or_panic(&self) -> Ieee802154Address { 327 match self { 328 HardwareAddress::Ieee802154(addr) => *addr, 329 #[allow(unreachable_patterns)] 330 _ => panic!("HardwareAddress is not Ethernet."), 331 } 332 } 333 } 334 335 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] 336 impl core::fmt::Display for HardwareAddress { fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result337 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 338 match self { 339 #[cfg(feature = "medium-ethernet")] 340 HardwareAddress::Ethernet(addr) => write!(f, "{addr}"), 341 #[cfg(feature = "medium-ieee802154")] 342 HardwareAddress::Ieee802154(addr) => write!(f, "{addr}"), 343 } 344 } 345 } 346 347 #[cfg(feature = "medium-ethernet")] 348 impl From<EthernetAddress> for HardwareAddress { from(addr: EthernetAddress) -> Self349 fn from(addr: EthernetAddress) -> Self { 350 HardwareAddress::Ethernet(addr) 351 } 352 } 353 354 #[cfg(feature = "medium-ieee802154")] 355 impl From<Ieee802154Address> for HardwareAddress { from(addr: Ieee802154Address) -> Self356 fn from(addr: Ieee802154Address) -> Self { 357 HardwareAddress::Ieee802154(addr) 358 } 359 } 360 361 #[cfg(not(feature = "medium-ieee802154"))] 362 pub const MAX_HARDWARE_ADDRESS_LEN: usize = 6; 363 #[cfg(feature = "medium-ieee802154")] 364 pub const MAX_HARDWARE_ADDRESS_LEN: usize = 8; 365 366 /// Unparsed hardware address. 367 /// 368 /// Used to make NDISC parsing agnostic of the hardware medium in use. 369 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] 370 #[derive(Debug, PartialEq, Eq, Clone, Copy)] 371 #[cfg_attr(feature = "defmt", derive(defmt::Format))] 372 pub struct RawHardwareAddress { 373 len: u8, 374 data: [u8; MAX_HARDWARE_ADDRESS_LEN], 375 } 376 377 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] 378 impl RawHardwareAddress { from_bytes(addr: &[u8]) -> Self379 pub fn from_bytes(addr: &[u8]) -> Self { 380 let mut data = [0u8; MAX_HARDWARE_ADDRESS_LEN]; 381 data[..addr.len()].copy_from_slice(addr); 382 383 Self { 384 len: addr.len() as u8, 385 data, 386 } 387 } 388 as_bytes(&self) -> &[u8]389 pub fn as_bytes(&self) -> &[u8] { 390 &self.data[..self.len as usize] 391 } 392 len(&self) -> usize393 pub const fn len(&self) -> usize { 394 self.len as usize 395 } 396 is_empty(&self) -> bool397 pub const fn is_empty(&self) -> bool { 398 self.len == 0 399 } 400 parse(&self, medium: Medium) -> Result<HardwareAddress>401 pub fn parse(&self, medium: Medium) -> Result<HardwareAddress> { 402 match medium { 403 #[cfg(feature = "medium-ethernet")] 404 Medium::Ethernet => { 405 if self.len() < 6 { 406 return Err(Error); 407 } 408 Ok(HardwareAddress::Ethernet(EthernetAddress::from_bytes( 409 self.as_bytes(), 410 ))) 411 } 412 #[cfg(feature = "medium-ieee802154")] 413 Medium::Ieee802154 => { 414 if self.len() < 8 { 415 return Err(Error); 416 } 417 Ok(HardwareAddress::Ieee802154(Ieee802154Address::from_bytes( 418 self.as_bytes(), 419 ))) 420 } 421 #[cfg(feature = "medium-ip")] 422 Medium::Ip => unreachable!(), 423 } 424 } 425 } 426 427 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] 428 impl core::fmt::Display for RawHardwareAddress { fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result429 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 430 for (i, &b) in self.as_bytes().iter().enumerate() { 431 if i != 0 { 432 write!(f, ":")?; 433 } 434 write!(f, "{b:02x}")?; 435 } 436 Ok(()) 437 } 438 } 439 440 #[cfg(feature = "medium-ethernet")] 441 impl From<EthernetAddress> for RawHardwareAddress { from(addr: EthernetAddress) -> Self442 fn from(addr: EthernetAddress) -> Self { 443 Self::from_bytes(addr.as_bytes()) 444 } 445 } 446 447 #[cfg(feature = "medium-ieee802154")] 448 impl From<Ieee802154Address> for RawHardwareAddress { from(addr: Ieee802154Address) -> Self449 fn from(addr: Ieee802154Address) -> Self { 450 Self::from_bytes(addr.as_bytes()) 451 } 452 } 453 454 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] 455 impl From<HardwareAddress> for RawHardwareAddress { from(addr: HardwareAddress) -> Self456 fn from(addr: HardwareAddress) -> Self { 457 Self::from_bytes(addr.as_bytes()) 458 } 459 } 460