NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
crates.io · #3349 most downloaded on crates.io
Linux SocketCAN library. Send and receive CAN frames via CANbus on Linux.
Last release 1 months ago
05 Sep 2026
Ships unpredictably
gaps range from 9 days to 5.8 years
Some releases are documented
notes for 13 of 24 stable releases
2 versions withdrawn
withdrawn after publishing
10 years old
26 releases · first in 2016
Breaking: removed the API that 3.x deprecated for this release. The free socket::set_socket_option() and set_socket_option_mult() functions (deprecate…
serde support for serializing and deserializing frames and other data types.netlink caabilitiesasync-std which is no longer maintained.neli and claplibudev 0.3 dependency with udev 0.9 for interface enumeration (enumerate feature).
udev reports errors as io::Error, so the bespoke From<libudev::Error> for Error conversion was removedsocket2 to v0.6
From<CanAddr> for SockAddr now fills socket2's new SockAddrStorage wrapper typenix to v0.31 (no API changes required) and dropped the unused process feature (only poll and net are used)itertools dependency. Its only use was joining a frame's payload bytes into a hex string when formatting a candump record, which now writes them straight to the formatter, as the Frame hex formatting already did — one allocation per byte fewer, and no dependencyserial_test dev-dependency to v3.5 (no API changes required)futures-timer dev-dependency to v3.0. Its Delay future now yields () instead of io::Result<()>, so the .await? in the tokio_send/smol_send examples became .awaitdocs.rs builds the documentation with all features again. v3.6.2 pinned it to a subset — features = ["netlink", "dump", "enumerate", "utils", "tokio"] — because building the async-io/async-std/smol features failed on the nightly compiler that docs.rs uses (#102). Direct async-std support is gone in 4.0 and the remaining smol path builds cleanly on current nightly, so all-features = true and the TODO it carried are restoredsocket::set_socket_option() and set_socket_option_mult() functions (deprecated in 3.4.0) are gone — use the identically-named SocketOptions trait methods; dump::Reader::records() (3.5.0) is gone, along with the CanDumpRecords iterator it was the only way to construct — use Reader's own Iterator impl, which yields a whole CanDumpRecord rather than a (u64, CanAnyFrame) pair; and CanInterface::set_full_ctrlmode() (3.2.0) is gone — use set_ctrlmodes()SOF_TIMESTAMPING_* flags are no longer re-exported from the crate root. They live with the rest of the timestamping API, in socketcan::timestamp, alongside CanTimestamps (which stays at the root as well) and the timespec converters. Import them from there: use socketcan::timestamp::SOF_TIMESTAMPING_RX_SOFTWARE;SOF_TIMESTAMPING_* values now come from libc rather than being repeated here. libc has carried the full set since 0.2.186; this crate names the subset it documents, re-typed from c_uint to u32 to match SocketOptions::set_timestamping(), and each keeps the note explaining what it selects. The ethtool_ts_info mirror and ETHTOOL_GET_TS_INFO are still absent upstream and stay defined hereframe module no longer re-exports the id module's contents (CAN_EFF_FLAG, CAN_MAX_DLEN, IdFlags, FdFlags, id_to_canid_t, …), and socket no longer re-exports CanAddr. Both blocks were marked in the source as "remove on the next major version". Import them from their own modules instead — socketcan::id::FdFlags, socketcan::id::ERR_MASK_ALL — or, for CanAddr, from the crate root, which re-exports itCanRawFrame now derives Debug, like every other frame type. It could not before: the libc frame structs only implement Debug with the extra_traits feature, which this crate now enablesSocketOptions::set_error_mask() is now a thin alias for set_error_filter() rather than a second copy of the same setsockopt call. Both names stay, since the mask spelling reads naturally next to ERR_MASK_ALL/ERR_MASK_NONEAsPtr::as_bytes and AsPtr::as_bytes_mut trait methods are now unsafe fn with a documented # Safety contract (reading uninitialised padding through the returned byte slice is UB). This completes the soundness fix begun in 3.6.0 (which marked the free as_bytes/as_bytes_mut helpers unsafe). Callers of these trait methods must wrap calls in unsafeserde feature (non-default) for serializing frames, errors, and interface configuration
CanFrame, CanAnyFrame, CanDataFrame, CanRemoteFrame, CanErrorFrame, CanFdFrame), CanId, IdFlags/FdFlags, CanFilter, CanTimestamps, dump::CanDumpRecord, every error type, and the netlink configuration types (InterfaceDetails, InterfaceCanParams, CanBitTiming, CanCtrlModes, Mtu, CanState, …)can_frame/canfd_frame, which have no serde impls, so they convert through logical repr types (CanDataFrameRepr and friends). The raw C structs are deliberately never serialized: they contain padding and possibly-uninitialised bytes, and their layout is not portable. Deserializing routes back through the normal constructors, so an out-of-range identifier or an over-long payload is rejected rather than producing a malformed frameserialize_bytes, so formats with a native byte-string type (MessagePack, CBOR, bincode) use it instead of a sequence of integers. JSON, which has no byte-string type, renders a byte arrayCanError serializes as a flat array of its causes rather than exposing its internal storage, and deserializing an empty array is an error — the non-empty invariant holds across serdeIdFlags/FdFlags serialize as flag-name strings ("BRS | FDF") via bitflags' serde supportError::Can round-trips exactly. An io::Error is lossy by necessity, since it implements neither serde trait: it is reduced to an ErrorKind name plus the message. After a round trip raw_os_error() is None, any source() chain is gone, and a kind with no stable name (or one a newer version introduced) arrives as ErrorKind::Other. This applies both to Error::Io and to the Io variant of a dump::ParseError nested in Error::Parser; the parser's other variants round-trip exactlyOption fields on the netlink configuration types are omitted rather than written as nulls, and are optional on input, so a config file need only carry the parameters you care aboutdump module. Previously neither worked — the parser rejected an error line outright (the error-class bits sit above CAN_EFF_MASK, so decoding the ID failed), while Display wrote the ID with CAN_ERR_FLAG stripped, so its own output re-parsed as a standard data frame
CAN_ERR_FLAG before decoding the identifier. An eight-digit ID field is ambiguous — extended data frame or error frame — and that bit is the only thing distinguishing them, which is how can-utils resolves it tooDisplay now emits the ID with CAN_ERR_FLAG included, so a line round-trips exactly. Confirmed against real candump -L outputCanError decoding, so a multi-condition error line decodes fullydump module docs now state the full grammar, cite the can-utils parse_canframe() comment as the source of truth, and cross-reference doc/CanDumpLogFormat.md_len8_dlc suffix (123#1122334455667788_E), which is still rejected with ParseError::InvalidCanFramecandump -x appends to each line — R or T, which way the frame went on the interface that logged it — is now parsed and emitted. CanDumpRecord gains a direction: Option<Direction> — breaking for anyone constructing the record as a struct literal — None for a log captured without the flag, and a line carrying the field round-trips with it. Based on #94 by @isikkema-ornl, re-done against the rewritten dump module
ParseError::InvalidFrameDirection rather than ignored, which is what the parser did with any fourth field before. Lowercase is accepted, although candump writes only uppercaseDirection serializes by name, and the record's field is omitted entirely when it is NoneDirection implements FromStr and Display, so the field converts both ways on its own, and FromStr reports ParseError::InvalidFrameDirection directly rather than an opaque unit errordump module docs and in doc/CanDumpLogFormat.md gains the field, with a section on what candump writes, examples captured from candump -L -x, and a note that canplayer accepts a line carrying itErrorCause type carrying one condition: TransmitTimeout, LostArbitration(u8), Controller(ControllerProblems), Protocol { types, location }, Transceiver { canh, canl }, NoAck, BusOff, BusError, Restarted, Counters { tx, rx }, DecodingFailure and Unknown(u32). It is #[non_exhaustive], since future kernels may define more error class bitsCanError is now the whole decoded frame: one error with a non-empty list of causes, one per class bit set in the frame's CAN ID. It has new(), from_multiple(), from_iter_checked(), first(), last(), len(), is_single(), causes(), contains_kind(), both IntoIterator impls, Display and embedded_can::Errorlost_arbitration(), controller(), protocol(), transceiver(), counters()) and predicates (is_transmit_timeout(), is_no_ack(), is_bus_off(), is_bus_error(), is_restarted(), has_counters()) reach a specific cause without matching over the list by handError::Can still carries a CanError, CanErrorFrame::into_error() keeps its name, and From<CanErrorFrame> for CanError still exists — all three now yield every condition the frame reported rather than a single oneUnknown(bits) and the whole error was lost. For example an mcp251xfd bus-error frame (CAN_ERR_PROT | CAN_ERR_BUSERROR | CAN_ERR_ACK) decoded to Unknown(0xA8); it now decodes to three causes, the protocol one naming all five violation types it reportedUnknown cause rather than being droppedControllerProblem and ViolationType enums (one variant per bit) are replaced by the bitflags types ControllerProblems and ViolationTypes, so data[1] and data[2] each decode to one cause carrying every condition the byte reports
can_change_state() helper ORs both the TX and RX state codes into data[1] whenever the two states match, so data[1] = 0x0C is the normal encoding of a symmetric warning transition — it used to decode to DecodingFailure, and now yields Controller(RX_WARNING | TX_WARNING)bitflags API: all(), bits(), contains(), iter() and the set operatorsControllerProblem::Active is now the ControllerProblems::ACTIVE flag, which displays as "back to error active". The kernel means "recovered to error-active state", which the old name read as the oppositefrom_bits_retain() can build such a set — the decoder truncates unknown bits — but the rendering no longer depends on thatErrorCause::Counters { tx, rx } covers CAN_ERR_CNT frames, which were previously dropped entirely. Added CAN_ERROR_WARNING_THRESHOLD, CAN_ERROR_PASSIVE_THRESHOLD and CAN_BUS_OFF_THRESHOLD re-exports for interpreting the counter valuesTransceiverError enum is replaced by ErrorCause::Transceiver { canh: Option<CanHighFault>, canl: Option<CanLowFault> }, decoded from data[4] — which was previously never read at all, leaving the whole TransceiverError enum unreachable. data[4] is two independent nibbles (CAN High in the low half, CAN Low in the high half), so a fault on both lines is one cause naming bothLocation gains the five codes that real controllers emit but linux/can/error.h does not name (ActiveErrorFlag, TolerateDominantBits, PassiveErrorFlag, ErrorDelimiter, OverloadFlag) — the sja1000 driver copies the raw 5-bit error-code-capture segment straight into data[3]. Unknown codes are now preserved as Location::Reserved(u8), so decoding a location cannot fail; TryFrom<u8> for Location is replaced by the infallible Location::from_raw()/as_raw()CanErrorDecodingFailure variants that no code path could produce: NotAnError, UnknownErrorType, NotEnoughData (leftovers from an older decoder) and InvalidLocation (now unreachable, per the above)Error gains a Parser variant (feature dump) carrying a dump::ParseError, replacing the From<ParseError> for Error conversion that flattened every parse error into an io::Error of kind InvalidData, keeping only its message. Parse errors now keep their identity, and ? still lifts them into socketcan::ErrorError gains an Nl variant (feature netlink) carrying the new nl::NlError, replacing the From<RouterError<T, P>> for Error conversion that flattened every netlink failure into an io::Error of kind Other holding nothing but the message text
NlError keeps what a caller can act on: Netlink { errno } for an error packet from the kernel — with errno() and io_kind() accessors, so a privileged operation refused to a normal user tests as PermissionDenied — plus NoAck, UnexpectedAck, BadSeqOrPid { seq, pid }, ClosedChannel, and Msg(String) for the message-level failures that have no structure worth keepingError::Io with their original ErrorKind, and with their errno where neli's socket layer had one, instead of arriving as kind OtherNlError is an owned, non-generic summary rather than the neli error itself. RouterError<T, P> is generic over the message type and payload and is 128 bytes wide, so carrying it would both put neli into this crate's public API — making a neli major bump breaking for downstream — and grow every Result<_, Error> from the current 48 bytes to matchio::Error nor any neli typeFrom<neli::err::RouterError<T, P>> for NlError, so code holding a neli error can reduce it directly. The crate-level From<…> for Error conversions only decide the split, keeping a genuine I/O failure as Error::Io and handing everything netlink-shaped to NlErrorCanInterface method now returns the crate-level Result instead of RouterInfoResult (that is, Result<_, neli::err::RouterError<Rtm, Ifinfomsg>>), and delete() returns Result<(), (Self, Error)>. Callers that matched a RouterError now match Error::Nl(NlError) or Error::Io
nl module: the RouterInfoError and RouterInfoResult aliases are gone, and the TryFrom impls that converted a neli Rtattr/RtBuffer to and from InterfaceCanParams are now the internal InterfaceCanParams::from_link_info() and InterfaceCanParams::to_rtbuffer(). What remains are the From<…> for Error impls for neli's error types, which is what lets ? convert at the boundary#![allow(clippy::result_large_err)] and its TODO are goneCanInterface::open() returns the crate-level Result as well, rather than Result<Self, nix::Error>. Together with the change above, that leaves no foreign error type anywhere in the nl module's API — every function there reports socketcan::Error
From<nix::Error> for Error, mapping an errno from a nix call onto Error::Io. Nothing is lost, since nix::Error is an errno: opening an unknown interface still reports ENODEV, now reachable through io::Error::raw_os_error() and kind() like any other system errorNo such device (os error 19) where nix would have written ENODEV: No such deviceErrorCause::kind() now maps protocol violations onto the specific embedded_can::ErrorKind values Bit, Form and Stuff rather than reporting everything as Other. CanError::kind() scans its causes and returns the first specific kind present, so a frame carrying both a controller warning and a missing ACK reports AcknowledgeCanError's Display joins its causes with "; ". A protocol violation names its location once and lists every type reported there — protocol violation at CRC sequence: frame format error, bit stuffing error, …ErrorCause::Unknown(_) is now printed in hexcan_frame now normalizes the frame's length field, which nothing on those paths previously checked. A length above the eight bytes can_frame::data holds is clamped, so data(), Debug and UpperHex can no longer panic on a caller-built struct — From<can_frame> for CanFrame accepted one unconditionally, and the two TryFrom impls checked only the flag bits. From<canfd_frame> for CanFdFrame already did the equivalent
CanErrorFrame::try_from() already forced this; From<can_frame> for CanFrame did not, so an error frame built that way could report len() == 0 while data() returned eight bytesCanInterface::create() and create_vcan() treat a requested index of Some(0) as unspecified, the same as None. Index 0 is how netlink itself spells "let the kernel assign one", so taking it literally returned a CanInterface addressing interface 0, and every later call on that handle went to the wrong place. The assigned index is now looked up by name, as the None path always did, and how index is treated is documented on create()CanDataFrame::set_data() now zeroes the payload bytes it vacates, so shortening a frame's data no longer leaves part of the previous payload in the unused tail of the struct handed to the kernel. CanFdFrame::set_data() already did this. Nothing reached the bus either way — the kernel transmits len bytes — and data() never exposed the tail, but AsPtr::as_bytes() promises every byte of the frame is written, and now it isCanFdSocket::read_raw_frame() reports a read whose length is neither CAN_MTU nor CANFD_MTU as InvalidData, matching the typed read_frame(). It previously returned io::Error::last_os_error() after a successful read, so it reported a stale errno — commonly Success (os error 0), of kind UncategorizedShouldRetry::should_retry(), which had stopped recognizing EINPROGRESS. It tested for an io::ErrorKind::Other carrying that errno, but the stdlib now decodes it to ErrorKind::InProgress — a variant this crate cannot name, since it is still unstable — so the arm was dead and the errno read as a hard failure. It is now matched on the errno itself, which restores the retry in Socket::write_frame_insist() and the WouldBlock result from the non-blocking embedded_can::nb::Can methodsdump parser, which is 0..=8 and not the full 0..=F nibble that doc/CanDumpLogFormat.md implied: SocketCAN caps a classical frame's length at CAN_MAX_DLEN in both directions, so a line like 123#RF describes a frame that could not be transmitted, and candump never emits one. Behavior is unchanged — such a line is still rejected with ParseError::InvalidCanFrame, where can-utils discards the nibble and yields DLC 0CAN_ERR_CRTL, CAN_ERR_CNT, …) are re-exported from socketcan::errors, so building or inspecting error frames no longer needs a direct libc dependency. Internally the decoder now uses these constants instead of hardcoded hex literalsnonblocking example now demonstrates non-blocking I/O instead of contradicting it. It handled WouldBlock with nb::block!, which spins until a frame arrives — leaving the thread exactly where a blocking socket would have. It now matches on WouldBlock itself, does its own waiting between attempts, and gives up after a deadline, which is the thing a blocking read cannot do without SO_RCVTIMEO. The send path is handled the same way, for a full send bufferenumerate example now prints what each discovered interface reports — index, up/down and controller state, MTU, clock, bitrates, the enabled control modes and the supported-mode mask — from a single netlink query per interface. A field it cannot read shows -, which is the normal case for a vcan. It now needs the netlink feature alongside enumerateplaylog example now replays a candump log at the speed it was recorded, rather than firing every frame as fast as the socket accepts it. Pass --fast for the old behavior. It also replays error frames, which it previously dropped without a wordCanInterface::details() used to answer Ok(InterfaceDetails { name: None, is_up: false, .. }) for a nonexistent index — a plausible-looking record for an interface that was never there — and every parameter getter answered Ok(None). The kernel does reply NLMSG_ERROR with ENODEV; because the request asks for no ACK, neli returns that as a message whose payload is not an Ifinfomsg rather than as an error, and the missing payload read as "no parameters set". The reply's errno is now checked in one place, so all of these report Error::Nl(NlError::Netlink { errno })CAN_RAW and stay frame-shaped, but J1939 and ISO-TP sockets carry reassembled payloads, so implementing either belongs in a separate crate. The pieces such a crate needs are now public, and the Other CAN protocols section of the crate documentation shows the three steps that open and bind one
CanAddr gained accessors — ifindex(), j1939_name(), j1939_pgn(), j1939_addr(), tp_rx_id(), tp_tx_id() — so an address can be read back, in particular a recvfrom() peer, without the caller reaching into the can_addr union itself. The union has no discriminator, so reading the variant that was not written reinterprets bytes rather than failing; that is documented on j1939_name() and pinned by a test, alongside tests that read every field back through both the index and the from_iface_* constructorsloopback(), recv_own_msgs(), join_filters() and error_filter() (with error_mask() alongside it, mirroring set_error_mask()). Each asks the kernel rather than caching anything, so it reports what the socket actually holds — including on a socket configured elsewhere, which is what a crate wrapping a foreign fd needsSocketOptions grew a complete and safe option API: set_socket_option_int()/get_socket_option_int() for the scalar case that covers nearly every CAN option — CAN_RAW_LOOPBACK, CAN_RAW_RECV_OWN_MSGS, CAN_RAW_FD_FRAMES, CAN_RAW_JOIN_FILTERS, the J1939 options — and set_socket_option_bytes()/get_socket_option_bytes() for one that is a struct or a list. None of the four is generic, and none needs unsafe: a c_int has no padding, and raw bytes carry no layout assumption. The setsockopt() call itself, including the empty-buffer case that clears an option with a null pointer and length zero, now lives in one placeset_socket_option() and set_socket_option_mult() are now unsafe fn. Sending a value as its raw bytes means reading every byte of T, which is undefined behaviour for a type with padding, so that requirement is stated as a # Safety contract rather than left implicit. A caller either wraps the call in unsafe or sidesteps it — set_socket_option_int(level, name, value) covers the common case. Within the crate only the two filter-list setters, which pass a slice of structs, still need ittimestamp::timespec_to_system_time() and timespec_to_duration() are public, for code parsing SCM_TIMESTAMPNS/SCM_TIMESTAMPING control messages off its own recvmsg(); CanTimestamps documents how to fill one in that waylinux/can.h and SOL_CAN_J1939 are re-exported from socket, documented with which of them a socket can actually be opened for — CAN_RAW, CAN_BCM, CAN_ISOTP and CAN_J1939 — versus CAN_TP16, CAN_TP20 and CAN_MCNET, which are reserved in the header with no in-tree implementation and report EPROTONOSUPPORT, and CAN_NPROTO, which is the count of protocol numbers rather than one of themJ1939_NO_NAME, J1939_NO_PGN, J1939_NO_ADDR, J1939_IDLE_ADDR and J1939_MAX_UNICAST_ADDR are available from addr. They are defined rather than re-exported, each typed for the CanAddr::new_j1939() parameter it belongs to: libc types J1939_NO_NAME as a c_ulong, which is 32 bits wide on a 32-bit target although the kernel field is a __u64, so a re-export would force a cast on some targets and warn about a needless one on othersserde feature is listed in the crate-level feature documentation, which had omitted itCanInterface::can_param() and set_can_param() are now internal, replaced by can_param_bytes() and set_can_param_bytes(). The generic pair was public in name only — both take an IflaCan, a type in a private module that no caller outside the crate could name, so neither was callable. Their bounds were neli's FromBytes/ToBytes/Size as well, so making the type public would have put neli back into this crate's API, which the rest of 4.0 worked to avoid
IFLA_CAN_* number — libc defines every one — and exchange the payload as bytes, so the escape hatch for an attribute this crate does not model stays open with no neli in any public signature. A nested attribute comes back as the whole nest. The typed accessors, and can_params() for all of them at once, remain the everyday pathCanInterface::supported_ctrlmodes() (feature netlink), reporting which control modes the driver supports as a mask of CAN_CTRLMODE_* bits — the capability question ctrlmodes() cannot answer, since that reports what is currently enabled. The mask also appears as InterfaceCanParams::ctrl_mode_supported, so can_params() and details() carry it too. None when the driver does not report it: the kernel has sent IFLA_CAN_CTRLMODE_EXT since 6.0, and a vcan never does
NLA_F_NESTED in the type field, so IFLA_CAN_CTRLMODE_EXT arrives as 0x8011 rather than 17 and never matched. The attribute type is now masked before it is matched, which also readies the parser for any other nested attribute0x01AE — LISTENONLY | 3_SAMPLES | ONE_SHOT | FD | FD_NON_ISO | CC_LEN8_DLC. tests/cansocket.rs gains a hardware test, opt-in through SOCKETCAN_FD_IFACE, that skips when the variable is unsetCanInterface::can_params() (feature netlink), which reads every CAN parameter of an interface in one netlink round trip and returns them as an InterfaceCanParams. Each individual getter — bit_timing(), state(), ctrlmodes(), restart_ms(), … — opens its own netlink socket and exchanges a message, so reading several of them cost that many round trips, even though the kernel's reply to a single RTM_GETLINK already carries the whole set. The getters now say so in their documentation and point at this method; details() documents that it is the same single query plus the interface's name, index, flags and MTUCanInterface::ctrlmodes() getter (feature netlink) to pair with set_ctrlmodes(), returning the kernel-reported control-mode bits as Option<CanCtrlModes>CanInterface::set_data_bitrate() now has the same debug-build sanity checks as set_bitrate() (bitrate and sample-point range), with an FD-appropriate upper bound of 8 Mbit/s for the data phaseOne column per quarter.
Restored documentation to docs.rs by omitting async-io features, which no longer build under nightly.
Restored documentation to docs.rs by omitting async-io features, which no longer build under nightly.
Fixed broken build in v3.6.0 for musl targets
Fixed broken build in v3.6.0 for musl targets
Added ability to get timestamp for received frames
Timestamps & lots of fixes!
CanTimestamps type carrying socket-layer, network-stack software, and hardware receive timestampsSocketOptions::set_recv_timestamp (SO_TIMESTAMPNS) and SocketOptions::set_timestamping (SO_TIMESTAMPING) to enable delivery on the socketSocket::read_frame_with_timestamp, Socket::read_frame_with_timestamps, and Socket::read_frame_with_hw_timestamp on the Socket trait (default implementations return ENOSYS to preserve semver for out-of-tree Socket implementors)CanSocket::has_hw_timestamps / CanFdSocket::has_hw_timestamps query interface capability via ETHTOOL_GET_TS_INFOSOF_TIMESTAMPING_* flag constants from the crate rootrecvmsg() call, eliminating the race window of the old SIOCGSTAMPNS approachtokio::CanSocket/CanFdSocket and async_io::CanSocket/CanFdSocket wrappersasync_io::CanSocket and async_io::CanFdSocket gained open_if(ifindex: u32) and open_addr(&CanAddr) constructors (parity with the tokio wrappers, which previously had all three)async_io::CanSocket and async_io::CanFdSocket now implement futures::Stream (yielding Result<CanFrame> / Result<CanAnyFrame>) and futures::Sink (over CanFrame / CanAnyFrame), parity with the tokio wrappers. The async-io, async-std, and smol features now pull in futures (previously it was wired in only via the tokio feature)async_io::CanSocket and async_io::CanFdSocket gained try_read_frame() and try_write_frame() methods, parity with the tokio wrappers (added in #84). Both return WouldBlock when no frame is available / send buffer is full and go straight to the underlying non-blocking fd (bypassing the async-io reactor); mixing with the async-path methods is safetokio_recvts — tokio mirror of can_recvts, prints software and hardware timestamps alongside each framePartialEq, Eq, and Hash — both the concrete frame structs (CanDataFrame, CanRemoteFrame, CanErrorFrame, CanFdFrame) and the wrapper enums (CanFrame, CanAnyFrame, CanRawFrame). Equality is field-wise on the underlying libc::can_frame / libc::canfd_frame, which means it includes every byte of the structure (id, dlc, flags, the libc __pad/__res0 fields, and the full data array). Note that set_data does not zero the unused trailing bytes of can_frame::data, so two semantically-equivalent frames built by different code paths may still compare unequal — callers should treat equality as "byte-identical wire image" rather than "same logical frame".extra_traits feature on the libc dependency so the trait derives can flow through (libc::can_frame / canfd_frame only derive(PartialEq, Eq, Hash) when that feature is on).recvmsg() ancillary control buffer is now properly aligned and validated; MSG_TRUNC/MSG_CTRUNC handled correctlytimespec_to_duration no longer wraps on a negative tv_sec in release buildsFrom<canfd_frame> for CanFdFrame normalises non-spec lengths so dlc() and data() stay consistent and no uninitialised bytes can leakTryFrom<can_frame> for CanErrorFrame forces can_dlc = CAN_MAX_DLEN so the len/dlc/data invariant holdsCanDataFrame::set_id and CanFdFrame::set_id preserve CAN_ERR_FLAG/CAN_RTR_FLAG bits in the ID wordCanId + u32 no longer panics on overflow in debug buildsAsPtr::as_bytes_mut now returns &mut [u8] instead of &[u8]rcan CLI no longer contains duplicate loopback subcommand armsexamples/can_recvts.rs now requests the full set of timestamp flags so software and hardware timestamps actually arriveexamples/fd_send.rs now sends an actual CAN FD framefmt::UpperHex on classic frames uses raw_id() (no flag-bit leakage), zero-pads the ID to 3 chars (SFF) / 8 chars (EFF), joins data bytes without spaces, and emits #R<dlc> for remote frames so the output matches candump's log formatfmt::UpperHex on CanFdFrame prints the FD flags as a single hex nibble between ## and the data bytes (no stray space)CanRemoteFrame::data() now returns &[] (spec-correct: remote frames carry only a DLC); use dlc() to read the requested lengthCanInterface::create rejects names of length IFNAMSIZ and above (off-by-one — IFNAMSIZ includes the trailing NUL)CAN_TERMINATION_DISABLED is now u16 (matches the rest of the termination API)From<libudev::Error> preserves the underlying description on the wrapped io::ErrorCanAddr gained hand-rolled PartialEq/Eq/Hash impls comparing (can_family, can_ifindex) only; deriving them would compare the can_addr union plus padding, which is unsoundCanAddr::Debug now renders the can_addr union bytes (J1939 / ISO-TP fields are no longer dropped)From<sockaddr_can> for CanAddr now debug_assert!s can_family == AF_CANavailable_interfaces() was silently ignoring udev errors and returning an empty list of interfaces. It now returns an error on udev failure.Sink::poll_close no longer attempts a spurious clear_ready(); Sink::start_send issues a single non-blocking write_frame() instead of busy-retrying via write_frame_insistset_socket_option_mult docdump::Reader caps each line at 64 KiB so a malformed or hostile log can't OOM the reader; over-long lines produce InvalidCanFramedump::Reader requires exactly six mantissa digits on the timestamp (real candump format), and uses checked arithmetic so an overflow errors instead of producing a wrong timestampdump::Reader propagates remote-frame DLC parse errors via InvalidCanFrame (previously silently coerced to 0); the DLC is now parsed as a hex nibble matching candump's R<X> formatdump::CanDumpRecord Display now emits parseable lines for error frames (<error_bits>#<8 hex bytes>) and FD frames (##<flag-nibble><bytes>), and zero-pads the ID width (3 hex for SFF, 8 hex for EFF) on all variantsError conversions:
From<neli::err::NlError<T, P>> (feature netlink) — netlink errors flow into the crate-level Error via io::Error::otherFrom<dump::ParseError> (feature dump) — dump-parse errors flow into Error via io::Error::new(InvalidData, …) (passing through I/O variants)Socket::read_frame documents concurrent-reader semantics (each &self reader sees a disjoint subset of frames)CanCtrlModes::has_mode documents that it inspects flags (kernel-reported state) and ignores pending mask bitsCanFdFrame::new_remote documents that CAN FD has no RTR by spec, so the method always returns Nonecrate::as_bytes / crate::as_bytes_mut helpers are now unsafe fn with a proper # Safety contract; call sites annotatedA major update to the dump module, with some usability improvements to frames and sockets.
A major update to the dump module, with some usability improvements to frames and sockets.
CanAnyFrame implements From trait for CanDataFrame, CanRemoteFrame, and CanErrorFrame.CanFdSocket implementa TryFrom trait for CanSocketdump module:
ParseError now implements std Error trait via thiserror::ErrorCanDumpRecord changes:
device field an owned StringClone and Display traits.
Display trait is compatible with the candump log record formatdump::Reader is now an Iterator itself, returning full CanDumpRecord itemsThis is a service release to publish a number of pull requests that have accumulated in the repository, including a number of bug fixes and improvemen
This is a service release to publish a number of pull requests that have accumulated in the repository, including a number of bug fixes and improvements on existing implementations.
CanAddr and related code into a new addr module.CanRawFrame encapsulatea either type of libc, raw, CAN frame (Classic or FD)Read and Write traits for CanSocketCanState publicCanFdSocket read_frame crash fixset_terminationCanInterface: add set_can_params() method to set multiple parametersitertools to v0.13, nix to v0.29, bitflags to v2.6, mio to v1#78 Fix memory error receiving CAN FD frames.
Serialized tokio unit tests and put them behind the "vcan_tests" feature
CanFD support for Tokio
Expanded Netlink functionality to configure and query the CAN interface.
Expanded Netlink functionality to configure and query the CAN interface.
InterfaceDetails to include CAN-specific parametersnelinl modulenl module into separate sources for higher and lower-level codeAdditional netlink implementation
Additional netlink implementation
All of tokio-socketcan has been merged into this crate and will be available with an async-tokio build feature.
Support for Rust async/await
async-tokio build feature.async-io for use with async-std and smolSocketOptions trait out of Socket trait for use with async (breaking)tokio or async-io.async-std and smol which just bring in the async-io module and alias the module name to async-std or smol, respectively, and build examples for each.Made CanAddr public and added functions to help interact with low-level sockaddr types. Sockets can now be opened with an address.
CanAddr public and added functions to help interact with low-level sockaddr types. Sockets can now be opened with an address.Error directly from a CanErrorFrame or std::io::ErrorKind.CanErrorFrame::new() now works.CanErrorFrame::new_error() is similar but more intuitive using a raw ID word.From<CanError> for CanErrorFrame to create an error frame from a CanError.Frame::from_raw_id() and Frame::remote_from_raw_id()Extensive rework of the crate to cleanup, refactor, and modernize the library and add some new features like CAN FD support.
Extensive rework of the crate to cleanup, refactor, and modernize the library and add some new features like CAN FD support.
CanFrame and/or CanFdFramelibc::can_framelibc::canfd_frameneli v0.6nix dependency to latest v0.23Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →