1 /* tap-rlc-graph.c
2  * LTE RLC channel graph info
3  *
4  * Originally from tcp_graph.c by Pavel Mores <pvl@uh.cz>
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * SPDX-License-Identifier: GPL-2.0-or-later
11  */
12 
13 #include "config.h"
14 
15 #include <stdlib.h>
16 
17 #include "tap-rlc-graph.h"
18 
19 #include <file.h>
20 #include <frame_tvbuff.h>
21 
22 #include <epan/epan_dissect.h>
23 #include <epan/tap.h>
24 
25 /* Return TRUE if the 2 sets of parameters refer to the same channel. */
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)26 gboolean compare_rlc_headers(guint16 ueid1, guint16 channelType1, guint16 channelId1, guint8 rlcMode1, guint8 direction1,
27                              guint16 ueid2, guint16 channelType2, guint16 channelId2, guint8 rlcMode2, guint8 direction2,
28                              gboolean frameIsControl)
29 {
30     /* Same direction, data - OK. */
31     if (!frameIsControl) {
32         return (direction1 == direction2) &&
33                (ueid1 == ueid2) &&
34                (channelType1 == channelType2) &&
35                (channelId1 == channelId2) &&
36                (rlcMode1 == rlcMode2);
37     }
38     else {
39         /* Control case */
40         if ((rlcMode1 == RLC_AM_MODE) && (rlcMode2 == RLC_AM_MODE)) {
41             return ((direction1 != direction2) &&
42                     (ueid1 == ueid2) &&
43                     (channelType1 == channelType2) &&
44                     (channelId1 == channelId2));
45         }
46         else {
47             return FALSE;
48         }
49     }
50 }
51 
52 /* This is the tap function used to identify a list of channels found in the current frame.  It is only used for the single,
53    currently selected frame. */
54 static tap_packet_status
tap_lte_rlc_packet(void * pct,packet_info * pinfo _U_,epan_dissect_t * edt _U_,const void * vip)55 tap_lte_rlc_packet(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip)
56 {
57     int       n;
58     gboolean  is_unique = TRUE;
59     th_t     *th        = (th_t *)pct;
60     const rlc_lte_tap_info *header = (const rlc_lte_tap_info*)vip;
61 
62     /* Check new header details against any/all stored ones */
63     for (n=0; n < th->num_hdrs; n++) {
64         rlc_lte_tap_info *stored = th->rlchdrs[n];
65 
66         if (compare_rlc_headers(stored->ueid, stored->channelType, stored->channelId, stored->rlcMode, stored->direction,
67                                 header->ueid, header->channelType, header->channelId, header->rlcMode, header->direction,
68                                 header->isControlPDU)) {
69             is_unique = FALSE;
70             break;
71         }
72     }
73 
74     /* Add address if unique and have space for it */
75     if (is_unique && (th->num_hdrs < MAX_SUPPORTED_CHANNELS)) {
76         /* Copy the tap stuct in as next header */
77         /* Need to take a deep copy of the tap struct, it may not be valid
78            to read after this function returns? */
79         th->rlchdrs[th->num_hdrs] = g_new(rlc_lte_tap_info,1);
80         *(th->rlchdrs[th->num_hdrs]) = *header;
81 
82         /* Store in direction of data though... */
83         if (th->rlchdrs[th->num_hdrs]->isControlPDU) {
84             th->rlchdrs[th->num_hdrs]->direction = !th->rlchdrs[th->num_hdrs]->direction;
85         }
86         th->num_hdrs++;
87     }
88 
89     return TAP_PACKET_DONT_REDRAW; /* i.e. no immediate redraw requested */
90 }
91 
92 /* Return an array of tap_info structs that were found while dissecting the current frame
93  * in the packet list. Errors are passed back to the caller, as they will be reported differently
94  * depending upon which GUI toolkit is being used. */
select_rlc_lte_session(capture_file * cf,struct rlc_segment * hdrs,gchar ** err_msg)95 rlc_lte_tap_info *select_rlc_lte_session(capture_file *cf,
96                                          struct rlc_segment *hdrs,
97                                          gchar **err_msg)
98 {
99     frame_data     *fdata;
100     epan_dissect_t  edt;
101     dfilter_t      *sfcode;
102 
103     GString        *error_string;
104     nstime_t        rel_ts;
105     /* Initialised to no known channels */
106     th_t            th = {0, {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}};
107 
108     if (cf->state == FILE_CLOSED) {
109         return NULL;
110     }
111 
112     /* No real filter yet */
113     if (!dfilter_compile("rlc-lte", &sfcode, err_msg)) {
114         return NULL;
115     }
116 
117     /* Dissect the data from the current frame. */
118     if (!cf_read_current_record(cf)) {
119         return NULL;  /* error reading the record */
120     }
121 
122     fdata = cf->current_frame;
123 
124     /* Set tap listener that will populate th. */
125     error_string = register_tap_listener("rlc-lte", &th, NULL, 0, NULL, tap_lte_rlc_packet, NULL, NULL);
126     if (error_string){
127         fprintf(stderr, "wireshark: Couldn't register rlc_lte_graph tap: %s\n",
128                 error_string->str);
129         g_string_free(error_string, TRUE);
130         exit(1);   /* XXX: fix this */
131     }
132 
133     epan_dissect_init(&edt, cf->epan, TRUE, FALSE);
134     epan_dissect_prime_with_dfilter(&edt, sfcode);
135     epan_dissect_run_with_taps(&edt, cf->cd_t, &cf->rec,
136                                frame_tvbuff_new_buffer(&cf->provider, fdata, &cf->buf),
137                                fdata, NULL);
138     rel_ts = edt.pi.rel_ts;
139     epan_dissect_cleanup(&edt);
140     remove_tap_listener(&th);
141 
142     if (th.num_hdrs == 0){
143         /* This "shouldn't happen", as the graph menu items won't
144          * even be enabled if the selected packet isn't an RLC PDU.
145          */
146         *err_msg = g_strdup("Selected packet doesn't have an RLC PDU");
147         return NULL;
148     }
149     /* XXX fix this later, we should show a dialog allowing the user
150      * to select which session he wants here */
151     if (th.num_hdrs>1){
152         /* Can only handle a single RLC channel yet */
153         *err_msg = g_strdup("The selected packet has more than one LTE RLC channel in it.");
154         return NULL;
155     }
156 
157     /* For now, still always choose the first/only one */
158     hdrs->num = fdata->num;
159     hdrs->rel_secs = (guint32) rel_ts.secs;
160     hdrs->rel_usecs = rel_ts.nsecs/1000;
161 
162     hdrs->ueid = th.rlchdrs[0]->ueid;
163     hdrs->channelType = th.rlchdrs[0]->channelType;
164     hdrs->channelId = th.rlchdrs[0]->channelId;
165     hdrs->rlcMode = th.rlchdrs[0]->rlcMode;
166     hdrs->isControlPDU = th.rlchdrs[0]->isControlPDU;
167     /* Flip direction if have control PDU */
168     hdrs->direction = !hdrs->isControlPDU ? th.rlchdrs[0]->direction : !th.rlchdrs[0]->direction;
169 
170     return th.rlchdrs[0];
171 }
172 
173 /* This is the tapping function to update stats when dissecting the whole packet list */
rlc_lte_tap_for_graph_data(void * pct,packet_info * pinfo,epan_dissect_t * edt _U_,const void * vip)174 static tap_packet_status rlc_lte_tap_for_graph_data(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
175 {
176     struct rlc_graph *graph  = (struct rlc_graph *)pct;
177     const rlc_lte_tap_info *rlchdr = (const rlc_lte_tap_info*)vip;
178 
179     /* See if this one matches graph's channel */
180     if (compare_rlc_headers(graph->ueid,   graph->channelType,  graph->channelId,  graph->rlcMode,   graph->direction,
181                             rlchdr->ueid,  rlchdr->channelType, rlchdr->channelId, rlchdr->rlcMode,  rlchdr->direction,
182                             rlchdr->isControlPDU)) {
183 
184         /* It matches.  Copy segment details out of tap struct */
185         struct rlc_segment *segment = g_new(struct rlc_segment, 1);
186         segment->next = NULL;
187         segment->num = pinfo->num;
188         segment->rel_secs = (guint32) pinfo->rel_ts.secs;
189         segment->rel_usecs = pinfo->rel_ts.nsecs/1000;
190 
191         segment->ueid = rlchdr->ueid;
192         segment->channelType = rlchdr->channelType;
193         segment->channelId = rlchdr->channelId;
194         segment->direction = rlchdr->direction;
195         segment->rlcMode = rlchdr->rlcMode;
196 
197         segment->isControlPDU = rlchdr->isControlPDU;
198 
199         if (!rlchdr->isControlPDU) {
200             /* Data */
201             segment->SN = rlchdr->sequenceNumber;
202             segment->isResegmented = rlchdr->isResegmented;
203             segment->pduLength = rlchdr->pduLength;
204         }
205         else {
206             /* Status PDU */
207             gint n;
208             segment->ACKNo = rlchdr->ACKNo;
209             segment->noOfNACKs = rlchdr->noOfNACKs;
210             for (n=0; n < rlchdr->noOfNACKs; n++) {
211                 segment->NACKs[n] = rlchdr->NACKs[n];
212             }
213         }
214 
215         /* Add segment to end of list */
216         if (graph->segments) {
217             /* Add to end of existing last element */
218             graph->last_segment->next = segment;
219         } else {
220             /* Make this the first (only) segment */
221             graph->segments = segment;
222         }
223 
224         /* This one is now the last one */
225         graph->last_segment = segment;
226     }
227 
228     return TAP_PACKET_DONT_REDRAW; /* i.e. no immediate redraw requested */
229 }
230 
231 /* If don't have a channel, try to get one from current frame, then read all frames looking for data
232  * for that channel. */
rlc_graph_segment_list_get(capture_file * cf,struct rlc_graph * g,gboolean stream_known,char ** err_string)233 gboolean rlc_graph_segment_list_get(capture_file *cf, struct rlc_graph *g, gboolean stream_known,
234                                     char **err_string)
235 {
236     struct rlc_segment current;
237     GString    *error_string;
238 
239     if (!cf || !g) {
240         /* Really shouldn't happen */
241         return FALSE;
242     }
243 
244     if (!stream_known) {
245         struct rlc_lte_tap_info *header = select_rlc_lte_session(cf, &current, err_string);
246         if (!header) {
247             /* Didn't have a channel, and current frame didn't provide one */
248             return FALSE;
249         }
250         g->channelSet = TRUE;
251         g->ueid = header->ueid;
252         g->channelType = header->channelType;
253         g->channelId = header->channelId;
254         g->rlcMode = header->rlcMode;
255         g->direction = header->direction;
256     }
257 
258 
259     /* Rescan all the packets and pick up all interesting RLC headers.
260      * We only filter for rlc-lte here for speed and do the actual compare
261      * in the tap listener
262      */
263 
264     g->last_segment = NULL;
265     error_string = register_tap_listener("rlc-lte", g, "rlc-lte", 0, NULL, rlc_lte_tap_for_graph_data, NULL, NULL);
266     if (error_string) {
267         fprintf(stderr, "wireshark: Couldn't register rlc_graph tap: %s\n",
268                 error_string->str);
269         g_string_free(error_string, TRUE);
270         exit(1);   /* XXX: fix this */
271     }
272     cf_retap_packets(cf);
273     remove_tap_listener(g);
274 
275     if (g->last_segment == NULL) {
276         *err_string = g_strdup("No packets found");
277         return FALSE;
278     }
279 
280     return TRUE;
281 }
282 
283 /* Free and zero the segments list of an rlc_graph struct */
rlc_graph_segment_list_free(struct rlc_graph * g)284 void rlc_graph_segment_list_free(struct rlc_graph * g)
285 {
286     struct rlc_segment *segment;
287 
288     /* Free all segments */
289     while (g->segments) {
290         segment = g->segments->next;
291         g_free(g->segments);
292         g->segments = segment;
293     }
294 }
295