mod.rs 17 KB

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