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 * SPDX-License-Identifier: GPL-2.0-or-later
15 #include <epan/packet.h>
16 #include <epan/reassemble.h>
17 #include <epan/expert.h>
19 void proto_register_gsm_cbch(void);
20 void proto_reg_handoff_gsm_cbch(void);
22 #define CBCH_FRAGMENT_SIZE 22
24 static const value_string block_type_lpd_strings
[] = {
25 { 0x00, "NOT Cell Broadcast"},
26 { 0x01, "Cell Broadcast"},
27 { 0x02, "NOT Cell Broadcast"},
28 { 0x03, "NOT Cell Broadcast"},
32 static const value_string block_type_seq_num_values
[] = {
33 { 0x00, "First Block"},
34 { 0x01, "Second Block"},
35 { 0x02, "Third Block"},
36 { 0x03, "Fourth Block"},
37 { 0x08, "First Schedule Block"},
38 { 0x0F, "Null message"},
42 static const value_string sched_type_values
[] = {
43 { 0x00, "messages formatted as specified in subclause 3.5 of 3GPP 44.012"},
47 /* Initialize the protocol and registered fields */
48 static int proto_cbch
;
50 static int hf_gsm_cbch_spare_bit
;
51 static int hf_gsm_cbch_lpd
;
52 static int hf_gsm_cbch_lb
;
53 static int hf_gsm_cbch_seq_num
;
54 static int hf_gsm_cbch_sched_type
;
55 static int hf_gsm_cbch_sched_begin_slot
;
56 static int hf_gsm_cbch_sched_spare
;
57 static int hf_gsm_cbch_sched_end_slot
;
58 static int hf_gsm_cbch_slot
;
59 /* static int hf_gsm_cbch_sched_msg_id; */
60 static int hf_gsm_cbch_padding
;
61 static int hf_gsm_cbch_block
;
63 /* These fields are used when reassembling cbch fragments
65 static int hf_cbch_fragments
;
66 static int hf_cbch_fragment
;
67 static int hf_cbch_fragment_overlap
;
68 static int hf_cbch_fragment_overlap_conflict
;
69 static int hf_cbch_fragment_multiple_tails
;
70 static int hf_cbch_fragment_too_long_fragment
;
71 static int hf_cbch_fragment_error
;
72 static int hf_cbch_fragment_count
;
73 static int hf_cbch_reassembled_in
;
74 static int hf_cbch_reassembled_length
;
76 /* Initialize the subtree pointers */
77 static int ett_cbch_msg
;
78 static int ett_schedule_msg
;
79 static int ett_schedule_new_msg
;
80 static int ett_cbch_fragment
;
81 static int ett_cbch_fragments
;
83 static expert_field ei_gsm_cbch_sched_end_slot
;
84 static expert_field ei_gsm_cbch_seq_num_null
;
85 static expert_field ei_gsm_cbch_seq_num_reserved
;
86 static expert_field ei_gsm_cbch_lpd
;
88 static dissector_handle_t cbs_handle
;
90 /* reassembly of CHCH blocks */
91 static reassembly_table cbch_block_reassembly_table
;
93 /* Structure needed for the fragmentation routines in reassemble.c
95 static const fragment_items cbch_frag_items
= {
100 &hf_cbch_fragment_overlap
,
101 &hf_cbch_fragment_overlap_conflict
,
102 &hf_cbch_fragment_multiple_tails
,
103 &hf_cbch_fragment_too_long_fragment
,
104 &hf_cbch_fragment_error
,
105 &hf_cbch_fragment_count
,
106 &hf_cbch_reassembled_in
,
107 &hf_cbch_reassembled_length
,
108 /* Reassembled data field */
113 static const range_string gsm_cbch_sched_begin_slot_rvals
[] = {
114 { 0, 0, "Out of range (ignoring message)" },
115 { 1, 1, "(apparently) Scheduled Scheduling Message" },
116 { 2, 48, "(apparently) Unscheduled Scheduling Message" },
117 { 49, 0xFF, "Out of range (ignoring message)" },
119 { 0x00, 0x00, NULL
},
123 dissect_schedule_message(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*top_tree
)
125 unsigned len
, offset
= 0;
126 uint8_t octet1
, i
, k
= 0;
127 uint8_t sched_begin
, sched_end
, new_slots
[48];
128 bool valid_message
= true;
129 uint16_t other_slots
[48];
130 proto_item
*item
= NULL
, *schedule_item
= NULL
;
131 proto_tree
*sched_tree
= NULL
, *sched_subtree
= NULL
;
133 len
= tvb_reported_length(tvb
);
135 col_append_str(pinfo
->cinfo
, COL_INFO
, " CBCH Schedule Message ");
137 schedule_item
= proto_tree_add_protocol_format(top_tree
, proto_cbch
, tvb
, 0, -1,
138 "GSM CBCH Schedule Message");
140 sched_tree
= proto_item_add_subtree(schedule_item
, ett_schedule_msg
);
142 proto_tree_add_item(sched_tree
, hf_gsm_cbch_sched_type
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
143 octet1
= tvb_get_uint8(tvb
, offset
);
144 if (0 == (octet1
& 0xC0))
146 proto_item
* slot_item
;
147 sched_begin
= octet1
& 0x3F;
148 proto_tree_add_item(sched_tree
, hf_gsm_cbch_sched_begin_slot
, tvb
, offset
++, 1, ENC_BIG_ENDIAN
);
149 if ((sched_begin
< 1) || (sched_begin
> 48))
151 valid_message
= false;
153 proto_tree_add_item(sched_tree
, hf_gsm_cbch_sched_spare
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
154 sched_end
= tvb_get_uint8(tvb
, offset
);
155 slot_item
= proto_tree_add_item(sched_tree
, hf_gsm_cbch_sched_end_slot
, tvb
, offset
++, 1, ENC_BIG_ENDIAN
);
156 if (sched_end
< sched_begin
)
158 expert_add_info(pinfo
, slot_item
, &ei_gsm_cbch_sched_end_slot
);
159 valid_message
= false;
164 /* build an array of new messages */
165 memset(&new_slots
, 0xFF, sizeof(new_slots
));
166 memset(&other_slots
, 0xFF, sizeof(other_slots
));
168 /* iterate over the octets */
172 octet1
= tvb_get_uint8(tvb
, offset
++);
174 /* iterate over the bits */
177 if (octet1
& (0x80>>j
))
179 new_slots
[k
++] = (i
<<3) + j
+ 1;
183 /* print the array of new messages */
184 sched_subtree
= proto_tree_add_subtree_format(sched_tree
, tvb
, offset
-6, 6, ett_schedule_new_msg
, &item
,
185 "This schedule contains %d slots with new messages", k
);
188 DISSECTOR_ASSERT(new_slots
[i
] <= 48);
189 octet1
= tvb_get_uint8(tvb
, offset
);
190 if ((octet1
& 0x80) == 0x80)
196 octet2
= tvb_get_uint8(tvb
, offset
+ 1);
197 msg_id
= ((octet1
&0x7F) << 8) + octet2
;
198 proto_tree_add_uint_format_value(sched_subtree
, hf_gsm_cbch_slot
, tvb
, offset
, 2, new_slots
[i
],
199 "%d, Message ID: %d, First transmission of an SMSCB within the Schedule Period",
200 new_slots
[i
], msg_id
);
202 other_slots
[new_slots
[i
] - 1] = msg_id
;
204 else if ((octet1
& 0xC0) == 0)
209 proto_tree_add_uint_format_value(sched_subtree
, hf_gsm_cbch_slot
, tvb
, offset
++, 1, new_slots
[i
],
210 "%d, Repeat of non-existent slot %d",
211 new_slots
[i
], octet1
);
213 else if (octet1
< new_slots
[i
])
215 proto_tree_add_uint_format_value(sched_subtree
, hf_gsm_cbch_slot
, tvb
, offset
++, 1, new_slots
[i
],
216 "%d, Message ID: %d, Repeat of Slot %d",
217 new_slots
[i
], other_slots
[octet1
- 1], octet1
);
218 other_slots
[new_slots
[i
] - 1] = other_slots
[octet1
- 1];
222 proto_tree_add_uint_format_value(sched_subtree
, hf_gsm_cbch_slot
, tvb
, offset
++, 1, new_slots
[i
],
223 "%d, Apparent forward reference to slot %d",
224 new_slots
[i
], octet1
);
227 else if (octet1
== 0x40)
230 proto_tree_add_uint_format_value(sched_subtree
, hf_gsm_cbch_slot
, tvb
, offset
++, 1, new_slots
[i
],
231 "%d Free Message Slot, optional reading", new_slots
[i
]);
232 other_slots
[new_slots
[i
] - 1] = 0xFFFE;
234 else if (octet1
== 0x41)
237 proto_tree_add_uint_format_value(sched_subtree
, hf_gsm_cbch_slot
, tvb
, offset
++, 1, new_slots
[i
],
238 "%d Free Message Slot, reading advised", new_slots
[i
]);
239 other_slots
[new_slots
[i
] - 1] = 0xFFFE;
244 proto_tree_add_uint_format_value(sched_subtree
, hf_gsm_cbch_slot
, tvb
, offset
, 1, new_slots
[i
],
245 "%d reserved MDT: %x", new_slots
[i
], octet1
);
246 other_slots
[new_slots
[i
] - 1] = 0xFFFE;
249 proto_item_set_end(item
, tvb
, offset
);
251 /* print schedule of other messages */
252 sched_subtree
= proto_tree_add_subtree(sched_tree
, tvb
, offset
, 0,
253 ett_schedule_new_msg
, &item
, "Other message slots in this schedule");
254 for (k
=0; offset
< len
; )
256 /* XXX I don't know if a message can validly contain more than
257 * 48 slots, but that's the size of the array we create so cap
258 * it there to avoid uninitialized memory errors (see bug
259 * https://gitlab.com/wireshark/wireshark/-/issues/9270) */
262 while ((k
<sched_end
) && (other_slots
[k
]!=0xFFFF))
269 octet1
= tvb_get_uint8(tvb
, offset
);
270 if ((octet1
& 0x80) == 0x80)
278 octet2
= tvb_get_uint8(tvb
, offset
+ 1);
279 msg_id
= ((octet1
&0x7F) << 8) + octet2
;
280 other_slots
[k
] = msg_id
;
282 proto_tree_add_uint_format_value(sched_subtree
, hf_gsm_cbch_slot
, tvb
, offset
, 2, k
,
283 "%d, Message: %d, First transmission of an SMSCB within the Schedule Period",
289 /* I'm not sure what's supposed to be dissected in this
290 * case. Perhaps just an expert info is appropriate?
291 * Regardless, we need to increment k to prevent an
293 * https://gitlab.com/wireshark/wireshark/-/issues/8730
298 else if (octet1
&& ((octet1
& 0xC0) == 0))
303 other_slots
[k
] = other_slots
[octet1
- 1];
305 proto_tree_add_uint_format_value(sched_subtree
, hf_gsm_cbch_slot
, tvb
, offset
++, 1, k
,
306 "%d, Message ID: %d, Repeat of Slot %d",
307 k
, other_slots
[octet1
- 1], octet1
);
312 proto_tree_add_uint_format_value(sched_subtree
, hf_gsm_cbch_slot
, tvb
, offset
++, 1, k
,
313 "%d, Apparent forward reference to slot %d",
317 else if (octet1
== 0x40)
321 proto_tree_add_uint_format_value(sched_subtree
, hf_gsm_cbch_slot
, tvb
, offset
++, 1, k
,
322 "%d Free Message Slot, optional reading", k
);
324 else if (octet1
== 0x41)
328 proto_tree_add_uint_format_value(sched_subtree
, hf_gsm_cbch_slot
, tvb
, offset
++, 1, k
,
329 "%d Free Message Slot, reading advised", k
);
335 proto_tree_add_uint_format_value(sched_subtree
, hf_gsm_cbch_slot
, tvb
, offset
, 1, k
,
336 "%d reserved MDT: %x", k
, octet1
);
339 proto_item_set_end(item
, tvb
, offset
);
340 proto_tree_add_item(sched_tree
, hf_gsm_cbch_padding
, tvb
, offset
, -1, ENC_NA
);
346 dissect_cbch(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
348 fragment_head
*frag_data
= NULL
;
349 uint8_t octet
, lb
, lpd
, seq_num
;
351 proto_item
*cbch_item
, *lpd_item
, *seq_item
;
352 proto_tree
*cbch_tree
;
353 tvbuff_t
*reass_tvb
= NULL
, *msg_tvb
= NULL
;
356 octet
= tvb_get_uint8(tvb
, offset
);
359 * create the protocol tree
361 cbch_item
= proto_tree_add_protocol_format(tree
, proto_cbch
, tvb
, 0, -1,
362 "GSM CBCH - Block (0x%02x)", octet
&3);
364 col_append_str(pinfo
->cinfo
, COL_PROTOCOL
, " CBCH");
366 cbch_tree
= proto_item_add_subtree(cbch_item
, ett_cbch_msg
);
368 proto_tree_add_uint(cbch_tree
, hf_gsm_cbch_block
, tvb
, offset
, 1, octet
);
370 proto_tree_add_uint(cbch_tree
, hf_gsm_cbch_spare_bit
, tvb
, offset
, 1, octet
);
371 lpd_item
= proto_tree_add_uint(cbch_tree
, hf_gsm_cbch_lpd
, tvb
, offset
, 1, octet
);
372 proto_tree_add_uint(cbch_tree
, hf_gsm_cbch_lb
, tvb
, offset
, 1, octet
);
373 seq_item
= proto_tree_add_uint(cbch_tree
, hf_gsm_cbch_seq_num
, tvb
, offset
, 1, octet
);
374 seq_num
= octet
& 0x0F;
375 lpd
= (octet
& 0x60) >> 5;
376 lb
= (octet
& 0x10) >> 4;
384 pinfo
->fragmented
= true;
385 /* we should have a unique ID for the reassembled page, but we don't really have anything from the protocol...
386 The GSM frame number div 4 might be used for this, but it has not been passed to this layer and does not
387 exist at all if the message is being passed over the RSL interface.
388 So we just use 0... */
390 /* after reassembly we will need to know if this is a scheduling message,
391 this information is carried in the initial sequence number, not the payload,
392 so we prepend the reassembly with the octet containing the initial sequence number
393 to allow later dissection of the payload */
394 frag_data
= fragment_add_seq_check(&cbch_block_reassembly_table
,
395 tvb
, offset
, pinfo
, 0, NULL
,
396 seq_num
& 0x03, CBCH_FRAGMENT_SIZE
+ 1, !lb
);
397 reass_tvb
= process_reassembled_data(tvb
, offset
, pinfo
, "Reassembled CBCH message",
398 frag_data
, &cbch_frag_items
, NULL
, cbch_tree
);
404 pinfo
->fragmented
= true;
405 offset
++; /* step to beginning of payload */
406 frag_data
= fragment_add_seq_check(&cbch_block_reassembly_table
,
407 tvb
, offset
, pinfo
, 0, NULL
,
408 seq_num
, CBCH_FRAGMENT_SIZE
, !lb
);
409 reass_tvb
= process_reassembled_data(tvb
, offset
, pinfo
, "Reassembled CBCH message",
410 frag_data
, &cbch_frag_items
, NULL
, cbch_tree
);
414 expert_add_info(pinfo
, seq_item
, &ei_gsm_cbch_seq_num_null
);
415 call_data_dissector(tvb
, pinfo
, cbch_tree
);
419 expert_add_info(pinfo
, seq_item
, &ei_gsm_cbch_seq_num_reserved
);
420 call_data_dissector(tvb
, pinfo
, cbch_tree
);
427 /* the tvb contains the reassmbled message prepended with the sequence number octet from the first block
428 We use this to determine whether this is a normal message or a scheduling message */
431 octet
= tvb_get_uint8(reass_tvb
, offset
++);
432 msg_tvb
= tvb_new_subset_remaining(reass_tvb
, offset
);
436 dissect_schedule_message(msg_tvb
, pinfo
, tree
);
440 call_dissector(cbs_handle
, msg_tvb
, pinfo
, tree
);
446 expert_add_info(pinfo
, lpd_item
, &ei_gsm_cbch_lpd
);
447 call_data_dissector(tvb
, pinfo
, cbch_tree
);
449 return tvb_captured_length(tvb
);
452 /* Register the protocol with Wireshark */
454 proto_register_gsm_cbch(void)
456 /* Setup list of header fields */
457 static hf_register_info hf_smscb
[] =
459 { &hf_gsm_cbch_spare_bit
,
460 { "GSM CBCH spare bit", "gsm_cbch.block_type.spare",
461 FT_UINT8
, BASE_HEX
, NULL
, 0x80,
465 { "GSM CBCH Link Protocol Discriminator", "gsm_cbch.block_type.lpd",
466 FT_UINT8
, BASE_DEC
, VALS(block_type_lpd_strings
), 0x60,
470 { "GSM CBCH Last Block", "gsm_cbch.block_type.lb",
471 FT_UINT8
, BASE_DEC
, NULL
, 0x10,
474 { &hf_gsm_cbch_seq_num
,
475 { "GSM CBCH Sequence Number", "gsm_cbch.block_type.seq_num",
476 FT_UINT8
, BASE_DEC
, VALS(block_type_seq_num_values
), 0x0F,
479 { &hf_gsm_cbch_sched_type
,
480 { "GSM CBCH Schedule Type", "gsm_cbch.sched_type",
481 FT_UINT8
, BASE_DEC
, VALS(sched_type_values
), 0xC0,
484 { &hf_gsm_cbch_sched_begin_slot
,
485 { "GSM CBCH Schedule Begin slot", "gsm_cbch.schedule_begin",
486 FT_UINT8
, BASE_DEC
|BASE_RANGE_STRING
, RVALS(gsm_cbch_sched_begin_slot_rvals
), 0x3F,
489 { &hf_gsm_cbch_sched_spare
,
490 { "GSM CBCH Schedule Spare Bits", "gsm_cbch.sched_spare",
491 FT_UINT8
, BASE_DEC
, NULL
, 0xC0,
494 { &hf_gsm_cbch_sched_end_slot
,
495 { "GSM CBCH Schedule End Slot", "gsm_cbch.sched_end",
496 FT_UINT8
, BASE_DEC
, NULL
, 0x3F,
500 { "Slot", "gsm_cbch.slot",
501 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
504 { &hf_gsm_cbch_padding
,
505 { "Padding", "gsm_cbch.padding",
506 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
509 { &hf_gsm_cbch_block
,
510 { "CBCH Block", "gsm_cbch.block",
511 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
516 { &hf_gsm_cbch_sched_msg_id
,
517 { "GSM CBCH Schedule Message ID", "gsm_cbch.sched_msg_id",
518 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
524 { &hf_cbch_fragment_overlap
,
525 { "Fragment overlap",
526 "gsm_cbch.fragment.overlap",
527 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
528 "Fragment overlaps with other fragments", HFILL
531 { &hf_cbch_fragment_overlap_conflict
,
532 { "Conflicting data in fragment overlap",
533 "gsm_cbch.fragment.overlap.conflict",
534 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
535 "Overlapping fragments contained conflicting data", HFILL
538 { &hf_cbch_fragment_multiple_tails
,
539 { "Multiple tail fragments found",
540 "gsm_cbch.fragment.multipletails",
541 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
542 "Several tails were found when defragmenting the packet", HFILL
545 { &hf_cbch_fragment_too_long_fragment
,
546 { "Fragment too long",
547 "gsm_cbch.fragment.toolongfragment",
548 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
549 "Fragment contained data past end of packet", HFILL
552 { &hf_cbch_fragment_error
,
553 { "Defragmentation error",
554 "gsm_cbch.fragment.error",
555 FT_FRAMENUM
, BASE_NONE
, NULL
, 0x0,
556 "Defragmentation error due to illegal fragments", HFILL
559 { &hf_cbch_fragment_count
,
560 { "Fragmentation count",
561 "gsm_cbch.fragment.count",
562 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
563 "Count of CBCH Fragments", HFILL
566 { &hf_cbch_reassembled_in
,
568 "gsm_cbch.reassembled.in",
569 FT_FRAMENUM
, BASE_NONE
, NULL
, 0x0,
570 "CBCH fragments are reassembled in the given packet", HFILL
573 { &hf_cbch_reassembled_length
,
574 { "Reassembled message length is one less than indicated here",
575 "gsm_cbch.reassembled.length",
576 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
577 "The total length of the reassembled message", HFILL
583 FT_FRAMENUM
, BASE_NONE
, NULL
, 0x0,
587 { &hf_cbch_fragments
,
589 "gsm_cbch.fragments",
590 FT_NONE
, BASE_NONE
, NULL
, 0x0,
596 /* Setup protocol subtree array */
597 static int *ett
[] = {
600 &ett_schedule_new_msg
,
605 expert_module_t
* expert_cbch
;
607 static ei_register_info ei
[] = {
608 { &ei_gsm_cbch_sched_end_slot
, { "gsm_cbch.sched_end.bad_range", PI_PROTOCOL
, PI_WARN
, "End Slot Number less than Begin Slot Number: ignoring message", EXPFILL
}},
609 { &ei_gsm_cbch_seq_num_null
, { "gsm_cbch.block_type.seq_num.null", PI_PROTOCOL
, PI_NOTE
, "NULL message", EXPFILL
}},
610 { &ei_gsm_cbch_seq_num_reserved
, { "gsm_cbch.block_type.seq_num.reserved", PI_PROTOCOL
, PI_NOTE
, "Reserved Sequence Number", EXPFILL
}},
611 { &ei_gsm_cbch_lpd
, { "gsm_cbch.block_type.lpd.invalid", PI_PROTOCOL
, PI_WARN
, "Invalid Link Protocol Discriminator", EXPFILL
}},
614 /* Register the protocol name and description */
615 proto_cbch
= proto_register_protocol("GSM Cell Broadcast Channel", "GSM CBCH", "gsm_cbch");
616 proto_register_field_array(proto_cbch
, hf_smscb
, array_length(hf_smscb
));
617 expert_cbch
= expert_register_protocol(proto_cbch
);
618 expert_register_field_array(expert_cbch
, ei
, array_length(ei
));
620 /* subdissector code */
621 register_dissector("gsm_cbch", dissect_cbch
, proto_cbch
);
623 reassembly_table_register(&cbch_block_reassembly_table
,
624 &addresses_reassembly_table_functions
);
627 proto_register_subtree_array(ett
, array_length(ett
));
631 proto_reg_handoff_gsm_cbch(void)
633 cbs_handle
= find_dissector_add_dependency("gsm_cbs", proto_cbch
);
637 * Editor modelines - https://www.wireshark.org/tools/modelines.html
642 * indent-tabs-mode: nil
645 * vi: set shiftwidth=4 tabstop=8 expandtab:
646 * :indentSize=4:tabSize=8:noTabs=true: