1 /* Routines for Huawei's FP Mux Header disassembly
2 * Protocol reference: EU Patent publication No. EP2053798
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
13 #include <epan/packet.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"
24 extern int proto_umts_mac
;
25 extern int proto_umts_rlc
;
27 void proto_register_fp_mux(void);
28 void proto_reg_handoff_fp_mux(void);
30 static int proto_fp_mux
;
31 static dissector_handle_t fp_mux_handle
;
32 static heur_dissector_list_t heur_subdissector_list
;
35 #define MAX_PAYLOADS 64
41 static int hf_fpmux_uid
;
42 static int hf_fpmux_extension_flag
;
43 static int hf_fpmux_length
;
46 static expert_field ei_fpm_length_needlessly_extended
;
47 static expert_field ei_fpm_too_many_payloads
;
48 static expert_field ei_fpm_bad_length
;
51 /* Place UID in proto tree */
52 static bool fp_mux_uid_in_tree
= true;
53 /* Call heuristic FP dissectors on payload */
54 static bool call_fp_heur
= true;
57 static const true_false_string fpmux_extension_flag_vals
= {
58 "Extension Present", "No Extension"
63 typedef struct fp_mux_info_t
{
66 fp_info
* fpinfos
[MAX_PAYLOADS
];
67 umts_mac_info
* macinfos
[MAX_PAYLOADS
];
68 rlc_info
* rlcinfos
[MAX_PAYLOADS
];
71 static void dissect_payload(tvbuff_t
*next_tvb
, packet_info
*pinfo
, proto_tree
*tree
, struct fp_mux_info_t
* fp_mux_info
, uint16_t payload_index
, uint16_t uid
)
73 heur_dtbl_entry_t
*hdtbl_entry
;
74 bool conv_dissected
; /* If the TVB was dissected using the conversation dissector*/
75 bool heur_dissected
; /* If the TVB was dissected using a heuristic dissector*/
76 uint32_t current_destport
,current_srcport
;
78 /* Saving old ports */
79 current_destport
= pinfo
->destport
;
80 current_srcport
= pinfo
->srcport
;
82 /* Replacing ports with UID (ports are used by the FP dissector) */
83 pinfo
->destport
= uid
;
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
]);
91 /* Trying a dissector assigned to the conversation (Usually from NBAP) */
92 conv_dissected
= try_conversation_dissector(&pinfo
->dst
, &pinfo
->src
, CONVERSATION_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 */
97 /* Trying heuristic dissector */
98 heur_dissected
= dissector_try_heuristic(heur_subdissector_list
, next_tvb
, pinfo
, tree
, &hdtbl_entry
, NULL
);
100 /* No heuristic dissector / TVB was rejected, show as data */
101 call_data_dissector(next_tvb
,pinfo
,tree
);
105 /* Trying heuristic dissectors disabled, show as data */
106 call_data_dissector(next_tvb
,pinfo
,tree
);
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);
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);
121 /* Setting a fence in the info column to aggregate all payloads' descriptions */
122 const char* 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
);
129 /* Restoring ports */
130 pinfo
->destport
= current_destport
;
131 pinfo
->srcport
= current_srcport
;
134 static int dissect_fp_mux(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
138 uint8_t length_field_size
;
140 uint32_t header_length
;
141 uint32_t total_length
;
143 uint32_t out_value
= 0;
144 uint32_t payload_index
= 0;
147 proto_tree
*fpmux_tree
= NULL
;
148 struct fp_mux_info_t
* fp_mux_info
;
150 total_length
= tvb_captured_length(tvb
);
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);
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
);
163 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "FP Mux");
164 col_clear(pinfo
->cinfo
, COL_INFO
);
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;
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
);
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
= (uint16_t)out_value
;
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
);
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);
187 /* Adding Length field */
189 /* Extended - Length is 15 bits */
190 length
= tvb_get_ntohs(tvb
, offset
) & 0x7FFF;
191 length_field_size
= 2;
194 /* Not extended - Length is 7 bits */
195 length
= tvb_get_uint8(tvb
, offset
) & 0x7F;
196 length_field_size
= 1;
198 proto_tree_add_uint(fpmux_tree
, hf_fpmux_length
, tvb
, offset
, length_field_size
, length
);
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");
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
));
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
);
215 offset
+= length_field_size
;
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
);
225 dissect_payload(next_tvb
,pinfo
,tree
,fp_mux_info
,payload_index
,uid
);
235 static bool heur_dissect_fp_mux(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
238 uint8_t length_field_size
;
240 uint32_t header_length
;
241 uint32_t total_length
;
244 conversation_t
*conversation
;
245 struct fp_mux_info_t
* fp_mux_info
;
247 total_length
= tvb_captured_length(tvb
);
248 if (total_length
== 0) {
252 fp_mux_info
= (fp_mux_info_t
* )p_get_proto_data(wmem_file_scope(), pinfo
, proto_fp_mux
, 0);
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
);
265 while(offset
< total_length
)
267 if(total_length
< offset
+ 2) {
270 ext_flag
= ((tvb_get_uint8(tvb
, offset
+ 2)&0x80)==0x80);
271 header_length
= ext_flag
? 4 : 3;
273 if(total_length
< offset
+ header_length
) {
277 offset
= offset
+ 2; /* Skipping UID */
279 /* Extended - Length is 15 bits */
280 length
= tvb_get_ntohs(tvb
, offset
) & 0x7FFF;
281 length_field_size
= 2;
284 /* Not extended - Length is 7 bits */
285 length
= tvb_get_uint8(tvb
, offset
) & 0x7F;
286 length_field_size
= 1;
289 if(length
< 3) { /* Minimal FP frame length is 3 bytes*/
293 offset
+= length_field_size
;
299 if(offset
> total_length
) {
304 /* Might be coincidental, let's hope other packets with more payloads arrive */
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
);
319 proto_register_fp_mux(void)
321 module_t
*fp_mux_module
;
322 expert_module_t
* expert_fp_mux
;
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
} },
330 static int *ett
[] = {
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
}},
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
);
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
));
349 /* Register heuristic table */
350 heur_subdissector_list
= register_heur_dissector_list_with_description("fp_mux", "Huawei FP Mux payload", proto_fp_mux
);
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",
365 proto_reg_handoff_fp_mux(void)
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
);
373 * Editor modelines - https://www.wireshark.org/tools/modelines.html
378 * indent-tabs-mode: nil
381 * vi: set shiftwidth=4 tabstop=8 expandtab:
382 * :indentSize=4:tabSize=8:noTabs=true: