Define some fcns & vars as static...
[metze/wireshark/wip.git] / epan / dissectors / packet-sndcp.c
1 /* packet-sndcp.c
2  * Routines for Subnetwork Dependent Convergence Protocol (SNDCP) dissection
3  * Copyright 2000, Christian Falckenberg <christian.falckenberg@nortelnetworks.com>
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * 
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <stdlib.h>
31
32 #include <glib.h>
33
34 #include <epan/packet.h>
35 #include <epan/reassemble.h>
36
37 /* Bitmasks for the bits in the address field
38 */
39 #define MASK_X      0x80
40 #define MASK_F      0x40
41 #define MASK_T      0x20
42 #define MASK_M      0x10
43
44 /* Initialize the protocol and registered fields 
45 */
46 static int proto_sndcp       = -1;
47 static int hf_sndcp_x        = -1;
48 static int hf_sndcp_f        = -1;
49 static int hf_sndcp_t        = -1;
50 static int hf_sndcp_m        = -1;
51 static int hf_sndcp_nsapi    = -1;
52 static int hf_sndcp_nsapib   = -1;
53 static int hf_sndcp_dcomp    = -1;
54 static int hf_sndcp_pcomp    = -1;
55 static int hf_sndcp_segment  = -1;
56 static int hf_sndcp_npdu1    = -1;
57 static int hf_sndcp_npdu2    = -1;
58
59 /* These fields are used when reassembling N-PDU fragments 
60 */
61 static int hf_npdu_fragments                    = -1;
62 static int hf_npdu_fragment                     = -1;
63 static int hf_npdu_fragment_overlap             = -1;
64 static int hf_npdu_fragment_overlap_conflict    = -1;
65 static int hf_npdu_fragment_multiple_tails      = -1;
66 static int hf_npdu_fragment_too_long_fragment   = -1;
67 static int hf_npdu_fragment_error               = -1;
68 static int hf_npdu_reassembled_in               = -1;
69 static int hf_npdu_reassembled_length           = -1;
70
71 /* Initialize the subtree pointers 
72 */
73 static gint ett_sndcp                   = -1;
74 static gint ett_sndcp_address_field     = -1;
75 static gint ett_sndcp_compression_field = -1;
76 static gint ett_sndcp_npdu_field        = -1;
77 static gint ett_npdu_fragment           = -1;
78 static gint ett_npdu_fragments          = -1;
79
80 /* Structure needed for the fragmentation routines in reassemble.c
81 */
82 static const fragment_items npdu_frag_items = {
83     &ett_npdu_fragment,
84     &ett_npdu_fragments,
85     &hf_npdu_fragments,
86     &hf_npdu_fragment,
87     &hf_npdu_fragment_overlap,
88     &hf_npdu_fragment_overlap_conflict,
89     &hf_npdu_fragment_multiple_tails,
90     &hf_npdu_fragment_too_long_fragment,
91     &hf_npdu_fragment_error,
92     &hf_npdu_reassembled_in,
93     &hf_npdu_reassembled_length,
94     "fragments"
95 };
96
97 /* dissectors for the data portion of this protocol
98  */
99 static dissector_handle_t data_handle;
100 static dissector_handle_t ip_handle;
101
102 /* reassembly of N-PDU
103  */
104 static GHashTable       *npdu_fragment_table = NULL;
105 static GHashTable       *sndcp_reassembled_table = NULL;
106
107 static void
108 sndcp_defragment_init(void)
109 {
110   fragment_table_init(&npdu_fragment_table);
111   reassembled_table_init(&sndcp_reassembled_table);
112 }
113
114 /* value strings
115  */
116 static const value_string nsapi_t[] = {
117         {  0, "Escape mechanism for future extensions"},
118         {  1, "Point-to-Multipoint (PTM-M) Information" },
119         {  2, "Reserved for future use" },
120         {  3, "Reserved for future use" },
121         {  4, "Reserved for future use" },
122         {  5, "Dynamically allocated"},
123         {  6, "Dynamically allocated"},
124         {  7, "Dynamically allocated"},
125         {  8, "Dynamically allocated"},
126         {  9, "Dynamically allocated"},
127         { 10, "Dynamically allocated"},
128         { 11, "Dynamically allocated"},
129         { 12, "Dynamically allocated"},
130         { 13, "Dynamically allocated"},
131         { 14, "Dynamically allocated"},
132         { 15, "Dynamically allocated"},
133         {  0, NULL },
134 };
135
136 static const value_string nsapi_abrv[] = {
137         {  0, "0"},
138         {  1, "PTM-M" },
139         {  2, "2" },
140         {  3, "3"},
141         {  4, "4" },
142         {  5, "DYN5" },
143         {  6, "DYN6" },
144         {  7, "DYN7" },
145         {  8, "DYN8" },
146         {  9, "DYN9" },
147         { 10, "DYN10" },
148         { 11, "DYN11" },
149         { 12, "DYN12" },
150         { 13, "DYN13" },
151         { 14, "DYN14" },
152         { 15, "DYN15" },
153         {  0, NULL },
154 };
155
156 static const value_string compression_vals[] = {
157         {  0, "No compression"},
158         {  1, "Pointer to selected protocol/data compression mechanism" },
159         {  2, "Pointer to selected protocol/data compression mechanism" },
160         {  3, "Pointer to selected protocol/data compression mechanism" },
161         {  4, "Pointer to selected protocol/data compression mechanism" },
162         {  5, "Pointer to selected protocol/data compression mechanism" },
163         {  6, "Pointer to selected protocol/data compression mechanism" },
164         {  7, "Pointer to selected protocol/data compression mechanism" },
165         {  8, "Pointer to selected protocol/data compression mechanism" },
166         {  9, "Pointer to selected protocol/data compression mechanism" },
167         { 10, "Pointer to selected protocol/data compression mechanism" },
168         { 11, "Pointer to selected protocol/data compression mechanism" },
169         { 12, "Pointer to selected protocol/data compression mechanism" },
170         { 13, "Pointer to selected protocol/data compression mechanism" },
171         { 14, "Pointer to selected protocol/data compression mechanism" },
172         { 15, "Pointer to selected protocol/data compression mechanism" },
173         { 0, NULL },
174 };
175
176 static const true_false_string x_bit = {
177   "Invalid",
178   "Set to 0 by transmitting SNDCP entity (ignored by receiver)"
179 };
180 static const true_false_string f_bit = {
181   "This SN-PDU is the first segment of an N-PDU",
182   "This SN-PDU is not the first segment of an N-PDU"
183 };
184 static const true_false_string t_bit = {
185   "SN-UNITDATA PDU",
186   "SN-DATA PDU"
187 };
188 static const true_false_string m_bit = {
189   "Not the last segment of N-PDU, more segments to follow",
190   "Last segment of N-PDU"
191 };
192
193 /* Code to actually dissect the packets 
194 */
195 static void
196 dissect_sndcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
197 {
198   guint8         addr_field, comp_field, npdu_field1, nsapi, dcomp=0, pcomp=0;
199   guint16        offset=0, npdu=0, segment=0, npdu_field2;
200   tvbuff_t      *next_tvb, *npdu_tvb;
201   gint           len;
202   gboolean       first, more_frags, unack;
203
204   /* Set up structures needed to add the protocol subtree and manage it 
205    */
206   proto_item *ti, *address_field_item, *compression_field_item, *npdu_field_item;
207   proto_tree *sndcp_tree = NULL, *address_field_tree, *compression_field_tree, *npdu_field_tree;
208
209   /* Make entries in Protocol column and clear Info column on summary display 
210    */
211   col_set_str(pinfo->cinfo, COL_PROTOCOL, "SNDCP");
212   col_clear(pinfo->cinfo, COL_INFO);
213   
214   /* create display subtree for the protocol 
215    */
216   if (tree) {
217     ti         = proto_tree_add_item(tree, proto_sndcp, tvb, 0, -1, FALSE);
218     sndcp_tree = proto_item_add_subtree(ti, ett_sndcp);
219   }
220    
221   /* get address field from next byte
222    */
223   addr_field = tvb_get_guint8(tvb,offset);
224   nsapi      = addr_field & 0xF;
225   first      = addr_field & MASK_F;
226   more_frags = addr_field & MASK_M;
227   unack      = addr_field & MASK_T;
228
229   /* add subtree for the address field 
230    */
231   if (tree) {
232     address_field_item = proto_tree_add_uint_format(sndcp_tree,hf_sndcp_nsapi,
233                                                     tvb, offset,1, nsapi, 
234                                                     "Address field  NSAPI: %d", nsapi );
235     address_field_tree = proto_item_add_subtree(address_field_item, ett_sndcp_address_field);
236     proto_tree_add_boolean(address_field_tree, hf_sndcp_x, tvb,offset,1, addr_field );
237     proto_tree_add_boolean(address_field_tree, hf_sndcp_f, tvb,offset,1, addr_field );
238     proto_tree_add_boolean(address_field_tree, hf_sndcp_t, tvb,offset,1, addr_field );
239     proto_tree_add_boolean(address_field_tree, hf_sndcp_m, tvb,offset,1, addr_field );
240     proto_tree_add_uint(address_field_tree, hf_sndcp_nsapib, tvb, offset, 1, addr_field ); 
241   }
242   offset++;
243
244   /* get compression pointers from next byte if this is the first segment
245    */
246   if (first) {
247     comp_field = tvb_get_guint8(tvb,offset);
248     dcomp      = comp_field & 0xF0;
249     pcomp      = comp_field & 0x0F;
250     
251     /* add subtree for the compression field 
252      */
253     if (tree) {
254       if (!pcomp) { 
255         if (!dcomp) {
256           compression_field_item = proto_tree_add_text(sndcp_tree, tvb, offset,1, "No compression");
257         }
258         else { 
259           compression_field_item = proto_tree_add_text(sndcp_tree, tvb, offset,1, "Data compression");
260         }
261       }
262       else { 
263         if (!dcomp) {
264           compression_field_item = proto_tree_add_text(sndcp_tree, tvb, offset,1, "Protocol compression");
265         }
266         else { 
267           compression_field_item = proto_tree_add_text(sndcp_tree, tvb, offset,1, "Data and Protocol compression");
268         }
269       }
270       compression_field_tree = proto_item_add_subtree(compression_field_item, ett_sndcp_compression_field);
271       proto_tree_add_uint(compression_field_tree, hf_sndcp_dcomp, tvb, offset, 1, comp_field );
272       proto_tree_add_uint(compression_field_tree, hf_sndcp_pcomp, tvb, offset, 1, comp_field );
273     }
274     offset++;
275
276     /* get N-PDU number from next byte for acknowledged mode (only for first segment)
277      */
278     if (!unack) {
279       npdu = npdu_field1 = tvb_get_guint8(tvb,offset);
280       if (check_col(pinfo->cinfo, COL_INFO))
281         col_add_fstr(pinfo->cinfo, COL_INFO, "SN-DATA N-PDU %d", npdu_field1);
282       if (tree) {
283         npdu_field_item = proto_tree_add_text(sndcp_tree, tvb, offset,1, "Acknowledged mode, N-PDU %d", npdu_field1 );
284         npdu_field_tree = proto_item_add_subtree(npdu_field_item, ett_sndcp_npdu_field);
285         proto_tree_add_uint(npdu_field_tree, hf_sndcp_npdu1, tvb, offset, 1, npdu_field1 );
286       }
287       offset++;
288     }
289   }
290
291   /* get segment and N-PDU number from next two bytes for unacknowledged mode 
292    */
293   if (unack) {
294     npdu_field2     = tvb_get_ntohs(tvb, offset);
295     segment         = (npdu_field2 & 0xF000) >> 12;
296     npdu            = (npdu_field2 & 0x0FFF);
297     if (check_col(pinfo->cinfo, COL_INFO)) 
298       col_add_fstr(pinfo->cinfo, COL_INFO, "SN-UNITDATA N-PDU %d (segment %d)", npdu, segment);
299     if (tree) {
300       npdu_field_item = proto_tree_add_text(sndcp_tree, tvb, offset,2, "Unacknowledged mode, N-PDU %d (segment %d)", npdu, segment );
301       npdu_field_tree = proto_item_add_subtree(npdu_field_item, ett_sndcp_npdu_field);
302       proto_tree_add_uint(npdu_field_tree, hf_sndcp_segment, tvb, offset, 2, npdu_field2 );
303       proto_tree_add_uint(npdu_field_tree, hf_sndcp_npdu2, tvb, offset, 2, npdu_field2 );
304     }
305     offset         += 2;
306   }
307
308   /* handle N-PDU data, reassemble if necessary 
309    */
310   if (first && !more_frags) {
311     next_tvb = tvb_new_subset_remaining (tvb, offset);
312     
313     if (!dcomp && !pcomp) {
314       call_dissector(ip_handle, next_tvb, pinfo, tree);
315     }
316     else {
317       call_dissector(data_handle, next_tvb, pinfo, tree);
318     }
319   }
320   else {
321     /* Try reassembling fragments 
322      */
323     fragment_data  *fd_npdu         = NULL;
324     guint32         reassembled_in  = 0;
325     gboolean        save_fragmented = pinfo->fragmented;
326     
327     len = tvb_length_remaining(tvb, offset);
328     if(len<=0){
329         return;
330     }
331
332     pinfo->fragmented = TRUE;
333
334     if (unack) 
335       fd_npdu  = fragment_add_seq_check(tvb, offset, pinfo, npdu,
336                                   npdu_fragment_table, sndcp_reassembled_table, segment, len, more_frags);
337     else
338       fd_npdu  = fragment_add(tvb, offset, pinfo, npdu,
339                                   npdu_fragment_table, offset, len, more_frags);
340
341     npdu_tvb = process_reassembled_data(tvb, offset, pinfo,
342                                         "Reassembled N-PDU", fd_npdu, &npdu_frag_items,
343                                         NULL, sndcp_tree);
344     if (fd_npdu) {
345       /* Reassembled 
346        */
347       reassembled_in = fd_npdu->reassembled_in;
348       if (pinfo->fd->num == reassembled_in) {
349         /* Reassembled in this very packet:
350          * We can safely hand the tvb to the IP dissector 
351          */
352         call_dissector(ip_handle, npdu_tvb, pinfo, tree);
353       } 
354       else {
355         /* Not reassembled in this packet 
356          */
357         if (check_col(pinfo->cinfo, COL_INFO)) {
358           col_append_fstr(pinfo->cinfo, COL_INFO,
359                           " (N-PDU payload reassembled in packet %u)",
360                           fd_npdu->reassembled_in);
361         }
362         if (tree) {
363           proto_tree_add_text(sndcp_tree, tvb, offset, -1, "Payload");
364         }
365       }
366     } else {
367       /* Not reassembled yet, or not reassembled at all 
368        */
369       if (check_col(pinfo->cinfo, COL_INFO)) {
370         if (unack)
371           col_append_fstr(pinfo->cinfo, COL_INFO, " (Unreassembled fragment %u)", segment);
372         else
373           col_append_str(pinfo->cinfo, COL_INFO, " (Unreassembled fragment)");
374       }
375       if (tree) {
376         proto_tree_add_text(sndcp_tree, tvb, offset, -1, "Payload");
377       }
378     }
379     /* Now reset fragmentation information in pinfo 
380      */
381     pinfo->fragmented = save_fragmented;
382   }
383 }
384
385
386 /* Register the protocol with Wireshark 
387    this format is required because a script is used to build the C function
388    that calls all the protocol registration.
389 */
390
391 void
392 proto_register_sndcp(void)
393 {                 
394   /* Setup list of header fields
395    */
396   static hf_register_info hf[] = {
397     { &hf_sndcp_nsapi,
398       { "NSAPI", 
399         "sndcp.nsapi", 
400         FT_UINT8, BASE_DEC, VALS(nsapi_abrv), 0x0,
401         "Network Layer Service Access Point Identifier", HFILL 
402       }
403     },
404     { &hf_sndcp_x, 
405       { "Spare bit",
406         "sndcp.x", 
407         FT_BOOLEAN,8, TFS(&x_bit), MASK_X, 
408         "Spare bit (should be 0)", HFILL 
409       }
410     },
411     { &hf_sndcp_f, 
412       { "First segment indicator bit", 
413         "sndcp.f", 
414         FT_BOOLEAN,8, TFS(&f_bit), MASK_F, 
415         NULL, HFILL 
416       }
417     },
418     { &hf_sndcp_t, 
419       { "Type", 
420         "sndcp.t", 
421         FT_BOOLEAN,8, TFS(&t_bit), MASK_T, 
422         "SN-PDU Type", HFILL 
423       }
424     },
425     { &hf_sndcp_m, 
426       { "More bit", 
427         "sndcp.m", 
428         FT_BOOLEAN,8, TFS(&m_bit), MASK_M, 
429         NULL, HFILL 
430       }
431     },
432     { &hf_sndcp_dcomp, 
433       { "DCOMP", 
434         "sndcp.dcomp", 
435         FT_UINT8, BASE_DEC, VALS(compression_vals), 0xF0, 
436         "Data compression coding", HFILL 
437       }
438     },
439     { &hf_sndcp_pcomp, 
440       { "PCOMP", 
441         "sndcp.pcomp", 
442         FT_UINT8, BASE_DEC, VALS(compression_vals), 0x0F, 
443         "Protocol compression coding", HFILL 
444       }
445     },
446     { &hf_sndcp_nsapib,
447       { "NSAPI", 
448         "sndcp.nsapib", 
449         FT_UINT8, BASE_DEC , VALS(nsapi_t), 0xf, 
450         "Network Layer Service Access Point Identifier",HFILL 
451       }
452     },  
453     { &hf_sndcp_segment, 
454       { "Segment", 
455         "sndcp.segment", 
456         FT_UINT16, BASE_DEC, NULL, 0xF000, 
457         "Segment number", HFILL 
458       }
459     },
460     { &hf_sndcp_npdu1, 
461       { "N-PDU", 
462         "sndcp.npdu", 
463         FT_UINT8, BASE_DEC, NULL, 0, 
464         NULL, HFILL 
465       }
466     },
467     { &hf_sndcp_npdu2, 
468       { "N-PDU", 
469         "sndcp.npdu", 
470         FT_UINT16, BASE_DEC, NULL, 0x0FFF, 
471         NULL, HFILL 
472       }
473     },
474
475     /* Fragment fields 
476      */
477     { &hf_npdu_fragment_overlap,
478       { "Fragment overlap",
479         "npdu.fragment.overlap",
480         FT_BOOLEAN, BASE_NONE, NULL, 0x0,
481         "Fragment overlaps with other fragments", HFILL
482       }
483     },
484     { &hf_npdu_fragment_overlap_conflict,
485       { "Conflicting data in fragment overlap",
486         "npdu.fragment.overlap.conflict",
487         FT_BOOLEAN, BASE_NONE, NULL, 0x0,
488         "Overlapping fragments contained conflicting data", HFILL
489       }
490     },
491     { &hf_npdu_fragment_multiple_tails,
492       { "Multiple tail fragments found",
493         "npdu.fragment.multipletails",
494         FT_BOOLEAN, BASE_NONE, NULL, 0x0,
495         "Several tails were found when defragmenting the packet", HFILL
496       }
497     },
498     { &hf_npdu_fragment_too_long_fragment,
499       { "Fragment too long",
500         "npdu.fragment.toolongfragment",
501         FT_BOOLEAN, BASE_NONE, NULL, 0x0,
502         "Fragment contained data past end of packet", HFILL
503       }
504     },
505     { &hf_npdu_fragment_error,
506       { "Defragmentation error",
507         "npdu.fragment.error",
508         FT_FRAMENUM, BASE_NONE, NULL, 0x0,
509         "Defragmentation error due to illegal fragments", HFILL
510       }
511     },
512     { &hf_npdu_reassembled_in,
513       { "Reassembled in",
514         "npdu.reassembled.in",
515         FT_FRAMENUM, BASE_NONE, NULL, 0x0,
516         "N-PDU fragments are reassembled in the given packet", HFILL
517       }
518     },
519     { &hf_npdu_reassembled_length,
520       { "Reassembled N-PDU length",
521         "npdu.reassembled.length",
522         FT_UINT32, BASE_DEC, NULL, 0x0,
523         "The total length of the reassembled payload", HFILL
524       }
525     },
526     { &hf_npdu_fragment,
527       { "N-PDU Fragment",
528         "npdu.fragment",
529         FT_FRAMENUM, BASE_NONE, NULL, 0x0,
530         NULL, HFILL
531       }
532     },
533     { &hf_npdu_fragments,
534       { "N-PDU Fragments",
535         "npdu.fragments",
536         FT_NONE, BASE_NONE, NULL, 0x0,
537         NULL, HFILL
538       }
539     }
540   };
541     
542     /* Setup protocol subtree array */
543   static gint *ett[] = {
544     &ett_sndcp     ,
545     &ett_sndcp_address_field,
546     &ett_sndcp_compression_field,
547     &ett_sndcp_npdu_field,
548     &ett_npdu_fragment,
549     &ett_npdu_fragments,
550   };
551
552   /* Register the protocol name and description */
553   proto_sndcp = proto_register_protocol("Subnetwork Dependent Convergence Protocol",
554                                         "SNDCP", "sndcp");
555
556   /* Required function calls to register the header fields and subtrees used */
557   proto_register_field_array(proto_sndcp, hf, array_length(hf));
558   proto_register_subtree_array(ett, array_length(ett));
559   register_dissector("sndcp", dissect_sndcp, proto_sndcp);
560   register_init_routine(sndcp_defragment_init);
561 }
562
563 /* If this dissector uses sub-dissector registration add a registration routine.
564    This format is required because a script is used to find these routines and
565    create the code that calls these routines.
566 */
567 void
568 proto_reg_handoff_sndcp(void)
569 {
570   dissector_handle_t sndcp_handle;
571
572   sndcp_handle = find_dissector("sndcp"); 
573
574   /* Register SNDCP dissector with LLC layer for SAPI 3,5,9 and 11 
575    */
576   dissector_add("llcgprs.sapi",  3, sndcp_handle);
577   dissector_add("llcgprs.sapi",  5, sndcp_handle);
578   dissector_add("llcgprs.sapi",  9, sndcp_handle);
579   dissector_add("llcgprs.sapi", 11, sndcp_handle);
580
581   /* Find IP and data handle for upper layer dissectors 
582    */
583   ip_handle   = find_dissector("ip"); 
584   data_handle = find_dissector("data");
585 }