89f339afa2
Have cf_read_current_record() take a capture_file as an argument and read, into its wtap_rec and Buffer for the currently-selected frame, information for the currently-selected frame. Rename cf_read_record_r() to cf_read_record(). That gives us 1) a routine that reads the currently-selected frame into the wtap_rec and Buffer for the currently-selected frame and 2) a routine that reads an arbitrary frame into the wtap_rec and Buffer supplied to it. If you *want* the currently-selected record, use the former, otherwise use the latter. Change-Id: If6bd5915dd5bc18334d7b89859822a19234153a4 Reviewed-on: https://code.wireshark.org/review/32858 Petri-Dish: Guy Harris <guy@alum.mit.edu> Tested-by: Petri Dish Buildbot Reviewed-by: Guy Harris <guy@alum.mit.edu>
296 lines
10 KiB
C
296 lines
10 KiB
C
/* tap-rlc-graph.c
|
|
* LTE RLC channel graph info
|
|
*
|
|
* Originally from tcp_graph.c by Pavel Mores <pvl@uh.cz>
|
|
*
|
|
* Wireshark - Network traffic analyzer
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
* Copyright 1998 Gerald Combs
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "tap-rlc-graph.h"
|
|
|
|
#include <file.h>
|
|
#include <frame_tvbuff.h>
|
|
|
|
#include <epan/epan_dissect.h>
|
|
#include <epan/tap.h>
|
|
|
|
/* Return TRUE if the 2 sets of parameters refer to the same channel. */
|
|
gboolean 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* This is the tap function used to identify a list of channels found in the current frame. It is only used for the single,
|
|
currently selected frame. */
|
|
static tap_packet_status
|
|
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 TAP_PACKET_DONT_REDRAW; /* i.e. no immediate redraw requested */
|
|
}
|
|
|
|
/* Return an array of tap_info structs that were found while dissecting the current frame
|
|
* in the packet list. Errors are passed back to the caller, as they will be reported differently
|
|
* depending upon which GUI toolkit is being used. */
|
|
rlc_lte_tap_info *select_rlc_lte_session(capture_file *cf,
|
|
struct rlc_segment *hdrs,
|
|
gchar **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;
|
|
}
|
|
|
|
/* No real filter yet */
|
|
if (!dfilter_compile("rlc-lte", &sfcode, err_msg)) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Dissect the data from the current frame. */
|
|
if (!cf_read_current_record(cf)) {
|
|
return NULL; /* error reading the record */
|
|
}
|
|
|
|
fdata = cf->current_frame;
|
|
|
|
/* Set tap listener that will populate th. */
|
|
error_string = register_tap_listener("rlc-lte", &th, NULL, 0, NULL, tap_lte_rlc_packet, NULL, 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); /* XXX: fix this */
|
|
}
|
|
|
|
epan_dissect_init(&edt, cf->epan, TRUE, FALSE);
|
|
epan_dissect_prime_with_dfilter(&edt, sfcode);
|
|
epan_dissect_run_with_taps(&edt, cf->cd_t, &cf->rec,
|
|
frame_tvbuff_new_buffer(&cf->provider, 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 the graph menu items won't
|
|
* even be enabled if the selected packet isn't an RLC PDU.
|
|
*/
|
|
*err_msg = g_strdup("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 */
|
|
*err_msg = g_strdup("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->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;
|
|
/* Flip direction if have control PDU */
|
|
hdrs->direction = !hdrs->isControlPDU ? th.rlchdrs[0]->direction : !th.rlchdrs[0]->direction;
|
|
|
|
return th.rlchdrs[0];
|
|
}
|
|
|
|
/* This is the tapping function to update stats when dissecting the whole packet list */
|
|
static tap_packet_status 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 graph's 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)) {
|
|
|
|
/* It matches. Copy segment details out of tap struct */
|
|
struct rlc_segment *segment = (struct rlc_segment *)g_malloc(sizeof(struct rlc_segment));
|
|
segment->next = NULL;
|
|
segment->num = pinfo->num;
|
|
segment->rel_secs = (guint32) pinfo->rel_ts.secs;
|
|
segment->rel_usecs = pinfo->rel_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;
|
|
segment->pduLength = rlchdr->pduLength;
|
|
}
|
|
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 segment to end of 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 TAP_PACKET_DONT_REDRAW; /* i.e. no immediate redraw requested */
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
struct rlc_segment current;
|
|
GString *error_string;
|
|
|
|
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);
|
|
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, 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);
|
|
|
|
if (g->last_segment == NULL) {
|
|
*err_string = g_strdup("No packets found");
|
|
return FALSE;
|
|
}
|
|
|
|
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;
|
|
}
|