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