aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-gsm_cbch.c
diff options
context:
space:
mode:
authorAnders Broman <anders.broman@ericsson.com>2012-01-26 19:51:48 +0000
committerAnders Broman <anders.broman@ericsson.com>2012-01-26 19:51:48 +0000
commite1d434ee6a56f86ad9e79e2f127382e3bc4409e6 (patch)
tree60dfbfa55e0b364d2caae8193486767a61fe8baf /epan/dissectors/packet-gsm_cbch.c
parent6981aa2c2a465247d0b418d5d6c42325b5a595ad (diff)
From Mike Morrin:
Add dissectors for GSM and UMTS Cell Broadcast protocols. ( - the patch for gsmtap ) https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=6770 svn path=/trunk/; revision=40735
Diffstat (limited to 'epan/dissectors/packet-gsm_cbch.c')
-rw-r--r--epan/dissectors/packet-gsm_cbch.c585
1 files changed, 585 insertions, 0 deletions
diff --git a/epan/dissectors/packet-gsm_cbch.c b/epan/dissectors/packet-gsm_cbch.c
new file mode 100644
index 0000000000..26c47df9d4
--- /dev/null
+++ b/epan/dissectors/packet-gsm_cbch.c
@@ -0,0 +1,585 @@
+/* packet-gsm_cbch.c
+ * Routines for GSM CBCH dissection - A.K.A. 3GPP 44.012 (GSM 04.12)
+ *
+ * Copyright 2011, Mike Morrin <mike.morrin [AT] ipaccess.com>
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+#include <stdlib.h>
+
+#include <string.h>
+
+#include <epan/packet.h>
+#include <epan/reassemble.h>
+#include "packet-cell_broadcast.h"
+
+#define CBCH_FRAGMENT_SIZE 22
+
+const value_string block_type_lpd_strings[] = {
+ { 0x00, "NOT Cell Broadcast"},
+ { 0x01, "Cell Broadcast"},
+ { 0x02, "NOT Cell Broadcast"},
+ { 0x03, "NOT Cell Broadcast"},
+ { 0, NULL}
+};
+
+const value_string block_type_seq_num_values[] = {
+ { 0x00, "First Block"},
+ { 0x01, "Second Block"},
+ { 0x02, "Third Block"},
+ { 0x03, "Fourth Block"},
+ { 0x08, "First Schedule Block"},
+ { 0xFF, "Null message"},
+ { 0, NULL}
+};
+
+const value_string sched_type_values[] = {
+ { 0x00, "messages formatted as specified in subclause 3.5 of 3GPP 44.012"},
+ { 0xFF, "Unknown schedule message format"},
+ { 0, NULL}
+};
+
+/* Initialize the protocol and registered fields */
+static int proto_cbch = -1;
+
+static int hf_gsm_cbch_spare_bit = -1;
+static int hf_gsm_cbch_lpd = -1;
+static int hf_gsm_cbch_lb = -1;
+static int hf_gsm_cbch_seq_num = -1;
+static int hf_gsm_cbch_sched_type = -1;
+static int hf_gsm_cbch_sched_begin_slot = -1;
+static int hf_gsm_cbch_sched_spare = -1;
+static int hf_gsm_cbch_sched_end_slot = -1;
+static int hf_gsm_cbch_sched_msg_id = -1;
+
+/* These fields are used when reassembling cbch fragments
+*/
+static int hf_cbch_fragments = -1;
+static int hf_cbch_fragment = -1;
+static int hf_cbch_fragment_overlap = -1;
+static int hf_cbch_fragment_overlap_conflict = -1;
+static int hf_cbch_fragment_multiple_tails = -1;
+static int hf_cbch_fragment_too_long_fragment = -1;
+static int hf_cbch_fragment_error = -1;
+static int hf_cbch_fragment_count = -1;
+static int hf_cbch_reassembled_in = -1;
+static int hf_cbch_reassembled_length = -1;
+
+/* Initialize the subtree pointers */
+static gint ett_cbch_msg = -1;
+static gint ett_schedule_msg = -1;
+static gint ett_schedule_new_msg = -1;
+static gint ett_cbch_fragment = -1;
+static gint ett_cbch_fragments = -1;
+
+static dissector_handle_t data_handle;
+static dissector_handle_t cbs_handle;
+
+/* reassembly of CHCH blocks */
+static GHashTable *fragment_block_table = NULL;
+static GHashTable *reassembled_message_table = NULL;
+
+/* Structure needed for the fragmentation routines in reassemble.c
+*/
+static const fragment_items cbch_frag_items = {
+ &ett_cbch_fragment,
+ &ett_cbch_fragments,
+ &hf_cbch_fragments,
+ &hf_cbch_fragment,
+ &hf_cbch_fragment_overlap,
+ &hf_cbch_fragment_overlap_conflict,
+ &hf_cbch_fragment_multiple_tails,
+ &hf_cbch_fragment_too_long_fragment,
+ &hf_cbch_fragment_error,
+ &hf_cbch_fragment_count,
+ &hf_cbch_reassembled_in,
+ &hf_cbch_reassembled_length,
+ "blocks"
+};
+
+static void cbch_defragment_init(void)
+{
+ fragment_table_init(&fragment_block_table);
+ reassembled_table_init(&reassembled_message_table);
+}
+
+static void dissect_schedule_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *top_tree)
+{
+ guint8 octet1, i, j, k=0, len, sched_begin, sched_end, new_slots[48] , offset = 0;
+ gboolean valid_message = TRUE;
+ guint16 other_slots[48];
+ proto_item *item = NULL, *schedule_item = NULL;
+ proto_tree *sched_tree = NULL, *sched_subtree = NULL;
+
+ len = tvb_length(tvb);
+
+ col_append_str(pinfo->cinfo, COL_INFO, " CBCH Schedule Message ");
+
+ schedule_item = proto_tree_add_protocol_format(top_tree, proto_cbch, tvb, 0, len,
+ "GSM CBCH Schedule Message");
+
+ sched_tree = proto_item_add_subtree(schedule_item, ett_schedule_msg);
+
+ proto_tree_add_item(sched_tree, hf_gsm_cbch_sched_type, tvb, offset, 1, ENC_BIG_ENDIAN);
+ octet1 = tvb_get_guint8(tvb, offset);
+ if (0 == (octet1 & 0xC0))
+ {
+ sched_begin = octet1 & 0x3F;
+ proto_tree_add_item(sched_tree, hf_gsm_cbch_sched_begin_slot, tvb, offset++, 1, ENC_BIG_ENDIAN);
+ if (1 == sched_begin)
+ {
+ proto_tree_add_text(sched_tree, tvb, offset - 1, 1, "(apparently) Scheduled Scheduling Message");
+ }
+ else if ((2 <= sched_begin) && (48 >= sched_begin))
+ {
+ proto_tree_add_text(sched_tree, tvb, offset - 1, 1, "(apparently) Unscheduled Scheduling Message");
+ }
+ else
+ {
+ proto_tree_add_text(sched_tree, tvb, offset - 1, 1, "Begin Slot Number out of range: ignoring message");
+ valid_message = FALSE;
+ }
+ proto_tree_add_item(sched_tree, hf_gsm_cbch_sched_spare, tvb, offset, 1, ENC_BIG_ENDIAN);
+ sched_end = tvb_get_guint8(tvb, offset);
+ proto_tree_add_item(sched_tree, hf_gsm_cbch_sched_end_slot, tvb, offset++, 1, ENC_BIG_ENDIAN);
+ if (sched_end < sched_begin)
+ {
+ proto_tree_add_text(sched_tree, tvb, offset - 1, 1, "End Slot Number less than Begin Slot Number: ignoring message");
+ valid_message = FALSE;
+ }
+
+ if (valid_message)
+ {
+ /* build an array of new messages */
+ memset(&new_slots, 0xFF, sizeof(new_slots));
+ memset(&other_slots, 0xFF, sizeof(other_slots));
+
+ /* iterate over the octets */
+ for (i=0; i<6; i++)
+ {
+ octet1 = tvb_get_guint8(tvb, offset++);
+
+ /* iterate over the bits */
+ for (j=0; j<8; j++)
+ {
+ if (octet1 & (0x80>>j))
+ {
+ new_slots[k++] = (i<<3) + j + 1;
+ }
+ }
+ }
+ /* print the array of new messages */
+ item = proto_tree_add_text(sched_tree, tvb, offset-6, 6, "This schedule contains %d slots with new messages", k);
+ sched_subtree = proto_item_add_subtree(item, ett_schedule_new_msg);
+ for (i=0; i<k; i++)
+ {
+ DISSECTOR_ASSERT(new_slots[i] < 48);
+ octet1 = tvb_get_guint8(tvb, offset);
+ if ((octet1 & 0x80) == 0x80)
+ {
+ /* MDT 1 */
+ guint8 octet2;
+ guint16 msg_id;
+
+ octet2 = tvb_get_guint8(tvb, offset + 1);
+ msg_id = ((octet1 &0x7F) << 8) + octet2;
+ proto_tree_add_text(sched_subtree, tvb, offset, 2,
+ "Slot: %d, Message ID: %d, First transmission of an SMSCB within the Schedule Period",
+ new_slots[i], msg_id);
+ offset +=2;
+ other_slots[new_slots[i] - 1] = msg_id;
+ }
+ else if ((octet1 & 0xC0) == 0)
+ {
+ /* MDT 00 */
+ if (octet1 < new_slots[i])
+ {
+ proto_tree_add_text(sched_subtree, tvb, offset++, 1,
+ "Slot: %d, Message ID: %d, Repeat of Slot %d",
+ new_slots[i], other_slots[octet1 - 1], octet1);
+ other_slots[new_slots[i] - 1] = other_slots[octet1 - 1];
+ }
+ else
+ {
+ proto_tree_add_text(sched_subtree, tvb, offset++, 1,
+ "Slot: %d, Apparent forward reference to slot %d",
+ new_slots[i], octet1);
+ }
+ }
+ else if ((octet1 == 0x40))
+ {
+ /* MDT 010000000 */
+ proto_tree_add_text(sched_subtree, tvb, offset++, 1, "Slot: %d Free Message Slot, optional reading", new_slots[k]);
+ other_slots[new_slots[i] - 1] = 0xFFFE;
+ }
+ else if ((octet1 == 0x41))
+ {
+ /* MDT 010000001 */
+ proto_tree_add_text(sched_subtree, tvb, offset++, 1, "Slot: %d Free Message Slot, reading advised", new_slots[k]);
+ other_slots[new_slots[i] - 1] = 0xFFFE;
+ }
+ else
+ {
+ /* reserved MDT */
+ proto_tree_add_text(sched_subtree, tvb, offset, 1, "Slot: %d reseved MDT: %x", new_slots[k], octet1);
+ other_slots[new_slots[i] - 1] = 0xFFFE;
+ }
+ }
+ proto_item_set_end(item, tvb, offset);
+
+ /* print schedule of other messages */
+ item = proto_tree_add_text(sched_tree, tvb, offset, 0, "Other message slots in this schedule");
+ sched_subtree = proto_item_add_subtree(item, ett_schedule_new_msg);
+ for (k=0; offset<len; j++)
+ {
+ while ((other_slots[k]!=0xFFFF) && (k<sched_end))
+ {
+ k++;
+ }
+ if (k >= sched_end)
+ break;
+
+ octet1 = tvb_get_guint8(tvb, offset);
+ if ((octet1 & 0x80) == 0x80)
+ {
+ if ((offset+1)<len)
+ {
+ /* MDT 1 */
+ guint8 octet2;
+ guint16 msg_id;
+
+ octet2 = tvb_get_guint8(tvb, offset + 1);
+ msg_id = ((octet1 &0x7F) << 8) + octet2;
+ other_slots[k] = msg_id;
+ proto_tree_add_text(sched_subtree, tvb, offset, 2,
+ "Slot: %d, Message: %d, First transmission of an SMSCB within the Schedule Period",
+ ++k, msg_id);
+ offset +=2;
+ }
+ }
+ else if ((octet1 & 0xC0) == 0)
+ {
+ /* MDT 00 */
+ if (octet1 < k)
+ {
+ other_slots[k] = other_slots[octet1 - 1];
+ proto_tree_add_text(sched_subtree, tvb, offset++, 1,
+ "Slot: %d, Message ID: %d, Repeat of Slot %d",
+ ++k, other_slots[octet1 - 1], octet1);
+ }
+ else
+ {
+ proto_tree_add_text(sched_subtree, tvb, offset++, 1,
+ "Slot: %d, Apparent forward reference to slot %d",
+ ++k, octet1);
+ }
+ }
+ else if ((octet1 == 0x40))
+ {
+ /* MDT 010000000 */
+ proto_tree_add_text(sched_subtree, tvb, offset++, 1, "Slot: %d Free Message Slot, optional reading", ++k);
+ }
+ else if ((octet1 == 0x41))
+ {
+ /* MDT 010000001 */
+ proto_tree_add_text(sched_subtree, tvb, offset++, 1, "Slot: %d Free Message Slot, reading advised", ++k);
+ }
+ else
+ {
+ /* reserved MDT */
+ proto_tree_add_text(sched_subtree, tvb, offset, 1, "Slot: %d reserved MDT: %x", ++k, octet1);
+ }
+ }
+ proto_item_set_end(item, tvb, offset);
+ proto_tree_add_text(sched_tree, tvb, offset, -1, "Padding");
+ }
+ }
+}
+
+static void
+dissect_cbch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ fragment_data * frag_data = NULL;
+ guint8 octet, lb, lpd, seq_num;
+ guint32 offset;
+ guint32 len;
+ proto_item *cbch_item = NULL;
+ proto_tree *cbch_tree = NULL;
+ tvbuff_t *reass_tvb = NULL, *msg_tvb = NULL;
+
+ len = tvb_length(tvb);
+ offset = 0;
+ octet = tvb_get_guint8(tvb, offset);
+
+ /*
+ * create the protocol tree
+ */
+ cbch_item = proto_tree_add_protocol_format(tree, proto_cbch, tvb, 0, len,
+ "GSM CBCH - Block (0x%02x)", octet&3);
+
+ col_append_str(pinfo->cinfo, COL_PROTOCOL, " CBCH");
+
+ cbch_tree = proto_item_add_subtree(cbch_item, ett_cbch_msg);
+
+ proto_tree_add_text(cbch_tree, tvb, offset, 1,
+ "CBCH Block");
+
+ proto_tree_add_uint(cbch_tree, hf_gsm_cbch_spare_bit, tvb, offset, 1, octet);
+ proto_tree_add_uint(cbch_tree, hf_gsm_cbch_lpd, tvb, offset, 1, octet);
+ proto_tree_add_uint(cbch_tree, hf_gsm_cbch_lb, tvb, offset, 1, octet);
+ proto_tree_add_uint(cbch_tree, hf_gsm_cbch_seq_num, tvb, offset, 1, octet);
+ seq_num = octet & 0x0F;
+ lpd = (octet & 0x60) >> 5;
+ lb = (octet & 0x10) >> 4;
+
+ if (lpd == 1)
+ {
+ switch (seq_num)
+ {
+ case 0x00:
+ case 0x08:
+ pinfo->fragmented = TRUE;
+ /* we should have a unique ID for the reassembled page, but we don't really have anything from the protocol...
+ The GSM frame number div 4 might be used for this, but it has not been passed to this layer and does not
+ exist at all if the message is being passed over the RSL interface.
+ So we just use 0... */
+
+ /* after reassembly we will need to know if this is a scheduling message,
+ this information is carried in the initial sequence number, not the payload,
+ so we prepend the reassembly with the octet containing the initial sequence number
+ to allow later dissection of the payload */
+ frag_data = fragment_add_seq_check(tvb, offset, pinfo, 0,
+ fragment_block_table, reassembled_message_table,
+ seq_num & 0x03, CBCH_FRAGMENT_SIZE + 1, !lb);
+ reass_tvb = process_reassembled_data(tvb, offset, pinfo, "Reassembled CBCH message",
+ frag_data, &cbch_frag_items, NULL, cbch_tree);
+ break;
+
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ pinfo->fragmented = TRUE;
+ offset++; /* step to beginning of payload */
+ frag_data = fragment_add_seq_check(tvb, offset, pinfo, 0,
+ fragment_block_table, reassembled_message_table, seq_num,
+ CBCH_FRAGMENT_SIZE, !lb);
+ reass_tvb = process_reassembled_data(tvb, offset, pinfo, "Reassembled CBCH message",
+ frag_data, &cbch_frag_items, NULL, cbch_tree);
+ break;
+
+ case 0x0F:
+ proto_tree_add_text(cbch_tree, tvb, offset, 1, "NULL message");
+ call_dissector(data_handle, tvb, pinfo, cbch_tree);
+ break;
+
+ default:
+ proto_tree_add_text(cbch_tree, tvb, offset, 1, "reserved Sequence Number");
+ call_dissector(data_handle, tvb, pinfo, cbch_tree);
+ break;
+ }
+ if (reass_tvb)
+ {
+ /* Reassembled */
+
+ /* the tvb contains the reassmbled message prepended with the sequence number octet from the first block
+ We use this to determine whether this is a normal message or a scheduling message */
+ offset = 0;
+
+ octet = tvb_get_guint8(reass_tvb, offset++);
+ msg_tvb = tvb_new_subset_remaining(reass_tvb, offset);
+
+ if (octet & 0x08)
+ {
+ dissect_schedule_message(msg_tvb, pinfo, tree);
+ }
+ else
+ {
+ call_dissector(cbs_handle, msg_tvb, pinfo, tree);
+ }
+ }
+ }
+ else
+ {
+ proto_tree_add_text(cbch_tree, tvb, offset, 1, "invalid Link Protocol Discriminator");
+ call_dissector(data_handle, tvb, pinfo, cbch_tree);
+ }
+}
+
+/* Register the protocol with Wireshark */
+void
+proto_register_gsm_cbch(void)
+{
+ /* Setup list of header fields */
+ static hf_register_info hf_smscb[] =
+ {
+ { &hf_gsm_cbch_spare_bit,
+ { "GSM CBCH spare bit", "gsm_cbch_block_type.spare",
+ FT_UINT8, BASE_HEX, NULL, 0x80,
+ NULL, HFILL}
+ },
+ { &hf_gsm_cbch_lpd,
+ { "GSM CBCH Link Protocol Discriminator", "gsm_cbch_block_type.lpd",
+ FT_UINT8, BASE_DEC, VALS(block_type_lpd_strings), 0x60,
+ NULL, HFILL}
+ },
+ { &hf_gsm_cbch_lb,
+ { "GSM CBCH Last Block", "gsm_cbch_block_type.lb",
+ FT_UINT8, BASE_DEC, NULL, 0x10,
+ "Last Block", HFILL}
+ },
+ { &hf_gsm_cbch_seq_num,
+ { "GSM CBCH Sequence Number", "gsm_cbch_block_type.seq_num",
+ FT_UINT8, BASE_DEC, VALS(block_type_seq_num_values), 0x0F,
+ NULL, HFILL}
+ },
+ { &hf_gsm_cbch_sched_type,
+ { "GSM CBCH Schedule Type", "gsm_cbch.sched_type",
+ FT_UINT8, BASE_DEC, VALS(sched_type_values), 0xC0,
+ NULL, HFILL}
+ },
+ { &hf_gsm_cbch_sched_begin_slot,
+ { "GSM CBCH Schedule Begin slot", "gsm_cbch.schedule_begin",
+ FT_UINT8, BASE_DEC, NULL, 0x3F,
+ NULL, HFILL}
+ },
+ { &hf_gsm_cbch_sched_spare,
+ { "GSM CBCH Schedule Spare Bits", "gsm_cbch.sched_spare",
+ FT_UINT8, BASE_DEC, NULL, 0xC0,
+ NULL, HFILL}
+ },
+ { &hf_gsm_cbch_sched_end_slot,
+ { "GSM CBCH Schedule End Slot", "gsm_cbch.sched_end",
+ FT_UINT8, BASE_DEC, NULL, 0x3F,
+ NULL, HFILL}
+ },
+ { &hf_gsm_cbch_sched_msg_id,
+ { "GSM CBCH Schedule Message ID", "gsm_cbch.sched_msg_id",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ NULL, HFILL}
+ },
+ /* Fragment fields
+ */
+ { &hf_cbch_fragment_overlap,
+ { "Fragment overlap",
+ "gsm_cbch.fragment.overlap",
+ FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Fragment overlaps with other fragments", HFILL
+ }
+ },
+ { &hf_cbch_fragment_overlap_conflict,
+ { "Conflicting data in fragment overlap",
+ "gsm_cbch.fragment.overlap.conflict",
+ FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Overlapping fragments contained conflicting data", HFILL
+ }
+ },
+ { &hf_cbch_fragment_multiple_tails,
+ { "Multiple tail fragments found",
+ "gsm_cbch.fragment.multipletails",
+ FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Several tails were found when defragmenting the packet", HFILL
+ }
+ },
+ { &hf_cbch_fragment_too_long_fragment,
+ { "Fragment too long",
+ "gsm_cbch.fragment.toolongfragment",
+ FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+ "Fragment contained data past end of packet", HFILL
+ }
+ },
+ { &hf_cbch_fragment_error,
+ { "Defragmentation error",
+ "gsm_cbch.fragment.error",
+ FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+ "Defragmentation error due to illegal fragments", HFILL
+ }
+ },
+ { &hf_cbch_fragment_count,
+ { "Fragmentation count",
+ "gsm_cbch.fragment.count",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Count of CBCH Fragments", HFILL
+ }
+ },
+ { &hf_cbch_reassembled_in,
+ { "Reassembled in",
+ "gsm_cbch.reassembled.in",
+ FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+ "CBCH fragments are reassembled in the given packet", HFILL
+ }
+ },
+ { &hf_cbch_reassembled_length,
+ { "Reassembled message length is one less than indicated here",
+ "gsm_cbch.reassembled.length",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "The total length of the reassembled message", HFILL
+ }
+ },
+ { &hf_cbch_fragment,
+ { "CBCH Fragment",
+ "gsm_cbch.fragment",
+ FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+ NULL, HFILL
+ }
+ },
+ { &hf_cbch_fragments,
+ { "CBCH Fragments",
+ "gsm_cbch.fragments",
+ FT_NONE, BASE_NONE, NULL, 0x0,
+ NULL, HFILL
+ }
+ }
+ };
+
+/* Setup protocol subtree array */
+ static gint *ett[] = {
+ &ett_cbch_msg,
+ &ett_schedule_msg,
+ &ett_schedule_new_msg,
+ &ett_cbch_fragment,
+ &ett_cbch_fragments,
+ };
+
+ /* Register the protocol name and description */
+ proto_cbch =
+ proto_register_protocol("GSM Cell Broadcast Channel", "GSM CBCH", "gsm_cbch");
+
+ proto_register_field_array(proto_cbch, hf_smscb, array_length(hf_smscb));
+
+ /* subdissector code */
+ register_dissector("gsm_cbch", dissect_cbch, proto_cbch);
+ register_init_routine(cbch_defragment_init);
+
+ /* subtree array */
+ proto_register_subtree_array(ett, array_length(ett));
+}
+
+void
+proto_reg_handoff_gsm_cbch(void)
+{
+ data_handle = find_dissector("data");
+ cbs_handle = find_dissector("gsm_cell_broadcast");
+}