Updates from Ed Warnicke.
[obnox/wireshark/wip.git] / packet-clnp.c
1 /* packet-clnp.c
2  * Routines for ISO/OSI network and transport protocol packet disassembly
3  *
4  * $Id: packet-clnp.c,v 1.19 2000/12/23 23:06:50 guy Exp $
5  * Laurent Deniel <deniel@worldnet.fr>
6  * Ralf Schneider <Ralf.Schneider@t-online.de>
7  *
8  * Ethereal - Network traffic analyzer
9  * By Gerald Combs <gerald@zing.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * 
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  * 
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  * 
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  *
27  */
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #ifdef HAVE_SYS_TYPES_H
34 # include <sys/types.h>
35 #endif
36
37 #include <stdio.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include <glib.h>
41 #include "prefs.h"
42 #include "packet.h"
43 #include "packet-osi.h"
44 #include "packet-osi-options.h"
45 #include "packet-isis.h"
46 #include "packet-esis.h"
47 #include "nlpid.h"
48
49 /* protocols and fields */
50
51 static int  proto_clnp         = -1;
52 static gint ett_clnp           = -1;
53 static gint ett_clnp_type      = -1;
54 static gint ett_clnp_disc_pdu  = -1;
55 static int  proto_cotp         = -1;
56 static gint ett_cotp           = -1;
57 static int  proto_cltp         = -1;
58 static gint ett_cltp           = -1;
59
60 static int hf_clnp_id          = -1;
61 static int hf_clnp_length      = -1;
62 static int hf_clnp_version     = -1;
63 static int hf_clnp_ttl         = -1;
64 static int hf_clnp_type        = -1;
65 static int hf_clnp_pdu_length  = -1;
66 static int hf_clnp_checksum    = -1;
67 static int hf_clnp_dest_length = -1;
68 static int hf_clnp_dest        = -1;
69 static int hf_clnp_src_length  = -1;
70 static int hf_clnp_src         = -1;
71
72 /*
73  * ISO 8473 OSI CLNP definition (see RFC994)
74  *
75  *            _________________________________
76  *           |           Fixed Part            |
77  *           |_________________________________|
78  *           |          Address Part           |
79  *           |_________________________________|
80  *           |   Segmentation Part (optional)  |
81  *           |_________________________________|
82  *           |     Options Part (optional)     |
83  *           |_________________________________|
84  *           |         Data (optional)         |
85  *           |_________________________________|
86  */
87
88 #define ISO8473_V1  0x01    /* CLNP version 1 */
89
90 /* Fixed part */
91
92 #define CNF_TYPE                0x1f
93 #define CNF_ERR_OK              0x20
94 #define CNF_MORE_SEGS           0x40
95 #define CNF_SEG_OK              0x80
96
97 #define DT_NPDU                 0x1C
98 #define MD_NPDU                 0x1D
99 #define ER_NPDU                 0x01
100 #define ERQ_NPDU                0x1E
101 #define ERP_NPDU                0x1F
102
103 static const value_string npdu_type_abbrev_vals[] = {
104   { DT_NPDU,    "DT" },
105   { MD_NPDU,    "MD" },
106   { ER_NPDU,    "ER" },
107   { ERQ_NPDU,   "ERQ" },
108   { ERP_NPDU,   "ERP" },
109   { 0,          NULL }
110 };
111
112 static const value_string npdu_type_vals[] = {
113   { DT_NPDU,    "Data" },
114   { MD_NPDU,    "Multicast Data" },
115   { ER_NPDU,    "Error Report" },
116   { ERQ_NPDU,   "Echo Request" },
117   { ERP_NPDU,   "Echo Response" },
118   { 0,          NULL }
119 };
120
121 /* field position */
122
123 #define P_CLNP_PROTO_ID         0
124 #define P_CLNP_HDR_LEN          1
125 #define P_CLNP_VERS             2
126 #define P_CLNP_TTL              3
127 #define P_CLNP_TYPE             4
128 #define P_CLNP_SEGLEN           5
129 #define P_CLNP_CKSUM            7
130 #define P_CLNP_ADDRESS_PART     9
131
132 /* Segmentation part */
133
134 struct clnp_segment {
135   u_short       cng_id;         /* data unit identifier */
136   u_short       cng_off;        /* segment offset */
137   u_short       cng_tot_len;    /* total length */
138 };
139
140 /* NSAP selector */
141
142 #define NSEL_NET                0x00
143 #define NSEL_NP                 0x20
144 #define NSEL_TP                 0x21
145
146 /*
147  * ISO8073 OSI COTP definition (see RFC905)
148  */
149
150 /* don't use specific TPDU types to avoid alignment problems & copy overhead */
151
152 /* TPDU definition */
153
154 #define ED_TPDU                 0x1     /* COTP */
155 #define EA_TPDU                 0x2     /* COTP */
156 #define UD_TPDU                 0x4     /* CLTP */
157 #define RJ_TPDU                 0x5     /* COTP */
158 #define AK_TPDU                 0x6     /* COTP */
159 #define ER_TPDU                 0x7     /* COTP */
160 #define DR_TPDU                 0x8     /* COTP */
161 #define DC_TPDU                 0xC     /* COTP */
162 #define CC_TPDU                 0xD     /* COTP */
163 #define CR_TPDU                 0xE     /* COTP */
164 #define DT_TPDU                 0xF     /* COTP */
165
166 /* field position */
167
168 #define P_LI                    0
169 #define P_TPDU                  1
170 #define P_CDT                   1
171 #define P_DST_REF               2
172 #define P_SRC_REF               4
173 #define P_TPDU_NR_0_1           2
174 #define P_TPDU_NR_234           4
175 #define P_VAR_PART_NDT          5
176 #define P_VAR_PART_EDT          8
177 #define P_VAR_PART_DC           6
178 #define P_CDT_IN_AK             8
179 #define P_CDT_IN_RJ             8
180 #define P_REJECT_ER             4
181 #define P_REASON_IN_DR          6
182 #define P_CLASS_OPTION          6
183
184 /* TPDU length indicator */
185
186 #define LI_NORMAL_DT_CLASS_01            2
187 #define LI_NORMAL_DT_WITH_CHECKSUM       8
188 #define LI_NORMAL_DT_WITHOUT_CHECKSUM    4
189 #define LI_EXTENDED_DT_WITH_CHECKSUM     11
190 #define LI_EXTENDED_DT_WITHOUT_CHECKSUM  7
191 #define LI_NORMAL_EA_WITH_CHECKSUM       8
192 #define LI_NORMAL_EA_WITHOUT_CHECKSUM    4
193 #define LI_EXTENDED_EA_WITH_CHECKSUM     11
194 #define LI_EXTENDED_EA_WITHOUT_CHECKSUM  7
195 #define LI_NORMAL_RJ                     4
196 #define LI_EXTENDED_RJ                   9
197 #define LI_MIN_DR                        6
198 #define LI_MAX_DC                        9
199 #define LI_MAX_AK                        27
200 #define LI_MAX_EA                        11
201 #define LI_MAX_ER                        8
202 /* XXX - can we always decide this based on whether the length
203    indicator is odd or not?  What if the variable part has an odd
204    number of octets? */
205 #define is_LI_NORMAL_AK(p)               ( ( p & 0x01 ) == 0 )
206
207 /* variant part */
208
209 #define VP_ACK_TIME             0x85
210 #define VP_RES_ERROR            0x86
211 #define VP_PRIORITY             0x87
212 #define VP_TRANSIT_DEL          0x88
213 #define VP_THROUGHPUT           0x89
214 #define VP_SEQ_NR               0x8A         /* in AK */
215 #define VP_REASSIGNMENT         0x8B
216 #define VP_FLOW_CNTL            0x8C         /* in AK */
217 #define VP_TPDU_SIZE            0xC0
218 #define VP_SRC_TSAP             0xC1         /* in CR/CC */
219 #define VP_DST_TSAP             0xC2
220 #define VP_CHECKSUM             0xC3
221 #define VP_VERSION_NR           0xC4
222 #define VP_PROTECTION           0xC5
223 #define VP_OPT_SEL              0xC6
224 #define VP_PROTO_CLASS          0xC7
225 #define VP_PREF_MAX_TPDU_SIZE   0xF0
226 #define VP_INACTIVITY_TIMER     0xF2
227
228 static const value_string tp_vpart_type_vals[] = {
229   { VP_ACK_TIME,                "ack time" },
230   { VP_RES_ERROR,               "res error" },
231   { VP_PRIORITY,                "priority" },
232   { VP_TRANSIT_DEL,             "transit delay" },
233   { VP_THROUGHPUT,              "throughput" },
234   { VP_SEQ_NR,                  "seq number" },
235   { VP_REASSIGNMENT,            "reassignment" },
236   { VP_FLOW_CNTL,               "flow control" },
237   { VP_TPDU_SIZE,               "tpdu-size" },
238   { VP_SRC_TSAP,                "src-tsap" },
239   { VP_DST_TSAP,                "dst-tsap" },
240   { VP_CHECKSUM,                "checksum" },
241   { VP_VERSION_NR,              "version" },
242   { VP_PROTECTION,              "protection" },
243   { VP_OPT_SEL,                 "options" },
244   { VP_PROTO_CLASS,             "proto class" },
245   { VP_PREF_MAX_TPDU_SIZE,      "preferred max TPDU size" },
246   { 0,                          NULL }
247 };
248
249 /* misc */
250
251 #define EXTRACT_SHORT(p)        pntohs(p)
252 #define EXTRACT_LONG(p)         pntohl(p)
253
254 /* global variables */
255
256 static u_char  li, tpdu, cdt;   /* common fields */
257 static u_short dst_ref;
258
259 /* List of dissectors to call for COTP packets put atop the Inactive
260    Subset of CLNP. */
261 static heur_dissector_list_t cotp_is_heur_subdissector_list;
262
263 /* options */
264 static guint tp_nsap_selector = NSEL_TP;
265 static gboolean always_decode_transport = FALSE;
266
267 /* function definitions */
268
269 #define MAX_TSAP_LEN    32
270 static gchar *print_tsap(const u_char *tsap, int length)
271 {
272
273   static gchar  str[3][MAX_TSAP_LEN * 2 + 1];
274   static gchar *cur;
275   gchar tmp[3];
276   gboolean allprintable;
277   int i;
278
279   if (cur == &str[0][0]) {
280     cur = &str[1][0];
281   } else if (cur == &str[1][0]) {  
282     cur = &str[2][0];
283   } else {  
284     cur = &str[0][0];
285   }
286
287
288   cur[0] = '\0';
289   if (length <= 0 || length > MAX_TSAP_LEN) 
290     sprintf(cur, "<unsupported TSAP length>");
291   else {    
292     allprintable=TRUE;
293     for (i=0;i<length;i++) {
294         /* If any byte is not printable ASCII, display the TSAP as a
295            series of hex byte values rather than as a string; this
296            means that, for example, accented letters will cause it
297            to be displayed as hex, but it also means that byte values
298            such as 0xff and 0xfe, which *are* printable ISO 8859/x
299            characters, won't be treated as printable - 0xfffffffe
300            is probably binary, not text. */
301         if (!(isascii(tsap[i]) && isprint(tsap[i]))) {
302           allprintable=FALSE;
303           break;
304           }      
305         }
306     if (!allprintable){
307       strcat(cur,"0x");
308       }
309     while (length != 0) {
310       if (allprintable)
311         sprintf(tmp, "%c", *tsap ++);
312       else
313         sprintf(tmp, "%02x", *tsap ++);
314       strcat(cur, tmp);
315       length --;
316     }
317   }
318   return cur;
319
320 } /* print_tsap */
321
322 static gboolean osi_decode_tp_var_part(tvbuff_t *tvb, int offset,
323                                       int vp_length, int class_option,
324                                       proto_tree *tree)
325 {
326   guint8  code, length;
327   guint8  c1;
328   guint16 s, s1,s2,s3,s4;
329   guint32 t1, t2, t3, t4;
330   guint32 pref_max_tpdu_size;
331
332   while (vp_length != 0) {
333     code = tvb_get_guint8(tvb, offset);
334     proto_tree_add_text(tree, tvb, offset, 1,
335                 "Parameter code:   0x%02x (%s)",
336                             code,
337                             val_to_str(code, tp_vpart_type_vals, "Unknown"));
338     offset += 1;
339     vp_length -= 1;
340
341     if (vp_length == 0)
342       break;
343     length = tvb_get_guint8(tvb, offset);
344     proto_tree_add_text(tree, tvb, offset, 1,
345                 "Parameter length: %u", length);
346     offset += 1;
347     vp_length -= 1;
348
349     switch (code) {
350
351     case VP_ACK_TIME:
352       s = tvb_get_ntohs(tvb, offset);
353       proto_tree_add_text(tree, tvb, offset, length, 
354                               "Ack time (ms): %u", s);
355       offset += length;
356       vp_length -= length;
357       break;
358
359     case VP_RES_ERROR:
360       proto_tree_add_text(tree, tvb, offset, 1,
361                 "Residual error rate, target value: 10^%u",
362                 tvb_get_guint8(tvb, offset));
363       offset += 1;
364       length -= 1;
365       vp_length -= 1;
366
367       proto_tree_add_text(tree, tvb, offset, 1,
368                 "Residual error rate, minimum acceptable: 10^%u",
369                 tvb_get_guint8(tvb, offset));
370       offset += 1;
371       length -= 1;
372       vp_length -= 1;
373
374
375       proto_tree_add_text(tree, tvb, offset, 1,
376                 "Residual error rate, TSDU size of interest: %u",
377                 1<<tvb_get_guint8(tvb, offset));
378       offset += 1;
379       length -= 1;
380       vp_length -= 1;
381
382       break;
383
384     case VP_PRIORITY:
385       s = tvb_get_ntohs(tvb, offset);
386       proto_tree_add_text(tree, tvb, offset, length,
387                 "Priority: %u", s);
388       offset += length;
389       vp_length -= length;
390       break;
391         
392     case VP_TRANSIT_DEL:
393       s1 = tvb_get_ntohs(tvb, offset);
394       proto_tree_add_text(tree, tvb, offset, 2,
395                 "Transit delay, target value, calling-called: %u ms", s1);
396       offset += 2;
397       length -= 2;
398       vp_length -= 2;
399
400       s2 = tvb_get_ntohs(tvb, offset);
401       proto_tree_add_text(tree, tvb, offset, 2,
402                 "Transit delay, maximum acceptable, calling-called: %u ms", s2);
403       offset += 2;
404       length -= 2;
405       vp_length -= 2;
406
407       s3 = tvb_get_ntohs(tvb, offset);
408       proto_tree_add_text(tree, tvb, offset, 2,
409                 "Transit delay, target value, called-calling: %u ms", s3);
410       offset += 2;
411       length -= 2;
412       vp_length -= 2;
413
414       s4 = tvb_get_ntohs(tvb, offset);
415       proto_tree_add_text(tree, tvb, offset, 2,
416                 "Transit delay, maximum acceptable, called-calling: %u ms", s4);
417       offset += 2;
418       length -= 2;
419       vp_length -= 2;
420       break;
421
422     case VP_THROUGHPUT:
423       t1 = tvb_get_ntoh24(tvb, offset);
424       proto_tree_add_text(tree, tvb, offset, 3,
425                 "Maximum throughput, target value, calling-called:       %u o/s", t1);
426       offset += 3;
427       length -= 3;
428       vp_length -= 3;
429
430       t2 = tvb_get_ntoh24(tvb, offset);
431       proto_tree_add_text(tree, tvb, offset, 3,
432                 "Maximum throughput, minimum acceptable, calling-called: %u o/s", t2);
433       offset += 3;
434       length -= 3;
435       vp_length -= 3;
436
437       t3 = tvb_get_ntoh24(tvb, offset);
438       proto_tree_add_text(tree, tvb, offset, 3,
439                 "Maximum throughput, target value, called-calling:       %u o/s", t3);
440       offset += 3;
441       length -= 3;
442       vp_length -= 3;
443
444       t4 = tvb_get_ntoh24(tvb, offset);
445       proto_tree_add_text(tree, tvb, offset, 3,
446                 "Maximum throughput, minimum acceptable, called-calling: %u o/s", t4);
447       offset += 3;
448       length -= 3;
449       vp_length -= 3;
450
451       if (length != 0) {        /* XXX - should be 0 or 12 */
452         t1 = tvb_get_ntoh24(tvb, offset);
453         proto_tree_add_text(tree, tvb, offset, 3,
454                 "Average throughput, target value, calling-called:       %u o/s", t1);
455         offset += 3;
456         length -= 3;
457         vp_length -= 3;
458
459         t2 = tvb_get_ntoh24(tvb, offset);
460         proto_tree_add_text(tree, tvb, offset, 3,
461                 "Average throughput, minimum acceptable, calling-called: %u o/s", t2);
462         offset += 3;
463         length -= 3;
464         vp_length -= 3;
465
466         t3 = tvb_get_ntoh24(tvb, offset);
467         proto_tree_add_text(tree, tvb, offset, 3,
468                 "Average throughput, target value, called-calling:       %u o/s", t3);
469         offset += 3;
470         length -= 3;
471         vp_length -= 3;
472
473         t4 = tvb_get_ntoh24(tvb, offset);
474         proto_tree_add_text(tree, tvb, offset, 3,
475                 "Average throughput, minimum acceptable, called-calling: %u o/s", t4);
476         offset += 3;
477         length -= 3;
478         vp_length -= 3;
479       }
480       break;
481
482     case VP_SEQ_NR:
483       proto_tree_add_text(tree, tvb, offset, 2,
484                 "Sequence number: 0x%04x", tvb_get_ntohs(tvb, offset));
485       offset += length;
486       vp_length -= length;
487       break;
488
489     case VP_REASSIGNMENT: 
490       proto_tree_add_text(tree, tvb, offset, 2,
491                 "Reassignment time: %u secs", tvb_get_ntohs(tvb, offset));
492       offset += length;
493       vp_length -= length;
494       break;
495
496     case VP_FLOW_CNTL:
497       proto_tree_add_text(tree, tvb, offset, 4,
498                 "Lower window edge: 0x%08x", tvb_get_ntohl(tvb, offset));
499       offset += 4;
500       length -= 4;
501       vp_length -= 4;
502
503       proto_tree_add_text(tree, tvb, offset, 2,
504                 "Sequence number: 0x%04x", tvb_get_ntohs(tvb, offset));
505       offset += 2;
506       length -= 2;
507       vp_length -= 2;
508
509       proto_tree_add_text(tree, tvb, offset, 2,
510                 "Credit: 0x%04x", tvb_get_ntohs(tvb, offset));
511       offset += 2;
512       length -= 2;
513       vp_length -= 2;
514
515       break;
516
517     case VP_TPDU_SIZE:
518       c1 = tvb_get_guint8(tvb, offset) & 0x0F;
519       proto_tree_add_text(tree, tvb, offset, length, 
520                 "TPDU size: %u", 2 << c1);
521       offset += length;
522       vp_length -= length;
523       break;
524
525     case VP_SRC_TSAP:
526       proto_tree_add_text(tree, tvb, offset, length,
527                 "Calling TSAP: %s",
528                 print_tsap(tvb_get_ptr(tvb, offset, length), length));
529       offset += length;
530       vp_length -= length;
531       break;
532
533     case VP_DST_TSAP:
534       proto_tree_add_text(tree, tvb, offset, length,
535                 "Called TSAP: %s",
536                 print_tsap(tvb_get_ptr(tvb, offset, length), length));
537       offset += length;
538       vp_length -= length;
539       break;
540
541     case VP_CHECKSUM:
542       proto_tree_add_text(tree, tvb, offset, length,
543                 "Checksum: 0x%04x", tvb_get_ntohs(tvb, offset));
544       offset += length;
545       vp_length -= length;
546       break;
547
548     case VP_VERSION_NR:
549       c1 = tvb_get_guint8(tvb, offset);
550       proto_tree_add_text(tree, tvb, offset, length,
551                 "Version: %u", c1);
552       offset += length;
553       vp_length -= length;
554       break;
555
556     case VP_OPT_SEL:
557       c1 = tvb_get_guint8(tvb, offset) & 0x0F;
558       switch (class_option) {
559
560       case 1:
561         if (c1 & 0x8)
562           proto_tree_add_text(tree, tvb, offset, 1,
563                                   "Use of network expedited data");
564         else
565           proto_tree_add_text(tree, tvb, offset, 1,
566                                   "Non use of network expedited data");
567         if (c1 & 0x4)
568           proto_tree_add_text(tree, tvb, offset, 1,
569                                   "Use of Receipt confirmation");
570         else
571           proto_tree_add_text(tree, tvb, offset, 1,
572                                   "Use of explicit AK variant");
573         break;
574
575       case 4:
576         if (c1 & 0x2)
577           proto_tree_add_text(tree, tvb, offset, 1,
578                                   "Non-use 16 bit checksum in class 4");
579         else
580           proto_tree_add_text(tree, tvb, offset, 1,
581                                   "Use 16 bit checksum ");
582         break;
583       }
584       if (c1 & 0x1)
585         proto_tree_add_text(tree, tvb, offset, 1,
586                                 "Use of transport expedited data transfer\n");
587       else
588         proto_tree_add_text(tree, tvb, offset, 1,
589                                 "Non-use of transport expedited data transfer");
590       offset += length;
591       vp_length -= length;
592       break;
593
594     case VP_PREF_MAX_TPDU_SIZE:
595       switch (length) {
596
597       case 1:
598         pref_max_tpdu_size = tvb_get_guint8(tvb, offset);
599         break;
600
601       case 2:
602         pref_max_tpdu_size = tvb_get_ntohs(tvb, offset);
603         break;
604
605       case 3:
606         pref_max_tpdu_size = tvb_get_ntoh24(tvb, offset);
607         break;
608
609       case 4:
610         pref_max_tpdu_size = tvb_get_ntohl(tvb, offset);
611         break;
612
613       default:
614         proto_tree_add_text(tree, tvb, offset, length,
615                 "Preferred maximum TPDU size: bogus length %u (not 1, 2, 3, or 4)",
616                 length);
617         return FALSE;
618       }
619       proto_tree_add_text(tree, tvb, offset, length,
620                 "Preferred maximum TPDU size: %u", pref_max_tpdu_size*128);
621       offset += length;
622       vp_length -= length;
623       break; 
624
625     case VP_INACTIVITY_TIMER:
626       proto_tree_add_text(tree, tvb, offset, length,
627                 "Inactivity timer: %u ms", tvb_get_ntohl(tvb, offset));
628       offset += length;
629       vp_length -= length;
630       break;
631         
632     case VP_PROTECTION:           /* user-defined */
633     case VP_PROTO_CLASS:          /* todo */
634     default:                      /* unknown, no decoding */
635       proto_tree_add_text(tree, tvb, offset, length,
636                               "Parameter value: <not shown>");
637       offset += length;
638       vp_length -= length;
639       break; 
640     }
641   } /* while */
642
643   return TRUE;
644 }
645
646 static int osi_decode_DR(tvbuff_t *tvb, int offset, 
647                          packet_info *pinfo, proto_tree *tree) 
648 {
649   proto_tree *cotp_tree;
650   proto_item *ti;
651   u_short src_ref;
652   u_char  reason;
653   char *str;
654   
655   if (li < LI_MIN_DR) 
656     return -1;
657   
658   src_ref = tvb_get_ntohs(tvb, offset + P_SRC_REF);
659   reason  = tvb_get_guint8(tvb, offset + P_REASON_IN_DR);
660
661   switch(reason) {
662     case (128+0): str = "Normal Disconnect"; break;
663     case (128+1): str = "Remote transport entity congestion"; break;
664     case (128+2): str = "Connection negotiation failed"; break;
665     case (128+3): str = "Duplicate source reference"; break;
666     case (128+4): str = "Mismatched references"; break;
667     case (128+5): str = "Protocol error"; break;
668     case (128+7): str = "Reference overflow"; break;
669     case (128+8): str = "Connection requestion refused"; break;
670     case (128+10):str = "Header or parameter length invalid"; break;
671     case (0):     str = "Reason not specified"; break;
672     case (1):     str = "Congestion at TSAP"; break;
673     case (2):     str = "Session entity not attached to TSAP"; break;
674     case (3):     str = "Address unknown"; break;
675     default:      return -1;
676       /*NOTREACHED*/
677       break;
678   }
679
680   if (check_col(pinfo->fd, COL_INFO))
681     col_append_fstr(pinfo->fd, COL_INFO,
682                 "DR TPDU src-ref: 0x%04x dst-ref: 0x%04x",
683                  src_ref, dst_ref);
684
685   if (tree) {
686     ti = proto_tree_add_item(tree, proto_cotp, tvb, offset, li + 1, FALSE);
687     cotp_tree = proto_item_add_subtree(ti, ett_cotp);
688     proto_tree_add_text(cotp_tree, tvb, offset,      1,
689                         "Length indicator: %u", li);
690     proto_tree_add_text(cotp_tree, tvb, offset +  1, 1, 
691                         "TPDU code: 0x%x (DR)", tpdu); 
692     proto_tree_add_text(cotp_tree, tvb, offset +  2, 2, 
693                         "Destination reference: 0x%04x", dst_ref);
694     proto_tree_add_text(cotp_tree, tvb, offset +  4, 2, 
695                         "Source reference: 0x%04x", src_ref);
696     proto_tree_add_text(cotp_tree, tvb, offset +  6, 1, 
697                         "Cause: %s", str);
698   }
699
700   offset += li + 1;
701
702   /* User data */
703   dissect_data(tvb, offset, pinfo, tree);
704   offset += tvb_length_remaining(tvb, offset);
705      /* we dissected all of the containing PDU */
706
707   return offset;
708
709 } /* osi_decode_DR */
710
711 static int osi_decode_DT(tvbuff_t *tvb, int offset, 
712                          packet_info *pinfo, proto_tree *tree,
713                          gboolean uses_inactive_subset,
714                          gboolean *subdissector_found)
715 {
716   proto_tree *cotp_tree = NULL;
717   proto_item *ti;
718   gboolean is_extended;
719   gboolean is_class_234;
720   u_int    tpdu_nr ;
721   u_int    fragment = 0;
722   tvbuff_t *next_tvb;
723     
724   /* VP_CHECKSUM is the only parameter allowed in the variable part.
725      (This means we may misdissect this if the packet is bad and
726      contains other parameters.) */
727   switch (li) {
728
729     case LI_NORMAL_DT_WITH_CHECKSUM      :
730       if (tvb_get_guint8(tvb, offset + P_VAR_PART_NDT) != VP_CHECKSUM)
731         return -1;
732       /* FALLTHROUGH */
733
734     case LI_NORMAL_DT_WITHOUT_CHECKSUM   :
735       tpdu_nr = tvb_get_guint8(tvb, offset + P_TPDU_NR_234);
736       if ( tpdu_nr & 0x80 )
737         tpdu_nr = tpdu_nr & 0x7F;
738       else
739         fragment = 1;
740       is_extended = FALSE;
741       is_class_234 = TRUE;
742       break;
743
744     case LI_EXTENDED_DT_WITH_CHECKSUM    :
745       if (tvb_get_guint8(tvb, offset + P_VAR_PART_EDT) != VP_CHECKSUM)
746         return -1;
747       /* FALLTHROUGH */
748
749     case LI_EXTENDED_DT_WITHOUT_CHECKSUM :
750       tpdu_nr = tvb_get_ntohl(tvb, offset + P_TPDU_NR_234);
751       if ( tpdu_nr & 0x80000000 )
752         tpdu_nr = tpdu_nr & 0x7FFFFFFF;
753       else
754         fragment = 1;
755       is_extended = TRUE;
756       is_class_234 = TRUE;
757       break;
758
759     case LI_NORMAL_DT_CLASS_01           :
760       tpdu_nr = tvb_get_guint8(tvb, offset + P_TPDU_NR_0_1);
761       if ( tpdu_nr & 0x80 )
762         tpdu_nr = tpdu_nr & 0x7F;
763       else
764         fragment = 1;      
765       is_extended = FALSE;
766       is_class_234 = FALSE;
767       break;
768
769     default : /* bad TPDU */
770       return -1;
771       /*NOTREACHED*/
772       break;
773   }
774
775   if (check_col(pinfo->fd, COL_INFO))
776     col_append_fstr(pinfo->fd, COL_INFO, "DT TPDU (%u) dst-ref: 0x%04x %s", 
777                  tpdu_nr,
778                  dst_ref,
779                  (fragment)? "(fragment)" : "");
780
781   if (tree) {
782     ti = proto_tree_add_item(tree, proto_cotp, tvb, offset, li + 1, FALSE);
783     cotp_tree = proto_item_add_subtree(ti, ett_cotp);
784     proto_tree_add_text(cotp_tree, tvb, offset, 1,
785                         "Length indicator: %u", li);
786   }
787   offset += 1;
788
789   if (tree) {
790     proto_tree_add_text(cotp_tree, tvb, offset, 1,
791                         "TPDU code: 0x%x (DT)", tpdu); 
792
793   }
794   offset += 1;
795   li -= 1;
796
797   if (is_class_234) {
798     if (tree) {
799       proto_tree_add_text(cotp_tree, tvb, offset, 2,
800                           "Destination reference: 0x%04x", dst_ref);
801     }
802     offset += 2;
803     li -= 2;
804   }
805
806   if (is_extended) {
807     if (tree) {
808       proto_tree_add_text(cotp_tree, tvb, offset, 4, 
809                             "TPDU number: 0x%08x (%s)", 
810                             tpdu_nr,
811                             (fragment)? "fragment":"complete");
812     }
813     offset += 4;
814     li -= 4;
815   } else {
816     if (tree) {
817       proto_tree_add_text(cotp_tree, tvb, offset, 1,
818                             "TPDU number: 0x%02x (%s)", 
819                             tpdu_nr,
820                             (fragment)? "fragment":"complete");
821     }
822     offset += 1;
823     li -= 1;
824   }
825
826   if (tree)
827     osi_decode_tp_var_part(tvb, offset, li, 4, cotp_tree);
828   offset += li;
829
830   next_tvb = tvb_new_subset(tvb, offset, -1, -1);
831   if (uses_inactive_subset){
832         if (dissector_try_heuristic(cotp_is_heur_subdissector_list, next_tvb,
833                                         pinfo, tree)) {
834                 *subdissector_found = TRUE;
835         } else {
836           /* Fill in other Dissectors using inactive subset here */
837           dissect_data(next_tvb, 0, pinfo, tree);
838         }
839   } else
840         dissect_data(next_tvb, 0, pinfo, tree);
841   offset += tvb_length_remaining(tvb, offset);
842      /* we dissected all of the containing PDU */
843
844   return offset;
845
846 } /* osi_decode_DT */
847
848 static int osi_decode_ED(tvbuff_t *tvb, int offset, 
849                          packet_info *pinfo, proto_tree *tree)
850 {
851   proto_tree *cotp_tree = NULL;
852   proto_item *ti;
853   gboolean is_extended;
854   u_int    tpdu_nr ;
855   tvbuff_t *next_tvb;
856
857   /* ED TPDUs are never fragmented */
858
859   /* VP_CHECKSUM is the only parameter allowed in the variable part.
860      (This means we may misdissect this if the packet is bad and
861      contains other parameters.) */
862   switch (li) {
863
864     case LI_NORMAL_DT_WITH_CHECKSUM      :
865       if (tvb_get_guint8(tvb, offset + P_VAR_PART_NDT) != VP_CHECKSUM)
866         return -1;
867       /* FALLTHROUGH */
868
869     case LI_NORMAL_DT_WITHOUT_CHECKSUM   :
870       tpdu_nr = tvb_get_guint8(tvb, offset + P_TPDU_NR_234);
871       if ( tpdu_nr & 0x80 )
872         tpdu_nr = tpdu_nr & 0x7F;
873       else
874         return -1;
875       is_extended = FALSE;
876       break;
877
878     case LI_EXTENDED_DT_WITH_CHECKSUM    :
879       if (tvb_get_guint8(tvb, offset + P_VAR_PART_EDT) != VP_CHECKSUM)
880         return -1;
881       /* FALLTHROUGH */
882
883     case LI_EXTENDED_DT_WITHOUT_CHECKSUM :
884       tpdu_nr = tvb_get_ntohl(tvb, offset + P_TPDU_NR_234);
885       if ( tpdu_nr & 0x80000000 )
886         tpdu_nr = tpdu_nr & 0x7FFFFFFF;
887       else
888         return -1;
889       is_extended = TRUE;
890       break;
891
892     default : /* bad TPDU */
893       return -1;
894       /*NOTREACHED*/
895       break;
896   } /* li */
897
898   if (check_col(pinfo->fd, COL_INFO))
899     col_append_fstr(pinfo->fd, COL_INFO, "ED TPDU (%u) dst-ref: 0x%04x", 
900                  tpdu_nr, dst_ref);
901
902   if (tree) {
903     ti = proto_tree_add_item(tree, proto_cotp, tvb, offset, li + 1, FALSE);
904     cotp_tree = proto_item_add_subtree(ti, ett_cotp);
905     proto_tree_add_text(cotp_tree, tvb, offset, 1,
906                         "Length indicator: %u", li);
907   }
908   offset += 1;
909
910   if (tree) {
911     proto_tree_add_text(cotp_tree, tvb, offset, 1, 
912                         "TPDU code: 0x%x (ED)", tpdu);
913   }
914   offset += 1;
915   li -= 1;
916
917   if (tree) {
918     proto_tree_add_text(cotp_tree, tvb, offset, 2,
919                         "Destination reference: 0x%04x", dst_ref);
920   }
921   offset += 2;
922   li -= 2;
923
924   if (is_extended) {
925     if (tree) {
926       proto_tree_add_text(cotp_tree, tvb, offset, 4,
927                             "TPDU number: 0x%02x", tpdu_nr);
928     }
929     offset += 4;
930     li -= 4;
931   } else {
932     if (tree) {
933       proto_tree_add_text(cotp_tree, tvb, offset, 1,
934                             "TPDU number: 0x%02x", tpdu_nr);    
935     }
936     offset += 1;
937     li -= 1;
938   }
939
940   if (tree)
941     osi_decode_tp_var_part(tvb, offset, li, 4, cotp_tree);
942   offset += li;
943
944   next_tvb = tvb_new_subset(tvb, offset, -1, -1);
945   dissect_data(next_tvb, 0, pinfo, tree);
946
947   offset += tvb_length_remaining(tvb, offset);
948      /* we dissected all of the containing PDU */
949
950   return offset;
951
952 } /* osi_decode_ED */
953
954 static int osi_decode_RJ(tvbuff_t *tvb, int offset, 
955                          packet_info *pinfo, proto_tree *tree)
956 {
957   proto_tree *cotp_tree;
958   proto_item *ti;
959   u_int    tpdu_nr ;
960   u_short  credit = 0;
961
962   switch(li) {
963     case LI_NORMAL_RJ   :
964       tpdu_nr = tvb_get_guint8(tvb, offset + P_TPDU_NR_234);
965       break;
966     case LI_EXTENDED_RJ :
967       tpdu_nr = tvb_get_ntohl(tvb, offset + P_TPDU_NR_234);
968       credit = tvb_get_ntohs(tvb, offset + P_CDT_IN_RJ);
969       break;
970     default :
971       return -1;
972       /*NOTREACHED*/
973       break;
974   }
975
976   if (check_col(pinfo->fd, COL_INFO))
977     col_append_fstr(pinfo->fd, COL_INFO, "RJ TPDU (%u) dst-ref: 0x%04x", 
978                  tpdu_nr, dst_ref);
979
980   if (tree) {
981     ti = proto_tree_add_item(tree, proto_cotp, tvb, offset, li + 1, FALSE);
982     cotp_tree = proto_item_add_subtree(ti, ett_cotp);
983     proto_tree_add_text(cotp_tree, tvb, offset,      1,
984                         "Length indicator: %u", li);
985     proto_tree_add_text(cotp_tree, tvb, offset +  1, 1, 
986                         "TPDU code: 0x%x (RJ)", tpdu); 
987     if (li == LI_NORMAL_RJ)
988       proto_tree_add_text(cotp_tree, tvb, offset +  1, 1, 
989                           "Credit: %u", cdt);
990     proto_tree_add_text(cotp_tree, tvb, offset +  2, 2, 
991                         "Destination reference: 0x%04x", dst_ref);
992     if (li == LI_NORMAL_RJ)
993       proto_tree_add_text(cotp_tree, tvb, offset +  4, 1, 
994                           "Your TPDU number: 0x%02x", tpdu_nr);
995     else {
996       proto_tree_add_text(cotp_tree, tvb, offset +  4, 4, 
997                           "Your TPDU number: 0x%02x", tpdu_nr);
998       proto_tree_add_text(cotp_tree, tvb, offset +  8, 2, 
999                           "Credit: 0x%02x", credit);
1000     }
1001   }
1002
1003   offset += li + 1;
1004
1005   return offset;
1006
1007 } /* osi_decode_RJ */
1008
1009 static int osi_decode_CC(tvbuff_t *tvb, int offset, 
1010                          packet_info *pinfo, proto_tree *tree)
1011 {
1012
1013   /* CC & CR decoding in the same function */
1014
1015   proto_tree *cotp_tree = NULL;
1016   proto_item *ti;
1017   u_short src_ref;
1018   u_char  class_option;
1019
1020   src_ref = tvb_get_ntohs(tvb, offset + P_SRC_REF);
1021   class_option = (tvb_get_guint8(tvb, offset + P_CLASS_OPTION) >> 4 ) & 0x0F;
1022   if (class_option > 4)
1023     return -1;
1024
1025   if (check_col(pinfo->fd, COL_INFO))
1026     col_append_fstr(pinfo->fd, COL_INFO,
1027                  "%s TPDU src-ref: 0x%04x dst-ref: 0x%04x",
1028                  (tpdu == CR_TPDU) ? "CR" : "CC",
1029                  src_ref,
1030                  dst_ref);
1031
1032   if (tree) {
1033     ti = proto_tree_add_item(tree, proto_cotp, tvb, offset, li + 1, FALSE);
1034     cotp_tree = proto_item_add_subtree(ti, ett_cotp);
1035     proto_tree_add_text(cotp_tree, tvb, offset, 1,
1036                         "Length indicator: %u", li);
1037   }
1038   offset += 1;
1039
1040   if (tree) {
1041     proto_tree_add_text(cotp_tree, tvb, offset, 1,
1042                         "TPDU code: 0x%x (%s)", tpdu,
1043                         (tpdu == CR_TPDU) ? "CR" : "CC");
1044   }
1045   offset += 1;
1046   li -= 1;
1047
1048   if (tree) {
1049     proto_tree_add_text(cotp_tree, tvb, offset, 2,
1050                         "Destination reference: 0x%04x", dst_ref);
1051   }
1052   offset += 2;
1053   li -= 2;
1054
1055   if (tree) {
1056     proto_tree_add_text(cotp_tree, tvb, offset, 2,
1057                         "Source reference: 0x%04x", src_ref);
1058   }
1059   offset += 2;
1060   li -= 2;
1061
1062   if (tree) {
1063     proto_tree_add_text(cotp_tree, tvb, offset, 1,
1064                         "Class option: 0x%02x", class_option);
1065   }
1066   offset += 1;
1067   li -= 1;
1068
1069   if (tree)
1070     osi_decode_tp_var_part(tvb, offset, li, class_option, cotp_tree);
1071   offset += li;
1072
1073   /* User data */
1074   dissect_data(tvb, offset, pinfo, tree);
1075   offset += tvb_length_remaining(tvb, offset);
1076      /* we dissected all of the containing PDU */
1077
1078   return offset;
1079
1080 } /* osi_decode_CC */
1081
1082 static int osi_decode_DC(tvbuff_t *tvb, int offset, 
1083                          packet_info *pinfo, proto_tree *tree)
1084 {
1085   proto_tree *cotp_tree = NULL;
1086   proto_item *ti;
1087   u_short src_ref;
1088
1089   if (li > LI_MAX_DC) 
1090     return -1;
1091
1092   src_ref = tvb_get_ntohs(tvb, offset + P_SRC_REF);
1093
1094   if (check_col(pinfo->fd, COL_INFO))
1095     col_append_fstr(pinfo->fd, COL_INFO,
1096                  "DC TPDU src-ref: 0x%04x dst-ref: 0x%04x", 
1097                  src_ref,
1098                  dst_ref);
1099
1100   if (tree) {
1101     ti = proto_tree_add_item(tree, proto_cotp, tvb, offset, li + 1, FALSE);
1102     cotp_tree = proto_item_add_subtree(ti, ett_cotp);
1103     proto_tree_add_text(cotp_tree, tvb, offset, 1,
1104                         "Length indicator: %u", li);
1105   }
1106   offset += 1;
1107
1108   if (tree) {
1109     proto_tree_add_text(cotp_tree, tvb, offset, 1,
1110                         "TPDU code: 0x%x (DC)", tpdu);
1111   }
1112   offset += 1;
1113   li -= 1;
1114
1115   if (tree) {
1116     proto_tree_add_text(cotp_tree, tvb, offset, 2,
1117                         "Destination reference: 0x%04x", dst_ref);
1118   }
1119   offset += 2;
1120   li -= 2;
1121
1122   if (tree) {
1123     proto_tree_add_text(cotp_tree, tvb, offset, 2,
1124                         "Source reference: 0x%04x", src_ref);
1125   }
1126   offset += 2;
1127   li -= 2;
1128
1129   if (tree)
1130     osi_decode_tp_var_part(tvb, offset, li, 4, cotp_tree);
1131   offset += li;
1132
1133   return offset;
1134
1135 } /* osi_decode_DC */
1136
1137 static int osi_decode_AK(tvbuff_t *tvb, int offset, 
1138                          packet_info *pinfo, proto_tree *tree)
1139 {
1140   proto_tree *cotp_tree = NULL;
1141   proto_item *ti;
1142   u_int      tpdu_nr;
1143   u_short    cdt_in_ak;
1144
1145   if (li > LI_MAX_AK) 
1146     return -1;
1147
1148   if (is_LI_NORMAL_AK(li)) {
1149
1150     tpdu_nr = tvb_get_guint8(tvb, offset + P_TPDU_NR_234);
1151
1152     if (check_col(pinfo->fd, COL_INFO))
1153       col_append_fstr(pinfo->fd, COL_INFO, "AK TPDU (%u) dst-ref: 0x%04x", 
1154                    tpdu_nr, dst_ref);
1155
1156     if (tree) {
1157       ti = proto_tree_add_item(tree, proto_cotp, tvb, offset, li + 1, FALSE);
1158       cotp_tree = proto_item_add_subtree(ti, ett_cotp);
1159       proto_tree_add_text(cotp_tree, tvb, offset, 1,
1160                           "Length indicator: %u", li);
1161     }
1162     offset += 1;
1163
1164     if (tree) {
1165       proto_tree_add_text(cotp_tree, tvb, offset, 1,
1166                           "TPDU code: 0x%x (AK)", tpdu);
1167       proto_tree_add_text(cotp_tree, tvb, offset, 1,
1168                           "Credit: %u", cdt);
1169     }
1170     offset += 1;
1171     li -= 1;
1172
1173     if (tree) {
1174       proto_tree_add_text(cotp_tree, tvb, offset, 2,
1175                           "Destination reference: 0x%04x", dst_ref);
1176     }
1177     offset += 2;
1178     li -= 2;
1179
1180     if (tree) {
1181       proto_tree_add_text(cotp_tree, tvb, offset, 1,
1182                           "Your TPDU number: 0x%02x", tpdu_nr);
1183     }
1184     offset += 1;
1185     li -= 1;
1186
1187     if (tree)
1188       osi_decode_tp_var_part(tvb, offset, li, 4, cotp_tree);
1189     offset += li;
1190
1191   } else { /* extended format */
1192     
1193     tpdu_nr   = tvb_get_ntohl(tvb, offset + P_TPDU_NR_234);
1194     cdt_in_ak = tvb_get_ntohs(tvb, offset + P_CDT_IN_AK);
1195
1196     if (check_col(pinfo->fd, COL_INFO))
1197       col_append_fstr(pinfo->fd, COL_INFO, "AK TPDU (%u) dst-ref: 0x%04x", 
1198                    tpdu_nr, dst_ref);
1199     
1200     if (tree) {
1201       ti = proto_tree_add_item(tree, proto_cotp, tvb, offset, li + 1, FALSE);
1202       cotp_tree = proto_item_add_subtree(ti, ett_cotp);
1203       proto_tree_add_text(cotp_tree, tvb, offset, 1,
1204                           "Length indicator: %u", li);
1205     }
1206     offset += 1;
1207
1208     if (tree) {
1209       proto_tree_add_text(cotp_tree, tvb, offset, 1,
1210                           "TPDU code: 0x%x (AK)", tpdu);
1211     }
1212     offset += 1;
1213     li -= 1;
1214
1215     if (tree) {
1216       proto_tree_add_text(cotp_tree, tvb, offset, 2,
1217                           "Destination reference: 0x%04x", dst_ref);
1218     }
1219     offset += 2;
1220     li -= 2;
1221
1222     if (tree) {
1223       proto_tree_add_text(cotp_tree, tvb, offset, 4,
1224                           "Your TPDU number: 0x%08x", tpdu_nr);
1225     }
1226     offset += 4;
1227     li -= 4;
1228
1229     if (tree) {
1230       proto_tree_add_text(cotp_tree, tvb, offset, 2,
1231                           "Credit: 0x%04x", cdt_in_ak);
1232     }
1233     offset += 2;
1234     li -= 2;
1235     
1236     if (tree)
1237       osi_decode_tp_var_part(tvb, offset, li, 4, cotp_tree);
1238     offset += li;
1239
1240   } /* is_LI_NORMAL_AK */
1241
1242   return offset;
1243
1244 } /* osi_decode_AK */
1245
1246 static int osi_decode_EA(tvbuff_t *tvb, int offset, 
1247                          packet_info *pinfo, proto_tree *tree)
1248 {
1249   proto_tree *cotp_tree = NULL;
1250   proto_item *ti;
1251   gboolean is_extended;
1252   u_int    tpdu_nr ;
1253
1254   if (li > LI_MAX_EA) 
1255     return -1;
1256
1257   /* VP_CHECKSUM is the only parameter allowed in the variable part.
1258      (This means we may misdissect this if the packet is bad and
1259      contains other parameters.) */
1260   switch (li) {
1261
1262     case LI_NORMAL_EA_WITH_CHECKSUM      :
1263       if (tvb_get_guint8(tvb, offset + P_VAR_PART_NDT) != VP_CHECKSUM ||
1264                 tvb_get_guint8(tvb, offset + P_VAR_PART_NDT + 1) != 2)
1265         return -1;
1266       /* FALLTHROUGH */
1267
1268     case LI_NORMAL_EA_WITHOUT_CHECKSUM   :
1269       tpdu_nr = tvb_get_guint8(tvb, offset + P_TPDU_NR_234);
1270       is_extended = FALSE;
1271       break;
1272
1273     case LI_EXTENDED_EA_WITH_CHECKSUM    :
1274       if (tvb_get_guint8(tvb, offset + P_VAR_PART_EDT) != VP_CHECKSUM ||
1275                 tvb_get_guint8(tvb, offset + P_VAR_PART_EDT + 1) != 2)
1276         return -1;
1277       /* FALLTHROUGH */
1278
1279     case LI_EXTENDED_EA_WITHOUT_CHECKSUM :
1280       tpdu_nr = tvb_get_ntohl(tvb, offset + P_TPDU_NR_234);
1281       is_extended = TRUE;
1282       break;
1283
1284     default : /* bad TPDU */
1285       return -1;
1286       /*NOTREACHED*/
1287       break;
1288   } /* li */
1289
1290   if (check_col(pinfo->fd, COL_INFO))
1291     col_append_fstr(pinfo->fd, COL_INFO, 
1292                  "EA TPDU (%u) dst-ref: 0x%04x", tpdu_nr, dst_ref);
1293
1294   if (tree) {
1295     ti = proto_tree_add_item(tree, proto_cotp, tvb, offset, li + 1, FALSE);
1296     cotp_tree = proto_item_add_subtree(ti, ett_cotp);
1297     proto_tree_add_text(cotp_tree, tvb, offset, 1,
1298                         "Length indicator: %u", li);
1299   }
1300   offset += 1;
1301
1302   if (tree) {
1303     proto_tree_add_text(cotp_tree, tvb, offset, 1,
1304                         "TPDU code: 0x%x (EA)", tpdu);
1305   }
1306   offset += 1;
1307   li -= 1;
1308
1309   if (tree) {
1310     proto_tree_add_text(cotp_tree, tvb, offset, 2,
1311                         "Destination reference: 0x%04x", dst_ref);
1312   }
1313   offset += 2;
1314   li -= 2;
1315
1316   if (is_extended) {
1317     if (tree) {
1318       proto_tree_add_text(cotp_tree, tvb, offset, 4,
1319                             "Your TPDU number: 0x%08x", tpdu_nr);
1320     }
1321     offset += 4;
1322     li -= 4;
1323   } else {
1324     if (tree) {
1325       proto_tree_add_text(cotp_tree, tvb, offset, 1,
1326                             "Your TPDU number: 0x%02x", tpdu_nr);
1327     }
1328     offset += 1;
1329     li -= 1;
1330   }
1331
1332   if (tree)
1333     osi_decode_tp_var_part(tvb, offset, li, 4, cotp_tree);
1334   offset += li;
1335
1336   return offset;
1337
1338 } /* osi_decode_EA */
1339
1340 static int osi_decode_ER(tvbuff_t *tvb, int offset, 
1341                          packet_info *pinfo, proto_tree *tree)
1342 {
1343   proto_tree *cotp_tree;
1344   proto_item *ti;
1345   u_char *str;
1346
1347   if (li > LI_MAX_ER) 
1348     return -1;
1349
1350   switch(tvb_get_guint8(tvb, offset + P_REJECT_ER)) {
1351     case 0 :
1352       str = "Reason not specified";
1353       break;
1354     case 1 :
1355       str = "Invalid parameter code";
1356       break;
1357     case 2 :
1358       str = "Invalid TPDU type";
1359       break;
1360     case 3 :
1361       str = "Invalid parameter value";
1362       break;
1363     default:
1364       return -1;
1365       /*NOTREACHED*/
1366       break;
1367   }
1368
1369   if (check_col(pinfo->fd, COL_INFO))
1370     col_append_fstr(pinfo->fd, COL_INFO, "ER TPDU dst-ref: 0x%04x", dst_ref);
1371
1372   if (tree) {
1373     ti = proto_tree_add_item(tree, proto_cotp, tvb, offset, li + 1, FALSE);
1374     cotp_tree = proto_item_add_subtree(ti, ett_cotp);
1375     proto_tree_add_text(cotp_tree, tvb, offset,      1,
1376                         "Length indicator: %u", li);
1377     proto_tree_add_text(cotp_tree, tvb, offset +  1, 1, 
1378                         "TPDU code: 0x%x (ER)", tpdu); 
1379     proto_tree_add_text(cotp_tree, tvb, offset +  2, 2, 
1380                         "Destination reference: 0x%04x", dst_ref);
1381     proto_tree_add_text(cotp_tree, tvb, offset +  4, 1, 
1382                         "Reject cause: %s", str);
1383   }
1384
1385   offset += li + 1;
1386
1387   return offset;
1388
1389 } /* osi_decode_ER */
1390
1391 static int osi_decode_UD(tvbuff_t *tvb, int offset, 
1392                          packet_info *pinfo, proto_tree *tree,
1393                          gboolean *subdissector_found)
1394 {
1395   proto_item *ti;
1396   proto_tree *cltp_tree = NULL;
1397   tvbuff_t   *next_tvb;
1398
1399   if (check_col(pinfo->fd, COL_INFO))
1400     col_append_str(pinfo->fd, COL_INFO, "UD TPDU");
1401
1402   if (tree) {
1403     ti = proto_tree_add_item(tree, proto_cltp, tvb, offset, li + 1, FALSE);
1404     cltp_tree = proto_item_add_subtree(ti, ett_cltp);
1405     proto_tree_add_text(cltp_tree, tvb, offset, 1,
1406                         "Length indicator: %u", li);
1407   }
1408   offset += 1;
1409
1410   if (tree) {
1411     proto_tree_add_text(cltp_tree, tvb, offset, 1, 
1412                         "TPDU code: 0x%x (UD)", tpdu);
1413   }
1414   offset += 1;
1415   li -= 1;
1416
1417   if (tree)
1418     osi_decode_tp_var_part(tvb, offset, li, 0, cltp_tree);
1419   offset += li;
1420
1421   next_tvb = tvb_new_subset(tvb, offset, -1, -1);
1422   dissect_data(next_tvb, 0, pinfo, tree);
1423   offset += tvb_length_remaining(tvb, offset);
1424      /* we dissected all of the containing PDU */
1425
1426   return offset;
1427
1428 } /* osi_decode_UD */
1429
1430 /* Returns TRUE if we found at least one valid COTP or CLTP PDU, FALSE
1431    otherwise.
1432
1433    There doesn't seem to be any way in which the OSI network layer protocol
1434    distinguishes between COTP and CLTP, but the first two octets of both
1435    protocols' headers mean the same thing - length and PDU type - and the
1436    only valid CLTP PDU type is not a valid COTP PDU type, so we'll handle
1437    both of them here. */
1438 static gboolean dissect_ositp_internal(tvbuff_t *tvb, packet_info *pinfo,
1439                   proto_tree *tree, gboolean uses_inactive_subset) 
1440 {
1441   int offset = 0;
1442   gboolean first_tpdu = TRUE;
1443   int new_offset;
1444   gboolean found_ositp = FALSE;
1445   gboolean is_cltp = FALSE;
1446   gboolean subdissector_found = FALSE;
1447
1448   if (!proto_is_protocol_enabled(proto_cotp))
1449     return FALSE;       /* COTP has been disabled */
1450   /* XXX - what about CLTP? */
1451
1452   pinfo->current_proto = "COTP";
1453
1454   /* Initialize the COL_INFO field; each of the TPDUs will have its
1455      information appended. */
1456   if (check_col(pinfo->fd, COL_INFO))
1457     col_add_str(pinfo->fd, COL_INFO, "");
1458
1459   while (tvb_offset_exists(tvb, offset)) {
1460     if (!first_tpdu) {
1461       if (check_col(pinfo->fd, COL_INFO))
1462         col_append_str(pinfo->fd, COL_INFO, ", ");
1463     }
1464     if ((li = tvb_get_guint8(tvb, offset + P_LI)) == 0) {
1465       if (check_col(pinfo->fd, COL_INFO))
1466         col_append_str(pinfo->fd, COL_INFO, "Length indicator is zero");
1467       if (!first_tpdu)
1468         dissect_data(tvb, offset, pinfo, tree);
1469       return found_ositp;
1470     }
1471
1472     tpdu    = (tvb_get_guint8(tvb, offset + P_TPDU) >> 4) & 0x0F;
1473     if (tpdu == UD_TPDU)
1474       pinfo->current_proto = "CLTP";    /* connectionless transport */
1475     cdt     = tvb_get_guint8(tvb, offset + P_CDT) & 0x0F;
1476     dst_ref = tvb_get_ntohs(tvb, offset + P_DST_REF);
1477
1478     switch (tpdu) {
1479       case CC_TPDU :
1480       case CR_TPDU :
1481         new_offset = osi_decode_CC(tvb, offset, pinfo, tree);
1482         break;
1483       case DR_TPDU :
1484         new_offset = osi_decode_DR(tvb, offset, pinfo, tree);
1485         break;
1486       case DT_TPDU :
1487         new_offset = osi_decode_DT(tvb, offset, pinfo, tree,
1488                                    uses_inactive_subset, &subdissector_found);
1489         break;
1490       case ED_TPDU :
1491         new_offset = osi_decode_ED(tvb, offset, pinfo, tree);
1492         break;
1493       case RJ_TPDU :
1494         new_offset = osi_decode_RJ(tvb, offset, pinfo, tree);
1495         break;
1496       case DC_TPDU :
1497         new_offset = osi_decode_DC(tvb, offset, pinfo, tree);
1498         break;
1499       case AK_TPDU :
1500         new_offset = osi_decode_AK(tvb, offset, pinfo, tree);
1501         break;
1502       case EA_TPDU :
1503         new_offset = osi_decode_EA(tvb, offset, pinfo, tree);
1504         break;
1505       case ER_TPDU :
1506         new_offset = osi_decode_ER(tvb, offset, pinfo, tree);
1507         break;
1508       case UD_TPDU :
1509         new_offset = osi_decode_UD(tvb, offset, pinfo, tree,
1510                                    &subdissector_found);
1511         is_cltp = TRUE;
1512         break;
1513       default      :
1514         if (first_tpdu && check_col(pinfo->fd, COL_INFO))
1515           col_append_fstr(pinfo->fd, COL_INFO, "Unknown TPDU type (0x%x)", tpdu);
1516         new_offset = -1;        /* bad PDU type */
1517         break;
1518     }
1519
1520     if (new_offset == -1) { /* incorrect TPDU */
1521       if (!first_tpdu)
1522         dissect_data(tvb, offset, pinfo, tree);
1523       break;
1524     }
1525
1526     if (first_tpdu) {
1527       /* Well, we found at least one valid COTP or CLTP PDU, so I guess this
1528          is either COTP or CLTP. */
1529       if (!subdissector_found && check_col(pinfo->fd, COL_PROTOCOL))
1530         col_set_str(pinfo->fd, COL_PROTOCOL, is_cltp ? "CLTP" : "COTP");
1531       found_ositp = TRUE;
1532     }
1533
1534     offset = new_offset;
1535     first_tpdu = FALSE;
1536   }
1537   return found_ositp;
1538 } /* dissect_ositp_internal */
1539
1540 static void dissect_ositp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) 
1541 {
1542   if (!dissect_ositp_internal(tvb, pinfo, tree, FALSE))
1543     dissect_data(tvb, 0, pinfo, tree);
1544 }
1545
1546
1547 /*
1548  *  CLNP part / main entry point 
1549 */
1550
1551 static void dissect_clnp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1552 {
1553   proto_tree *clnp_tree = NULL;
1554   proto_item *ti;
1555   guint8      cnf_proto_id;
1556   guint8      cnf_hdr_len;
1557   guint8      cnf_vers;
1558   guint8      cnf_ttl;
1559   guint8      cnf_type;
1560   char        flag_string[6+1];
1561   char       *pdu_type_string;
1562   proto_tree *type_tree;
1563   guint16     segment_length;
1564   guint16     segment_offset = 0;
1565   guint16     cnf_cksum;
1566   int         offset;
1567   u_char      src_len, dst_len, nsel, opt_len = 0;
1568   guint8     *dst_addr, *src_addr;
1569   guint       len;
1570   guint       next_length;
1571   proto_tree *discpdu_tree;
1572   tvbuff_t   *next_tvb;
1573
1574   CHECK_DISPLAY_AS_DATA(proto_clnp, tvb, pinfo, tree);
1575
1576   pinfo->current_proto = "CLNP";
1577
1578   if (check_col(pinfo->fd, COL_PROTOCOL))
1579     col_set_str(pinfo->fd, COL_PROTOCOL, "CLNP");
1580
1581   cnf_proto_id = tvb_get_guint8(tvb, P_CLNP_PROTO_ID);
1582   if (cnf_proto_id == NLPID_NULL) {
1583     if (check_col(pinfo->fd, COL_INFO))
1584       col_set_str(pinfo->fd, COL_INFO, "Inactive subset");
1585     if (tree) {
1586       ti = proto_tree_add_item(tree, proto_clnp, tvb, P_CLNP_PROTO_ID, 1, FALSE);
1587       clnp_tree = proto_item_add_subtree(ti, ett_clnp);
1588       proto_tree_add_uint_format(clnp_tree, hf_clnp_id, tvb, P_CLNP_PROTO_ID, 1, 
1589                                  cnf_proto_id,
1590                                  "Inactive subset");
1591     } 
1592     next_tvb = tvb_new_subset(tvb, 1, -1, -1);
1593     dissect_ositp_internal(next_tvb, pinfo, tree, TRUE);
1594     return;
1595   } 
1596  
1597   /* return if version not known */
1598   cnf_vers = tvb_get_guint8(tvb, P_CLNP_VERS);
1599   if (cnf_vers != ISO8473_V1) {
1600     dissect_data(tvb, 0, pinfo, tree);
1601     return;
1602   }
1603
1604   /* fixed part decoding */
1605   cnf_hdr_len = tvb_get_guint8(tvb, P_CLNP_HDR_LEN);
1606   opt_len = cnf_hdr_len;
1607
1608   if (tree) {
1609     ti = proto_tree_add_item(tree, proto_clnp, tvb, 0, cnf_hdr_len, FALSE);
1610     clnp_tree = proto_item_add_subtree(ti, ett_clnp);
1611     proto_tree_add_uint(clnp_tree, hf_clnp_id, tvb, P_CLNP_PROTO_ID, 1, 
1612                                cnf_proto_id);
1613     proto_tree_add_uint(clnp_tree, hf_clnp_length, tvb, P_CLNP_HDR_LEN, 1, 
1614                         cnf_hdr_len); 
1615     proto_tree_add_uint(clnp_tree, hf_clnp_version, tvb, P_CLNP_VERS, 1, 
1616                         cnf_vers);
1617     cnf_ttl = tvb_get_guint8(tvb, P_CLNP_TTL);
1618     proto_tree_add_uint_format(clnp_tree, hf_clnp_ttl, tvb, P_CLNP_TTL, 1, 
1619                                cnf_ttl,
1620                                "Holding Time : %u (%u.%u secs)", 
1621                                cnf_ttl, cnf_ttl / 2, (cnf_ttl % 2) * 5);
1622   }
1623
1624   cnf_type = tvb_get_guint8(tvb, P_CLNP_TYPE);
1625   pdu_type_string = val_to_str(cnf_type & CNF_TYPE, npdu_type_abbrev_vals,
1626                                 "Unknown (0x%02x)");
1627   flag_string[0] = '\0';
1628   if (cnf_type & CNF_SEG_OK)
1629     strcat(flag_string, "S ");
1630   if (cnf_type & CNF_MORE_SEGS)
1631     strcat(flag_string, "M ");
1632   if (cnf_type & CNF_ERR_OK)
1633     strcat(flag_string, "E ");
1634   if (tree) {
1635     ti = proto_tree_add_uint_format(clnp_tree, hf_clnp_type, tvb, P_CLNP_TYPE, 1,
1636                                cnf_type,
1637                                "PDU Type     : 0x%02x (%s%s)",
1638                                cnf_type,
1639                                flag_string,
1640                                pdu_type_string);
1641     type_tree = proto_item_add_subtree(ti, ett_clnp_type);
1642     proto_tree_add_text(type_tree, tvb, P_CLNP_TYPE, 1, "%s",
1643                         decode_boolean_bitfield(cnf_type, CNF_SEG_OK, 8,
1644                                       "Segmentation permitted",
1645                                       "Segmentation not permitted"));
1646     proto_tree_add_text(type_tree, tvb, P_CLNP_TYPE, 1, "%s",
1647                         decode_boolean_bitfield(cnf_type, CNF_MORE_SEGS, 8,
1648                                       "More segments",
1649                                       "Last segment"));
1650     proto_tree_add_text(type_tree, tvb, P_CLNP_TYPE, 1, "%s",
1651                         decode_boolean_bitfield(cnf_type, CNF_ERR_OK, 8,
1652                                       "Report error if PDU discarded",
1653                                       "Don't report error if PDU discarded"));
1654     proto_tree_add_text(type_tree, tvb, P_CLNP_TYPE, 1, "%s",
1655                         decode_enumerated_bitfield(cnf_type, CNF_TYPE, 8,
1656                                       npdu_type_vals, "%s"));
1657   }
1658
1659   /* If we don't have the full header - i.e., not enough to see the
1660      segmentation part and determine whether this datagram is segmented
1661      or not - set the Info column now; we'll get an exception before
1662      we set it otherwise. */
1663
1664   if (!tvb_bytes_exist(tvb, 0, cnf_hdr_len)) {
1665     if (check_col(pinfo->fd, COL_INFO))
1666       col_add_fstr(pinfo->fd, COL_INFO, "%s NPDU %s", pdu_type_string, flag_string);
1667   }
1668
1669   segment_length = tvb_get_ntohs(tvb, P_CLNP_SEGLEN);
1670   if (tree) {
1671     proto_tree_add_uint(clnp_tree, hf_clnp_pdu_length, tvb, P_CLNP_SEGLEN, 2,
1672                         segment_length);
1673     cnf_cksum = tvb_get_ntohs(tvb, P_CLNP_CKSUM);
1674     proto_tree_add_uint_format(clnp_tree, hf_clnp_checksum, tvb, P_CLNP_CKSUM, 2,
1675                                cnf_cksum,
1676                                "Checksum     : 0x%04x",
1677                                cnf_cksum);
1678     opt_len -= 9; /* Fixed part of Hesder */
1679   } /* tree */
1680
1681   /* address part */
1682   
1683   offset = P_CLNP_ADDRESS_PART;
1684   dst_len  = tvb_get_guint8(tvb, offset);
1685   dst_addr = tvb_get_ptr(tvb, offset + 1, dst_len);
1686   nsel     = tvb_get_guint8(tvb, offset + dst_len);
1687   src_len  = tvb_get_guint8(tvb, offset + dst_len + 1);
1688   src_addr = tvb_get_ptr(tvb, offset + dst_len + 2, src_len);
1689
1690   if (tree) {
1691     proto_tree_add_uint(clnp_tree, hf_clnp_dest_length, tvb, offset, 1, 
1692                         dst_len);
1693     proto_tree_add_bytes_format(clnp_tree, hf_clnp_dest, tvb, offset + 1 , dst_len, 
1694                                dst_addr,
1695                                " DA : %s", 
1696                                print_nsap_net(dst_addr, dst_len));
1697     proto_tree_add_uint(clnp_tree, hf_clnp_src_length, tvb, 
1698                         offset + 1 + dst_len, 1, src_len);
1699     proto_tree_add_bytes_format(clnp_tree, hf_clnp_src, tvb, 
1700                                offset + dst_len + 2, src_len,
1701                                src_addr,
1702                                " SA : %s", 
1703                                print_nsap_net(src_addr, src_len));
1704
1705     opt_len -= dst_len + src_len +2;
1706   }
1707
1708   if (check_col(pinfo->fd, COL_RES_NET_SRC))
1709     col_add_fstr(pinfo->fd, COL_RES_NET_SRC, "%s", print_nsap_net(src_addr, src_len));
1710   if (check_col(pinfo->fd, COL_RES_NET_DST))
1711     col_add_fstr(pinfo->fd, COL_RES_NET_DST, "%s", print_nsap_net(dst_addr, dst_len));
1712
1713   /* Segmentation Part */
1714
1715   offset += dst_len + src_len + 2;
1716
1717   if (cnf_type & CNF_SEG_OK) {
1718     struct clnp_segment seg;                    /* XXX - not used */
1719     tvb_memcpy(tvb, (guint8 *)&seg, offset, sizeof(seg));       /* XXX - not used */
1720     
1721     segment_offset = tvb_get_ntohs(tvb, offset + 2);
1722     if (tree) {
1723       proto_tree_add_text(clnp_tree, tvb, offset, 2, 
1724                         "Data unit identifier: %06u",
1725                         tvb_get_ntohs(tvb, offset));
1726       proto_tree_add_text(clnp_tree, tvb, offset + 2 , 2,
1727                         "Segment offset      : %6u", 
1728                         segment_offset);
1729       proto_tree_add_text(clnp_tree, tvb, offset + 4 , 2,
1730                         "Total length        : %6u", 
1731                         tvb_get_ntohs(tvb, offset + 4));
1732     }
1733     
1734     offset  += 6;
1735     opt_len -= 6;
1736   }
1737
1738   if (tree) {
1739     /* To do : decode options  */
1740 /*
1741     proto_tree_add_text(clnp_tree, tvb, offset, 
1742                         cnf_hdr_len - offset,
1743                         "Options/Data: <not shown>");
1744 */
1745 /* QUICK HACK Option Len:= PDU_Hd_length-( FixedPart+AddresPart+SegmentPart )*/
1746
1747     dissect_osi_options( 0xff, 
1748                          opt_len,
1749                          tvb, offset, pinfo, clnp_tree ); 
1750   }
1751
1752   /* Length of CLNP datagram plus headers above it. */
1753   len = segment_length;
1754
1755   /* Set the payload and captured-payload lengths to the minima of (the
1756      datagram length plus the length of the headers above it) and the
1757      frame lengths. */
1758   if (pinfo->len > len)
1759     pinfo->len = len;
1760   if (pinfo->captured_len > len)
1761     pinfo->captured_len = len;
1762
1763   offset = cnf_hdr_len;
1764
1765   /* For now, dissect the payload of segments other than the initial
1766      segment as data, rather than handing them off to the transport
1767      protocol, just as we do with fragments other than the first
1768      fragment in a fragmented IP datagram; in the future, we will
1769      probably reassemble fragments for IP, and may reassemble segments
1770      for CLNP. */
1771   if ((cnf_type & CNF_SEG_OK) && segment_offset != 0) {
1772     if (check_col(pinfo->fd, COL_INFO))
1773       col_add_fstr(pinfo->fd, COL_INFO, "Fragmented %s NPDU %s(off=%u)",
1774                 pdu_type_string, flag_string, segment_offset);
1775     dissect_data(tvb, offset, pinfo, tree);
1776     return;
1777   }
1778
1779   if (tvb_offset_exists(tvb, offset)) {
1780     switch (cnf_type & CNF_TYPE) {
1781
1782     case DT_NPDU:
1783     case MD_NPDU:
1784       /* Continue with COTP if any data.
1785          XXX - if this isn't the first Derived PDU of a segmented Initial
1786          PDU, skip that? */
1787
1788       if (nsel == (char)tp_nsap_selector || always_decode_transport) { 
1789         next_tvb = tvb_new_subset(tvb, offset, -1, -1);
1790         if (dissect_ositp_internal(next_tvb, pinfo, tree, FALSE))
1791           return;       /* yes, it appears to be COTP or CLTP */
1792       }
1793       break;
1794
1795     case ER_NPDU:
1796       /* The payload is the header and "none, some, or all of the data
1797          part of the discarded PDU", i.e. it's like an ICMP error;
1798          dissect it as a CLNP PDU. */
1799       if (tree) {
1800         next_length = tvb_length_remaining(tvb, offset);
1801         if (next_length != 0) {
1802           /* We have payload; dissect it.
1803              Make the columns non-writable, so the packet isn't shown
1804              in the summary based on what the discarded PDU's contents
1805              are. */
1806           col_set_writable(pinfo->fd, FALSE);
1807           ti = proto_tree_add_text(clnp_tree, tvb, offset, next_length,
1808             "Discarded PDU");
1809           discpdu_tree = proto_item_add_subtree(ti, ett_clnp_disc_pdu);
1810           next_tvb = tvb_new_subset(tvb, offset, -1, -1);
1811           dissect_clnp(next_tvb, pinfo, discpdu_tree);
1812           offset += next_length;
1813         }
1814       }
1815       break;
1816
1817     case ERQ_NPDU:
1818     case ERP_NPDU:
1819       /* XXX - dissect this */
1820       break;
1821     }
1822   }
1823   if (check_col(pinfo->fd, COL_INFO))
1824     col_add_fstr(pinfo->fd, COL_INFO, "%s NPDU %s", pdu_type_string, flag_string);
1825   next_tvb = tvb_new_subset(tvb, offset, -1, -1);
1826   dissect_data(next_tvb, 0, pinfo, tree);
1827
1828 } /* dissect_clnp */
1829
1830
1831 void proto_register_clnp(void)
1832 {
1833   static hf_register_info hf[] = {
1834     { &hf_clnp_id,
1835       { "Network Layer Protocol Identifier", "clnp.nlpi", FT_UINT8, BASE_HEX, 
1836         VALS(nlpid_vals), 0x0, "" }},
1837
1838     { &hf_clnp_length,
1839       { "HDR Length   ", "clnp.len",       FT_UINT8, BASE_DEC, NULL, 0x0, "" }},
1840
1841     { &hf_clnp_version,
1842       { "Version      ", "clnp.version",  FT_UINT8, BASE_DEC, NULL, 0x0, "" }},
1843
1844     { &hf_clnp_ttl,
1845       { "Holding Time ", "clnp.ttl",       FT_UINT8, BASE_DEC, NULL, 0x0, "" }},
1846
1847     { &hf_clnp_type,
1848       { "PDU Type     ", "clnp.type",     FT_UINT8, BASE_DEC, NULL, 0x0, "" }},
1849
1850     { &hf_clnp_pdu_length,
1851       { "PDU length   ", "clnp.pdu.len",  FT_UINT16, BASE_DEC, NULL, 0x0, "" }},
1852
1853     { &hf_clnp_checksum,
1854       { "Checksum     ", "clnp.checksum", FT_UINT16, BASE_DEC, NULL, 0x0, "" }},
1855
1856     { &hf_clnp_dest_length,
1857       { "DAL ", "clnp.dsap.len", FT_UINT8, BASE_DEC, NULL, 0x0, "" }},
1858
1859     { &hf_clnp_dest,
1860       { " DA ", "clnp.dsap",     FT_BYTES, BASE_NONE, NULL, 0x0, "" }},
1861
1862     { &hf_clnp_src_length,
1863       { "SAL ", "clnp.ssap.len", FT_UINT8, BASE_DEC, NULL, 0x0, "" }},
1864
1865     { &hf_clnp_src,
1866       { " SA ", "clnp.ssap",     FT_BYTES, BASE_NONE, NULL, 0x0, "" }},
1867   };
1868   static gint *ett[] = {
1869     &ett_clnp,
1870     &ett_clnp_type,
1871     &ett_clnp_disc_pdu,
1872   };
1873
1874   module_t *clnp_module;
1875
1876   proto_clnp = proto_register_protocol(PROTO_STRING_CLNP, "clnp");
1877   proto_register_field_array(proto_clnp, hf, array_length(hf));
1878   proto_register_subtree_array(ett, array_length(ett));
1879
1880   clnp_module = prefs_register_module("clnp", "CLNP", NULL);
1881   prefs_register_uint_preference(clnp_module, "tp_nsap_selector",
1882         "NSAP selector for Transport Protocol (last byte in hexa)",
1883         "NSAP selector for Transport Protocol (last byte in hexa)",
1884         16, &tp_nsap_selector);
1885   prefs_register_bool_preference(clnp_module, "always_decode_transport",
1886         "Always try to decode NSDU as transport PDUs",
1887         "Always try to decode NSDU as transport PDUs",
1888         &always_decode_transport);
1889
1890 }
1891
1892 void proto_register_cotp(void)
1893 {
1894   /*        static hf_register_info hf[] = {
1895                 { &variable,
1896                 { "Name",           "cotp.abbreviation", TYPE, VALS_POINTER }},
1897         };*/
1898         static gint *ett[] = {
1899                 &ett_cotp,
1900         };
1901
1902         proto_cotp = proto_register_protocol(PROTO_STRING_COTP, "cotp");
1903  /*       proto_register_field_array(proto_cotp, hf, array_length(hf));*/
1904         proto_register_subtree_array(ett, array_length(ett));
1905
1906 /* subdissector code */
1907         register_heur_dissector_list("cotp_is", &cotp_is_heur_subdissector_list);
1908
1909         register_dissector("ositp", dissect_ositp);
1910 }
1911
1912 void proto_register_cltp(void)
1913 {
1914   /*        static hf_register_info hf[] = {
1915                 { &variable,
1916                 { "Name",           "cltp.abbreviation", TYPE, VALS_POINTER }},
1917         };*/
1918         static gint *ett[] = {
1919                 &ett_cltp,
1920         };
1921
1922         proto_cltp = proto_register_protocol(PROTO_STRING_CLTP, "cltp");
1923  /*       proto_register_field_array(proto_cotp, hf, array_length(hf));*/
1924         proto_register_subtree_array(ett, array_length(ett));
1925 }
1926
1927 void
1928 proto_reg_handoff_clnp(void)
1929 {
1930         dissector_add("osinl", NLPID_ISO8473_CLNP, dissect_clnp);
1931         dissector_add("osinl", NLPID_NULL, dissect_clnp);       /* Inactive subset */
1932 }