IsoTp Sockets

Introduction

Under Linux, CAN interfaces can be managed the same way as network interfaces. The support for each CAN driver is written directly in the Linux kernel, inside a module called SocketCAN. Therefore, it is possible to send data over CAN through sockets, which offer a unique interface for all CAN drivers.

SocketCAN allows a user the send CAN messages on the CAN layer and, starting from Linux 5.10.0, ISO-TP frames as well. Meaning that a user can write a payload into an ISO-TP socket, this payload will be subdivided into multiple CAN messages and sent to a receiver following the ISO-15765 standard

For version prior to 5.10.0, this out-of-tree loadable kernel module can be compiled and loaded in the Linux kernel, enabling ISO-TP sockets.

A socket is a standard interface for communication protocols and as anything generic, it can be complex to configure. The isotp.socket is a wrapper over a native IsoTP socket providing a friendly and pythonic interface for easy configuration. It does not offer any additional functionality that your operating system can’t provide, it makes the syntax clean and warns you in case of wrong usage.

Troubleshooting

  • My socket module does not include the `CAN_ISOTP` constant

That means that your Python version does not include support for IsoTP protocol. It should be included starting from Python 3.7, under Linux build only. See Python issue and Pull request

  • When I create the socket, I get `OSError [errno XX] : Protocol not supported`.

Your Linux kernel does not support ISO-TP sockets, you need to manually load it. Follow the steps given it the out-of-tree repository. You needs to compile the module, install the .ko file and then run insmod can-isotp.ko as Super User. Then your OS will accept to create an ISO-TP sockets.

  • I get a timeout when calling send().

A normal use case for that issue is that the receiver failed to respond with a flow control message.

Also, some kernel versions have been hit with a bug. A race condition within the isotp kernel module can cause the send() function to return an unexpected timeout. The patch has been published in this PR. This github issue also covers the matter

Examples

Without this project

SOL_CAN_ISOTP = 106 # These constants exist in the module header, not in Python.
CAN_ISOTP_RECV_FC = 2
# Many more exists.

import socket
import struct

s = socket.socket(socket.AF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP)
s2 = socket.socket(socket.AF_CAN, socket.SOCK_DGRAM, socket.CAN_ISOTP)
# Configuring the sockets with ugly struct.pack() that requires knowledge of the driver
s.setsockopt(SOL_CAN_ISOTP, CAN_ISOTP_RECV_FC, struct.pack("=BBB", 0x10, 3,0)) #bs, stmin, wftmax
#s.setsockopt(SOL_CAN_ISOTP, CAN_ISOTP_OPTS, struct.pack(...))
#s.setsockopt(SOL_CAN_ISOTP, CAN_ISOTP_LL_OPTS, struct.pack(...))

s.bind(("vcan0", 0x123, 0x456)) #rxid, txid with confusing order.
s2.bind(("vcan0", 0x456, 0x123)) #rxid, txid
s2.send(b"Hello, this is a long payload sent in small chunks of 8 bytes.")
print(s.recv(4095))

With this project

import isotp

s = isotp.socket()
s2 = isotp.socket()
# Configuring the sockets.
s.set_fc_opts(stmin=5, bs=10)
#s.set_general_opts(...)
#s.set_ll_opts(...)

s.bind("vcan0", isotp.Address(rxid=0x123, txid=0x456))
s2.bind("vcan0", isotp.Address(rxid=0x456, txid=0x123))
s2.send(b"Hello, this is a long payload sent in small chunks of 8 bytes.")
print(s.recv())

Usage

class isotp.socket(timeout=0.1)[source]

A IsoTP socket wrapper for easy configuration

Parameters:

timeout (int) – The underlying socket timeout set with settimeout. Makes the reception thread sleep

socket.bind(interface, address)[source]

Binds the socket to an address. If no address is provided, all additional parameters will be used to create an address. This is mainly to allow a syntax such as sock.bind('vcan0', rxid=0x123, txid=0x456) for backward compatibility.

Parameters:
  • interface (string) – The network interface to use

  • address – The address to bind to.

Type:

isotp.Address


To configure a socket, few methods are available

socket.set_opts(optflag=None, frame_txtime=None, ext_address=None, txpad=None, rxpad=None, rx_ext_address=None, tx_stmin=None)

Sets the general options of the socket

Parameters:
  • optflag (int) – A list of flags modifying the protocol behaviour. Refer to socket.flags

  • frame_txtime (int) – Frame transmission time (N_As/N_Ar) in nanoseconds.

  • ext_address (int) – The extended address to use. If not None, flags.EXTEND_ADDR will be set.

  • txpad (int) – The byte to use to pad the transmitted CAN messages. If not None, flags.TX_PADDING will be set

  • rxpad (int) – The byte to use to pad the transmitted CAN messages. If not None, flags.RX_PADDING will be set

  • rx_ext_address (int) – The extended address to use in reception. If not None, flags.RX_EXT_ADDR will be set

  • tx_stmin (int) – Sets the transmit separation time (time between consecutive frame) in nanoseconds. This value will override the value received through FlowControl frame. If not None, flags.FORCE_TXSTMIN will be set

socket.set_fc_opts(bs=None, stmin=None, wftmax=None)

Sets the flow control options of the socket

Parameters:
  • bs (int) – The block size sent in the flow control message. Indicates the number of consecutive frame a sender can send before the socket sends a new flow control. A block size of 0 means that no additional flow control message will be sent (block size of infinity)

  • stmin (int) – The minimum separation time sent in the flow control message. Indicates the amount of time to wait between 2 consecutive frame. This value will be sent as is over CAN. Values from 1 to 127 means milliseconds. Values from 0xF1 to 0xF9 means 100us to 900us. 0 Means no timing requirements

  • wftmax (int) – Maximum number of wait frame (flow control message with flow status=1) allowed before dropping a message. 0 means that wait frame are not allowed

socket.set_ll_opts(mtu=None, tx_dl=None, tx_flags=None)

Sets the link layer options. Default values are set to work with CAN 2.0. Link layer may be configure to work in CAN FD.

Parameters:
  • mtu (int) – The internal CAN frame structure size. Possible values are defined in isotp.socket.LinkLayerProtocol

  • tx_dl (int) – The CAN message payload length. For CAN 2.0, this value should be 8. For CAN FD, possible values are 8,12,16,20,24,32,48,64

  • tx_flags (int) – Link layer flags.


class isotp.socket.flags
LISTEN_MODE = 1

Puts the socket in Listen mode, which prevents transmission of data

EXTEND_ADDR = 2

When set, an address extension byte (set in socket general options) will be added to each payload sent. Unless RX_EXT_ADDR is also set, this value will be expected for reception as well

TX_PADDING = 4

Enables padding of transmitted data with a byte set in the socket general options

RX_PADDING = 8

Indicates that data padding is possible in reception. Must be set for CHK_PAD_LEN and CHK_PAD_DATA to have an effect

CHK_PAD_LEN = 16

Makes the socket validate the padding length of the CAN message

CHK_PAD_DATA = 32

Makes the socket validate the padding bytes of the CAN message

HALF_DUPLEX = 64

Sets the socket in half duplex mode, forcing transmission and reception to happen sequentially

FORCE_TXSTMIN = 128

Forces the socket to use the separation time sets in general options, overriding stmin value received in flow control frames.

FORCE_RXSTMIN = 256

Forces the socket to ignore any message received faster than stmin given in the flow control frame

RX_EXT_ADDR = 512

When sets, a different extended address can be used for reception than for transmission.

WAIT_TX_DONE = 1024

When set, we wait for tx completion to make sure the PDU is completely passed to the CAN netdevice queue.

class isotp.socket.LinkLayerProtocol
CAN = 16

Internal structure size of a CAN 2.0 frame

CAN_FD = 72

Internal structure size of a CAN FD frame