2 * Routines for GSM CBCH dissection - A.K.A. 3GPP 44.012 (GSM 04.12)
4 * Copyright 2011, Mike Morrin <mike.morrin [AT] ipaccess.com>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
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.
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.
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.
27 #include <epan/packet.h>
28 #include <epan/reassemble.h>
29 #include "packet-cell_broadcast.h"
31 void proto_register_gsm_cbch(void);
32 void proto_reg_handoff_gsm_cbch(void);
34 #define CBCH_FRAGMENT_SIZE 22
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"},
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"},
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"},
60 /* Initialize the protocol and registered fields */
61 static int proto_cbch = -1;
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; */
74 /* These fields are used when reassembling cbch fragments
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;
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;
94 static dissector_handle_t data_handle;
95 static dissector_handle_t cbs_handle;
97 /* reassembly of CHCH blocks */
98 static reassembly_table cbch_block_reassembly_table;
100 /* Structure needed for the fragmentation routines in reassemble.c
102 static const fragment_items cbch_frag_items = {
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 */
121 cbch_defragment_init(void)
123 reassembly_table_init(&cbch_block_reassembly_table,
124 &addresses_reassembly_table_functions);
128 dissect_schedule_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *top_tree)
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;
138 len = tvb_length(tvb);
140 col_append_str(pinfo->cinfo, COL_INFO, " CBCH Schedule Message ");
142 schedule_item = proto_tree_add_protocol_format(top_tree, proto_cbch, tvb, 0, len,
143 "GSM CBCH Schedule Message");
145 sched_tree = proto_item_add_subtree(schedule_item, ett_schedule_msg);
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))
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)
155 proto_tree_add_text(sched_tree, tvb, offset - 1, 1, "(apparently) Scheduled Scheduling Message");
157 else if ((2 <= sched_begin) && (48 >= sched_begin))
159 proto_tree_add_text(sched_tree, tvb, offset - 1, 1, "(apparently) Unscheduled Scheduling Message");
163 proto_tree_add_text(sched_tree, tvb, offset - 1, 1, "Begin Slot Number out of range: ignoring message");
164 valid_message = FALSE;
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)
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;
177 /* build an array of new messages */
178 memset(&new_slots, 0xFF, sizeof(new_slots));
179 memset(&other_slots, 0xFF, sizeof(other_slots));
181 /* iterate over the octets */
184 octet1 = tvb_get_guint8(tvb, offset++);
186 /* iterate over the bits */
189 if (octet1 & (0x80>>j))
191 new_slots[k++] = (i<<3) + j + 1;
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);
200 DISSECTOR_ASSERT(new_slots[i] <= 48);
201 octet1 = tvb_get_guint8(tvb, offset);
202 if ((octet1 & 0x80) == 0x80)
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);
214 other_slots[new_slots[i] - 1] = msg_id;
216 else if ((octet1 & 0xC0) == 0)
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);
225 else if (octet1 < new_slots[i])
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];
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);
239 else if (octet1 == 0x40)
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;
246 else if (octet1 == 0x41)
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;
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;
261 proto_item_set_end(item, tvb, offset);
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++)
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) */
274 while ((k<sched_end) && (other_slots[k]!=0xFFFF))
281 octet1 = tvb_get_guint8(tvb, offset);
282 if ((octet1 & 0x80) == 0x80)
290 octet2 = tvb_get_guint8(tvb, offset + 1);
291 msg_id = ((octet1 &0x7F) << 8) + octet2;
292 other_slots[k] = msg_id;
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",
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
305 * https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=8730
310 else if (octet1 && ((octet1 & 0xC0) == 0))
315 other_slots[k] = other_slots[octet1 - 1];
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);
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",
329 else if (octet1 == 0x40)
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);
336 else if (octet1 == 0x41)
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);
347 proto_tree_add_uint_format_value(sched_subtree, hf_gsm_cbch_slot, tvb, offset, 1, k,
348 "%d reserved MDT: %x", k, octet1);
351 proto_item_set_end(item, tvb, offset);
352 proto_tree_add_text(sched_tree, tvb, offset, -1, "Padding");
358 dissect_cbch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
360 fragment_head *frag_data = NULL;
361 guint8 octet, lb, lpd, seq_num;
364 proto_item *cbch_item = NULL;
365 proto_tree *cbch_tree = NULL;
366 tvbuff_t *reass_tvb = NULL, *msg_tvb = NULL;
368 len = tvb_length(tvb);
370 octet = tvb_get_guint8(tvb, offset);
373 * create the protocol tree
375 cbch_item = proto_tree_add_protocol_format(tree, proto_cbch, tvb, 0, len,
376 "GSM CBCH - Block (0x%02x)", octet&3);
378 col_append_str(pinfo->cinfo, COL_PROTOCOL, " CBCH");
380 cbch_tree = proto_item_add_subtree(cbch_item, ett_cbch_msg);
382 proto_tree_add_text(cbch_tree, tvb, offset, 1, "CBCH Block");
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;
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... */
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);
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);
428 proto_tree_add_text(cbch_tree, tvb, offset, 1, "NULL message");
429 call_dissector(data_handle, tvb, pinfo, cbch_tree);
433 proto_tree_add_text(cbch_tree, tvb, offset, 1, "reserved Sequence Number");
434 call_dissector(data_handle, tvb, pinfo, cbch_tree);
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 */
445 octet = tvb_get_guint8(reass_tvb, offset++);
446 msg_tvb = tvb_new_subset_remaining(reass_tvb, offset);
450 dissect_schedule_message(msg_tvb, pinfo, tree);
454 call_dissector(cbs_handle, msg_tvb, pinfo, tree);
460 proto_tree_add_text(cbch_tree, tvb, offset, 1, "invalid Link Protocol Discriminator");
461 call_dissector(data_handle, tvb, pinfo, cbch_tree);
465 /* Register the protocol with Wireshark */
467 proto_register_gsm_cbch(void)
469 /* Setup list of header fields */
470 static hf_register_info hf_smscb[] =
472 { &hf_gsm_cbch_spare_bit,
473 { "GSM CBCH spare bit", "gsm_cbch.block_type.spare",
474 FT_UINT8, BASE_HEX, NULL, 0x80,
478 { "GSM CBCH Link Protocol Discriminator", "gsm_cbch.block_type.lpd",
479 FT_UINT8, BASE_DEC, VALS(block_type_lpd_strings), 0x60,
483 { "GSM CBCH Last Block", "gsm_cbch.block_type.lb",
484 FT_UINT8, BASE_DEC, NULL, 0x10,
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,
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,
497 { &hf_gsm_cbch_sched_begin_slot,
498 { "GSM CBCH Schedule Begin slot", "gsm_cbch.schedule_begin",
499 FT_UINT8, BASE_DEC, NULL, 0x3F,
502 { &hf_gsm_cbch_sched_spare,
503 { "GSM CBCH Schedule Spare Bits", "gsm_cbch.sched_spare",
504 FT_UINT8, BASE_DEC, NULL, 0xC0,
507 { &hf_gsm_cbch_sched_end_slot,
508 { "GSM CBCH Schedule End Slot", "gsm_cbch.sched_end",
509 FT_UINT8, BASE_DEC, NULL, 0x3F,
513 { "Slot", "gsm_cbch.slot",
514 FT_UINT8, BASE_DEC, NULL, 0x0,
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,
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
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
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
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
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
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
568 { &hf_cbch_reassembled_in,
570 "gsm_cbch.reassembled.in",
571 FT_FRAMENUM, BASE_NONE, NULL, 0x0,
572 "CBCH fragments are reassembled in the given packet", HFILL
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
585 FT_FRAMENUM, BASE_NONE, NULL, 0x0,
589 { &hf_cbch_fragments,
591 "gsm_cbch.fragments",
592 FT_NONE, BASE_NONE, NULL, 0x0,
598 /* Setup protocol subtree array */
599 static gint *ett[] = {
602 &ett_schedule_new_msg,
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));
611 /* subdissector code */
612 register_dissector("gsm_cbch", dissect_cbch, proto_cbch);
613 register_init_routine(cbch_defragment_init);
616 proto_register_subtree_array(ett, array_length(ett));
620 proto_reg_handoff_gsm_cbch(void)
622 data_handle = find_dissector("data");
623 cbs_handle = find_dissector("gsm_cbs");