From Pascal Quantin:
[metze/wireshark/wip.git] / epan / dissectors / packet-gsm_a_rp.c
1 /* packet-gsm_a_rp.c
2  * Routines for GSM A Interface RP dissection - SMS GSM layer 3
3  *
4  * Copyright 2003, Michael Lum <mlum [AT] telostech.com>
5  * In association with Telos Technology Inc.
6  *
7  * Title                3GPP                    Other
8  *
9  *   Reference [5]
10  *   Point-to-Point (PP) Short Message Service (SMS)
11  *   support on mobile radio interface
12  *   (3GPP TS 24.011 version 4.1.1 Release 4)
13  *
14  * $Id$
15  *
16  * Wireshark - Network traffic analyzer
17  * By Gerald Combs <gerald@wireshark.org>
18  * Copyright 1998 Gerald Combs
19  *
20  * This program is free software; you can redistribute it and/or
21  * modify it under the terms of the GNU General Public License
22  * as published by the Free Software Foundation; either version 2
23  * of the License, or (at your option) any later version.
24  *
25  * This program is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28  * GNU General Public License for more details.
29  *
30  * You should have received a copy of the GNU General Public License
31  * along with this program; if not, write to the Free Software
32  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
33  */
34
35 #ifdef HAVE_CONFIG_H
36 # include "config.h"
37 #endif
38
39 #include <stdio.h>
40 #include <stdlib.h>
41
42 #include <string.h>
43
44 #include <epan/packet.h>
45 #include <epan/tap.h>
46
47 #include "packet-sccp.h"
48 #include "packet-gsm_a_common.h"
49
50 /* PROTOTYPES/FORWARDS */
51
52 static const value_string gsm_rp_msg_strings[] = {
53         { 0x00, "RP-DATA (MS to Network)" },
54         { 0x01, "RP-DATA (Network to MS)" },
55         { 0x02, "RP-ACK (MS to Network)" },
56         { 0x03, "RP-ACK (Network to MS)" },
57         { 0x04, "RP-ERROR (MS to Network)" },
58         { 0x05, "RP-ERROR (Network to MS)" },
59         { 0x06, "RP-SMMA (MS to Network)" },
60         { 0, NULL }
61 };
62
63 const value_string gsm_rp_elem_strings[] = {
64         /* Short Message Service RP Information Elements [5] 8.2 */
65         { 0x00, "RP-Message Reference" },
66         { 0x00, "RP-Origination Address" },
67         { 0x00, "RP-Destination Address" },
68         { 0x00, "RP-User Data" },
69         { 0x00, "RP-Cause" },
70         { 0, NULL }
71 };
72
73 /* Initialize the protocol and registered fields */
74 static int proto_a_rp = -1;
75
76 static int hf_gsm_a_rp_msg_type = -1;
77 int hf_gsm_a_rp_elem_id = -1;
78
79 /* Initialize the subtree pointers */
80 static gint ett_rp_msg = -1;
81
82 static char a_bigbuf[1024];
83
84 static dissector_table_t sms_dissector_table;   /* SMS TPDU */
85
86 static packet_info *g_pinfo;
87 static proto_tree *g_tree;
88
89 typedef enum
90 {
91         /* Short Message Service Information Elements [5] 8.2 */
92         DE_RP_MESSAGE_REF,                              /* RP-Message Reference */
93         DE_RP_ORIG_ADDR,                                /* RP-Origination Address */
94         DE_RP_DEST_ADDR,                                /* RP-Destination Address */
95         DE_RP_USER_DATA,                                /* RP-User Data */
96         DE_RP_CAUSE,                                    /* RP-Cause */
97         DE_RP_NONE                                                      /* NONE */
98 }
99 rp_elem_idx_t;
100
101 #define NUM_GSM_RP_ELEM (sizeof(gsm_rp_elem_strings)/sizeof(value_string))
102 gint ett_gsm_rp_elem[NUM_GSM_RP_ELEM];
103
104 /*
105  * [5] 8.2.3
106  */
107 static guint8
108 de_rp_message_ref(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len _U_, gchar *add_string _U_, int string_len _U_)
109 {
110         guint8  oct;
111         guint32 curr_offset;
112
113         curr_offset = offset;
114
115         oct = tvb_get_guint8(tvb, curr_offset);
116
117         proto_tree_add_text(tree,
118                 tvb, curr_offset, 1,
119                 "RP-Message Reference: 0x%02x (%u)",
120                 oct,
121                 oct);
122
123         curr_offset++;
124
125         /* no length check possible */
126
127         return(curr_offset - offset);
128 }
129
130 /*
131  * [5] 8.2.5.1
132  */
133 static guint8
134 de_rp_orig_addr(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string, int string_len)
135 {
136         return(de_cld_party_bcd_num(tvb, tree, offset, len, add_string, string_len));
137 }
138
139 /*
140  * [5] 8.2.5.2
141  */
142 static guint8
143 de_rp_dest_addr(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string, int string_len)
144 {
145         return(de_cld_party_bcd_num(tvb, tree, offset, len, add_string, string_len));
146 }
147
148 /*
149  * [5] 8.2.5.3
150  */
151 static guint8
152 de_rp_user_data(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string _U_, int string_len _U_)
153 {
154         guint32 curr_offset;
155         tvbuff_t        *tpdu_tvb;
156
157         curr_offset = offset;
158
159         proto_tree_add_text(tree, tvb, curr_offset, len,
160                 "TPDU (not displayed)");
161
162         /*
163          * dissect the embedded TPDU message
164          */
165         tpdu_tvb = tvb_new_subset(tvb, curr_offset, len, len);
166
167         dissector_try_port(sms_dissector_table, 0, tpdu_tvb, g_pinfo, g_tree);
168
169         curr_offset += len;
170
171         EXTRANEOUS_DATA_CHECK(len, curr_offset - offset);
172
173         return(curr_offset - offset);
174 }
175
176 /*
177  * [5] 8.2.5.4
178  */
179 static guint8
180 de_rp_cause(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string, int string_len)
181 {
182         guint8  oct;
183         guint32 curr_offset;
184         const gchar *str;
185
186         curr_offset = offset;
187
188         oct = tvb_get_guint8(tvb, curr_offset);
189
190         other_decode_bitfield_value(a_bigbuf, oct, 0x80, 8);
191         proto_tree_add_text(tree,
192                 tvb, curr_offset, 1,
193                 "%s :  Extension: %s",
194                 a_bigbuf,
195                 (oct & 0x80) ? "extended" : "not extended");
196
197         switch (oct & 0x7f)
198         {
199         case 1: str = "Unassigned (unallocated) number"; break;
200         case 8: str = "Operator determined barring"; break;
201         case 10: str = "Call barred"; break;
202         case 11: str = "Reserved"; break;
203         case 21: str = "Short message transfer rejected"; break;
204         case 22: str = "Memory capacity exceeded"; break;
205         case 27: str = "Destination out of order"; break;
206         case 28: str = "Unidentified subscriber"; break;
207         case 29: str = "Facility rejected"; break;
208         case 30: str = "Unknown subscriber"; break;
209         case 38: str = "Network out of order"; break;
210         case 41: str = "Temporary failure"; break;
211         case 42: str = "Congestion"; break;
212         case 47: str = "Resources unavailable, unspecified"; break;
213         case 50: str = "Requested facility not subscribed"; break;
214         case 69: str = "Requested facility not implemented"; break;
215         case 81: str = "Invalid short message transfer reference value"; break;
216         case 95: str = "Semantically incorrect message"; break;
217         case 96: str = "Invalid mandatory information"; break;
218         case 97: str = "Message type non-existent or not implemented"; break;
219         case 98: str = "Message not compatible with short message protocol state"; break;
220         case 99: str = "Information element non-existent or not implemented"; break;
221         case 111: str = "Protocol error, unspecified"; break;
222         case 127: str = "Interworking, unspecified"; break;
223         default:
224                 str = "Reserved";
225                 break;
226         }
227
228         other_decode_bitfield_value(a_bigbuf, oct, 0x7f, 8);
229         proto_tree_add_text(tree,
230                 tvb, curr_offset, 1,
231                 "%s :  Cause: (%u) %s",
232                 a_bigbuf,
233                 oct & 0x7f,
234                 str);
235
236         curr_offset++;
237
238         if (add_string)
239                 g_snprintf(add_string, string_len, " - (%u) %s", oct & 0x7f, str);
240
241         NO_MORE_DATA_CHECK(len);
242
243         proto_tree_add_text(tree,
244                 tvb, curr_offset, len - (curr_offset - offset),
245                 "Diagnostic field");
246
247         curr_offset += len - (curr_offset - offset);
248
249         EXTRANEOUS_DATA_CHECK(len, curr_offset - offset);
250
251         return(curr_offset - offset);
252 }
253
254 guint8 (*rp_elem_fcn[])(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len, gchar *add_string, int string_len) = {
255         /* Short Message Service Information Elements [5] 8.2 */
256         de_rp_message_ref,      /* RP-Message Reference */
257         de_rp_orig_addr,        /* RP-Origination Address */
258         de_rp_dest_addr,        /* RP-Destination Address */
259         de_rp_user_data,        /* RP-User Data */
260         de_rp_cause,    /* RP-Cause */
261         NULL,   /* NONE */
262 };
263
264 /* MESSAGE FUNCTIONS */
265
266 /*
267  * [5] 7.3.1.1
268  */
269 static void
270 rp_data_n_ms(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len)
271 {
272         guint32 curr_offset;
273         guint32 consumed;
274         guint   curr_len;
275
276         curr_offset = offset;
277         curr_len = len;
278
279         g_pinfo->p2p_dir = P2P_DIR_SENT;
280
281         ELEM_MAND_V(GSM_A_PDU_TYPE_RP, DE_RP_MESSAGE_REF);
282
283         ELEM_MAND_LV(GSM_A_PDU_TYPE_RP, DE_RP_ORIG_ADDR, "");
284
285         ELEM_MAND_LV(GSM_A_PDU_TYPE_RP, DE_RP_DEST_ADDR, "");
286
287         ELEM_MAND_LV(GSM_A_PDU_TYPE_RP, DE_RP_USER_DATA, "");
288
289         EXTRANEOUS_DATA_CHECK(curr_len, 0);
290 }
291
292 /*
293  * [5] 7.3.1.2
294  */
295 static void
296 rp_data_ms_n(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len)
297 {
298         guint32 curr_offset;
299         guint32 consumed;
300         guint   curr_len;
301
302         curr_offset = offset;
303         curr_len = len;
304
305         g_pinfo->p2p_dir = P2P_DIR_RECV;
306
307         ELEM_MAND_V(GSM_A_PDU_TYPE_RP, DE_RP_MESSAGE_REF);
308
309         ELEM_MAND_LV(GSM_A_PDU_TYPE_RP, DE_RP_ORIG_ADDR, "");
310
311         ELEM_MAND_LV(GSM_A_PDU_TYPE_RP, DE_RP_DEST_ADDR, "");
312
313         ELEM_MAND_LV(GSM_A_PDU_TYPE_RP, DE_RP_USER_DATA, "");
314
315         EXTRANEOUS_DATA_CHECK(curr_len, 0);
316 }
317
318 /*
319  * [5] 7.3.2
320  */
321 static void
322 rp_smma(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len)
323 {
324         guint32 curr_offset;
325         guint32 consumed;
326         guint   curr_len;
327
328         curr_offset = offset;
329         curr_len = len;
330
331         ELEM_MAND_V(GSM_A_PDU_TYPE_RP, DE_RP_MESSAGE_REF);
332
333         EXTRANEOUS_DATA_CHECK(curr_len, 0);
334 }
335
336 /*
337  * [5] 7.3.3
338  */
339 static void
340 rp_ack_n_ms(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len)
341 {
342         guint32 curr_offset;
343         guint32 consumed;
344         guint   curr_len;
345
346         curr_offset = offset;
347         curr_len = len;
348
349         g_pinfo->p2p_dir = P2P_DIR_SENT;
350
351         ELEM_MAND_V(GSM_A_PDU_TYPE_RP, DE_RP_MESSAGE_REF);
352
353         ELEM_OPT_TLV(0x41, GSM_A_PDU_TYPE_RP, DE_RP_USER_DATA, "");
354
355         EXTRANEOUS_DATA_CHECK(curr_len, 0);
356 }
357
358 /*
359  * [5] 7.3.3
360  */
361 static void
362 rp_ack_ms_n(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len)
363 {
364         guint32 curr_offset;
365         guint32 consumed;
366         guint   curr_len;
367
368         curr_offset = offset;
369         curr_len = len;
370
371         g_pinfo->p2p_dir = P2P_DIR_RECV;
372
373         ELEM_MAND_V(GSM_A_PDU_TYPE_RP, DE_RP_MESSAGE_REF);
374
375         ELEM_OPT_TLV(0x41, GSM_A_PDU_TYPE_RP, DE_RP_USER_DATA, "");
376
377         EXTRANEOUS_DATA_CHECK(curr_len, 0);
378 }
379
380 /*
381  * [5] 7.3.4
382  */
383 static void
384 rp_error_n_ms(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len)
385 {
386         guint32 curr_offset;
387         guint32 consumed;
388         guint   curr_len;
389
390         curr_offset = offset;
391         curr_len = len;
392
393         g_pinfo->p2p_dir = P2P_DIR_SENT;
394
395         ELEM_MAND_V(GSM_A_PDU_TYPE_RP, DE_RP_MESSAGE_REF);
396
397         ELEM_MAND_LV(GSM_A_PDU_TYPE_RP, DE_RP_CAUSE, "");
398
399         ELEM_OPT_TLV(0x41, GSM_A_PDU_TYPE_RP, DE_RP_USER_DATA, "");
400
401         EXTRANEOUS_DATA_CHECK(curr_len, 0);
402 }
403
404 /*
405  * [5] 7.3.4
406  */
407 static void
408 rp_error_ms_n(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len)
409 {
410         guint32 curr_offset;
411         guint32 consumed;
412         guint   curr_len;
413
414         curr_offset = offset;
415         curr_len = len;
416
417         g_pinfo->p2p_dir = P2P_DIR_RECV;
418
419         ELEM_MAND_V(GSM_A_PDU_TYPE_RP, DE_RP_MESSAGE_REF);
420
421         ELEM_MAND_LV(GSM_A_PDU_TYPE_RP, DE_RP_CAUSE, "");
422
423         ELEM_OPT_TLV(0x41, GSM_A_PDU_TYPE_RP, DE_RP_USER_DATA, "");
424
425         EXTRANEOUS_DATA_CHECK(curr_len, 0);
426 }
427
428 #define NUM_GSM_RP_MSG (sizeof(gsm_rp_msg_strings)/sizeof(value_string))
429 static gint ett_gsm_rp_msg[NUM_GSM_RP_MSG];
430 static void (*rp_msg_fcn[])(tvbuff_t *tvb, proto_tree *tree, guint32 offset, guint len) = {
431         rp_data_ms_n,   /* RP-DATA (MS to Network) */
432         rp_data_n_ms,   /* RP-DATA (Network to MS */
433         rp_ack_ms_n,    /* RP-ACK (MS to Network) */
434         rp_ack_n_ms,    /* RP-ACK (Network to MS) */
435         rp_error_ms_n,  /* RP-ERROR (MS to Network) */
436         rp_error_n_ms,  /* RP-ERROR (Network to MS) */
437         rp_smma,        /* RP-SMMA (MS to Network) */
438         NULL,   /* NONE */
439 };
440
441 /* GENERIC DISSECTOR FUNCTIONS */
442
443 static void
444 dissect_rp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
445 {
446         guint8  oct;
447         guint32 offset, saved_offset;
448         guint32 len;
449         gint    idx;
450         proto_item      *rp_item = NULL;
451         proto_tree      *rp_tree = NULL;
452         const gchar     *str;
453
454         if (check_col(pinfo->cinfo, COL_INFO))
455         {
456                 col_append_str(pinfo->cinfo, COL_INFO, "(RP) ");
457         }
458
459         /*
460          * In the interest of speed, if "tree" is NULL, don't do any work
461          * not necessary to generate protocol tree items.
462          */
463         if (!tree)
464         {
465                 return;
466         }
467
468         offset = 0;
469         saved_offset = offset;
470
471         g_pinfo = pinfo;
472         g_tree = tree;
473
474         len = tvb_length(tvb);
475
476         /*
477          * add RP message name
478          */
479         oct = tvb_get_guint8(tvb, offset++);
480
481         str = match_strval_idx((guint32) oct, gsm_rp_msg_strings, &idx);
482
483         /*
484          * create the protocol tree
485          */
486         if (str == NULL)
487         {
488                 rp_item =
489                         proto_tree_add_protocol_format(tree, proto_a_rp, tvb, 0, len,
490                                 "GSM A-I/F RP - Unknown RP Message Type (0x%02x)",
491                                 oct);
492
493                 rp_tree = proto_item_add_subtree(rp_item, ett_rp_msg);
494         }
495         else
496         {
497                 rp_item =
498                         proto_tree_add_protocol_format(tree, proto_a_rp, tvb, 0, -1,
499                                 "GSM A-I/F RP - %s",
500                                 str);
501
502                 rp_tree = proto_item_add_subtree(rp_item, ett_gsm_rp_msg[idx]);
503
504                 if (check_col(pinfo->cinfo, COL_INFO))
505                 {
506                         col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", str);
507                 }
508         }
509
510         /*
511          * add RP message name
512          */
513         proto_tree_add_uint_format(rp_tree, hf_gsm_a_rp_msg_type,
514                 tvb, saved_offset, 1, oct, "Message Type %s", str ? str : "(Unknown)");
515
516         if (str == NULL) return;
517
518         if ((len - offset) <= 0) return;
519
520         /*
521          * decode elements
522          */
523         if (rp_msg_fcn[idx] == NULL)
524         {
525                 proto_tree_add_text(rp_tree,
526                         tvb, offset, len - offset,
527                 "Message Elements");
528         }
529         else
530         {
531                 (*rp_msg_fcn[idx])(tvb, rp_tree, offset, len - offset);
532         }
533 }
534
535 /* Register the protocol with Wireshark */
536 void
537 proto_register_gsm_a_rp(void)
538 {
539         guint           i;
540         guint           last_offset;
541
542         /* Setup list of header fields */
543
544         static hf_register_info hf[] =
545         {
546         { &hf_gsm_a_rp_msg_type,
547                 { "RP Message Type",    "gsm_a.rp_msg_type",
548                 FT_UINT8, BASE_HEX, VALS(gsm_rp_msg_strings), 0x0,
549                 "", HFILL }
550         },
551         { &hf_gsm_a_rp_elem_id,
552                 { "Element ID", "gsm_a_rp.elem_id",
553                 FT_UINT8, BASE_DEC, NULL, 0,
554                 "", HFILL }
555         },
556         };
557
558         /* Setup protocol subtree array */
559 #define NUM_INDIVIDUAL_ELEMS    1
560         static gint *ett[NUM_INDIVIDUAL_ELEMS +
561                         NUM_GSM_RP_MSG +
562                         NUM_GSM_RP_ELEM];
563
564         ett[0] = &ett_rp_msg;
565
566         last_offset = NUM_INDIVIDUAL_ELEMS;
567
568         for (i=0; i < NUM_GSM_RP_MSG; i++, last_offset++)
569         {
570                 ett_gsm_rp_msg[i] = -1;
571                 ett[last_offset] = &ett_gsm_rp_msg[i];
572         }
573
574         for (i=0; i < NUM_GSM_RP_ELEM; i++, last_offset++)
575         {
576                 ett_gsm_rp_elem[i] = -1;
577                 ett[last_offset] = &ett_gsm_rp_elem[i];
578         }
579
580         /* Register the protocol name and description */
581
582         proto_a_rp =
583                 proto_register_protocol("GSM A-I/F RP", "GSM RP", "gsm_a_rp");
584
585         proto_register_field_array(proto_a_rp, hf, array_length(hf));
586
587         sms_dissector_table =
588                 register_dissector_table("gsm_a.sms_tpdu", "GSM SMS TPDU",
589                 FT_UINT8, BASE_DEC);
590
591         proto_register_subtree_array(ett, array_length(ett));
592
593         register_dissector("gsm_a_rp", dissect_rp, proto_a_rp);
594 }
595
596 void
597 proto_reg_handoff_gsm_a_rp(void)
598 {
599 }