Don't do fcn calls in arg of g_?to??(); Macro may very well eval args multiple times.
[obnox/wireshark/wip.git] / epan / dissectors / packet-sscop.c
1 /* packet-sscop.c
2  * Routines for SSCOP (Q.2110, Q.SAAL) frame disassembly
3  * Guy Harris <guy@alum.mit.edu>
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998
10  *
11  *
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.
16  *
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.
21  *
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <glib.h>
32 #include <epan/packet.h>
33 #include <prefs.h>
34 #include "packet-sscop.h"
35
36 int proto_sscop = -1;
37
38 static int hf_sscop_type = -1;
39 static int hf_sscop_sq = -1;
40 static int hf_sscop_mr = -1;
41 static int hf_sscop_s = -1;
42 static int hf_sscop_ps = -1;
43 static int hf_sscop_r = -1;
44 static int hf_sscop_stat_s = -1;
45 static int hf_sscop_stat_count = -1;
46
47 static gint ett_sscop = -1;
48 static gint ett_stat = -1;
49
50 static dissector_handle_t q2931_handle;
51 static dissector_handle_t data_handle;
52 static dissector_handle_t sscf_nni_handle;
53 static dissector_handle_t alcap_handle;
54 static dissector_handle_t nbap_handle;
55
56 static module_t *sscop_module;
57
58 static range_t *global_udp_port_range;
59
60 static dissector_handle_t sscop_handle;
61
62
63 static enum_val_t sscop_payload_dissector_options[] = {
64   { "data",     "Data (no further dissection)", DATA_DISSECTOR },
65   { "Q.2931",   "Q.2931",       Q2931_DISSECTOR },
66   { "SSCF-NNI", "SSCF-NNI (MTP3-b)",            SSCF_NNI_DISSECTOR },
67   { "ALCAP",    "ALCAP",                        ALCAP_DISSECTOR },
68   { "NBAP",     "NBAP",                         NBAP_DISSECTOR },
69   { NULL,       NULL,                           0 }
70 };
71
72 static guint sscop_payload_dissector = Q2931_DISSECTOR;
73 static dissector_handle_t default_handle;
74
75 static sscop_info_t sscop_info;
76 /*
77  * See
78  *
79  *      http://www.protocols.com/pbook/atmsig.htm
80  *
81  * for some information on SSCOP, although, alas, not the actual PDU
82  * type values - those I got from the FreeBSD 3.2 ATM code.
83  */
84
85 /*
86  * SSCOP PDU types.
87  */
88 #define SSCOP_TYPE_MASK 0x0f
89
90 #define SSCOP_BGN       0x01    /* Begin */
91 #define SSCOP_BGAK      0x02    /* Begin Acknowledge */
92 #define SSCOP_BGREJ     0x07    /* Begin Reject */
93 #define SSCOP_END       0x03    /* End */
94 #define SSCOP_ENDAK     0x04    /* End Acknowledge */
95 #define SSCOP_RS        0x05    /* Resynchronization */
96 #define SSCOP_RSAK      0x06    /* Resynchronization Acknowledge */
97 #define SSCOP_SD        0x08    /* Sequenced Data */
98 #define SSCOP_SDP       0x09    /* Sequenced Data with Poll */
99 #define SSCOP_POLL      0x0a    /* Status Request */
100 #define SSCOP_STAT      0x0b    /* Solicited Status Response */
101 #define SSCOP_USTAT     0x0c    /* Unsolicited Status Response */
102 #define SSCOP_UD        0x0d    /* Unnumbered Data */
103 #define SSCOP_MD        0x0e    /* Management Data */
104 #define SSCOP_ER        0x09    /* Error Recovery */
105 #define SSCOP_ERAK      0x0f    /* Error Acknowledge */
106
107 #define SSCOP_S         0x10    /* Source bit in End PDU */
108
109 /*
110  * XXX - how to distinguish SDP from ER?
111  */
112 static const value_string sscop_type_vals[] = {
113         { SSCOP_BGN,   "Begin" },
114         { SSCOP_BGAK,  "Begin Acknowledge" },
115         { SSCOP_BGREJ, "Begin Reject" },
116         { SSCOP_END,   "End" },
117         { SSCOP_ENDAK, "End Acknowledge" },
118         { SSCOP_RS,    "Resynchronization" },
119         { SSCOP_RSAK,  "Resynchronization Acknowledge" },
120         { SSCOP_SD,    "Sequenced Data" },
121 #if 0
122         { SSCOP_SDP,   "Sequenced Data with Poll" },
123 #endif
124         { SSCOP_POLL,  "Status Request" },
125         { SSCOP_STAT,  "Solicited Status Response" },
126         { SSCOP_USTAT, "Unsolicited Status Response" },
127         { SSCOP_UD,    "Unnumbered Data" },
128         { SSCOP_MD,    "Management Data" },
129         { SSCOP_ER,    "Error Recovery" },
130         { SSCOP_ERAK,  "Error Acknowledge" },
131         { 0,            NULL }
132 };
133
134 /*
135  * The SSCOP "header" is a trailer, so the "offsets" are computed based
136  * on the length of the packet.
137  */
138
139 /*
140  * PDU type.
141  */
142 #define SSCOP_PDU_TYPE  (reported_length - 4)   /* single byte */
143
144 /*
145  * Begin PDU, Begin Acknowledge PDU (no N(SQ) in it), Resynchronization
146  * PDU, Resynchronization Acknowledge PDU (no N(SQ) in it in Q.SAAL),
147  * Error Recovery PDU, Error Recovery Acknoledge PDU (no N(SQ) in it).
148  */
149 #define SSCOP_N_SQ      (reported_length - 5)   /* One byte */
150 #define SSCOP_N_MR      (reported_length - 4)   /* lower 3 bytes thereof */
151
152 /*
153  * Sequenced Data PDU (no N(PS) in it), Sequenced Data with Poll PDU,
154  * Poll PDU.
155  */
156 #define SSCOP_N_PS      (reported_length - 8)   /* lower 3 bytes thereof */
157 #define SSCOP_N_S       (reported_length - 4)   /* lower 3 bytes thereof */
158
159 /*
160  * Solicited Status PDU, Unsolicited Status PDU (no N(PS) in it).
161  */
162 #define SSCOP_SS_N_PS   (reported_length - 12)  /* lower 3 bytes thereof */
163 #define SSCOP_SS_N_MR   (reported_length - 8)   /* lower 3 bytes thereof */
164 #define SSCOP_SS_N_R    (reported_length - 4)   /* lower 3 bytes thereof */
165
166 static void dissect_stat_list(proto_tree *tree, tvbuff_t *tvb,guint h) {
167         gint n,i;
168         proto_item* pi;
169
170         if ((n = (tvb_reported_length(tvb))/4 - h)) {
171                 pi = proto_tree_add_text(tree,tvb,0,n*4,"SD List");
172                 tree = proto_item_add_subtree(pi,ett_stat);
173
174                 for (i = 0; i < n; i++) {
175                         proto_tree_add_item(tree, hf_sscop_stat_s, tvb, i*4 + 1,3,ENC_BIG_ENDIAN);
176                 }
177         }
178 }
179
180 extern void
181 dissect_sscop_and_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, dissector_handle_t payload_handle)
182 {
183   guint reported_length;
184   proto_item *ti;
185   proto_tree *sscop_tree = NULL;
186   guint8 sscop_pdu_type;
187   int pdu_len;
188   int pad_len;
189   tvbuff_t *next_tvb;
190
191   reported_length = tvb_reported_length(tvb);   /* frame length */
192   sscop_pdu_type = tvb_get_guint8(tvb, SSCOP_PDU_TYPE);
193   sscop_info.type = sscop_pdu_type & SSCOP_TYPE_MASK;
194
195   col_set_str(pinfo->cinfo, COL_PROTOCOL, "SSCOP");
196   if (check_col(pinfo->cinfo, COL_INFO))
197     col_add_str(pinfo->cinfo, COL_INFO, val_to_str(sscop_info.type, sscop_type_vals,
198                                         "Unknown PDU type (0x%02x)"));
199
200   /*
201    * Find the length of the PDU and, if there's any payload and
202    * padding, the length of the padding.
203    */
204   switch (sscop_info.type) {
205
206   case SSCOP_SD:
207     pad_len = (sscop_pdu_type >> 6) & 0x03;
208     pdu_len = 4;
209     break;
210
211   case SSCOP_BGN:
212   case SSCOP_BGAK:
213   case SSCOP_BGREJ:
214   case SSCOP_END:
215   case SSCOP_RS:
216 #if 0
217   case SSCOP_SDP:
218 #endif
219     pad_len = (sscop_pdu_type >> 6) & 0x03;
220     sscop_info.payload_len = pdu_len = 8;
221     break;
222
223   case SSCOP_UD:
224     pad_len = (sscop_pdu_type >> 6) & 0x03;
225     sscop_info.payload_len = pdu_len = 4;
226     break;
227
228   default:
229     pad_len = 0;
230     pdu_len = reported_length;  /* No payload, just SSCOP */
231         sscop_info.payload_len = 0;
232     break;
233   }
234
235   if (tree) {
236     ti = proto_tree_add_protocol_format(tree, proto_sscop, tvb,
237                                         reported_length - pdu_len,
238                                         pdu_len, "SSCOP");
239     sscop_tree = proto_item_add_subtree(ti, ett_sscop);
240
241     proto_tree_add_item(sscop_tree, hf_sscop_type, tvb, SSCOP_PDU_TYPE, 1,ENC_BIG_ENDIAN);
242
243     switch (sscop_info.type) {
244
245     case SSCOP_BGN:
246     case SSCOP_RS:
247     case SSCOP_ER:
248       proto_tree_add_item(sscop_tree, hf_sscop_sq, tvb, SSCOP_N_SQ, 1,ENC_BIG_ENDIAN);
249       proto_tree_add_item(sscop_tree, hf_sscop_mr, tvb, SSCOP_N_MR + 1, 3, ENC_BIG_ENDIAN);
250       break;
251
252     case SSCOP_END:
253       proto_tree_add_text(sscop_tree, tvb, SSCOP_PDU_TYPE, 1,
254           "Source: %s", (sscop_pdu_type & SSCOP_S) ? "SSCOP" : "User");
255       break;
256
257     case SSCOP_BGAK:
258     case SSCOP_RSAK:
259                 proto_tree_add_item(sscop_tree, hf_sscop_mr, tvb, SSCOP_N_MR + 1, 3, ENC_BIG_ENDIAN);
260       break;
261
262     case SSCOP_ERAK:
263                 proto_tree_add_item(sscop_tree, hf_sscop_mr, tvb, SSCOP_N_MR + 1, 3, ENC_BIG_ENDIAN);
264       break;
265
266     case SSCOP_SD:
267                 proto_tree_add_item(sscop_tree, hf_sscop_s, tvb, SSCOP_N_S + 1, 3, ENC_BIG_ENDIAN);
268       break;
269
270 #if 0
271     case SSCOP_SDP:
272 #endif
273     case SSCOP_POLL:
274       proto_tree_add_item(sscop_tree, hf_sscop_ps, tvb, SSCOP_N_PS + 1, 3,ENC_BIG_ENDIAN);
275           proto_tree_add_item(sscop_tree, hf_sscop_s, tvb, SSCOP_N_S + 1, 3,ENC_BIG_ENDIAN);
276       break;
277
278     case SSCOP_STAT:
279                 proto_tree_add_item(sscop_tree, hf_sscop_ps, tvb, SSCOP_SS_N_PS + 1, 3,ENC_BIG_ENDIAN);
280                 proto_tree_add_item(sscop_tree, hf_sscop_mr, tvb, SSCOP_SS_N_MR + 1, 3, ENC_BIG_ENDIAN);
281                 proto_tree_add_item(sscop_tree, hf_sscop_r, tvb, SSCOP_SS_N_R + 1, 3,ENC_BIG_ENDIAN);
282                 dissect_stat_list(sscop_tree,tvb,3);
283       break;
284
285     case SSCOP_USTAT:
286                 proto_tree_add_item(sscop_tree, hf_sscop_mr, tvb, SSCOP_SS_N_MR + 1, 3, ENC_BIG_ENDIAN);
287                 proto_tree_add_item(sscop_tree, hf_sscop_r, tvb, SSCOP_SS_N_R + 1, 3,ENC_BIG_ENDIAN);
288                 dissect_stat_list(sscop_tree,tvb,2);
289       break;
290     }
291   }
292
293   /*
294    * Dissect the payload, if any.
295    *
296    * XXX - what about a Management Data PDU?
297    */
298   switch (sscop_info.type) {
299
300   case SSCOP_SD:
301   case SSCOP_UD:
302   case SSCOP_BGN:
303   case SSCOP_BGAK:
304   case SSCOP_BGREJ:
305   case SSCOP_END:
306   case SSCOP_RS:
307 #if 0
308   case SSCOP_SDP:
309 #endif
310     if (tree) {
311       proto_tree_add_text(sscop_tree, tvb, SSCOP_PDU_TYPE, 1,
312                         "Pad length: %u", pad_len);
313     }
314
315     /*
316      * Compute length of data in PDU - subtract the trailer length
317      * and the pad length from the reported length.
318      */
319     reported_length -= (pdu_len + pad_len);
320
321     if (reported_length != 0) {
322       /*
323        * We know that we have all of the payload, because we know we have
324        * at least 4 bytes of data after the payload, i.e. the SSCOP trailer.
325        * Therefore, we know that the captured length of the payload is
326        * equal to the length of the payload.
327        */
328       next_tvb = tvb_new_subset(tvb, 0, reported_length, reported_length);
329       if (sscop_info.type == SSCOP_SD)
330       {
331                   call_dissector(payload_handle, next_tvb, pinfo, tree);
332       }
333     break;
334   }
335 }
336 }
337
338 static void dissect_sscop(tvbuff_t* tvb, packet_info* pinfo,proto_tree* tree)
339 {
340     struct _sscop_payload_info  *p_sscop_info;
341     dissector_handle_t subdissector;
342
343         /* Look for packet info for subdissector information */
344     p_sscop_info = p_get_proto_data(pinfo->fd, proto_sscop);
345
346         if ( p_sscop_info
347                  && ( subdissector = p_sscop_info->subdissector )
348                  && ( subdissector == data_handle
349                           || subdissector == q2931_handle
350                           || subdissector == sscf_nni_handle
351                           || subdissector == alcap_handle
352                           || subdissector == nbap_handle) )
353                 dissect_sscop_and_payload(tvb,pinfo,tree,subdissector);
354     else
355                 dissect_sscop_and_payload(tvb,pinfo,tree,default_handle);
356 }
357
358
359 static void range_delete_callback(guint32 port)
360 {
361     if (port) {
362         dissector_delete_uint("udp.port", port, sscop_handle);
363     }
364 }
365
366 static void range_add_callback(guint32 port)
367 {
368     if (port) {
369         dissector_add_uint("udp.port", port, sscop_handle);
370     }
371 }
372
373 /* Make sure handles for various protocols are initialized */
374 static void initialize_handles_once(void) {
375     static gboolean initialized = FALSE;
376     if (!initialized) {
377                 sscop_handle = create_dissector_handle(dissect_sscop, proto_sscop);
378
379                 q2931_handle = find_dissector("q2931");
380                 data_handle = find_dissector("data");
381                 sscf_nni_handle = find_dissector("sscf-nni");
382                 alcap_handle = find_dissector("alcap");
383                 nbap_handle = find_dissector("nbap");
384
385                 initialized = TRUE;
386     }
387 }
388
389 gboolean sscop_allowed_subdissector(dissector_handle_t handle)
390 {
391     initialize_handles_once();
392     if (handle == q2931_handle || handle == data_handle
393         || handle == sscf_nni_handle || handle == alcap_handle
394         || handle == nbap_handle)
395         return TRUE;
396     return FALSE;
397 }
398
399 void
400 proto_reg_handoff_sscop(void)
401 {
402   static gboolean prefs_initialized = FALSE;
403   static range_t *udp_port_range;
404
405   if (!prefs_initialized) {
406     initialize_handles_once();
407     prefs_initialized = TRUE;
408
409   } else {
410
411     range_foreach(udp_port_range, range_delete_callback);
412     g_free(udp_port_range);
413
414   }
415
416   udp_port_range = range_copy(global_udp_port_range);
417
418   range_foreach(udp_port_range, range_add_callback);
419
420   switch(sscop_payload_dissector) {
421           case DATA_DISSECTOR: default_handle = data_handle; break;
422           case Q2931_DISSECTOR: default_handle = q2931_handle; break;
423           case SSCF_NNI_DISSECTOR: default_handle = sscf_nni_handle; break;
424           case ALCAP_DISSECTOR: default_handle = alcap_handle; break;
425           case NBAP_DISSECTOR: default_handle = nbap_handle; break;
426         }
427
428 }
429
430 void
431 proto_register_sscop(void)
432 {
433         static hf_register_info hf[] = {
434                 { &hf_sscop_type, { "PDU Type", "sscop.type", FT_UINT8, BASE_HEX,       VALS(sscop_type_vals), SSCOP_TYPE_MASK, NULL, HFILL }},
435                 { &hf_sscop_sq, { "N(SQ)", "sscop.sq", FT_UINT8, BASE_DEC,      NULL, 0x0, NULL, HFILL }},
436                 { &hf_sscop_mr, { "N(MR)", "sscop.mr", FT_UINT24, BASE_DEC,     NULL, 0x0, NULL, HFILL }},
437                 { &hf_sscop_s, { "N(S)", "sscop.s", FT_UINT24, BASE_DEC, NULL, 0x0, NULL, HFILL }},
438                 { &hf_sscop_ps, { "N(PS)", "sscop.ps", FT_UINT24, BASE_DEC, NULL, 0x0, NULL, HFILL }},
439                 { &hf_sscop_r, { "N(R)", "sscop.r", FT_UINT24, BASE_DEC, NULL, 0x0, NULL, HFILL }},
440                 { &hf_sscop_stat_s, { "N(S)", "sscop.stat.s", FT_UINT24, BASE_DEC, NULL, 0x0,NULL, HFILL }},
441                 { &hf_sscop_stat_count, { "Number of NACKed pdus", "sscop.stat.count", FT_UINT32, BASE_DEC, NULL, 0x0,NULL, HFILL }}
442         };
443
444   static gint *ett[] = {
445     &ett_sscop,
446         &ett_stat
447   };
448
449   proto_sscop = proto_register_protocol("SSCOP", "SSCOP", "sscop");
450   proto_register_field_array(proto_sscop, hf, array_length(hf));
451   proto_register_subtree_array(ett, array_length(ett));
452   register_dissector("sscop", dissect_sscop, proto_sscop);
453
454   sscop_module = prefs_register_protocol(proto_sscop, proto_reg_handoff_sscop);
455
456   global_udp_port_range = range_empty();
457
458   prefs_register_range_preference(sscop_module, "udp.ports",
459                                  "SSCOP UDP port range",
460                                  "Set the UDP port for SSCOP messages encapsulated in UDP (0 to disable)",
461                                  &global_udp_port_range, MAX_UDP_PORT);
462
463   prefs_register_enum_preference(sscop_module, "payload",
464                                  "SSCOP payload protocol",
465                                  "SSCOP payload (dissector to call on SSCOP payload)",
466                                  (gint *)&sscop_payload_dissector,
467                                  sscop_payload_dissector_options, FALSE);
468 }
469