From d236271dfbe014e45f1b2e8894798ea887aa1f82 Mon Sep 17 00:00:00 2001 From: Willem Melching Date: Mon, 1 Apr 2024 12:30:53 +0200 Subject: [PATCH] [SocketCAN] read IFF_ECHO flag from interface --- src/can/adapter.rs | 2 +- src/socketcan/mod.rs | 58 ++++++++++++++++++++++++++++++------------ tests/adapter_tests.rs | 14 +++++----- tests/isotp_tests.rs | 2 +- tests/uds_tests.rs | 2 +- 5 files changed, 51 insertions(+), 27 deletions(-) diff --git a/src/can/adapter.rs b/src/can/adapter.rs index bcc8286..282acdd 100644 --- a/src/can/adapter.rs +++ b/src/can/adapter.rs @@ -9,7 +9,7 @@ pub fn get_adapter() -> Result #[cfg(target_os = "linux")] { // TODO: iterate over all available SocketCAN adapters to also find things like vcan0 - if let Ok(socket) = crate::socketcan::SocketCan::new_async_from_name("can0") { + if let Ok(socket) = crate::socketcan::SocketCan::new_async("can0") { return Ok(socket); } } diff --git a/src/socketcan/mod.rs b/src/socketcan/mod.rs index 09156a5..bf315e5 100644 --- a/src/socketcan/mod.rs +++ b/src/socketcan/mod.rs @@ -6,17 +6,45 @@ use crate::Result; use socketcan::socket::Socket; use socketcan::SocketOptions; use std::collections::VecDeque; -use tracing::info; mod frame; +const IFF_ECHO: u64 = 1 << 18; // include/uapi/linux/if.h + /// Aadapter for a [`socketcan::CanFdSocket`]. pub struct SocketCan { socket: socketcan::CanFdSocket, + + /// If the IFF_ECHO flag is set on the interface, it will implement proper ACK logic. If the flag is not set, the kernel will emulate this. + #[allow(dead_code)] + iff_echo: bool, +} + +fn read_iff_echo(if_name: &str) -> Option { + // Check IFF_ECHO. SIOCGIFADDR only returns the lower 16 bits of the interface flags, + // so we read the value from sysfs instead. Alternatively, we could use netlink, but + // that is probably not worth the complexity. + + let flags = std::fs::read_to_string(format!("/sys/class/net/{}/flags", if_name)).ok()?; + let flags = flags.trim(); + let flags = flags.strip_prefix("0x")?; + let flags = u64::from_str_radix(flags, 16).ok()?; + + Some(flags & IFF_ECHO != 0) } impl SocketCan { - pub fn new(socket: socketcan::CanFdSocket) -> Self { + pub fn new_async(name: &str) -> Result { + let socket = SocketCan::new(name)?; + Ok(AsyncCanAdapter::new(socket)) + } + + pub fn new(name: &str) -> Result { + let socket = match socketcan::CanFdSocket::open(name) { + Ok(socket) => socket, + Err(_) => return Err(crate::error::Error::NotFound), + }; + socket.set_nonblocking(true).unwrap(); socket.set_loopback(true).unwrap(); socket.set_recv_own_msgs(true).unwrap(); @@ -25,25 +53,23 @@ impl SocketCan { socket.as_raw_socket().set_recv_buffer_size(1_000_000).ok(); if let Ok(sz) = socket.as_raw_socket().recv_buffer_size() { - info!("SocketCAN receive buffer size {}", sz); + tracing::info!("SocketCAN receive buffer size {}", sz); } - Self { socket } - } + // Read IFF_ECHO flag from interface + let iff_echo = match read_iff_echo(name) { + Some(iff_echo) => iff_echo, + None => { + tracing::warn!("Could not read flags for interface. Assuming IFF_ECHO is not set."); + false + } + }; - pub fn new_async_from_name(name: &str) -> Result { - if let Ok(socket) = socketcan::CanFdSocket::open(name) { - SocketCan::new_async(socket) - } else { - Err(crate::error::Error::NotFound) + if !iff_echo { + tracing::warn!("IFF_ECHO is not set on the interface. ACK support is emulated by the Linux Kernel."); } - } - - pub fn new_async(socket: socketcan::CanFdSocket) -> Result { - let socket = SocketCan::new(socket); - info!("Connected to SocketCan"); - Ok(AsyncCanAdapter::new(socket)) + Ok(SocketCan { socket, iff_echo }) } } diff --git a/tests/adapter_tests.rs b/tests/adapter_tests.rs index 0e8ab26..d0a8e5d 100644 --- a/tests/adapter_tests.rs +++ b/tests/adapter_tests.rs @@ -82,8 +82,7 @@ async fn panda_bulk_send_async() { #[serial_test::serial] fn socketcan_bulk_send_sync() { use socketcan::Socket; - let socket = socketcan::CanFdSocket::open("can0").unwrap(); - let mut adapter = automotive::socketcan::SocketCan::new(socket); + let mut adapter = automotive::socketcan::SocketCan::new("can0").unwrap(); bulk_send_sync(&mut adapter); } @@ -91,7 +90,7 @@ fn socketcan_bulk_send_sync() { #[tokio::test] #[serial_test::serial] async fn socketcan_bulk_send_async() { - let adapter = automotive::socketcan::SocketCan::new_async_from_name("can0").unwrap(); + let adapter = automotive::socketcan::SocketCan::new_async("can0").unwrap(); bulk_send(&adapter).await; } @@ -99,7 +98,7 @@ async fn socketcan_bulk_send_async() { // #[tokio::test] // #[serial_test::serial] // async fn vcan_bulk_send_fd() { -// let adapter = automotive::socketcan::SocketCan::new_async_from_name("can0").unwrap(); +// let adapter = automotive::socketcan::SocketCan::new_async("can0").unwrap(); // adapter.send(&Frame::new(0, 0x123.into(), &[0u8; 64])).await; // } @@ -108,8 +107,7 @@ async fn socketcan_bulk_send_async() { #[serial_test::serial] fn vcan_bulk_send_sync() { use socketcan::Socket; - let socket = socketcan::CanFdSocket::open("vcan0").unwrap(); - let mut adapter = automotive::socketcan::SocketCan::new(socket); + let mut adapter = automotive::socketcan::SocketCan::new("vcan0").unwrap(); bulk_send_sync(&mut adapter); } @@ -117,7 +115,7 @@ fn vcan_bulk_send_sync() { #[tokio::test] #[serial_test::serial] async fn vcan_bulk_send_async() { - let adapter = automotive::socketcan::SocketCan::new_async_from_name("vcan0").unwrap(); + let adapter = automotive::socketcan::SocketCan::new_async("vcan0").unwrap(); bulk_send(&adapter).await; } @@ -125,7 +123,7 @@ async fn vcan_bulk_send_async() { #[tokio::test] #[serial_test::serial] async fn vcan_bulk_send_fd() { - let adapter = automotive::socketcan::SocketCan::new_async_from_name("vcan0").unwrap(); + let adapter = automotive::socketcan::SocketCan::new_async("vcan0").unwrap(); adapter .send(&Frame::new(0, 0x123.into(), &[0u8; 64]).unwrap()) .await; diff --git a/tests/isotp_tests.rs b/tests/isotp_tests.rs index 0dc9283..1a36a67 100644 --- a/tests/isotp_tests.rs +++ b/tests/isotp_tests.rs @@ -69,7 +69,7 @@ async fn vecu_spawn(adapter: &AsyncCanAdapter, config: VECUConfig) -> ChildGuard } async fn isotp_test_echo(msg_len: usize, config: VECUConfig) { - let adapter = automotive::socketcan::SocketCan::new_async_from_name("vcan0").unwrap(); + let adapter = automotive::socketcan::SocketCan::new_async("vcan0").unwrap(); let _vecu = vecu_spawn(&adapter, config).await; let mut isotp_config = IsoTPConfig::new(0, Identifier::Standard(0x7a1)); diff --git a/tests/uds_tests.rs b/tests/uds_tests.rs index 1426142..ed6e59f 100644 --- a/tests/uds_tests.rs +++ b/tests/uds_tests.rs @@ -33,7 +33,7 @@ async fn vecu_spawn(adapter: &AsyncCanAdapter) -> ChildGuard { #[tokio::test] #[serial_test::serial] async fn uds_test_sids() { - let adapter = automotive::socketcan::SocketCan::new_async_from_name("vcan0").unwrap(); + let adapter = automotive::socketcan::SocketCan::new_async("vcan0").unwrap(); let _vecu = vecu_spawn(&adapter).await; let mut isotp_config = IsoTPConfig::new(0, Identifier::Standard(0x7a1));