Skip to content

Add listen-only mode support via ip link (issue #25) - #27

Open
knakazawa-ics wants to merge 1 commit into
intrepidcs:masterfrom
knakazawa-ics:feature/listenonly-issue25
Open

Add listen-only mode support via ip link (issue #25)#27
knakazawa-ics wants to merge 1 commit into
intrepidcs:masterfrom
knakazawa-ics:feature/listenonly-issue25

Conversation

@knakazawa-ics

@knakazawa-ics knakazawa-ics commented Aug 11, 2026

Copy link
Copy Markdown

This PR adds CAN listen-only mode support, addressing the listen-only portion of #25.
It pairs with intrepidcs/icsscand#20; the two are meant to land
together. One design point needs your call: the settings-record encoding (see "Open
question" below).

What this does

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

ip -details link show can0 reports the LISTEN-ONLY ctrlmode flag, and the device
stops acknowledging and transmitting while still receiving (verified on hardware, below).

Implementation notes

  • CAN_CTRLMODE_LISTENONLY is advertised only when a v3.2+ usermode daemon is
    connected, so nothing changes for released daemons (3.1.x and older).
  • can_changelink() stores ctrlmode without calling into the driver and requires the
    interface down, so the accumulated configuration is reported from ndo_open. The
    report is skipped while the bitrate is still zero and when nothing changed since the
    last report, so repeated ifup/ifdown cycles do not re-apply unchanged settings.
    Baudrates seeded by the daemon via SIOCSBAUDRATE count as already reported.
  • bitrate_changed gains a second writer (ndo_open); the test-then-clear in
    check_bitrate_change() is now an xchg(), closing the pre-existing window in
    which a set landing between the test and the clear could be lost, with
    WRITE_ONCE/READ_ONCE at the setters and the poll() reader.
  • No version bump and no CHANGELOG entry are included; I left those out assuming they
    happen at release time, and can add them if you prefer.

Open question: settings-record encoding

The listen-only flag is carried in bit 30 of the existing
intrepid_pending_tx_info.count field (bitrate_const caps arbitration bitrates at
1 Mbit/s, so the flag cannot collide with a real bitrate value; the record layout and
the read() protocol are unchanged). The settings-change record is upstream's design,
so treat this encoding as a proposal: if there are in-flight daemon changes that parse
count as a raw bitrate, or you would rather use a different record type, I can
change both sides. The paired icsscand PR masks the flag before using the bitrate.

Compatibility matrix (verified)

  • old daemon (<= 3.1.x) + this module: all new paths are gated off; behavior unchanged.
  • new daemon (3.2) + old module (3.1.x): records arrive without the flag; listen-only
    is correctly rejected by netlink (not advertised).
  • new + new: full feature.

One release-timing assumption to be aware of: icsscand master already reports 3.2.0,
so the daemon-minor gate assumes this pair lands before a 3.2.0 daemon release. If a
stock 3.2.0 daemon without the companion change were to ship first, it would receive
the flag bit and misread it as a bitrate. In that case the daemon should be bumped to
3.3.0 and the gates here changed to minor > 2; happy to adjust to whatever release
plan you prefer.

Known limitations

  • Mode application is asynchronous with no ack channel back to netlink (same as the
    existing bitrate path): if the daemon cannot apply the mode, ip -details will still
    show LISTEN-ONLY. The daemon logs an error in that case.
  • SocketCAN core does not block TX in listen-only; frames written to the socket are
    forwarded to the device, which suppresses them (no transmit receipts are generated).
  • A device mode already stored in the hardware (e.g. set from Vehicle Spy) is not
    reflected back into the netlink ctrlmode at interface creation; the kernel state is
    authoritative once configured through ip link.

Testing

Ubuntu 22.04.5, runtime-tested on kernels 5.15.0-187 (built together with the gnu89
fix from #26) and 6.8.0-136; also cross-compiles clean for aarch64. Run with a
neoVI FIRE 3 and a ValueCAN 4-2, each exercised as the SocketCAN device under test
with the other as the remote node. The daemon was started with
LIBICSNEO_USE_SERVD=0 (stock discovery is currently affected by intrepidcs/icsscand#21).

  • listen-only on: accepted, daemon applies CAN_SETTINGS.Mode = LISTEN_ONLY; candump
    still receives, the remote transmitter gets no ACK (continuous retransmissions, as
    expected with no ACK present), local TX is suppressed by the device.
  • listen-only off: all 5/5 test frames delivered again exactly once, cansend works.
  • CAN FD: fd on + dbitrate combined with listen-only behaves the same (BRS FD
    frames received while listen-only with no ACK generated; clean 5/5 in both
    directions after listen-only off).
  • Repeated ifup/ifdown with unchanged settings produces no further settings writes.
  • Regression: bitrate-only configuration behaves as before and does not touch the mode.

Advertise CAN_CTRLMODE_LISTENONLY when a v3.2+ usermode daemon is
connected, and report the control mode to the daemon alongside the
bitrate.

Because can_changelink() stores ctrlmode without calling into the driver
(and requires the interface to be down), the accumulated configuration
is reported from ndo_open. The report is skipped when the bitrate is
still zero or when nothing changed since the last report, so repeated
ifup/ifdown cycles do not re-apply unchanged settings to the hardware.
Baudrates seeded by the daemon via SIOCSBAUDRATE count as already
reported for the same reason.

The listen-only flag is carried in a spare high bit of the existing
intrepid_pending_tx_info.count field; bitrate_const caps arbitration
bitrates at 1 Mbit/s so the flag cannot collide with a bitrate value.
The record layout and the read() protocol are unchanged, and nothing new
is sent to daemons that do not announce v3.2+. This encoding is a
proposal and pairs with the corresponding icsscand change, which masks
the flag before using the bitrate.

The bitrate_changed test-then-clear in check_bitrate_change() is now an
xchg(), closing the pre-existing window in which a set landing between
the test and the clear could be lost, with WRITE_ONCE/READ_ONCE at the
setters and the poll() reader.

Signed-off-by: Koki Nakazawa <knakazawa@intrepidcs.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant