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