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
- fields: Dict[str, Any]
- name
- original
- overloaded_fields: Dict[str, Any]
- parent
- 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
- stop_dissection_after
- 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:
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