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