2 * Routines for dissection of SAE J1939
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * SPDX-License-Identifier: GPL-2.0-or-later
16 #include <epan/packet.h>
17 #include <epan/address_types.h>
18 #include <epan/to_str.h>
20 #include "packet-socketcan.h"
22 void proto_register_j1939(void);
23 void proto_reg_handoff_j1939(void);
25 static dissector_handle_t j1939_handle
;
27 static int proto_j1939
;
29 static int hf_j1939_can_id
;
30 static int hf_j1939_priority
;
31 static int hf_j1939_pgn
;
32 static int hf_j1939_data_page
;
33 static int hf_j1939_extended_data_page
;
34 static int hf_j1939_pdu_format
;
35 static int hf_j1939_pdu_specific
;
36 static int hf_j1939_src_addr
;
37 static int hf_j1939_dst_addr
;
38 static int hf_j1939_group_extension
;
39 static int hf_j1939_data
;
42 static int ett_j1939_can
;
43 static int ett_j1939_message
;
45 static int j1939_address_type
= -1;
46 static dissector_table_t subdissector_pgn_table
;
48 static const value_string j1939_address_vals
[] = {
52 {3,"Transmission #1"},
53 {4,"Transmission #2"},
54 {5,"Shift Console - Primary"},
55 {6,"Shift Console - Secondary"},
56 {7,"Power TakeOff - (Main or Rear)"},
57 {8,"Axle - Steering"},
58 {9,"Axle - Drive #1"},
59 {10,"Axle - Drive #2"},
60 {11,"Brakes - System Controller"},
61 {12,"Brakes - Steer Axle"},
62 {13,"Brakes - Drive axle #1"},
63 {14,"Brakes - Drive Axle #2"},
64 {15,"Retarder - Engine"},
65 {16,"Retarder - Driveline"},
66 {17,"Cruise Control"},
68 {19,"Steering Controller"},
69 {20,"Suspension - Steer Axle"},
70 {21,"Suspension - Drive Axle #1"},
71 {22,"Suspension - Drive Axle #2"},
72 {23,"Instrument Cluster #1"},
74 {25,"Passenger-Operator Climate Control #1"},
75 {26,"Alternator/Electrical Charging System"},
76 {27,"Aerodynamic Control"},
77 {28,"Vehicle Navigation"},
78 {29,"Vehicle Security"},
79 {30,"Electrical System"},
80 {31,"Starter System"},
81 {32,"Tractor-Trailer Bridge #1"},
82 {33,"Body Controller"},
83 {34,"Auxiliary Valve Control or Engine Air System Valve Control"},
85 {36,"Power TakeOff (Front or Secondary)"},
86 {37,"Off Vehicle Gateway"},
87 {38,"Virtual Terminal (in cab)"},
88 {39,"Management Computer #1"},
89 {40,"Cab Display #1"},
90 {41,"Retarder, Exhaust, Engine #1"},
91 {42,"Headway Controller"},
92 {43,"On-Board Diagnostic Unit"},
93 {44,"Retarder, Exhaust, Engine #2"},
94 {45,"Endurance Braking System"},
95 {46,"Hydraulic Pump Controller"},
96 {47,"Suspension - System Controller #1"},
97 {48,"Pneumatic - System Controller"},
98 {49,"Cab Controller - Primary"},
99 {50,"Cab Controller - Secondary"},
100 {51,"Tire Pressure Controller"},
101 {52,"Ignition Control Module #1"},
102 {53,"Ignition Control Module #2"},
103 {54,"Seat Control #1"},
104 {55,"Lighting - Operator Controls"},
105 {56,"Rear Axle Steering Controller #1"},
106 {57,"Water Pump Controller"},
107 {58,"Passenger-Operator Climate Control #2"},
108 {59,"Transmission Display - Primary"},
109 {60,"Transmission Display - Secondary"},
110 {61,"Exhaust Emission Controller"},
111 {62,"Vehicle Dynamic Stability Controller"},
113 {64,"Suspension - System Controller #2"},
114 {65,"Information System Controller #1"},
116 {67,"Clutch/Converter Unit"},
117 {68,"Auxiliary Heater #1"},
118 {69,"Auxiliary Heater #2"},
119 {70,"Engine Valve Controller"},
120 {71,"Chassis Controller #1"},
121 {72,"Chassis Controller #2"},
122 {73,"Propulsion Battery Charger"},
123 {74,"Communications Unit, Cellular"},
124 {75,"Communications Unit, Satellite"},
125 {76,"Communications Unit, Radio"},
126 {77,"Steering Column Unit"},
127 {78,"Fan Drive Controller"},
128 {79,"Seat Control #2"},
129 {80,"Parking brake controller"},
130 {81,"Aftertreatment #1 system gas intake"},
131 {82,"Aftertreatment #1 system gas outlet"},
132 {83,"Safety Restraint System"},
133 {84,"Cab Display #2"},
134 {85,"Diesel Particulate Filter Controller"},
135 {86,"Aftertreatment #2 system gas intake"},
136 {87,"Aftertreatment #2 system gas outlet"},
137 {88,"Safety Restraint System #2"},
138 {89,"Atmospheric Sensor"},
139 {248,"File Server / Printer"},
140 {249,"Off Board Diagnostic-Service Tool #1"},
141 {250,"Off Board Diagnostic-Service Tool #2"},
142 {251,"On-Board Data Logger"},
143 {252,"Reserved for Experimental Use"},
144 {253,"Reserved for OEM"},
145 {254,"Null Address"},
150 static value_string_ext j1939_address_vals_ext
= VALUE_STRING_EXT_INIT(j1939_address_vals
);
153 j1939_fmt_address(char *result
, uint32_t addr
)
155 if ((addr
< 128) || (addr
> 247))
156 snprintf(result
, ITEM_LABEL_LENGTH
, "%d (%s)", addr
, val_to_str_ext_const(addr
, &j1939_address_vals_ext
, "Reserved"));
158 snprintf(result
, ITEM_LABEL_LENGTH
, "%d (Arbitrary)", addr
);
161 static int dissect_j1939(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
163 proto_item
*ti
, *can_id_item
;
164 proto_tree
*j1939_tree
, *can_tree
, *msg_tree
;
167 struct can_info can_info
;
168 uint32_t data_length
= tvb_reported_length(tvb
);
170 uint8_t *src_addr
, *dest_addr
;
172 DISSECTOR_ASSERT(data
);
173 can_info
= *((struct can_info
*)data
);
175 if ((can_info
.id
& CAN_ERR_FLAG
) ||
176 !(can_info
.id
& CAN_EFF_FLAG
))
178 /* Error frames and frames with standards ids are not for us */
182 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "J1939");
183 col_clear(pinfo
->cinfo
, COL_INFO
);
185 ti
= proto_tree_add_item(tree
, proto_j1939
, tvb
, offset
, tvb_reported_length(tvb
), ENC_NA
);
186 j1939_tree
= proto_item_add_subtree(ti
, ett_j1939
);
188 can_tree
= proto_tree_add_subtree_format(j1939_tree
, tvb
, 0, 0,
189 ett_j1939_can
, NULL
, "CAN Identifier: 0x%08x", can_info
.id
);
190 can_id_item
= proto_tree_add_uint(can_tree
, hf_j1939_can_id
, tvb
, 0, 0, can_info
.id
);
191 proto_item_set_generated(can_id_item
);
192 ti
= proto_tree_add_uint(can_tree
, hf_j1939_priority
, tvb
, 0, 0, can_info
.id
);
193 proto_item_set_generated(ti
);
194 ti
= proto_tree_add_uint(can_tree
, hf_j1939_extended_data_page
, tvb
, 0, 0, can_info
.id
);
195 proto_item_set_generated(ti
);
196 ti
= proto_tree_add_uint(can_tree
, hf_j1939_data_page
, tvb
, 0, 0, can_info
.id
);
197 proto_item_set_generated(ti
);
198 ti
= proto_tree_add_uint(can_tree
, hf_j1939_pdu_format
, tvb
, 0, 0, can_info
.id
);
199 proto_item_set_generated(ti
);
200 ti
= proto_tree_add_uint(can_tree
, hf_j1939_pdu_specific
, tvb
, 0, 0, can_info
.id
);
201 proto_item_set_generated(ti
);
202 ti
= proto_tree_add_uint(can_tree
, hf_j1939_src_addr
, tvb
, 0, 0, can_info
.id
);
203 proto_item_set_generated(ti
);
205 /* Set source address */
206 src_addr
= (uint8_t*)wmem_alloc(pinfo
->pool
, 1);
207 *src_addr
= (uint8_t)(can_info
.id
& 0xFF);
208 set_address(&pinfo
->src
, j1939_address_type
, 1, (const void*)src_addr
);
210 pgn
= (can_info
.id
& 0x3FFFF00) >> 8;
212 /* If PF < 240, PS is destination address, last byte of PGN is cleared */
213 if (((can_info
.id
& 0xFF0000) >> 16) < 240)
217 ti
= proto_tree_add_uint(can_tree
, hf_j1939_dst_addr
, tvb
, 0, 0, can_info
.id
);
218 proto_item_set_generated(ti
);
222 ti
= proto_tree_add_uint(can_tree
, hf_j1939_group_extension
, tvb
, 0, 0, can_info
.id
);
223 proto_item_set_generated(ti
);
226 /* Fill in "destination" address even if its "broadcast" */
227 dest_addr
= (uint8_t*)wmem_alloc(pinfo
->pool
, 1);
228 *dest_addr
= (uint8_t)((can_info
.id
& 0xFF00) >> 8);
229 set_address(&pinfo
->dst
, j1939_address_type
, 1, (const void*)dest_addr
);
231 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "PGN: %-6" PRIu32
, pgn
);
233 if (can_info
.id
& CAN_RTR_FLAG
)
235 /* RTR frames don't have payload */
236 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " %s", "(Remote Transmission Request)");
240 /* For now just include raw bytes */
241 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " %s", tvb_bytes_to_str_punct(pinfo
->pool
, tvb
, 0, data_length
, ' '));
244 msg_tree
= proto_tree_add_subtree(j1939_tree
, tvb
, 0, tvb_reported_length(tvb
), ett_j1939_message
, NULL
, "Message");
246 ti
= proto_tree_add_uint(msg_tree
, hf_j1939_pgn
, tvb
, 0, 0, pgn
);
247 proto_item_set_generated(ti
);
249 if (!dissector_try_uint_new(subdissector_pgn_table
, pgn
, tvb
, pinfo
, msg_tree
, true, data
))
251 proto_tree_add_item(msg_tree
, hf_j1939_data
, tvb
, 0, tvb_reported_length(tvb
), ENC_NA
);
254 return tvb_captured_length(tvb
);
257 static int J1939_addr_to_str(const address
* addr
, char *buf
, int buf_len
)
259 const uint8_t *addrdata
= (const uint8_t *)addr
->data
;
261 uint32_to_str_buf(*addrdata
, buf
, buf_len
);
262 return (int)strlen(buf
);
265 static int J1939_addr_str_len(const address
* addr _U_
)
267 return 11; /* Leaves required space (10 bytes) for uint_to_str_back() */
270 static const char* J1939_col_filter_str(const address
* addr _U_
, bool is_src
)
273 return "j1939.src_addr";
275 return "j1939.dst_addr";
278 static int J1939_addr_len(void)
283 void proto_register_j1939(void)
285 static hf_register_info hf
[] = {
287 {"CAN Identifier", "j1939.can_id",
288 FT_UINT32
, BASE_HEX
, NULL
, CAN_EFF_MASK
, NULL
, HFILL
}
290 { &hf_j1939_priority
,
291 {"Priority", "j1939.priority",
292 FT_UINT32
, BASE_DEC
, NULL
, 0x1C000000, NULL
, HFILL
}
296 FT_UINT32
, BASE_DEC
, NULL
, 0x03FFFFFF, NULL
, HFILL
}
298 { &hf_j1939_extended_data_page
,
299 {"Extended Data Page", "j1939.ex_data_page",
300 FT_UINT32
, BASE_DEC
, NULL
, 0x02000000, NULL
, HFILL
}
302 { &hf_j1939_data_page
,
303 {"Data Page", "j1939.data_page",
304 FT_UINT32
, BASE_DEC
, NULL
, 0x01000000, NULL
, HFILL
}
306 { &hf_j1939_pdu_format
,
307 {"PDU Format", "j1939.pdu_format",
308 FT_UINT32
, BASE_DEC
, NULL
, 0x00FF0000, NULL
, HFILL
}
310 { &hf_j1939_pdu_specific
,
311 {"PDU Specific", "j1939.pdu_specific",
312 FT_UINT32
, BASE_DEC
, NULL
, 0x0000FF00, NULL
, HFILL
}
314 { &hf_j1939_src_addr
,
315 {"Source Address", "j1939.src_addr",
316 FT_UINT32
, BASE_CUSTOM
, CF_FUNC(j1939_fmt_address
), 0x000000FF, NULL
, HFILL
}
318 { &hf_j1939_dst_addr
,
319 {"Destination Address", "j1939.dst_addr",
320 FT_UINT32
, BASE_CUSTOM
, CF_FUNC(j1939_fmt_address
), 0x0000FF00, NULL
, HFILL
}
322 { &hf_j1939_group_extension
,
323 {"Group Extension", "j1939.group_extension",
324 FT_UINT32
, BASE_DEC
, NULL
, 0x0000FF00, NULL
, HFILL
}
327 {"Data", "j1939.data",
328 FT_BYTES
, BASE_NONE
|BASE_ALLOW_ZERO
, NULL
, 0x0, NULL
, HFILL
}
332 static int *ett
[] = {
338 proto_j1939
= proto_register_protocol("SAE J1939", "J1939", "j1939");
340 proto_register_field_array(proto_j1939
, hf
, array_length(hf
));
341 proto_register_subtree_array(ett
, array_length(ett
));
343 subdissector_pgn_table
= register_dissector_table("j1939.pgn", "PGN Handle", proto_j1939
, FT_UINT32
, BASE_DEC
);
345 j1939_address_type
= address_type_dissector_register("AT_J1939", "J1939 Address", J1939_addr_to_str
, J1939_addr_str_len
, NULL
, J1939_col_filter_str
, J1939_addr_len
, NULL
, NULL
);
347 j1939_handle
= register_dissector("j1939", dissect_j1939
, proto_j1939
);
351 proto_reg_handoff_j1939(void)
353 dissector_add_for_decode_as("can.subdissector", j1939_handle
);
357 * Editor modelines - https://www.wireshark.org/tools/modelines.html
362 * indent-tabs-mode: nil
365 * vi: set shiftwidth=4 tabstop=8 expandtab:
366 * :indentSize=4:tabSize=8:noTabs=true: