convert to proto_tree_add_subtree[_format]
[metze/wireshark/wip.git] / epan / dissectors / packet-gsm_cbch.c
1 /* packet-gsm_cbch.c
2  * Routines for GSM CBCH dissection - A.K.A. 3GPP 44.012 (GSM 04.12)
3  *
4  * Copyright 2011, Mike Morrin <mike.morrin [AT] ipaccess.com>
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24
25 #include "config.h"
26
27 #include <epan/packet.h>
28 #include <epan/reassemble.h>
29 #include "packet-cell_broadcast.h"
30
31 void proto_register_gsm_cbch(void);
32 void proto_reg_handoff_gsm_cbch(void);
33
34 #define CBCH_FRAGMENT_SIZE 22
35
36 const value_string block_type_lpd_strings[] = {
37     { 0x00, "NOT Cell Broadcast"},
38     { 0x01, "Cell Broadcast"},
39     { 0x02, "NOT Cell Broadcast"},
40     { 0x03, "NOT Cell Broadcast"},
41     {    0, NULL}
42 };
43
44 const value_string block_type_seq_num_values[] = {
45     { 0x00, "First Block"},
46     { 0x01, "Second Block"},
47     { 0x02, "Third Block"},
48     { 0x03, "Fourth Block"},
49     { 0x08, "First Schedule Block"},
50     { 0xFF, "Null message"},
51     {    0, NULL}
52 };
53
54 const value_string sched_type_values[] = {
55     { 0x00, "messages formatted as specified in subclause 3.5 of 3GPP 44.012"},
56     { 0xFF, "Unknown schedule message format"},
57     {    0, NULL}
58 };
59
60 /* Initialize the protocol and registered fields */
61 static int proto_cbch = -1;
62
63 static int hf_gsm_cbch_spare_bit = -1;
64 static int hf_gsm_cbch_lpd = -1;
65 static int hf_gsm_cbch_lb = -1;
66 static int hf_gsm_cbch_seq_num = -1;
67 static int hf_gsm_cbch_sched_type = -1;
68 static int hf_gsm_cbch_sched_begin_slot = -1;
69 static int hf_gsm_cbch_sched_spare = -1;
70 static int hf_gsm_cbch_sched_end_slot = -1;
71 static int hf_gsm_cbch_slot = -1;
72 /* static int hf_gsm_cbch_sched_msg_id = -1; */
73
74 /* These fields are used when reassembling cbch fragments
75  */
76 static int hf_cbch_fragments = -1;
77 static int hf_cbch_fragment = -1;
78 static int hf_cbch_fragment_overlap = -1;
79 static int hf_cbch_fragment_overlap_conflict = -1;
80 static int hf_cbch_fragment_multiple_tails = -1;
81 static int hf_cbch_fragment_too_long_fragment = -1;
82 static int hf_cbch_fragment_error = -1;
83 static int hf_cbch_fragment_count = -1;
84 static int hf_cbch_reassembled_in = -1;
85 static int hf_cbch_reassembled_length = -1;
86
87 /* Initialize the subtree pointers */
88 static gint ett_cbch_msg = -1;
89 static gint ett_schedule_msg = -1;
90 static gint ett_schedule_new_msg = -1;
91 static gint ett_cbch_fragment = -1;
92 static gint ett_cbch_fragments = -1;
93
94 static dissector_handle_t data_handle;
95 static dissector_handle_t cbs_handle;
96
97 /* reassembly of CHCH blocks */
98 static reassembly_table cbch_block_reassembly_table;
99
100 /* Structure needed for the fragmentation routines in reassemble.c
101  */
102 static const fragment_items cbch_frag_items = {
103     &ett_cbch_fragment,
104     &ett_cbch_fragments,
105     &hf_cbch_fragments,
106     &hf_cbch_fragment,
107     &hf_cbch_fragment_overlap,
108     &hf_cbch_fragment_overlap_conflict,
109     &hf_cbch_fragment_multiple_tails,
110     &hf_cbch_fragment_too_long_fragment,
111     &hf_cbch_fragment_error,
112     &hf_cbch_fragment_count,
113     &hf_cbch_reassembled_in,
114     &hf_cbch_reassembled_length,
115     /* Reassembled data field */
116     NULL,
117     "blocks"
118 };
119
120 static void
121 cbch_defragment_init(void)
122 {
123     reassembly_table_init(&cbch_block_reassembly_table,
124                           &addresses_reassembly_table_functions);
125 }
126
127 static void
128 dissect_schedule_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *top_tree)
129 {
130     guint       len, offset     = 0;
131     guint8      octet1, i, j, k = 0;
132     guint8      sched_begin, sched_end, new_slots[48];
133     gboolean    valid_message   = TRUE;
134     guint16     other_slots[48];
135     proto_item *item            = NULL, *schedule_item = NULL;
136     proto_tree *sched_tree      = NULL, *sched_subtree = NULL;
137
138     len = tvb_length(tvb);
139
140     col_append_str(pinfo->cinfo, COL_INFO, " CBCH Schedule Message ");
141
142     schedule_item = proto_tree_add_protocol_format(top_tree, proto_cbch, tvb, 0, len,
143                                                    "GSM CBCH Schedule Message");
144
145     sched_tree = proto_item_add_subtree(schedule_item, ett_schedule_msg);
146
147     proto_tree_add_item(sched_tree, hf_gsm_cbch_sched_type, tvb, offset, 1, ENC_BIG_ENDIAN);
148     octet1 = tvb_get_guint8(tvb, offset);
149     if (0 == (octet1 & 0xC0))
150     {
151         sched_begin = octet1 & 0x3F;
152         proto_tree_add_item(sched_tree, hf_gsm_cbch_sched_begin_slot, tvb, offset++, 1, ENC_BIG_ENDIAN);
153         if (1 == sched_begin)
154         {
155             proto_tree_add_text(sched_tree, tvb, offset - 1, 1, "(apparently) Scheduled Scheduling Message");
156         }
157         else if ((2 <= sched_begin) && (48 >= sched_begin))
158         {
159             proto_tree_add_text(sched_tree, tvb, offset - 1, 1, "(apparently) Unscheduled Scheduling Message");
160         }
161         else
162         {
163             proto_tree_add_text(sched_tree, tvb, offset - 1, 1, "Begin Slot Number out of range: ignoring message");
164             valid_message = FALSE;
165         }
166         proto_tree_add_item(sched_tree, hf_gsm_cbch_sched_spare, tvb, offset, 1, ENC_BIG_ENDIAN);
167         sched_end = tvb_get_guint8(tvb, offset);
168         proto_tree_add_item(sched_tree, hf_gsm_cbch_sched_end_slot, tvb, offset++, 1, ENC_BIG_ENDIAN);
169         if (sched_end < sched_begin)
170         {
171             proto_tree_add_text(sched_tree, tvb, offset - 1, 1, "End Slot Number less than Begin Slot Number: ignoring message");
172             valid_message = FALSE;
173         }
174
175         if (valid_message)
176         {
177             /* build an array of new messages */
178             memset(&new_slots,   0xFF, sizeof(new_slots));
179             memset(&other_slots, 0xFF, sizeof(other_slots));
180
181             /* iterate over the octets */
182             for (i=0; i<6; i++)
183             {
184                 octet1 = tvb_get_guint8(tvb, offset++);
185
186                 /* iterate over the bits */
187                 for (j=0; j<8; j++)
188                 {
189                     if (octet1 & (0x80>>j))
190                     {
191                         new_slots[k++] = (i<<3) + j + 1;
192                     }
193                 }
194             }
195             /* print the array of new messages */
196             sched_subtree = proto_tree_add_subtree_format(sched_tree, tvb, offset-6, 6, ett_schedule_new_msg, &item,
197                                                             "This schedule contains %d slots with new messages", k);
198             for (i=0; i<k; i++)
199             {
200                 DISSECTOR_ASSERT(new_slots[i] <= 48);
201                 octet1 = tvb_get_guint8(tvb, offset);
202                 if ((octet1 & 0x80) == 0x80)
203                 {
204                     /* MDT 1 */
205                     guint8 octet2;
206                     guint16 msg_id;
207
208                     octet2 = tvb_get_guint8(tvb, offset + 1);
209                     msg_id = ((octet1 &0x7F) << 8) + octet2;
210                     proto_tree_add_uint_format_value(sched_subtree, hf_gsm_cbch_slot, tvb, offset, 2, new_slots[i],
211                                         "%d, Message ID: %d, First transmission of an SMSCB within the Schedule Period",
212                                         new_slots[i], msg_id);
213                     offset +=2;
214                     other_slots[new_slots[i] - 1] = msg_id;
215                 }
216                 else if ((octet1 & 0xC0) == 0)
217                 {
218                     /* MDT 00 */
219                     if (octet1 == 0)
220                     {
221                         proto_tree_add_uint_format_value(sched_subtree, hf_gsm_cbch_slot, tvb, offset++, 1, new_slots[i],
222                                             "%d, Repeat of non-existant slot %d",
223                                             new_slots[i], octet1);
224                     }
225                     else if (octet1 < new_slots[i])
226                     {
227                         proto_tree_add_uint_format_value(sched_subtree, hf_gsm_cbch_slot, tvb, offset++, 1, new_slots[i],
228                                             "%d, Message ID: %d, Repeat of Slot %d",
229                                             new_slots[i], other_slots[octet1 - 1], octet1);
230                         other_slots[new_slots[i] - 1] = other_slots[octet1 - 1];
231                     }
232                     else
233                     {
234                         proto_tree_add_uint_format_value(sched_subtree, hf_gsm_cbch_slot, tvb, offset++, 1, new_slots[i],
235                                             "%d, Apparent forward reference to slot %d",
236                                             new_slots[i], octet1);
237                     }
238                 }
239                 else if (octet1 == 0x40)
240                 {
241                     /* MDT 010000000 */
242                     proto_tree_add_uint_format_value(sched_subtree, hf_gsm_cbch_slot, tvb, offset++, 1, new_slots[k],
243                                     "%d Free Message Slot, optional reading", new_slots[k]);
244                     other_slots[new_slots[i] - 1] = 0xFFFE;
245                 }
246                 else if (octet1 == 0x41)
247                 {
248                     /* MDT 010000001 */
249                     proto_tree_add_uint_format_value(sched_subtree, hf_gsm_cbch_slot, tvb, offset++, 1, new_slots[k],
250                                      "%d Free Message Slot, reading advised", new_slots[k]);
251                     other_slots[new_slots[i] - 1] = 0xFFFE;
252                 }
253                 else
254                 {
255                     /* reserved MDT */
256                     proto_tree_add_uint_format_value(sched_subtree, hf_gsm_cbch_slot, tvb, offset, 1, new_slots[k],
257                                      "%d reserved MDT: %x", new_slots[k], octet1);
258                     other_slots[new_slots[i] - 1] = 0xFFFE;
259                 }
260             }
261             proto_item_set_end(item, tvb, offset);
262
263             /* print schedule of other messages */
264             sched_subtree = proto_tree_add_subtree(sched_tree, tvb, offset, 0,
265                                 ett_schedule_new_msg, &item, "Other message slots in this schedule");
266             for (k=0; offset < len; j++)
267             {
268                 /* XXX I don't know if a message can validly contain more than
269                  * 48 slots, but that's the size of the array we create so cap
270                  * it there to avoid uninitialized memory errors (see bug
271                  * https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9270) */
272                 if (sched_end > 48)
273                     sched_end = 48;
274                 while ((k<sched_end) && (other_slots[k]!=0xFFFF))
275                 {
276                     k++;
277                 }
278                 if (k >= sched_end)
279                     break;
280
281                 octet1 = tvb_get_guint8(tvb, offset);
282                 if ((octet1 & 0x80) == 0x80)
283                 {
284                     if ((offset+1)<len)
285                     {
286                         /* MDT 1 */
287                         guint8  octet2;
288                         guint16 msg_id;
289
290                         octet2 = tvb_get_guint8(tvb, offset + 1);
291                         msg_id = ((octet1 &0x7F) << 8) + octet2;
292                         other_slots[k] = msg_id;
293                         k++;
294                         proto_tree_add_uint_format_value(sched_subtree, hf_gsm_cbch_slot, tvb, offset, 2, k,
295                                             "%d, Message: %d, First transmission of an SMSCB within the Schedule Period",
296                                             k, msg_id);
297                         offset +=2;
298                     }
299                     else
300                     {
301                         /* I'm not sure what's supposed to be dissected in this
302                          * case. Perhaps just an expert info is appropriate?
303                          * Regardless, we need to increment k to prevent an
304                          * infinite loop, see
305                          * https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=8730
306                          */
307                         ++k;
308                     }
309                 }
310                 else if (octet1 && ((octet1 & 0xC0) == 0))
311                 {
312                     /* MDT 00 */
313                     if (octet1 < k)
314                     {
315                         other_slots[k] = other_slots[octet1 - 1];
316                         k++;
317                         proto_tree_add_uint_format_value(sched_subtree, hf_gsm_cbch_slot, tvb, offset++, 1, k,
318                                             "%d, Message ID: %d, Repeat of Slot %d",
319                                             k, other_slots[octet1 - 1], octet1);
320                     }
321                     else
322                     {
323                         k++;
324                         proto_tree_add_uint_format_value(sched_subtree, hf_gsm_cbch_slot, tvb, offset++, 1, k,
325                                             "%d, Apparent forward reference to slot %d",
326                                             k, octet1);
327                     }
328                 }
329                 else if (octet1 == 0x40)
330                 {
331                     /* MDT 010000000 */
332                     k++;
333                     proto_tree_add_uint_format_value(sched_subtree, hf_gsm_cbch_slot, tvb, offset++, 1, k,
334                             "%d Free Message Slot, optional reading", k);
335                 }
336                 else if (octet1 == 0x41)
337                 {
338                     /* MDT 010000001 */
339                     k++;
340                     proto_tree_add_uint_format_value(sched_subtree, hf_gsm_cbch_slot, tvb, offset++, 1, k,
341                             "%d Free Message Slot, reading advised", k);
342                 }
343                 else
344                 {
345                     /* reserved MDT */
346                     k++;
347                     proto_tree_add_uint_format_value(sched_subtree, hf_gsm_cbch_slot, tvb, offset, 1, k,
348                             "%d reserved MDT: %x", k, octet1);
349                 }
350             }
351             proto_item_set_end(item, tvb, offset);
352             proto_tree_add_text(sched_tree, tvb, offset, -1, "Padding");
353         }
354     }
355 }
356
357 static void
358 dissect_cbch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
359 {
360     fragment_head *frag_data = NULL;
361     guint8         octet, lb, lpd, seq_num;
362     guint32        offset;
363     guint32        len;
364     proto_item    *cbch_item = NULL;
365     proto_tree    *cbch_tree = NULL;
366     tvbuff_t      *reass_tvb = NULL, *msg_tvb = NULL;
367
368     len    = tvb_length(tvb);
369     offset = 0;
370     octet  = tvb_get_guint8(tvb, offset);
371
372     /*
373      * create the protocol tree
374      */
375     cbch_item = proto_tree_add_protocol_format(tree, proto_cbch, tvb, 0, len,
376                                                "GSM CBCH - Block (0x%02x)", octet&3);
377
378     col_append_str(pinfo->cinfo, COL_PROTOCOL, " CBCH");
379
380     cbch_tree = proto_item_add_subtree(cbch_item, ett_cbch_msg);
381
382     proto_tree_add_text(cbch_tree, tvb, offset, 1, "CBCH Block");
383
384     proto_tree_add_uint(cbch_tree, hf_gsm_cbch_spare_bit, tvb, offset, 1, octet);
385     proto_tree_add_uint(cbch_tree, hf_gsm_cbch_lpd,       tvb, offset, 1, octet);
386     proto_tree_add_uint(cbch_tree, hf_gsm_cbch_lb,        tvb, offset, 1, octet);
387     proto_tree_add_uint(cbch_tree, hf_gsm_cbch_seq_num,   tvb, offset, 1, octet);
388     seq_num =  octet & 0x0F;
389     lpd     = (octet & 0x60) >> 5;
390     lb      = (octet & 0x10) >> 4;
391
392     if (lpd == 1)
393     {
394         switch (seq_num)
395         {
396         case 0x00:
397         case 0x08:
398             pinfo->fragmented = TRUE;
399             /* we should have a unique ID for the reassembled page, but we don't really have anything from the protocol...
400                The GSM frame number div 4 might be used for this, but it has not been passed to this layer and does not
401                exist at all if the message is being passed over the RSL interface.
402                So we just use 0... */
403
404             /* after reassembly we will need to know if this is a scheduling message,
405                this information is carried in the initial sequence number, not the payload,
406                so we prepend the reassembly with the octet containing the initial sequence number
407                to allow later dissection of the payload */
408             frag_data = fragment_add_seq_check(&cbch_block_reassembly_table,
409                                                tvb, offset, pinfo, 0, NULL,
410                                                seq_num & 0x03, CBCH_FRAGMENT_SIZE + 1, !lb);
411             reass_tvb = process_reassembled_data(tvb, offset, pinfo, "Reassembled CBCH message",
412                                                  frag_data, &cbch_frag_items, NULL, cbch_tree);
413             break;
414
415         case 0x01:
416         case 0x02:
417         case 0x03:
418             pinfo->fragmented = TRUE;
419             offset++; /* step to beginning of payload */
420             frag_data = fragment_add_seq_check(&cbch_block_reassembly_table,
421                                                tvb, offset, pinfo, 0, NULL,
422                                                seq_num, CBCH_FRAGMENT_SIZE, !lb);
423             reass_tvb = process_reassembled_data(tvb, offset, pinfo, "Reassembled CBCH message",
424                                                  frag_data, &cbch_frag_items, NULL, cbch_tree);
425             break;
426
427         case 0x0F:
428             proto_tree_add_text(cbch_tree, tvb, offset, 1, "NULL message");
429             call_dissector(data_handle, tvb, pinfo, cbch_tree);
430             break;
431
432         default:
433             proto_tree_add_text(cbch_tree, tvb, offset, 1, "reserved Sequence Number");
434             call_dissector(data_handle, tvb, pinfo, cbch_tree);
435             break;
436         }
437         if (reass_tvb)
438         {
439             /* Reassembled */
440
441             /* the tvb contains the reassmbled message prepended with the sequence number octet from the first block
442                We use this to determine whether this is a normal message or a scheduling message */
443             offset = 0;
444
445             octet = tvb_get_guint8(reass_tvb, offset++);
446             msg_tvb = tvb_new_subset_remaining(reass_tvb, offset);
447
448             if (octet & 0x08)
449             {
450                 dissect_schedule_message(msg_tvb, pinfo, tree);
451             }
452             else
453             {
454                 call_dissector(cbs_handle, msg_tvb, pinfo, tree);
455             }
456         }
457     }
458     else
459     {
460         proto_tree_add_text(cbch_tree, tvb, offset, 1, "invalid Link Protocol Discriminator");
461         call_dissector(data_handle, tvb, pinfo, cbch_tree);
462     }
463 }
464
465 /* Register the protocol with Wireshark */
466 void
467 proto_register_gsm_cbch(void)
468 {
469     /* Setup list of header fields */
470     static hf_register_info hf_smscb[] =
471         {
472             { &hf_gsm_cbch_spare_bit,
473               { "GSM CBCH spare bit",  "gsm_cbch.block_type.spare",
474                 FT_UINT8, BASE_HEX, NULL, 0x80,
475                 NULL, HFILL}
476             },
477             { &hf_gsm_cbch_lpd,
478               { "GSM CBCH Link Protocol Discriminator",  "gsm_cbch.block_type.lpd",
479                 FT_UINT8, BASE_DEC, VALS(block_type_lpd_strings), 0x60,
480                 NULL, HFILL}
481             },
482             { &hf_gsm_cbch_lb,
483               { "GSM CBCH Last Block", "gsm_cbch.block_type.lb",
484                 FT_UINT8, BASE_DEC, NULL, 0x10,
485                 NULL, HFILL}
486             },
487             { &hf_gsm_cbch_seq_num,
488               { "GSM CBCH Sequence Number",  "gsm_cbch.block_type.seq_num",
489                 FT_UINT8, BASE_DEC, VALS(block_type_seq_num_values), 0x0F,
490                 NULL, HFILL}
491             },
492             { &hf_gsm_cbch_sched_type,
493               { "GSM CBCH Schedule Type", "gsm_cbch.sched_type",
494                 FT_UINT8, BASE_DEC, VALS(sched_type_values), 0xC0,
495                 NULL, HFILL}
496             },
497             { &hf_gsm_cbch_sched_begin_slot,
498               { "GSM CBCH Schedule Begin slot", "gsm_cbch.schedule_begin",
499                 FT_UINT8, BASE_DEC, NULL, 0x3F,
500                 NULL, HFILL}
501             },
502             { &hf_gsm_cbch_sched_spare,
503               { "GSM CBCH Schedule Spare Bits", "gsm_cbch.sched_spare",
504                 FT_UINT8, BASE_DEC, NULL, 0xC0,
505                 NULL, HFILL}
506             },
507             { &hf_gsm_cbch_sched_end_slot,
508               { "GSM CBCH Schedule End Slot",   "gsm_cbch.sched_end",
509                 FT_UINT8, BASE_DEC, NULL, 0x3F,
510                 NULL, HFILL}
511             },
512             { &hf_gsm_cbch_slot,
513               { "Slot",   "gsm_cbch.slot",
514                 FT_UINT8, BASE_DEC, NULL, 0x0,
515                 NULL, HFILL}
516             },
517 #if 0
518             { &hf_gsm_cbch_sched_msg_id,
519               { "GSM CBCH Schedule Message ID", "gsm_cbch.sched_msg_id",
520                 FT_UINT16, BASE_DEC, NULL, 0x0,
521                 NULL, HFILL}
522             },
523 #endif
524             /* Fragment fields
525              */
526             { &hf_cbch_fragment_overlap,
527               {   "Fragment overlap",
528                   "gsm_cbch.fragment.overlap",
529                   FT_BOOLEAN, BASE_NONE, NULL, 0x0,
530                   "Fragment overlaps with other fragments", HFILL
531               }
532             },
533             { &hf_cbch_fragment_overlap_conflict,
534               {   "Conflicting data in fragment overlap",
535                   "gsm_cbch.fragment.overlap.conflict",
536                   FT_BOOLEAN, BASE_NONE, NULL, 0x0,
537                   "Overlapping fragments contained conflicting data", HFILL
538               }
539             },
540             { &hf_cbch_fragment_multiple_tails,
541               {   "Multiple tail fragments found",
542                   "gsm_cbch.fragment.multipletails",
543                   FT_BOOLEAN, BASE_NONE, NULL, 0x0,
544                   "Several tails were found when defragmenting the packet", HFILL
545               }
546             },
547             { &hf_cbch_fragment_too_long_fragment,
548               {   "Fragment too long",
549                   "gsm_cbch.fragment.toolongfragment",
550                   FT_BOOLEAN, BASE_NONE, NULL, 0x0,
551                   "Fragment contained data past end of packet", HFILL
552               }
553             },
554             { &hf_cbch_fragment_error,
555               {   "Defragmentation error",
556                   "gsm_cbch.fragment.error",
557                   FT_FRAMENUM, BASE_NONE, NULL, 0x0,
558                   "Defragmentation error due to illegal fragments", HFILL
559               }
560             },
561             { &hf_cbch_fragment_count,
562               {   "Fragmentation count",
563                   "gsm_cbch.fragment.count",
564                   FT_UINT32, BASE_DEC, NULL, 0x0,
565                   "Count of CBCH Fragments", HFILL
566               }
567             },
568             { &hf_cbch_reassembled_in,
569               {   "Reassembled in",
570                   "gsm_cbch.reassembled.in",
571                   FT_FRAMENUM, BASE_NONE, NULL, 0x0,
572                   "CBCH fragments are reassembled in the given packet", HFILL
573               }
574             },
575             { &hf_cbch_reassembled_length,
576               {   "Reassembled message length is one less than indicated here",
577                   "gsm_cbch.reassembled.length",
578                   FT_UINT32, BASE_DEC, NULL, 0x0,
579                   "The total length of the reassembled message", HFILL
580               }
581             },
582             { &hf_cbch_fragment,
583               {   "CBCH Fragment",
584                   "gsm_cbch.fragment",
585                   FT_FRAMENUM, BASE_NONE, NULL, 0x0,
586                   NULL, HFILL
587               }
588             },
589             { &hf_cbch_fragments,
590               {   "CBCH Fragments",
591                   "gsm_cbch.fragments",
592                   FT_NONE, BASE_NONE, NULL, 0x0,
593                   NULL, HFILL
594               }
595             }
596         };
597
598 /* Setup protocol subtree array */
599     static gint *ett[] = {
600         &ett_cbch_msg,
601         &ett_schedule_msg,
602         &ett_schedule_new_msg,
603         &ett_cbch_fragment,
604         &ett_cbch_fragments,
605     };
606
607     /* Register the protocol name and description */
608     proto_cbch = proto_register_protocol("GSM Cell Broadcast Channel", "GSM CBCH", "gsm_cbch");
609     proto_register_field_array(proto_cbch, hf_smscb, array_length(hf_smscb));
610
611     /* subdissector code */
612     register_dissector("gsm_cbch", dissect_cbch, proto_cbch);
613     register_init_routine(cbch_defragment_init);
614
615     /* subtree array */
616     proto_register_subtree_array(ett, array_length(ett));
617 }
618
619 void
620 proto_reg_handoff_gsm_cbch(void)
621 {
622     data_handle = find_dissector("data");
623     cbs_handle  = find_dissector("gsm_cbs");
624 }