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