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