1 /* Routines for Huawei's FP Mux Header disassembly
2  * Protocol reference: EU Patent publication No. EP2053798
3  *
4  * Wireshark - Network traffic analyzer
5  * By Gerald Combs <gerald@wireshark.org>
6  * Copyright 1998 Gerald Combs
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  */
10 
11 #include "config.h"
12 
13 #include <epan/packet.h>
14 #include <wiretap/wtap.h>
15 #include <epan/conversation.h>
16 #include <epan/expert.h>
17 #include <epan/proto_data.h>
18 #include "packet-umts_fp.h"
19 #include "packet-umts_mac.h"
20 #include "packet-umts_rlc.h"
21 
22 /* Externals */
23 extern int proto_fp;
24 extern int proto_umts_mac;
25 extern int proto_umts_rlc;
26 
27 void proto_register_fp_mux(void);
28 void proto_reg_handoff_fp_mux(void);
29 
30 static int proto_fp_mux = -1;
31 static dissector_handle_t fp_mux_handle;
32 static heur_dissector_list_t heur_subdissector_list;
33 
34 /* Constants */
35 #define MAX_PAYLOADS 64
36 
37 /* Trees */
38 static int ett_fpmux = -1;
39 
40 /* Fields */
41 static int hf_fpmux_uid = -1;
42 static int hf_fpmux_extension_flag = -1;
43 static int hf_fpmux_length = -1;
44 
45 /* Expert Fields */
46 static expert_field ei_fpm_length_needlessly_extended = EI_INIT;
47 static expert_field ei_fpm_too_many_payloads = EI_INIT;
48 static expert_field ei_fpm_bad_length = EI_INIT;
49 
50 /* Preferences */
51 /* Place UID in proto tree */
52 static gboolean fp_mux_uid_in_tree = TRUE;
53 /* Call heuristic FP dissectors on payload */
54 static gboolean call_fp_heur = TRUE;
55 
56 /* Enum Values */
57 static const true_false_string fpmux_extension_flag_vals = {
58     "Extension Present", "No Extension"
59 };
60 
61 
62 /* Per-packet info */
63 typedef struct fp_mux_info_t {
64     guint32        srcport;
65     guint32        destport;
66     fp_info*       fpinfos[MAX_PAYLOADS];
67     umts_mac_info* macinfos[MAX_PAYLOADS];
68     rlc_info*      rlcinfos[MAX_PAYLOADS];
69 } fp_mux_info_t;
70 
dissect_payload(tvbuff_t * next_tvb,packet_info * pinfo,proto_tree * tree,struct fp_mux_info_t * fp_mux_info,guint16 payload_index,guint16 uid)71 static void dissect_payload(tvbuff_t *next_tvb, packet_info *pinfo, proto_tree *tree, struct fp_mux_info_t* fp_mux_info, guint16 payload_index, guint16 uid)
72 {
73     heur_dtbl_entry_t *hdtbl_entry;
74     gboolean conv_dissected; /* If the TVB was dissected using the conversation dissector*/
75     gboolean heur_dissected; /* If the TVB was dissected using a heuristic dissector*/
76     guint32 current_destport,current_srcport;
77 
78     /* Saving old ports */
79     current_destport = pinfo->destport;
80     current_srcport = pinfo->srcport;
81 
82     /* Replacing ports with UID (ports are used by the FP dissector) */
83     pinfo->destport = uid;
84     pinfo->srcport = 0;
85 
86     /* Adding previously created FP/MAC/RLC info */
87     p_add_proto_data(wmem_file_scope(), pinfo, proto_fp, 0, fp_mux_info->fpinfos[payload_index]);
88     p_add_proto_data(wmem_file_scope(), pinfo, proto_umts_mac, 0, fp_mux_info->macinfos[payload_index]);
89     p_add_proto_data(wmem_file_scope(), pinfo, proto_umts_rlc, 0, fp_mux_info->rlcinfos[payload_index]);
90 
91     /* Trying a dissector assigned to the conversation (Usually from NBAP) */
92     conv_dissected = try_conversation_dissector(&pinfo->dst, &pinfo->src, ENDPOINT_UDP,
93                                  pinfo->destport, pinfo->srcport, next_tvb, pinfo, tree, NULL, 0);
94     if (!conv_dissected) {
95         /* No conversation dissector / TVB was rejected, try other options */
96         if(call_fp_heur) {
97             /* Trying heuristic dissector */
98             heur_dissected = dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, tree, &hdtbl_entry, NULL);
99             if(!heur_dissected) {
100                 /* No heuristic dissector / TVB was rejected, show as data */
101                 call_data_dissector(next_tvb,pinfo,tree);
102             }
103         }
104         else {
105             /* Trying heuristic dissectors disabled, show as data */
106             call_data_dissector(next_tvb,pinfo,tree);
107         }
108     }
109 
110     /* Saving FP/MAC/RLC Info which the sub dissector might have attached */
111     fp_mux_info->fpinfos[payload_index] =  (fp_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_fp, 0);
112     fp_mux_info->macinfos[payload_index] = (umts_mac_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_umts_mac, 0);
113     fp_mux_info->rlcinfos[payload_index] = (rlc_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_umts_rlc, 0);
114 
115     /* Removing FP/MAC/RLC info from the packet */
116     /* to allow other packets to be dissected correctly */
117     p_remove_proto_data(wmem_file_scope(), pinfo, proto_fp, 0);
118     p_remove_proto_data(wmem_file_scope(), pinfo, proto_umts_mac, 0);
119     p_remove_proto_data(wmem_file_scope(), pinfo, proto_umts_rlc, 0);
120 
121     /* Setting a fence in the info column to aggregate all payloads' descriptions */
122     const gchar* info = col_get_text(pinfo->cinfo, COL_INFO);
123     if (info != NULL && *info != '\0') {
124         /* Only creating fence if the column's current text isn't NULL or an empty string */
125         col_append_str(pinfo->cinfo, COL_INFO, " ");
126         col_set_fence(pinfo->cinfo, COL_INFO);
127     }
128 
129     /* Restoring ports */
130     pinfo->destport = current_destport;
131     pinfo->srcport = current_srcport;
132 }
133 
dissect_fp_mux(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)134 static int dissect_fp_mux(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
135 {
136     guint16 uid;
137     gboolean ext_flag;
138     guint8 length_field_size;
139     guint16 length;
140     guint32 header_length;
141     guint32 total_length;
142     guint32 offset = 0;
143     guint32 out_value = 0;
144     guint32 payload_index = 0;
145     tvbuff_t *next_tvb;
146     proto_item *ti;
147     proto_tree *fpmux_tree = NULL;
148     struct fp_mux_info_t* fp_mux_info;
149 
150     total_length = tvb_captured_length(tvb);
151 
152     /* Adding FP MUX info*/
153     fp_mux_info = (fp_mux_info_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_fp_mux, 0);
154     if (!fp_mux_info) {
155         fp_mux_info = wmem_new0(wmem_file_scope(), struct fp_mux_info_t);
156         /* remember 'lower' UDP layer port information so we can later
157          * differentiate 'lower' UDP layer from 'user data' UDP layer */
158         fp_mux_info->srcport = pinfo->srcport;
159         fp_mux_info->destport = pinfo->destport;
160         p_add_proto_data(wmem_file_scope(), pinfo, proto_fp_mux, 0, fp_mux_info);
161     }
162 
163     col_set_str(pinfo->cinfo, COL_PROTOCOL, "FP Mux");
164     col_clear(pinfo->cinfo, COL_INFO);
165 
166     while (offset != total_length) {
167         ext_flag = tvb_get_bits(tvb, (offset+2)*8, 1, ENC_NA) == 0x01;
168         header_length = ext_flag ? 4 : 3;
169 
170         /* Adding another FP Mux tree */
171         ti = proto_tree_add_item(tree, proto_fp_mux, tvb, offset, header_length, ENC_NA);
172         fpmux_tree = proto_item_add_subtree(ti, ett_fpmux);
173 
174         /* Adding User Identifier field */
175         proto_tree_add_item_ret_uint(fpmux_tree, hf_fpmux_uid, tvb, offset, 2, ENC_BIG_ENDIAN, &out_value);
176         uid = (guint16)out_value;
177         offset += 2;
178         /* Appending User Identifier to FP Mux tree label */
179         if (fp_mux_uid_in_tree) {
180             proto_item_append_text(ti, ", Uid: %d", uid);
181         }
182 
183         /* Adding Extension Flag */
184         ti = proto_tree_add_boolean(fpmux_tree, hf_fpmux_extension_flag, tvb, offset, 1, ext_flag);
185         proto_item_append_text(ti," (%d)", ext_flag ? 1 : 0);
186 
187         /* Adding Length field */
188         if(ext_flag) {
189             /* Extended - Length is 15 bits */
190             length = tvb_get_ntohs(tvb, offset) & 0x7FFF;
191             length_field_size = 2;
192         }
193         else {
194             /* Not extended - Length is 7 bits */
195             length = tvb_get_guint8(tvb, offset) & 0x7F;
196             length_field_size = 1;
197         }
198         proto_tree_add_uint(fpmux_tree, hf_fpmux_length, tvb, offset, length_field_size, length);
199         if(length == 0) {
200             /* Length is zero. Showing error and aborting dissection*/
201             proto_tree_add_expert_format(fpmux_tree, pinfo, &ei_fpm_bad_length, tvb, offset, length_field_size,
202                 "Bad length: payload length can't be 0");
203             return total_length;
204         }
205         if (length > total_length - offset) {
206             /* Length value too big. Showing error and aborting dissection*/
207             proto_tree_add_expert_format(fpmux_tree, pinfo, &ei_fpm_bad_length, tvb, offset, length_field_size,
208                 "Bad length: payload length exceeds remaining data length (%d) ", (total_length - offset));
209             return total_length;
210         }
211         if (length < 128 && ext_flag) {
212             /* Length could fit in 7 bits yet the length field was extended */
213             proto_tree_add_expert(fpmux_tree, pinfo, &ei_fpm_length_needlessly_extended, tvb, offset, length_field_size);
214         }
215         offset += length_field_size;
216 
217         /* Dissecting Payload */
218         next_tvb = tvb_new_subset_length(tvb,offset,length);
219         if(payload_index >= MAX_PAYLOADS) {
220             /* Too many FP payloads. Showing error and aboring dissection*/
221             proto_tree_add_expert_format(fpmux_tree, pinfo, &ei_fpm_too_many_payloads, tvb, offset, -1,
222                 "Too many FP packets muxed in a single packet ( Maximum expected: %d )", MAX_PAYLOADS);
223             return total_length;
224         }
225         dissect_payload(next_tvb,pinfo,tree,fp_mux_info,payload_index,uid);
226         offset += length;
227 
228         payload_index++;
229     }
230 
231     return total_length;
232 }
233 
234 
heur_dissect_fp_mux(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data)235 static int heur_dissect_fp_mux(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
236 {
237     gboolean ext_flag;
238     guint8 length_field_size;
239     guint16 length;
240     guint32 header_length;
241     guint32 total_length;
242     guint32 offset = 0;
243     guint32 chunks = 0;
244     conversation_t *conversation;
245     struct fp_mux_info_t* fp_mux_info;
246 
247     total_length = tvb_captured_length(tvb);
248     if (total_length == 0) {
249         return FALSE;
250     }
251 
252     fp_mux_info = (fp_mux_info_t* )p_get_proto_data(wmem_file_scope(), pinfo, proto_fp_mux, 0);
253     if (fp_mux_info) {
254         if (fp_mux_info->srcport == pinfo->srcport &&
255             fp_mux_info->destport == pinfo->destport) {
256             /* Already framed as FP Mux*/
257             dissect_fp_mux(tvb, pinfo, tree, data);
258             return TRUE;
259         }
260         else {
261             return FALSE;
262         }
263     }
264 
265     while(offset < total_length)
266     {
267         if(total_length < offset + 2) {
268             return FALSE;
269         }
270         ext_flag = ((tvb_get_guint8(tvb, offset + 2)&0x80)==0x80);
271         header_length = ext_flag ? 4 : 3;
272 
273         if(total_length < offset + header_length) {
274             return FALSE;
275         }
276 
277         offset = offset + 2; /* Skipping UID */
278         if(ext_flag) {
279             /* Extended - Length is 15 bits */
280             length = tvb_get_ntohs(tvb, offset) & 0x7FFF;
281             length_field_size = 2;
282         }
283         else {
284             /* Not extended - Length is 7 bits */
285             length = tvb_get_guint8(tvb, offset) & 0x7F;
286             length_field_size = 1;
287         }
288 
289         if(length < 3) { /* Minimal FP frame length is 3 bytes*/
290             return FALSE;
291         }
292 
293         offset += length_field_size;
294         offset += length;
295 
296         chunks++;
297     }
298 
299     if(offset > total_length) {
300         return FALSE;
301     }
302 
303     if(chunks == 1) {
304         /* Might be coincidental, let's hope other packets with more payloads arrive */
305         return FALSE;
306     }
307 
308     /* This is FP Mux! */
309     /* Set conversation dissector and dissect */
310     conversation = find_or_create_conversation(pinfo);
311     conversation_set_dissector(conversation, fp_mux_handle);
312     dissect_fp_mux(tvb, pinfo, tree, data);
313 
314     return TRUE;
315 }
316 
317 
318 void
proto_register_fp_mux(void)319 proto_register_fp_mux(void)
320 {
321     module_t *fp_mux_module;
322     expert_module_t* expert_fp_mux;
323 
324     static hf_register_info hf[] = {
325         { &hf_fpmux_uid, { "User Identifier", "fp_mux.uid", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
326         { &hf_fpmux_extension_flag, { "Extension", "fp_mux.ef", FT_BOOLEAN, BASE_NONE, TFS(&fpmux_extension_flag_vals), 0, "Extension Flag", HFILL } },
327         { &hf_fpmux_length, { "Length", "fp_mux.length", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
328     };
329 
330     static gint *ett[] = {
331         &ett_fpmux
332     };
333 
334     static ei_register_info ei[] = {
335          { &ei_fpm_length_needlessly_extended, { "fp_mux.needlessly_extended_length", PI_PROTOCOL, PI_WARN, "Length field needlessly extended", EXPFILL }},
336          { &ei_fpm_too_many_payloads, { "fp_mux.too_many_payloads", PI_PROTOCOL, PI_ERROR, "Too many FP packets muxed in a single packet", EXPFILL }},
337          { &ei_fpm_bad_length, { "fp_mux.bad_length", PI_PROTOCOL, PI_ERROR, "Bad length", EXPFILL }},
338     };
339 
340     /* Register protocol */
341     proto_fp_mux = proto_register_protocol("Huawei FP Multiplexing Header", "FP Mux", "fp_mux");
342     fp_mux_handle =register_dissector("fp_mux", dissect_fp_mux, proto_fp_mux);
343 
344     proto_register_field_array(proto_fp_mux, hf, array_length(hf));
345     proto_register_subtree_array(ett, array_length(ett));
346     expert_fp_mux = expert_register_protocol(proto_fp_mux);
347     expert_register_field_array(expert_fp_mux, ei, array_length(ei));
348 
349     /* Register heuristic table */
350     heur_subdissector_list = register_heur_dissector_list("fp_mux", proto_fp_mux);
351 
352     /* Register configuration preferences */
353     fp_mux_module = prefs_register_protocol(proto_fp_mux, NULL);
354     prefs_register_bool_preference(fp_mux_module, "uid_in_tree",
355                                  "Show UID in protocol tree",
356                                  "Whether the UID value should be appended in the protocol tree",
357                                  &fp_mux_uid_in_tree);
358     prefs_register_bool_preference(fp_mux_module, "call_heur_fp",
359                                  "Call Heuristic FP Dissectors",
360                                  "Whether to try heuristic FP dissectors for the muxed payloads",
361                                  &call_fp_heur);
362 }
363 
364 void
proto_reg_handoff_fp_mux(void)365 proto_reg_handoff_fp_mux(void)
366 {
367     dissector_add_uint_range_with_preference("udp.port", "", fp_mux_handle);
368     heur_dissector_add("udp", heur_dissect_fp_mux, "FP Mux over UDP", "fp_mux_udp", proto_fp_mux, HEURISTIC_DISABLE);
369 }
370 
371 
372 /*
373  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
374  *
375  * Local variables:
376  * c-basic-offset: 4
377  * tab-width: 8
378  * indent-tabs-mode: nil
379  * End:
380  *
381  * vi: set shiftwidth=4 tabstop=8 expandtab:
382  * :indentSize=4:tabSize=8:noTabs=true:
383  */
384