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
SAE J1939 standard
Linux kernel J1939 documentation: https://www.kernel.org/doc/html/latest/networking/j1939.html
- class scapy.contrib.j1939.J1939(_pkt, /, *, data=b'')[source]
Bases:
PacketSAE 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, sodatamay be larger than 8 bytes.Addressing information –
priority,pgn,src,dst– is stored in__slots__rather than as wire fields (the same approach used byISOTP). These attributes are populated byNativeJ1939Socketupon 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'>]
- comments: List[bytes] | None
- default_fields: Dict[str, Any]
- direction: int | None
- dst: int
- explicit: int
- fields: Dict[str, Any]
- name: str | None
- original: bytes
- overloaded_fields: Dict[str, Any]
- 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
- sniffed_on: NetworkInterface | str | None
- src: int
- 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:
SuperSocketSoftware 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_J1939socket module. It is API-compatible withNativeJ1939Socketand works on any platform that has a CAN socket layer (Linux SocketCAN viaNativeCANSocket, or any platform viaPythonCANSocket).The implementation mirrors
ISOTPSoftSocket: a background thread driven byTimeoutSchedulerpolls the CAN socket and advances the TP state machine, soJ1939SoftSocketcan send Flow-Control (CTS / ACK / ABORT) frames even beforerecv()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
CANSocketinstance 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, matchingNativeJ1939Socket.
- close(timeout: float | None = None) None[source]
Close the socket.
- Parameters:
timeout – how long a transmission in progress may still take;
Nonederives it from what is left to send, so a broadcast is not truncated, and0closes 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
J1939message.Blocks until a complete message is available or the socket is closed. Returns
Noneif 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 theSuperSocketcontract holds.
- static select(sockets: List[SuperSocket | ObjectPipe[Any]], remain: float | None = None) List[SuperSocket | ObjectPipe[Any]][source]
Support
sniff()onJ1939SoftSocket.
- send(x: Packet) int[source]
Enqueue x for transmission.
If x is a
J1939packet itspgn,dst, andpriorityattributes 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=7gives; 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:
objectSoftware implementation of the SAE J1939 Transport Protocol state machine.
All state is stored here so that the garbage collector can reclaim a
J1939SoftSocketeven while the backgroundTimeoutSchedulerthread holds a reference to this object.- Parameters:
can_socket – a
CANSocketused for raw CAN I/Osrc_addr – this node’s J1939 source address (0x00–0xFD)
listen_only – when
Truethe 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 viarecv().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, matchingNativeJ1939Socket.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.- close(timeout: float | None = None) None[source]
Stop the state machine.
- Parameters:
timeout – how long to let a transmission in progress finish.
Nonederives a budget from what is still queued, so that a large BAM is not truncated;0shuts 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_DELAYper packet, so a full 1785-byte message legitimately takes 12.75 s; an RTS/CTS session is bounded by its own timeouts.
- recv() Tuple[J1939, float | EDecimal] | None[source]
Return the next received
J1939message from the queue.Returns
Nonewhen 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 byclose()to reliably deliver the frame (e.g. inside awith J1939SoftSocket(...) as s:block).- Raises:
Scapy_Exception – if the payload exceeds the
_J1939_TP_MAX_DATAbytes 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:
CANJ1939 CAN frame – the 29-bit extended CAN identifier decoded as J1939.
Inherits from
CANso that all CAN lifecycle methods are reused automatically:pre_dissect/post_build– byte-order swap controlled byconf.contribs['CAN']['swap-bytes'](Wireshark vs PF_CAN format).extract_padding– padding removal controlled byconf.contribs['CAN']['remove-padding'].
The only structural difference from
CANis that the 29-bitidentifierfield 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
pgnanddstare 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_ADDRfor 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_CANfrom aCANpacket.The wire bytes are identical so this is simply a class change. The packet timestamp is preserved from pkt.
- property pgn: int
PGN (Parameter Group Number) derived from
data_page,pdu_format, andpdu_specific.
- 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:
PacketJ1939 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'>]
- class scapy.contrib.j1939.J1939_TP_CM_ABORT(_pkt, /, *, ctrl=255, reason=0, reserved=65535, reserved2=255, pgn=0)[source]
Bases:
PacketJ1939 TP Connection Management – Connection Abort.
- aliastypes = [<class 'scapy.contrib.j1939.J1939_TP_CM_ABORT'>, <class 'scapy.packet.Packet'>]
- class scapy.contrib.j1939.J1939_TP_CM_ACK(_pkt, /, *, ctrl=19, total_size=0, num_packets=0, reserved=255, pgn=0)[source]
Bases:
PacketJ1939 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'>]
- class scapy.contrib.j1939.J1939_TP_CM_BAM(_pkt, /, *, ctrl=32, total_size=0, num_packets=0, reserved=255, pgn=0)[source]
Bases:
PacketJ1939 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'>]
- class scapy.contrib.j1939.J1939_TP_CM_CTS(_pkt, /, *, ctrl=17, num_packets=0, next_packet=1, reserved=65535, pgn=0)[source]
Bases:
PacketJ1939 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'>]
- class scapy.contrib.j1939.J1939_TP_CM_RTS(_pkt, /, *, ctrl=16, total_size=0, num_packets=0, max_packets=255, pgn=0)[source]
Bases:
PacketJ1939 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'>]
- class scapy.contrib.j1939.J1939_TP_DT(_pkt, /, *, seq_num=1, data=b'\xff\xff\xff\xff\xff\xff\xff')[source]
Bases:
PacketJ1939 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'>]
- 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:
SuperSocketLinux 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?NativeCANSocketusesSOCK_RAW / CAN_RAW, while this class usesSOCK_DGRAM / CAN_J1939. The socket type, protocol,send()logic (sendtowith 4-tuple destination vs plainsend),recv()logic (recvmsgfor J1939 ancillary data vs raw bytes + byte-order swap), and address binding API are all fundamentally different. Inheriting fromNativeCANSocketwould 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_ADDRfor promiscuous reception of all addresses)pgn – PGN to bind to (
socket.J1939_NO_PGNfor all PGNs)promisc – Enable promiscuous mode – receive all J1939 messages on the interface regardless of destination address
filters – Optional list of
j1939_filterdicts; each may contain the keysname,name_mask,pgn,pgn_mask,addr,addr_maskbasecls – 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
baseclsinstance (default:J1939) withpriority,pgn,src, anddstpopulated from the kernel.
- 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_ADDRfor 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