Wiki / Software

SocketCAN

The Linux kernel's CAN subsystem. It exposes a CAN bus as a network interface so ordinary sockets, tcpdump and Wireshark work on it, which is why every Linux CAN tool looks the way it does.

SocketCAN is the CAN implementation in the Linux kernel. Its defining decision is to model a CAN bus as a network interface rather than a character device, so a CAN controller appears as can0 alongside eth0, and you talk to it with the Berkeley socket API.

Why that design matters

Treating CAN as a network interface means the entire existing Linux networking toolchain applies for free:

  • ip link configures it, exactly like any other interface;
  • multiple programs can receive the same frames simultaneously, because the kernel demultiplexes, instead of one process owning the serial port;
  • tcpdump and wireshark capture it natively;
  • filtering happens in the kernel, so a program interested in three arbitration IDs is not woken for the other few thousand frames a second;
  • a virtual interface (vcan) behaves identically with no hardware at all, which is what makes CAN work testable and teachable.

The alternative designs (a vendor library over a USB serial protocol) give you one process, one tool, and a different API per adapter.

Bringing an interface up

sudo ip link set can0 type can bitrate 500000
sudo ip link set up can0
ip -details -statistics link show can0     # state, error counters, bitrate

The bitrate must match the bus exactly. Get it wrong and the controller floods error frames and eventually goes bus-off, which on a real vehicle bus is disruptive.

A virtual bus for development:

sudo modprobe vcan
sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0

Listen-only mode is the safe default when touching an unknown bus, because it prevents the controller from transmitting acknowledgement bits:

sudo ip link set can0 type can bitrate 500000 listen-only on

The socket API

int s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
struct sockaddr_can addr = { .can_family = AF_CAN, .can_ifindex = idx };
bind(s, (struct sockaddr *)&addr, sizeof(addr));
struct can_frame f;
read(s, &f, sizeof(f));       /* f.can_id, f.can_dlc, f.data[8] */

Python gets the same thing through python-can with the socketcan backend.

Four protocol families sit on top of PF_CAN: - CAN_RAW, individual frames. What candump and cansend use. - CAN_BCM, the broadcast manager, which offloads cyclic transmission and change-based filtering into the kernel. This is how you send a frame every 20 ms without a userspace timer loop. - CAN_ISOTP, ISO 15765-2 segmentation, which turns multi-frame transfers into a stream socket. Required for uds. - CAN_J1939, the heavy-vehicle stack.

Pitfalls

  • Bitrate mismatch is the most common failure, and it looks like a dead bus rather than an error.
  • A CAN controller acknowledges every frame it receives, so simply being connected changes the bus. Use listen-only when observing.
  • Interfaces are not persistent. They must be brought up after every reboot or replug.
  • Not every adapter uses SocketCAN. Cheap slcan dongles present a serial protocol and need slcand to appear as a network interface; some vendor adapters never do.
  • Error frames and controller state are visible in ip -details link show. When something is wrong, that output is the first thing to read.

What it pairs with

can-utils is the toolset built on it (candump, cansend, cangen, canplayer), usb-can-adapter is the hardware, iso-tp and uds are the layers above, and wireshark decodes what you capture.

Further reading