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