/* tap-diameter-avp.c * Copyright 2010 Andrej Kuehnal * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later */ /* * This TAP enables extraction of most important diameter fields in text format. * - much more performance than -T text and -T pdml * - more powerful than -T field and -z proto,colinfo * - exacltly one text line per diameter message * - multiple diameter messages in one frame supported * E.g. one device watchdog answer and two credit control answers * in one TCP packet produces 3 text lines. * - several fields with same name within one diameter message supported * E.g. Multiple AVP(444) Subscription-Id-Data once with IMSI once with MSISDN * - several grouped AVPs supported * E.g. Zero or more Multiple-Services-Credit-Control AVPs(456) */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include void register_tap_listener_diameteravp(void); /* used to keep track of the statistics for an entire program interface */ typedef struct _diameteravp_t { guint32 frame; guint32 diammsg_toprocess; guint32 cmd_code; guint32 req_count; guint32 ans_count; guint32 paired_ans_count; gchar *filter; } diameteravp_t; /* Copied from proto.c */ static gboolean tree_traverse_pre_order(proto_tree *tree, proto_tree_traverse_func func, gpointer data) { proto_node *pnode = tree; proto_node *child; proto_node *current; if (func(pnode, data)) return TRUE; child = pnode->first_child; while (child != NULL) { current = child; child = current->next; if (tree_traverse_pre_order((proto_tree *)current, func, data)) return TRUE; } return FALSE; } static gboolean diam_tree_to_csv(proto_node *node, gpointer data) { char *val_str = NULL; char *val_tmp = NULL; ftenum_t ftype; field_info *fi; header_field_info *hfi; if (!node) { fprintf(stderr, "traverse end: empty node. node='%p' data='%p'\n", (void *)node, (void *)data); return FALSE; } fi = node->finfo; hfi = fi ? fi->hfinfo : NULL; if (!hfi) { fprintf(stderr, "traverse end: hfi not found. node='%p'\n", (void *)node); return FALSE; } ftype = fvalue_type_ftenum(&fi->value); if (ftype != FT_NONE && ftype != FT_PROTOCOL) { /* convert value to string */ val_tmp = fvalue_to_string_repr(NULL, &fi->value, FTREPR_DISPLAY, hfi->display); if (val_tmp) { val_str = g_strdup(val_tmp); wmem_free(NULL, val_tmp); } else val_str = ws_strdup_printf("unsupported type: %s", ftype_name(ftype)); /*printf("traverse: name='%s', abbrev='%s',desc='%s', val='%s'\n", hfi->name, hfi->abbrev, ftype_name(hfi->type), val_str);*/ printf("%s='%s' ", hfi->name, val_str); g_free(val_str); } return FALSE; } static tap_packet_status diameteravp_packet(void *pds, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pdi, tap_flags_t flags _U_) { tap_packet_status ret = TAP_PACKET_DONT_REDRAW; double resp_time = 0.; gboolean is_request = TRUE; guint32 cmd_code = 0; guint32 req_frame = 0; guint32 ans_frame = 0; guint32 diam_child_node = 0; proto_node *current = NULL; proto_node *node = NULL; header_field_info *hfi = NULL; field_info *finfo = NULL; const diameter_req_ans_pair_t *dp = (const diameter_req_ans_pair_t *)pdi; diameteravp_t *ds = NULL; /* Validate paramerers. */ if (!dp || !edt || !edt->tree) return ret; /* Several diameter messages within one frame are possible. * * Check if we processing the message in same frame like befor or in new frame.*/ ds = (diameteravp_t *)pds; if (pinfo->num > ds->frame) { ds->frame = pinfo->num; ds->diammsg_toprocess = 0; } else { ds->diammsg_toprocess += 1; } /* Extract data from request/answer pair provided by diameter dissector.*/ is_request = dp->processing_request; cmd_code = dp->cmd_code; req_frame = dp->req_frame; ans_frame = dp->ans_frame; if (!is_request) { nstime_t ns; nstime_delta(&ns, &pinfo->abs_ts, &dp->req_time); resp_time = nstime_to_sec(&ns); resp_time = resp_time < 0. ? 0. : resp_time; } /* Check command code provided by command line option.*/ if (ds->cmd_code && ds->cmd_code != cmd_code) return ret; /* Loop over top level nodes */ node = edt->tree->first_child; while (node != NULL) { current = node; node = current->next; finfo = current->finfo; hfi = finfo ? finfo->hfinfo : NULL; /*fprintf(stderr, "DEBUG: diameteravp_packet %d %p %p node=%p abbrev=%s\n", cmd_code, edt, edt->tree, current, hfi->abbrev);*/ /* process current diameter subtree in the current frame. */ if (hfi && hfi->abbrev && strcmp(hfi->abbrev, "diameter") == 0) { /* Process current diameter message in the frame */ if (ds->diammsg_toprocess == diam_child_node) { if (is_request) { ds->req_count++; } else { ds->ans_count++; if (req_frame > 0) ds->paired_ans_count++; } /* Output frame data.*/ printf("frame='%u' time='%f' src='%s' srcport='%u' dst='%s' dstport='%u' proto='diameter' msgnr='%u' is_request='%d' cmd='%u' req_frame='%u' ans_frame='%u' resp_time='%f' ", pinfo->num, nstime_to_sec(&pinfo->abs_ts), address_to_str(pinfo->pool, &pinfo->src), pinfo->srcport, address_to_str(pinfo->pool, &pinfo->dst), pinfo->destport, ds->diammsg_toprocess, is_request, cmd_code, req_frame, ans_frame, resp_time); /* Visit selected nodes of one diameter message.*/ tree_traverse_pre_order(current, diam_tree_to_csv, &ds); /* End of message.*/ printf("\n"); /*printf("hfi: name='%s', msg_curr='%d' abbrev='%s',type='%s'\n", hfi->name, diam_child_node, hfi->abbrev, ftype_name(hfi->type));*/ } diam_child_node++; } } return ret; } static void diameteravp_draw(void *pds) { diameteravp_t *ds = (diameteravp_t *)pds; /* printing results */ printf("=== Diameter Summary ===\nrequest count:\t%u\nanswer count:\t%u\nreq/ans pairs:\t%u\n", ds->req_count, ds->ans_count, ds->paired_ans_count); } static void diameteravp_init(const char *opt_arg, void *userdata _U_) { diameteravp_t *ds; gchar *field = NULL; gchar **tokens; guint opt_count = 0; guint opt_idx = 0; GString *filter = NULL; GString *error_string = NULL; ds = g_new(diameteravp_t, 1); ds->frame = 0; ds->diammsg_toprocess = 0; ds->cmd_code = 0; ds->req_count = 0; ds->ans_count = 0; ds->paired_ans_count = 0; ds->filter = NULL; filter = g_string_new("diameter"); /* Split command line options. */ tokens = g_strsplit(opt_arg, ",", 1024); opt_count = 0; while (tokens[opt_count]) opt_count++; if (opt_count > 2) { /* if the token is a not-null string and it's not *, the conversion must succeeed */ if (strlen(tokens[2]) > 0 && tokens[2][0] != '*') { if (!ws_strtou32(tokens[2], NULL, &ds->cmd_code)) { fprintf(stderr, "Invalid integer token: %s\n", tokens[2]); g_strfreev(tokens); exit(1); } } } /* Loop over diameter field names. */ for (opt_idx=3; opt_idxfilter = g_string_free(filter, FALSE); error_string = register_tap_listener("diameter", ds, ds->filter, 0, NULL, diameteravp_packet, diameteravp_draw, NULL); if (error_string) { /* error, we failed to attach to the tap. clean up */ g_free(ds); cmdarg_err("Couldn't register diam,csv tap: %s", error_string->str); g_string_free(error_string, TRUE); exit(1); } } static stat_tap_ui diameteravp_ui = { REGISTER_STAT_GROUP_GENERIC, NULL, "diameter,avp", diameteravp_init, 0, NULL }; void register_tap_listener_diameteravp(void) { register_stat_tap_ui(&diameteravp_ui, NULL); } /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 8 * tab-width: 8 * indent-tabs-mode: t * End: * * vi: set shiftwidth=8 tabstop=8 noexpandtab: * :indentSize=8:tabSize=8:noTabs=false: */