/src/wireshark/wiretap/socketcan.c
Line | Count | Source |
1 | | /** @file |
2 | | * |
3 | | * Common functionality for all wiretaps handling SocketCAN encapsulation |
4 | | * |
5 | | * Wiretap Library |
6 | | * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu> |
7 | | * |
8 | | * SPDX-License-Identifier: GPL-2.0-or-later |
9 | | */ |
10 | | |
11 | | |
12 | | #include "config.h" |
13 | | #include "socketcan.h" |
14 | | #include <epan/dissectors/packet-socketcan.h> |
15 | | |
16 | | typedef struct can_frame { |
17 | | uint32_t can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */ |
18 | | uint8_t can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */ |
19 | | uint8_t __pad; /* padding */ |
20 | | uint8_t __res0; /* reserved / padding */ |
21 | | uint8_t __res1; /* reserved / padding */ |
22 | | uint8_t data[CAN_MAX_DLEN]; |
23 | | } can_frame_t; |
24 | | |
25 | | typedef struct canfd_frame { |
26 | | uint32_t can_id; /* 32 bit CAN_ID + EFF flag */ |
27 | | uint8_t len; /* frame payload length in byte */ |
28 | | uint8_t flags; /* additional flags for CAN FD */ |
29 | | uint8_t __res0; /* reserved / padding */ |
30 | | uint8_t __res1; /* reserved / padding */ |
31 | | uint8_t data[CANFD_MAX_DLEN]; |
32 | | } canfd_frame_t; |
33 | | |
34 | | typedef struct can_priv_data { |
35 | | GHashTable* interface_ids; /* map name/description/link-layer type to interface ID */ |
36 | | unsigned num_interface_ids; /* Number of interface IDs assigned */ |
37 | | |
38 | | void (*tap_close)(void*); |
39 | | void* tap_priv; |
40 | | |
41 | | } can_priv_data_t; |
42 | | |
43 | | /* |
44 | | * Hash table to map interface name to interface ID. |
45 | | */ |
46 | | |
47 | | static gboolean |
48 | | destroy_if_name(void* key, void* value _U_, void* user_data _U_) |
49 | 0 | { |
50 | 0 | char* name = (char*)key; |
51 | |
|
52 | 0 | g_free(name); |
53 | |
|
54 | 0 | return true; |
55 | 0 | } |
56 | | |
57 | | static void |
58 | | add_new_if_name(can_priv_data_t* can_data, const char* name, void** result) |
59 | 0 | { |
60 | 0 | char* new_name; |
61 | |
|
62 | 0 | new_name = g_strdup(name); |
63 | 0 | *result = GUINT_TO_POINTER(can_data->num_interface_ids); |
64 | 0 | g_hash_table_insert(can_data->interface_ids, (void*)new_name, *result); |
65 | 0 | can_data->num_interface_ids++; |
66 | 0 | } |
67 | | |
68 | | static void |
69 | | wtap_socketcan_close(wtap* wth) |
70 | 0 | { |
71 | | //Clean up wiretap data |
72 | 0 | can_priv_data_t* data = (can_priv_data_t*)wth->priv; |
73 | 0 | if (data->tap_close != NULL) |
74 | 0 | data->tap_close(data->tap_priv); |
75 | | |
76 | | //Cleanup our interface data |
77 | 0 | g_hash_table_foreach_remove(data->interface_ids, destroy_if_name, NULL); |
78 | 0 | g_hash_table_destroy(data->interface_ids); |
79 | 0 | g_free(data); |
80 | |
|
81 | 0 | wth->priv = NULL; |
82 | 0 | } |
83 | | |
84 | | void* |
85 | | wtap_socketcan_get_private_data(wtap* wth) |
86 | 0 | { |
87 | 0 | can_priv_data_t* socket_can_data = (can_priv_data_t*)wth->priv; |
88 | 0 | return socket_can_data->tap_priv; |
89 | 0 | } |
90 | | |
91 | | void |
92 | | wtap_set_as_socketcan(wtap* wth, int file_type_subtype, int tsprec, void* tap_priv, void (*tap_close)(void*)) |
93 | 0 | { |
94 | | //Create the private data that wraps over the wiretap's private data |
95 | 0 | can_priv_data_t* socketcan_priv_data = g_new0(can_priv_data_t, 1); |
96 | 0 | socketcan_priv_data->interface_ids = g_hash_table_new(g_str_hash, g_str_equal); |
97 | |
|
98 | 0 | socketcan_priv_data->tap_priv = tap_priv; |
99 | 0 | socketcan_priv_data->tap_close = tap_close; |
100 | |
|
101 | 0 | wth->file_type_subtype = file_type_subtype; |
102 | 0 | wth->file_encap = WTAP_ENCAP_SOCKETCAN; |
103 | 0 | wth->file_tsprec = tsprec; |
104 | 0 | wth->subtype_close = wtap_socketcan_close; |
105 | 0 | wth->priv = socketcan_priv_data; |
106 | 0 | } |
107 | | |
108 | | bool |
109 | | wtap_socketcan_gen_packet(wtap* wth, wtap_rec* rec, const wtap_can_msg_t* msg, char* module_name, int* err, char** err_info) |
110 | 0 | { |
111 | 0 | bool is_fd = false, |
112 | 0 | is_eff = false, |
113 | 0 | is_rtr = false, |
114 | 0 | is_err = false; |
115 | |
|
116 | 0 | switch (msg->type) |
117 | 0 | { |
118 | 0 | case MSG_TYPE_STD: |
119 | | //No flags |
120 | 0 | break; |
121 | 0 | case MSG_TYPE_EXT: |
122 | 0 | is_eff = true; |
123 | 0 | break; |
124 | 0 | case MSG_TYPE_STD_RTR: |
125 | 0 | is_rtr = true; |
126 | 0 | break; |
127 | 0 | case MSG_TYPE_EXT_RTR: |
128 | 0 | is_rtr = is_eff = true; |
129 | 0 | break; |
130 | 0 | case MSG_TYPE_STD_FD: |
131 | 0 | is_fd = true; |
132 | 0 | break; |
133 | 0 | case MSG_TYPE_EXT_FD: |
134 | 0 | is_fd = is_eff = true; |
135 | 0 | break; |
136 | 0 | case MSG_TYPE_ERR: |
137 | 0 | is_err = true; |
138 | 0 | break; |
139 | |
|
140 | 0 | } |
141 | | |
142 | | /* Generate Exported PDU tags for the packet info */ |
143 | 0 | ws_buffer_clean(&rec->data); |
144 | |
|
145 | 0 | if (is_fd) |
146 | 0 | { |
147 | 0 | canfd_frame_t canfd_frame = { 0 }; |
148 | | |
149 | | /* |
150 | | * There's a maximum of CANFD_MAX_DLEN bytes in a CAN-FD frame. |
151 | | */ |
152 | 0 | if (msg->data.length > CANFD_MAX_DLEN) { |
153 | 0 | *err = WTAP_ERR_BAD_FILE; |
154 | 0 | if (err_info != NULL) { |
155 | 0 | *err_info = ws_strdup_printf("%s: File has %u-byte CAN FD packet, bigger than maximum of %u", |
156 | 0 | module_name, msg->data.length, CANFD_MAX_DLEN); |
157 | 0 | } |
158 | 0 | return false; |
159 | 0 | } |
160 | | |
161 | 0 | canfd_frame.can_id = g_htonl((msg->id & (is_eff ? CAN_EFF_MASK : CAN_SFF_MASK)) | |
162 | 0 | (is_eff ? CAN_EFF_FLAG : 0) | |
163 | 0 | (is_err ? CAN_ERR_FLAG : 0)); |
164 | 0 | canfd_frame.flags = msg->flags | CANFD_FDF; |
165 | 0 | canfd_frame.len = msg->data.length; |
166 | 0 | memcpy(canfd_frame.data, msg->data.data, msg->data.length); |
167 | |
|
168 | 0 | ws_buffer_append(&rec->data, (uint8_t*)&canfd_frame, sizeof(canfd_frame)); |
169 | 0 | } |
170 | 0 | else |
171 | 0 | { |
172 | 0 | can_frame_t can_frame = { 0 }; |
173 | | |
174 | | /* |
175 | | * There's a maximum of CAN_MAX_DLEN bytes in a CAN frame. |
176 | | */ |
177 | 0 | if (msg->data.length > CAN_MAX_DLEN) { |
178 | 0 | *err = WTAP_ERR_BAD_FILE; |
179 | 0 | if (err_info != NULL) { |
180 | 0 | *err_info = ws_strdup_printf("%s: File has %u-byte CAN packet, bigger than maximum of %u", |
181 | 0 | module_name, msg->data.length, CAN_MAX_DLEN); |
182 | 0 | } |
183 | 0 | return false; |
184 | 0 | } |
185 | | |
186 | 0 | can_frame.can_id = g_htonl((msg->id & (is_eff ? CAN_EFF_MASK : CAN_SFF_MASK)) | |
187 | 0 | (is_rtr ? CAN_RTR_FLAG : 0) | |
188 | 0 | (is_eff ? CAN_EFF_FLAG : 0) | |
189 | 0 | (is_err ? CAN_ERR_FLAG : 0)); |
190 | 0 | can_frame.can_dlc = msg->data.length; |
191 | 0 | memcpy(can_frame.data, msg->data.data, msg->data.length); |
192 | |
|
193 | 0 | ws_buffer_append(&rec->data, (uint8_t*)&can_frame, sizeof(can_frame)); |
194 | 0 | } |
195 | | |
196 | 0 | wtap_setup_packet_rec(rec, wth->file_encap); |
197 | 0 | rec->block = wtap_block_create(WTAP_BLOCK_PACKET); |
198 | 0 | rec->presence_flags = WTAP_HAS_TS; |
199 | 0 | rec->ts = msg->ts; |
200 | 0 | rec->tsprec = wth->file_tsprec; |
201 | |
|
202 | 0 | rec->rec_header.packet_header.caplen = (uint32_t)ws_buffer_length(&rec->data); |
203 | 0 | rec->rec_header.packet_header.len = (uint32_t)ws_buffer_length(&rec->data); |
204 | |
|
205 | 0 | if (msg->interface_id != 0xFFFFFFFF) { |
206 | 0 | rec->presence_flags |= WTAP_HAS_INTERFACE_ID; |
207 | 0 | rec->rec_header.packet_header.interface_id = msg->interface_id; |
208 | 0 | } |
209 | |
|
210 | 0 | return true; |
211 | 0 | } |
212 | | |
213 | | uint32_t |
214 | | wtap_socketcan_find_or_create_new_interface(wtap* wth, const char* name) |
215 | 0 | { |
216 | 0 | void* result = NULL; |
217 | 0 | can_priv_data_t* can_data = (can_priv_data_t*)wth->priv; |
218 | |
|
219 | 0 | if (!g_hash_table_lookup_extended(can_data->interface_ids, name, NULL, &result)) |
220 | 0 | { |
221 | 0 | wtap_block_t int_data; |
222 | 0 | wtapng_if_descr_mandatory_t* int_data_mand; |
223 | | |
224 | | /* |
225 | | * Not found; make a new entry. |
226 | | */ |
227 | 0 | add_new_if_name(can_data, name, &result); |
228 | | |
229 | | /* |
230 | | * Now make a new IDB and add it. |
231 | | */ |
232 | 0 | int_data = wtap_block_create(WTAP_BLOCK_IF_ID_AND_INFO); |
233 | 0 | int_data_mand = (wtapng_if_descr_mandatory_t*)wtap_block_get_mandatory_data(int_data); |
234 | |
|
235 | 0 | int_data_mand->wtap_encap = WTAP_ENCAP_SOCKETCAN; |
236 | 0 | int_data_mand->tsprecision = WTAP_TSPREC_USEC; |
237 | 0 | int_data_mand->time_units_per_second = 1000000; /* Microsecond resolution */ |
238 | 0 | int_data_mand->snap_len = WTAP_MAX_PACKET_SIZE_STANDARD; /* XXX - not known */ |
239 | |
|
240 | 0 | wtap_block_add_uint8_option(int_data, OPT_IDB_TSRESOL, 0x06); /* microsecond resolution */ |
241 | | /* Interface statistics */ |
242 | 0 | int_data_mand->num_stat_entries = 0; |
243 | 0 | int_data_mand->interface_statistics = NULL; |
244 | |
|
245 | 0 | wtap_block_set_string_option_value(int_data, OPT_IDB_NAME, name, strlen(name)); |
246 | 0 | wtap_add_idb(wth, int_data); |
247 | 0 | } |
248 | | return GPOINTER_TO_UINT(result); |
249 | 0 | } |