/src/wireshark/wiretap/autosar_dlt.c
Line | Count | Source |
1 | | /* autosar_dlt.c |
2 | | * |
3 | | * Wiretap Library |
4 | | * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu> |
5 | | * |
6 | | * Support for DLT file format as defined by AUTOSAR et. al. |
7 | | * Copyright (c) 2022-2022 by Dr. Lars Voelker <lars.voelker@technica-engineering.de> |
8 | | * |
9 | | * SPDX-License-Identifier: GPL-2.0-or-later |
10 | | */ |
11 | | |
12 | | /* |
13 | | * Sources for specification: |
14 | | * https://www.autosar.org/fileadmin/standards/R24-11/CP/AUTOSAR_CP_SWS_DiagnosticLogAndTrace.pdf |
15 | | * https://www.autosar.org/fileadmin/standards/R24-11/FO/AUTOSAR_FO_PRS_LogAndTraceProtocol.pdf |
16 | | * https://github.com/COVESA/dlt-viewer |
17 | | */ |
18 | | |
19 | | #include <config.h> |
20 | 0 | #define WS_LOG_DOMAIN LOG_DOMAIN_WIRETAP |
21 | | |
22 | | #include <errno.h> |
23 | | #include "autosar_dlt.h" |
24 | | |
25 | | #include "file_wrappers.h" |
26 | | #include "wtap_module.h" |
27 | | |
28 | | static const uint8_t dlt_magic[] = { 'D', 'L', 'T', 0x01 }; |
29 | | |
30 | | static int autosar_dlt_file_type_subtype = -1; |
31 | | |
32 | | |
33 | | void register_autosar_dlt(void); |
34 | | static bool autosar_dlt_read(wtap *wth, wtap_rec *rec, int *err, char **err_info, int64_t *data_offset); |
35 | | static bool autosar_dlt_seek_read(wtap *wth, int64_t seek_off, wtap_rec* rec, int *err, char **err_info); |
36 | | static void autosar_dlt_close(wtap *wth); |
37 | | |
38 | | |
39 | | typedef struct autosar_dlt_blockheader { |
40 | | uint8_t magic[4]; |
41 | | uint32_t timestamp_s; |
42 | | uint32_t timestamp_us; |
43 | | uint8_t ecu_id[4]; |
44 | | } autosar_dlt_blockheader_t; |
45 | | |
46 | | typedef struct autosar_dlt_itemheader { |
47 | | uint8_t header_type; |
48 | | uint8_t counter; |
49 | | uint16_t length; |
50 | | } autosar_dlt_itemheader_t; |
51 | | |
52 | | |
53 | | typedef struct autosar_dlt_data { |
54 | | GHashTable *ecu_to_iface_ht; |
55 | | uint32_t next_interface_id; |
56 | | } autosar_dlt_t; |
57 | | |
58 | | typedef struct autosar_dlt_params { |
59 | | wtap *wth; |
60 | | wtap_rec *rec; |
61 | | FILE_T fh; |
62 | | |
63 | | autosar_dlt_t *dlt_data; |
64 | | } autosar_dlt_params_t; |
65 | | |
66 | | static int |
67 | 0 | autosar_dlt_calc_key(uint8_t ecu[4]) { |
68 | 0 | return (int)(ecu[0] << 24 | ecu[1] << 16 | ecu[2] << 8 | ecu[3]); |
69 | 0 | } |
70 | | |
71 | | static uint32_t |
72 | 0 | autosar_dlt_add_interface(autosar_dlt_params_t *params, uint8_t ecu[4]) { |
73 | 0 | wtap_block_t int_data = wtap_block_create(WTAP_BLOCK_IF_ID_AND_INFO); |
74 | 0 | wtapng_if_descr_mandatory_t *if_descr_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(int_data); |
75 | |
|
76 | 0 | if_descr_mand->wtap_encap = WTAP_ENCAP_AUTOSAR_DLT; |
77 | 0 | wtap_block_add_string_option(int_data, OPT_IDB_NAME, (char *)ecu, 4); |
78 | 0 | if_descr_mand->time_units_per_second = 1000 * 1000 * 1000; |
79 | 0 | if_descr_mand->tsprecision = WTAP_TSPREC_NSEC; |
80 | 0 | wtap_block_add_uint8_option(int_data, OPT_IDB_TSRESOL, 9); |
81 | 0 | if_descr_mand->snap_len = WTAP_MAX_PACKET_SIZE_STANDARD; |
82 | 0 | if_descr_mand->num_stat_entries = 0; |
83 | 0 | if_descr_mand->interface_statistics = NULL; |
84 | 0 | wtap_add_idb(params->wth, int_data); |
85 | |
|
86 | 0 | int32_t key = autosar_dlt_calc_key(ecu); |
87 | 0 | uint32_t iface_id = params->dlt_data->next_interface_id++; |
88 | 0 | g_hash_table_insert(params->dlt_data->ecu_to_iface_ht, GINT_TO_POINTER(key), GUINT_TO_POINTER(iface_id)); |
89 | |
|
90 | 0 | return iface_id; |
91 | 0 | } |
92 | | |
93 | | static uint32_t |
94 | 0 | autosar_dlt_lookup_interface(autosar_dlt_params_t *params, uint8_t ecu[4]) { |
95 | 0 | int32_t key = autosar_dlt_calc_key(ecu); |
96 | |
|
97 | 0 | if (params->dlt_data->ecu_to_iface_ht == NULL) { |
98 | 0 | return 0; |
99 | 0 | } |
100 | | |
101 | 0 | void *iface = NULL; |
102 | 0 | bool found = g_hash_table_lookup_extended(params->dlt_data->ecu_to_iface_ht, GINT_TO_POINTER(key), NULL, &iface); |
103 | |
|
104 | 0 | if (found) { |
105 | 0 | return GPOINTER_TO_UINT(iface); |
106 | 0 | } else { |
107 | 0 | return autosar_dlt_add_interface(params, ecu); |
108 | 0 | } |
109 | 0 | } |
110 | | |
111 | | static void |
112 | 0 | fix_endianness_autosar_dlt_blockheader(autosar_dlt_blockheader_t *header) { |
113 | 0 | header->timestamp_s = GUINT32_FROM_LE(header->timestamp_s); |
114 | 0 | header->timestamp_us = GUINT32_FROM_LE(header->timestamp_us); |
115 | 0 | } |
116 | | |
117 | | static void |
118 | 0 | fix_endianness_autosar_dlt_itemheader(autosar_dlt_itemheader_t *header) { |
119 | 0 | header->length = GUINT16_FROM_BE(header->length); |
120 | 0 | } |
121 | | |
122 | | static bool |
123 | 0 | autosar_dlt_read_block(autosar_dlt_params_t *params, int64_t start_pos, int *err, char **err_info) { |
124 | 0 | autosar_dlt_blockheader_t header; |
125 | 0 | autosar_dlt_itemheader_t item_header; |
126 | |
|
127 | 0 | if (!wtap_read_bytes_or_eof(params->fh, &header, sizeof header, err, err_info)) { |
128 | 0 | if (*err == WTAP_ERR_SHORT_READ) { |
129 | 0 | *err = WTAP_ERR_BAD_FILE; |
130 | 0 | g_free(*err_info); |
131 | 0 | *err_info = ws_strdup_printf("AUTOSAR DLT: Capture file cut short! Cannot find storage header at pos 0x%" PRIx64 "!", start_pos); |
132 | 0 | } |
133 | 0 | return false; |
134 | 0 | } |
135 | | |
136 | 0 | fix_endianness_autosar_dlt_blockheader(&header); |
137 | |
|
138 | 0 | if (memcmp(header.magic, dlt_magic, sizeof(dlt_magic))) { |
139 | 0 | *err = WTAP_ERR_BAD_FILE; |
140 | 0 | *err_info = ws_strdup_printf("AUTOSAR DLT: Bad capture file! Object magic is not DLT\\x01 at pos 0x%" PRIx64 "!", start_pos); |
141 | 0 | return false; |
142 | 0 | } |
143 | | |
144 | | /* Set to the byte after the magic. */ |
145 | 0 | uint64_t current_start_of_item = file_tell(params->fh) - sizeof header + 4; |
146 | |
|
147 | 0 | if (!wtap_read_bytes_or_eof(params->fh, &item_header, sizeof item_header, err, err_info)) { |
148 | 0 | *err = WTAP_ERR_BAD_FILE; |
149 | 0 | g_free(*err_info); |
150 | 0 | *err_info = ws_strdup_printf("AUTOSAR DLT: Capture file cut short! Not enough bytes for item header at pos 0x%" PRIx64 "!", start_pos); |
151 | 0 | return false; |
152 | 0 | } |
153 | | |
154 | 0 | fix_endianness_autosar_dlt_itemheader(&item_header); |
155 | |
|
156 | 0 | if (file_seek(params->fh, current_start_of_item, SEEK_SET, err) < 0) { |
157 | 0 | return false; |
158 | 0 | } |
159 | | |
160 | 0 | ws_buffer_assure_space(¶ms->rec->data, (size_t)(item_header.length + sizeof header)); |
161 | | |
162 | | /* Creating AUTOSAR DLT Encapsulation Header: |
163 | | * uint32_t time_s |
164 | | * uint32_t time_us |
165 | | * uint8_t[4] ecuname |
166 | | * uint8_t[1] 0x00 (termination) |
167 | | * uint8_t[3] reserved |
168 | | */ |
169 | 0 | void *tmpbuf = g_malloc0(sizeof header); |
170 | 0 | if (!wtap_read_bytes_or_eof(params->fh, tmpbuf, sizeof header - 4, err, err_info)) { |
171 | | /* this would have been caught before ...*/ |
172 | 0 | g_free(tmpbuf); |
173 | 0 | *err = WTAP_ERR_BAD_FILE; |
174 | 0 | g_free(*err_info); |
175 | 0 | *err_info = ws_strdup_printf("AUTOSAR DLT: Internal Error! Not enough bytes for storage header at pos 0x%" PRIx64 "!", start_pos); |
176 | 0 | return false; |
177 | 0 | } |
178 | 0 | ws_buffer_append(¶ms->rec->data, tmpbuf, (size_t)(sizeof header)); |
179 | 0 | g_free(tmpbuf); |
180 | |
|
181 | 0 | tmpbuf = g_try_malloc0(item_header.length); |
182 | 0 | if (tmpbuf == NULL) { |
183 | 0 | *err = ENOMEM; /* we assume we're out of memory */ |
184 | 0 | return false; |
185 | 0 | } |
186 | | |
187 | 0 | if (!wtap_read_bytes_or_eof(params->fh, tmpbuf, item_header.length, err, err_info)) { |
188 | 0 | g_free(tmpbuf); |
189 | 0 | *err = WTAP_ERR_BAD_FILE; |
190 | 0 | g_free(*err_info); |
191 | 0 | *err_info = ws_strdup_printf("AUTOSAR DLT: Capture file cut short! Not enough bytes for item at pos 0x%" PRIx64 "!", start_pos); |
192 | 0 | return false; |
193 | 0 | } |
194 | 0 | ws_buffer_append(¶ms->rec->data, tmpbuf, (size_t)(item_header.length)); |
195 | 0 | g_free(tmpbuf); |
196 | |
|
197 | 0 | wtap_setup_packet_rec(params->rec, WTAP_ENCAP_AUTOSAR_DLT); |
198 | 0 | params->rec->block = wtap_block_create(WTAP_BLOCK_PACKET); |
199 | 0 | params->rec->presence_flags = WTAP_HAS_TS | WTAP_HAS_CAP_LEN | WTAP_HAS_INTERFACE_ID; |
200 | 0 | params->rec->tsprec = WTAP_TSPREC_USEC; |
201 | 0 | params->rec->ts.secs = header.timestamp_s; |
202 | 0 | params->rec->ts.nsecs = header.timestamp_us * 1000; |
203 | |
|
204 | 0 | params->rec->rec_header.packet_header.caplen = (uint32_t)(item_header.length + sizeof header); |
205 | 0 | params->rec->rec_header.packet_header.len = (uint32_t)(item_header.length + sizeof header); |
206 | 0 | params->rec->rec_header.packet_header.interface_id = autosar_dlt_lookup_interface(params, header.ecu_id); |
207 | |
|
208 | 0 | return true; |
209 | 0 | } |
210 | | |
211 | 0 | static bool autosar_dlt_read(wtap *wth, wtap_rec *rec, int *err, char **err_info, int64_t *data_offset) { |
212 | 0 | autosar_dlt_params_t dlt_tmp; |
213 | |
|
214 | 0 | dlt_tmp.wth = wth; |
215 | 0 | dlt_tmp.fh = wth->fh; |
216 | 0 | dlt_tmp.rec = rec; |
217 | 0 | dlt_tmp.dlt_data = (autosar_dlt_t *)wth->priv; |
218 | |
|
219 | 0 | *data_offset = file_tell(wth->fh); |
220 | |
|
221 | 0 | if (!autosar_dlt_read_block(&dlt_tmp, *data_offset, err, err_info)) { |
222 | 0 | ws_debug("couldn't read packet block (data_offset is %" PRId64 ")", *data_offset); |
223 | 0 | return false; |
224 | 0 | } |
225 | | |
226 | 0 | return true; |
227 | 0 | } |
228 | | |
229 | 0 | static bool autosar_dlt_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec, int *err, char **err_info) { |
230 | 0 | autosar_dlt_params_t dlt_tmp; |
231 | |
|
232 | 0 | dlt_tmp.wth = wth; |
233 | 0 | dlt_tmp.fh = wth->random_fh; |
234 | 0 | dlt_tmp.rec = rec; |
235 | 0 | dlt_tmp.dlt_data = (autosar_dlt_t *)wth->priv; |
236 | |
|
237 | 0 | if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) |
238 | 0 | return false; |
239 | | |
240 | 0 | if (!autosar_dlt_read_block(&dlt_tmp, seek_off, err, err_info)) { |
241 | 0 | ws_debug("couldn't read packet block (seek_off: %" PRId64 ") (err=%d).", seek_off, *err); |
242 | 0 | return false; |
243 | 0 | } |
244 | | |
245 | 0 | return true; |
246 | 0 | } |
247 | | |
248 | 0 | static void autosar_dlt_close(wtap *wth) { |
249 | 0 | autosar_dlt_t *dlt = (autosar_dlt_t *)wth->priv; |
250 | |
|
251 | 0 | if (dlt != NULL && dlt->ecu_to_iface_ht != NULL) { |
252 | 0 | g_hash_table_destroy(dlt->ecu_to_iface_ht); |
253 | 0 | dlt->ecu_to_iface_ht = NULL; |
254 | 0 | } |
255 | |
|
256 | 0 | g_free(dlt); |
257 | 0 | wth->priv = NULL; |
258 | |
|
259 | 0 | return; |
260 | 0 | } |
261 | | |
262 | | wtap_open_return_val |
263 | 0 | autosar_dlt_open(wtap *wth, int *err, char **err_info) { |
264 | 0 | uint8_t magic[4]; |
265 | 0 | autosar_dlt_t *dlt; |
266 | |
|
267 | 0 | ws_debug("opening file"); |
268 | |
|
269 | 0 | if (!wtap_read_bytes_or_eof(wth->fh, &magic, sizeof magic, err, err_info)) { |
270 | 0 | ws_debug("wtap_read_bytes_or_eof() failed, err = %d.", *err); |
271 | 0 | if (*err == 0 || *err == WTAP_ERR_SHORT_READ) { |
272 | 0 | *err = 0; |
273 | 0 | g_free(*err_info); |
274 | 0 | *err_info = NULL; |
275 | 0 | return WTAP_OPEN_NOT_MINE; |
276 | 0 | } |
277 | 0 | return WTAP_OPEN_ERROR; |
278 | 0 | } |
279 | | |
280 | 0 | if (memcmp(magic, dlt_magic, sizeof(dlt_magic))) { |
281 | 0 | return WTAP_OPEN_NOT_MINE; |
282 | 0 | } |
283 | | |
284 | 0 | if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) { |
285 | 0 | return WTAP_OPEN_ERROR; |
286 | 0 | } |
287 | | |
288 | 0 | dlt = g_new(autosar_dlt_t, 1); |
289 | 0 | dlt->ecu_to_iface_ht = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL); |
290 | 0 | dlt->next_interface_id = 0; |
291 | |
|
292 | 0 | wth->priv = (void *)dlt; |
293 | 0 | wth->file_encap = WTAP_ENCAP_AUTOSAR_DLT; |
294 | 0 | wth->snapshot_length = 0; |
295 | 0 | wth->file_tsprec = WTAP_TSPREC_UNKNOWN; |
296 | 0 | wth->subtype_read = autosar_dlt_read; |
297 | 0 | wth->subtype_seek_read = autosar_dlt_seek_read; |
298 | 0 | wth->subtype_close = autosar_dlt_close; |
299 | 0 | wth->file_type_subtype = autosar_dlt_file_type_subtype; |
300 | |
|
301 | 0 | return WTAP_OPEN_MINE; |
302 | 0 | } |
303 | | |
304 | | /* Options for interface blocks. */ |
305 | | static const struct supported_option_type interface_block_options_supported[] = { |
306 | | /* No comments, just an interface name. */ |
307 | | { OPT_IDB_NAME, ONE_OPTION_SUPPORTED } |
308 | | }; |
309 | | |
310 | | static const struct supported_block_type dlt_blocks_supported[] = { |
311 | | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }, |
312 | | { WTAP_BLOCK_IF_ID_AND_INFO, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(interface_block_options_supported) }, |
313 | | }; |
314 | | |
315 | | static const struct file_type_subtype_info dlt_info = { |
316 | | "AUTOSAR DLT Logfile", "dlt", "dlt", NULL, |
317 | | false, BLOCKS_SUPPORTED(dlt_blocks_supported), |
318 | | NULL, NULL, NULL |
319 | | }; |
320 | | |
321 | | void register_autosar_dlt(void) |
322 | 15 | { |
323 | 15 | autosar_dlt_file_type_subtype = wtap_register_file_type_subtype(&dlt_info); |
324 | | |
325 | | /* |
326 | | * Register name for backwards compatibility with the |
327 | | * wtap_filetypes table in Lua. |
328 | | */ |
329 | 15 | wtap_register_backwards_compatibility_lua_name("DLT", autosar_dlt_file_type_subtype); |
330 | 15 | } |
331 | | |
332 | | |
333 | | /* |
334 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
335 | | * |
336 | | * Local variables: |
337 | | * c-basic-offset: 4 |
338 | | * tab-width: 8 |
339 | | * indent-tabs-mode: nil |
340 | | * End: |
341 | | * |
342 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
343 | | * :indentSize=4:tabSize=8:noTabs=true: |
344 | | */ |