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