1mod field {
74 pub type Field = ::core::ops::Range<usize>;
75 pub type Rest = ::core::ops::RangeFrom<usize>;
76}
77
78pub mod pretty_print;
79
80#[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
81mod arp;
82#[cfg(feature = "proto-dhcpv4")]
83pub(crate) mod dhcpv4;
84#[cfg(feature = "proto-dns")]
85pub(crate) mod dns;
86#[cfg(feature = "medium-ethernet")]
87mod ethernet;
88#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
89mod icmp;
90#[cfg(feature = "proto-ipv4")]
91mod icmpv4;
92#[cfg(feature = "proto-ipv6")]
93mod icmpv6;
94#[cfg(feature = "medium-ieee802154")]
95pub mod ieee802154;
96#[cfg(feature = "proto-ipv4")]
97mod igmp;
98pub(crate) mod ip;
99#[cfg(feature = "proto-ipv4")]
100pub(crate) mod ipv4;
101#[cfg(feature = "proto-ipv6")]
102pub(crate) mod ipv6;
103#[cfg(feature = "proto-ipv6")]
104mod ipv6ext_header;
105#[cfg(feature = "proto-ipv6")]
106mod ipv6fragment;
107#[cfg(feature = "proto-ipv6")]
108mod ipv6hbh;
109#[cfg(feature = "proto-ipv6")]
110mod ipv6option;
111#[cfg(feature = "proto-ipv6")]
112mod ipv6routing;
113#[cfg(feature = "proto-ipv6")]
114mod mld;
115#[cfg(all(
116 feature = "proto-ipv6",
117 any(feature = "medium-ethernet", feature = "medium-ieee802154")
118))]
119mod ndisc;
120#[cfg(all(
121 feature = "proto-ipv6",
122 any(feature = "medium-ethernet", feature = "medium-ieee802154")
123))]
124mod ndiscoption;
125#[cfg(feature = "proto-rpl")]
126mod rpl;
127#[cfg(all(feature = "proto-sixlowpan", feature = "medium-ieee802154"))]
128mod sixlowpan;
129mod tcp;
130mod udp;
131
132#[cfg(feature = "proto-ipsec-ah")]
133mod ipsec_ah;
134
135#[cfg(feature = "proto-ipsec-esp")]
136mod ipsec_esp;
137
138use core::fmt;
139
140use crate::phy::Medium;
141
142pub use self::pretty_print::PrettyPrinter;
143
144#[cfg(feature = "medium-ethernet")]
145pub use self::ethernet::{
146 Address as EthernetAddress, EtherType as EthernetProtocol, Frame as EthernetFrame,
147 HEADER_LEN as ETHERNET_HEADER_LEN, Repr as EthernetRepr,
148};
149
150#[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
151pub use self::arp::{
152 Hardware as ArpHardware, Operation as ArpOperation, Packet as ArpPacket, Repr as ArpRepr,
153};
154
155#[cfg(feature = "proto-rpl")]
156pub use self::rpl::{
157 InstanceId as RplInstanceId, Repr as RplRepr, data::HopByHopOption as RplHopByHopRepr,
158 data::Packet as RplHopByHopPacket, options::Packet as RplOptionPacket,
159 options::Repr as RplOptionRepr,
160};
161
162#[cfg(all(feature = "proto-sixlowpan", feature = "medium-ieee802154"))]
163pub use self::sixlowpan::{
164 AddressContext as SixlowpanAddressContext, NextHeader as SixlowpanNextHeader, SixlowpanPacket,
165 frag::{Key as SixlowpanFragKey, Packet as SixlowpanFragPacket, Repr as SixlowpanFragRepr},
166 iphc::{Packet as SixlowpanIphcPacket, Repr as SixlowpanIphcRepr},
167 nhc::{
168 ExtHeaderId as SixlowpanExtHeaderId, ExtHeaderPacket as SixlowpanExtHeaderPacket,
169 ExtHeaderRepr as SixlowpanExtHeaderRepr, NhcPacket as SixlowpanNhcPacket,
170 UdpNhcPacket as SixlowpanUdpNhcPacket, UdpNhcRepr as SixlowpanUdpNhcRepr,
171 },
172};
173
174#[cfg(feature = "medium-ieee802154")]
175pub use self::ieee802154::{
176 Address as Ieee802154Address, AddressingMode as Ieee802154AddressingMode,
177 Frame as Ieee802154Frame, FrameType as Ieee802154FrameType,
178 FrameVersion as Ieee802154FrameVersion, Pan as Ieee802154Pan, Repr as Ieee802154Repr,
179};
180
181pub use self::ip::{
182 Address as IpAddress, Cidr as IpCidr, Endpoint as IpEndpoint,
183 ListenEndpoint as IpListenEndpoint, Protocol as IpProtocol, Repr as IpRepr,
184 Version as IpVersion,
185};
186
187#[cfg(feature = "proto-ipv4")]
188pub use self::ipv4::{
189 Address as Ipv4Address, Cidr as Ipv4Cidr, HEADER_LEN as IPV4_HEADER_LEN, Key as Ipv4FragKey,
190 MIN_MTU as IPV4_MIN_MTU, MULTICAST_ALL_ROUTERS as IPV4_MULTICAST_ALL_ROUTERS,
191 MULTICAST_ALL_SYSTEMS as IPV4_MULTICAST_ALL_SYSTEMS, Packet as Ipv4Packet, Repr as Ipv4Repr,
192};
193
194#[cfg(feature = "proto-ipv4")]
195pub(crate) use self::ipv4::AddressExt as Ipv4AddressExt;
196
197#[cfg(feature = "proto-ipv6")]
198pub use self::ipv6::{
199 Address as Ipv6Address, Cidr as Ipv6Cidr, HEADER_LEN as IPV6_HEADER_LEN,
200 LINK_LOCAL_ALL_MLDV2_ROUTERS as IPV6_LINK_LOCAL_ALL_MLDV2_ROUTERS,
201 LINK_LOCAL_ALL_NODES as IPV6_LINK_LOCAL_ALL_NODES,
202 LINK_LOCAL_ALL_ROUTERS as IPV6_LINK_LOCAL_ALL_ROUTERS,
203 LINK_LOCAL_ALL_RPL_NODES as IPV6_LINK_LOCAL_ALL_RPL_NODES, MIN_MTU as IPV6_MIN_MTU,
204 Packet as Ipv6Packet, Repr as Ipv6Repr,
205};
206#[cfg(feature = "proto-ipv6")]
207pub(crate) use self::ipv6::{AddressExt as Ipv6AddressExt, MulticastScope as Ipv6MulticastScope};
208
209#[cfg(feature = "proto-ipv6")]
210pub use self::ipv6option::{
211 FailureType as Ipv6OptionFailureType, Ipv6Option, Ipv6OptionsIterator, Repr as Ipv6OptionRepr,
212 RouterAlert as Ipv6OptionRouterAlert, Type as Ipv6OptionType,
213};
214
215#[cfg(feature = "proto-ipv6")]
216pub use self::ipv6ext_header::{Header as Ipv6ExtHeader, Repr as Ipv6ExtHeaderRepr};
217
218#[cfg(feature = "proto-ipv6")]
219pub use self::ipv6fragment::{Header as Ipv6FragmentHeader, Repr as Ipv6FragmentRepr};
220
221#[cfg(feature = "proto-ipv6")]
222pub use self::ipv6hbh::{Header as Ipv6HopByHopHeader, Repr as Ipv6HopByHopRepr};
223
224#[cfg(feature = "proto-ipv6")]
225pub use self::ipv6routing::{
226 Header as Ipv6RoutingHeader, Repr as Ipv6RoutingRepr, Type as Ipv6RoutingType,
227};
228
229#[cfg(feature = "proto-ipv4")]
230pub use self::icmpv4::{
231 DstUnreachable as Icmpv4DstUnreachable, Message as Icmpv4Message, Packet as Icmpv4Packet,
232 ParamProblem as Icmpv4ParamProblem, Redirect as Icmpv4Redirect, Repr as Icmpv4Repr,
233 TimeExceeded as Icmpv4TimeExceeded,
234};
235
236#[cfg(feature = "proto-ipv4")]
237pub use self::igmp::{IgmpVersion, Packet as IgmpPacket, Repr as IgmpRepr};
238
239#[cfg(feature = "proto-ipv6")]
240pub use self::icmpv6::{
241 DstUnreachable as Icmpv6DstUnreachable, Message as Icmpv6Message, Packet as Icmpv6Packet,
242 ParamProblem as Icmpv6ParamProblem, Repr as Icmpv6Repr, TimeExceeded as Icmpv6TimeExceeded,
243};
244
245#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
246pub use self::icmp::Repr as IcmpRepr;
247
248#[cfg(all(
249 feature = "proto-ipv6",
250 any(feature = "medium-ethernet", feature = "medium-ieee802154")
251))]
252pub use self::ndisc::{
253 NeighborFlags as NdiscNeighborFlags, Repr as NdiscRepr, RouterFlags as NdiscRouterFlags,
254};
255
256#[cfg(all(
257 feature = "proto-ipv6",
258 any(feature = "medium-ethernet", feature = "medium-ieee802154")
259))]
260pub use self::ndiscoption::{
261 NdiscOption, PrefixInfoFlags as NdiscPrefixInfoFlags,
262 PrefixInformation as NdiscPrefixInformation, RedirectedHeader as NdiscRedirectedHeader,
263 Repr as NdiscOptionRepr, Type as NdiscOptionType,
264};
265
266#[cfg(feature = "proto-ipv6")]
267pub use self::mld::{
268 AddressRecord as MldAddressRecord, AddressRecordRepr as MldAddressRecordRepr,
269 RecordType as MldRecordType, Repr as MldRepr,
270};
271
272pub use self::udp::{HEADER_LEN as UDP_HEADER_LEN, Packet as UdpPacket, Repr as UdpRepr};
273
274pub use self::tcp::{
275 Control as TcpControl, HEADER_LEN as TCP_HEADER_LEN, Packet as TcpPacket, Repr as TcpRepr,
276 SeqNumber as TcpSeqNumber, TcpOption, TcpTimestampGenerator, TcpTimestampRepr,
277};
278
279#[cfg(feature = "proto-dhcpv4")]
280pub use self::dhcpv4::{
281 CLIENT_PORT as DHCP_CLIENT_PORT, DhcpOption, DhcpOptionWriter, Flags as DhcpFlags,
282 MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT, MessageType as DhcpMessageType,
283 OpCode as DhcpOpCode, Packet as DhcpPacket, Repr as DhcpRepr, SERVER_PORT as DHCP_SERVER_PORT,
284};
285
286#[cfg(feature = "proto-dns")]
287pub use self::dns::{
288 Flags as DnsFlags, Opcode as DnsOpcode, Packet as DnsPacket, Question as DnsQuestion,
289 Rcode as DnsRcode, Record as DnsRecord, RecordData as DnsRecordData, Repr as DnsRepr,
290 Type as DnsQueryType,
291};
292
293#[cfg(feature = "proto-ipsec-ah")]
294pub use self::ipsec_ah::{Packet as IpSecAuthHeaderPacket, Repr as IpSecAuthHeaderRepr};
295
296#[cfg(feature = "proto-ipsec-esp")]
297pub use self::ipsec_esp::{Packet as IpSecEspPacket, Repr as IpSecEspRepr};
298
299#[derive(Debug, Clone, Copy, PartialEq, Eq)]
303#[cfg_attr(feature = "defmt", derive(defmt::Format))]
304pub struct Error;
305
306#[cfg(feature = "std")]
307impl std::error::Error for Error {}
308
309impl fmt::Display for Error {
310 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311 write!(f, "wire::Error")
312 }
313}
314
315pub type Result<T> = core::result::Result<T, Error>;
316
317#[cfg(any(
319 feature = "medium-ip",
320 feature = "medium-ethernet",
321 feature = "medium-ieee802154"
322))]
323#[derive(Debug, Clone, Copy, PartialEq, Eq)]
324#[cfg_attr(feature = "defmt", derive(defmt::Format))]
325pub enum HardwareAddress {
326 #[cfg(feature = "medium-ip")]
327 Ip,
328 #[cfg(feature = "medium-ethernet")]
329 Ethernet(EthernetAddress),
330 #[cfg(feature = "medium-ieee802154")]
331 Ieee802154(Ieee802154Address),
332}
333
334#[cfg(any(
335 feature = "medium-ip",
336 feature = "medium-ethernet",
337 feature = "medium-ieee802154"
338))]
339#[cfg(test)]
340impl Default for HardwareAddress {
341 fn default() -> Self {
342 #![allow(unreachable_code)]
343 #[cfg(feature = "medium-ethernet")]
344 {
345 return Self::Ethernet(EthernetAddress::default());
346 }
347 #[cfg(feature = "medium-ip")]
348 {
349 return Self::Ip;
350 }
351 #[cfg(feature = "medium-ieee802154")]
352 {
353 Self::Ieee802154(Ieee802154Address::default())
354 }
355 }
356}
357
358#[cfg(any(
359 feature = "medium-ip",
360 feature = "medium-ethernet",
361 feature = "medium-ieee802154"
362))]
363impl HardwareAddress {
364 pub const fn as_bytes(&self) -> &[u8] {
365 match self {
366 #[cfg(feature = "medium-ip")]
367 HardwareAddress::Ip => unreachable!(),
368 #[cfg(feature = "medium-ethernet")]
369 HardwareAddress::Ethernet(addr) => addr.as_bytes(),
370 #[cfg(feature = "medium-ieee802154")]
371 HardwareAddress::Ieee802154(addr) => addr.as_bytes(),
372 }
373 }
374
375 pub fn is_unicast(&self) -> bool {
377 match self {
378 #[cfg(feature = "medium-ip")]
379 HardwareAddress::Ip => unreachable!(),
380 #[cfg(feature = "medium-ethernet")]
381 HardwareAddress::Ethernet(addr) => addr.is_unicast(),
382 #[cfg(feature = "medium-ieee802154")]
383 HardwareAddress::Ieee802154(addr) => addr.is_unicast(),
384 }
385 }
386
387 pub fn is_broadcast(&self) -> bool {
389 match self {
390 #[cfg(feature = "medium-ip")]
391 HardwareAddress::Ip => unreachable!(),
392 #[cfg(feature = "medium-ethernet")]
393 HardwareAddress::Ethernet(addr) => addr.is_broadcast(),
394 #[cfg(feature = "medium-ieee802154")]
395 HardwareAddress::Ieee802154(addr) => addr.is_broadcast(),
396 }
397 }
398
399 #[cfg(feature = "medium-ethernet")]
400 pub(crate) fn ethernet_or_panic(&self) -> EthernetAddress {
401 match self {
402 HardwareAddress::Ethernet(addr) => *addr,
403 #[allow(unreachable_patterns)]
404 _ => panic!("HardwareAddress is not Ethernet."),
405 }
406 }
407
408 #[cfg(feature = "medium-ieee802154")]
409 pub(crate) fn ieee802154_or_panic(&self) -> Ieee802154Address {
410 match self {
411 HardwareAddress::Ieee802154(addr) => *addr,
412 #[allow(unreachable_patterns)]
413 _ => panic!("HardwareAddress is not Ethernet."),
414 }
415 }
416
417 #[inline]
418 pub(crate) fn medium(&self) -> Medium {
419 match self {
420 #[cfg(feature = "medium-ip")]
421 HardwareAddress::Ip => Medium::Ip,
422 #[cfg(feature = "medium-ethernet")]
423 HardwareAddress::Ethernet(_) => Medium::Ethernet,
424 #[cfg(feature = "medium-ieee802154")]
425 HardwareAddress::Ieee802154(_) => Medium::Ieee802154,
426 }
427 }
428
429 pub fn as_eui_64(&self) -> Option<[u8; 8]> {
430 match self {
431 #[cfg(feature = "medium-ip")]
432 HardwareAddress::Ip => None,
433 #[cfg(feature = "medium-ethernet")]
434 HardwareAddress::Ethernet(ethernet) => ethernet.as_eui_64(),
435 #[cfg(feature = "medium-ieee802154")]
436 HardwareAddress::Ieee802154(ieee802154) => ieee802154.as_eui_64(),
437 }
438 }
439}
440
441#[cfg(any(
442 feature = "medium-ip",
443 feature = "medium-ethernet",
444 feature = "medium-ieee802154"
445))]
446impl core::fmt::Display for HardwareAddress {
447 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
448 match self {
449 #[cfg(feature = "medium-ip")]
450 HardwareAddress::Ip => write!(f, "no hardware addr"),
451 #[cfg(feature = "medium-ethernet")]
452 HardwareAddress::Ethernet(addr) => write!(f, "{addr}"),
453 #[cfg(feature = "medium-ieee802154")]
454 HardwareAddress::Ieee802154(addr) => write!(f, "{addr}"),
455 }
456 }
457}
458
459#[cfg(feature = "medium-ethernet")]
460impl From<EthernetAddress> for HardwareAddress {
461 fn from(addr: EthernetAddress) -> Self {
462 HardwareAddress::Ethernet(addr)
463 }
464}
465
466#[cfg(feature = "medium-ieee802154")]
467impl From<Ieee802154Address> for HardwareAddress {
468 fn from(addr: Ieee802154Address) -> Self {
469 HardwareAddress::Ieee802154(addr)
470 }
471}
472
473#[cfg(not(feature = "medium-ieee802154"))]
474pub const MAX_HARDWARE_ADDRESS_LEN: usize = 6;
475#[cfg(feature = "medium-ieee802154")]
476pub const MAX_HARDWARE_ADDRESS_LEN: usize = 8;
477
478#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
482#[derive(Debug, PartialEq, Eq, Clone, Copy)]
483#[cfg_attr(feature = "defmt", derive(defmt::Format))]
484pub struct RawHardwareAddress {
485 len: u8,
486 data: [u8; MAX_HARDWARE_ADDRESS_LEN],
487}
488
489#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
490impl RawHardwareAddress {
491 pub fn from_bytes(addr: &[u8]) -> Self {
496 let mut data = [0u8; MAX_HARDWARE_ADDRESS_LEN];
497 data[..addr.len()].copy_from_slice(addr);
498
499 Self {
500 len: addr.len() as u8,
501 data,
502 }
503 }
504
505 pub fn as_bytes(&self) -> &[u8] {
506 &self.data[..self.len as usize]
507 }
508
509 pub const fn len(&self) -> usize {
510 self.len as usize
511 }
512
513 pub const fn is_empty(&self) -> bool {
514 self.len == 0
515 }
516
517 pub fn parse(&self, medium: Medium) -> Result<HardwareAddress> {
518 match medium {
519 #[cfg(feature = "medium-ethernet")]
520 Medium::Ethernet => {
521 if self.len() != 6 {
522 return Err(Error);
523 }
524 Ok(HardwareAddress::Ethernet(EthernetAddress::from_bytes(
525 self.as_bytes(),
526 )))
527 }
528 #[cfg(feature = "medium-ieee802154")]
529 Medium::Ieee802154 => {
530 if self.len() != 8 {
531 return Err(Error);
532 }
533 Ok(HardwareAddress::Ieee802154(Ieee802154Address::from_bytes(
534 self.as_bytes(),
535 )))
536 }
537 #[cfg(feature = "medium-ip")]
538 Medium::Ip => unreachable!(),
539 }
540 }
541}
542
543#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
544impl core::fmt::Display for RawHardwareAddress {
545 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
546 for (i, &b) in self.as_bytes().iter().enumerate() {
547 if i != 0 {
548 write!(f, ":")?;
549 }
550 write!(f, "{b:02x}")?;
551 }
552 Ok(())
553 }
554}
555
556#[cfg(feature = "medium-ethernet")]
557impl From<EthernetAddress> for RawHardwareAddress {
558 fn from(addr: EthernetAddress) -> Self {
559 Self::from_bytes(addr.as_bytes())
560 }
561}
562
563#[cfg(feature = "medium-ieee802154")]
564impl From<Ieee802154Address> for RawHardwareAddress {
565 fn from(addr: Ieee802154Address) -> Self {
566 Self::from_bytes(addr.as_bytes())
567 }
568}
569
570#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
571impl From<HardwareAddress> for RawHardwareAddress {
572 fn from(addr: HardwareAddress) -> Self {
573 Self::from_bytes(addr.as_bytes())
574 }
575}
576
577#[cfg(test)]
578mod tests {
579 use super::*;
580 use rstest::rstest;
581
582 #[rstest]
583 #[cfg(feature = "medium-ethernet")]
584 #[case((Medium::Ethernet, &[0u8; 6][..]), Ok(HardwareAddress::Ethernet(EthernetAddress([0, 0, 0, 0, 0, 0]))))]
585 #[cfg(feature = "medium-ethernet")]
586 #[case((Medium::Ethernet, &[1u8; 5][..]), Err(Error))]
587 #[cfg(feature = "medium-ethernet")]
588 #[case((Medium::Ethernet, &[1u8; 7][..]), Err(Error))]
589 #[cfg(feature = "medium-ieee802154")]
590 #[case((Medium::Ieee802154, &[0u8; 8][..]), Ok(HardwareAddress::Ieee802154(Ieee802154Address::Extended([0, 0, 0, 0, 0, 0, 0, 0]))))]
591 #[cfg(feature = "medium-ieee802154")]
592 #[case((Medium::Ieee802154, &[1u8; 2][..]), Err(Error))]
593 #[cfg(feature = "medium-ieee802154")]
594 #[case((Medium::Ieee802154, &[1u8; 1][..]), Err(Error))]
595 fn parse_hardware_address(
596 #[case] input: (Medium, &[u8]),
597 #[case] expected: Result<HardwareAddress>,
598 ) {
599 let (medium, input) = input;
600
601 if input.len() < MAX_HARDWARE_ADDRESS_LEN {
605 let raw = RawHardwareAddress::from_bytes(input);
606 assert_eq!(raw.parse(medium), expected);
607 }
608 }
609}