diff options
author | Martin Mathieson <martin.r.mathieson@googlemail.com> | 2015-10-10 15:53:45 -0700 |
---|---|---|
committer | Martin Mathieson <martin.r.mathieson@googlemail.com> | 2015-10-11 21:59:45 +0000 |
commit | 3221dbf542217cea5acd9ec764cf4779edaf65e8 (patch) | |
tree | 8999d3a38ee9a687737e35cdd39bde1ea7d18a2e | |
parent | a6673b3fde1cac904a405cb1125d547d064d3aa7 (diff) |
LTE RLC graphs - initial version
Change-Id: Ic5f2c353ae1f787ac19cb575a938cb093ff5f6dc
Reviewed-on: https://code.wireshark.org/review/10930
Petri-Dish: Martin Mathieson <martin.r.mathieson@googlemail.com>
Reviewed-by: Martin Mathieson <martin.r.mathieson@googlemail.com>
-rw-r--r-- | epan/dissectors/packet-rlc-lte.h | 4 | ||||
-rw-r--r-- | ui/CMakeLists.txt | 1 | ||||
-rw-r--r-- | ui/Makefile.common | 2 | ||||
-rw-r--r-- | ui/gtk/rlc_lte_graph.c | 595 | ||||
-rw-r--r-- | ui/qt/CMakeLists.txt | 3 | ||||
-rw-r--r-- | ui/qt/Makefile.am | 2 | ||||
-rw-r--r-- | ui/qt/Makefile.common | 4 | ||||
-rw-r--r-- | ui/qt/Wireshark.pro | 3 | ||||
-rw-r--r-- | ui/qt/lte_rlc_graph_dialog.cpp | 643 | ||||
-rw-r--r-- | ui/qt/lte_rlc_graph_dialog.h | 118 | ||||
-rw-r--r-- | ui/qt/lte_rlc_graph_dialog.ui | 275 | ||||
-rw-r--r-- | ui/qt/main_window.cpp | 1 | ||||
-rw-r--r-- | ui/qt/main_window.h | 1 | ||||
-rw-r--r-- | ui/qt/main_window.ui | 11 | ||||
-rw-r--r-- | ui/qt/main_window_slots.cpp | 7 | ||||
-rw-r--r-- | ui/tap-rlc-graph.c | 309 | ||||
-rw-r--r-- | ui/tap-rlc-graph.h | 103 |
17 files changed, 1631 insertions, 451 deletions
diff --git a/epan/dissectors/packet-rlc-lte.h b/epan/dissectors/packet-rlc-lte.h index 4a5d162bd0..d945c03830 100644 --- a/epan/dissectors/packet-rlc-lte.h +++ b/epan/dissectors/packet-rlc-lte.h @@ -20,6 +20,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#ifndef PACKET_RLC_LTE_H +#define PACKET_RLC_LTE_H + /* rlcMode */ #define RLC_TM_MODE 1 #define RLC_UM_MODE 2 @@ -153,4 +156,5 @@ void set_rlc_lte_drb_li_field(packet_info *pinfo, guint16 ueid, guint8 drbid, gb continues until the end of the frame) */ #define RLC_LTE_PAYLOAD_TAG 0x01 +#endif diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 3a36920839..55831df520 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -54,6 +54,7 @@ set(COMMON_UI_SRC tap-rtp-common.c tap-sctp-analysis.c tap-sequence-analysis.c + tap-rlc-graph.c tap-tcp-stream.c text_import.c time_shift.c diff --git a/ui/Makefile.common b/ui/Makefile.common index 2e24319d42..fd17475a36 100644 --- a/ui/Makefile.common +++ b/ui/Makefile.common @@ -72,6 +72,7 @@ WIRESHARK_UI_SRC = \ ssl_key_export.c \ tap_export_pdu.c \ tap-iax2-analysis.c \ + tap-rlc-graph.c \ tap-rtp-common.c \ tap-sctp-analysis.c \ tap-sequence-analysis.c \ @@ -116,6 +117,7 @@ noinst_HEADERS = \ ssl_key_export.h \ tap_export_pdu.h \ tap-iax2-analysis.h \ + tap-rlc-graph.h \ tap-rtp-analysis.h \ tap-rtp-common.h \ tap-sctp-analysis.h \ diff --git a/ui/gtk/rlc_lte_graph.c b/ui/gtk/rlc_lte_graph.c index b318800bb7..a352b21a20 100644 --- a/ui/gtk/rlc_lte_graph.c +++ b/ui/gtk/rlc_lte_graph.c @@ -32,6 +32,8 @@ # include <gdk/gdkkeysyms-compat.h> #endif +#include <ui/tap-rlc-graph.h> + #include <epan/packet.h> #include <epan/epan_dissect.h> #include <epan/dissectors/packet-rlc-lte.h> @@ -63,28 +65,7 @@ extern int proto_rlc_lte; -struct segment { - struct segment *next; - guint32 num; /* framenum */ - guint32 rel_secs; - guint32 rel_usecs; - guint32 abs_secs; - guint32 abs_usecs; - - gboolean isControlPDU; - guint16 SN; - guint16 isResegmented; - guint16 ACKNo; - #define MAX_NACKs 128 - guint16 noOfNACKs; - guint16 NACKs[MAX_NACKs]; - - guint16 ueid; - guint16 channelType; - guint16 channelId; - guint8 rlcMode; - guint8 direction; -}; + struct line { double x1, y1, x2, y2; @@ -119,7 +100,7 @@ struct ellipse_params { struct element { ElementType type; GdkRGBA *elment_color_p; - struct segment *parent; + struct rlc_segment *parent; union { struct line_params line; struct ellipse_params ellipse; @@ -132,7 +113,7 @@ struct element_list { }; struct axis { - struct graph *g; /* which graph we belong to */ + struct gtk_rlc_graph *g; /* which graph we belong to */ GtkWidget *drawing_area; /* Double-buffering to avoid flicker */ #if GTK_CHECK_VERSION(2,22,0) @@ -199,7 +180,9 @@ struct grab { }; -struct graph { + +/* struct rlc_graph is shared between GTK and Qt implementations. */ +struct gtk_rlc_graph { #define GRAPH_DESTROYED (1 << 0) int flags; GtkWidget *toplevel; /* keypress handler needs this */ @@ -239,18 +222,7 @@ struct graph { struct cross cross; struct axis *x_axis, *y_axis; - /* List of segments to show */ - struct segment *segments; - - /* These are filled in with the channel/direction this graph is showing */ - guint16 ueid; - guint16 channelType; - guint16 channelId; - guint8 rlcMode; - guint8 direction; - - /* Lists of elements to draw */ - struct element_list *elists; /* element lists */ + struct rlc_graph graph; /* Colours, etc to be used in drawing */ struct style_rlc_lte style; @@ -272,37 +244,31 @@ static int refnum = 0; /*static int debugging = DBS_TPUT_ELMTS;*/ static int debugging = 0; -static void create_gui(struct graph * ); -static void create_drawing_area(struct graph * ); +static void create_gui(struct gtk_rlc_graph * ); +static void create_drawing_area(struct gtk_rlc_graph * ); static void callback_toplevel_destroy(GtkWidget * , gpointer ); static void callback_create_help(GtkWidget * , gpointer ); static void get_mouse_position(GtkWidget *, int *pointer_x, int *pointer_y, GdkModifierType *mask); -static rlc_lte_tap_info *select_rlc_lte_session(capture_file *, struct segment * ); -static int compare_headers(guint16 ueid1, guint16 channelType1, guint16 channelId1, guint8 rlcMode1, guint8 direction1, - guint16 ueid2, guint16 channelType2, guint16 channelId2, guint8 rlcMode2, guint8 direction2, - gboolean isControlFrame); -static void get_data_control_counts(struct graph *g, int *data, int *acks, int *nacks); - -static struct graph *graph_new(void); -static void graph_destroy(struct graph * ); -static void graph_initialize_values(struct graph * ); -static void graph_init_sequence(struct graph * ); -static void draw_element_line(struct graph * , struct element * , cairo_t * , GdkRGBA *new_color); -static void draw_element_ellipse(struct graph * , struct element * , cairo_t *cr , GdkRGBA *new_color); -static void graph_display(struct graph * ); -static void graph_pixmaps_create(struct graph * ); -static void graph_pixmaps_switch(struct graph * ); -static void graph_pixmap_draw(struct graph * ); -static void graph_pixmap_display(struct graph * ); -static void graph_element_lists_make(struct graph * ); -static void graph_element_lists_free(struct graph * ); -static void graph_element_lists_initialize(struct graph * ); -static void graph_title_pixmap_create(struct graph * ); -static void graph_title_pixmap_draw(struct graph * ); -static void graph_title_pixmap_display(struct graph * ); -static void graph_segment_list_get(struct graph *, gboolean channel_known ); -static void graph_segment_list_free(struct graph * ); -static void graph_select_segment(struct graph * , int , int ); +static void get_data_control_counts(struct gtk_rlc_graph *g, int *data, int *acks, int *nacks); + +static struct gtk_rlc_graph *graph_new(void); +static void graph_destroy(struct gtk_rlc_graph * ); +static void graph_initialize_values(struct gtk_rlc_graph * ); +static void graph_init_sequence(struct gtk_rlc_graph * ); +static void draw_element_line(struct gtk_rlc_graph * , struct element * , cairo_t * , GdkRGBA *new_color); +static void draw_element_ellipse(struct gtk_rlc_graph * , struct element * , cairo_t *cr , GdkRGBA *new_color); +static void graph_display(struct gtk_rlc_graph * ); +static void graph_pixmaps_create(struct gtk_rlc_graph * ); +static void graph_pixmaps_switch(struct gtk_rlc_graph * ); +static void graph_pixmap_draw(struct gtk_rlc_graph * ); +static void graph_pixmap_display(struct gtk_rlc_graph * ); +static void graph_element_lists_make(struct gtk_rlc_graph * ); +static void graph_element_lists_free(struct gtk_rlc_graph * ); +static void graph_element_lists_initialize(struct gtk_rlc_graph * ); +static void graph_title_pixmap_create(struct gtk_rlc_graph * ); +static void graph_title_pixmap_draw(struct gtk_rlc_graph * ); +static void graph_title_pixmap_display(struct gtk_rlc_graph * ); +static void graph_select_segment(struct gtk_rlc_graph * , int , int ); static int line_detect_collision(struct element * , int , int ); static int ellipse_detect_collision(struct element *e, int x, int y); static void axis_pixmaps_create(struct axis * ); @@ -318,15 +284,15 @@ static void axis_ticks_down(int * , int * ); static void axis_destroy(struct axis * ); static int get_label_dim(struct axis * , int , double ); -static void toggle_crosshairs(struct graph *); -static void cross_draw(struct graph * , int x, int y); -static void cross_erase(struct graph * ); -static void zoomrect_draw(struct graph * , int , int ); -static void zoomrect_erase(struct graph * ); +static void toggle_crosshairs(struct gtk_rlc_graph *); +static void cross_draw(struct gtk_rlc_graph * , int x, int y); +static void cross_erase(struct gtk_rlc_graph * ); +static void zoomrect_draw(struct gtk_rlc_graph * , int , int ); +static void zoomrect_erase(struct gtk_rlc_graph * ); static gboolean motion_notify_event(GtkWidget * , GdkEventMotion * , gpointer ); -static void toggle_time_origin(struct graph * ); -static void restore_initial_graph_view(struct graph *g); +static void toggle_time_origin(struct gtk_rlc_graph * ); +static void restore_initial_graph_view(struct gtk_rlc_graph *g); static gboolean configure_event(GtkWidget * , GdkEventConfigure * , gpointer ); #if GTK_CHECK_VERSION(3,0,0) static gboolean draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data); @@ -336,10 +302,10 @@ static gboolean expose_event(GtkWidget * , GdkEventExpose * , gpointer ); static gboolean button_press_event(GtkWidget * , GdkEventButton * , gpointer ); static gboolean button_release_event(GtkWidget * , GdkEventButton * , gpointer ); static gboolean key_press_event(GtkWidget * , GdkEventKey * , gpointer ); -static void graph_initialize(struct graph *); -static void graph_get_bounds(struct graph *); -static void graph_read_config(struct graph *); -static void rlc_lte_make_elmtlist(struct graph *); +static void graph_initialize(struct gtk_rlc_graph *); +static void graph_get_bounds(struct gtk_rlc_graph *); +static void graph_read_config(struct gtk_rlc_graph *); +static void rlc_lte_make_elmtlist(struct gtk_rlc_graph *); #if defined(_WIN32) && !defined(__MINGW32__) && (_MSC_VER < 1800) /* Starting VS2013, rint already defined in math.h. No need to redefine */ @@ -407,13 +373,19 @@ static void unset_busy_cursor(GdkWindow *w) void rlc_lte_graph_cb(GtkAction *action _U_, gpointer user_data _U_) { - struct segment current; - struct graph *g; + struct rlc_segment current; + struct gtk_rlc_graph *g; + gchar *err_msg = NULL; + gboolean free_err_msg = FALSE; debug(DBS_FENTRY) puts("rlc_lte_graph_cb()"); /* Can we choose an RLC channel from the selected frame? */ - if (!select_rlc_lte_session(&cfile, ¤t)) { + if (!select_rlc_lte_session(&cfile, ¤t, &err_msg, &free_err_msg)) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_msg); + if (free_err_msg) { + g_free(err_msg); + } return; } @@ -425,7 +397,7 @@ void rlc_lte_graph_cb(GtkAction *action _U_, gpointer user_data _U_) graph_initialize_values(g); /* Get our list of segments from the packet list */ - graph_segment_list_get(g, FALSE); + rlc_graph_segment_list_get(&cfile, &(g->graph), FALSE, &err_msg, &free_err_msg); create_gui(g); graph_init_sequence(g); @@ -435,9 +407,11 @@ void rlc_lte_graph_known_channel_launch(guint16 ueid, guint8 rlcMode, guint16 channelType, guint16 channelId, guint8 direction) { - struct graph *g; + struct gtk_rlc_graph *g; + gchar *err_msg = NULL; + gboolean free_err_msg = FALSE; - debug(DBS_FENTRY) puts("rlc_lte_graph_known_channel()"); + debug(DBS_FENTRY) puts("rlc_lte_graph_known_channel_launch()"); if (!(g = graph_new())) { return; @@ -447,28 +421,29 @@ void rlc_lte_graph_known_channel_launch(guint16 ueid, guint8 rlcMode, graph_initialize_values(g); /* Can set channel info for graph now */ - g->ueid = ueid; - g->rlcMode = rlcMode; - g->channelType = channelType; - g->channelId = channelId; - g->direction = direction; + g->graph.ueid = ueid; + g->graph.rlcMode = rlcMode; + g->graph.channelType = channelType; + g->graph.channelId = channelId; + g->graph.direction = direction; + g->graph.channelSet = TRUE; /* Get our list of segments from the packet list */ - graph_segment_list_get(g, TRUE); + rlc_graph_segment_list_get(&cfile, &(g->graph), TRUE, &err_msg, &free_err_msg); create_gui(g); graph_init_sequence(g); } -static void create_gui(struct graph *g) +static void create_gui(struct gtk_rlc_graph *g) { debug(DBS_FENTRY) puts("create_gui()"); /* create_text_widget(g); */ create_drawing_area(g); } -static void create_drawing_area(struct graph *g) +static void create_drawing_area(struct gtk_rlc_graph *g) { #if GTK_CHECK_VERSION(3,0,0) GtkStyleContext *context; @@ -482,10 +457,10 @@ static void create_drawing_area(struct graph *g) /* Set channel details in title */ g_snprintf(window_title, WINDOW_TITLE_LENGTH, "LTE RLC Graph %d: %s (UE-%u, chan=%s%u %s - %s)", refnum, display_name, - g->ueid, (g->channelType == CHANNEL_TYPE_SRB) ? "SRB" : "DRB", - g->channelId, - (g->direction == DIRECTION_UPLINK) ? "UL" : "DL", - (g->rlcMode == RLC_UM_MODE) ? "UM" : "AM"); + g->graph.ueid, (g->graph.channelType == CHANNEL_TYPE_SRB) ? "SRB" : "DRB", + g->graph.channelId, + (g->graph.direction == DIRECTION_UPLINK) ? "UL" : "DL", + (g->graph.rlcMode == RLC_UM_MODE) ? "UM" : "AM"); g_free(display_name); g->toplevel = dlg_window_new("RLC Graph"); gtk_window_set_title(GTK_WINDOW(g->toplevel), window_title); @@ -550,11 +525,11 @@ static void create_drawing_area(struct graph *g) static void callback_toplevel_destroy(GtkWidget *widget _U_, gpointer data) { - struct graph *g = (struct graph * )data; + struct gtk_rlc_graph *g = (struct gtk_rlc_graph * )data; if (!(g->flags & GRAPH_DESTROYED)) { g->flags |= GRAPH_DESTROYED; - graph_destroy((struct graph * )data); + graph_destroy((struct gtk_rlc_graph * )data); } } @@ -608,11 +583,11 @@ static void get_mouse_position(GtkWidget *widget, int *pointer_x, int *pointer_y #endif } -static struct graph *graph_new(void) +static struct gtk_rlc_graph *graph_new(void) { - struct graph *g; + struct gtk_rlc_graph *g; - g = (struct graph * )g_malloc0(sizeof(struct graph)); + g = (struct gtk_rlc_graph * )g_malloc0(sizeof(struct gtk_rlc_graph)); graph_element_lists_initialize(g); g->x_axis = (struct axis * )g_malloc0(sizeof(struct axis)); @@ -636,7 +611,7 @@ static struct graph *graph_new(void) return g; } -static void graph_initialize_values(struct graph *g) +static void graph_initialize_values(struct gtk_rlc_graph *g) { g->geom.width = g->wp.width = 750; g->geom.height = g->wp.height = 550; @@ -654,7 +629,7 @@ static void graph_initialize_values(struct graph *g) g->grab.grabbed = 0; } -static void graph_init_sequence(struct graph *g) +static void graph_init_sequence(struct gtk_rlc_graph *g) { debug(DBS_FENTRY) puts("graph_init_sequence()"); @@ -679,7 +654,7 @@ static void graph_init_sequence(struct graph *g) axis_display(g->x_axis); } -static void graph_initialize(struct graph *g) +static void graph_initialize(struct gtk_rlc_graph *g) { debug(DBS_FENTRY) puts("graph_initialize()"); graph_get_bounds(g); @@ -691,7 +666,7 @@ static void graph_initialize(struct graph *g) graph_read_config(g); } -static void graph_destroy(struct graph *g) +static void graph_destroy(struct gtk_rlc_graph *g) { debug(DBS_FENTRY) puts("graph_destroy()"); @@ -716,318 +691,38 @@ static void graph_destroy(struct graph *g) #endif /* GTK_CHECK_VERSION(2,22,0) */ g_free(g->x_axis); g_free(g->y_axis); - graph_segment_list_free(g); + rlc_graph_segment_list_free(&g->graph); graph_element_lists_free(g); g_free(g); } - -typedef struct rlc_scan_t { - struct graph *g; - struct segment *last; -} rlc_scan_t; - - -static int -tapall_rlc_lte_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip) -{ - rlc_scan_t *ts = (rlc_scan_t *)pct; - struct graph *g = ts->g; - const rlc_lte_tap_info *rlchdr = (const rlc_lte_tap_info*)vip; - - /* See if this one matches current channel */ - if (compare_headers(g->ueid, g->channelType, g->channelId, g->rlcMode, g->direction, - rlchdr->ueid, rlchdr->channelType, rlchdr->channelId, rlchdr->rlcMode, rlchdr->direction, - rlchdr->isControlPDU)) { - - struct segment *segment = (struct segment *)g_malloc(sizeof(struct segment)); - - /* It matches. Add to end of segment list */ - segment->next = NULL; - segment->num = pinfo->fd->num; - segment->rel_secs = (guint32) pinfo->rel_ts.secs; - segment->rel_usecs = pinfo->rel_ts.nsecs/1000; - segment->abs_secs = (guint32) pinfo->fd->abs_ts.secs; - segment->abs_usecs = pinfo->fd->abs_ts.nsecs/1000; - - segment->ueid = rlchdr->ueid; - segment->channelType = rlchdr->channelType; - segment->channelId = rlchdr->channelId; - segment->direction = rlchdr->direction; - - segment->isControlPDU = rlchdr->isControlPDU; - - if (!rlchdr->isControlPDU) { - /* Data */ - segment->SN = rlchdr->sequenceNumber; - segment->isResegmented = rlchdr->isResegmented; - } - else { - /* Status PDU */ - gint n; - segment->ACKNo = rlchdr->ACKNo; - segment->noOfNACKs = rlchdr->noOfNACKs; - for (n=0; n < rlchdr->noOfNACKs; n++) { - segment->NACKs[n] = rlchdr->NACKs[n]; - } - } - - /* Add to list */ - if (ts->g->segments) { - /* Add to end of existing last element */ - ts->last->next = segment; - } else { - /* Make this the first (only) segment */ - ts->g->segments = segment; - } - - /* This one is now the last one */ - ts->last = segment; - } - - return 0; -} - - -/* Here we collect all the external data we will ever need */ -static void graph_segment_list_get(struct graph *g, gboolean channel_known) -{ - struct segment current; - GString *error_string; - rlc_scan_t ts; - current.ueid = 0; - current.rlcMode = 0; - current.channelType = 0; - current.channelId = 0; - current.isControlPDU = 0; - current.direction = 0; - - debug(DBS_FENTRY) puts("graph_segment_list_get()"); - - if (!channel_known) { - select_rlc_lte_session(&cfile, ¤t); - - g->ueid = current.ueid; - g->rlcMode = current.rlcMode; - g->channelType = current.channelType; - g->channelId = current.channelId; - g->direction = (!current.isControlPDU) ? current.direction : !current.direction; - } - - /* rescan all the packets and pick up all frames for this channel. - * we only filter for LTE RLC here for speed and do the actual compare - * in the tap listener - */ - - ts.g = g; - ts.last = NULL; - error_string = register_tap_listener("rlc-lte", &ts, "rlc-lte", 0, NULL, tapall_rlc_lte_packet, NULL); - if (error_string){ - fprintf(stderr, "wireshark: Couldn't register rlc_lte_graph tap: %s\n", - error_string->str); - g_string_free(error_string, TRUE); - exit(1); - } - cf_retap_packets(&cfile); - remove_tap_listener(&ts); -} - - -typedef struct _th_t { - int num_hdrs; - #define MAX_SUPPORTED_CHANNELS 8 - rlc_lte_tap_info *rlchdrs[MAX_SUPPORTED_CHANNELS]; -} th_t; - - -static int -tap_lte_rlc_packet(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip) -{ - int n; - gboolean is_unique = TRUE; - th_t *th = (th_t *)pct; - const rlc_lte_tap_info *header = (const rlc_lte_tap_info*)vip; - - /* Check new header details against any/all stored ones */ - for (n=0; n < th->num_hdrs; n++) { - rlc_lte_tap_info *stored = th->rlchdrs[n]; - - if (compare_headers(stored->ueid, stored->channelType, stored->channelId, stored->rlcMode, stored->direction, - header->ueid, header->channelType, header->channelId, header->rlcMode, header->direction, - header->isControlPDU)) { - is_unique = FALSE; - break; - } - } - - /* Add address if unique and have space for it */ - if (is_unique && (th->num_hdrs < MAX_SUPPORTED_CHANNELS)) { - /* Copy the tap stuct in as next header */ - /* Need to take a deep copy of the tap struct, it may not be valid - to read after this function returns? */ - th->rlchdrs[th->num_hdrs] = g_new(rlc_lte_tap_info,1); - *(th->rlchdrs[th->num_hdrs]) = *header; - - /* Store in direction of data though... */ - if (th->rlchdrs[th->num_hdrs]->isControlPDU) { - th->rlchdrs[th->num_hdrs]->direction = !th->rlchdrs[th->num_hdrs]->direction; - } - th->num_hdrs++; - } - - return 0; -} - - -/* XXX should be enhanced so that if we have multiple RLC channels in the same MAC frame - * then present the user with a dialog where the user can select WHICH RLC - * channel to graph. - */ -static rlc_lte_tap_info *select_rlc_lte_session(capture_file *cf, struct segment *hdrs) -{ - frame_data *fdata; - epan_dissect_t edt; - dfilter_t *sfcode; - gchar *err_msg; - GString *error_string; - nstime_t rel_ts; - th_t th = {0, {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}}; - - if (cf->state == FILE_CLOSED) { - return NULL; - } - - fdata = cf->current_frame; - - /* no real filter yet */ - if (!dfilter_compile("rlc-lte", &sfcode, &err_msg)) { - simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_msg); - g_free(err_msg); - return NULL; - } - - /* dissect the current record */ - if (!cf_read_record(cf, fdata)) { - return NULL; /* error reading the record */ - } - - error_string = register_tap_listener("rlc-lte", &th, NULL, 0, NULL, tap_lte_rlc_packet, NULL); - if (error_string){ - fprintf(stderr, "wireshark: Couldn't register rlc_lte_graph tap: %s\n", - error_string->str); - g_string_free(error_string, TRUE); - exit(1); - } - - epan_dissect_init(&edt, cf->epan, TRUE, FALSE); - epan_dissect_prime_dfilter(&edt, sfcode); - epan_dissect_run_with_taps(&edt, cf->cd_t, &cf->phdr, frame_tvbuff_new_buffer(fdata, &cf->buf), fdata, NULL); - rel_ts = edt.pi.rel_ts; - epan_dissect_cleanup(&edt); - remove_tap_listener(&th); - - if (th.num_hdrs == 0){ - /* This "shouldn't happen", as our menu items shouldn't - * even be enabled if the selected packet isn't an RLC PDU - * as rlc_lte_graph_selected_packet_enabled() is used - * to determine whether to enable any of our menu items. */ - simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, - "Selected packet doesn't have an RLC PDU"); - return NULL; - } - /* XXX fix this later, we should show a dialog allowing the user - to select which session he wants here - */ - if (th.num_hdrs>1){ - /* can only handle a single RLC channel yet */ - simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, - "The selected packet has more than one LTE RLC channel " - "in it."); - return NULL; - } - - /* For now, still always choose the first/only one */ - hdrs->num = fdata->num; - hdrs->rel_secs = (guint32) rel_ts.secs; - hdrs->rel_usecs = rel_ts.nsecs/1000; - hdrs->abs_secs = (guint32) fdata->abs_ts.secs; - hdrs->abs_usecs = fdata->abs_ts.nsecs/1000; - - hdrs->ueid = th.rlchdrs[0]->ueid; - hdrs->channelType = th.rlchdrs[0]->channelType; - hdrs->channelId = th.rlchdrs[0]->channelId; - hdrs->rlcMode = th.rlchdrs[0]->rlcMode; - hdrs->isControlPDU = th.rlchdrs[0]->isControlPDU; - hdrs->direction = !hdrs->isControlPDU ? th.rlchdrs[0]->direction : !th.rlchdrs[0]->direction; - - return th.rlchdrs[0]; -} - -static int compare_headers(guint16 ueid1, guint16 channelType1, guint16 channelId1, guint8 rlcMode1, guint8 direction1, - guint16 ueid2, guint16 channelType2, guint16 channelId2, guint8 rlcMode2, guint8 direction2, - gboolean frameIsControl) -{ - /* Same direction, data - OK. */ - if (!frameIsControl) { - return (direction1 == direction2) && - (ueid1 == ueid2) && - (channelType1 == channelType2) && - (channelId1 == channelId2) && - (rlcMode1 == rlcMode2); - } - else { - if (frameIsControl && (rlcMode1 == RLC_AM_MODE) && (rlcMode2 == RLC_AM_MODE)) { - return ((direction1 != direction2) && - (ueid1 == ueid2) && - (channelType1 == channelType2) && - (channelId1 == channelId2)); - } - else { - return FALSE; - } - } -} - -/* Free all segments in the graph */ -static void graph_segment_list_free(struct graph *g) -{ - struct segment *segment; - - while (g->segments) { - segment = g->segments->next; - g_free(g->segments); - g->segments = segment; - } - g->segments = NULL; -} - -static void graph_element_lists_initialize(struct graph *g) +static void graph_element_lists_initialize(struct gtk_rlc_graph *g) { - g->elists = (struct element_list *)g_malloc0(sizeof(struct element_list)); - g->elists->elements = NULL; - g->elists->next = NULL; + g->graph.elists = (struct element_list *)g_malloc0(sizeof(struct element_list)); + g->graph.elists->elements = NULL; + g->graph.elists->next = NULL; } -static void graph_element_lists_make(struct graph *g) +static void graph_element_lists_make(struct gtk_rlc_graph *g) { debug(DBS_FENTRY) puts("graph_element_lists_make()"); rlc_lte_make_elmtlist(g); } -static void graph_element_lists_free(struct graph *g) +static void graph_element_lists_free(struct gtk_rlc_graph *g) { struct element_list *list, *next_list; - for (list=g->elists; list; list=next_list) { + for (list=g->graph.elists; list; list=next_list) { g_free(list->elements); next_list = list->next; g_free(list); } - g->elists = NULL; /* just to make debugging easier */ + g->graph.elists = NULL; /* just to make debugging easier */ } -static void graph_title_pixmap_create(struct graph *g) +static void graph_title_pixmap_create(struct gtk_rlc_graph *g) { #if GTK_CHECK_VERSION(2,22,0) if (g->title_surface){ @@ -1049,7 +744,7 @@ static void graph_title_pixmap_create(struct graph *g) #endif } -static void graph_title_pixmap_draw(struct graph *g) +static void graph_title_pixmap_draw(struct gtk_rlc_graph *g) { gint w, h; PangoLayout *layout; @@ -1073,7 +768,7 @@ static void graph_title_pixmap_draw(struct graph *g) cairo_destroy(cr); } -static void graph_title_pixmap_display(struct graph *g) +static void graph_title_pixmap_display(struct gtk_rlc_graph *g) { cairo_t *cr; @@ -1088,7 +783,7 @@ static void graph_title_pixmap_display(struct graph *g) cairo_destroy(cr); } -static void graph_pixmaps_create(struct graph *g) +static void graph_pixmaps_create(struct gtk_rlc_graph *g) { debug(DBS_FENTRY) puts("graph_pixmaps_create()"); #if GTK_CHECK_VERSION(2,22,0) @@ -1129,7 +824,7 @@ static void graph_pixmaps_create(struct graph *g) } -static void graph_display(struct graph *g) +static void graph_display(struct gtk_rlc_graph *g) { set_busy_cursor(gtk_widget_get_window(g->drawing_area)); graph_pixmap_draw(g); @@ -1138,7 +833,7 @@ static void graph_display(struct graph *g) graph_pixmap_display(g); } -static void graph_pixmap_display(struct graph *g) +static void graph_pixmap_display(struct gtk_rlc_graph *g) { cairo_t *cr; @@ -1153,12 +848,12 @@ static void graph_pixmap_display(struct graph *g) cairo_destroy(cr); } -static void graph_pixmaps_switch(struct graph *g) +static void graph_pixmaps_switch(struct gtk_rlc_graph *g) { g->displayed = 1 ^ g->displayed; } -static void graph_pixmap_draw(struct graph *g) +static void graph_pixmap_draw(struct gtk_rlc_graph *g) { struct element_list *list; struct element *e; @@ -1199,7 +894,7 @@ static void graph_pixmap_draw(struct graph *g) cairo_set_line_width(cr_elements, 1.0); /* Draw all elements */ - for (list=g->elists; list; list=list->next) { + for (list=g->graph.elists; list; list=list->next) { for (e=list->elements; e->type != ELMT_NONE; e++) { /* Work out if we need to change colour */ @@ -1244,7 +939,7 @@ static void graph_pixmap_draw(struct graph *g) cairo_destroy(cr_elements); } -static void draw_element_line(struct graph *g, struct element *e, cairo_t *cr, +static void draw_element_line(struct gtk_rlc_graph *g, struct element *e, cairo_t *cr, GdkRGBA *new_color) { int xx1, xx2, yy1, yy2; @@ -1282,7 +977,7 @@ static void draw_element_line(struct graph *g, struct element *e, cairo_t *cr, cairo_line_to(cr, xx2+0.5, yy2+0.5); } -static void draw_element_ellipse(struct graph *g, struct element *e, cairo_t *cr, +static void draw_element_ellipse(struct gtk_rlc_graph *g, struct element *e, cairo_t *cr, GdkRGBA *new_color) { gdouble w = e->p.ellipse.dim.width; @@ -1376,7 +1071,7 @@ static void axis_display(struct axis *axis) /* These show sequence numbers. Avoid subdividing whole numbers. */ static void v_axis_pixmap_draw(struct axis *axis) { - struct graph *g = axis->g; + struct gtk_rlc_graph *g = axis->g; int i; double major_tick; int not_disp, offset, imin, imax; @@ -1476,7 +1171,7 @@ static void v_axis_pixmap_draw(struct axis *axis) show 3 decimal places? */ static void h_axis_pixmap_draw(struct axis *axis) { - struct graph *g = axis->g; + struct gtk_rlc_graph *g = axis->g; int i; double major_tick, minor_tick; int not_disp, rdigits, offset, imin, imax; @@ -1759,7 +1454,7 @@ static double axis_zoom_get(struct axis *axis, int dir) } } -static void graph_select_segment(struct graph *g, int x, int y) +static void graph_select_segment(struct gtk_rlc_graph *g, int x, int y) { struct element_list *list; struct element *e; @@ -1772,7 +1467,7 @@ static void graph_select_segment(struct graph *g, int x, int y) set_busy_cursor(gtk_widget_get_window(g->drawing_area)); - for (list=g->elists; list; list=list->next) { + for (list=g->graph.elists; list; list=list->next) { for (e=list->elements; e->type != ELMT_NONE; e++) { switch (e->type) { case ELMT_LINE: @@ -1854,7 +1549,7 @@ static int ellipse_detect_collision(struct element *e, int x, int y) static gboolean configure_event(GtkWidget *widget _U_, GdkEventConfigure *event, gpointer user_data) { - struct graph *g = (struct graph *)user_data; + struct gtk_rlc_graph *g = (struct gtk_rlc_graph *)user_data; struct zoom new_zoom; int cur_g_width, cur_g_height; int cur_wp_width, cur_wp_height; @@ -1910,7 +1605,7 @@ static gboolean configure_event(GtkWidget *widget _U_, GdkEventConfigure *event, static gboolean draw_event(GtkWidget *widget _U_, cairo_t *cr, gpointer user_data) { - struct graph *g = (struct graph *)user_data; + struct gtk_rlc_graph *g = (struct gtk_rlc_graph *)user_data; debug(DBS_FENTRY) puts("draw_event()"); @@ -1934,7 +1629,7 @@ draw_event(GtkWidget *widget _U_, cairo_t *cr, gpointer user_data) #else static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { - struct graph *g = (struct graph *)user_data; + struct gtk_rlc_graph *g = (struct gtk_rlc_graph *)user_data; cairo_t *cr; debug(DBS_FENTRY) puts("expose_event()"); @@ -1969,7 +1664,7 @@ static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer static void -perform_zoom(struct graph *g, struct zoomfactor *zf, +perform_zoom(struct gtk_rlc_graph *g, struct zoomfactor *zf, int origin_x, int origin_y) { int cur_width = g->geom.width, cur_height = g->geom.height; @@ -2014,7 +1709,7 @@ perform_zoom(struct graph *g, struct zoomfactor *zf, } static void -do_zoom_rectangle(struct graph *g, struct irect lcl_zoomrect) +do_zoom_rectangle(struct gtk_rlc_graph *g, struct irect lcl_zoomrect) { int cur_width = g->wp.width, cur_height = g->wp.height; /* Make copy of geom1 before working out zoom */ @@ -2110,7 +1805,7 @@ do_zoom_rectangle(struct graph *g, struct irect lcl_zoomrect) /* Zoom because of keyboard or mouse press */ -static void do_zoom_common(struct graph *g, GdkEventButton *event, +static void do_zoom_common(struct gtk_rlc_graph *g, GdkEventButton *event, gboolean lock_vertical, gboolean lock_horizontal) { int cur_width = g->geom.width, cur_height = g->geom.height; @@ -2218,21 +1913,21 @@ static void do_zoom_common(struct graph *g, GdkEventButton *event, } -static void do_zoom_keyboard(struct graph *g, +static void do_zoom_keyboard(struct gtk_rlc_graph *g, gboolean lock_vertical, gboolean lock_horizontal) { do_zoom_common(g, NULL, lock_vertical, lock_horizontal); } -static void do_zoom_mouse(struct graph *g, GdkEventButton *event) +static void do_zoom_mouse(struct gtk_rlc_graph *g, GdkEventButton *event) { do_zoom_common(g, event, event->state & GDK_SHIFT_MASK, event->state & GDK_CONTROL_MASK); } -static void do_zoom_in_keyboard(struct graph *g, +static void do_zoom_in_keyboard(struct gtk_rlc_graph *g, gboolean lock_vertical, gboolean lock_horizontal) { @@ -2240,7 +1935,7 @@ static void do_zoom_in_keyboard(struct graph *g, do_zoom_keyboard(g, lock_vertical, lock_horizontal); } -static void do_zoom_out_keyboard(struct graph *g, +static void do_zoom_out_keyboard(struct gtk_rlc_graph *g, gboolean lock_vertical, gboolean lock_horizontal) { @@ -2248,7 +1943,7 @@ static void do_zoom_out_keyboard(struct graph *g, do_zoom_keyboard(g, lock_vertical, lock_horizontal); } -static void do_key_motion(struct graph *g) +static void do_key_motion(struct gtk_rlc_graph *g) { if (g->geom.x > g->wp.x) { g->geom.x = g->wp.x; @@ -2275,25 +1970,25 @@ static void do_key_motion(struct graph *g) } } -static void do_key_motion_up(struct graph *g, int step) +static void do_key_motion_up(struct gtk_rlc_graph *g, int step) { g->geom.y += step; do_key_motion(g); } -static void do_key_motion_down(struct graph *g, int step) +static void do_key_motion_down(struct gtk_rlc_graph *g, int step) { g->geom.y -= step; do_key_motion(g); } -static void do_key_motion_left(struct graph *g, int step) +static void do_key_motion_left(struct gtk_rlc_graph *g, int step) { g->geom.x += step; do_key_motion(g); } -static void do_key_motion_right(struct graph *g, int step) +static void do_key_motion_right(struct gtk_rlc_graph *g, int step) { g->geom.x -= step; do_key_motion(g); @@ -2301,7 +1996,7 @@ static void do_key_motion_right(struct graph *g, int step) static gboolean button_press_event(GtkWidget *widget _U_, GdkEventButton *event, gpointer user_data) { - struct graph *g = (struct graph *)user_data; + struct gtk_rlc_graph *g = (struct gtk_rlc_graph *)user_data; debug(DBS_FENTRY) puts("button_press_event()"); @@ -2327,7 +2022,7 @@ static gboolean button_press_event(GtkWidget *widget _U_, GdkEventButton *event, static gboolean button_release_event(GtkWidget *widget _U_, GdkEventButton *event _U_, gpointer user_data) { - struct graph *g = (struct graph *)user_data; + struct gtk_rlc_graph *g = (struct gtk_rlc_graph *)user_data; /* Turn off grab if right button released */ if (event->button == MOUSE_BUTTON_RIGHT) { @@ -2363,7 +2058,7 @@ static gboolean button_release_event(GtkWidget *widget _U_, GdkEventButton *even static gboolean motion_notify_event(GtkWidget *widget _U_, GdkEventMotion *event, gpointer user_data) { - struct graph *g = (struct graph *)user_data; + struct gtk_rlc_graph *g = (struct gtk_rlc_graph *)user_data; int x, y; GdkModifierType state; @@ -2428,7 +2123,7 @@ static gboolean motion_notify_event(GtkWidget *widget _U_, GdkEventMotion *event static gboolean key_press_event(GtkWidget *widget _U_, GdkEventKey *event, gpointer user_data) { - struct graph *g = (struct graph *)user_data; + struct gtk_rlc_graph *g = (struct gtk_rlc_graph *)user_data; int step; debug(DBS_FENTRY) puts("key_press_event()"); @@ -2518,7 +2213,7 @@ static gboolean key_press_event(GtkWidget *widget _U_, GdkEventKey *event, gpoin return TRUE; } -static void toggle_crosshairs(struct graph *g) +static void toggle_crosshairs(struct gtk_rlc_graph *g) { /* Toggle state */ g->cross.draw ^= 1; @@ -2533,7 +2228,7 @@ static void toggle_crosshairs(struct graph *g) } } -static void cross_draw(struct graph *g, int x, int y) +static void cross_draw(struct gtk_rlc_graph *g, int x, int y) { /* Shouldn't draw twice onto the same position if haven't erased in the meantime! */ @@ -2566,7 +2261,7 @@ static void cross_draw(struct graph *g, int x, int y) g->cross.erase_needed = TRUE; } -static void cross_erase(struct graph *g) +static void cross_erase(struct gtk_rlc_graph *g) { int x = g->cross.x; int y = g->cross.y; @@ -2581,7 +2276,7 @@ static void cross_erase(struct graph *g) g->cross.erase_needed = FALSE; } -static void zoomrect_draw(struct graph *g, int x, int y) +static void zoomrect_draw(struct gtk_rlc_graph *g, int x, int y) { if ((zoomrect.x > g->wp.x) && (zoomrect.x < g->wp.x + g->wp.width) && (zoomrect.y > g->wp.y) && (zoomrect.y < g->wp.y + g->wp.height) && @@ -2601,7 +2296,7 @@ static void zoomrect_draw(struct graph *g, int x, int y) g->zoomrect_erase_needed = TRUE; } -static void zoomrect_erase(struct graph *g) +static void zoomrect_erase(struct gtk_rlc_graph *g) { /* Just redraw what is in the pixmap buffer */ graph_pixmap_display(g); @@ -2610,7 +2305,7 @@ static void zoomrect_erase(struct graph *g) /* Toggle between showing the time starting at 0, or time in capture */ -static void toggle_time_origin(struct graph *g) +static void toggle_time_origin(struct gtk_rlc_graph *g) { g->style.flags ^= TIME_ORIGIN; @@ -2625,7 +2320,7 @@ static void toggle_time_origin(struct graph *g) axis_display(g->x_axis); } -static void restore_initial_graph_view(struct graph *g) +static void restore_initial_graph_view(struct gtk_rlc_graph *g) { g->geom.width = g->wp.width; g->geom.height = g->wp.height; @@ -2642,14 +2337,14 @@ static void restore_initial_graph_view(struct graph *g) } /* Walk the segment list, totalling up data PDUs, status ACKs and NACKs */ -static void get_data_control_counts(struct graph *g, int *data, int *acks, int *nacks) +static void get_data_control_counts(struct gtk_rlc_graph *g, int *data, int *acks, int *nacks) { - struct segment *tmp; + struct rlc_segment *tmp; *data = 0; *acks = 0; *nacks = 0; - for (tmp=g->segments; tmp; tmp=tmp->next) { + for (tmp=g->graph.segments; tmp; tmp=tmp->next) { if (tmp->isControlPDU) { (*acks)++; (*nacks) += tmp->noOfNACKs; @@ -2665,9 +2360,9 @@ static void get_data_control_counts(struct graph *g, int *data, int *acks, int * * Not currently trying to work out the upper bound of the window, as we * don't reliably know the RLC channel state variables... */ -static void graph_get_bounds(struct graph *g) +static void graph_get_bounds(struct gtk_rlc_graph *g) { - struct segment *tmp; + struct rlc_segment *tmp; double tim; gboolean data_frame_seen = FALSE; double data_tim_low = 0; @@ -2684,7 +2379,7 @@ static void graph_get_bounds(struct graph *g) guint32 ack_seq_high = 0; /* Go through all segments to determine "bounds" */ - for (tmp=g->segments; tmp; tmp=tmp->next) { + for (tmp=g->graph.segments; tmp; tmp=tmp->next) { if (!tmp->isControlPDU) { /* DATA frame */ @@ -2753,7 +2448,7 @@ static void graph_get_bounds(struct graph *g) g->zoom.y = (g->geom.height -1) / g->bounds.height; } -static void graph_read_config(struct graph *g) +static void graph_read_config(struct gtk_rlc_graph *g) { /* Black for PDUs */ g->style.seq_color.red = (double)0 / 65535.0; @@ -2791,12 +2486,12 @@ static void graph_read_config(struct graph *g) g->x_axis->label[1] = NULL; } -static void rlc_lte_make_elmtlist(struct graph *g) +static void rlc_lte_make_elmtlist(struct gtk_rlc_graph *g) { - struct segment *tmp; + struct rlc_segment *tmp; struct element *elements0, *e0; /* list of elmts showing control */ struct element *elements1, *e1; /* list of elmts showing data */ - struct segment *last_status_segment = NULL; + struct rlc_segment *last_status_segment = NULL; double xx0, yy0; gboolean ack_seen = FALSE; guint32 seq_base; @@ -2808,7 +2503,7 @@ static void rlc_lte_make_elmtlist(struct graph *g) debug(DBS_FENTRY) puts("rlc_lte_make_elmtlist()"); /* Allocate all needed elements up-front */ - if (g->elists->elements == NULL) { + if (g->graph.elists->elements == NULL) { get_data_control_counts(g, &data, &acks, &nacks); /* Allocate elements for data */ @@ -2820,18 +2515,18 @@ static void rlc_lte_make_elmtlist(struct graph *g) e1 = elements1 = (struct element *)g_malloc(n*sizeof(struct element)); /* Allocate container for 2nd list of elements */ - g->elists->next = (struct element_list *)g_malloc0(sizeof(struct element_list)); + g->graph.elists->next = (struct element_list *)g_malloc0(sizeof(struct element_list)); } else { - e0 = elements0 = g->elists->elements; - e1 = elements1 = g->elists->next->elements; + e0 = elements0 = g->graph.elists->elements; + e1 = elements1 = g->graph.elists->next->elements; } xx0 = g->bounds.x0; yy0 = g->bounds.y0; seq_base = (guint32) yy0; - for (tmp=g->segments; tmp; tmp=tmp->next) { + for (tmp=g->graph.segments; tmp; tmp=tmp->next) { double secs; double x, y; @@ -2992,8 +2687,8 @@ static void rlc_lte_make_elmtlist(struct graph *g) /* Complete both element lists */ e0->type = ELMT_NONE; e1->type = ELMT_NONE; - g->elists->elements = elements0; - g->elists->next->elements = elements1; + g->graph.elists->elements = elements0; + g->graph.elists->next->elements = elements1; } #if defined(_WIN32) && !defined(__MINGW32__) && (_MSC_VER < 1800) diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt index d214b4826f..376ad38b87 100644 --- a/ui/qt/CMakeLists.txt +++ b/ui/qt/CMakeLists.txt @@ -81,6 +81,7 @@ set(WIRESHARK_QT_HEADERS lbm_lbtru_transport_dialog.h lbm_stream_dialog.h lbm_uimflow_dialog.h + lte_rlc_graph_dialog.h lte_mac_statistics_dialog.h lte_rlc_statistics_dialog.h main_status_bar.h @@ -224,6 +225,7 @@ set(WIRESHARK_QT_SRC lbm_lbtru_transport_dialog.cpp lbm_stream_dialog.cpp lbm_uimflow_dialog.cpp + lte_rlc_graph_dialog.cpp lte_mac_statistics_dialog.cpp lte_rlc_statistics_dialog.cpp main_status_bar.cpp @@ -365,6 +367,7 @@ set(WIRESHARK_QT_UI lbm_lbtru_transport_dialog.ui lbm_stream_dialog.ui lbm_uimflow_dialog.ui + lte_rlc_graph_dialog.ui main_welcome.ui main_window.ui main_window_preferences_frame.ui diff --git a/ui/qt/Makefile.am b/ui/qt/Makefile.am index 13bd0d2db7..fb7f2f592f 100644 --- a/ui/qt/Makefile.am +++ b/ui/qt/Makefile.am @@ -202,6 +202,8 @@ lbm_stream_dialog.$(OBJEXT): ui_lbm_stream_dialog.h lbm_uimflow_dialog.$(OBJEXT): ui_lbm_uimflow_dialog.h +lte_rlc_graph_dialog.$(OBJEXT): ui_lte_rlc_graph_dialog.h + main_welcome.$(OBJEXT): ui_main_welcome.h main_window.$(OBJEXT): ui_main_window.h diff --git a/ui/qt/Makefile.common b/ui/qt/Makefile.common index 1af30ecbfb..409dca819c 100644 --- a/ui/qt/Makefile.common +++ b/ui/qt/Makefile.common @@ -66,6 +66,7 @@ NODIST_GENERATED_HEADER_FILES = \ ui_lbm_lbtru_transport_dialog.h \ ui_lbm_stream_dialog.h \ ui_lbm_uimflow_dialog.h \ + ui_lte_rlc_graph_dialog.h \ ui_main_welcome.h \ ui_main_window.h \ ui_main_window_preferences_frame.h \ @@ -202,6 +203,7 @@ MOC_HDRS = \ lbm_stream_dialog.h \ lbm_uimflow_dialog.h \ lte_mac_statistics_dialog.h \ + lte_rlc_graph_dialog.h \ lte_rlc_statistics_dialog.h \ main_status_bar.h \ main_welcome.h \ @@ -310,6 +312,7 @@ UI_FILES = \ lbm_lbtru_transport_dialog.ui \ lbm_stream_dialog.ui \ lbm_uimflow_dialog.ui \ + lte_rlc_graph_dialog.ui \ main_welcome.ui \ main_window.ui \ main_window_preferences_frame.ui \ @@ -456,6 +459,7 @@ WIRESHARK_QT_SRC = \ lbm_stream_dialog.cpp \ lbm_uimflow_dialog.cpp \ lte_mac_statistics_dialog.cpp \ + lte_rlc_graph_dialog.cpp \ lte_rlc_statistics_dialog.cpp \ main_status_bar.cpp \ main_welcome.cpp \ diff --git a/ui/qt/Wireshark.pro b/ui/qt/Wireshark.pro index a056d5b6b9..a2361c9124 100644 --- a/ui/qt/Wireshark.pro +++ b/ui/qt/Wireshark.pro @@ -245,6 +245,7 @@ FORMS += \ lbm_lbtru_transport_dialog.ui \ lbm_stream_dialog.ui \ lbm_uimflow_dialog.ui \ + lte_rlc_graph_dialog.ui \ main_welcome.ui \ main_window.ui \ main_window_preferences_frame.ui \ @@ -329,6 +330,7 @@ HEADERS += $$HEADERS_WS_C \ lbm_stream_dialog.h \ lbm_uimflow_dialog.h \ lte_mac_statistics_dialog.h \ + lte_rlc_graph_dialog.h \ lte_rlc_statistics_dialog.h \ main_window_preferences_frame.h \ manage_interfaces_dialog.h \ @@ -722,6 +724,7 @@ SOURCES += \ lbm_stream_dialog.cpp \ lbm_uimflow_dialog.cpp \ lte_mac_statistics_dialog.cpp \ + lte_rlc_graph_dialog.cpp \ lte_rlc_statistics_dialog.cpp \ main_status_bar.cpp \ main_welcome.cpp \ diff --git a/ui/qt/lte_rlc_graph_dialog.cpp b/ui/qt/lte_rlc_graph_dialog.cpp new file mode 100644 index 0000000000..8b60df7e22 --- /dev/null +++ b/ui/qt/lte_rlc_graph_dialog.cpp @@ -0,0 +1,643 @@ +/* lte_rlc_graph_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "lte_rlc_graph_dialog.h" +#include <ui_lte_rlc_graph_dialog.h> + +#include <epan/epan.h> +#include <epan/epan_dissect.h> +#include <epan/tap.h> +#include <epan/stat_tap_ui.h> + +#include <epan/tvbuff-int.h> +#include <epan/tvbuff.h> +#include <frame_tvbuff.h> + +#include "tango_colors.h" + +#include <QMenu> +#include <QRubberBand> + +#include "qt_ui_utils.h" +#include "wireshark_application.h" +#include "simple_dialog.h" + +#include "globals.h" + +#include <epan/dissectors/packet-rlc-lte.h> + +#include <ui/tap-rlc-graph.h> + +// TODO: +// - better handling of zooming (select area like TCP and/or Jim's patch for 1 dimension at a time) +// - get launched from RLC stats for a pre-known channel +// - how to avoid panning or zooming out to -ve (x or y axis) +// - goto packet functionality when click on segments + +const QRgb graph_color_seq = 0x000000; // Black. +const QRgb graph_color_ack = tango_sky_blue_4; // Blue for ACK lines +const QRgb graph_color_nack = tango_scarlet_red_3; // Red for NACKs +const QRgb graph_color_resegmented = 0x888888; // Grey for resegmentations + +// Size of selectable packet points in the base graph +const double pkt_point_size_ = 3.0; + + +// Constructor. +LteRlcGraphDialog::LteRlcGraphDialog(QWidget &parent, CaptureFile &cf) : + WiresharkDialog(parent, cf), + ui(new Ui::LteRlcGraphDialog), + mouse_drags_(true), + rubber_band_(NULL) +{ + ui->setupUi(this); + + // XXX Use recent settings instead + resize(parent.width() * 4 / 5, parent.height() * 3 / 4); + + QCustomPlot *rp = ui->rlcPlot; + rp->xAxis->setLabel(tr("Time")); + rp->yAxis->setLabel(tr("Sequence Number")); + + // TODO: don't want all of these... + ctx_menu_ = new QMenu(this); + ctx_menu_->addAction(ui->actionZoomIn); + ctx_menu_->addAction(ui->actionZoomOut); + ctx_menu_->addAction(ui->actionReset); + ctx_menu_->addSeparator(); + ctx_menu_->addAction(ui->actionMoveRight10); + ctx_menu_->addAction(ui->actionMoveLeft10); + ctx_menu_->addAction(ui->actionMoveUp10); + ctx_menu_->addAction(ui->actionMoveUp100); + ctx_menu_->addAction(ui->actionMoveDown10); + ctx_menu_->addAction(ui->actionMoveDown100); + ctx_menu_->addAction(ui->actionMoveRight1); + ctx_menu_->addAction(ui->actionMoveLeft1); + ctx_menu_->addAction(ui->actionMoveUp1); + ctx_menu_->addAction(ui->actionMoveDown1); +// ctx_menu_.addSeparator(); +// ctx_menu_->addAction(ui->actionGoToPacket); + ctx_menu_->addSeparator(); + ctx_menu_->addAction(ui->actionDragZoom); +// ctx_menu_->addAction(ui->actionToggleTimeOrigin); + ctx_menu_->addAction(ui->actionCrosshairs); + + // Zero out this struct. + memset(&graph_, 0, sizeof(graph_)); + + // If no channel chosen already, try to use currently selected frame. + findChannel(); + + // Set window title here. + if (graph_.channelSet) { + QString dlg_title = tr("LTE RLC Graph (UE=%1 chan=%2%3 %4 - %5)") + .arg(graph_.ueid) + .arg((graph_.channelType == CHANNEL_TYPE_SRB) ? "SRB" : "DRB") + .arg(graph_.channelId) + .arg((graph_.direction == DIRECTION_UPLINK) ? "UL" : "DL") + .arg((graph_.rlcMode == RLC_UM_MODE) ? "UM" : "AM"); + setWindowTitle(dlg_title); + } + else { + setWindowTitle(tr("LTE RLC Graph - no channel selected")); + } + + // Set colours/styles for each of the traces on the graph. + QCustomPlot *sp = ui->rlcPlot; + base_graph_ = sp->addGraph(); // All: Selectable segments + base_graph_->setPen(QPen(QBrush(graph_color_seq), 0.25)); + + reseg_graph_ = sp->addGraph(); + reseg_graph_->setPen(QPen(QBrush(graph_color_resegmented), 0.25)); + + acks_graph_ = sp->addGraph(); + acks_graph_->setPen(QPen(QBrush(graph_color_ack), 1.0)); + + nacks_graph_ = sp->addGraph(); + nacks_graph_->setPen(QPen(QBrush(graph_color_nack), 0.25)); + + connect(rp, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(graphClicked(QMouseEvent*))); + connect(rp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*))); + connect(rp, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(mouseReleased(QMouseEvent*))); + + // Extract the data that the graph can use. + fillGraph(); +} + +// Destructor +LteRlcGraphDialog::~LteRlcGraphDialog() +{ + delete ui; +} + +// See if the given segment matches the channel this graph is plotting. +bool LteRlcGraphDialog::compareHeaders(rlc_segment *seg) +{ + return compare_rlc_headers(graph_.ueid, graph_.channelType, + graph_.channelId, graph_.rlcMode, graph_.direction, + seg->ueid, seg->channelType, + seg->channelId, seg->rlcMode, seg->direction, + seg->isControlPDU); +} + +// Look for channel to plot based upon currently selected frame. +void LteRlcGraphDialog::findChannel() +{ + char *err_string = NULL; + gboolean free_err_string = FALSE; + rlc_graph_segment_list_free(&graph_); + if (!rlc_graph_segment_list_get(cap_file_.capFile(), &graph_, graph_.channelSet, + &err_string, &free_err_string)) { + // Pop up an error box to report error. + simple_error_message_box("%s", err_string); + if (free_err_string) { + g_free(err_string); + } + } +} + +// Fill in graph data based upon what was read into the rlc_graph struct. +void LteRlcGraphDialog::fillGraph() +{ + QCustomPlot *sp = ui->rlcPlot; + + // We should always have 4 graphs, but cover case if no channel was chosen. + if (sp->graphCount() < 1) { + return; + } + + base_graph_->setLineStyle(QCPGraph::lsNone); // dot + reseg_graph_->setLineStyle(QCPGraph::lsNone); // dot + acks_graph_->setLineStyle(QCPGraph::lsStepLeft); // to get step effect... + nacks_graph_->setLineStyle(QCPGraph::lsNone); // dot, but bigger. + + // Will show all graphs with data we find. + for (int i = 0; i < sp->graphCount(); i++) { + sp->graph(i)->clearData(); + sp->graph(i)->setVisible(true); + } + + // NACKs are shown bigger than others. + base_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_)); + reseg_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_)); + acks_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_)); + nacks_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_*2)); + + + ts_offset_ = 0; + seq_offset_ = 0; // TODO: needed? + bool first = true; + + // Map timestamps -> segments in first pass. + time_stamp_map_.clear(); + for (struct rlc_segment *seg = graph_.segments; seg != NULL; seg = seg->next) { + if (!compareHeaders(seg)) { + continue; + } + double ts = seg->rel_secs + seg->rel_usecs / 1000000.0; + if (first) { + // Take note of first sequence number seen. + if (seq_origin_zero_) { + seq_offset_ = seg->SN; + } + first = false; + } + time_stamp_map_.insertMulti(ts - ts_offset_, seg); + } + + // Now sequence numbers. + QVector<double> seq_time, seq, + reseg_seq_time, reseg_seq, + acks_time, acks, + nacks_time, nacks; + for (struct rlc_segment *seg = graph_.segments; seg != NULL; seg = seg->next) { + double ts = (seg->rel_secs + seg->rel_usecs / 1000000.0) - ts_offset_; + if (compareHeaders(seg)) { + if (!seg->isControlPDU) { + // Data + if (seg->isResegmented) { + reseg_seq_time.append(ts); + reseg_seq.append(seg->SN - seq_offset_); + } + else { + seq_time.append(ts); + seq.append(seg->SN - seq_offset_); + } + } + else { + // Status (ACKs/NACKs) + acks_time.append(ts); + acks.append(seg->ACKNo-1); + for (int n=0; n < seg->noOfNACKs; n++) { + nacks_time.append(ts); + nacks.append(seg->NACKs[n]); + } + } + } + } + + // Add the data from the graphs. + base_graph_->setData(seq_time, seq); + reseg_graph_->setData(reseg_seq_time, reseg_seq); + acks_graph_->setData(acks_time, acks); + nacks_graph_->setData(nacks_time, nacks); + + sp->setEnabled(true); + + // Auto-size... + mouseMoved(NULL); + resetAxes(); + + // XXX QCustomPlot doesn't seem to draw any sort of focus indicator. + sp->setFocus(); +} + +// Copied from TCP graphs, seems like a kludge to get the graph resized immediately after it is built... +void LteRlcGraphDialog::showEvent(QShowEvent *) +{ + resetAxes(); +} + +// Respond to a key press. +void LteRlcGraphDialog::keyPressEvent(QKeyEvent *event) +{ + int pan_pixels = event->modifiers() & Qt::ShiftModifier ? 1 : 10; + + switch(event->key()) { + case Qt::Key_Minus: + case Qt::Key_Underscore: // Shifted minus on U.S. keyboards + case Qt::Key_O: // GTK+ + case Qt::Key_R: + zoomAxes(false); + break; + case Qt::Key_Plus: + case Qt::Key_Equal: // Unshifted plus on U.S. keyboards + case Qt::Key_I: // GTK+ + zoomAxes(true); + break; + + case Qt::Key_Right: + case Qt::Key_L: + panAxes(pan_pixels, 0); + break; + case Qt::Key_Left: + case Qt::Key_H: + panAxes(-1 * pan_pixels, 0); + break; + case Qt::Key_Up: + case Qt::Key_K: + panAxes(0, pan_pixels); + break; + case Qt::Key_Down: + case Qt::Key_J: + panAxes(0, -1 * pan_pixels); + break; + + case Qt::Key_PageUp: + panAxes(0, 20 * pan_pixels); + break; + case Qt::Key_PageDown: + panAxes(0, -20 * pan_pixels); + break; + + case Qt::Key_Space: +// toggleTracerStyle(); + break; + + case Qt::Key_0: + case Qt::Key_ParenRight: // Shifted 0 on U.S. keyboards + case Qt::Key_Home: + resetAxes(); + break; + + case Qt::Key_G: +// on_actionGoToPacket_triggered(); + break; + case Qt::Key_T: +// on_actionToggleTimeOrigin_triggered(); + break; + case Qt::Key_Z: + on_actionDragZoom_triggered(); + break; + } + + WiresharkDialog::keyPressEvent(event); +} + +void LteRlcGraphDialog::zoomAxes(bool in) +{ + QCustomPlot *rp = ui->rlcPlot; + double h_factor = rp->axisRect()->rangeZoomFactor(Qt::Horizontal); + double v_factor = rp->axisRect()->rangeZoomFactor(Qt::Vertical); + + if (!in) { + h_factor = pow(h_factor, -1); + v_factor = pow(v_factor, -1); + } + + rp->xAxis->scaleRange(h_factor, rp->xAxis->range().center()); + rp->yAxis->scaleRange(v_factor, rp->yAxis->range().center()); + rp->replot(); +} + +void LteRlcGraphDialog::panAxes(int x_pixels, int y_pixels) +{ + QCustomPlot *rp = ui->rlcPlot; + double h_pan = 0.0; + double v_pan = 0.0; + + h_pan = rp->xAxis->range().size() * x_pixels / rp->xAxis->axisRect()->width(); + v_pan = rp->yAxis->range().size() * y_pixels / rp->yAxis->axisRect()->height(); + // The GTK+ version won't pan unless we're zoomed. Should we do the same here? + if (h_pan) { + rp->xAxis->moveRange(h_pan); + rp->replot(); + } + if (v_pan) { + rp->yAxis->moveRange(v_pan); + rp->replot(); + } +} + +// Don't accidentally zoom into a 1x1 rect if you happen to click on the graph +// in zoom mode. +const int min_zoom_pixels_ = 20; +QRectF LteRlcGraphDialog::getZoomRanges(QRect zoom_rect) +{ + QRectF zoom_ranges = QRectF(); + + if (zoom_rect.width() < min_zoom_pixels_ && zoom_rect.height() < min_zoom_pixels_) { + return zoom_ranges; + } + + QCustomPlot *rp = ui->rlcPlot; + QRect zr = zoom_rect.normalized(); + QRect ar = rp->axisRect()->rect(); + if (ar.intersects(zr)) { + QRect zsr = ar.intersected(zr); + zoom_ranges.setX(rp->xAxis->range().lower + + rp->xAxis->range().size() * (zsr.left() - ar.left()) / ar.width()); + zoom_ranges.setWidth(rp->xAxis->range().size() * zsr.width() / ar.width()); + + // QRects grow down + zoom_ranges.setY(rp->yAxis->range().lower + + rp->yAxis->range().size() * (ar.bottom() - zsr.bottom()) / ar.height()); + zoom_ranges.setHeight(rp->yAxis->range().size() * zsr.height() / ar.height()); + } + return zoom_ranges; +} + +void LteRlcGraphDialog::graphClicked(QMouseEvent *event) +{ + QCustomPlot *rp = ui->rlcPlot; + + if (event->button() == Qt::RightButton) { + // XXX We should find some way to get rlcPlot to handle a + // contextMenuEvent instead. + ctx_menu_->exec(event->globalPos()); + } else if (mouse_drags_) { + if (rp->axisRect()->rect().contains(event->pos())) { + rp->setCursor(QCursor(Qt::ClosedHandCursor)); + } +// on_actionGoToPacket_triggered(); + } else { + if (!rubber_band_) { + rubber_band_ = new QRubberBand(QRubberBand::Rectangle, rp); + } + rb_origin_ = event->pos(); + rubber_band_->setGeometry(QRect(rb_origin_, QSize())); + rubber_band_->show(); + } + rp->setFocus(); +} + +void LteRlcGraphDialog::mouseMoved(QMouseEvent *event) +{ + QCustomPlot *rp = ui->rlcPlot; + QString hint; + Qt::CursorShape shape = Qt::ArrowCursor; + + if (event) { + if (event->buttons().testFlag(Qt::LeftButton)) { + if (mouse_drags_) { + shape = Qt::ClosedHandCursor; + } else { + shape = Qt::CrossCursor; + } + } else if (rp->axisRect()->rect().contains(event->pos())) { + if (mouse_drags_) { + shape = Qt::OpenHandCursor; + } else { + shape = Qt::CrossCursor; + } + } + rp->setCursor(QCursor(shape)); + } + + if (mouse_drags_) { +// double ts = 0; +// packet_num_ = 0; +// int interval_packet = -1; + +// if (event && tracer_->graph()) { +// tracer_->setGraphKey(rp->xAxis->pixelToCoord(event->pos().x())); +// ts = tracer_->position->key(); + +// QTreeWidgetItem *ti = ui->graphTreeWidget->topLevelItem(0); +// IOGraph *iog = NULL; +// if (ti) { +// iog = ti->data(name_col_, Qt::UserRole).value<IOGraph *>(); +// interval_packet = iog->packetFromTime(ts); +// } +// } + +// if (interval_packet < 0) { +// hint += tr("Hover over the graph for details."); +// } else { +// QString msg = tr("No packets in interval"); +// QString val; +// if (interval_packet > 0) { +// packet_num_ = (guint32) interval_packet; +// msg = tr("%1 %2") +// .arg(!file_closed_ ? tr("Click to select packet") : tr("Packet")) +// .arg(packet_num_); +// val = " = " + QString::number(tracer_->position->value(), 'g', 4); +// } +// hint += tr("%1 (%2s%3).") +// .arg(msg) +// .arg(QString::number(ts, 'g', 4)) +// .arg(val); +// } + rp->replot(); + } else { + if (event && rubber_band_ && rubber_band_->isVisible()) { + rubber_band_->setGeometry(QRect(rb_origin_, event->pos()).normalized()); + QRectF zoom_ranges = getZoomRanges(QRect(rb_origin_, event->pos())); + if (zoom_ranges.width() > 0.0 && zoom_ranges.height() > 0.0) { + hint += tr("Release to zoom, x = %1 to %2, y = %3 to %4") + .arg(zoom_ranges.x()) + .arg(zoom_ranges.x() + zoom_ranges.width()) + .arg(zoom_ranges.y()) + .arg(zoom_ranges.y() + zoom_ranges.height()); + } else { + hint += tr("Unable to select range."); + } + } else { + hint += tr("Click to select a portion of the graph."); + } + } + + hint.prepend("<small><i>"); + hint.append("</i></small>"); + ui->hintLabel->setText(hint); +} + +void LteRlcGraphDialog::mouseReleased(QMouseEvent *event) +{ + QCustomPlot *rp = ui->rlcPlot; + if (rubber_band_) { + rubber_band_->hide(); + if (!mouse_drags_) { + QRectF zoom_ranges = getZoomRanges(QRect(rb_origin_, event->pos())); + if (zoom_ranges.width() > 0.0 && zoom_ranges.height() > 0.0) { + rp->xAxis->setRangeLower(zoom_ranges.x()); + rp->xAxis->setRangeUpper(zoom_ranges.x() + zoom_ranges.width()); + rp->yAxis->setRangeLower(zoom_ranges.y()); + rp->yAxis->setRangeUpper(zoom_ranges.y() + zoom_ranges.height()); + rp->replot(); + } + } + } else if (rp->cursor().shape() == Qt::ClosedHandCursor) { + rp->setCursor(QCursor(Qt::OpenHandCursor)); + } +} + +void LteRlcGraphDialog::resetAxes() +{ + QCustomPlot *rp = ui->rlcPlot; + + QCPRange x_range = rp->xAxis->scaleType() == QCPAxis::stLogarithmic ? + rp->xAxis->range().sanitizedForLogScale() : rp->xAxis->range(); + + double pixel_pad = 10.0; // per side + + rp->rescaleAxes(true); + base_graph_->rescaleValueAxis(false, true); + + double axis_pixels = rp->xAxis->axisRect()->width(); + rp->xAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, x_range.center()); + + axis_pixels = rp->yAxis->axisRect()->height(); + rp->yAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, rp->yAxis->range().center()); + + rp->replot(); +} + +void LteRlcGraphDialog::on_actionReset_triggered() +{ + resetAxes(); +} + +void LteRlcGraphDialog::on_actionZoomIn_triggered() +{ + zoomAxes(true); +} + +void LteRlcGraphDialog::on_actionZoomOut_triggered() +{ + zoomAxes(false); +} + +void LteRlcGraphDialog::on_actionMoveUp10_triggered() +{ + panAxes(0, 10); +} + +void LteRlcGraphDialog::on_actionMoveUp100_triggered() +{ + panAxes(0, 100); +} + +void LteRlcGraphDialog::on_actionMoveLeft10_triggered() +{ + panAxes(-10, 0); +} + +void LteRlcGraphDialog::on_actionMoveRight10_triggered() +{ + panAxes(10, 0); +} + +void LteRlcGraphDialog::on_actionMoveDown10_triggered() +{ + panAxes(0, -10); +} + +void LteRlcGraphDialog::on_actionMoveDown100_triggered() +{ + panAxes(0, -100); +} + +void LteRlcGraphDialog::on_actionMoveUp1_triggered() +{ + panAxes(0, 1); +} + +void LteRlcGraphDialog::on_actionMoveLeft1_triggered() +{ + panAxes(-1, 0); +} + +void LteRlcGraphDialog::on_actionMoveRight1_triggered() +{ + panAxes(1, 0); +} + +void LteRlcGraphDialog::on_actionMoveDown1_triggered() +{ + panAxes(0, -1); +} + +void LteRlcGraphDialog::on_actionDragZoom_triggered() +{ +// if (mouse_drags_) { +// ui->zoomRadioButton->toggle(); +// } else { +// ui->dragRadioButton->toggle(); +// } +} + + +// No need to register tap listeners here. This is done +// in calls to the common functions in ui/tap-rlc-graph.c + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/qt/lte_rlc_graph_dialog.h b/ui/qt/lte_rlc_graph_dialog.h new file mode 100644 index 0000000000..eaadfc4b85 --- /dev/null +++ b/ui/qt/lte_rlc_graph_dialog.h @@ -0,0 +1,118 @@ +/* lte_rlc_graph_dialog.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef LTE_RLC_GRAPH_DIALOG_H +#define LTE_RLC_GRAPH_DIALOG_H + +#include "wireshark_dialog.h" +#include <ui/tap-rlc-graph.h> + +#include "qcustomplot.h" + +class QMenu; +class QRubberBand; + +namespace Ui { +class LteRlcGraphDialog; +} + +class LteRlcGraphDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + // TODO: will need to add another constructor option to give channel explicitly, + // rather than find in currently selected packet, for when launch graph from + // RLC statistics dialog. + explicit LteRlcGraphDialog(QWidget &parent, CaptureFile &cf); + ~LteRlcGraphDialog(); + +protected: + void showEvent(QShowEvent *event); + void keyPressEvent(QKeyEvent *event); + +private: + Ui::LteRlcGraphDialog *ui; + bool mouse_drags_; + QRubberBand *rubber_band_; + QPoint rb_origin_; + QMenu *ctx_menu_; + + // Data gleaned directly from tapping packets (shared with gtk impl) + struct rlc_graph graph_; + + // Data + QMap<double, struct rlc_segment *> time_stamp_map_; + double ts_offset_; + QMap<double, struct rlc_segment *> sequence_num_map_; + double seq_offset_; + bool seq_origin_zero_; + + QCPGraph *base_graph_; // Clickable packets + QCPGraph *reseg_graph_; + QCPGraph *acks_graph_; + QCPGraph *nacks_graph_; + + bool compareHeaders(rlc_segment *seg); + + void findChannel(); + void fillGraph(); + + void zoomAxes(bool in); + void panAxes(int x_pixels, int y_pixels); + QRectF getZoomRanges(QRect zoom_rect); + +private slots: + void graphClicked(QMouseEvent *event); + void mouseMoved(QMouseEvent *event); + void mouseReleased(QMouseEvent *event); + void resetAxes(); + + void on_actionReset_triggered(); + void on_actionZoomIn_triggered(); + void on_actionZoomOut_triggered(); + void on_actionMoveUp10_triggered(); + void on_actionMoveLeft10_triggered(); + void on_actionMoveRight10_triggered(); + void on_actionMoveDown10_triggered(); + void on_actionMoveUp1_triggered(); + void on_actionMoveLeft1_triggered(); + void on_actionMoveRight1_triggered(); + void on_actionMoveDown1_triggered(); + void on_actionDragZoom_triggered(); + void on_actionMoveUp100_triggered(); + void on_actionMoveDown100_triggered(); +}; + +#endif // LTE_RLC_GRAPH_DIALOG_H + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/qt/lte_rlc_graph_dialog.ui b/ui/qt/lte_rlc_graph_dialog.ui new file mode 100644 index 0000000000..827a1bb62b --- /dev/null +++ b/ui/qt/lte_rlc_graph_dialog.ui @@ -0,0 +1,275 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>LteRlcGraphDialog</class> + <widget class="QDialog" name="LteRlcGraphDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>660</width> + <height>447</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout" stretch="1,0,0"> + <item> + <widget class="QCustomPlot" name="rlcPlot" native="true"/> + </item> + <item> + <widget class="QLabel" name="hintLabel"> + <property name="toolTip"> + <string><html><head/><body> + +<h3>Valuable and amazing time-saving keyboard shortcuts</h3> +<table><tbody> + +<tr><th>+</th><td>Zoom in</td></th> +<tr><th>-</th><td>Zoom out</td></th> +<tr><th>0</th><td>Reset graph to its initial state</td></th> + +<tr><th>→</th><td>Move right 10 pixels</td></th> +<tr><th>←</th><td>Move left 10 pixels</td></th> +<tr><th>↑</th><td>Move up 10 pixels</td></th> +<tr><th>↓</th><td>Move down 10 pixels</td></th> +<tr><th><i>Shift+</i>→</th><td>Move right 1 pixel</td></th> +<tr><th><i>Shift+</i>←</th><td>Move left 1 pixel</td></th> +<tr><th><i>Shift+</i>↑</th><td>Move up 1 pixel</td></th> +<tr><th><i>Shift+</i>↓</th><td>Move down 1 pixel</td></th> + +<tr><th>g</th><td>Go to packet under cursor</td></th> + +<tr><th>z</th><td>Toggle mouse drag / zoom</td></th> +<tr><th>t</th><td>Toggle capture / session time origin</td></th> +<tr><th>Space</th><td>Toggle crosshairs</td></th> + +</tbody></table> +</body></html></string> + </property> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Save</set> + </property> + </widget> + </item> + </layout> + <action name="actionReset"> + <property name="text"> + <string>Reset Graph</string> + </property> + <property name="toolTip"> + <string>Reset the graph to its initial state.</string> + </property> + <property name="shortcut"> + <string>0</string> + </property> + </action> + <action name="actionZoomIn"> + <property name="text"> + <string>Zoom In</string> + </property> + <property name="toolTip"> + <string>Zoom In</string> + </property> + <property name="shortcut"> + <string>+</string> + </property> + </action> + <action name="actionZoomOut"> + <property name="text"> + <string>Zoom Out</string> + </property> + <property name="toolTip"> + <string>Zoom Out</string> + </property> + <property name="shortcut"> + <string>-</string> + </property> + </action> + <action name="actionMoveUp10"> + <property name="text"> + <string>Move Up 10 Pixels</string> + </property> + <property name="toolTip"> + <string>Move Up 10 Pixels</string> + </property> + <property name="shortcut"> + <string>Up</string> + </property> + </action> + <action name="actionMoveLeft10"> + <property name="text"> + <string>Move Left 10 Pixels</string> + </property> + <property name="toolTip"> + <string>Move Left 10 Pixels</string> + </property> + <property name="shortcut"> + <string>Left</string> + </property> + </action> + <action name="actionMoveRight10"> + <property name="text"> + <string>Move Right 10 Pixels</string> + </property> + <property name="toolTip"> + <string>Move Right 10 Pixels</string> + </property> + <property name="shortcut"> + <string>Right</string> + </property> + </action> + <action name="actionMoveDown10"> + <property name="text"> + <string>Move Down 10 Pixels</string> + </property> + <property name="toolTip"> + <string>Move Down 10 Pixels</string> + </property> + <property name="shortcut"> + <string>Down</string> + </property> + </action> + <action name="actionMoveUp1"> + <property name="text"> + <string>Move Up 1 Pixel</string> + </property> + <property name="toolTip"> + <string>Move Up 1 Pixel</string> + </property> + <property name="shortcut"> + <string>Shift+Up</string> + </property> + </action> + <action name="actionMoveLeft1"> + <property name="text"> + <string>Move Left 1 Pixel</string> + </property> + <property name="toolTip"> + <string>Move Left 1 Pixel</string> + </property> + <property name="shortcut"> + <string>Shift+Left</string> + </property> + </action> + <action name="actionMoveRight1"> + <property name="text"> + <string>Move Right 1 Pixel</string> + </property> + <property name="toolTip"> + <string>Move Right 1 Pixel</string> + </property> + <property name="shortcut"> + <string>Shift+Right</string> + </property> + </action> + <action name="actionMoveDown1"> + <property name="text"> + <string>Move Down 1 Pixel</string> + </property> + <property name="toolTip"> + <string>Move down 1 Pixel</string> + </property> + <property name="shortcut"> + <string>Shift+Down</string> + </property> + </action> + <action name="actionDragZoom"> + <property name="text"> + <string>Drag / Zoom</string> + </property> + <property name="toolTip"> + <string>Toggle mouse drag / zoom behavior</string> + </property> + <property name="shortcut"> + <string>Z</string> + </property> + </action> + <action name="actionCrosshairs"> + <property name="text"> + <string>Crosshairs</string> + </property> + <property name="toolTip"> + <string>Toggle crosshairs</string> + </property> + <property name="shortcut"> + <string>Space</string> + </property> + </action> + <action name="actionMoveUp100"> + <property name="text"> + <string>Move Up 100 Pixels</string> + </property> + <property name="toolTip"> + <string>Move Up 100 Pixels</string> + </property> + <property name="shortcut"> + <string>PgUp</string> + </property> + </action> + <action name="actionMoveDown100"> + <property name="text"> + <string>Move Up 100 Pixels</string> + </property> + <property name="toolTip"> + <string>Move Up 100 Pixels</string> + </property> + <property name="shortcut"> + <string>PgDown</string> + </property> + </action> + </widget> + <customwidgets> + <customwidget> + <class>QCustomPlot</class> + <extends>QWidget</extends> + <header>qcustomplot.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>LteRlcGraphDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>LteRlcGraphDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/ui/qt/main_window.cpp b/ui/qt/main_window.cpp index b053c0c1cf..7c2d2c05bd 100644 --- a/ui/qt/main_window.cpp +++ b/ui/qt/main_window.cpp @@ -2156,6 +2156,7 @@ void MainWindow::addDynamicMenus() wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary); wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteMacStatistics); wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcStatistics); + wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcGraph); wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary); // Fill in each menu diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h index 8f25f1e334..dd883ffcac 100644 --- a/ui/qt/main_window.h +++ b/ui/qt/main_window.h @@ -540,6 +540,7 @@ private slots: void on_actionTelephonyLteRlcStatistics_triggered(); void statCommandLteRlcStatistics(const char *arg, void *); void on_actionTelephonyLteMacStatistics_triggered(); + void on_actionTelephonyLteRlcGraph_triggered(); void on_actionTelephonyIax2StreamAnalysis_triggered(); void on_actionTelephonyISUPMessages_triggered(); void on_actionTelephonyMtp3Summary_triggered(); diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui index 72fc47ea43..b9baa1d738 100644 --- a/ui/qt/main_window.ui +++ b/ui/qt/main_window.ui @@ -2449,7 +2449,16 @@ <property name="toolTip"> <string>LTE RLC statistics</string> </property> - </action> <action name="actionTelephonyMtp3Summary"> + </action> + <action name="actionTelephonyLteRlcGraph"> + <property name="text"> + <string>RLC Graph</string> + </property> + <property name="toolTip"> + <string>LTE RLC graph</string> + </property> + </action> + <action name="actionTelephonyMtp3Summary"> <property name="text"> <string>MTP3 Summary</string> </property> diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp index 2579f02169..395df9faf7 100644 --- a/ui/qt/main_window_slots.cpp +++ b/ui/qt/main_window_slots.cpp @@ -114,6 +114,7 @@ #include "lbm_lbtru_transport_dialog.h" #include "lte_mac_statistics_dialog.h" #include "lte_rlc_statistics_dialog.h" +#include "lte_rlc_graph_dialog.h" #include "mtp3_summary_dialog.h" #include "multicast_statistics_dialog.h" #include "packet_comment_dialog.h" @@ -3025,6 +3026,12 @@ void MainWindow::on_actionTelephonyLteRlcStatistics_triggered() statCommandLteRlcStatistics(NULL, NULL); } +void MainWindow::on_actionTelephonyLteRlcGraph_triggered() +{ + LteRlcGraphDialog *lrg_dialog = new LteRlcGraphDialog(*this, capture_file_); + lrg_dialog->show(); +} + void MainWindow::on_actionTelephonyMtp3Summary_triggered() { Mtp3SummaryDialog *mtp3s_dialog = new Mtp3SummaryDialog(*this, capture_file_); diff --git a/ui/tap-rlc-graph.c b/ui/tap-rlc-graph.c new file mode 100644 index 0000000000..f5945a7b56 --- /dev/null +++ b/ui/tap-rlc-graph.c @@ -0,0 +1,309 @@ +/* tap-rlc-stream.c + * LTE RLC stream statistics + * + * Originally from tcp_graph.c by Pavel Mores <pvl@uh.cz> + * Win32 port: rwh@unifiedtech.com + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include "tap-rlc-graph.h" + +#include <file.h> +#include <frame_tvbuff.h> + +#include <epan/epan.h> +#include <epan/epan_dissect.h> +#include <epan/packet.h> +#include <epan/tap.h> + + +int compare_rlc_headers(guint16 ueid1, guint16 channelType1, guint16 channelId1, guint8 rlcMode1, guint8 direction1, + guint16 ueid2, guint16 channelType2, guint16 channelId2, guint8 rlcMode2, guint8 direction2, + gboolean frameIsControl) +{ + /* Same direction, data - OK. */ + if (!frameIsControl) { + return (direction1 == direction2) && + (ueid1 == ueid2) && + (channelType1 == channelType2) && + (channelId1 == channelId2) && + (rlcMode1 == rlcMode2); + } + else { + if (frameIsControl && (rlcMode1 == RLC_AM_MODE) && (rlcMode2 == RLC_AM_MODE)) { + return ((direction1 != direction2) && + (ueid1 == ueid2) && + (channelType1 == channelType2) && + (channelId1 == channelId2)); + } + else { + return FALSE; + } + } +} + + +static int +tap_lte_rlc_packet(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip) +{ + int n; + gboolean is_unique = TRUE; + th_t *th = (th_t *)pct; + const rlc_lte_tap_info *header = (const rlc_lte_tap_info*)vip; + + /* Check new header details against any/all stored ones */ + for (n=0; n < th->num_hdrs; n++) { + rlc_lte_tap_info *stored = th->rlchdrs[n]; + + if (compare_rlc_headers(stored->ueid, stored->channelType, stored->channelId, stored->rlcMode, stored->direction, + header->ueid, header->channelType, header->channelId, header->rlcMode, header->direction, + header->isControlPDU)) { + is_unique = FALSE; + break; + } + } + + /* Add address if unique and have space for it */ + if (is_unique && (th->num_hdrs < MAX_SUPPORTED_CHANNELS)) { + /* Copy the tap stuct in as next header */ + /* Need to take a deep copy of the tap struct, it may not be valid + to read after this function returns? */ + th->rlchdrs[th->num_hdrs] = g_new(rlc_lte_tap_info,1); + *(th->rlchdrs[th->num_hdrs]) = *header; + + /* Store in direction of data though... */ + if (th->rlchdrs[th->num_hdrs]->isControlPDU) { + th->rlchdrs[th->num_hdrs]->direction = !th->rlchdrs[th->num_hdrs]->direction; + } + th->num_hdrs++; + } + + return 0; +} + +/* Return an array of tap_info structs that were found while dissecting the current frame + in the packet list */ +rlc_lte_tap_info *select_rlc_lte_session(capture_file *cf, + struct rlc_segment *hdrs, + gchar **err_msg, gboolean *free_err_msg) +{ + frame_data *fdata; + epan_dissect_t edt; + dfilter_t *sfcode; + + GString *error_string; + nstime_t rel_ts; + /* Initialised to no known channels */ + th_t th = {0, {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}}; + + if (cf->state == FILE_CLOSED) { + return NULL; + } + + fdata = cf->current_frame; + + /* No real filter yet */ + if (!dfilter_compile("rlc-lte", &sfcode, err_msg)) { + *free_err_msg = TRUE; + return NULL; + } + + /* Dissect the current record */ + if (!cf_read_record(cf, fdata)) { + return NULL; /* error reading the record */ + } + + /* Set tap listener that will populate th. */ + error_string = register_tap_listener("rlc-lte", &th, NULL, 0, NULL, tap_lte_rlc_packet, NULL); + if (error_string){ + fprintf(stderr, "wireshark: Couldn't register rlc_lte_graph tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); + } + + epan_dissect_init(&edt, cf->epan, TRUE, FALSE); + epan_dissect_prime_dfilter(&edt, sfcode); + epan_dissect_run_with_taps(&edt, cf->cd_t, &cf->phdr, frame_tvbuff_new_buffer(fdata, &cf->buf), fdata, NULL); + rel_ts = edt.pi.rel_ts; + epan_dissect_cleanup(&edt); + remove_tap_listener(&th); + + if (th.num_hdrs == 0){ + /* This "shouldn't happen", as our menu items shouldn't + * even be enabled if the selected packet isn't an RLC PDU + * as rlc_lte_graph_selected_packet_enabled() is used + * to determine whether to enable any of our menu items. */ + *err_msg = (char*)"Selected packet doesn't have an RLC PDU"; + *free_err_msg = FALSE; + return NULL; + } + /* XXX fix this later, we should show a dialog allowing the user + to select which session he wants here + */ + if (th.num_hdrs>1){ + /* Can only handle a single RLC channel yet */ + *err_msg = (char*)"The selected packet has more than one LTE RLC channel in it."; + *free_err_msg = FALSE; + return NULL; + } + + /* For now, still always choose the first/only one */ + hdrs->num = fdata->num; + hdrs->rel_secs = (guint32) rel_ts.secs; + hdrs->rel_usecs = rel_ts.nsecs/1000; + hdrs->abs_secs = (guint32) fdata->abs_ts.secs; + hdrs->abs_usecs = fdata->abs_ts.nsecs/1000; + + hdrs->ueid = th.rlchdrs[0]->ueid; + hdrs->channelType = th.rlchdrs[0]->channelType; + hdrs->channelId = th.rlchdrs[0]->channelId; + hdrs->rlcMode = th.rlchdrs[0]->rlcMode; + hdrs->isControlPDU = th.rlchdrs[0]->isControlPDU; + hdrs->direction = !hdrs->isControlPDU ? th.rlchdrs[0]->direction : !th.rlchdrs[0]->direction; + + return th.rlchdrs[0]; +} + + +int rlc_lte_tap_for_graph_data(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip) +{ + struct rlc_graph *graph = (struct rlc_graph *)pct; + const rlc_lte_tap_info *rlchdr = (const rlc_lte_tap_info*)vip; + + /* See if this one matches current channel */ + if (compare_rlc_headers(graph->ueid, graph->channelType, graph->channelId, graph->rlcMode, graph->direction, + rlchdr->ueid, rlchdr->channelType, rlchdr->channelId, rlchdr->rlcMode, rlchdr->direction, + rlchdr->isControlPDU)) { + + struct rlc_segment *segment = (struct rlc_segment *)g_malloc(sizeof(struct rlc_segment)); + + /* It matches. Add to end of segment list */ + segment->next = NULL; + segment->num = pinfo->fd->num; + segment->rel_secs = (guint32) pinfo->rel_ts.secs; + segment->rel_usecs = pinfo->rel_ts.nsecs/1000; + segment->abs_secs = (guint32) pinfo->fd->abs_ts.secs; + segment->abs_usecs = pinfo->fd->abs_ts.nsecs/1000; + + segment->ueid = rlchdr->ueid; + segment->channelType = rlchdr->channelType; + segment->channelId = rlchdr->channelId; + segment->direction = rlchdr->direction; + segment->rlcMode = rlchdr->rlcMode; + + segment->isControlPDU = rlchdr->isControlPDU; + + if (!rlchdr->isControlPDU) { + /* Data */ + segment->SN = rlchdr->sequenceNumber; + segment->isResegmented = rlchdr->isResegmented; + } + else { + /* Status PDU */ + gint n; + segment->ACKNo = rlchdr->ACKNo; + segment->noOfNACKs = rlchdr->noOfNACKs; + for (n=0; n < rlchdr->noOfNACKs; n++) { + segment->NACKs[n] = rlchdr->NACKs[n]; + } + } + + /* Add to list */ + if (graph->segments) { + /* Add to end of existing last element */ + graph->last_segment->next = segment; + } else { + /* Make this the first (only) segment */ + graph->segments = segment; + } + + /* This one is now the last one */ + graph->last_segment = segment; + } + + return 0; +} + +/* If don't have a channel, try to get one from current frame, then read all frames looking for data + for that channel. */ +gboolean rlc_graph_segment_list_get(capture_file *cf, struct rlc_graph *g, gboolean stream_known, + char **err_string, gboolean *free_err_string) +{ + struct rlc_segment current; + GString *error_string; + + g_log(NULL, G_LOG_LEVEL_DEBUG, "graph_segment_list_get()"); + + if (!cf || !g) { + /* Really shouldn't happen */ + return FALSE; + } + + if (!stream_known) { + struct rlc_lte_tap_info *header = select_rlc_lte_session(cf, ¤t, err_string, free_err_string); + if (!header) { + /* Didn't have a channel, and current frame didn't provide one */ + return FALSE; + } + g->channelSet = TRUE; + g->ueid = header->ueid; + g->channelType = header->channelType; + g->channelId = header->channelId; + g->rlcMode = header->rlcMode; + g->direction = header->direction; + } + + + /* rescan all the packets and pick up all interesting RLC headers. + * we only filter for rlc-lte here for speed and do the actual compare + * in the tap listener + */ + + g->last_segment = NULL; + error_string = register_tap_listener("rlc-lte", g, "rlc-lte", 0, NULL, rlc_lte_tap_for_graph_data, NULL); + if (error_string) { + fprintf(stderr, "wireshark: Couldn't register rlc_graph tap: %s\n", + error_string->str); + g_string_free(error_string, TRUE); + exit(1); /* XXX: fix this */ + } + cf_retap_packets(cf); + remove_tap_listener(g); + + return TRUE; +} + +/* Free and zero the segments list of an rlc_graph struct */ +void rlc_graph_segment_list_free(struct rlc_graph * g) +{ + struct rlc_segment *segment; + + /* Free all segments */ + while (g->segments) { + segment = g->segments->next; + g_free(g->segments); + g->segments = segment; + } + /* Set head of list to NULL too */ + g->segments = NULL; +} diff --git a/ui/tap-rlc-graph.h b/ui/tap-rlc-graph.h new file mode 100644 index 0000000000..1312ef12c2 --- /dev/null +++ b/ui/tap-rlc-graph.h @@ -0,0 +1,103 @@ +/* tap-rlc-stream.h + * LTE RLC stream statistics + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __TAP_RLC_GRAPH_H__ +#define __TAP_RLC_GRAPH_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include <epan/epan.h> +#include <epan/packet.h> +#include <wiretap/wtap.h> +#include <cfile.h> +#include <epan/dissectors/packet-rlc-lte.h> + +struct rlc_segment { + struct rlc_segment *next; + guint32 num; /* framenum */ + guint32 rel_secs; + guint32 rel_usecs; + guint32 abs_secs; + guint32 abs_usecs; + + gboolean isControlPDU; + guint16 SN; + guint16 isResegmented; + guint16 ACKNo; + #define MAX_NACKs 128 + guint16 noOfNACKs; + guint16 NACKs[MAX_NACKs]; + + guint16 ueid; + guint16 channelType; + guint16 channelId; + guint8 rlcMode; + guint8 direction; +}; + +/* A collection of channels that may be found in one frame. Used when working out + which channel(s) are present in a frame. */ +typedef struct _th_t { + int num_hdrs; + #define MAX_SUPPORTED_CHANNELS 8 + rlc_lte_tap_info *rlchdrs[MAX_SUPPORTED_CHANNELS]; +} th_t; + +struct rlc_graph { + /* List of segments to show */ + struct rlc_segment *segments; + struct rlc_segment *last_segment; + + /* These are filled in with the channel/direction this graph is showing */ + gboolean channelSet; + guint16 ueid; + guint16 channelType; + guint16 channelId; + guint8 rlcMode; + guint8 direction; + + /* Lists of elements to draw. N.B. GTK version only. */ + struct element_list *elists; +}; + +gboolean rlc_graph_segment_list_get(capture_file *cf, struct rlc_graph *tg, gboolean stream_known, + char **err_string, gboolean *free_err_string); +void rlc_graph_segment_list_free(struct rlc_graph * ); + + + +int compare_rlc_headers(guint16 ueid1, guint16 channelType1, guint16 channelId1, guint8 rlcMode1, guint8 direction1, + guint16 ueid2, guint16 channelType2, guint16 channelId2, guint8 rlcMode2, guint8 direction2, + gboolean isControlFrame); +rlc_lte_tap_info *select_rlc_lte_session(capture_file *cf, struct rlc_segment *hdrs, + gchar **err_msg, gboolean *free_err_msg); +int rlc_lte_tap_for_graph_data(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif |