Support for the new restart TLV, from Hannes Gredler.
[obnox/wireshark/wip.git] / packet-wtp.c
index 9640dd76d814a9ed4da565b5c758da21fb76bcdd..99f796d2f3778204843bae615cb54d842ef6913a 100644 (file)
@@ -2,11 +2,11 @@
  *
  * Routines to dissect WTP component of WAP traffic.
  * 
- * $Id: packet-wtp.c,v 1.19 2001/09/11 14:36:31 guy Exp $
+ * $Id: packet-wtp.c,v 1.26 2002/01/21 07:36:47 guy Exp $
  *
  * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@zing.org>
- * Copyright 1998 Didier Jorand
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 1998 Gerald Combs
  *
  * WAP dissector based on original work by Ben Fowler
  * Updated by Neil Hunter <neil.hunter@energis-squared.com>
@@ -53,7 +53,8 @@
 
 #include <string.h>
 #include <glib.h>
-#include "packet.h"
+#include <epan/packet.h>
+#include "reassemble.h"
 #include "packet-wap.h"
 #include "packet-wtp.h"
 #include "packet-wsp.h"
@@ -144,15 +145,16 @@ static const value_string vals_transaction_classes[] = {
 };
 
 /* File scoped variables for the protocol and registered fields */
-static int proto_wtp                                                   = HF_EMPTY;
+static int proto_wtp                                   = HF_EMPTY;
 
 /* These fields used by fixed part of header */
 static int hf_wtp_header_fixed_part                    = HF_EMPTY;
+static int hf_wtp_header_sub_pdu_size                  = HF_EMPTY;
 static int hf_wtp_header_flag_continue                         = HF_EMPTY;
-static int hf_wtp_header_pdu_type                              = HF_EMPTY;
+static int hf_wtp_header_pdu_type                      = HF_EMPTY;
 static int hf_wtp_header_flag_Trailer                  = HF_EMPTY;
-static int hf_wtp_header_flag_RID                              = HF_EMPTY;
-static int hf_wtp_header_flag_TID                              = HF_EMPTY;
+static int hf_wtp_header_flag_RID                      = HF_EMPTY;
+static int hf_wtp_header_flag_TID                      = HF_EMPTY;
 static int hf_wtp_header_flag_TID_response             = HF_EMPTY;
 
 /* These fields used by Invoke packets */
@@ -160,39 +162,65 @@ static int hf_wtp_header_Inv_version                      = HF_EMPTY;
 static int hf_wtp_header_Inv_flag_TIDNew               = HF_EMPTY;
 static int hf_wtp_header_Inv_flag_UP                   = HF_EMPTY;
 static int hf_wtp_header_Inv_Reserved                  = HF_EMPTY;
-static int hf_wtp_header_Inv_TransactionClass  = HF_EMPTY;
+static int hf_wtp_header_Inv_TransactionClass          = HF_EMPTY;
 
 
 static int hf_wtp_header_variable_part                         = HF_EMPTY;
-static int hf_wtp_data                                                         = HF_EMPTY;
+static int hf_wtp_data                                         = HF_EMPTY;
 
 static int hf_wtp_header_Ack_flag_TVETOK               = HF_EMPTY;
-static int hf_wtp_header_Abort_type                            = HF_EMPTY;
-static int hf_wtp_header_Abort_reason_provider = HF_EMPTY;
+static int hf_wtp_header_Abort_type                    = HF_EMPTY;
+static int hf_wtp_header_Abort_reason_provider         = HF_EMPTY;
 static int hf_wtp_header_Abort_reason_user             = HF_EMPTY;
 static int hf_wtp_header_sequence_number               = HF_EMPTY;
 static int hf_wtp_header_missing_packets               = HF_EMPTY;
 
 /* Initialize the subtree pointers */
-static gint ett_wtp                                                    = ETT_EMPTY;
-static gint ett_header                                                         = ETT_EMPTY;
+static gint ett_wtp                                    = ETT_EMPTY;
+static gint ett_header                                         = ETT_EMPTY;
 
 /* Handle for WSP dissector */
 static dissector_handle_t wsp_handle;
 
-/* Declarations */
-static char transaction_class(unsigned char octet);
-static char pdu_type(unsigned char octet);
-static char retransmission_indicator(unsigned char octet);
+/*
+ * reassembly of WSP
+ */
+static GHashTable      *wtp_fragment_table = NULL;
+
+static void
+wtp_defragment_init(void)
+{
+       fragment_table_init(&wtp_fragment_table);
+}
+
+/*
+ * Extract some bitfields
+ */
+#define pdu_type(octet)                        (((octet) >> 3) & 0x0F) /* Note pdu type must not be 0x00 */
+#define transaction_class(octet)       ((octet) & 0x03)        /* ......XX */
+#define transmission_trailer(octet)    (((octet) >> 1) & 0x01) /* ......X. */
+
+static char retransmission_indicator(unsigned char octet)
+{
+       switch ( pdu_type(octet) ) {
+               case INVOKE:
+               case RESULT:
+               case ACK:
+               case SEGMENTED_INVOKE:
+               case SEGMENTED_RESULT:
+               case NEGATIVE_ACK:
+                       return octet & 0x01;    /* .......X */
+               default:
+                       return 0;
+       }
+}
 
 /* Code to actually dissect the packets */
 static void
 dissect_wtp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
-       frame_data *fdata = pinfo->fd;
-
-       char szInfo[ 50 ];
-       int offCur                      = 0; /* current offset from start of WTP data */
+       char            szInfo[ 50 ];
+       int             offCur          = 0; /* current offset from start of WTP data */
 
        /* bytes at offset 0 - 3 */
        /*
@@ -202,59 +230,102 @@ dissect_wtp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        unsigned char  b0;
 
        /* continuation flag */
-       unsigned char   fCon;
-       unsigned char   fRID;
+       unsigned char   fCon;                   /* Continue flag        */
+       unsigned char   fRID;                   /* Re-transmission indicator    */
+       unsigned char   fTTR = '\0';            /* Transmission trailer */
        guint           cbHeader        = 0;
        guint           vHeader         = 0;
        int             abortType       = 0;
 
 /* Set up structures we will need to add the protocol subtree and manage it */
-       proto_item *ti;
-       proto_tree *wtp_tree;
-       proto_tree *wtp_header_fixed;
+       proto_item      *ti;
+       proto_tree      *wtp_tree = NULL;
+       proto_tree      *wtp_header_fixed;
        
-       char pdut;
-       char clsTransaction             = ' ';
-       int cchInfo;
-       int numMissing = 0;             /* Number of missing packets in a negative ack */
-       int i;
-       tvbuff_t *wsp_tvb = NULL;
+       char            pdut;
+       char            clsTransaction  = ' ';
+       int             cchInfo;
+       int             numMissing = 0;         /* Number of missing packets in a negative ack */
+       int             i;
+       tvbuff_t        *wsp_tvb = NULL;
+       fragment_data   *fd_head = NULL;
+       guint8          psn = 0;                /* Packet sequence number       */
+       guint16         TID = 0;                /* Transaction-Id       */
 
-#ifdef DEBUG
-       fprintf( stderr, "dissect_wtp: (Entering) Frame data at %p\n", fdata ); 
-       fprintf( stderr, "dissect_wtp: tvb length is %d\n", tvb_reported_length( tvb ) ); 
-#endif
-       if (check_col(fdata, COL_INFO)) {
-               col_clear(fdata, COL_INFO);
-       };
-    
        b0 = tvb_get_guint8 (tvb, offCur + 0);
+       /* Discover Concatenated PDUs */
+       if (b0 == 0) {
+               if (tree) {
+                       wtp_tree = proto_tree_add_item(tree, proto_wtp, tvb, offCur, 1, bo_little_endian);
+               }
+               offCur = 1;
+               i = 1;
+               while (offCur < (int) tvb_reported_length (tvb)) {
+                       b0 = tvb_get_guint8 (tvb, offCur + 0);
+                       if (b0 & 0x80) {
+                               vHeader = 2;
+                               cbHeader = ((b0 & 0x7f) << 8) |
+                                           tvb_get_guint8 (tvb, offCur + 1);
+                       } else {
+                               vHeader = 1;
+                               cbHeader = b0;
+                       }
+                       if (tree) {
+                               proto_tree_add_item(wtp_tree, hf_wtp_header_sub_pdu_size, tvb, offCur, vHeader, bo_big_endian);
+                       }
+                       if (i > 1 && check_col(pinfo->cinfo, COL_INFO)) {
+                               col_append_str (pinfo->cinfo, COL_INFO, ", ");
+                       }
+                       wsp_tvb = tvb_new_subset(tvb, offCur + vHeader, -1, cbHeader);
+                       dissect_wtp_common (wsp_tvb, pinfo, wtp_tree);
+                       offCur += vHeader + cbHeader;
+                       i++;
+               }
+               return;
+       }
        fCon = b0 & 0x80;
        fRID = retransmission_indicator( b0 );
        pdut = pdu_type( b0 );
 
        /* Develop the string to put in the Info column */
        cchInfo = snprintf( szInfo, sizeof( szInfo ), "WTP %s",
-           val_to_str(pdut, vals_pdu_type, "Unknown PDU type 0x%x"));
+                   val_to_str(pdut, vals_pdu_type, "Unknown PDU type 0x%x"));
 
        switch( pdut ) {
                case INVOKE:
+                       fTTR = transmission_trailer( b0 );
+                       TID = tvb_get_ntohs(tvb, offCur + 1);
+                       psn = 0;
                        clsTransaction = transaction_class( tvb_get_guint8 (tvb, offCur + 3) );
                        snprintf( szInfo + cchInfo, sizeof( szInfo ) - cchInfo, " Class %d", clsTransaction  );
+                       cbHeader = 4;
+                       break;
 
-               case ABORT:
                case SEGMENTED_INVOKE:
                case SEGMENTED_RESULT:
+                       fTTR = transmission_trailer( b0 );
+                       TID = tvb_get_ntohs(tvb, offCur + 1);
+                       psn = tvb_get_guint8(tvb, offCur + 3);
+                       cbHeader = 4;
+                       break;
+
+               case ABORT:
                        cbHeader = 4;
                        break;
 
                case RESULT:
+                       fTTR = transmission_trailer( b0 );
+                       TID = tvb_get_ntohs(tvb, offCur + 1);
+                       psn = 0;
+                       cbHeader = 3;
+                       break;
+
                case ACK:
                        cbHeader = 3;
                        break;
 
                case NEGATIVE_ACK:
-                       /* Varible number of missing packets */
+                       /* Variable number of missing packets */
                        numMissing = tvb_get_guint8 (tvb, offCur + 3);
                        cbHeader = numMissing + 4;
                        break;
@@ -266,7 +337,7 @@ dissect_wtp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        if( fRID ) {
                strcat( szInfo, " R" );
        };
-       if( fCon ) {
+       if( fCon ) {                    /* Scan variable part (TPI's)   */
                unsigned char   tCon;
                unsigned char   tByte;
 
@@ -285,17 +356,18 @@ dissect_wtp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        fprintf( stderr, "dissect_wtp: cbHeader = %d\n", cbHeader );  
 #endif
 
-/* This field shows up as the "Info" column in the display; you should make
-   it, if possible, summarize what's in the packet, so that a user looking
-   at the list of packets can tell what type of packet it is. */
-       if (check_col(fdata, COL_INFO)) {
+       /* This field shows up as the "Info" column in the display; you should make
+          it, if possible, summarize what's in the packet, so that a user looking
+          at the list of packets can tell what type of packet it is. */
+       if (check_col(pinfo->cinfo, COL_INFO) &&
+           (tvb_length_remaining(tvb, offCur + cbHeader + vHeader) <= 0)) {
 #ifdef DEBUG
                fprintf( stderr, "dissect_wtp: (6) About to set info_col header to %s\n", szInfo );
 #endif
-               col_add_str(fdata, COL_INFO, szInfo );
+               col_append_str(pinfo->cinfo, COL_INFO, szInfo );
        };
-/* In the interest of speed, if "tree" is NULL, don't do any work not
-   necessary to generate protocol tree items. */
+       /* In the interest of speed, if "tree" is NULL, don't do any work not
+          necessary to generate protocol tree items. */
        if (tree) {
 #ifdef DEBUG
                fprintf( stderr, "dissect_wtp: cbHeader = %d\n", cbHeader );  
@@ -316,10 +388,7 @@ dissect_wtp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                        ti = proto_tree_add_item( wtp_tree, hf_wtp_header_fixed_part, tvb, offCur, cbHeader, bo_little_endian );
                        fprintf( stderr, "dissect_wtp: (6) Returned from proto_tree_add_item\n" );  
 #endif
-                       wtp_header_fixed = proto_item_add_subtree(
-                                       ti, 
-                                       ett_header 
-                               );
+                       wtp_header_fixed = proto_item_add_subtree(ti, ett_header);
 
                        /* Add common items: only CON and PDU Type */
                        ti = proto_tree_add_item(
@@ -421,42 +490,73 @@ dissect_wtp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                                                offCur + cbHeader,              /* start */
                                                vHeader,                        /* length */
                                                "What should go here!",         /* value */
-                                               "Header (Variable part) %02X %02X %02X %02X"  ,                 /* format */
+                                               "Header (Variable part) %02X %02X %02X %02X",   /* format */
                                                0, 1, 2, 3
                                    );
                        } else {
                                /* There is no variable part */
-                       }; /* End of variable part of header */
+                       }       /* End of variable part of header */
                }
        } else {
 #ifdef DEBUG
                fprintf( stderr, "dissect_wtp: (4) tree was %p\n", tree ); 
 #endif
        }
-
-       /* Any remaining data ought to be WSP data,
-        * so hand off to the WSP dissector */
-       if (tvb_reported_length (tvb) > cbHeader + vHeader)
+       /*
+        * Any remaining data ought to be WSP data,
+        * so hand off (defragmented) to the WSP dissector
+        */
+       if (tvb_length_remaining(tvb, offCur + cbHeader + vHeader) > 0)
        {
-               wsp_tvb = tvb_new_subset(tvb, cbHeader + vHeader, -1,
-                       tvb_reported_length (tvb)-cbHeader-vHeader);
-               call_dissector(wsp_handle, wsp_tvb, pinfo, tree);
-       }
+               int     dataOffset = offCur + cbHeader + vHeader;
+               guint32 dataLen = tvb_length_remaining(tvb, offCur + cbHeader + vHeader);
+               gboolean save_fragmented;
 
-#ifdef DEBUG
-       fprintf( stderr, "dissect_wtp: (leaving) fdata->cinfo is %p\n", fdata->cinfo ); 
-#endif
+               if ((pdut == SEGMENTED_INVOKE) || (pdut == SEGMENTED_RESULT) ||
+                   (((pdut == INVOKE) || (pdut == RESULT)) && (!fTTR)))        /* 1st part of segment  */
+               {
+                       save_fragmented = pinfo->fragmented;
+                       pinfo->fragmented = TRUE;
+                       fd_head = fragment_add_seq(tvb, dataOffset, pinfo, TID,
+                                       wtp_fragment_table, psn, dataLen, !fTTR);
+                       if (fd_head != NULL)            /* Reassembled  */
+                       {
+                               wsp_tvb = tvb_new_real_data(fd_head->data,
+                                                       fd_head->len,
+                                                       fd_head->len,
+                                                       "Reassembled");
+                               tvb_set_child_real_data_tvbuff(tvb, wsp_tvb);
+                               pinfo->fd->data_src = g_slist_append(pinfo->fd->data_src, wsp_tvb);
+                               pinfo->fragmented = FALSE;
+                               call_dissector(wsp_handle, wsp_tvb, pinfo, tree);
+                       }
+                       else
+                       {
+                               if (check_col(pinfo->cinfo, COL_INFO))          /* Won't call WSP so display */
+                                       col_append_str(pinfo->cinfo, COL_INFO, szInfo );
+                       }
+                       pinfo->fragmented = save_fragmented;
+               }
+               else                            /* Normal packet, call next dissector   */
+               {
+                       wsp_tvb = tvb_new_subset(tvb, dataOffset, -1, dataLen);
+                       call_dissector(wsp_handle, wsp_tvb, pinfo, tree);
+               }
+       }
 }
 
 /*
  * Called directly from UDP.
  * Put "WTP+WSP" into the "Protocol" column.
  */
-void
+static void
 dissect_wtp_fromudp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
-       if (check_col(pinfo->fd, COL_PROTOCOL))
-               col_set_str(pinfo->fd, COL_PROTOCOL, "WTP+WSP" );
+       if (check_col(pinfo->cinfo, COL_PROTOCOL))
+               col_set_str(pinfo->cinfo, COL_PROTOCOL, "WTP+WSP" );
+       if (check_col(pinfo->cinfo, COL_INFO)) {
+               col_clear(pinfo->cinfo, COL_INFO);
+       }
 
        dissect_wtp_common(tvb, pinfo, tree);
 }
@@ -473,40 +573,15 @@ dissect_wtp_fromudp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 static void
 dissect_wtp_fromwap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
-       if (check_col(pinfo->fd, COL_PROTOCOL))
-               col_set_str(pinfo->fd, COL_PROTOCOL, "WTLS+WTP+WSP" );
+       if (check_col(pinfo->cinfo, COL_PROTOCOL))
+               col_set_str(pinfo->cinfo, COL_PROTOCOL, "WTLS+WTP+WSP" );
+       if (check_col(pinfo->cinfo, COL_INFO)) {
+               col_clear(pinfo->cinfo, COL_INFO);
+       }
 
        dissect_wtp_common(tvb, pinfo, tree);
 }
 
-static char pdu_type(unsigned char octet)
-{
-       char ch = (octet >> 3) & 0x0F;
-       /* Note pdu type must not be 0x00 */
-       return ch;
-};
-
-static char transaction_class(unsigned char octet)
-{
-       char ch = (octet >> 0) & 0x03; /* ......XX */
-       return ch;
-};
-
-static char retransmission_indicator(unsigned char octet)
-{
-       switch ( pdu_type(octet) ) {
-               case INVOKE:
-               case RESULT:
-               case ACK:
-               case SEGMENTED_INVOKE:
-               case SEGMENTED_RESULT:
-               case NEGATIVE_ACK:
-                       return (octet >> 0) & 0x01; /* ......,X */
-               default:
-                       return 0;
-       }
-};
-
 /* Register the protocol with Ethereal */
 void
 proto_register_wtp(void)
@@ -521,6 +596,13 @@ proto_register_wtp(void)
                                "Fixed part of the header", HFILL
                        }
                },
+               { &hf_wtp_header_sub_pdu_size,
+                       {       "Sub PDU size",           
+                               "wtp.sub_pdu_size",
+                               FT_BYTES, BASE_HEX, NULL, 0x0,
+                               "Size of Sub-PDU", HFILL
+                       }
+               },
                { &hf_wtp_header_flag_continue,
                        {       "Continue Flag",           
                                "wtp.continue_flag",
@@ -677,17 +759,21 @@ proto_register_wtp(void)
        proto_register_subtree_array(ett, array_length(ett));
 
        register_dissector("wtp", dissect_wtp_fromwap, proto_wtp);
+       register_dissector("wtp-udp", dissect_wtp_fromudp, proto_wtp);
+       register_init_routine(wtp_defragment_init);
 };
 
 void
 proto_reg_handoff_wtp(void)
 {
+       dissector_handle_t wtp_fromudp_handle;
+
        /*
         * Get a handle for the connection-oriented WSP dissector - if WTP
         * PDUs have data, it is WSP.
         */
        wsp_handle = find_dissector("wsp-co");
 
-       dissector_add("udp.port", UDP_PORT_WTP_WSP, dissect_wtp_fromudp,
-           proto_wtp);
+       wtp_fromudp_handle = find_dissector("wtp-udp");
+       dissector_add("udp.port", UDP_PORT_WTP_WSP, wtp_fromudp_handle);
 }