scapy.contrib.j1939

SAE J1939 - Vehicle network protocol for heavy-duty vehicles.

J1939 uses 29-bit extended CAN identifiers to encode a structured addressing scheme. The 29-bit identifier is partitioned as follows:

Bits 28-26 : Priority        (3 bits, 0 = highest)
Bit  25    : Reserved        (1 bit)
Bit  24    : Data Page       (1 bit)
Bits 23-16 : PDU Format      (8 bits, PF)
Bits 15-8  : PDU Specific    (8 bits, PS)
                 PF < 240 → Destination Address (PDU1, peer-to-peer)
                 PF ≥ 240 → Group Extension     (PDU2, broadcast)
Bits  7-0  : Source Address  (8 bits, SA)
PGN (Parameter Group Number):

PDU1 (PF < 240): PGN = (DP << 16) | (PF << 8) — PS is DA PDU2 (PF ≥ 240): PGN = (DP << 16) | (PF << 8) | GE — broadcast only

References

class scapy.contrib.j1939.J1939(_pkt, /, *, data=b'')[source]

Bases: Packet

SAE J1939 application-layer message.

This class represents a J1939 message at the application layer. When used with NativeJ1939Socket, the Linux kernel J1939 stack handles transport-protocol framing (segmentation / reassembly) automatically, so data may be larger than 8 bytes.

Addressing information – priority, pgn, src, dst – is stored in __slots__ rather than as wire fields (the same approach used by ISOTP). These attributes are populated by NativeJ1939Socket upon reception.

Example:

>>> msg = J1939(b'\x01\x02\x03', pgn=0xFECA, src=0x00, dst=0xFF)
>>> msg.pgn
65226
>>> msg.src
0
aliastypes = [<class 'scapy.contrib.j1939.J1939'>, <class 'scapy.packet.Packet'>]
answers(other: Packet) → int[source]
comments: List[bytes] | None
default_fields: Dict[str, Any]
direction: int | None
dst: int
explicit: int
fields: Dict[str, Any]
fields_desc: ClassVar[List[Field[Any, Any] | _FieldContainer]] = [<StrField (J1939).data>]
fieldtype: Dict[str, Field[Any, Any] | _FieldContainer]
mysummary() → str[source]
name: str | None
original: bytes
overload_fields: Dict[Type[Packet], Dict[str, Any]]
overloaded_fields: Dict[str, Any]
packetfields: List[Field[Any, Any] | _FieldContainer]
parent: Packet | None
payload: Packet
pgn: int
post_transforms
priority: int
process_information: Dict[str, Any] | None
raw_packet_cache: bytes | None
raw_packet_cache_fields: Dict[str, Any] | None
sent_time: EDecimal | float | None
sniffed_on: NetworkInterface | str | None
src: int
stop_dissection_after: Type[Packet] | None
time: EDecimal | float
underlayer: Packet | None
wirelen: int | None
class scapy.contrib.j1939.J1939SoftSocket(can_socket: 'CANSocket' | None = None, src_addr: int = 255, basecls: Type[Packet] = <class 'scapy.contrib.j1939.J1939'>, listen_only: bool = False, pgn: int = 262144)[source]

Bases: SuperSocket

Software J1939 application-layer socket over a CANSocket.

Implements the SAE J1939 Transport Protocol (segmentation and reassembly) entirely in Python, without requiring the Linux kernel CAN_J1939 socket module. It is API-compatible with NativeJ1939Socket and works on any platform that has a CAN socket layer (Linux SocketCAN via NativeCANSocket, or any platform via PythonCANSocket).

The implementation mirrors ISOTPSoftSocket: a background thread driven by TimeoutScheduler polls the CAN socket and advances the TP state machine, so J1939SoftSocket can send Flow-Control (CTS / ACK / ABORT) frames even before recv() is called.

Example – broadcast receive:

>>> cansock = NativeCANSocket("vcan0")
>>> with J1939SoftSocket(cansock, src_addr=0x00) as s:
...     pkt = s.recv()

Example – broadcast send:

>>> cansock = NativeCANSocket("vcan0")
>>> with J1939SoftSocket(cansock, src_addr=0x00) as s:
...     s.send(J1939(b'\x01\x02', pgn=0xFECA, dst=0xFF))
Parameters:
  • can_socket – a CANSocket instance or a CAN interface name string (Linux only)

  • src_addr – this node’s J1939 source address (0x00–0xFD); defaults to socket.J1939_NO_ADDR (0xFF = no address)

  • basecls – packet class for received messages (default: J1939)

  • listen_only – when True, never send CTS / ACK / ABORT frames; all received TP sessions are still reassembled and delivered. Useful for passive bus monitoring.

  • pgn – when not socket.J1939_NO_PGN, only messages whose PGN matches this value are delivered; socket.J1939_NO_PGN (the default) accepts every PGN, matching NativeJ1939Socket.

close(timeout: float | None = None) → None[source]

Close the socket.

Parameters:

timeout – how long a transmission in progress may still take; None derives it from what is left to send, so a broadcast is not truncated, and 0 closes at once.

property closed: bool
desc = 'read/write J1939 messages using a software transport-protocol implementation'
recv(x: int = 65535, **kwargs: Any) → Packet | None[source]

Receive the next J1939 message.

Blocks until a complete message is available or the socket is closed. Returns None if the socket is closed before a message arrives.

recv_raw(x: int = 65535) → Tuple[Type[Packet] | None, bytes | None, float | None][source]

Receive the payload of the next message, without its addressing.

recv() is what callers normally want, since a J1939 message is only meaningful together with its PGN and addresses; this exists so that the SuperSocket contract holds.

static select(sockets: List[SuperSocket | ObjectPipe[Any]], remain: float | None = None) → List[SuperSocket | ObjectPipe[Any]][source]

Support sniff() on J1939SoftSocket.

send(x: Packet) → int[source]

Enqueue x for transmission.

If x is a J1939 packet its pgn, dst, and priority attributes are used. Payloads of 8 bytes or fewer are sent as a single CAN frame; larger payloads use the J1939 Transport Protocol automatically (BAM for broadcast, RTS/CTS for unicast).

The TP.CM and TP.DT frames of a multi-packet message carry the priority of the message itself, so a caller controls the whole transfer with one value. J1939-21 suggests 7 for both, which is what priority=7 gives; the class default is 6.

Raises:

Scapy_Exception – if the payload is larger than the 1785 bytes the transport protocol can carry

class scapy.contrib.j1939.J1939TPImplementation(can_socket: 'CANSocket', src_addr: int, listen_only: bool = False, pgn_filter: int = 262144, basecls: Type[Packet] | None = None)[source]

Bases: object

Software implementation of the SAE J1939 Transport Protocol state machine.

All state is stored here so that the garbage collector can reclaim a J1939SoftSocket even while the background TimeoutScheduler thread holds a reference to this object.

Parameters:
  • can_socket – a CANSocket used for raw CAN I/O

  • src_addr – this node’s J1939 source address (0x00–0xFD)

  • listen_only – when True the implementation never sends CTS, ACK, or ABORT frames, allowing passive monitoring of TP sessions without influencing the bus. Received payloads are still reassembled and delivered via recv().

  • pgn_filter – when not socket.J1939_NO_PGN, only messages whose PGN matches this value are delivered. socket.J1939_NO_PGN (the default) accepts all PGNs, matching NativeJ1939Socket.

  • basecls – packet class used for delivered messages, defaulting to J1939

Receptions are tracked per (source address, destination) pair, so several ECUs may transfer at the same time, up to _J1939_MAX_RX_SESSIONS.

basecls: Type[Packet]
can_recv() → None[source]
close(timeout: float | None = None) → None[source]

Stop the state machine.

Parameters:

timeout – how long to let a transmission in progress finish. None derives a budget from what is still queued, so that a large BAM is not truncated; 0 shuts down at once.

drain_timeout() → float[source]

Time a pending transmission still needs, as a close() budget.

A BAM is paced at _J1939_TP_BAM_DELAY per packet, so a full 1785-byte message legitimately takes 12.75 s; an RTS/CTS session is bounded by its own timeouts.

on_can_recv(pkt: Packet) → None[source]

Decode pkt as a J1939_CAN frame and route it.

recv() → Tuple[J1939, float | EDecimal] | None[source]

Return the next received J1939 message from the queue.

Returns None when the socket is closed while a caller is waiting, rather than letting the closed queue raise into the caller.

rx_sessions: Dict[Tuple[int, int], _J1939_RXSession]
send(msg: Packet) → None[source]

Enqueue msg for transmission.

Also schedules an immediate TX poll so the message is picked up without waiting for the next 5 ms polling interval. This allows send() followed immediately by close() to reliably deliver the frame (e.g. inside a with J1939SoftSocket(...) as s: block).

Raises:

Scapy_Exception – if the payload exceeds the _J1939_TP_MAX_DATA bytes the transport protocol can describe

tx_buf: bytes | None
tx_state: int
tx_timeout_handle: Any | None
scapy.contrib.j1939.J1939_BROADCAST_ADDR = 255

Global broadcast address

class scapy.contrib.j1939.J1939_CAN(_pkt, /, *, flags=<Flag 4 (extended)>, priority=6, reserved=0, data_page=0, pdu_format=254, pdu_specific=255, src=254, length=None, reserved2=0, data=b'')[source]

Bases: CAN

J1939 CAN frame – the 29-bit extended CAN identifier decoded as J1939.

Inherits from CAN so that all CAN lifecycle methods are reused automatically:

  • pre_dissect / post_build – byte-order swap controlled by conf.contribs['CAN']['swap-bytes'] (Wireshark vs PF_CAN format).

  • extract_padding – padding removal controlled by conf.contribs['CAN']['remove-padding'].

The only structural difference from CAN is that the 29-bit identifier field is decomposed into the six J1939 sub-fields (priority, reserved, data_page, pdu_format, pdu_specific, src), while the wire layout remains identical.

CAN identifier sub-fields:

priority    (bits 28-26): message priority, 0 = highest, 7 = lowest
reserved    (bit  25)   : reserved, should be 0
data_page   (bit  24)   : selects one of two parameter group tables
pdu_format  (bits 23-16): determines PDU type (< 240 → PDU1)
pdu_specific(bits 15-8) : DA if PDU1, Group Extension if PDU2
src         (bits  7-0) : source address

Convenience properties pgn and dst are derived from the sub-fields.

Example:

>>> pkt = J1939_CAN(priority=6, pdu_format=0xFE, pdu_specific=0xCA,
...                 src=0x00, data=b'\xff' * 8)
>>> hex(pkt.pgn)
'0xfeca'
>>> hex(pkt.dst)
'0xff'
aliastypes = [<class 'scapy.contrib.j1939.J1939_CAN'>, <class 'scapy.layers.can.CAN'>, <class 'scapy.packet.Packet'>]
classmethod dispatch_hook(_pkt: bytes | None = None, *args: Any, **kargs: Any) → Type[Packet][source]
property dst: int

Destination address for PDU1 frames; socket.J1939_NO_ADDR for PDU2.

fields_desc: ClassVar[List[Field[Any, Any] | _FieldContainer]] = [<FlagsField (J1939_CAN).flags>, <BitField (J1939_CAN).priority>, <BitField (J1939_CAN).reserved>, <BitField (J1939_CAN).data_page>, <ByteField (J1939_CAN).pdu_format>, <ByteField (J1939_CAN).pdu_specific>, <ByteField (J1939_CAN).src>, <FieldLenField (J1939_CAN).length>, <ThreeBytesField (J1939_CAN).reserved2>, <StrLenField (J1939_CAN).data>]
classmethod from_can(pkt: CAN) → J1939_CAN[source]

Create a J1939_CAN from a CAN packet.

The wire bytes are identical so this is simply a class change. The packet timestamp is preserved from pkt.

mysummary() → str[source]
property pgn: int

PGN (Parameter Group Number) derived from data_page, pdu_format, and pdu_specific.

to_can() → CAN[source]

Convert to a standard CAN packet.

The wire bytes are identical so this is simply a class change.

scapy.contrib.j1939.J1939_PGN_TP_CM = 60416

Transport Protocol – Connection Management

scapy.contrib.j1939.J1939_PGN_TP_DT = 60160

Transport Protocol – Data Transfer

class scapy.contrib.j1939.J1939_TP_CM(_pkt, /)[source]

Bases: Packet

J1939 TP Connection Management frame dispatcher.

Parses a raw 8-byte TP.CM payload and returns the appropriate sub-class.

Example:

>>> J1939_TP_CM(bytes([32, 20, 0, 3, 0xFF, 0xCA, 0xFE, 0x00]))
<J1939_TP_CM_BAM  ctrl=32 total_size=20 num_packets=3 ... >
aliastypes = [<class 'scapy.contrib.j1939.J1939_TP_CM'>, <class 'scapy.packet.Packet'>]
classmethod dispatch_hook(_pkt: bytes | None = None, *args: Any, **kargs: Any) → Type[Packet][source]
do_dissect(s: bytes) → bytes[source]
class scapy.contrib.j1939.J1939_TP_CM_ABORT(_pkt, /, *, ctrl=255, reason=0, reserved=65535, reserved2=255, pgn=0)[source]

Bases: Packet

J1939 TP Connection Management – Connection Abort.

aliastypes = [<class 'scapy.contrib.j1939.J1939_TP_CM_ABORT'>, <class 'scapy.packet.Packet'>]
fields_desc: ClassVar[List[Field[Any, Any] | _FieldContainer]] = [<ByteField (J1939_TP_CM_ABORT).ctrl>, <ByteField (J1939_TP_CM_ABORT).reason>, <ShortField (J1939_TP_CM_ABORT).reserved>, <ByteField (J1939_TP_CM_ABORT).reserved2>, <XLE3BytesField (J1939_TP_CM_ABORT).pgn>]
class scapy.contrib.j1939.J1939_TP_CM_ACK(_pkt, /, *, ctrl=19, total_size=0, num_packets=0, reserved=255, pgn=0)[source]

Bases: Packet

J1939 TP Connection Management – End of Message Acknowledge (EoMAck).

Sent by the receiver after all TP.DT packets have been received.

aliastypes = [<class 'scapy.contrib.j1939.J1939_TP_CM_ACK'>, <class 'scapy.packet.Packet'>]
fields_desc: ClassVar[List[Field[Any, Any] | _FieldContainer]] = [<ByteField (J1939_TP_CM_ACK).ctrl>, <LEShortField (J1939_TP_CM_ACK).total_size>, <ByteField (J1939_TP_CM_ACK).num_packets>, <ByteField (J1939_TP_CM_ACK).reserved>, <XLE3BytesField (J1939_TP_CM_ACK).pgn>]
class scapy.contrib.j1939.J1939_TP_CM_BAM(_pkt, /, *, ctrl=32, total_size=0, num_packets=0, reserved=255, pgn=0)[source]

Bases: Packet

J1939 TP Connection Management – Broadcast Announce Message (BAM).

Announces a forthcoming multi-packet broadcast; no CTS handshake is used.

aliastypes = [<class 'scapy.contrib.j1939.J1939_TP_CM_BAM'>, <class 'scapy.packet.Packet'>]
fields_desc: ClassVar[List[Field[Any, Any] | _FieldContainer]] = [<ByteField (J1939_TP_CM_BAM).ctrl>, <LEShortField (J1939_TP_CM_BAM).total_size>, <ByteField (J1939_TP_CM_BAM).num_packets>, <ByteField (J1939_TP_CM_BAM).reserved>, <XLE3BytesField (J1939_TP_CM_BAM).pgn>]
class scapy.contrib.j1939.J1939_TP_CM_CTS(_pkt, /, *, ctrl=17, num_packets=0, next_packet=1, reserved=65535, pgn=0)[source]

Bases: Packet

J1939 TP Connection Management – Clear To Send (CTS).

Response to J1939_TP_CM_RTS; authorises the sender to transmit up to num_packets TP.DT packets starting from next_packet.

aliastypes = [<class 'scapy.contrib.j1939.J1939_TP_CM_CTS'>, <class 'scapy.packet.Packet'>]
fields_desc: ClassVar[List[Field[Any, Any] | _FieldContainer]] = [<ByteField (J1939_TP_CM_CTS).ctrl>, <ByteField (J1939_TP_CM_CTS).num_packets>, <ByteField (J1939_TP_CM_CTS).next_packet>, <ShortField (J1939_TP_CM_CTS).reserved>, <XLE3BytesField (J1939_TP_CM_CTS).pgn>]
class scapy.contrib.j1939.J1939_TP_CM_RTS(_pkt, /, *, ctrl=16, total_size=0, num_packets=0, max_packets=255, pgn=0)[source]

Bases: Packet

J1939 TP Connection Management – Request To Send (RTS).

Sent before a peer-to-peer multi-packet message to announce the total size and packet count. Uses PGN 0xEC00.

aliastypes = [<class 'scapy.contrib.j1939.J1939_TP_CM_RTS'>, <class 'scapy.packet.Packet'>]
fields_desc: ClassVar[List[Field[Any, Any] | _FieldContainer]] = [<ByteField (J1939_TP_CM_RTS).ctrl>, <LEShortField (J1939_TP_CM_RTS).total_size>, <ByteField (J1939_TP_CM_RTS).num_packets>, <ByteField (J1939_TP_CM_RTS).max_packets>, <XLE3BytesField (J1939_TP_CM_RTS).pgn>]
class scapy.contrib.j1939.J1939_TP_DT(_pkt, /, *, seq_num=1, data=b'\xff\xff\xff\xff\xff\xff\xff')[source]

Bases: Packet

J1939 TP Data Transfer frame.

Each TP.DT packet carries up to 7 bytes of payload; the first byte is the sequence number (1–255). Unused bytes are padded with 0xFF.

aliastypes = [<class 'scapy.contrib.j1939.J1939_TP_DT'>, <class 'scapy.packet.Packet'>]
fields_desc: ClassVar[List[Field[Any, Any] | _FieldContainer]] = [<ByteField (J1939_TP_DT).seq_num>, <StrFixedLenField (J1939_TP_DT).data>]
class scapy.contrib.j1939.NativeJ1939Socket(channel: str | None = None, src_name: int = 0, src_addr: int = 255, pgn: int = 262144, promisc: bool = True, filters: Dict[str, int]] | None=None, basecls: Type[Packet] = <class 'scapy.contrib.j1939.J1939'>, **kwargs: Any)[source]

Bases: SuperSocket

Linux kernel J1939 socket (PF_CAN / SOCK_DGRAM / CAN_J1939).

The kernel J1939 stack handles transport-protocol framing automatically: messages larger than 8 bytes are fragmented / reassembled transparently, and the application deals only with complete J1939 messages.

Note

Design – why not inherit from NativeCANSocket?

NativeCANSocket uses SOCK_RAW / CAN_RAW, while this class uses SOCK_DGRAM / CAN_J1939. The socket type, protocol, send() logic (sendto with 4-tuple destination vs plain send), recv() logic (recvmsg for J1939 ancillary data vs raw bytes + byte-order swap), and address binding API are all fundamentally different. Inheriting from NativeCANSocket would override or bypass every method, making the hierarchy misleading rather than helpful.

Parameters:
  • channel – CAN interface name (default: can0)

  • src_name – 64-bit J1939 NAME of this node (0 = no name)

  • src_addr – Source address to bind to (socket.J1939_NO_ADDR for promiscuous reception of all addresses)

  • pgn – PGN to bind to (socket.J1939_NO_PGN for all PGNs)

  • promisc – Enable promiscuous mode – receive all J1939 messages on the interface regardless of destination address

  • filters – Optional list of j1939_filter dicts; each may contain the keys name, name_mask, pgn, pgn_mask, addr, addr_mask

  • basecls – Packet class used to wrap received payloads (default: J1939)

Example – sniff all J1939 traffic on vcan0:

>>> sock = NativeJ1939Socket("vcan0", promisc=True)
>>> pkt = sock.recv()
>>> print(pkt.pgn, pkt.src, pkt.data)

Example – send a J1939 message:

>>> sock = NativeJ1939Socket("vcan0", src_addr=0x00)
>>> sock.send(J1939(data=b'\x01\x02', pgn=0xFECA, dst=0xFF))
desc = 'read/write J1939 messages using Linux kernel PF_CAN/CAN_J1939 sockets'
recv(x: int = 4096, **kwargs: Any) → Packet | None[source]

Receive one J1939 message, including addressing metadata.

Returns a basecls instance (default: J1939) with priority, pgn, src, and dst populated from the kernel.

recv_raw(x: int = 4096) → Tuple[Type[Packet] | None, bytes | None, float | None][source]

Returns a tuple (cls, pkt_data, timestamp).

Note

The returned pkt_data is only the raw J1939 payload bytes. Addressing metadata (PGN, source/destination address, priority) is unavailable through this low-level interface; use recv() instead to obtain a fully populated J1939 packet.

send(x: Packet) → int[source]

Send a J1939 message.

If x is a J1939 packet, the pgn, dst, and priority attributes are used. For other packet types the raw bytes are sent to the socket’s default destination.

scapy.contrib.j1939.can_id_to_j1939(can_id: int) → Dict[str, int][source]

Decode a 29-bit CAN identifier to a dictionary of J1939 fields.

Parameters:

can_id – 29-bit extended CAN identifier

Returns:

dict with keys priority, reserved, data_page, pdu_format, pdu_specific, src

scapy.contrib.j1939.dst_from_fields(pdu_format: int, pdu_specific: int) → int[source]

Return the destination address encoded in J1939 identifier fields.

Parameters:
  • pdu_format – PDU format byte (0-255)

  • pdu_specific – PDU specific byte (0-255)

Returns:

destination address (0x00-0xFF), or socket.J1939_NO_ADDR for PDU2

scapy.contrib.j1939.j1939_to_can_id(priority: int, reserved: int, data_page: int, pdu_format: int, pdu_specific: int, src: int) → int[source]

Encode J1939 fields into a 29-bit CAN identifier.

Returns:

29-bit CAN identifier value

scapy.contrib.j1939.pgn_from_fields(data_page: int, pdu_format: int, pdu_specific: int) → int[source]

Compute the PGN from J1939 CAN identifier sub-fields.

Parameters:
  • data_page – data page bit (0 or 1)

  • pdu_format – PDU format byte (0-255)

  • pdu_specific – PDU specific byte (0-255)

Returns:

18-bit PGN value

scapy.contrib.j1939.pgn_is_pdu1(pgn: int) → bool[source]

Return True if pgn is a PDU1 (peer-to-peer) Parameter Group Number.