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>
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 #include <epan/packet.h>
30 #include <epan/reassemble.h>
31 #include "packet-cell_broadcast.h"
33 #define CBCH_FRAGMENT_SIZE 22
35 const value_string block_type_lpd_strings[] = {
36 { 0x00, "NOT Cell Broadcast"},
37 { 0x01, "Cell Broadcast"},
38 { 0x02, "NOT Cell Broadcast"},
39 { 0x03, "NOT Cell Broadcast"},
43 const value_string block_type_seq_num_values[] = {
44 { 0x00, "First Block"},
45 { 0x01, "Second Block"},
46 { 0x02, "Third Block"},
47 { 0x03, "Fourth Block"},
48 { 0x08, "First Schedule Block"},
49 { 0xFF, "Null message"},
53 const value_string sched_type_values[] = {
54 { 0x00, "messages formatted as specified in subclause 3.5 of 3GPP 44.012"},
55 { 0xFF, "Unknown schedule message format"},
59 /* Initialize the protocol and registered fields */
60 static int proto_cbch = -1;
62 static int hf_gsm_cbch_spare_bit = -1;
63 static int hf_gsm_cbch_lpd = -1;
64 static int hf_gsm_cbch_lb = -1;
65 static int hf_gsm_cbch_seq_num = -1;
66 static int hf_gsm_cbch_sched_type = -1;
67 static int hf_gsm_cbch_sched_begin_slot = -1;
68 static int hf_gsm_cbch_sched_spare = -1;
69 static int hf_gsm_cbch_sched_end_slot = -1;
70 /* static int hf_gsm_cbch_sched_msg_id = -1; */
72 /* These fields are used when reassembling cbch fragments
74 static int hf_cbch_fragments = -1;
75 static int hf_cbch_fragment = -1;
76 static int hf_cbch_fragment_overlap = -1;
77 static int hf_cbch_fragment_overlap_conflict = -1;
78 static int hf_cbch_fragment_multiple_tails = -1;
79 static int hf_cbch_fragment_too_long_fragment = -1;
80 static int hf_cbch_fragment_error = -1;
81 static int hf_cbch_fragment_count = -1;
82 static int hf_cbch_reassembled_in = -1;
83 static int hf_cbch_reassembled_length = -1;
85 /* Initialize the subtree pointers */
86 static gint ett_cbch_msg = -1;
87 static gint ett_schedule_msg = -1;
88 static gint ett_schedule_new_msg = -1;
89 static gint ett_cbch_fragment = -1;
90 static gint ett_cbch_fragments = -1;
92 static dissector_handle_t data_handle;
93 static dissector_handle_t cbs_handle;
95 /* reassembly of CHCH blocks */
96 static reassembly_table cbch_block_reassembly_table;
98 /* Structure needed for the fragmentation routines in reassemble.c
100 static const fragment_items cbch_frag_items = {
105 &hf_cbch_fragment_overlap,
106 &hf_cbch_fragment_overlap_conflict,
107 &hf_cbch_fragment_multiple_tails,
108 &hf_cbch_fragment_too_long_fragment,
109 &hf_cbch_fragment_error,
110 &hf_cbch_fragment_count,
111 &hf_cbch_reassembled_in,
112 &hf_cbch_reassembled_length,
113 /* Reassembled data field */
119 cbch_defragment_init(void)
121 reassembly_table_init(&cbch_block_reassembly_table,
122 &addresses_reassembly_table_functions);
126 dissect_schedule_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *top_tree)
128 guint len, offset = 0;
129 guint8 octet1, i, j, k = 0;
130 guint8 sched_begin, sched_end, new_slots[48];
131 gboolean valid_message = TRUE;
132 guint16 other_slots[48];
133 proto_item *item = NULL, *schedule_item = NULL;
134 proto_tree *sched_tree = NULL, *sched_subtree = NULL;
136 len = tvb_length(tvb);
138 col_append_str(pinfo->cinfo, COL_INFO, " CBCH Schedule Message ");
140 schedule_item = proto_tree_add_protocol_format(top_tree, proto_cbch, tvb, 0, len,
141 "GSM CBCH Schedule Message");
143 sched_tree = proto_item_add_subtree(schedule_item, ett_schedule_msg);
145 proto_tree_add_item(sched_tree, hf_gsm_cbch_sched_type, tvb, offset, 1, ENC_BIG_ENDIAN);
146 octet1 = tvb_get_guint8(tvb, offset);
147 if (0 == (octet1 & 0xC0))
149 sched_begin = octet1 & 0x3F;
150 proto_tree_add_item(sched_tree, hf_gsm_cbch_sched_begin_slot, tvb, offset++, 1, ENC_BIG_ENDIAN);
151 if (1 == sched_begin)
153 proto_tree_add_text(sched_tree, tvb, offset - 1, 1, "(apparently) Scheduled Scheduling Message");
155 else if ((2 <= sched_begin) && (48 >= sched_begin))
157 proto_tree_add_text(sched_tree, tvb, offset - 1, 1, "(apparently) Unscheduled Scheduling Message");
161 proto_tree_add_text(sched_tree, tvb, offset - 1, 1, "Begin Slot Number out of range: ignoring message");
162 valid_message = FALSE;
164 proto_tree_add_item(sched_tree, hf_gsm_cbch_sched_spare, tvb, offset, 1, ENC_BIG_ENDIAN);
165 sched_end = tvb_get_guint8(tvb, offset);
166 proto_tree_add_item(sched_tree, hf_gsm_cbch_sched_end_slot, tvb, offset++, 1, ENC_BIG_ENDIAN);
167 if (sched_end < sched_begin)
169 proto_tree_add_text(sched_tree, tvb, offset - 1, 1, "End Slot Number less than Begin Slot Number: ignoring message");
170 valid_message = FALSE;
175 /* build an array of new messages */
176 memset(&new_slots, 0xFF, sizeof(new_slots));
177 memset(&other_slots, 0xFF, sizeof(other_slots));
179 /* iterate over the octets */
182 octet1 = tvb_get_guint8(tvb, offset++);
184 /* iterate over the bits */
187 if (octet1 & (0x80>>j))
189 new_slots[k++] = (i<<3) + j + 1;
193 /* print the array of new messages */
194 item = proto_tree_add_text(sched_tree, tvb, offset-6, 6, "This schedule contains %d slots with new messages", k);
195 sched_subtree = proto_item_add_subtree(item, ett_schedule_new_msg);
198 DISSECTOR_ASSERT(new_slots[i] < 48);
199 octet1 = tvb_get_guint8(tvb, offset);
200 if ((octet1 & 0x80) == 0x80)
206 octet2 = tvb_get_guint8(tvb, offset + 1);
207 msg_id = ((octet1 &0x7F) << 8) + octet2;
208 proto_tree_add_text(sched_subtree, tvb, offset, 2,
209 "Slot: %d, Message ID: %d, First transmission of an SMSCB within the Schedule Period",
210 new_slots[i], msg_id);
212 other_slots[new_slots[i] - 1] = msg_id;
214 else if ((octet1 & 0xC0) == 0)
217 if (octet1 < new_slots[i])
219 proto_tree_add_text(sched_subtree, tvb, offset++, 1,
220 "Slot: %d, Message ID: %d, Repeat of Slot %d",
221 new_slots[i], other_slots[octet1 - 1], octet1);
222 other_slots[new_slots[i] - 1] = other_slots[octet1 - 1];
226 proto_tree_add_text(sched_subtree, tvb, offset++, 1,
227 "Slot: %d, Apparent forward reference to slot %d",
228 new_slots[i], octet1);
231 else if (octet1 == 0x40)
234 proto_tree_add_text(sched_subtree, tvb, offset++, 1, "Slot: %d Free Message Slot, optional reading", new_slots[k]);
235 other_slots[new_slots[i] - 1] = 0xFFFE;
237 else if (octet1 == 0x41)
240 proto_tree_add_text(sched_subtree, tvb, offset++, 1, "Slot: %d Free Message Slot, reading advised", new_slots[k]);
241 other_slots[new_slots[i] - 1] = 0xFFFE;
246 proto_tree_add_text(sched_subtree, tvb, offset, 1, "Slot: %d reserved MDT: %x", new_slots[k], octet1);
247 other_slots[new_slots[i] - 1] = 0xFFFE;
250 proto_item_set_end(item, tvb, offset);
252 /* print schedule of other messages */
253 item = proto_tree_add_text(sched_tree, tvb, offset, 0, "Other message slots in this schedule");
254 sched_subtree = proto_item_add_subtree(item, ett_schedule_new_msg);
255 for (k=0; offset < len; j++)
257 while ((other_slots[k]!=0xFFFF) && (k<sched_end))
264 octet1 = tvb_get_guint8(tvb, offset);
265 if ((octet1 & 0x80) == 0x80)
273 octet2 = tvb_get_guint8(tvb, offset + 1);
274 msg_id = ((octet1 &0x7F) << 8) + octet2;
275 other_slots[k] = msg_id;
276 proto_tree_add_text(sched_subtree, tvb, offset, 2,
277 "Slot: %d, Message: %d, First transmission of an SMSCB within the Schedule Period",
283 /* I'm not sure what's supposed to be dissected in this
284 * case. Perhaps just an expert info is appropriate?
285 * Regardless, we need to increment k to prevent an
287 * https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=8730
292 else if ((octet1 & 0xC0) == 0)
297 other_slots[k] = other_slots[octet1 - 1];
298 proto_tree_add_text(sched_subtree, tvb, offset++, 1,
299 "Slot: %d, Message ID: %d, Repeat of Slot %d",
300 ++k, other_slots[octet1 - 1], octet1);
304 proto_tree_add_text(sched_subtree, tvb, offset++, 1,
305 "Slot: %d, Apparent forward reference to slot %d",
309 else if (octet1 == 0x40)
312 proto_tree_add_text(sched_subtree, tvb, offset++, 1, "Slot: %d Free Message Slot, optional reading", ++k);
314 else if (octet1 == 0x41)
317 proto_tree_add_text(sched_subtree, tvb, offset++, 1, "Slot: %d Free Message Slot, reading advised", ++k);
322 proto_tree_add_text(sched_subtree, tvb, offset, 1, "Slot: %d reserved MDT: %x", ++k, octet1);
325 proto_item_set_end(item, tvb, offset);
326 proto_tree_add_text(sched_tree, tvb, offset, -1, "Padding");
332 dissect_cbch(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
334 fragment_head *frag_data = NULL;
335 guint8 octet, lb, lpd, seq_num;
338 proto_item *cbch_item = NULL;
339 proto_tree *cbch_tree = NULL;
340 tvbuff_t *reass_tvb = NULL, *msg_tvb = NULL;
342 len = tvb_length(tvb);
344 octet = tvb_get_guint8(tvb, offset);
347 * create the protocol tree
349 cbch_item = proto_tree_add_protocol_format(tree, proto_cbch, tvb, 0, len,
350 "GSM CBCH - Block (0x%02x)", octet&3);
352 col_append_str(pinfo->cinfo, COL_PROTOCOL, " CBCH");
354 cbch_tree = proto_item_add_subtree(cbch_item, ett_cbch_msg);
356 proto_tree_add_text(cbch_tree, tvb, offset, 1, "CBCH Block");
358 proto_tree_add_uint(cbch_tree, hf_gsm_cbch_spare_bit, tvb, offset, 1, octet);
359 proto_tree_add_uint(cbch_tree, hf_gsm_cbch_lpd, tvb, offset, 1, octet);
360 proto_tree_add_uint(cbch_tree, hf_gsm_cbch_lb, tvb, offset, 1, octet);
361 proto_tree_add_uint(cbch_tree, hf_gsm_cbch_seq_num, tvb, offset, 1, octet);
362 seq_num = octet & 0x0F;
363 lpd = (octet & 0x60) >> 5;
364 lb = (octet & 0x10) >> 4;
372 pinfo->fragmented = TRUE;
373 /* we should have a unique ID for the reassembled page, but we don't really have anything from the protocol...
374 The GSM frame number div 4 might be used for this, but it has not been passed to this layer and does not
375 exist at all if the message is being passed over the RSL interface.
376 So we just use 0... */
378 /* after reassembly we will need to know if this is a scheduling message,
379 this information is carried in the initial sequence number, not the payload,
380 so we prepend the reassembly with the octet containing the initial sequence number
381 to allow later dissection of the payload */
382 frag_data = fragment_add_seq_check(&cbch_block_reassembly_table,
383 tvb, offset, pinfo, 0, NULL,
384 seq_num & 0x03, CBCH_FRAGMENT_SIZE + 1, !lb);
385 reass_tvb = process_reassembled_data(tvb, offset, pinfo, "Reassembled CBCH message",
386 frag_data, &cbch_frag_items, NULL, cbch_tree);
392 pinfo->fragmented = TRUE;
393 offset++; /* step to beginning of payload */
394 frag_data = fragment_add_seq_check(&cbch_block_reassembly_table,
395 tvb, offset, pinfo, 0, NULL,
396 seq_num, CBCH_FRAGMENT_SIZE, !lb);
397 reass_tvb = process_reassembled_data(tvb, offset, pinfo, "Reassembled CBCH message",
398 frag_data, &cbch_frag_items, NULL, cbch_tree);
402 proto_tree_add_text(cbch_tree, tvb, offset, 1, "NULL message");
403 call_dissector(data_handle, tvb, pinfo, cbch_tree);
407 proto_tree_add_text(cbch_tree, tvb, offset, 1, "reserved Sequence Number");
408 call_dissector(data_handle, tvb, pinfo, cbch_tree);
415 /* the tvb contains the reassmbled message prepended with the sequence number octet from the first block
416 We use this to determine whether this is a normal message or a scheduling message */
419 octet = tvb_get_guint8(reass_tvb, offset++);
420 msg_tvb = tvb_new_subset_remaining(reass_tvb, offset);
424 dissect_schedule_message(msg_tvb, pinfo, tree);
428 call_dissector(cbs_handle, msg_tvb, pinfo, tree);
434 proto_tree_add_text(cbch_tree, tvb, offset, 1, "invalid Link Protocol Discriminator");
435 call_dissector(data_handle, tvb, pinfo, cbch_tree);
439 /* Register the protocol with Wireshark */
441 proto_register_gsm_cbch(void)
443 /* Setup list of header fields */
444 static hf_register_info hf_smscb[] =
446 { &hf_gsm_cbch_spare_bit,
447 { "GSM CBCH spare bit", "gsm_cbch.block_type.spare",
448 FT_UINT8, BASE_HEX, NULL, 0x80,
452 { "GSM CBCH Link Protocol Discriminator", "gsm_cbch.block_type.lpd",
453 FT_UINT8, BASE_DEC, VALS(block_type_lpd_strings), 0x60,
457 { "GSM CBCH Last Block", "gsm_cbch.block_type.lb",
458 FT_UINT8, BASE_DEC, NULL, 0x10,
461 { &hf_gsm_cbch_seq_num,
462 { "GSM CBCH Sequence Number", "gsm_cbch.block_type.seq_num",
463 FT_UINT8, BASE_DEC, VALS(block_type_seq_num_values), 0x0F,
466 { &hf_gsm_cbch_sched_type,
467 { "GSM CBCH Schedule Type", "gsm_cbch.sched_type",
468 FT_UINT8, BASE_DEC, VALS(sched_type_values), 0xC0,
471 { &hf_gsm_cbch_sched_begin_slot,
472 { "GSM CBCH Schedule Begin slot", "gsm_cbch.schedule_begin",
473 FT_UINT8, BASE_DEC, NULL, 0x3F,
476 { &hf_gsm_cbch_sched_spare,
477 { "GSM CBCH Schedule Spare Bits", "gsm_cbch.sched_spare",
478 FT_UINT8, BASE_DEC, NULL, 0xC0,
481 { &hf_gsm_cbch_sched_end_slot,
482 { "GSM CBCH Schedule End Slot", "gsm_cbch.sched_end",
483 FT_UINT8, BASE_DEC, NULL, 0x3F,
487 { &hf_gsm_cbch_sched_msg_id,
488 { "GSM CBCH Schedule Message ID", "gsm_cbch.sched_msg_id",
489 FT_UINT16, BASE_DEC, NULL, 0x0,
495 { &hf_cbch_fragment_overlap,
496 { "Fragment overlap",
497 "gsm_cbch.fragment.overlap",
498 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
499 "Fragment overlaps with other fragments", HFILL
502 { &hf_cbch_fragment_overlap_conflict,
503 { "Conflicting data in fragment overlap",
504 "gsm_cbch.fragment.overlap.conflict",
505 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
506 "Overlapping fragments contained conflicting data", HFILL
509 { &hf_cbch_fragment_multiple_tails,
510 { "Multiple tail fragments found",
511 "gsm_cbch.fragment.multipletails",
512 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
513 "Several tails were found when defragmenting the packet", HFILL
516 { &hf_cbch_fragment_too_long_fragment,
517 { "Fragment too long",
518 "gsm_cbch.fragment.toolongfragment",
519 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
520 "Fragment contained data past end of packet", HFILL
523 { &hf_cbch_fragment_error,
524 { "Defragmentation error",
525 "gsm_cbch.fragment.error",
526 FT_FRAMENUM, BASE_NONE, NULL, 0x0,
527 "Defragmentation error due to illegal fragments", HFILL
530 { &hf_cbch_fragment_count,
531 { "Fragmentation count",
532 "gsm_cbch.fragment.count",
533 FT_UINT32, BASE_DEC, NULL, 0x0,
534 "Count of CBCH Fragments", HFILL
537 { &hf_cbch_reassembled_in,
539 "gsm_cbch.reassembled.in",
540 FT_FRAMENUM, BASE_NONE, NULL, 0x0,
541 "CBCH fragments are reassembled in the given packet", HFILL
544 { &hf_cbch_reassembled_length,
545 { "Reassembled message length is one less than indicated here",
546 "gsm_cbch.reassembled.length",
547 FT_UINT32, BASE_DEC, NULL, 0x0,
548 "The total length of the reassembled message", HFILL
554 FT_FRAMENUM, BASE_NONE, NULL, 0x0,
558 { &hf_cbch_fragments,
560 "gsm_cbch.fragments",
561 FT_NONE, BASE_NONE, NULL, 0x0,
567 /* Setup protocol subtree array */
568 static gint *ett[] = {
571 &ett_schedule_new_msg,
576 /* Register the protocol name and description */
577 proto_cbch = proto_register_protocol("GSM Cell Broadcast Channel", "GSM CBCH", "gsm_cbch");
578 proto_register_field_array(proto_cbch, hf_smscb, array_length(hf_smscb));
580 /* subdissector code */
581 register_dissector("gsm_cbch", dissect_cbch, proto_cbch);
582 register_init_routine(cbch_defragment_init);
585 proto_register_subtree_array(ett, array_length(ett));
589 proto_reg_handoff_gsm_cbch(void)
591 data_handle = find_dissector("data");
592 cbs_handle = find_dissector("gsm_cbs");