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
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
original
overload_fields: Dict[Type[Packet], Dict[str, Any]]
overloaded_fields: Dict[str, Any]
packetfields: List[Field[Any, Any] | _FieldContainer]
parent
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
time: EDecimal | float
underlayer
wirelen: int | 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.