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