Browse Source

Merge pull request #455 from smoltcp-rs/defmt

Add defmt logging support
Dario Nieuwenhuis 4 years ago
parent
commit
a62eaacdf3

+ 6 - 0
.github/workflows/test.yml

@@ -70,6 +70,12 @@ jobs:
         features:
           # These feature sets cannot run tests, so we only check they build.
           - medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async
+        include:
+          # defmt doesn't support 1.40
+          - rust: stable
+            features: defmt medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async
+          - rust: nightly
+            features: defmt medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async
 
     steps:
       - uses: actions/checkout@v2

+ 8 - 0
Cargo.toml

@@ -21,6 +21,7 @@ byteorder = { version = "1.0", default-features = false }
 log = { version = "0.4.4", default-features = false, optional = true }
 libc = { version = "0.2.18", optional = true }
 bitflags = { version = "1.0", default-features = false }
+defmt = { version = "0.2.0", optional = true }
 
 [dev-dependencies]
 env_logger = "0.5"
@@ -46,6 +47,13 @@ verbose = []
 "socket-tcp" = ["socket"]
 "socket-icmp" = ["socket"]
 "async" = []
+
+defmt-trace = []
+defmt-debug = []
+defmt-info = []
+defmt-warn = []
+defmt-error = []
+
 default = [
   "std", "log", # needed for `cargo test --no-default-features --features default` :/
   "medium-ethernet", "medium-ip",

+ 2 - 0
examples/loopback.rs

@@ -21,6 +21,7 @@ mod mock {
     use core::cell::Cell;
 
     #[derive(Debug)]
+    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
     pub struct Clock(Cell<Instant>);
 
     impl Clock {
@@ -46,6 +47,7 @@ mod mock {
 
     // should be AtomicU64 but that's unstable
     #[derive(Debug, Clone)]
+    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
     pub struct Clock(Arc<AtomicUsize>);
 
     impl Clock {

+ 1 - 0
fuzz/fuzz_targets/tcp_headers.rs

@@ -24,6 +24,7 @@ mod mock {
 
     // should be AtomicU64 but that's unstable
     #[derive(Debug, Clone)]
+    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
     pub struct Clock(Arc<AtomicUsize>);
 
     impl Clock {

+ 4 - 0
src/dhcp/clientv4.rs

@@ -22,6 +22,7 @@ const PARAMETER_REQUEST_LIST: &[u8] = &[
 
 /// IPv4 configuration data returned by `client.poll()`
 #[derive(Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Config {
     pub address: Option<Ipv4Cidr>,
     pub router: Option<Ipv4Address>,
@@ -29,6 +30,7 @@ pub struct Config {
 }
 
 #[derive(Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 struct RequestState {
     retry: u16,
     endpoint_ip: Ipv4Address,
@@ -37,12 +39,14 @@ struct RequestState {
 }
 
 #[derive(Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 struct RenewState {
     endpoint_ip: Ipv4Address,
     server_identifier: Ipv4Address,
 }
 
 #[derive(Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 enum ClientState {
     /// Discovering the DHCP server
     Discovering,

+ 4 - 0
src/iface/interface.rs

@@ -243,6 +243,7 @@ let iface = InterfaceBuilder::new(device)
 }
 
 #[derive(Debug, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 #[cfg(feature = "medium-ethernet")]
 enum EthernetPacket<'a> {
     #[cfg(feature = "proto-ipv4")]
@@ -251,6 +252,7 @@ enum EthernetPacket<'a> {
 }
 
 #[derive(Debug, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub(crate) enum IpPacket<'a> {
     #[cfg(feature = "proto-ipv4")]
     Icmpv4((Ipv4Repr, Icmpv4Repr<'a>)),
@@ -591,6 +593,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT>
                     Medium::Ethernet => {
                         inner.process_ethernet(sockets, timestamp, &frame).map_err(|err| {
                             net_debug!("cannot process ingress packet: {}", err);
+                            #[cfg(not(feature = "defmt"))]
                             net_debug!("packet dump follows:\n{}",
                                     PrettyPrinter::<EthernetFrame<&[u8]>>::new("", &frame));
                             err
@@ -1844,6 +1847,7 @@ mod test {
     }
 
     #[derive(Debug, PartialEq)]
+    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
     struct MockTxToken;
 
     impl TxToken for MockTxToken {

+ 2 - 0
src/iface/neighbor.rs

@@ -11,6 +11,7 @@ use crate::time::{Duration, Instant};
 /// A neighbor mapping translates from a protocol address to a hardware address,
 /// and contains the timestamp past which the mapping should be discarded.
 #[derive(Debug, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Neighbor {
     hardware_addr: EthernetAddress,
     expires_at:    Instant,
@@ -18,6 +19,7 @@ pub struct Neighbor {
 
 /// An answer to a neighbor cache lookup.
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub(crate) enum Answer {
     /// The neighbor address is in the cache and not expired.
     Found(EthernetAddress),

+ 1 - 0
src/iface/route.rs

@@ -11,6 +11,7 @@ use crate::wire::{Ipv6Address, Ipv6Cidr};
 
 /// A prefix of addresses that should be routed via a router
 #[derive(Debug, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Route {
     pub via_router: IpAddress,
     /// `None` means "forever".

+ 12 - 0
src/lib.rs

@@ -64,6 +64,14 @@
 //! feature ever defined, to ensure that, when the representation layer is unable to make sense
 //! of a packet, it is still logged correctly and in full.
 //!
+//! # Minimum Supported Rust Version (MSRV)
+//!
+//! This crate is guaranteed to compile on stable Rust 1.40 and up with any valid set of features.
+//! It *might* compile on older versions but that may change in any new patch release.
+//!
+//! The exception is when using the `defmt` feature, in which case `defmt`'s MSRV applies, which
+//! is higher than 1.40.
+//!
 //! [wire]: wire/index.html
 //! [osi]: https://en.wikipedia.org/wiki/OSI_model
 //! [berk]: https://en.wikipedia.org/wiki/Berkeley_sockets
@@ -97,6 +105,9 @@ compile_error!("You must enable at least one of the following features: proto-ip
 ))]
 compile_error!("If you enable the socket feature, you must enable at least one of the following features: socket-raw, socket-udp, socket-tcp, socket-icmp");
 
+#[cfg(all(feature = "defmt", feature = "log"))]
+compile_error!("You must enable at most one of the following features: defmt, log");
+
 use core::fmt;
 
 #[macro_use]
@@ -116,6 +127,7 @@ pub mod dhcp;
 /// The error type for the networking stack.
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 #[non_exhaustive]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum Error {
     /// An operation cannot proceed because a buffer is empty or full.
     Exhausted,

+ 8 - 1
src/macros.rs

@@ -5,7 +5,13 @@ macro_rules! net_log {
     (debug, $($arg:expr),*) => { log::debug!($($arg),*); };
 }
 
-#[cfg(not(feature = "log"))]
+#[cfg(feature = "defmt")]
+macro_rules! net_log {
+    (trace, $($arg:expr),*) => { defmt::trace!($($arg),*); };
+    (debug, $($arg:expr),*) => { defmt::debug!($($arg),*); };
+}
+
+#[cfg(not(any(feature = "log", feature = "defmt")))]
 #[macro_use]
 macro_rules! net_log {
     ($level:ident, $($arg:expr),*) => { $( let _ = $arg; )* }
@@ -30,6 +36,7 @@ macro_rules! enum_with_unknown {
         }
     ) => {
         #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
         $( #[$enum_attr] )*
         pub enum $name {
             $(

+ 2 - 0
src/phy/fault_injector.rs

@@ -19,6 +19,7 @@ fn xorshift32(state: &mut u32) -> u32 {
 const MTU: usize = 1536;
 
 #[derive(Debug, Default, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 struct Config {
     corrupt_pct: u8,
     drop_pct:    u8,
@@ -30,6 +31,7 @@ struct Config {
 }
 
 #[derive(Debug, Clone)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 struct State {
     rng_seed:    u32,
     refilled_at: Instant,

+ 1 - 0
src/phy/fuzz_injector.rs

@@ -18,6 +18,7 @@ pub trait Fuzzer {
 /// smoltcp, and is not for production use.
 #[allow(unused)]
 #[derive(Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct FuzzInjector<D: for<'a> Device<'a>, FTx: Fuzzer, FRx: Fuzzer> {
     inner:   D,
     fuzz_tx: FTx,

+ 4 - 0
src/phy/mod.rs

@@ -119,6 +119,7 @@ pub use self::tuntap_interface::TunTapInterface;
 
 /// A description of checksum behavior for a particular protocol.
 #[derive(Debug, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum Checksum {
     /// Verify checksum when receiving and compute checksum when sending.
     Both,
@@ -156,6 +157,7 @@ impl Checksum {
 
 /// A description of checksum behavior for every supported protocol.
 #[derive(Debug, Clone, Default)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 #[non_exhaustive]
 pub struct ChecksumCapabilities {
     pub ipv4: Checksum,
@@ -188,6 +190,7 @@ impl ChecksumCapabilities {
 /// Higher-level protocols may achieve higher throughput or lower latency if they consider
 /// the bandwidth or packet size limitations.
 #[derive(Debug, Clone, Default)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 #[non_exhaustive]
 pub struct DeviceCapabilities {
     /// Medium of the device.
@@ -229,6 +232,7 @@ pub struct DeviceCapabilities {
 
 /// Type of medium of a device.
 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum Medium {
     /// Ethernet medium. Devices of this type send and receive Ethernet frames,
     /// and interfaces using it must do neighbor discovery via ARP or NDISC.

+ 2 - 0
src/phy/pcap_writer.rs

@@ -21,6 +21,7 @@ enum_with_unknown! {
 
 /// Packet capture mode.
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum PcapMode {
     /// Capture both received and transmitted packets.
     Both,
@@ -118,6 +119,7 @@ impl<T: Write> PcapSink for RefCell<T> {
 /// [libpcap]: https://wiki.wireshark.org/Development/LibpcapFileFormat
 /// [sink]: trait.PcapSink.html
 #[derive(Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct PcapWriter<D, S>
     where D: for<'a> Device<'a>,
           S: PcapSink + Clone,

+ 1 - 0
src/socket/icmp.rs

@@ -22,6 +22,7 @@ use crate::wire::{IpAddress, IpEndpoint, IpProtocol, IpRepr};
 ///
 /// [IcmpSocket::bind]: struct.IcmpSocket.html#method.bind
 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum Endpoint {
     Unspecified,
     Ident(u16),

+ 2 - 0
src/socket/meta.rs

@@ -7,6 +7,7 @@ use crate::time::{Duration, Instant};
 /// This enum tracks whether the socket should be polled based on the neighbor it is
 /// going to send packets to.
 #[derive(Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 enum NeighborState {
     /// Socket can be polled immediately.
     Active,
@@ -29,6 +30,7 @@ impl Default for NeighborState {
 /// This includes things that only external (to the socket, that is) code
 /// is interested in, but which are more conveniently stored inside the socket itself.
 #[derive(Debug, Default)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Meta {
     /// Handle of this socket within its enclosing `SocketSet`.
     /// Mainly useful for debug output.

+ 1 - 0
src/socket/mod.rs

@@ -61,6 +61,7 @@ pub(crate) use self::ref_::Session as SocketSession;
 
 /// Gives an indication on the next time the socket should be polled.
 #[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub(crate) enum PollAt {
     /// The socket needs to be polled immidiately.
     Now,

+ 1 - 0
src/socket/set.rs

@@ -17,6 +17,7 @@ pub struct Item<'a> {
 
 /// A handle, identifying a socket in a set.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Handle(usize);
 
 impl fmt::Display for Handle {

+ 2 - 0
src/socket/tcp.rs

@@ -21,6 +21,7 @@ pub type SocketBuffer<'a> = RingBuffer<'a, u8>;
 ///
 /// [RFC 793]: https://tools.ietf.org/html/rfc793
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum State {
     Closed,
     Listen,
@@ -147,6 +148,7 @@ impl RttEstimator {
 }
 
 #[derive(Debug, Clone, Copy, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 enum Timer {
     Idle {
         keep_alive_at: Option<Instant>,

+ 2 - 0
src/storage/assembler.rs

@@ -5,6 +5,7 @@ pub struct TooManyHolesError;
 
 /// A contiguous chunk of absent data, followed by a contiguous chunk of present data.
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 struct Contig {
     hole_size: usize,
     data_size: usize
@@ -80,6 +81,7 @@ const CONTIG_COUNT: usize = 4;
 /// Currently, up to a hardcoded limit of 4 or 32 holes can be tracked in the buffer.
 #[derive(Debug)]
 #[cfg_attr(test, derive(PartialEq, Eq, Clone))]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Assembler {
     #[cfg(not(any(feature = "std", feature = "alloc")))]
     contigs: [Contig; CONTIG_COUNT],

+ 1 - 0
src/storage/packet_buffer.rs

@@ -5,6 +5,7 @@ use crate::storage::RingBuffer;
 
 /// Size and header of a packet.
 #[derive(Debug, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct PacketMetadata<H> {
     size:   usize,
     header: Option<H>

+ 2 - 0
src/time.rs

@@ -22,6 +22,7 @@ use core::{ops, fmt};
 /// * A value less than `0` indicates a time before the starting
 ///   point.
 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Instant {
     pub millis: i64,
 }
@@ -135,6 +136,7 @@ impl ops::Sub<Instant> for Instant {
 
 /// A relative amount of time.
 #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Duration {
     pub millis: u64,
 }

+ 2 - 0
src/wire/arp.rs

@@ -22,6 +22,7 @@ enum_with_unknown! {
 
 /// A read/write wrapper around an Address Resolution Protocol packet buffer.
 #[derive(Debug, PartialEq, Clone)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Packet<T: AsRef<[u8]>> {
     buffer: T
 }
@@ -253,6 +254,7 @@ use crate::wire::{EthernetAddress, Ipv4Address};
 
 /// A high-level representation of an Address Resolution Protocol packet.
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 #[non_exhaustive]
 pub enum Repr {
     /// An Ethernet and IPv4 Address Resolution Protocol packet.

+ 3 - 0
src/wire/dhcpv4.rs

@@ -43,6 +43,7 @@ impl MessageType {
 
 /// A representation of a single DHCP option.
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum DhcpOption<'a> {
     EndOfList,
     Pad,
@@ -200,6 +201,7 @@ impl<'a> DhcpOption<'a> {
 
 /// A read/write wrapper around a Dynamic Host Configuration Protocol packet buffer.
 #[derive(Debug, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Packet<T: AsRef<[u8]>> {
     buffer: T
 }
@@ -624,6 +626,7 @@ impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> {
 ///
 /// The `options` field has a variable length.
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Repr<'a> {
     /// This field is also known as `op` in the RFC. It indicates the type of DHCP message this
     /// packet represents.

+ 3 - 0
src/wire/ethernet.rs

@@ -25,6 +25,7 @@ impl fmt::Display for EtherType {
 
 /// A six-octet Ethernet II address.
 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Address(pub [u8; 6]);
 
 impl Address {
@@ -78,6 +79,7 @@ impl fmt::Display for Address {
 
 /// A read/write wrapper around an Ethernet II frame buffer.
 #[derive(Debug, Clone)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Frame<T: AsRef<[u8]>> {
     buffer: T
 }
@@ -246,6 +248,7 @@ impl<T: AsRef<[u8]>> PrettyPrint for Frame<T> {
 
 /// A high-level representation of an Internet Protocol version 4 packet header.
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Repr {
     pub src_addr:    Address,
     pub dst_addr:    Address,

+ 1 - 0
src/wire/icmp.rs

@@ -4,6 +4,7 @@ use crate::wire::icmpv4;
 use crate::wire::icmpv6;
 
 #[derive(Clone, PartialEq, Eq, Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum Repr<'a> {
     #[cfg(feature = "proto-ipv4")]
     Ipv4(icmpv4::Repr<'a>),

+ 2 - 0
src/wire/icmpv4.rs

@@ -167,6 +167,7 @@ enum_with_unknown! {
 
 /// A read/write wrapper around an Internet Control Message Protocol version 4 packet buffer.
 #[derive(Debug, PartialEq, Clone)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Packet<T: AsRef<[u8]>> {
     buffer: T
 }
@@ -366,6 +367,7 @@ impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
 
 /// A high-level representation of an Internet Control Message Protocol version 4 packet header.
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 #[non_exhaustive]
 pub enum Repr<'a> {
     EchoRequest {

+ 2 - 0
src/wire/icmpv6.rs

@@ -191,6 +191,7 @@ impl fmt::Display for TimeExceeded {
 
 /// A read/write wrapper around an Internet Control Message Protocol version 6 packet buffer.
 #[derive(Debug, PartialEq, Clone)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Packet<T: AsRef<[u8]>> {
     pub(super) buffer: T
 }
@@ -503,6 +504,7 @@ impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
 
 /// A high-level representation of an Internet Control Message Protocol version 6 packet header.
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 #[non_exhaustive]
 pub enum Repr<'a> {
     DstUnreachable {

+ 3 - 0
src/wire/igmp.rs

@@ -23,6 +23,7 @@ enum_with_unknown! {
 
 /// A read/write wrapper around an Internet Group Management Protocol v1/v2 packet buffer.
 #[derive(Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Packet<T: AsRef<[u8]>> {
     buffer: T,
 }
@@ -171,6 +172,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
 
 /// A high-level representation of an Internet Group Management Protocol v1/v2 header.
 #[derive(Debug, PartialEq, Eq, Clone)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum Repr {
     MembershipQuery {
         max_resp_time: Duration,
@@ -188,6 +190,7 @@ pub enum Repr {
 
 /// Type of IGMP membership report version
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum IgmpVersion {
     /// IGMPv1
     Version1,

+ 34 - 0
src/wire/ip.rs

@@ -10,6 +10,7 @@ use crate::wire::{Ipv6Address, Ipv6Cidr, Ipv6Packet, Ipv6Repr};
 
 /// Internet protocol version.
 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 #[non_exhaustive]
 pub enum Version {
     Unspecified,
@@ -260,6 +261,19 @@ impl fmt::Display for Address {
     }
 }
 
+#[cfg(feature = "defmt")]
+impl defmt::Format for Address {
+    fn format(&self, f: defmt::Formatter) {
+        match self {
+            &Address::Unspecified     => defmt::write!(f, "{:?}", "*"),
+            #[cfg(feature = "proto-ipv4")]
+            &Address::Ipv4(addr)      => defmt::write!(f, "{:?}", addr),
+            #[cfg(feature = "proto-ipv6")]
+            &Address::Ipv6(addr)      => defmt::write!(f, "{:?}", addr),
+        }
+    }
+}
+
 /// A specification of a CIDR block, containing an address and a variable-length
 /// subnet masking prefix length.
 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
@@ -370,6 +384,18 @@ impl fmt::Display for Cidr {
     }
 }
 
+#[cfg(feature = "defmt")]
+impl defmt::Format for Cidr {
+    fn format(&self, f: defmt::Formatter) {
+        match self {
+            #[cfg(feature = "proto-ipv4")]
+            &Cidr::Ipv4(cidr)      => defmt::write!(f, "{:?}", cidr),
+            #[cfg(feature = "proto-ipv6")]
+            &Cidr::Ipv6(cidr)      => defmt::write!(f, "{:?}", cidr),
+        }
+    }
+}
+
 /// An internet endpoint address.
 ///
 /// An endpoint can be constructed from a port, in which case the address is unspecified.
@@ -430,6 +456,13 @@ impl fmt::Display for Endpoint {
     }
 }
 
+#[cfg(feature = "defmt")]
+impl defmt::Format for Endpoint {
+    fn format(&self, f: defmt::Formatter) {
+        defmt::write!(f, "{:?}:{=u16}", self.addr, self.port);
+    }
+}
+
 impl From<u16> for Endpoint {
     fn from(port: u16) -> Endpoint {
         Endpoint { addr: Address::Unspecified, port }
@@ -448,6 +481,7 @@ impl<T: Into<Address>> From<(T, u16)> for Endpoint {
 /// high-level representation for some IP protocol version, or an unspecified representation,
 /// which permits the `IpAddress::Unspecified` addresses.
 #[derive(Debug, Clone, PartialEq, Eq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 #[non_exhaustive]
 pub enum Repr {
     Unspecified {

+ 16 - 0
src/wire/ipv4.rs

@@ -112,6 +112,13 @@ impl fmt::Display for Address {
     }
 }
 
+#[cfg(feature = "defmt")]
+impl defmt::Format for Address {
+    fn format(&self, f: defmt::Formatter) {
+        defmt::write!(f, "{=u8}.{=u8}.{=u8}.{=u8}", self.0[0], self.0[1], self.0[2], self.0[3])
+    }
+}
+
 /// A specification of an IPv4 CIDR block, containing an address and a variable-length
 /// subnet masking prefix length.
 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
@@ -227,8 +234,16 @@ impl fmt::Display for Cidr {
     }
 }
 
+#[cfg(feature = "defmt")]
+impl defmt::Format for Cidr {
+    fn format(&self, f: defmt::Formatter) {
+        defmt::write!(f, "{:?}/{=u8}", self.address, self.prefix_len);
+    }
+}
+
 /// A read/write wrapper around an Internet Protocol version 4 packet buffer.
 #[derive(Debug, PartialEq, Clone)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Packet<T: AsRef<[u8]>> {
     buffer: T
 }
@@ -552,6 +567,7 @@ impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
 
 /// A high-level representation of an Internet Protocol version 4 packet header.
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Repr {
     pub src_addr:    Address,
     pub dst_addr:    Address,

+ 4 - 0
src/wire/ipv6.rs

@@ -17,6 +17,7 @@ pub const MIN_MTU: usize = 1280;
 
 /// A sixteen-octet IPv6 address.
 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Address(pub [u8; 16]);
 
 impl Address {
@@ -267,6 +268,7 @@ impl From<ipv4::Address> for Address {
 /// A specification of an IPv6 CIDR block, containing an address and a variable-length
 /// subnet masking prefix length.
 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Cidr {
     address:    Address,
     prefix_len: u8,
@@ -328,6 +330,7 @@ impl fmt::Display for Cidr {
 
 /// A read/write wrapper around an Internet Protocol version 6 packet buffer.
 #[derive(Debug, PartialEq, Clone)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Packet<T: AsRef<[u8]>> {
     buffer: T
 }
@@ -593,6 +596,7 @@ impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
 
 /// A high-level representation of an Internet Protocol version 6 packet header.
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Repr {
     /// IPv6 address of the source node.
     pub src_addr:    Address,

+ 2 - 0
src/wire/ipv6fragment.rs

@@ -7,6 +7,7 @@ pub use super::IpProtocol as Protocol;
 
 /// A read/write wrapper around an IPv6 Fragment Header.
 #[derive(Debug, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Header<T: AsRef<[u8]>> {
     buffer: T
 }
@@ -158,6 +159,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> {
 
 /// A high-level representation of an IPv6 Fragment header.
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Repr {
     /// The type of header immediately following the Fragment header.
     pub next_header: Protocol,

+ 2 - 0
src/wire/ipv6hopbyhop.rs

@@ -6,6 +6,7 @@ pub use super::IpProtocol as Protocol;
 
 /// A read/write wrapper around an IPv6 Hop-by-Hop Options Header.
 #[derive(Debug, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Header<T: AsRef<[u8]>> {
     buffer: T
 }
@@ -157,6 +158,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> {
 
 /// A high-level representation of an IPv6 Hop-by-Hop Options header.
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Repr<'a> {
     /// The type of header immediately following the Hop-by-Hop Options header.
     pub next_header: Protocol,

+ 3 - 0
src/wire/ipv6option.rs

@@ -58,6 +58,7 @@ impl From<Type> for FailureType {
 
 /// A read/write wrapper around an IPv6 Extension Header Option.
 #[derive(Debug, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Ipv6Option<T: AsRef<[u8]>> {
     buffer: T
 }
@@ -214,6 +215,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Ipv6Option<&'a T> {
 
 /// A high-level representation of an IPv6 Extension Header Option.
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 #[non_exhaustive]
 pub enum Repr<'a> {
     Pad1,
@@ -278,6 +280,7 @@ impl<'a> Repr<'a> {
 
 /// A iterator for IPv6 options.
 #[derive(Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Ipv6OptionsIterator<'a> {
     pos: usize,
     length: usize,

+ 2 - 0
src/wire/ipv6routing.rs

@@ -51,6 +51,7 @@ impl fmt::Display for Type {
 
 /// A read/write wrapper around an IPv6 Routing Header buffer.
 #[derive(Debug, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Header<T: AsRef<[u8]>> {
     buffer: T
 }
@@ -389,6 +390,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> {
 
 /// A high-level representation of an IPv6 Routing Header.
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 #[non_exhaustive]
 pub enum Repr<'a> {
     Type2 {

+ 2 - 0
src/wire/mld.rs

@@ -166,6 +166,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
 
 /// A read/write wrapper around an MLDv2 Listener Report Message Address Record.
 #[derive(Debug, PartialEq, Clone)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct AddressRecord<T: AsRef<[u8]>> {
     buffer: T
 }
@@ -295,6 +296,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> AddressRecord<T> {
 
 /// A high-level representation of an MLDv2 packet header.
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum Repr<'a> {
     Query {
         max_resp_code: u16,

+ 3 - 0
src/wire/ndisc.rs

@@ -10,6 +10,7 @@ use crate::time::Duration;
 use crate::wire::Ipv6Address;
 
 bitflags! {
+    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
     pub struct RouterFlags: u8 {
         const MANAGED = 0b10000000;
         const OTHER   = 0b01000000;
@@ -17,6 +18,7 @@ bitflags! {
 }
 
 bitflags! {
+    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
     pub struct NeighborFlags: u8 {
         const ROUTER    = 0b10000000;
         const SOLICITED = 0b01000000;
@@ -189,6 +191,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
 
 /// A high-level representation of an Neighbor Discovery packet header.
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum Repr<'a> {
     RouterSolicit {
         lladdr: Option<EthernetAddress>

+ 5 - 0
src/wire/ndiscoption.rs

@@ -36,6 +36,7 @@ impl fmt::Display for Type {
 }
 
 bitflags! {
+    #[cfg_attr(feature = "defmt", derive(defmt::Format))]
     pub struct PrefixInfoFlags: u8 {
         const ON_LINK  = 0b10000000;
         const ADDRCONF = 0b01000000;
@@ -46,6 +47,7 @@ bitflags! {
 ///
 /// [NDISC Option]: https://tools.ietf.org/html/rfc4861#section-4.6
 #[derive(Debug, PartialEq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct NdiscOption<T: AsRef<[u8]>> {
     buffer: T
 }
@@ -393,6 +395,7 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for NdiscOption<&'a T> {
 }
 
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct PrefixInformation {
     pub prefix_len: u8,
     pub flags: PrefixInfoFlags,
@@ -402,6 +405,7 @@ pub struct PrefixInformation {
 }
 
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct RedirectedHeader<'a> {
     pub header: Ipv6Repr,
     pub data: &'a [u8]
@@ -409,6 +413,7 @@ pub struct RedirectedHeader<'a> {
 
 /// A high-level representation of an NDISC Option.
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum Repr<'a> {
     SourceLinkLayerAddr(EthernetAddress),
     TargetLinkLayerAddr(EthernetAddress),

+ 1 - 0
src/wire/pretty_print.rs

@@ -34,6 +34,7 @@ use core::marker::PhantomData;
 
 /// Indentation state.
 #[derive(Debug)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct PrettyIndent {
     prefix: &'static str,
     level:  usize

+ 5 - 0
src/wire/tcp.rs

@@ -11,6 +11,7 @@ use crate::wire::ip::checksum;
 /// A sequence number is a monotonically advancing integer modulo 2<sup>32</sup>.
 /// Sequence numbers do not have a discontiguity when compared pairwise across a signed overflow.
 #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct SeqNumber(pub i32);
 
 impl fmt::Display for SeqNumber {
@@ -67,6 +68,7 @@ impl cmp::PartialOrd for SeqNumber {
 
 /// A read/write wrapper around a Transmission Control Protocol packet buffer.
 #[derive(Debug, PartialEq, Clone)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Packet<T: AsRef<[u8]>> {
     buffer: T
 }
@@ -550,6 +552,7 @@ impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
 
 /// A representation of a single TCP option.
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum TcpOption<'a> {
     EndOfList,
     NoOperation,
@@ -702,6 +705,7 @@ impl<'a> TcpOption<'a> {
 
 /// The possible control flags of a Transmission Control Protocol packet.
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub enum Control {
     None,
     Psh,
@@ -731,6 +735,7 @@ impl Control {
 
 /// A high-level representation of a Transmission Control Protocol packet.
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Repr<'a> {
     pub src_port:     u16,
     pub dst_port:     u16,

+ 2 - 0
src/wire/udp.rs

@@ -8,6 +8,7 @@ use crate::wire::ip::checksum;
 
 /// A read/write wrapper around an User Datagram Protocol packet buffer.
 #[derive(Debug, PartialEq, Clone)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Packet<T: AsRef<[u8]>> {
     buffer: T
 }
@@ -199,6 +200,7 @@ impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
 
 /// A high-level representation of an User Datagram Protocol packet.
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
 pub struct Repr<'a> {
     pub src_port: u16,
     pub dst_port: u16,