/src/wireshark/epan/dissectors/packet-collectd.c
Line | Count | Source |
1 | | /* packet-collectd.c |
2 | | * Routines for collectd (http://collectd.org/) network plugin dissection |
3 | | * |
4 | | * https://github.com/collectd/collectd/wiki/Binary-protocol |
5 | | * |
6 | | * Copyright 2008 Bruno Premont <bonbons at linux-vserver.org> |
7 | | * Copyright 2009-2013 Florian Forster <octo at collectd.org> |
8 | | * |
9 | | * Wireshark - Network traffic analyzer |
10 | | * By Gerald Combs <gerald@wireshark.org> |
11 | | * Copyright 1998 Gerald Combs |
12 | | * |
13 | | * SPDX-License-Identifier: GPL-2.0-or-later |
14 | | */ |
15 | | |
16 | | #include "config.h" |
17 | | |
18 | | #include <epan/packet.h> |
19 | | #include <epan/expert.h> |
20 | | #include <epan/proto_data.h> |
21 | | #include <epan/stats_tree.h> |
22 | | #include <epan/to_str.h> |
23 | | #include <epan/uat.h> |
24 | | #include <epan/exceptions.h> |
25 | | |
26 | | #include <wsutil/str_util.h> |
27 | | #include <wsutil/wsgcrypt.h> |
28 | | |
29 | 0 | #define STR_NONNULL(str) ((str) ? ((const char*)str) : "(null)") |
30 | | |
31 | 0 | #define TYPE_HOST 0x0000 |
32 | 0 | #define TYPE_TIME 0x0001 |
33 | 0 | #define TYPE_TIME_HR 0x0008 |
34 | 0 | #define TYPE_PLUGIN 0x0002 |
35 | 0 | #define TYPE_PLUGIN_INSTANCE 0x0003 |
36 | 0 | #define TYPE_TYPE 0x0004 |
37 | 0 | #define TYPE_TYPE_INSTANCE 0x0005 |
38 | 0 | #define TYPE_VALUES 0x0006 |
39 | 0 | #define TYPE_INTERVAL 0x0007 |
40 | 0 | #define TYPE_INTERVAL_HR 0x0009 |
41 | 0 | #define TYPE_MESSAGE 0x0100 |
42 | 0 | #define TYPE_SEVERITY 0x0101 |
43 | 0 | #define TYPE_SIGN_SHA256 0x0200 |
44 | 0 | #define TYPE_ENCR_AES256 0x0210 |
45 | | |
46 | | void proto_register_collectd(void); |
47 | | |
48 | | static dissector_handle_t collectd_handle; |
49 | | |
50 | 6 | #define TAP_DATA_KEY 0 |
51 | 6 | #define COL_DATA_KEY 1 |
52 | | |
53 | | typedef struct value_data_s { |
54 | | const char *host; |
55 | | int host_off; |
56 | | int host_len; |
57 | | uint64_t time_value; |
58 | | int time_off; |
59 | | uint64_t interval; |
60 | | int interval_off; |
61 | | const char *plugin; |
62 | | int plugin_off; |
63 | | int plugin_len; |
64 | | const char *plugin_instance; |
65 | | int plugin_instance_off; |
66 | | int plugin_instance_len; |
67 | | const char *type; |
68 | | int type_off; |
69 | | int type_len; |
70 | | const char *type_instance; |
71 | | int type_instance_off; |
72 | | int type_instance_len; |
73 | | } value_data_t; |
74 | | |
75 | | typedef struct notify_data_s { |
76 | | const char *host; |
77 | | int host_off; |
78 | | int host_len; |
79 | | uint64_t time_value; |
80 | | int time_off; |
81 | | uint64_t severity; |
82 | | int severity_off; |
83 | | const char *message; |
84 | | int message_off; |
85 | | int message_len; |
86 | | } notify_data_t; |
87 | | |
88 | | struct string_counter_s; |
89 | | typedef struct string_counter_s string_counter_t; |
90 | | struct string_counter_s |
91 | | { |
92 | | const char *string; |
93 | | int count; |
94 | | string_counter_t *next; |
95 | | }; |
96 | | |
97 | | typedef struct tap_data_s { |
98 | | int values_num; |
99 | | |
100 | | string_counter_t *hosts; |
101 | | string_counter_t *plugins; |
102 | | string_counter_t *types; |
103 | | } tap_data_t; |
104 | | |
105 | | typedef struct column_data_s { |
106 | | unsigned pkt_plugins; |
107 | | unsigned pkt_values; |
108 | | unsigned pkt_messages; |
109 | | unsigned pkt_unknown; |
110 | | unsigned pkt_errors; |
111 | | |
112 | | const char *pkt_host; |
113 | | } column_data_t; |
114 | | |
115 | | static const value_string part_names[] = { |
116 | | { TYPE_VALUES, "VALUES" }, |
117 | | { TYPE_TIME, "TIME" }, |
118 | | { TYPE_TIME_HR, "TIME_HR" }, |
119 | | { TYPE_INTERVAL, "INTERVAL" }, |
120 | | { TYPE_INTERVAL_HR, "INTERVAL_HR" }, |
121 | | { TYPE_HOST, "HOST" }, |
122 | | { TYPE_PLUGIN, "PLUGIN" }, |
123 | | { TYPE_PLUGIN_INSTANCE, "PLUGIN_INSTANCE" }, |
124 | | { TYPE_TYPE, "TYPE" }, |
125 | | { TYPE_TYPE_INSTANCE, "TYPE_INSTANCE" }, |
126 | | { TYPE_MESSAGE, "MESSAGE" }, |
127 | | { TYPE_SEVERITY, "SEVERITY" }, |
128 | | { TYPE_SIGN_SHA256, "SIGNATURE" }, |
129 | | { TYPE_ENCR_AES256, "ENCRYPTED_DATA" }, |
130 | | { 0, NULL } |
131 | | }; |
132 | | |
133 | 0 | #define TYPE_VALUE_COUNTER 0x00 |
134 | 0 | #define TYPE_VALUE_GAUGE 0x01 |
135 | 0 | #define TYPE_VALUE_DERIVE 0x02 |
136 | 0 | #define TYPE_VALUE_ABSOLUTE 0x03 |
137 | | static const value_string valuetypenames[] = { |
138 | | { TYPE_VALUE_COUNTER, "COUNTER" }, |
139 | | { TYPE_VALUE_GAUGE, "GAUGE" }, |
140 | | { TYPE_VALUE_DERIVE, "DERIVE" }, |
141 | | { TYPE_VALUE_ABSOLUTE, "ABSOLUTE" }, |
142 | | { 0, NULL } |
143 | | }; |
144 | | |
145 | | #define SEVERITY_FAILURE 0x01 |
146 | | #define SEVERITY_WARNING 0x02 |
147 | | #define SEVERITY_OKAY 0x04 |
148 | | static const val64_string severity_names[] = { |
149 | | { SEVERITY_FAILURE, "FAILURE" }, |
150 | | { SEVERITY_WARNING, "WARNING" }, |
151 | | { SEVERITY_OKAY, "OKAY" }, |
152 | | { 0, NULL } |
153 | | }; |
154 | | |
155 | 14 | #define UDP_PORT_COLLECTD 25826 /* Not IANA registered */ |
156 | | |
157 | | static int proto_collectd; |
158 | | static int tap_collectd = -1; |
159 | | |
160 | | static int hf_collectd_type; |
161 | | static int hf_collectd_length; |
162 | | static int hf_collectd_data; |
163 | | static int hf_collectd_data_host; |
164 | | static int hf_collectd_data_time; |
165 | | static int hf_collectd_data_interval; |
166 | | static int hf_collectd_data_plugin; |
167 | | static int hf_collectd_data_plugin_inst; |
168 | | static int hf_collectd_data_type; |
169 | | static int hf_collectd_data_type_inst; |
170 | | static int hf_collectd_data_valcnt; |
171 | | static int hf_collectd_val_type; |
172 | | static int hf_collectd_val_counter; |
173 | | static int hf_collectd_val_gauge; |
174 | | static int hf_collectd_val_derive; |
175 | | static int hf_collectd_val_absolute; |
176 | | static int hf_collectd_val_unknown; |
177 | | static int hf_collectd_data_severity; |
178 | | static int hf_collectd_data_message; |
179 | | static int hf_collectd_data_sighash; |
180 | | static int hf_collectd_data_sighash_status; |
181 | | static int hf_collectd_data_initvec; |
182 | | static int hf_collectd_data_username_len; |
183 | | static int hf_collectd_data_username; |
184 | | static int hf_collectd_data_encrypted; |
185 | | |
186 | | static int ett_collectd; |
187 | | static int ett_collectd_string; |
188 | | static int ett_collectd_integer; |
189 | | static int ett_collectd_part_value; |
190 | | static int ett_collectd_value; |
191 | | static int ett_collectd_valinfo; |
192 | | static int ett_collectd_signature; |
193 | | static int ett_collectd_encryption; |
194 | | static int ett_collectd_dispatch; |
195 | | static int ett_collectd_invalid_length; |
196 | | static int ett_collectd_unknown; |
197 | | |
198 | | static int st_collectd_packets = -1; |
199 | | static int st_collectd_values = -1; |
200 | | static int st_collectd_values_hosts = -1; |
201 | | static int st_collectd_values_plugins = -1; |
202 | | static int st_collectd_values_types = -1; |
203 | | |
204 | | static expert_field ei_collectd_type; |
205 | | static expert_field ei_collectd_invalid_length; |
206 | | static expert_field ei_collectd_data_valcnt; |
207 | | static expert_field ei_collectd_garbage; |
208 | | static expert_field ei_collectd_sighash_bad; |
209 | | |
210 | | /* Prototype for the handoff function */ |
211 | | void proto_reg_handoff_collectd (void); |
212 | | |
213 | | typedef struct { |
214 | | char *username; |
215 | | char *password; |
216 | | |
217 | | bool cipher_hd_created; |
218 | | bool md_hd_created; |
219 | | gcry_cipher_hd_t cipher_hd; |
220 | | gcry_md_hd_t md_hd; |
221 | | |
222 | | } uat_collectd_record_t; |
223 | | |
224 | | static uat_collectd_record_t *uat_collectd_records; |
225 | | |
226 | | static uat_t *collectd_uat; |
227 | | static unsigned num_uat; |
228 | | |
229 | | UAT_CSTRING_CB_DEF(uat_collectd_records, username, uat_collectd_record_t) |
230 | | UAT_CSTRING_CB_DEF(uat_collectd_records, password, uat_collectd_record_t) |
231 | | |
232 | | static void* |
233 | 0 | uat_collectd_record_copy_cb(void* n, const void* o, size_t size _U_) { |
234 | 0 | uat_collectd_record_t* new_rec = (uat_collectd_record_t *)n; |
235 | 0 | const uat_collectd_record_t* old_rec = (const uat_collectd_record_t *)o; |
236 | |
|
237 | 0 | new_rec->username = g_strdup(old_rec->username); |
238 | 0 | new_rec->password = g_strdup(old_rec->password); |
239 | |
|
240 | 0 | new_rec->cipher_hd_created = FALSE; |
241 | 0 | new_rec->md_hd_created = FALSE; |
242 | |
|
243 | 0 | return new_rec; |
244 | 0 | } |
245 | | |
246 | | static bool |
247 | 0 | uat_collectd_record_update_cb(void* r, char** err _U_) { |
248 | 0 | uat_collectd_record_t* rec = (uat_collectd_record_t *)r; |
249 | |
|
250 | 0 | if (rec->cipher_hd_created) { |
251 | 0 | gcry_cipher_close(rec->cipher_hd); |
252 | 0 | rec->cipher_hd_created = false; |
253 | 0 | } |
254 | 0 | if (rec->md_hd_created) { |
255 | 0 | gcry_md_close(rec->md_hd); |
256 | 0 | rec->md_hd_created = false; |
257 | 0 | } |
258 | |
|
259 | 0 | return true; |
260 | 0 | } |
261 | | |
262 | | static void |
263 | 0 | uat_collectd_record_free_cb(void* r) { |
264 | 0 | uat_collectd_record_t* rec = (uat_collectd_record_t *)r; |
265 | |
|
266 | 0 | g_free(rec->username); |
267 | 0 | g_free(rec->password); |
268 | |
|
269 | 0 | if (rec->cipher_hd_created) { |
270 | 0 | gcry_cipher_close(rec->cipher_hd); |
271 | 0 | rec->cipher_hd_created = false; |
272 | 0 | } |
273 | 0 | if (rec->md_hd_created) { |
274 | 0 | gcry_md_close(rec->md_hd); |
275 | 0 | rec->md_hd_created = false; |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | | static uat_collectd_record_t* |
280 | | collectd_get_record(const char* username) |
281 | 0 | { |
282 | 0 | uat_collectd_record_t *record = NULL; |
283 | 0 | for (unsigned i = 0; i < num_uat; ++i) { |
284 | 0 | record = &uat_collectd_records[i]; |
285 | 0 | if (strcmp(username, record->username) == 0) { |
286 | 0 | return record; |
287 | 0 | } |
288 | 0 | } |
289 | 0 | return NULL; |
290 | 0 | } |
291 | | |
292 | | static gcry_cipher_hd_t* |
293 | | collectd_get_cipher(const char* username) |
294 | 0 | { |
295 | 0 | uat_collectd_record_t *record = collectd_get_record(username); |
296 | 0 | if (record == NULL) { |
297 | 0 | return NULL; |
298 | 0 | } |
299 | 0 | if (record->cipher_hd_created) { |
300 | 0 | return &record->cipher_hd; |
301 | 0 | } |
302 | 0 | gcry_error_t err; |
303 | 0 | unsigned char password_hash[32]; |
304 | 0 | DISSECTOR_ASSERT(record->password); |
305 | 0 | gcry_md_hash_buffer(GCRY_MD_SHA256, password_hash, record->password, strlen(record->password)); |
306 | 0 | if (gcry_cipher_open(&record->cipher_hd, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, 0)) { |
307 | 0 | gcry_cipher_close(record->cipher_hd); |
308 | 0 | ws_debug("error opening aes256 cipher handle"); |
309 | 0 | return NULL; |
310 | 0 | } |
311 | | |
312 | 0 | err = gcry_cipher_setkey(record->cipher_hd, password_hash, sizeof(password_hash)); |
313 | 0 | if (err != 0) { |
314 | 0 | gcry_cipher_close(record->cipher_hd); |
315 | 0 | ws_debug("error setting key"); |
316 | 0 | return NULL; |
317 | 0 | } |
318 | 0 | record->cipher_hd_created = true; |
319 | 0 | return &record->cipher_hd; |
320 | 0 | } |
321 | | |
322 | | static gcry_md_hd_t* |
323 | | collectd_get_md(const char* username) |
324 | 0 | { |
325 | 0 | uat_collectd_record_t *record = collectd_get_record(username); |
326 | 0 | if (record == NULL) { |
327 | 0 | return NULL; |
328 | 0 | } |
329 | 0 | if (record->md_hd_created) { |
330 | 0 | gcry_md_reset(record->md_hd); |
331 | 0 | return &record->md_hd; |
332 | 0 | } |
333 | 0 | gcry_error_t err; |
334 | 0 | DISSECTOR_ASSERT(record->password); |
335 | 0 | err = gcry_md_open(&record->md_hd, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); |
336 | 0 | if (err != 0) { |
337 | 0 | gcry_md_close(record->md_hd); |
338 | 0 | ws_debug("error opening sha256 message digest handle: %s", gcry_strerror(err)); |
339 | 0 | return NULL; |
340 | 0 | } |
341 | | |
342 | 0 | err = gcry_md_setkey(record->md_hd, record->password, strlen(record->password)); |
343 | 0 | if (err != 0) { |
344 | 0 | gcry_md_close(record->md_hd); |
345 | 0 | ws_debug("error setting key: %s", gcry_strerror(err)); |
346 | 0 | return NULL; |
347 | 0 | } |
348 | 0 | record->md_hd_created = true; |
349 | 0 | return &record->md_hd; |
350 | 0 | } |
351 | | |
352 | | static nstime_t |
353 | | collectd_time_to_nstime (uint64_t t) |
354 | 0 | { |
355 | 0 | nstime_t nstime = NSTIME_INIT_ZERO; |
356 | 0 | nstime.secs = (time_t) (t / 1073741824); |
357 | 0 | nstime.nsecs = (int) (((double) (t % 1073741824)) / 1.073741824); |
358 | |
|
359 | 0 | return (nstime); |
360 | 0 | } |
361 | | |
362 | | static void |
363 | | collectd_stats_tree_init (stats_tree *st) |
364 | 0 | { |
365 | 0 | st_collectd_packets = stats_tree_create_node (st, "Packets", 0, STAT_DT_INT, false); |
366 | 0 | st_collectd_values = stats_tree_create_node (st, "Values", 0, STAT_DT_INT, true); |
367 | |
|
368 | 0 | st_collectd_values_hosts = stats_tree_create_pivot (st, "By host", |
369 | 0 | st_collectd_values); |
370 | 0 | st_collectd_values_plugins = stats_tree_create_pivot (st, "By plugin", |
371 | 0 | st_collectd_values); |
372 | 0 | st_collectd_values_types = stats_tree_create_pivot (st, "By type", |
373 | 0 | st_collectd_values); |
374 | 0 | } /* void collectd_stats_tree_init */ |
375 | | |
376 | | static tap_packet_status |
377 | | collectd_stats_tree_packet (stats_tree *st, packet_info *pinfo _U_, |
378 | | epan_dissect_t *edt _U_, const void *user_data, tap_flags_t flags _U_) |
379 | 0 | { |
380 | 0 | const tap_data_t *td; |
381 | 0 | string_counter_t *sc; |
382 | |
|
383 | 0 | td = (const tap_data_t *)user_data; |
384 | 0 | if (td == NULL) |
385 | 0 | return (TAP_PACKET_DONT_REDRAW); |
386 | | |
387 | 0 | tick_stat_node (st, "Packets", 0, false); |
388 | 0 | increase_stat_node (st, "Values", 0, true, td->values_num); |
389 | |
|
390 | 0 | for (sc = td->hosts; sc != NULL; sc = sc->next) |
391 | 0 | { |
392 | 0 | int i; |
393 | 0 | for (i = 0; i < sc->count; i++) |
394 | 0 | stats_tree_tick_pivot (st, st_collectd_values_hosts, |
395 | 0 | sc->string); |
396 | 0 | } |
397 | |
|
398 | 0 | for (sc = td->plugins; sc != NULL; sc = sc->next) |
399 | 0 | { |
400 | 0 | int i; |
401 | 0 | for (i = 0; i < sc->count; i++) |
402 | 0 | stats_tree_tick_pivot (st, st_collectd_values_plugins, |
403 | 0 | sc->string); |
404 | 0 | } |
405 | |
|
406 | 0 | for (sc = td->types; sc != NULL; sc = sc->next) |
407 | 0 | { |
408 | 0 | int i; |
409 | 0 | for (i = 0; i < sc->count; i++) |
410 | 0 | stats_tree_tick_pivot (st, st_collectd_values_types, |
411 | 0 | sc->string); |
412 | 0 | } |
413 | |
|
414 | 0 | return (TAP_PACKET_REDRAW); |
415 | 0 | } /* int collectd_stats_tree_packet */ |
416 | | |
417 | | static void |
418 | | collectd_stats_tree_register (void) |
419 | 14 | { |
420 | 14 | stats_tree_register ("collectd", "collectd", "Collectd", 0, |
421 | 14 | collectd_stats_tree_packet, |
422 | 14 | collectd_stats_tree_init, NULL); |
423 | 14 | } /* void register_collectd_stat_trees */ |
424 | | |
425 | | static void |
426 | | collectd_proto_tree_add_assembled_metric (tvbuff_t *tvb, |
427 | | int offset, int length, |
428 | | value_data_t const *vdispatch, proto_tree *root) |
429 | 0 | { |
430 | 0 | proto_item *root_item; |
431 | 0 | proto_tree *subtree; |
432 | 0 | nstime_t nstime; |
433 | |
|
434 | 0 | subtree = proto_tree_add_subtree(root, tvb, offset + 6, length - 6, |
435 | 0 | ett_collectd_dispatch, &root_item, "Assembled metric"); |
436 | 0 | proto_item_set_generated (root_item); |
437 | |
|
438 | 0 | proto_tree_add_string (subtree, hf_collectd_data_host, tvb, |
439 | 0 | vdispatch->host_off, vdispatch->host_len, |
440 | 0 | STR_NONNULL (vdispatch->host)); |
441 | |
|
442 | 0 | proto_tree_add_string (subtree, hf_collectd_data_plugin, tvb, |
443 | 0 | vdispatch->plugin_off, vdispatch->plugin_len, |
444 | 0 | STR_NONNULL (vdispatch->plugin)); |
445 | |
|
446 | 0 | if (vdispatch->plugin_instance) |
447 | 0 | proto_tree_add_string (subtree, |
448 | 0 | hf_collectd_data_plugin_inst, tvb, |
449 | 0 | vdispatch->plugin_instance_off, |
450 | 0 | vdispatch->plugin_instance_len, |
451 | 0 | vdispatch->plugin_instance); |
452 | |
|
453 | 0 | proto_tree_add_string (subtree, hf_collectd_data_type, tvb, |
454 | 0 | vdispatch->type_off, vdispatch->type_len, |
455 | 0 | STR_NONNULL (vdispatch->type)); |
456 | |
|
457 | 0 | if (vdispatch->type_instance) |
458 | 0 | proto_tree_add_string (subtree, |
459 | 0 | hf_collectd_data_type_inst, tvb, |
460 | 0 | vdispatch->type_instance_off, |
461 | 0 | vdispatch->type_instance_len, |
462 | 0 | vdispatch->type_instance); |
463 | |
|
464 | 0 | nstime = collectd_time_to_nstime (vdispatch->time_value); |
465 | 0 | proto_tree_add_time (subtree, hf_collectd_data_time, tvb, |
466 | 0 | vdispatch->time_off, /* length = */ 8, &nstime); |
467 | |
|
468 | 0 | nstime = collectd_time_to_nstime (vdispatch->interval); |
469 | 0 | proto_tree_add_time (subtree, hf_collectd_data_interval, tvb, |
470 | 0 | vdispatch->interval_off, /* length = */ 8, &nstime); |
471 | 0 | } |
472 | | |
473 | | static void |
474 | | collectd_proto_tree_add_assembled_notification (tvbuff_t *tvb, |
475 | | int offset, int length, |
476 | | notify_data_t const *ndispatch, proto_tree *root) |
477 | 0 | { |
478 | 0 | proto_item *root_item; |
479 | 0 | proto_tree *subtree; |
480 | 0 | nstime_t nstime; |
481 | |
|
482 | 0 | subtree = proto_tree_add_subtree(root, tvb, offset + 6, length - 6, |
483 | 0 | ett_collectd_dispatch, &root_item, "Assembled notification"); |
484 | 0 | proto_item_set_generated (root_item); |
485 | |
|
486 | 0 | proto_tree_add_string (subtree, hf_collectd_data_host, tvb, |
487 | 0 | ndispatch->host_off, ndispatch->host_len, |
488 | 0 | STR_NONNULL (ndispatch->host)); |
489 | |
|
490 | 0 | nstime = collectd_time_to_nstime (ndispatch->time_value); |
491 | 0 | proto_tree_add_time (subtree, hf_collectd_data_time, tvb, |
492 | 0 | ndispatch->time_off, /* length = */ 8, &nstime); |
493 | |
|
494 | 0 | proto_tree_add_uint64 (subtree, hf_collectd_data_severity, tvb, |
495 | 0 | ndispatch->severity_off, /* length = */ 8, |
496 | 0 | ndispatch->severity); |
497 | |
|
498 | 0 | proto_tree_add_string (subtree, hf_collectd_data_message, tvb, |
499 | 0 | ndispatch->message_off, ndispatch->message_len, |
500 | 0 | ndispatch->message); |
501 | 0 | } |
502 | | |
503 | | static int |
504 | | dissect_collectd_string (tvbuff_t *tvb, packet_info *pinfo, int type_hf, |
505 | | int offset, int *ret_offset, int *ret_length, |
506 | | const char **ret_string, proto_tree *tree_root, |
507 | | proto_item **ret_item) |
508 | 0 | { |
509 | 0 | proto_tree *pt; |
510 | 0 | proto_item *pi; |
511 | 0 | int type; |
512 | 0 | int length; |
513 | 0 | int size; |
514 | |
|
515 | 0 | size = tvb_reported_length_remaining (tvb, offset); |
516 | 0 | if (size < 4) |
517 | 0 | { |
518 | | /* This should never happen, because `dissect_collectd' checks |
519 | | * for this condition already. */ |
520 | 0 | return (-1); |
521 | 0 | } |
522 | | |
523 | 0 | type = tvb_get_ntohs(tvb, offset); |
524 | 0 | length = tvb_get_ntohs(tvb, offset + 2); |
525 | |
|
526 | 0 | pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length, |
527 | 0 | ett_collectd_string, &pi, "collectd %s segment: ", |
528 | 0 | val_to_str_const (type, part_names, "UNKNOWN")); |
529 | |
|
530 | 0 | if (length > size) |
531 | 0 | { |
532 | 0 | proto_item_append_text(pt, "Length = %i <BAD>", length); |
533 | 0 | expert_add_info_format(pinfo, pt, &ei_collectd_invalid_length, |
534 | 0 | "String part with invalid part length: " |
535 | 0 | "Part is longer than rest of package."); |
536 | 0 | return (-1); |
537 | 0 | } |
538 | | |
539 | 0 | *ret_offset = offset + 4; |
540 | 0 | *ret_length = length - 4; |
541 | |
|
542 | 0 | proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); |
543 | 0 | proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length); |
544 | 0 | proto_tree_add_item_ret_string (pt, type_hf, tvb, *ret_offset, *ret_length, ENC_ASCII, pinfo->pool, (const uint8_t**)ret_string); |
545 | |
|
546 | 0 | proto_item_append_text(pt, "\"%s\"", *ret_string); |
547 | |
|
548 | 0 | if (ret_item != NULL) |
549 | 0 | *ret_item = pi; |
550 | |
|
551 | 0 | return 0; |
552 | 0 | } /* int dissect_collectd_string */ |
553 | | |
554 | | static int |
555 | | dissect_collectd_integer (tvbuff_t *tvb, packet_info *pinfo, int type_hf, |
556 | | int offset, int *ret_offset, uint64_t *ret_value, |
557 | | proto_tree *tree_root, proto_item **ret_item) |
558 | 0 | { |
559 | 0 | proto_tree *pt; |
560 | 0 | proto_item *pi; |
561 | 0 | int type; |
562 | 0 | int length; |
563 | 0 | int size; |
564 | |
|
565 | 0 | size = tvb_reported_length_remaining (tvb, offset); |
566 | 0 | if (size < 4) |
567 | 0 | { |
568 | | /* This should never happen, because `dissect_collectd' checks |
569 | | * for this condition already. */ |
570 | 0 | return (-1); |
571 | 0 | } |
572 | | |
573 | 0 | type = tvb_get_ntohs(tvb, offset); |
574 | 0 | length = tvb_get_ntohs(tvb, offset + 2); |
575 | |
|
576 | 0 | if (size < 12) |
577 | 0 | { |
578 | 0 | pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1, |
579 | 0 | ett_collectd_integer, NULL, "collectd %s segment: <BAD>", |
580 | 0 | val_to_str_const (type, part_names, "UNKNOWN")); |
581 | |
|
582 | 0 | proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, |
583 | 0 | type); |
584 | 0 | proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, |
585 | 0 | length); |
586 | 0 | proto_tree_add_expert_format(pt, pinfo, &ei_collectd_garbage, tvb, offset + 4, -1, |
587 | 0 | "Garbage at end of packet: Length = %i <BAD>", |
588 | 0 | size - 4); |
589 | |
|
590 | 0 | return (-1); |
591 | 0 | } |
592 | | |
593 | 0 | if (length != 12) |
594 | 0 | { |
595 | 0 | pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1, |
596 | 0 | ett_collectd_integer, &pi, "collectd %s segment: <BAD>", |
597 | 0 | val_to_str_const (type, part_names, "UNKNOWN")); |
598 | |
|
599 | 0 | proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, |
600 | 0 | type); |
601 | 0 | pi = proto_tree_add_uint (pt, hf_collectd_length, tvb, |
602 | 0 | offset + 2, 2, length); |
603 | 0 | expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length, |
604 | 0 | "Invalid length field for an integer part."); |
605 | |
|
606 | 0 | return (-1); |
607 | 0 | } |
608 | | |
609 | 0 | *ret_offset = offset + 4; |
610 | 0 | *ret_value = tvb_get_ntoh64 (tvb, offset + 4); |
611 | | |
612 | | /* Convert the version 4.* time format to the version 5.* time format. */ |
613 | 0 | if ((type == TYPE_TIME) || (type == TYPE_INTERVAL)) |
614 | 0 | *ret_value *= 1073741824; |
615 | | |
616 | | /* Create an entry in the protocol tree for this part. The value is |
617 | | * printed depending on the "type" variable: TIME{,_HR} as absolute |
618 | | * time, INTERVAL{,_HR} as relative time, uint64 otherwise. */ |
619 | 0 | if ((type == TYPE_TIME) || (type == TYPE_TIME_HR)) |
620 | 0 | { |
621 | 0 | nstime_t nstime; |
622 | 0 | char *strtime; |
623 | |
|
624 | 0 | nstime = collectd_time_to_nstime (*ret_value); |
625 | 0 | strtime = abs_time_to_str (pinfo->pool, &nstime, ABSOLUTE_TIME_LOCAL, /* show_zone = */ true); |
626 | 0 | pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length, |
627 | 0 | ett_collectd_integer, &pi, "collectd %s segment: %s", |
628 | 0 | val_to_str_const (type, part_names, "UNKNOWN"), |
629 | 0 | STR_NONNULL (strtime)); |
630 | 0 | } |
631 | 0 | else if ((type == TYPE_INTERVAL) || (type == TYPE_INTERVAL_HR)) |
632 | 0 | { |
633 | 0 | nstime_t nstime; |
634 | 0 | char *strtime; |
635 | |
|
636 | 0 | nstime = collectd_time_to_nstime (*ret_value); |
637 | 0 | strtime = rel_time_to_str (pinfo->pool, &nstime); |
638 | 0 | pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length, |
639 | 0 | ett_collectd_integer, &pi, "collectd %s segment: %s", |
640 | 0 | val_to_str_const (type, part_names, "UNKNOWN"), |
641 | 0 | strtime); |
642 | 0 | } |
643 | 0 | else |
644 | 0 | { |
645 | 0 | pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length, |
646 | 0 | ett_collectd_integer, &pi, "collectd %s segment: %"PRIu64, |
647 | 0 | val_to_str_const (type, part_names, "UNKNOWN"), |
648 | 0 | *ret_value); |
649 | 0 | } |
650 | |
|
651 | 0 | if (ret_item != NULL) |
652 | 0 | *ret_item = pi; |
653 | |
|
654 | 0 | proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); |
655 | 0 | proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, |
656 | 0 | length); |
657 | 0 | if ((type == TYPE_TIME) || (type == TYPE_INTERVAL) |
658 | 0 | || (type == TYPE_TIME_HR) || (type == TYPE_INTERVAL_HR)) |
659 | 0 | { |
660 | 0 | nstime_t nstime; |
661 | |
|
662 | 0 | nstime = collectd_time_to_nstime (*ret_value); |
663 | 0 | proto_tree_add_time (pt, type_hf, tvb, offset + 4, 8, &nstime); |
664 | 0 | } |
665 | 0 | else |
666 | 0 | { |
667 | 0 | proto_tree_add_item (pt, type_hf, tvb, offset + 4, 8, ENC_BIG_ENDIAN); |
668 | 0 | } |
669 | |
|
670 | 0 | return 0; |
671 | 0 | } /* int dissect_collectd_integer */ |
672 | | |
673 | | static void |
674 | | dissect_collectd_values(tvbuff_t *tvb, int msg_off, int val_cnt, |
675 | | proto_tree *collectd_tree) |
676 | 0 | { |
677 | 0 | proto_tree *values_tree, *value_tree; |
678 | 0 | int i; |
679 | |
|
680 | 0 | values_tree = proto_tree_add_subtree_format(collectd_tree, tvb, msg_off + 6, val_cnt * 9, |
681 | 0 | ett_collectd_value, NULL, "%d value%s", val_cnt, |
682 | 0 | plurality (val_cnt, "", "s")); |
683 | |
|
684 | 0 | for (i = 0; i < val_cnt; i++) |
685 | 0 | { |
686 | 0 | int value_offset; |
687 | |
|
688 | 0 | int value_type_offset; |
689 | 0 | uint8_t value_type; |
690 | | |
691 | | /* Calculate the offsets of the type byte and the actual value. */ |
692 | 0 | value_offset = msg_off + 6 |
693 | 0 | + val_cnt /* value types */ |
694 | 0 | + (i * 8); /* previous values */ |
695 | |
|
696 | 0 | value_type_offset = msg_off + 6 + i; |
697 | 0 | value_type = tvb_get_uint8 (tvb, value_type_offset); |
698 | |
|
699 | 0 | switch (value_type) { |
700 | 0 | case TYPE_VALUE_COUNTER: |
701 | 0 | { |
702 | 0 | uint64_t val64; |
703 | |
|
704 | 0 | val64 = tvb_get_ntoh64 (tvb, value_offset); |
705 | 0 | value_tree = proto_tree_add_subtree_format(values_tree, tvb, msg_off + 6, |
706 | 0 | val_cnt * 9, ett_collectd_valinfo, NULL, |
707 | 0 | "Counter: %"PRIu64, val64); |
708 | |
|
709 | 0 | proto_tree_add_item (value_tree, hf_collectd_val_type, |
710 | 0 | tvb, value_type_offset, 1, ENC_BIG_ENDIAN); |
711 | 0 | proto_tree_add_item (value_tree, |
712 | 0 | hf_collectd_val_counter, tvb, |
713 | 0 | value_offset, 8, ENC_BIG_ENDIAN); |
714 | 0 | break; |
715 | 0 | } |
716 | | |
717 | 0 | case TYPE_VALUE_GAUGE: |
718 | 0 | { |
719 | 0 | double val; |
720 | |
|
721 | 0 | val = tvb_get_letohieee_double (tvb, value_offset); |
722 | 0 | value_tree = proto_tree_add_subtree_format(values_tree, tvb, msg_off + 6, |
723 | 0 | val_cnt * 9, ett_collectd_valinfo, NULL, |
724 | 0 | "Gauge: %g", val); |
725 | |
|
726 | 0 | proto_tree_add_item (value_tree, hf_collectd_val_type, |
727 | 0 | tvb, value_type_offset, 1, ENC_BIG_ENDIAN); |
728 | | /* Set the `little endian' flag to true here, because |
729 | | * collectd stores doubles in x86 representation. */ |
730 | 0 | proto_tree_add_item (value_tree, hf_collectd_val_gauge, |
731 | 0 | tvb, value_offset, 8, ENC_LITTLE_ENDIAN); |
732 | 0 | break; |
733 | 0 | } |
734 | | |
735 | 0 | case TYPE_VALUE_DERIVE: |
736 | 0 | { |
737 | 0 | int64_t val64; |
738 | |
|
739 | 0 | val64 = tvb_get_ntoh64 (tvb, value_offset); |
740 | 0 | value_tree = proto_tree_add_subtree_format(values_tree, tvb, msg_off + 6, |
741 | 0 | val_cnt * 9, ett_collectd_valinfo, NULL, |
742 | 0 | "Derive: %"PRIi64, val64); |
743 | |
|
744 | 0 | proto_tree_add_item (value_tree, hf_collectd_val_type, |
745 | 0 | tvb, value_type_offset, 1, ENC_BIG_ENDIAN); |
746 | 0 | proto_tree_add_item (value_tree, |
747 | 0 | hf_collectd_val_derive, tvb, |
748 | 0 | value_offset, 8, ENC_BIG_ENDIAN); |
749 | 0 | break; |
750 | 0 | } |
751 | | |
752 | 0 | case TYPE_VALUE_ABSOLUTE: |
753 | 0 | { |
754 | 0 | uint64_t val64; |
755 | |
|
756 | 0 | val64 = tvb_get_ntoh64 (tvb, value_offset); |
757 | 0 | value_tree = proto_tree_add_subtree_format(values_tree, tvb, msg_off + 6, |
758 | 0 | val_cnt * 9, ett_collectd_valinfo, NULL, |
759 | 0 | "Absolute: %"PRIu64, val64); |
760 | |
|
761 | 0 | proto_tree_add_item (value_tree, hf_collectd_val_type, |
762 | 0 | tvb, value_type_offset, 1, ENC_BIG_ENDIAN); |
763 | 0 | proto_tree_add_item (value_tree, |
764 | 0 | hf_collectd_val_absolute, tvb, |
765 | 0 | value_offset, 8, ENC_BIG_ENDIAN); |
766 | 0 | break; |
767 | 0 | } |
768 | | |
769 | 0 | default: |
770 | 0 | { |
771 | 0 | uint64_t val64; |
772 | |
|
773 | 0 | val64 = tvb_get_ntoh64 (tvb, value_offset); |
774 | 0 | value_tree = proto_tree_add_subtree_format(values_tree, tvb, msg_off + 6, |
775 | 0 | val_cnt * 9, ett_collectd_valinfo, NULL, |
776 | 0 | "Unknown: %"PRIx64, |
777 | 0 | val64); |
778 | |
|
779 | 0 | proto_tree_add_item (value_tree, hf_collectd_val_type, |
780 | 0 | tvb, value_type_offset, 1, ENC_BIG_ENDIAN); |
781 | 0 | proto_tree_add_item (value_tree, hf_collectd_val_unknown, |
782 | 0 | tvb, value_offset, 8, ENC_BIG_ENDIAN); |
783 | 0 | break; |
784 | 0 | } |
785 | 0 | } /* switch (value_type) */ |
786 | 0 | } /* for (i = 0; i < val_cnt; i++) */ |
787 | 0 | } /* void dissect_collectd_values */ |
788 | | |
789 | | static int |
790 | | dissect_collectd_part_values (tvbuff_t *tvb, packet_info *pinfo, int offset, |
791 | | value_data_t *vdispatch, proto_tree *tree_root) |
792 | 0 | { |
793 | 0 | proto_tree *pt; |
794 | 0 | proto_item *pi; |
795 | 0 | int type; |
796 | 0 | int length; |
797 | 0 | int size; |
798 | 0 | int values_count; |
799 | 0 | int corrected_values_count; |
800 | |
|
801 | 0 | size = tvb_reported_length_remaining (tvb, offset); |
802 | 0 | if (size < 4) |
803 | 0 | { |
804 | | /* This should never happen, because `dissect_collectd' checks |
805 | | * for this condition already. */ |
806 | 0 | return (-1); |
807 | 0 | } |
808 | | |
809 | 0 | type = tvb_get_ntohs (tvb, offset); |
810 | 0 | length = tvb_get_ntohs (tvb, offset + 2); |
811 | |
|
812 | 0 | if (size < 15) |
813 | 0 | { |
814 | 0 | pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1, |
815 | 0 | ett_collectd_part_value, NULL, "collectd %s segment: <BAD>", |
816 | 0 | val_to_str_const (type, part_names, "UNKNOWN")); |
817 | |
|
818 | 0 | proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); |
819 | 0 | proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, |
820 | 0 | length); |
821 | 0 | proto_tree_add_expert_format(pt, pinfo, &ei_collectd_garbage, tvb, offset + 4, -1, |
822 | 0 | "Garbage at end of packet: Length = %i <BAD>", |
823 | 0 | size - 4); |
824 | 0 | return (-1); |
825 | 0 | } |
826 | | |
827 | 0 | if ((length < 15) || ((length % 9) != 6)) |
828 | 0 | { |
829 | 0 | pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1, |
830 | 0 | ett_collectd_part_value, &pi, "collectd %s segment: <BAD>", |
831 | 0 | val_to_str_const (type, part_names, "UNKNOWN")); |
832 | |
|
833 | 0 | proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); |
834 | 0 | pi = proto_tree_add_uint (pt, hf_collectd_length, tvb, |
835 | 0 | offset + 2, 2, length); |
836 | 0 | expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length, |
837 | 0 | "Invalid length field for a values part."); |
838 | |
|
839 | 0 | return (-1); |
840 | 0 | } |
841 | | |
842 | 0 | values_count = tvb_get_ntohs (tvb, offset + 4); |
843 | 0 | corrected_values_count = (length - 6) / 9; |
844 | |
|
845 | 0 | if (values_count != corrected_values_count) |
846 | 0 | { |
847 | 0 | pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length, |
848 | 0 | ett_collectd_part_value, NULL, |
849 | 0 | "collectd %s segment: %d (%d) value%s <BAD>", |
850 | 0 | val_to_str_const (type, part_names, "UNKNOWN"), |
851 | 0 | values_count, corrected_values_count, |
852 | 0 | plurality(values_count, "", "s")); |
853 | 0 | } |
854 | 0 | else |
855 | 0 | { |
856 | 0 | pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length, |
857 | 0 | ett_collectd_part_value, NULL, |
858 | 0 | "collectd %s segment: %d value%s", |
859 | 0 | val_to_str_const (type, part_names, "UNKNOWN"), |
860 | 0 | values_count, |
861 | 0 | plurality(values_count, "", "s")); |
862 | 0 | } |
863 | |
|
864 | 0 | proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); |
865 | 0 | proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, length); |
866 | |
|
867 | 0 | pi = proto_tree_add_item (pt, hf_collectd_data_valcnt, tvb, |
868 | 0 | offset + 4, 2, ENC_BIG_ENDIAN); |
869 | 0 | if (values_count != corrected_values_count) |
870 | 0 | expert_add_info(pinfo, pi, &ei_collectd_data_valcnt); |
871 | |
|
872 | 0 | values_count = corrected_values_count; |
873 | |
|
874 | 0 | dissect_collectd_values (tvb, offset, values_count, pt); |
875 | 0 | collectd_proto_tree_add_assembled_metric (tvb, offset + 6, length - 6, |
876 | 0 | vdispatch, pt); |
877 | |
|
878 | 0 | return 0; |
879 | 0 | } /* void dissect_collectd_part_values */ |
880 | | |
881 | | static int |
882 | | dissect_collectd_signature (tvbuff_t *tvb, packet_info *pinfo, |
883 | | int offset, proto_tree *tree_root) |
884 | 0 | { |
885 | 0 | proto_item *pi; |
886 | 0 | proto_tree *pt; |
887 | 0 | int type; |
888 | 0 | int length; |
889 | 0 | int size; |
890 | 0 | const char *username; |
891 | |
|
892 | 0 | size = tvb_reported_length_remaining (tvb, offset); |
893 | 0 | if (size < 4) |
894 | 0 | { |
895 | | /* This should never happen, because `dissect_collectd' checks |
896 | | * for this condition already. */ |
897 | 0 | return (-1); |
898 | 0 | } |
899 | | |
900 | 0 | type = tvb_get_ntohs (tvb, offset); |
901 | 0 | length = tvb_get_ntohs (tvb, offset + 2); |
902 | |
|
903 | 0 | if (size < 36) /* remaining packet size too small for signature */ |
904 | 0 | { |
905 | 0 | pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1, |
906 | 0 | ett_collectd_signature, NULL, "collectd %s segment: <BAD>", |
907 | 0 | val_to_str_const (type, part_names, "UNKNOWN")); |
908 | |
|
909 | 0 | proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); |
910 | 0 | proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, |
911 | 0 | length); |
912 | 0 | proto_tree_add_expert_format(pt, pinfo, &ei_collectd_garbage, tvb, offset + 4, -1, |
913 | 0 | "Garbage at end of packet: Length = %i <BAD>", |
914 | 0 | size - 4); |
915 | 0 | return (-1); |
916 | 0 | } |
917 | | |
918 | 0 | if (length < 36) |
919 | 0 | { |
920 | 0 | pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1, |
921 | 0 | ett_collectd_signature, NULL, "collectd %s segment: <BAD>", |
922 | 0 | val_to_str_const (type, part_names, "UNKNOWN")); |
923 | |
|
924 | 0 | proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); |
925 | 0 | pi = proto_tree_add_uint (pt, hf_collectd_length, tvb, |
926 | 0 | offset + 2, 2, length); |
927 | 0 | expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length, |
928 | 0 | "Invalid length field for a signature part."); |
929 | |
|
930 | 0 | return (-1); |
931 | 0 | } |
932 | | |
933 | 0 | pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length, |
934 | 0 | ett_collectd_signature, NULL, "collectd %s segment: HMAC-SHA-256", |
935 | 0 | val_to_str_const (type, part_names, "UNKNOWN")); |
936 | |
|
937 | 0 | proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); |
938 | 0 | proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, |
939 | 0 | length); |
940 | | // proto_tree_add_checksum adds two ti but only returns the first, |
941 | | // which makes it hard to move the username after the second item, |
942 | | // so extract the string directly, then add a username item later. |
943 | | // |
944 | | // XXX - Are we sure this string is ASCII? Probably UTF-8 these days. |
945 | | // The same goes for all the other strings in the protocol. |
946 | 0 | username = (char*)tvb_get_string_enc(pinfo->pool, tvb, offset + 36, length - 36, ENC_ASCII); |
947 | 0 | uint8_t *hash = NULL; |
948 | 0 | gcry_md_hd_t *md_hd = collectd_get_md(username); |
949 | 0 | if (md_hd) { |
950 | 0 | uint8_t *buffer = tvb_memdup(pinfo->pool, tvb, offset + 36, tvb_reported_length_remaining(tvb, offset + 36)); |
951 | 0 | gcry_md_write(*md_hd, buffer, size - 36); |
952 | 0 | hash = gcry_md_read(*md_hd, GCRY_MD_SHA256); |
953 | 0 | if (hash == NULL) { |
954 | 0 | ws_debug("gcry_md_read failed"); |
955 | 0 | } |
956 | 0 | } |
957 | 0 | proto_tree_add_checksum_bytes(pt, tvb, offset + 4, hf_collectd_data_sighash, |
958 | 0 | hf_collectd_data_sighash_status, &ei_collectd_sighash_bad, pinfo, |
959 | 0 | hash, 32, hash ? PROTO_CHECKSUM_VERIFY : PROTO_CHECKSUM_NO_FLAGS); |
960 | 0 | proto_tree_add_item(pt, hf_collectd_data_username, tvb, offset + 36, length - 36, ENC_ASCII); |
961 | 0 | return 0; |
962 | 0 | } /* int dissect_collectd_signature */ |
963 | | |
964 | | /* We recurse after decrypting. In practice encryption is always the first |
965 | | * part and contains everything, so we could avoid recursion by checking |
966 | | * for it at the start of dissect_collect and not try to decrypt encrypted |
967 | | * parts in other positions. */ |
968 | | static int |
969 | | // NOLINTNEXTLINE(misc-no-recursion) |
970 | | dissect_collectd_parts(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_); |
971 | | |
972 | | static int |
973 | | // NOLINTNEXTLINE(misc-no-recursion) |
974 | | dissect_collectd_encrypted(tvbuff_t *tvb, packet_info *pinfo, |
975 | | int offset, proto_tree *tree_root) |
976 | 0 | { |
977 | 0 | proto_item *pi; |
978 | 0 | proto_tree *pt; |
979 | 0 | int type; |
980 | 0 | int length; |
981 | 0 | int size; |
982 | 0 | int username_length; |
983 | 0 | const char *username; |
984 | |
|
985 | 0 | size = tvb_reported_length_remaining (tvb, offset); |
986 | 0 | if (size < 4) |
987 | 0 | { |
988 | | /* This should never happen, because `dissect_collectd' checks |
989 | | * for this condition already. */ |
990 | 0 | return (-1); |
991 | 0 | } |
992 | | |
993 | 0 | type = tvb_get_ntohs (tvb, offset); |
994 | 0 | length = tvb_get_ntohs (tvb, offset + 2); |
995 | |
|
996 | 0 | if (size < 42) /* remaining packet size too small for signature */ |
997 | 0 | { |
998 | 0 | pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1, |
999 | 0 | ett_collectd_encryption, NULL, "collectd %s segment: <BAD>", |
1000 | 0 | val_to_str_const (type, part_names, "UNKNOWN")); |
1001 | |
|
1002 | 0 | proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); |
1003 | 0 | proto_tree_add_uint (pt, hf_collectd_length, tvb, offset + 2, 2, |
1004 | 0 | length); |
1005 | 0 | proto_tree_add_expert_format(pt, pinfo, &ei_collectd_garbage, tvb, offset + 4, -1, |
1006 | 0 | "Garbage at end of packet: Length = %i <BAD>", |
1007 | 0 | size - 4); |
1008 | 0 | return (-1); |
1009 | 0 | } |
1010 | | |
1011 | 0 | if (length < 42) |
1012 | 0 | { |
1013 | 0 | pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1, |
1014 | 0 | ett_collectd_encryption, NULL, "collectd %s segment: <BAD>", |
1015 | 0 | val_to_str_const (type, part_names, "UNKNOWN")); |
1016 | |
|
1017 | 0 | proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); |
1018 | 0 | pi = proto_tree_add_uint (pt, hf_collectd_length, tvb, |
1019 | 0 | offset + 2, 2, length); |
1020 | 0 | expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length, |
1021 | 0 | "Invalid length field for an encryption part."); |
1022 | |
|
1023 | 0 | return (-1); |
1024 | 0 | } |
1025 | | |
1026 | 0 | username_length = tvb_get_ntohs (tvb, offset + 4); |
1027 | 0 | if (username_length > (length - 42)) |
1028 | 0 | { |
1029 | 0 | pt = proto_tree_add_subtree_format(tree_root, tvb, offset, -1, |
1030 | 0 | ett_collectd_encryption, NULL, "collectd %s segment: <BAD>", |
1031 | 0 | val_to_str_const (type, part_names, "UNKNOWN")); |
1032 | |
|
1033 | 0 | proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, 2, type); |
1034 | 0 | proto_tree_add_uint (pt, hf_collectd_length, tvb, |
1035 | 0 | offset + 2, 2, length); |
1036 | 0 | pi = proto_tree_add_uint (pt, hf_collectd_data_username_len, tvb, |
1037 | 0 | offset + 4, 2, length); |
1038 | 0 | expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length, |
1039 | 0 | "Invalid username length field for an encryption part."); |
1040 | |
|
1041 | 0 | return (-1); |
1042 | 0 | } |
1043 | | |
1044 | 0 | pt = proto_tree_add_subtree_format(tree_root, tvb, offset, length, |
1045 | 0 | ett_collectd_encryption, NULL, "collectd %s segment: AES-256", |
1046 | 0 | val_to_str_const (type, part_names, "UNKNOWN")); |
1047 | |
|
1048 | 0 | proto_tree_add_uint(pt, hf_collectd_type, tvb, offset, 2, type); |
1049 | 0 | offset += 2; |
1050 | 0 | proto_tree_add_uint(pt, hf_collectd_length, tvb, offset, 2, length); |
1051 | 0 | offset += 2; |
1052 | 0 | proto_tree_add_uint(pt, hf_collectd_data_username_len, tvb, offset, 2, username_length); |
1053 | 0 | offset += 2; |
1054 | 0 | proto_tree_add_item_ret_string(pt, hf_collectd_data_username, tvb, offset, username_length, ENC_ASCII, pinfo->pool, (const uint8_t**)&username); |
1055 | 0 | offset += username_length; |
1056 | |
|
1057 | 0 | proto_tree_add_item(pt, hf_collectd_data_initvec, tvb, |
1058 | 0 | offset, 16, ENC_NA); |
1059 | 0 | offset += 16; |
1060 | |
|
1061 | 0 | int buffer_size = length - (22 + username_length); |
1062 | | // Must be >= 20 (checked above) |
1063 | 0 | proto_tree_add_item(pt, hf_collectd_data_encrypted, tvb, |
1064 | 0 | offset, |
1065 | 0 | buffer_size, ENC_NA); |
1066 | 0 | gcry_cipher_hd_t *cipher_hd; |
1067 | 0 | cipher_hd = collectd_get_cipher(username); |
1068 | 0 | if (cipher_hd) { |
1069 | 0 | gcry_error_t err; |
1070 | 0 | uint8_t iv[16]; |
1071 | 0 | tvb_memcpy(tvb, iv, offset - 16, 16); |
1072 | 0 | err = gcry_cipher_setiv(*cipher_hd, iv, 16); |
1073 | 0 | if (err != 0) { |
1074 | 0 | ws_debug("error setting key: %s", gcry_strerror(err)); |
1075 | 0 | return 0; // Should there be another return code for this? |
1076 | 0 | } |
1077 | 0 | uint8_t *buffer = tvb_memdup(pinfo->pool, tvb, offset, buffer_size); |
1078 | 0 | err = gcry_cipher_decrypt(*cipher_hd, buffer, buffer_size, NULL, 0); |
1079 | 0 | if (err != 0) { |
1080 | 0 | ws_debug("gcry_cipher_decrypt failed: %s", gcry_strerror(err)); |
1081 | 0 | return 0; // Should there be another return code for this? |
1082 | 0 | } |
1083 | 0 | tvbuff_t *decrypted_tvb = tvb_new_child_real_data(tvb, buffer, buffer_size, buffer_size); |
1084 | 0 | add_new_data_source(pinfo, decrypted_tvb, "Decrypted collectd"); |
1085 | 0 | uint8_t hash[20]; |
1086 | 0 | gcry_md_hash_buffer(GCRY_MD_SHA1, hash, buffer + 20, buffer_size - 20); |
1087 | 0 | proto_tree_add_checksum_bytes(pt, decrypted_tvb, 0, hf_collectd_data_sighash, |
1088 | 0 | hf_collectd_data_sighash_status, &ei_collectd_sighash_bad, pinfo, |
1089 | 0 | hash, 20, PROTO_CHECKSUM_VERIFY); |
1090 | 0 | if (tvb_memeql(decrypted_tvb, 0, hash, 20) == 0) { |
1091 | | // We recurse here, but consumed 22 + username_len bytes |
1092 | | // so we'll run out of packet before stack exhaustion. |
1093 | 0 | dissect_collectd_parts(tvb_new_subset_remaining(decrypted_tvb, 20), pinfo, tree_root, NULL); |
1094 | 0 | } |
1095 | 0 | } |
1096 | 0 | return 0; |
1097 | 0 | } /* int dissect_collectd_encrypted */ |
1098 | | |
1099 | | static int |
1100 | | stats_account_string (wmem_allocator_t *scope, string_counter_t **ret_list, const char *new_value) |
1101 | 0 | { |
1102 | 0 | string_counter_t *entry; |
1103 | |
|
1104 | 0 | if (ret_list == NULL) |
1105 | 0 | return (-1); |
1106 | | |
1107 | 0 | if (new_value == NULL) |
1108 | 0 | new_value = "(null)"; |
1109 | |
|
1110 | 0 | for (entry = *ret_list; entry != NULL; entry = entry->next) |
1111 | 0 | if (strcmp (new_value, entry->string) == 0) |
1112 | 0 | { |
1113 | 0 | entry->count++; |
1114 | 0 | return 0; |
1115 | 0 | } |
1116 | | |
1117 | 0 | entry = (string_counter_t *)wmem_alloc0 (scope, sizeof (*entry)); |
1118 | 0 | entry->string = wmem_strdup (scope, new_value); |
1119 | 0 | entry->count = 1; |
1120 | 0 | entry->next = *ret_list; |
1121 | |
|
1122 | 0 | *ret_list = entry; |
1123 | |
|
1124 | 0 | return 0; |
1125 | 0 | } |
1126 | | |
1127 | | static int |
1128 | | // NOLINTNEXTLINE(misc-no-recursion) |
1129 | | dissect_collectd_parts(tvbuff_t *tvb, packet_info *pinfo, proto_tree *collectd_tree, void* data _U_) |
1130 | 3 | { |
1131 | 3 | int offset; |
1132 | 3 | int size; |
1133 | 3 | value_data_t vdispatch; |
1134 | 3 | notify_data_t ndispatch; |
1135 | 3 | int status; |
1136 | 3 | proto_item *pi; |
1137 | 3 | proto_tree *pt; |
1138 | | |
1139 | 3 | memset(&vdispatch, '\0', sizeof(vdispatch)); |
1140 | 3 | memset(&ndispatch, '\0', sizeof(ndispatch)); |
1141 | | |
1142 | 3 | tap_data_t *tap_data = p_get_proto_data(pinfo->pool, pinfo, proto_collectd, TAP_DATA_KEY); |
1143 | 3 | column_data_t *col_data = p_get_proto_data(pinfo->pool, pinfo, proto_collectd, COL_DATA_KEY); |
1144 | | |
1145 | 3 | status = 0; |
1146 | 3 | offset = 0; |
1147 | 3 | size = tvb_reported_length(tvb); |
1148 | 4 | while ((size > 0) && (status == 0)) |
1149 | 4 | { |
1150 | 4 | int part_type; |
1151 | 4 | int part_length; |
1152 | | |
1153 | | /* Check if there are at least four bytes left first. |
1154 | | * Four bytes are used to read the type and the length |
1155 | | * of the next part. If there's less, there's some garbage |
1156 | | * at the end of the packet. */ |
1157 | 4 | if (size < 4) |
1158 | 2 | { |
1159 | 2 | proto_tree_add_expert_format(collectd_tree, pinfo, &ei_collectd_garbage, tvb, |
1160 | 2 | offset, -1, |
1161 | 2 | "Garbage at end of packet: Length = %i <BAD>", |
1162 | 2 | size); |
1163 | 2 | col_data->pkt_errors++; |
1164 | 2 | break; |
1165 | 2 | } |
1166 | | |
1167 | | /* dissect a message entry */ |
1168 | 2 | part_type = tvb_get_ntohs (tvb, offset); |
1169 | 2 | part_length = tvb_get_ntohs (tvb, offset + 2); |
1170 | | |
1171 | | /* Check if the length of the part is in the valid range. Don't |
1172 | | * confuse this with the above: Here we check the information |
1173 | | * provided in the packet.. */ |
1174 | 2 | if ((part_length < 4) || (part_length > size)) |
1175 | 1 | { |
1176 | 1 | pt = proto_tree_add_subtree_format(collectd_tree, tvb, |
1177 | 1 | offset, part_length, ett_collectd_invalid_length, NULL, |
1178 | 1 | "collectd %s segment: Length = %i <BAD>", |
1179 | 1 | val_to_str_const (part_type, part_names, "UNKNOWN"), |
1180 | 1 | part_length); |
1181 | | |
1182 | 1 | proto_tree_add_uint (pt, hf_collectd_type, tvb, offset, |
1183 | 1 | 2, part_type); |
1184 | 1 | pi = proto_tree_add_uint (pt, hf_collectd_length, tvb, |
1185 | 1 | offset + 2, 2, part_length); |
1186 | | |
1187 | 1 | if (part_length < 4) |
1188 | 0 | expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length, |
1189 | 0 | "Bad part length: Is %i, expected at least 4", |
1190 | 0 | part_length); |
1191 | 1 | else |
1192 | 1 | expert_add_info_format(pinfo, pi, &ei_collectd_invalid_length, |
1193 | 1 | "Bad part length: Larger than remaining packet size."); |
1194 | | |
1195 | 1 | col_data->pkt_errors++; |
1196 | 1 | break; |
1197 | 1 | } |
1198 | | |
1199 | | /* The header information looks okay, let's tend to the actual |
1200 | | * payload in this part. */ |
1201 | 1 | switch (part_type) { |
1202 | 0 | case TYPE_HOST: |
1203 | 0 | { |
1204 | 0 | status = dissect_collectd_string (tvb, pinfo, |
1205 | 0 | hf_collectd_data_host, |
1206 | 0 | offset, |
1207 | 0 | &vdispatch.host_off, |
1208 | 0 | &vdispatch.host_len, |
1209 | 0 | &vdispatch.host, |
1210 | 0 | collectd_tree, /* item = */ NULL); |
1211 | 0 | if (status != 0) |
1212 | 0 | col_data->pkt_errors++; |
1213 | 0 | else |
1214 | 0 | { |
1215 | 0 | if (col_data->pkt_host == NULL) |
1216 | 0 | col_data->pkt_host = vdispatch.host; |
1217 | 0 | ndispatch.host_off = vdispatch.host_off; |
1218 | 0 | ndispatch.host_len = vdispatch.host_len; |
1219 | 0 | ndispatch.host = vdispatch.host; |
1220 | 0 | } |
1221 | |
|
1222 | 0 | break; |
1223 | 0 | } |
1224 | | |
1225 | 0 | case TYPE_PLUGIN: |
1226 | 0 | { |
1227 | 0 | status = dissect_collectd_string (tvb, pinfo, |
1228 | 0 | hf_collectd_data_plugin, |
1229 | 0 | offset, |
1230 | 0 | &vdispatch.plugin_off, |
1231 | 0 | &vdispatch.plugin_len, |
1232 | 0 | &vdispatch.plugin, |
1233 | 0 | collectd_tree, /* item = */ NULL); |
1234 | 0 | if (status != 0) |
1235 | 0 | col_data->pkt_errors++; |
1236 | 0 | else |
1237 | 0 | col_data->pkt_plugins++; |
1238 | |
|
1239 | 0 | break; |
1240 | 0 | } |
1241 | | |
1242 | 0 | case TYPE_PLUGIN_INSTANCE: |
1243 | 0 | { |
1244 | 0 | status = dissect_collectd_string (tvb, pinfo, |
1245 | 0 | hf_collectd_data_plugin_inst, |
1246 | 0 | offset, |
1247 | 0 | &vdispatch.plugin_instance_off, |
1248 | 0 | &vdispatch.plugin_instance_len, |
1249 | 0 | &vdispatch.plugin_instance, |
1250 | 0 | collectd_tree, /* item = */ NULL); |
1251 | 0 | if (status != 0) |
1252 | 0 | col_data->pkt_errors++; |
1253 | |
|
1254 | 0 | break; |
1255 | 0 | } |
1256 | | |
1257 | 0 | case TYPE_TYPE: |
1258 | 0 | { |
1259 | 0 | status = dissect_collectd_string (tvb, pinfo, |
1260 | 0 | hf_collectd_data_type, |
1261 | 0 | offset, |
1262 | 0 | &vdispatch.type_off, |
1263 | 0 | &vdispatch.type_len, |
1264 | 0 | &vdispatch.type, |
1265 | 0 | collectd_tree, /* item = */ NULL); |
1266 | 0 | if (status != 0) |
1267 | 0 | col_data->pkt_errors++; |
1268 | |
|
1269 | 0 | break; |
1270 | 0 | } |
1271 | | |
1272 | 0 | case TYPE_TYPE_INSTANCE: |
1273 | 0 | { |
1274 | 0 | status = dissect_collectd_string (tvb, pinfo, |
1275 | 0 | hf_collectd_data_type_inst, |
1276 | 0 | offset, |
1277 | 0 | &vdispatch.type_instance_off, |
1278 | 0 | &vdispatch.type_instance_len, |
1279 | 0 | &vdispatch.type_instance, |
1280 | 0 | collectd_tree, /* item = */ NULL); |
1281 | 0 | if (status != 0) |
1282 | 0 | col_data->pkt_errors++; |
1283 | |
|
1284 | 0 | break; |
1285 | 0 | } |
1286 | | |
1287 | 0 | case TYPE_TIME: |
1288 | 0 | case TYPE_TIME_HR: |
1289 | 0 | { |
1290 | 0 | pi = NULL; |
1291 | 0 | status = dissect_collectd_integer (tvb, pinfo, |
1292 | 0 | hf_collectd_data_time, |
1293 | 0 | offset, |
1294 | 0 | &vdispatch.time_off, |
1295 | 0 | &vdispatch.time_value, |
1296 | 0 | collectd_tree, &pi); |
1297 | 0 | if (status != 0) |
1298 | 0 | col_data->pkt_errors++; |
1299 | |
|
1300 | 0 | break; |
1301 | 0 | } |
1302 | | |
1303 | 0 | case TYPE_INTERVAL: |
1304 | 0 | case TYPE_INTERVAL_HR: |
1305 | 0 | { |
1306 | 0 | status = dissect_collectd_integer (tvb, pinfo, |
1307 | 0 | hf_collectd_data_interval, |
1308 | 0 | offset, |
1309 | 0 | &vdispatch.interval_off, |
1310 | 0 | &vdispatch.interval, |
1311 | 0 | collectd_tree, /* item = */ NULL); |
1312 | 0 | if (status != 0) |
1313 | 0 | col_data->pkt_errors++; |
1314 | |
|
1315 | 0 | break; |
1316 | 0 | } |
1317 | | |
1318 | 0 | case TYPE_VALUES: |
1319 | 0 | { |
1320 | 0 | status = dissect_collectd_part_values (tvb, pinfo, |
1321 | 0 | offset, |
1322 | 0 | &vdispatch, |
1323 | 0 | collectd_tree); |
1324 | 0 | if (status != 0) |
1325 | 0 | col_data->pkt_errors++; |
1326 | 0 | else |
1327 | 0 | col_data->pkt_values++; |
1328 | |
|
1329 | 0 | tap_data->values_num++; |
1330 | 0 | stats_account_string (pinfo->pool, |
1331 | 0 | &tap_data->hosts, |
1332 | 0 | vdispatch.host); |
1333 | 0 | stats_account_string (pinfo->pool, |
1334 | 0 | &tap_data->plugins, |
1335 | 0 | vdispatch.plugin); |
1336 | 0 | stats_account_string (pinfo->pool, |
1337 | 0 | &tap_data->types, |
1338 | 0 | vdispatch.type); |
1339 | |
|
1340 | 0 | break; |
1341 | 0 | } |
1342 | | |
1343 | 0 | case TYPE_MESSAGE: |
1344 | 0 | { |
1345 | 0 | pi = NULL; |
1346 | 0 | status = dissect_collectd_string (tvb, pinfo, |
1347 | 0 | hf_collectd_data_message, |
1348 | 0 | offset, |
1349 | 0 | &ndispatch.message_off, |
1350 | 0 | &ndispatch.message_len, |
1351 | 0 | &ndispatch.message, |
1352 | 0 | collectd_tree, &pi); |
1353 | 0 | if (status != 0) |
1354 | 0 | { |
1355 | 0 | col_data->pkt_errors++; |
1356 | 0 | break; |
1357 | 0 | } |
1358 | 0 | col_data->pkt_messages++; |
1359 | |
|
1360 | 0 | pt = proto_item_get_subtree (pi); |
1361 | |
|
1362 | 0 | collectd_proto_tree_add_assembled_notification (tvb, |
1363 | 0 | offset + 4, part_length - 1, |
1364 | 0 | &ndispatch, pt); |
1365 | |
|
1366 | 0 | break; |
1367 | 0 | } |
1368 | | |
1369 | 0 | case TYPE_SEVERITY: |
1370 | 0 | { |
1371 | 0 | pi = NULL; |
1372 | 0 | status = dissect_collectd_integer (tvb, pinfo, |
1373 | 0 | hf_collectd_data_severity, |
1374 | 0 | offset, |
1375 | 0 | &ndispatch.severity_off, |
1376 | 0 | &ndispatch.severity, |
1377 | 0 | collectd_tree, &pi); |
1378 | 0 | if (status != 0) |
1379 | 0 | col_data->pkt_errors++; |
1380 | 0 | else |
1381 | 0 | { |
1382 | 0 | proto_item_set_text (pi, |
1383 | 0 | "collectd SEVERITY segment: " |
1384 | 0 | "%s (%"PRIu64")", |
1385 | 0 | val64_to_str_const (ndispatch.severity, severity_names, "UNKNOWN"), |
1386 | 0 | ndispatch.severity); |
1387 | 0 | } |
1388 | |
|
1389 | 0 | break; |
1390 | 0 | } |
1391 | | |
1392 | 0 | case TYPE_SIGN_SHA256: |
1393 | 0 | { |
1394 | 0 | status = dissect_collectd_signature (tvb, pinfo, |
1395 | 0 | offset, |
1396 | 0 | collectd_tree); |
1397 | 0 | if (status != 0) |
1398 | 0 | col_data->pkt_errors++; |
1399 | |
|
1400 | 0 | break; |
1401 | 0 | } |
1402 | | |
1403 | 0 | case TYPE_ENCR_AES256: |
1404 | 0 | { |
1405 | 0 | status = dissect_collectd_encrypted (tvb, pinfo, |
1406 | 0 | offset, collectd_tree); |
1407 | 0 | if (status != 0) |
1408 | 0 | col_data->pkt_errors++; |
1409 | |
|
1410 | 0 | break; |
1411 | 0 | } |
1412 | | |
1413 | 1 | default: |
1414 | 1 | { |
1415 | 1 | col_data->pkt_unknown++; |
1416 | 1 | pt = proto_tree_add_subtree_format(collectd_tree, tvb, |
1417 | 1 | offset, part_length, ett_collectd_unknown, NULL, |
1418 | 1 | "collectd %s segment: %i bytes", |
1419 | 1 | val_to_str_const(part_type, part_names, "UNKNOWN"), |
1420 | 1 | part_length); |
1421 | | |
1422 | 1 | pi = proto_tree_add_uint (pt, hf_collectd_type, tvb, |
1423 | 1 | offset, 2, part_type); |
1424 | 1 | proto_tree_add_uint (pt, hf_collectd_length, tvb, |
1425 | 1 | offset + 2, 2, part_length); |
1426 | 1 | proto_tree_add_item (pt, hf_collectd_data, tvb, |
1427 | 1 | offset + 4, part_length - 4, ENC_NA); |
1428 | | |
1429 | 1 | expert_add_info_format(pinfo, pi, &ei_collectd_type, |
1430 | 1 | "Unknown part type %#x. Cannot decode data.", |
1431 | 1 | part_type); |
1432 | 1 | } |
1433 | 1 | } /* switch (part_type) */ |
1434 | | |
1435 | 1 | offset += part_length; |
1436 | 1 | size -= part_length; |
1437 | 1 | } /* while ((size > 4) && (status == 0)) */ |
1438 | | |
1439 | 3 | return tvb_captured_length(tvb); |
1440 | 3 | } |
1441 | | |
1442 | | static int |
1443 | | dissect_collectd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
1444 | 3 | { |
1445 | 3 | proto_item *pi; |
1446 | 3 | proto_tree *collectd_tree; |
1447 | | |
1448 | 3 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "collectd"); |
1449 | 3 | col_clear(pinfo->cinfo, COL_INFO); |
1450 | | |
1451 | 3 | tap_data_t *tap_data = wmem_new0(pinfo->pool, tap_data_t); |
1452 | 3 | p_add_proto_data(pinfo->pool, pinfo, proto_collectd, TAP_DATA_KEY, tap_data); |
1453 | | |
1454 | 3 | column_data_t *col_data = wmem_new0(pinfo->pool, column_data_t); |
1455 | 3 | p_add_proto_data(pinfo->pool, pinfo, proto_collectd, COL_DATA_KEY, col_data); |
1456 | | |
1457 | | /* create the collectd protocol tree */ |
1458 | 3 | pi = proto_tree_add_item(tree, proto_collectd, tvb, 0, -1, ENC_NA); |
1459 | 3 | collectd_tree = proto_item_add_subtree(pi, ett_collectd); |
1460 | | |
1461 | 3 | dissect_collectd_parts(tvb, pinfo, collectd_tree, data); |
1462 | | |
1463 | | /* Put summary information in columns */ |
1464 | 3 | col_add_fstr(pinfo->cinfo, COL_INFO, "Host=%s, %2d value%s for %d plugin%s %d message%s", |
1465 | 3 | col_data->pkt_host, |
1466 | 3 | col_data->pkt_values, plurality(col_data->pkt_values, " ", "s"), |
1467 | 3 | col_data->pkt_plugins, plurality(col_data->pkt_plugins, ", ", "s,"), |
1468 | 3 | col_data->pkt_messages, plurality(col_data->pkt_messages, ", ", "s")); |
1469 | | |
1470 | 3 | if (col_data->pkt_unknown) { |
1471 | 1 | col_append_fstr(pinfo->cinfo, COL_INFO, ", %d unknown", |
1472 | 1 | col_data->pkt_unknown); |
1473 | 1 | } |
1474 | | |
1475 | 3 | if (col_data->pkt_errors) { |
1476 | 3 | col_add_fstr(pinfo->cinfo, COL_INFO, ", %d error%s", |
1477 | 3 | col_data->pkt_errors, plurality(col_data->pkt_errors, "", "s")); |
1478 | 3 | } |
1479 | | |
1480 | | /* Dispatch tap data. */ |
1481 | 3 | tap_queue_packet(tap_collectd, pinfo, tap_data); |
1482 | | |
1483 | 3 | return tvb_captured_length(tvb); |
1484 | 3 | } /* void dissect_collectd */ |
1485 | | |
1486 | | void proto_register_collectd(void) |
1487 | 14 | { |
1488 | 14 | expert_module_t* expert_collectd; |
1489 | 14 | module_t *collectd_module; |
1490 | | |
1491 | | /* Setup list of header fields */ |
1492 | 14 | static hf_register_info hf[] = { |
1493 | 14 | { &hf_collectd_type, |
1494 | 14 | { "Type", "collectd.type", FT_UINT16, BASE_HEX, |
1495 | 14 | VALS(part_names), 0x0, NULL, HFILL } |
1496 | 14 | }, |
1497 | 14 | { &hf_collectd_length, |
1498 | 14 | { "Length", "collectd.len", FT_UINT16, BASE_DEC, |
1499 | 14 | NULL, 0x0, NULL, HFILL } |
1500 | 14 | }, |
1501 | 14 | { &hf_collectd_data, |
1502 | 14 | { "Payload", "collectd.data", FT_BYTES, BASE_NONE, |
1503 | 14 | NULL, 0x0, NULL, HFILL } |
1504 | 14 | }, |
1505 | 14 | { &hf_collectd_data_host, |
1506 | 14 | { "Host name", "collectd.data.host", FT_STRING, BASE_NONE, |
1507 | 14 | NULL, 0x0, NULL, HFILL } |
1508 | 14 | }, |
1509 | 14 | { &hf_collectd_data_interval, |
1510 | 14 | { "Interval", "collectd.data.interval", FT_RELATIVE_TIME, BASE_NONE, |
1511 | 14 | NULL, 0x0, NULL, HFILL } |
1512 | 14 | }, |
1513 | 14 | { &hf_collectd_data_time, |
1514 | 14 | { "Timestamp", "collectd.data.time", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, |
1515 | 14 | NULL, 0x0, NULL, HFILL } |
1516 | 14 | }, |
1517 | 14 | { &hf_collectd_data_plugin, |
1518 | 14 | { "Plugin", "collectd.data.plugin", FT_STRING, BASE_NONE, |
1519 | 14 | NULL, 0x0, NULL, HFILL } |
1520 | 14 | }, |
1521 | 14 | { &hf_collectd_data_plugin_inst, |
1522 | 14 | { "Plugin instance", "collectd.data.plugin.inst", FT_STRING, BASE_NONE, |
1523 | 14 | NULL, 0x0, NULL, HFILL } |
1524 | 14 | }, |
1525 | 14 | { &hf_collectd_data_type, |
1526 | 14 | { "Type", "collectd.data.type", FT_STRING, BASE_NONE, |
1527 | 14 | NULL, 0x0, NULL, HFILL } |
1528 | 14 | }, |
1529 | 14 | { &hf_collectd_data_type_inst, |
1530 | 14 | { "Type instance", "collectd.data.type.inst", FT_STRING, BASE_NONE, |
1531 | 14 | NULL, 0x0, NULL, HFILL } |
1532 | 14 | }, |
1533 | 14 | { &hf_collectd_data_valcnt, |
1534 | 14 | { "Value count", "collectd.data.valcnt", FT_UINT16, BASE_DEC, |
1535 | 14 | NULL, 0x0, NULL, HFILL } |
1536 | 14 | }, |
1537 | 14 | { &hf_collectd_val_type, |
1538 | 14 | { "Value type", "collectd.val.type", FT_UINT8, BASE_HEX, |
1539 | 14 | VALS(valuetypenames), 0x0, NULL, HFILL } |
1540 | 14 | }, |
1541 | 14 | { &hf_collectd_val_counter, |
1542 | 14 | { "Counter value", "collectd.val.counter", FT_UINT64, BASE_DEC, |
1543 | 14 | NULL, 0x0, NULL, HFILL } |
1544 | 14 | }, |
1545 | 14 | { &hf_collectd_val_gauge, |
1546 | 14 | { "Gauge value", "collectd.val.gauge", FT_DOUBLE, BASE_NONE, |
1547 | 14 | NULL, 0x0, NULL, HFILL } |
1548 | 14 | }, |
1549 | 14 | { &hf_collectd_val_derive, |
1550 | 14 | { "Derive value", "collectd.val.derive", FT_INT64, BASE_DEC, |
1551 | 14 | NULL, 0x0, NULL, HFILL } |
1552 | 14 | }, |
1553 | 14 | { &hf_collectd_val_absolute, |
1554 | 14 | { "Absolute value", "collectd.val.absolute", FT_UINT64, BASE_DEC, |
1555 | 14 | NULL, 0x0, NULL, HFILL } |
1556 | 14 | }, |
1557 | 14 | { &hf_collectd_val_unknown, |
1558 | 14 | { "Value of unknown type", "collectd.val.unknown", FT_UINT64, BASE_HEX, |
1559 | 14 | NULL, 0x0, NULL, HFILL } |
1560 | 14 | }, |
1561 | 14 | { &hf_collectd_data_severity, |
1562 | 14 | { "Severity", "collectd.data.severity", FT_UINT64, BASE_HEX | BASE_VAL64_STRING, |
1563 | 14 | VALS64(severity_names), |
1564 | 14 | 0x0, NULL, HFILL } |
1565 | 14 | }, |
1566 | 14 | { &hf_collectd_data_message, |
1567 | 14 | { "Message", "collectd.data.message", FT_STRING, BASE_NONE, |
1568 | 14 | NULL, 0x0, NULL, HFILL } |
1569 | 14 | }, |
1570 | 14 | { &hf_collectd_data_sighash, |
1571 | 14 | { "Signature", "collectd.data.sighash", FT_BYTES, BASE_NONE, |
1572 | 14 | NULL, 0x0, NULL, HFILL } |
1573 | 14 | }, |
1574 | 14 | { &hf_collectd_data_sighash_status, |
1575 | 14 | { "Signature", "collectd.data.sighash.status", FT_UINT8, BASE_NONE, |
1576 | 14 | VALS(proto_checksum_vals), 0x0, NULL, HFILL } |
1577 | 14 | }, |
1578 | 14 | { &hf_collectd_data_initvec, |
1579 | 14 | { "Init vector", "collectd.data.initvec", FT_BYTES, BASE_NONE, |
1580 | 14 | NULL, 0x0, NULL, HFILL } |
1581 | 14 | }, |
1582 | 14 | { &hf_collectd_data_username_len, |
1583 | 14 | { "Username length", "collectd.data.username_length", FT_UINT16, BASE_DEC, |
1584 | 14 | NULL, 0x0, NULL, HFILL } |
1585 | 14 | }, |
1586 | 14 | { &hf_collectd_data_username, |
1587 | 14 | { "Username", "collectd.data.username", FT_STRING, BASE_NONE, |
1588 | 14 | NULL, 0x0, NULL, HFILL } |
1589 | 14 | }, |
1590 | 14 | { &hf_collectd_data_encrypted, |
1591 | 14 | { "Encrypted data", "collectd.data.encrypted", FT_BYTES, BASE_NONE, |
1592 | 14 | NULL, 0x0, NULL, HFILL } |
1593 | 14 | }, |
1594 | 14 | }; |
1595 | | |
1596 | | /* Setup protocol subtree array */ |
1597 | 14 | static int *ett[] = { |
1598 | 14 | &ett_collectd, |
1599 | 14 | &ett_collectd_string, |
1600 | 14 | &ett_collectd_integer, |
1601 | 14 | &ett_collectd_part_value, |
1602 | 14 | &ett_collectd_value, |
1603 | 14 | &ett_collectd_valinfo, |
1604 | 14 | &ett_collectd_signature, |
1605 | 14 | &ett_collectd_encryption, |
1606 | 14 | &ett_collectd_dispatch, |
1607 | 14 | &ett_collectd_invalid_length, |
1608 | 14 | &ett_collectd_unknown, |
1609 | 14 | }; |
1610 | | |
1611 | 14 | static ei_register_info ei[] = { |
1612 | 14 | { &ei_collectd_invalid_length, { "collectd.invalid_length", PI_MALFORMED, PI_ERROR, "Invalid length", EXPFILL }}, |
1613 | 14 | { &ei_collectd_garbage, { "collectd.garbage", PI_MALFORMED, PI_ERROR, "Garbage at end of packet", EXPFILL }}, |
1614 | 14 | { &ei_collectd_data_valcnt, { "collectd.data.valcnt.mismatch", PI_MALFORMED, PI_WARN, "Number of values and length of part do not match. Assuming length is correct.", EXPFILL }}, |
1615 | 14 | { &ei_collectd_type, { "collectd.type.unknown", PI_UNDECODED, PI_NOTE, "Unknown part type", EXPFILL }}, |
1616 | 14 | { &ei_collectd_sighash_bad, { "collectd.data.sighash.bad", PI_CHECKSUM, PI_ERROR, "Bad hash", EXPFILL }}, |
1617 | 14 | }; |
1618 | | |
1619 | | /* Register the protocol name and description */ |
1620 | 14 | proto_collectd = proto_register_protocol("collectd network data", "collectd", "collectd"); |
1621 | | |
1622 | | /* Required function calls to register the header fields and subtrees used */ |
1623 | 14 | proto_register_field_array(proto_collectd, hf, array_length(hf)); |
1624 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
1625 | 14 | expert_collectd = expert_register_protocol(proto_collectd); |
1626 | 14 | expert_register_field_array(expert_collectd, ei, array_length(ei)); |
1627 | | |
1628 | 14 | collectd_module = prefs_register_protocol(proto_collectd, NULL); |
1629 | | |
1630 | 14 | static uat_field_t collectd_uat_flds[] = { |
1631 | 14 | UAT_FLD_CSTRING(uat_collectd_records, username, "Username", "Username"), |
1632 | 14 | UAT_FLD_CSTRING(uat_collectd_records, password, "Password", "Password"), |
1633 | 14 | UAT_END_FIELDS |
1634 | 14 | }; |
1635 | | |
1636 | 14 | collectd_uat = uat_new("collectd Authentication", |
1637 | 14 | sizeof(uat_collectd_record_t), |
1638 | 14 | "collectd", |
1639 | 14 | true, |
1640 | 14 | &uat_collectd_records, |
1641 | 14 | &num_uat, |
1642 | 14 | UAT_AFFECTS_DISSECTION, |
1643 | 14 | NULL, |
1644 | 14 | uat_collectd_record_copy_cb, |
1645 | 14 | uat_collectd_record_update_cb, |
1646 | 14 | uat_collectd_record_free_cb, |
1647 | 14 | NULL, |
1648 | 14 | NULL, |
1649 | 14 | collectd_uat_flds); |
1650 | | |
1651 | 14 | prefs_register_uat_preference(collectd_module, "auth", "Authentication", "A table of user credentials for verifying signatures and decrypting encrypted packets", collectd_uat); |
1652 | | |
1653 | 14 | tap_collectd = register_tap ("collectd"); |
1654 | | |
1655 | 14 | collectd_handle = register_dissector("collectd", dissect_collectd, proto_collectd); |
1656 | 14 | } |
1657 | | |
1658 | | void proto_reg_handoff_collectd (void) |
1659 | 14 | { |
1660 | 14 | dissector_add_uint_with_preference("udp.port", UDP_PORT_COLLECTD, collectd_handle); |
1661 | | |
1662 | 14 | collectd_stats_tree_register (); |
1663 | 14 | } /* void proto_reg_handoff_collectd */ |
1664 | | |
1665 | | /* |
1666 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
1667 | | * |
1668 | | * Local variables: |
1669 | | * c-basic-offset: 8 |
1670 | | * tab-width: 8 |
1671 | | * indent-tabs-mode: t |
1672 | | * End: |
1673 | | * |
1674 | | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
1675 | | * :indentSize=8:tabSize=8:noTabs=false: |
1676 | | */ |