Add a separate hash table to the reassembly code for reassembled
[obnox/wireshark/wip.git] / packet-osi-options.c
1 /* packet-osi-options.c
2  * Routines for the decode of ISO/OSI option part 
3  * Covers:
4  * ISO  8473 CLNP (ConnectionLess Mode Network Service Protocol)
5  * ISO 10589 ISIS (Intradomain Routing Information Exchange Protocol)
6  * ISO  9542 ESIS (End System To Intermediate System Routing Exchange Protocol)
7  *
8  * $Id: packet-osi-options.c,v 1.11 2002/04/14 23:04:03 guy Exp $
9  * Ralf Schneider <Ralf.Schneider@t-online.de>
10  *
11  * Ethereal - Network traffic analyzer
12  * By Gerald Combs <gerald@ethereal.com>
13  * Copyright 1998 Gerald Combs
14  * 
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  * 
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  * 
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
28  */
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #ifdef HAVE_SYS_TYPES_H
35 # include <sys/types.h>
36 #endif
37
38 #include <stdio.h>
39 #include <string.h>
40 #include <glib.h>
41 #include <epan/packet.h>
42 #include "nlpid.h"
43 #include "packet-osi.h"
44 #include "packet-isis.h"
45 #include "packet-isis-clv.h"
46 #include "packet-isis-hello.h"
47 #include "packet-isis-lsp.h"
48 #include "packet-isis-snp.h"
49 #include "packet-esis.h"
50 #include "packet-osi-options.h"
51
52 #define OSI_OPT_SECURITY           0xc5
53 #define OSI_OPT_QOS_MAINTANANCE    0xc3
54 #define OSI_OPT_PRIORITY           0xcd
55 #define OSI_OPT_ADDRESS_MASK       0xe1
56 #define OSI_OPT_SNPA_MASK          0xe2
57 #define OSI_OPT_ES_CONFIG_TIMER    0xc6
58
59 #define OSI_OPT_MAX_PRIORITY       0x0e
60
61 #define OSI_OPT_PADDING            0xcc
62 #define OSI_OPT_SOURCE_ROUTING     0xc8
63 #define OSI_OPT_RECORD_OF_ROUTE    0xcb
64 #define OSI_OPT_REASON_OF_DISCARD  0xc1
65
66 #define OSI_OPT_SEC_MASK           0xc0
67 #define OSI_OPT_SEC_RESERVED       0x00
68 #define OSI_OPT_SEC_SRC_ADR_SPEC   0x40
69 #define OSI_OPT_SEC_DST_ADR_SPEC   0x80
70 #define OSI_OPT_SEC_GLOBAL_UNIQUE  0xc0
71
72 #define OSI_OPT_QOS_MASK           0xc0
73 #define OSI_OPT_QOS_RESERVED       0x00
74 #define OSI_OPT_QOS_SRC_ADR_SPEC   0x40
75 #define OSI_OPT_QOS_DST_ADR_SPEC   0x80
76 #define OSI_OPT_QOS_GLOBAL_UNIQUE  0xc0
77  
78 #define OSI_OPT_QOS_SUB_MASK        0x3f
79 #define OSI_OPT_QOS_SUB_RSVD        0x20
80 #define OSI_OPT_QOS_SUB_SEQ_VS_TRS  0x10
81 #define OSI_OPT_QOS_SUB_CONG_EXPED  0x08
82 #define OSI_OPT_QOS_SUB_TSD_VS_COST 0x04
83 #define OSI_OPT_QOS_SUB_RESERR_TRS  0x02
84 #define OSI_OPT_QOS_SUB_RESERR_COST 0x01
85
86 #define OSI_OPT_RFD_GENERAL         0x00
87 #define OSI_OPT_RFD_ADDRESS         0x80
88 #define OSI_OPT_RFD_SOURCE_ROUTING  0x90
89 #define OSI_OPT_RFD_LIFETIME        0xa0
90 #define OSI_OPT_RFD_PDU_DISCARDED   0xb0
91 #define OSI_OPT_RFD_REASSEMBLY      0xc0
92
93 #define OSI_OPT_RFD_MASK            0xf0
94 #define OSI_OPT_RFD_SUB_MASK        0x0f
95
96
97
98
99 static gint ott_osi_options       = -1;
100 static gint ott_osi_qos           = -1;
101 static gint ott_osi_route         = -1;
102 static gint ott_osi_redirect      = -1;
103
104 static const value_string osi_opt_sec_vals[] = {
105         { OSI_OPT_SEC_RESERVED,      "Reserved"},
106         { OSI_OPT_SEC_SRC_ADR_SPEC,  "Source Address Specific"},
107         { OSI_OPT_SEC_DST_ADR_SPEC,  "Destination Address Specific"},
108         { OSI_OPT_SEC_GLOBAL_UNIQUE, "Globally Unique"},
109         { 0,            NULL} };
110      
111 static const value_string osi_opt_qos_vals[] = {
112         { OSI_OPT_QOS_RESERVED,      "Reserved"},
113         { OSI_OPT_QOS_SRC_ADR_SPEC,  "Source Address Specific"},
114         { OSI_OPT_QOS_DST_ADR_SPEC,  "Destination Address Specific"},
115         { OSI_OPT_QOS_GLOBAL_UNIQUE, "Globally Unique"},
116         { 0,            NULL} };
117      
118 static const value_string osi_opt_qos_sub_vals[] = {
119         { 0x20,  " xx10 0000 Reserved"},
120         { 0x10,  " xx01 0000 Sequencing versus transit delay"},
121         { 0x08,  " xx00 1000 Congestion experienced"},
122         { 0x04,  " xx00 0100 Transit delay versus cost"},
123         { 0x02,  " xx00 0010 Residual error probability versus transit delay"},
124         { 0x01,  " xx00 0001 Residual error probability versus cost"},
125         { 0,            NULL} };
126   
127 static const value_string osi_opt_rfd_general[] = {
128         { 0x00, "Reason not specified"},
129         { 0x01, "Protocol procedure error"},
130         { 0x02, "Incorrect checksum"},
131         { 0x03, "PDU discarded due to congestion"},
132         { 0x04, "Header syntax error ( cannot be parsed )"},
133         { 0x05, "Segmentation needed but not permitted"},
134         { 0x06, "Incomplete PDU received"},
135         { 0x07, "Duplicate option"},
136         { 0,    NULL} };
137      
138 static const value_string osi_opt_rfd_address[] = {
139         { 0x00, "Destination Address unreachable"},
140         { 0x01, "Destination Address unknown"},
141         { 0,    NULL} };
142      
143 static const value_string osi_opt_rfd_src_route[] = {
144         { 0x00, "Unspecified source routing error"},
145         { 0x01, "Syntax error in source routing field"},
146         { 0x02, "Unknown address in source routing field"},
147         { 0x03, "Path not acceptable"},
148         { 0,    NULL} };
149      
150 static const value_string osi_opt_rfd_lifetime[] = {
151         { 0x00, "Lifetime expired while data unit in transit"},
152         { 0x01, "Lifetime expired during reassembly"},
153         { 0,    NULL} };
154      
155 static const value_string osi_opt_rfd_discarded[] = {
156         { 0x00, "Unsupported option not specified"},
157         { 0x01, "Unsupported protocol version"},
158         { 0x02, "Unsupported security option"},
159         { 0x03, "Unsupported source routing option"},
160         { 0x04, "Unsupported recording of route option"},
161         { 0,    NULL} };
162      
163 static const value_string osi_opt_rfd_reassembly[] = {
164         { 0x00, "Reassembly interference"},
165         { 0,    NULL} };
166      
167
168 static void
169 dissect_option_qos( const u_char type, const u_char sub_type, int offset,
170                     u_char len, tvbuff_t *tvb, proto_tree *tree ) {
171
172   u_char      tmp_type = 0;
173   proto_item *ti;
174   proto_tree *osi_qos_tree = NULL;
175   
176   
177   ti = proto_tree_add_text( tree, tvb, offset, len,
178                             "Quality of service maintenance: %s",
179                        val_to_str( type, osi_opt_qos_vals, "Unknown (0x%x)") );
180   
181   osi_qos_tree = proto_item_add_subtree( ti, ott_osi_qos );
182                            
183   if ( OSI_OPT_SEC_MASK == type ) {     /* Analye BIT field to get all Values */
184
185     tmp_type = sub_type & OSI_OPT_QOS_SUB_RSVD;
186     if ( tmp_type ) {
187          proto_tree_add_text( osi_qos_tree, tvb, offset, len,
188          val_to_str( tmp_type, osi_opt_qos_sub_vals, "Unknown (0x%x)") );
189     }
190     tmp_type = sub_type & OSI_OPT_QOS_SUB_SEQ_VS_TRS;
191     if ( tmp_type ) {
192          proto_tree_add_text( osi_qos_tree, tvb, offset, len,
193          val_to_str( tmp_type, osi_opt_qos_sub_vals, "Unknown (0x%x)") );
194     }
195     tmp_type = sub_type &OSI_OPT_QOS_SUB_CONG_EXPED;
196     if ( tmp_type ) {
197          proto_tree_add_text( osi_qos_tree, tvb, offset, len,
198          val_to_str( tmp_type, osi_opt_qos_sub_vals, "Unknown (0x%x)") );
199     }
200     tmp_type = sub_type & OSI_OPT_QOS_SUB_TSD_VS_COST;
201     
202     if ( tmp_type ) {
203          proto_tree_add_text( osi_qos_tree, tvb, offset, len,
204          val_to_str( tmp_type, osi_opt_qos_sub_vals, "Unknown (0x%x)") );
205     }
206     tmp_type = sub_type & OSI_OPT_QOS_SUB_RESERR_TRS;
207     if ( tmp_type ) {
208          proto_tree_add_text( osi_qos_tree, tvb, offset, len,
209          val_to_str( tmp_type, osi_opt_qos_sub_vals, "Unknown (0x%x)") );
210     }
211     tmp_type = sub_type & OSI_OPT_QOS_SUB_RESERR_COST;
212     if ( tmp_type ) {
213          proto_tree_add_text( osi_qos_tree, tvb, offset, len,
214          val_to_str( tmp_type, osi_opt_qos_sub_vals, "Unknown (0x%x)") );
215     }
216   }
217 }
218
219 static void
220 dissect_option_route( u_char parm_type, u_char offset, u_char parm_len, 
221                       tvbuff_t *tvb, proto_tree *tree ) {
222
223   u_char      next_hop = 0;
224   u_char      this_hop = 0;
225   u_char      netl     = 0;
226   u_char      last_hop = 0;
227   u_char      cnt_hops = 0;
228   
229   proto_item *ti;
230   proto_tree *osi_route_tree = NULL;
231
232   static const value_string osi_opt_route[] = {
233         { 0x03, "No Network Entity Titles Recorded Yet"},
234         { 0xff, "Recording Terminated !"},
235         { 0,    NULL} };
236
237   if ( parm_type == OSI_OPT_SOURCE_ROUTING ) {
238     next_hop = tvb_get_guint8(tvb, offset + 1 );
239     netl     = tvb_get_guint8(tvb, next_hop + 2 );
240     this_hop = offset + 3;         /* points to first netl */
241
242     ti = proto_tree_add_text( tree, tvb, offset + next_hop, netl, 
243             "Source Routing: %s   ( Next Hop Highlighted In Data Buffer )",
244             (tvb_get_guint8(tvb, offset) == 0) ? "Partial Source Routing" :
245                                                  "Complete Source Routing"  ); 
246   }
247   else {
248     last_hop = tvb_get_guint8(tvb, offset + 1 );
249         /* points to the end of the list */
250     netl     = tvb_get_guint8(tvb, last_hop );
251         /* mis-used to highlight buffer */
252
253     ti = proto_tree_add_text( tree, tvb, offset + next_hop, netl,
254             "Record of Route: %s : %s",
255             (tvb_get_guint8(tvb, offset) == 0) ? "Partial Source Routing" :
256                                                  "Complete Source Routing" ,
257             val_to_str( last_hop, osi_opt_route, "Unknown (0x%x" ) );
258     if ( 255 == last_hop ) 
259       this_hop = parm_len + 1;   /* recording terminated, nothing to show */
260     else
261       this_hop = offset + 3;
262   }
263   osi_route_tree = proto_item_add_subtree( ti, ott_osi_route );
264   
265   while ( this_hop < parm_len ) {
266     netl = tvb_get_guint8(tvb, this_hop + 1);
267     proto_tree_add_text( osi_route_tree, tvb, offset + this_hop, netl,
268                   "Hop #%3u NETL: %2u, NET: %s",
269                   cnt_hops++,
270                   netl,
271                   print_nsap_net( tvb_get_ptr(tvb, this_hop + 1, netl), netl ) );
272     this_hop += 1 + netl;
273   }
274 }
275
276
277
278
279
280 static void
281 dissect_option_rfd( const u_char error, const u_char field, u_char offset,
282                           u_char len, tvbuff_t *tvb, proto_tree *tree ) {
283   u_char error_class = 0;
284   char   *format_string[] = 
285              { "Reason for discard {General}        : %s, in field %u",
286                "Reason for discard {Address}        : %s, in field %u",
287                "Reason for discard {Source Routing}: %s, in field %u",
288                "Reason for discard {Lifetime}       : %s, in field %u",
289                "Reason for discard {PDU discarded}  : %s, in field %u",
290                "Reason for discard {Reassembly}     : %s, in field %u"
291              };
292   
293   error_class = error & OSI_OPT_RFD_MASK;
294
295   if ( OSI_OPT_RFD_GENERAL == error_class ) {
296     proto_tree_add_text( tree, tvb, offset + field, 1, format_string[0],
297                          val_to_str( error & OSI_OPT_RFD_SUB_MASK,
298                                osi_opt_rfd_general, "Unknown (0x%x)"), field );
299   }
300   else if ( OSI_OPT_RFD_ADDRESS == error_class ) {
301     proto_tree_add_text( tree, tvb, offset + field, 1, format_string[1],
302                          val_to_str( error & OSI_OPT_RFD_SUB_MASK,
303                                osi_opt_rfd_address, "Unknown (0x%x)"), field );
304   }
305   else if ( OSI_OPT_RFD_SOURCE_ROUTING == error_class ) {
306     proto_tree_add_text( tree, tvb, offset + field, 1, format_string[2],
307                          val_to_str( error & OSI_OPT_RFD_SUB_MASK,
308                              osi_opt_rfd_src_route, "Unknown (0x%x)"), field );
309   }
310   else if ( OSI_OPT_RFD_LIFETIME == error_class ) {
311     proto_tree_add_text( tree, tvb, offset + field, 1, format_string[3],
312                          val_to_str( error & OSI_OPT_RFD_SUB_MASK,
313                               osi_opt_rfd_lifetime, "Unknown (0x%x)"), field );
314   }
315   else if ( OSI_OPT_RFD_PDU_DISCARDED == error_class ) {
316     proto_tree_add_text( tree, tvb, offset + field, 1, format_string[4],
317                          val_to_str( error & OSI_OPT_RFD_SUB_MASK,
318                              osi_opt_rfd_discarded, "Unknown (0x%x)"), field );
319   }
320   else if ( OSI_OPT_RFD_REASSEMBLY == error_class ) {
321     proto_tree_add_text( tree, tvb, offset + field, 1, format_string[5],
322                          val_to_str( error & OSI_OPT_RFD_SUB_MASK,
323                             osi_opt_rfd_reassembly, "Unknown (0x%x)"), field );
324   }
325   else {
326     proto_tree_add_text( tree, tvb, offset, len,
327                          "Reason for discard: UNKNOWN Error Class" );
328   } 
329 }
330
331 /* ############################## Dissection Functions ###################### */
332
333 /*
334  * Name: dissect_osi_options()
335  * 
336  * Description:
337  *   Main entry area for esis de-mangling.  This will build the
338  *   main esis tree data and call the sub-protocols as needed.
339  *
340  * Input:
341  *   u_char       : PDU type to check if option is allowed or not
342  *   u_char       : length of option section 
343  *   tvbuff_t *   : tvbuff containing packet data
344  *   int          : offset into packet where we are (packet_data[offset]== start
345  *                  of what we care about)
346  *   proto_tree * : tree of display data.  May be NULL.
347  *
348  * Output:
349  *   void, but we will add to the proto_tree if it is not NULL.
350  */
351 void
352 dissect_osi_options( u_char pdu_type, u_char opt_len, tvbuff_t *tvb, 
353                      int offset, proto_tree *tree) {
354    proto_item *ti;
355    proto_tree *osi_option_tree = NULL;
356    u_char      parm_len        = 0;
357    u_char      parm_type       = 0;
358    guint8      octet;
359
360    if (tree) {
361      if ( 0 == opt_len ) {
362        proto_tree_add_text( tree, tvb, offset, 0, 
363                             "### No Options for this PDU ###" );
364        return;
365      }
366      
367      ti = proto_tree_add_text( tree, tvb, offset, opt_len,
368                                "### Option Section ###" );
369      osi_option_tree = proto_item_add_subtree( ti, ott_osi_options );
370
371      while ( 0 < opt_len ) {
372         parm_type   = (int) tvb_get_guint8(tvb, offset);
373         offset++;
374         parm_len    = (int) tvb_get_guint8(tvb, offset);
375         offset++;
376          
377         switch ( parm_type ) {
378           case   OSI_OPT_QOS_MAINTANANCE:
379                  octet = tvb_get_guint8(tvb, offset);
380                  dissect_option_qos( octet&OSI_OPT_QOS_MASK,
381                                      octet&OSI_OPT_QOS_SUB_MASK,
382                                      offset, parm_len, tvb, osi_option_tree );
383           break;
384           case   OSI_OPT_SECURITY:
385                  octet = tvb_get_guint8(tvb, offset);
386                  proto_tree_add_text( osi_option_tree, tvb, offset, parm_len,
387                   "Security type: %s",
388                   val_to_str( octet&OSI_OPT_SEC_MASK,
389                               osi_opt_sec_vals, "Unknown (0x%x)")  );
390           break;
391           case   OSI_OPT_PRIORITY:
392                  octet = tvb_get_guint8(tvb, offset);
393                  if ( OSI_OPT_MAX_PRIORITY >= octet ) { 
394                    proto_tree_add_text( osi_option_tree, tvb, offset, parm_len,
395                                         "Priority    : %u", octet );
396                  }
397                  else {
398                    proto_tree_add_text( osi_option_tree, tvb, offset, parm_len,
399                                         "Priority    : %u ( Invalid )", 
400                                         octet );
401                  } 
402           break;
403           case   OSI_OPT_ADDRESS_MASK:
404                  proto_tree_add_text( osi_option_tree, tvb, offset, parm_len,
405                   "Address Mask: %s",
406                   print_area( tvb_get_ptr(tvb, offset, parm_len), parm_len ) );
407           break;
408           case   OSI_OPT_SNPA_MASK:
409                  proto_tree_add_text( osi_option_tree, tvb, offset, parm_len,
410                   "SNPA Mask   : %s",
411                   print_system_id( tvb_get_ptr(tvb, offset, parm_len), parm_len ));
412           break;
413           case   OSI_OPT_ES_CONFIG_TIMER:
414                  proto_tree_add_text( osi_option_tree, tvb, offset, parm_len,
415                   "ESCT     : %u seconds", tvb_get_ntohs( tvb, offset ) ); 
416           break;
417           case   OSI_OPT_PADDING:
418                  proto_tree_add_text( osi_option_tree, tvb, offset, parm_len,
419                   "Padding  : %u Octets", parm_len ) ;
420           break;
421           case   OSI_OPT_SOURCE_ROUTING:
422           case   OSI_OPT_RECORD_OF_ROUTE:
423                  dissect_option_route( parm_type,
424                                        offset, parm_len, tvb, osi_option_tree );
425           break;
426           case   OSI_OPT_REASON_OF_DISCARD:
427                  dissect_option_rfd( tvb_get_guint8(tvb, offset),
428                                      tvb_get_guint8(tvb, offset + 1),
429                                      offset, parm_len, tvb, osi_option_tree );
430           break;
431         }
432         opt_len -= parm_len + 2;
433         offset  += parm_len;
434       }
435    } 
436 } /* dissect-osi-options */
437
438
439 /*
440  * Name: proto_register_osi_options()
441  *
442  * Description:
443  *      main register for esis protocol set.  We register some display
444  *      formats and the protocol module variables.
445  *
446  *      NOTE: this procedure to autolinked by the makefile process that
447  *      builds register.c
448  *
449  * Input:
450  *      void
451  *
452  * Output:
453  *      void
454  */
455
456 void
457 proto_register_osi_options(void) {
458   static gint *ott[] = {
459     &ott_osi_options,
460     &ott_osi_qos,
461     &ott_osi_route,
462     &ott_osi_redirect,
463   };
464   proto_register_subtree_array( ott, array_length(ott));
465 }