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)
219 proto_tree_add_text(sched_subtree
, tvb
, offset
++, 1,
220 "Slot: %d, Repeat of non-existant slot %d",
221 new_slots
[i
], octet1
);
223 else if (octet1
< new_slots
[i
])
225 proto_tree_add_text(sched_subtree
, tvb
, offset
++, 1,
226 "Slot: %d, Message ID: %d, Repeat of Slot %d",
227 new_slots
[i
], other_slots
[octet1
- 1], octet1
);
228 other_slots
[new_slots
[i
] - 1] = other_slots
[octet1
- 1];
232 proto_tree_add_text(sched_subtree
, tvb
, offset
++, 1,
233 "Slot: %d, Apparent forward reference to slot %d",
234 new_slots
[i
], octet1
);
237 else if (octet1
== 0x40)
240 proto_tree_add_text(sched_subtree
, tvb
, offset
++, 1, "Slot: %d Free Message Slot, optional reading", new_slots
[k
]);
241 other_slots
[new_slots
[i
] - 1] = 0xFFFE;
243 else if (octet1
== 0x41)
246 proto_tree_add_text(sched_subtree
, tvb
, offset
++, 1, "Slot: %d Free Message Slot, reading advised", new_slots
[k
]);
247 other_slots
[new_slots
[i
] - 1] = 0xFFFE;
252 proto_tree_add_text(sched_subtree
, tvb
, offset
, 1, "Slot: %d reserved MDT: %x", new_slots
[k
], octet1
);
253 other_slots
[new_slots
[i
] - 1] = 0xFFFE;
256 proto_item_set_end(item
, tvb
, offset
);
258 /* print schedule of other messages */
259 item
= proto_tree_add_text(sched_tree
, tvb
, offset
, 0, "Other message slots in this schedule");
260 sched_subtree
= proto_item_add_subtree(item
, ett_schedule_new_msg
);
261 for (k
=0; offset
< len
; j
++)
263 /* XXX I don't know if a message can validly contain more than
264 * 48 slots, but that's the size of the array we create so cap
265 * it there to avoid uninitialized memory errors (see bug
266 * https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9270) */
269 while ((k
<sched_end
) && (other_slots
[k
]!=0xFFFF))
276 octet1
= tvb_get_guint8(tvb
, offset
);
277 if ((octet1
& 0x80) == 0x80)
285 octet2
= tvb_get_guint8(tvb
, offset
+ 1);
286 msg_id
= ((octet1
&0x7F) << 8) + octet2
;
287 other_slots
[k
] = msg_id
;
288 proto_tree_add_text(sched_subtree
, tvb
, offset
, 2,
289 "Slot: %d, Message: %d, First transmission of an SMSCB within the Schedule Period",
295 /* I'm not sure what's supposed to be dissected in this
296 * case. Perhaps just an expert info is appropriate?
297 * Regardless, we need to increment k to prevent an
299 * https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=8730
304 else if ((octet1
& 0xC0) == 0)
309 other_slots
[k
] = other_slots
[octet1
- 1];
310 proto_tree_add_text(sched_subtree
, tvb
, offset
++, 1,
311 "Slot: %d, Message ID: %d, Repeat of Slot %d",
312 ++k
, other_slots
[octet1
- 1], octet1
);
316 proto_tree_add_text(sched_subtree
, tvb
, offset
++, 1,
317 "Slot: %d, Apparent forward reference to slot %d",
321 else if (octet1
== 0x40)
324 proto_tree_add_text(sched_subtree
, tvb
, offset
++, 1, "Slot: %d Free Message Slot, optional reading", ++k
);
326 else if (octet1
== 0x41)
329 proto_tree_add_text(sched_subtree
, tvb
, offset
++, 1, "Slot: %d Free Message Slot, reading advised", ++k
);
334 proto_tree_add_text(sched_subtree
, tvb
, offset
, 1, "Slot: %d reserved MDT: %x", ++k
, octet1
);
337 proto_item_set_end(item
, tvb
, offset
);
338 proto_tree_add_text(sched_tree
, tvb
, offset
, -1, "Padding");
344 dissect_cbch(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
346 fragment_head
*frag_data
= NULL
;
347 guint8 octet
, lb
, lpd
, seq_num
;
350 proto_item
*cbch_item
= NULL
;
351 proto_tree
*cbch_tree
= NULL
;
352 tvbuff_t
*reass_tvb
= NULL
, *msg_tvb
= NULL
;
354 len
= tvb_length(tvb
);
356 octet
= tvb_get_guint8(tvb
, offset
);
359 * create the protocol tree
361 cbch_item
= proto_tree_add_protocol_format(tree
, proto_cbch
, tvb
, 0, len
,
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_text(cbch_tree
, tvb
, offset
, 1, "CBCH Block");
370 proto_tree_add_uint(cbch_tree
, hf_gsm_cbch_spare_bit
, tvb
, offset
, 1, octet
);
371 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 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 proto_tree_add_text(cbch_tree
, tvb
, offset
, 1, "NULL message");
415 call_dissector(data_handle
, tvb
, pinfo
, cbch_tree
);
419 proto_tree_add_text(cbch_tree
, tvb
, offset
, 1, "reserved Sequence Number");
420 call_dissector(data_handle
, 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_guint8(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 proto_tree_add_text(cbch_tree
, tvb
, offset
, 1, "invalid Link Protocol Discriminator");
447 call_dissector(data_handle
, tvb
, pinfo
, cbch_tree
);
451 /* Register the protocol with Wireshark */
453 proto_register_gsm_cbch(void)
455 /* Setup list of header fields */
456 static hf_register_info hf_smscb
[] =
458 { &hf_gsm_cbch_spare_bit
,
459 { "GSM CBCH spare bit", "gsm_cbch.block_type.spare",
460 FT_UINT8
, BASE_HEX
, NULL
, 0x80,
464 { "GSM CBCH Link Protocol Discriminator", "gsm_cbch.block_type.lpd",
465 FT_UINT8
, BASE_DEC
, VALS(block_type_lpd_strings
), 0x60,
469 { "GSM CBCH Last Block", "gsm_cbch.block_type.lb",
470 FT_UINT8
, BASE_DEC
, NULL
, 0x10,
473 { &hf_gsm_cbch_seq_num
,
474 { "GSM CBCH Sequence Number", "gsm_cbch.block_type.seq_num",
475 FT_UINT8
, BASE_DEC
, VALS(block_type_seq_num_values
), 0x0F,
478 { &hf_gsm_cbch_sched_type
,
479 { "GSM CBCH Schedule Type", "gsm_cbch.sched_type",
480 FT_UINT8
, BASE_DEC
, VALS(sched_type_values
), 0xC0,
483 { &hf_gsm_cbch_sched_begin_slot
,
484 { "GSM CBCH Schedule Begin slot", "gsm_cbch.schedule_begin",
485 FT_UINT8
, BASE_DEC
, NULL
, 0x3F,
488 { &hf_gsm_cbch_sched_spare
,
489 { "GSM CBCH Schedule Spare Bits", "gsm_cbch.sched_spare",
490 FT_UINT8
, BASE_DEC
, NULL
, 0xC0,
493 { &hf_gsm_cbch_sched_end_slot
,
494 { "GSM CBCH Schedule End Slot", "gsm_cbch.sched_end",
495 FT_UINT8
, BASE_DEC
, NULL
, 0x3F,
499 { &hf_gsm_cbch_sched_msg_id
,
500 { "GSM CBCH Schedule Message ID", "gsm_cbch.sched_msg_id",
501 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
507 { &hf_cbch_fragment_overlap
,
508 { "Fragment overlap",
509 "gsm_cbch.fragment.overlap",
510 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
511 "Fragment overlaps with other fragments", HFILL
514 { &hf_cbch_fragment_overlap_conflict
,
515 { "Conflicting data in fragment overlap",
516 "gsm_cbch.fragment.overlap.conflict",
517 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
518 "Overlapping fragments contained conflicting data", HFILL
521 { &hf_cbch_fragment_multiple_tails
,
522 { "Multiple tail fragments found",
523 "gsm_cbch.fragment.multipletails",
524 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
525 "Several tails were found when defragmenting the packet", HFILL
528 { &hf_cbch_fragment_too_long_fragment
,
529 { "Fragment too long",
530 "gsm_cbch.fragment.toolongfragment",
531 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
532 "Fragment contained data past end of packet", HFILL
535 { &hf_cbch_fragment_error
,
536 { "Defragmentation error",
537 "gsm_cbch.fragment.error",
538 FT_FRAMENUM
, BASE_NONE
, NULL
, 0x0,
539 "Defragmentation error due to illegal fragments", HFILL
542 { &hf_cbch_fragment_count
,
543 { "Fragmentation count",
544 "gsm_cbch.fragment.count",
545 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
546 "Count of CBCH Fragments", HFILL
549 { &hf_cbch_reassembled_in
,
551 "gsm_cbch.reassembled.in",
552 FT_FRAMENUM
, BASE_NONE
, NULL
, 0x0,
553 "CBCH fragments are reassembled in the given packet", HFILL
556 { &hf_cbch_reassembled_length
,
557 { "Reassembled message length is one less than indicated here",
558 "gsm_cbch.reassembled.length",
559 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
560 "The total length of the reassembled message", HFILL
566 FT_FRAMENUM
, BASE_NONE
, NULL
, 0x0,
570 { &hf_cbch_fragments
,
572 "gsm_cbch.fragments",
573 FT_NONE
, BASE_NONE
, NULL
, 0x0,
579 /* Setup protocol subtree array */
580 static gint
*ett
[] = {
583 &ett_schedule_new_msg
,
588 /* Register the protocol name and description */
589 proto_cbch
= proto_register_protocol("GSM Cell Broadcast Channel", "GSM CBCH", "gsm_cbch");
590 proto_register_field_array(proto_cbch
, hf_smscb
, array_length(hf_smscb
));
592 /* subdissector code */
593 register_dissector("gsm_cbch", dissect_cbch
, proto_cbch
);
594 register_init_routine(cbch_defragment_init
);
597 proto_register_subtree_array(ett
, array_length(ett
));
601 proto_reg_handoff_gsm_cbch(void)
603 data_handle
= find_dissector("data");
604 cbs_handle
= find_dissector("gsm_cbs");