diff options
Diffstat (limited to 'packet-vj.c')
-rw-r--r-- | packet-vj.c | 762 |
1 files changed, 508 insertions, 254 deletions
diff --git a/packet-vj.c b/packet-vj.c index fff8e31287..1c6cec5eae 100644 --- a/packet-vj.c +++ b/packet-vj.c @@ -1,7 +1,7 @@ /* packet-vj.c * Routines for Van Jacobson header decompression. * - * $Id: packet-vj.c,v 1.8 2002/05/18 21:19:48 guy Exp $ + * $Id: packet-vj.c,v 1.9 2002/05/20 00:56:30 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -10,7 +10,7 @@ * Copyright (c) 2001 by QUALCOMM, Incorporated. * All Rights reserved. * - * Routines to compress and uncompress tcp packets (for transmission + * Routines to compress and uncompress TCP packets (for transmission * over low speed serial lines). * * Copyright (c) 1989 Regents of the University of California. @@ -72,34 +72,33 @@ #include <epan/packet.h> #include "packet-ppp.h" #include "ppptypes.h" +#include "ipproto.h" #include "in_cksum.h" -#include "epan/tvbuff.h" /* Define relevant IP/TCP parameters */ +#define IP_FIELD_TOT_LEN 2 /* Total length field in IP hdr */ +#define IP_FIELD_PROTOCOL 9 /* Protocol field byte in IP hdr */ +#define IP_ADDR_SIZE 4 /* Size in bytes of IPv4 address */ #define IP_FIELD_SRC 12 /* Byte 12 in IP hdr - src address */ #define IP_FIELD_DST 16 /* Byte 16 in IP hdr - dst address */ -#define IP_ADDR_SIZE 4 /* Size in bytes of IPv4 address */ -#define IP_FIELD_PROTOCOL 9 /* Protocol field byte in IP hdr */ -#define IP_PROTOCOL_TCP 0x06 /* Protocol field value for TCP */ #define IP_HDR_LEN 20 /* Minimum IP header length */ #define IP_HDR_LEN_MASK 0x0f /* Mask for header length field */ #define IP_MAX_OPT_LEN 44 /* Max length of IP options */ #define TCP_HDR_LEN 20 /* Minimum TCP header length */ #define TCP_MAX_OPT_LEN 44 /* Max length of TCP options */ -#define TCP_SIMUL_CONV 256 /* Number of simul. TCP conversations */ #define TCP_SIMUL_CONV_MAX 256 /* Max number of simul. TCP conversations */ -#define CHANGE_PUSH_BIT 0x10 /* TCP push bit changed */ #define TCP_PUSH_BIT 0x08 /* TCP push bit */ #define TCP_URG_BIT 0x20 /* TCP urgent bit */ /* Bits in first octet of compressed packet */ /* flag bits for what changed in a packet */ -#define NEW_C 0x40 -#define NEW_I 0x20 -#define NEW_S 0x08 -#define NEW_A 0x04 -#define NEW_W 0x02 -#define NEW_U 0x01 +#define NEW_C 0x40 /* Connection number changed */ +#define NEW_I 0x20 /* IP sequence number change by value != 1 */ +#define CHANGE_PUSH_BIT 0x10 /* TCP push bit set */ +#define NEW_S 0x08 /* Sequence number changed */ +#define NEW_A 0x04 /* Ack sequence number changed */ +#define NEW_W 0x02 /* Window changed */ +#define NEW_U 0x01 /* Urgent pointer present */ /* reserved, special-case values of above */ #define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */ @@ -113,9 +112,6 @@ /* Define for 0 */ #define ZERO 0 -/* Two byte CRC */ -#define CRC_LEN sizeof(guint16) - /* VJ Mem Chunk defines */ #define VJ_DATA_SIZE 128 /* Max IP hdr(64)+Max TCP hdr(64) */ #define VJ_ATOM_COUNT 250 /* Number of Atoms per block */ @@ -132,7 +128,7 @@ typedef struct { guint16 cksum; guint32 src; guint32 dst; -} iphdr_type; +} iphdr_type; typedef struct { guint16 srcport; @@ -169,6 +165,25 @@ typedef struct { /* Initialize the protocol and registered fields */ static int proto_vj = -1; +static int hf_vj_change_mask = -1; +static int hf_vj_change_mask_c = -1; +static int hf_vj_change_mask_i = -1; +static int hf_vj_change_mask_p = -1; +static int hf_vj_change_mask_s = -1; +static int hf_vj_change_mask_a = -1; +static int hf_vj_change_mask_w = -1; +static int hf_vj_change_mask_u = -1; +static int hf_vj_connection_number = -1; +static int hf_vj_tcp_cksum = -1; +static int hf_vj_urp = -1; +static int hf_vj_win_delta = -1; +static int hf_vj_ack_delta = -1; +static int hf_vj_seq_delta = -1; +static int hf_vj_ip_id_delta = -1; + +static gint ett_vj = -1; +static gint ett_vj_changes = -1; + /* Protocol handles */ static dissector_handle_t ip_handle; static dissector_handle_t data_handle; @@ -180,113 +195,215 @@ static slcompress *rx_tx_state[RX_TX_STATE_COUNT] = {NULL, NULL}; /* Mem Chunks for storing decompressed headers */ static GMemChunk *vj_header_memchunk = NULL; typedef struct { - guint32 offset; + int offset; /* uppermost bit is "can't dissect" flag */ guint8 data[VJ_DATA_SIZE]; } vj_header_t; /* Function prototypes */ -static void decodes(tvbuff_t *tvb, guint32 *offset, gint16 *val); -static void decodel(tvbuff_t *tvb, guint32 *offset, gint32 *val); +static int get_unsigned_delta(tvbuff_t *tvb, int *offsetp, int hf, + proto_tree *tree); +static int get_signed_delta(tvbuff_t *tvb, int *offsetp, int hf, + proto_tree *tree); static guint16 ip_csum(const guint8 *ptr, guint32 len); -static slcompress *slhc_init(gint rslots); +static slcompress *slhc_init(void); static void vj_init(void); -static gint vjuc_check(tvbuff_t *tvb, slcompress *comp); +static gint vjuc_check(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + slcompress *comp); static void vjuc_update_state(tvbuff_t *tvb, slcompress *comp, guint8 index); static gint vjuc_tvb_setup(tvbuff_t *tvb, tvbuff_t **dst_tvb, - slcompress *comp, frame_data *fd); -static gint vjc_check(tvbuff_t *src_tvb, slcompress *comp); -static gint vjc_update_state(tvbuff_t *src_tvb, slcompress *comp, - frame_data *fd); -static gint vjc_tvb_setup(tvbuff_t *src_tvb, tvbuff_t **dst_tvb, - frame_data *fd); + packet_info *pinfo, slcompress *comp); +static gint vjc_process(tvbuff_t *src_tvb, packet_info *pinfo, proto_tree *tree, + slcompress *comp); +static gint vjc_tvb_setup(tvbuff_t *src_tvb, tvbuff_t **dst_tvb, + packet_info *pinfo); /* Dissector for VJ Uncompressed packets */ static void dissect_vjuc(tvbuff_t *tvb, packet_info *pinfo, proto_tree * tree) { + proto_item *ti; + proto_tree *vj_tree = NULL; + slcompress *comp; + gint conn_index; tvbuff_t *next_tvb = NULL; - slcompress *comp = NULL; - gint conn_index = ZERO; - gint err = VJ_OK; - /* - * XXX - add error information to the Info column? - */ - if(check_col(pinfo->cinfo, COL_INFO)) - col_set_str(pinfo->cinfo, COL_INFO, "VJ uncompressed TCP"); + if(check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_INFO, "PPP VJ"); + + if(tree != NULL) { + ti = proto_tree_add_protocol_format(tree, proto_vj, tvb, 0, -1, + "PPP VJ Compression: Uncompressed data"); + vj_tree = proto_item_add_subtree(ti, ett_vj); + } + + if(!ppp_vj_decomp) { + /* VJ decompression turned off */ + if(check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "VJ uncompressed TCP (decompression disabled)"); + if(tree != NULL) + call_dissector(data_handle, tvb, pinfo, vj_tree); + return; + } - /* Return if VJ is off or direction is not known */ - if(ppp_vj_decomp == FALSE || pinfo->p2p_dir == P2P_DIR_UNKNOWN) - err = VJ_ERROR; + if(pinfo->p2p_dir == P2P_DIR_UNKNOWN) { + /* Direction of the traffic unknown - can't decompress */ + if(check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "VJ uncompressed TCP (direction unknown)"); + if(tree != NULL) + call_dissector(data_handle, tvb, pinfo, vj_tree); + return; + } - if((comp = rx_tx_state[pinfo->p2p_dir]) == NULL) - err = VJ_ERROR; + if((comp = rx_tx_state[pinfo->p2p_dir]) == NULL) { + /* No state information found - can't decompress */ + if(check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "VJ uncompressed TCP (no state information)"); + if(tree != NULL) + call_dissector(data_handle, tvb, pinfo, vj_tree); + return; + } /* Check if packet malformed. */ - if(err == VJ_OK) - err = conn_index = vjuc_check(tvb, comp); + conn_index = vjuc_check(tvb, pinfo, vj_tree, comp); + if(conn_index == VJ_ERROR) + return; /* Set up tvb containing decompressed packet */ - if(err != VJ_ERROR) - err = vjuc_tvb_setup(tvb, &next_tvb, comp, pinfo->fd); + if(vjuc_tvb_setup(tvb, &next_tvb, pinfo, comp) == VJ_ERROR) + return; - /* If packet seen for first time update state */ - if(pinfo->fd->flags.visited != 1 && err == VJ_OK) + /* + * No errors, so: + * + * if packet seen for first time update state; + * call IP dissector. + */ + if(!pinfo->fd->flags.visited) vjuc_update_state(next_tvb, comp, conn_index); - - /* If no errors call IP dissector else dissect as data. */ - if(err == VJ_OK) - call_dissector(ip_handle, next_tvb, pinfo, tree); - else - call_dissector(data_handle, tvb, pinfo, tree); + call_dissector(ip_handle, next_tvb, pinfo, tree); } /* Dissector for VJ Compressed packets */ static void dissect_vjc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { + proto_item *ti; + proto_tree *vj_tree = NULL; tvbuff_t *next_tvb = NULL; slcompress *comp = NULL; - gint err = VJ_OK; + gint err = VJ_ERROR; - /* - * XXX - add error information to the Info column? - */ - if(check_col(pinfo->cinfo, COL_INFO)) - col_set_str(pinfo->cinfo, COL_INFO, "VJ compressed TCP"); + if(check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_INFO, "PPP VJ"); - /* Return if VJ is off or direction is not known */ - if(ppp_vj_decomp == FALSE || pinfo->p2p_dir == P2P_DIR_UNKNOWN) - err = VJ_ERROR; - - if((comp = rx_tx_state[pinfo->p2p_dir]) == NULL) - err = VJ_ERROR; + if(tree != NULL) { + ti = proto_tree_add_protocol_format(tree, proto_vj, tvb, 0, -1, + "PPP VJ Compression: Compressed data"); + vj_tree = proto_item_add_subtree(ti, ett_vj); + } - /* Check if packet malformed. */ - if(err != VJ_ERROR) - err = vjc_check(tvb, comp); + if(!ppp_vj_decomp) { + /* VJ decompression turned off */ + if(check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "VJ compressed TCP (decompression disabled)"); + if(tree != NULL) + call_dissector(data_handle, tvb, pinfo, vj_tree); + return; + } - /* If packet seen for first time update state */ - if(pinfo->fd->flags.visited != 1 && err == VJ_OK) { - err = vjc_update_state(tvb, comp, pinfo->fd); + if(pinfo->p2p_dir == P2P_DIR_UNKNOWN) { + /* Direction of the traffic unknown - can't decompress */ + if(check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "VJ compressed TCP (direction unknown)"); + if(tree != NULL) + call_dissector(data_handle, tvb, pinfo, vj_tree); + return; } - /* Set up tvb containing decompressed packet */ - if(err == VJ_OK) - err = vjc_tvb_setup(tvb, &next_tvb, pinfo->fd); + if((comp = rx_tx_state[pinfo->p2p_dir]) == NULL) { + /* No state information found - can't decompress */ + if(check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "VJ compressed TCP (no state information)"); + if(tree != NULL) + call_dissector(data_handle, tvb, pinfo, vj_tree); + return; + } - /* If no errors call IP dissector else dissect as data */ - if(err == VJ_OK) - call_dissector(ip_handle, next_tvb, pinfo, tree); - else - call_dissector(data_handle, tvb, pinfo, tree); + /* Process the compressed data header */ + if(vjc_process(tvb, pinfo, vj_tree, comp) == VJ_ERROR) + return; + + /* Not malformed - set up tvb containing decompressed packet */ + err = vjc_tvb_setup(tvb, &next_tvb, pinfo); + if(err == VJ_ERROR) { + if(tree != NULL) + call_dissector(data_handle, tvb, pinfo, vj_tree); + return; + } + + /* No errors, so call IP dissector */ + call_dissector(ip_handle, next_tvb, pinfo, tree); } -/* Registeration functions for dissectors */ +/* Registration functions for dissectors */ void proto_register_vj(void) { + static hf_register_info hf[] = { + { &hf_vj_change_mask, + { "Change mask", "vj.change_mask", FT_UINT8, BASE_HEX, + NULL, 0x0, "", HFILL }}, + { &hf_vj_change_mask_c, + { "Connection changed", "vj.change_mask_c", FT_BOOLEAN, 8, + NULL, NEW_C, "Connection number changed", HFILL }}, + { &hf_vj_change_mask_i, + { "IP ID change != 1", "vj.change_mask_i", FT_BOOLEAN, 8, + NULL, NEW_I, "IP ID changed by a value other than 1", HFILL }}, + { &hf_vj_change_mask_p, + { "Push bit set", "vj.change_mask_p", FT_BOOLEAN, 8, + NULL, CHANGE_PUSH_BIT, "TCP PSH flag set", HFILL }}, + { &hf_vj_change_mask_s, + { "Sequence number changed", "vj.change_mask_s", FT_BOOLEAN, 8, + NULL, NEW_S, "Sequence number changed", HFILL }}, + { &hf_vj_change_mask_a, + { "Ack number changed", "vj.change_mask_a", FT_BOOLEAN, 8, + NULL, NEW_A, "Acknowledgement sequence number changed", HFILL }}, + { &hf_vj_change_mask_w, + { "Window changed", "vj.change_mask_w", FT_BOOLEAN, 8, + NULL, NEW_W, "TCP window changed", HFILL }}, + { &hf_vj_change_mask_u, + { "Urgent pointer set", "vj.change_mask_u", FT_BOOLEAN, 8, + NULL, NEW_U, "Urgent pointer set", HFILL }}, + { &hf_vj_connection_number, + { "Connection number", "vj.connection_number", FT_UINT8, BASE_DEC, + NULL, 0x0, "Connection number", HFILL }}, + { &hf_vj_tcp_cksum, + { "TCP checksum", "vj.tcp_cksum", FT_UINT16, BASE_HEX, + NULL, 0x0, "TCP checksum", HFILL }}, + { &hf_vj_urp, + { "Urgent pointer", "vj.urp", FT_UINT16, BASE_DEC, + NULL, 0x0, "Urgent pointer", HFILL }}, + { &hf_vj_win_delta, + { "Window delta", "vj.win_delta", FT_INT16, BASE_DEC, + NULL, 0x0, "Delta for window", HFILL }}, + { &hf_vj_ack_delta, + { "Ack delta", "vj.ack_delta", FT_UINT16, BASE_DEC, + NULL, 0x0, "Delta for acknowledgment sequence number", HFILL }}, + { &hf_vj_seq_delta, + { "Sequence delta", "vj.seq_delta", FT_UINT16, BASE_DEC, + NULL, 0x0, "Delta for sequence number", HFILL }}, + { &hf_vj_ip_id_delta, + { "IP ID delta", "vj.ip_id_delta", FT_UINT16, BASE_DEC, + NULL, 0x0, "Delta for IP ID", HFILL }}, + }; + static gint *ett[] = { + &ett_vj, + &ett_vj_changes, + }; + proto_vj = proto_register_protocol("PPP VJ Compression", "PPP VJ", "vj"); + proto_register_field_array(proto_vj, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); register_init_routine(&vj_init); } @@ -325,30 +442,27 @@ vj_init(void) g_free(pstate); g_free(pslc); } - rx_tx_state[i] = slhc_init(TCP_SIMUL_CONV); + rx_tx_state[i] = slhc_init(); } return; } /* Initialization routine for VJ decompression */ static slcompress * -slhc_init(gint rslots) +slhc_init(void) { - size_t rsize = rslots * sizeof(cstate); + size_t rsize = TCP_SIMUL_CONV_MAX * sizeof(cstate); slcompress *comp = g_malloc(sizeof(slcompress)); - if(rslots < ZERO || rslots > TCP_SIMUL_CONV_MAX) - return NULL; - - if (comp != NULL) { + if(comp != NULL) { memset(comp, ZERO, sizeof(slcompress)); - if ((comp->rstate = g_malloc(rsize)) == NULL) { + if((comp->rstate = g_malloc(rsize)) == NULL) { g_free(comp); comp = NULL; } else { memset(comp->rstate, ZERO, rsize); - comp->rslot_limit = rslots - 1; + comp->rslot_limit = TCP_SIMUL_CONV_MAX - 1; comp->recv_current = TCP_SIMUL_CONV_MAX - 1; comp->flags |= SLF_TOSS; } @@ -360,222 +474,359 @@ slhc_init(gint rslots) static gint vjc_tvb_setup(tvbuff_t *src_tvb, tvbuff_t **dst_tvb, - frame_data * fd) + packet_info *pinfo) { vj_header_t *hdr_buf; + guint8 offset; guint8 *data_ptr; - guint8 *pbuf = NULL; - gint hdr_len = ZERO; - gint buf_len = ZERO; - guint8 offset = ZERO; + iphdr_type *ip; + tcphdr_type *thp; + gint hdr_len; + gint buf_len; + guint8 *pbuf; g_assert(src_tvb); /* Get decompressed header stored in fd protocol area */ - hdr_buf = p_get_proto_data(fd, proto_vj); - if(hdr_buf == NULL) + hdr_buf = p_get_proto_data(pinfo->fd, proto_vj); + if(hdr_buf == NULL) { + if(check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "VJ compressed TCP (decompressed data not available)"); return VJ_ERROR; + } + + if(check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "VJ compressed TCP"); /* Get the data offset in the tvbuff */ offset = hdr_buf->offset; /* Copy header and form tvb */ data_ptr = hdr_buf->data; - hdr_len = lo_nibble(((iphdr_type *)data_ptr)->ihl_version) * 4; - hdr_len += TCP_OFFSET(((tcphdr_type *)(data_ptr + hdr_len))) * 4; + ip = (iphdr_type *)data_ptr; + hdr_len = lo_nibble(ip->ihl_version) * 4; + thp = (tcphdr_type *)(data_ptr + hdr_len); + hdr_len += TCP_OFFSET(thp) * 4; buf_len = tvb_length(src_tvb) + hdr_len - offset; - pbuf = g_malloc(buf_len); + pbuf = g_malloc(buf_len); memcpy(pbuf, data_ptr, hdr_len); tvb_memcpy(src_tvb, pbuf + hdr_len, offset, buf_len - hdr_len); - *dst_tvb = tvb_new_real_data(pbuf, buf_len, buf_len); + *dst_tvb = tvb_new_real_data(pbuf, buf_len, ntohs(ip->tot_len)); tvb_set_child_real_data_tvbuff(src_tvb, *dst_tvb); - add_new_data_source(fd, *dst_tvb, "VJ Decompressed"); + add_new_data_source(pinfo->fd, *dst_tvb, "VJ Decompressed"); return VJ_OK; } -/* For VJ compressed packets update the decompressor state */ +/* + * For VJ compressed packet: + * + * check if it is malformed; + * dissect the relevant fields; + * update the decompressor state on the first pass. + */ static gint -vjc_update_state(tvbuff_t *src_tvb, slcompress *comp, frame_data *fd) +vjc_process(tvbuff_t *src_tvb, packet_info *pinfo, proto_tree *tree, + slcompress *comp) { + int offset = ZERO; + gint changes; + proto_item *ti; + proto_tree *changes_tree; + guint8 conn_index; + cstate *cs; + iphdr_type *ip; + tcphdr_type *thp; + guint16 tcp_cksum; + gint hdrlen = ZERO; + guint16 word; + int delta; + gint len; vj_header_t *buf_hdr; guint8 *data_ptr; - cstate *cs = &comp->rstate[comp->recv_current]; - tcphdr_type *thp = &cs->cs_tcp; - iphdr_type *ip = &cs->cs_ip; - gint changes = ZERO; - gint len = ZERO; - gint hdrlen = ZERO; - guint32 offset = ZERO; - guint16 word = ZERO; - g_assert(src_tvb); - g_assert(comp); - g_assert(fd); + if(tvb_length(src_tvb) < 3){ + if(check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "VJ compressed TCP (not enough data available)"); + if(tree != NULL) + call_dissector(data_handle, src_tvb, pinfo, tree); + comp->flags |= SLF_TOSS; + return VJ_ERROR; + } /* Read the change byte */ - changes = tvb_get_guint8(src_tvb, offset++); - if(changes & NEW_C) - offset++; + changes = tvb_get_guint8(src_tvb, offset); + if(tree != NULL) { + switch (changes & SPECIALS_MASK) { + + case SPECIAL_I: + ti = proto_tree_add_uint_format(tree, hf_vj_change_mask, src_tvb, + offset, 1, changes, + "Change mask: 0x%02x (echoed interactive traffic)", + changes); + break; + + case SPECIAL_D: + ti = proto_tree_add_uint_format(tree, hf_vj_change_mask, src_tvb, + offset, 1, changes, + "Change mask: 0x%02x (unidirectional data)", + changes); + break; + + default: + /* + * XXX - summarize bits? + */ + ti = proto_tree_add_uint_format(tree, hf_vj_change_mask, src_tvb, + offset, 1, changes, + "Change mask: 0x%02x", changes); + break; + } + changes_tree = proto_item_add_subtree(ti, ett_vj_changes); + proto_tree_add_boolean(changes_tree, hf_vj_change_mask_c, src_tvb, + offset, 1, changes); + proto_tree_add_boolean(changes_tree, hf_vj_change_mask_i, src_tvb, + offset, 1, changes); + proto_tree_add_boolean(changes_tree, hf_vj_change_mask_p, src_tvb, + offset, 1, changes); + proto_tree_add_boolean(changes_tree, hf_vj_change_mask_s, src_tvb, + offset, 1, changes); + proto_tree_add_boolean(changes_tree, hf_vj_change_mask_a, src_tvb, + offset, 1, changes); + proto_tree_add_boolean(changes_tree, hf_vj_change_mask_w, src_tvb, + offset, 1, changes); + proto_tree_add_boolean(changes_tree, hf_vj_change_mask_u, src_tvb, + offset, 1, changes); + } + offset++; + + if(changes & NEW_C){ /* Read conn index */ + conn_index = tvb_get_guint8(src_tvb, offset); + if(tree != NULL) + proto_tree_add_uint(tree, hf_vj_connection_number, src_tvb, offset, 1, + conn_index); + offset++; + if(conn_index > comp->rslot_limit) { + if(check_col(pinfo->cinfo, COL_INFO)) { + col_add_fstr(pinfo->cinfo, COL_INFO, "VJ compressed TCP (index (%u) < highest receive slot ID (%u))", + conn_index, comp->rslot_limit); + } + comp->flags |= SLF_TOSS; + return VJ_ERROR; + } + comp->flags &= ~SLF_TOSS; + comp->recv_current = conn_index; + } + else { + if(comp->flags & SLF_TOSS) { + if(check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "VJ compressed TCP (previous data bad)"); + return VJ_ERROR; + } + } + + cs = &comp->rstate[comp->recv_current]; + thp = &cs->cs_tcp; + ip = &cs->cs_ip; /* Build TCP and IP headers */ - hdrlen = lo_nibble(ip->ihl_version) * 4 + TCP_OFFSET(thp) * 4; - thp->cksum = htons(tvb_get_ntohs(src_tvb, offset)); + tcp_cksum = tvb_get_ntohs(src_tvb, offset); + if(tree != NULL) + proto_tree_add_uint(tree, hf_vj_tcp_cksum, src_tvb, offset, 2, tcp_cksum); + if(!pinfo->fd->flags.visited) { + hdrlen = lo_nibble(ip->ihl_version) * 4 + TCP_OFFSET(thp) * 4; + thp->cksum = htons(tcp_cksum); + } offset += 2; - if (changes & CHANGE_PUSH_BIT) - thp->flags |= TCP_PUSH_BIT; - else - thp->flags &= ~TCP_PUSH_BIT; + if(!pinfo->fd->flags.visited) { + if(changes & CHANGE_PUSH_BIT) + thp->flags |= TCP_PUSH_BIT; + else + thp->flags &= ~TCP_PUSH_BIT; + } /* Deal with special cases and normal deltas */ switch(changes & SPECIALS_MASK){ case SPECIAL_I: /* Echoed terminal traffic */ - word = ntohs(ip->tot_len) - hdrlen; - thp->ack_seq = htonl( ntohl(thp->ack_seq) + word); - thp->seq = htonl( ntohl(thp->seq) + word); - break; + if(!pinfo->fd->flags.visited) { + word = ntohs(ip->tot_len) - hdrlen; + thp->ack_seq = htonl(ntohl(thp->ack_seq) + word); + thp->seq = htonl(ntohl(thp->seq) + word); + } + break; case SPECIAL_D: /* Unidirectional data */ - thp->seq = htonl( ntohl(thp->seq) + ntohs(ip->tot_len) - hdrlen); - break; + if(!pinfo->fd->flags.visited) + thp->seq = htonl(ntohl(thp->seq) + ntohs(ip->tot_len) - hdrlen); + break; default: if(changes & NEW_U){ - thp->urg_ptr = ZERO; - decodes(src_tvb, &offset, &thp->urg_ptr); - thp->flags |= TCP_URG_BIT; - } - else - thp->flags &= ~TCP_URG_BIT; - if(changes & NEW_W) - decodes(src_tvb, &offset, &thp->window); - if(changes & NEW_A) - decodel(src_tvb, &offset, &thp->ack_seq); - if(changes & NEW_S) - decodel(src_tvb, &offset, &thp->seq); - break; + delta = get_unsigned_delta(src_tvb, &offset, hf_vj_urp, tree); + if(!pinfo->fd->flags.visited) { + thp->urg_ptr = delta; + thp->flags |= TCP_URG_BIT; + } + } else { + if(!pinfo->fd->flags.visited) + thp->flags &= ~TCP_URG_BIT; + } + if(changes & NEW_W) { + delta = get_signed_delta(src_tvb, &offset, hf_vj_win_delta, tree); + if(!pinfo->fd->flags.visited) + thp->window = htons(ntohs(thp->window) + delta); + } + if(changes & NEW_A) { + delta = get_unsigned_delta(src_tvb, &offset, hf_vj_ack_delta, tree); + if(!pinfo->fd->flags.visited) + thp->ack_seq = htonl(ntohl(thp->ack_seq) + delta); + } + if(changes & NEW_S) { + delta = get_unsigned_delta(src_tvb, &offset, hf_vj_seq_delta, tree); + if(!pinfo->fd->flags.visited) + thp->seq = htonl(ntohl(thp->seq) + delta); + } + break; } if(changes & NEW_I) - decodes(src_tvb, &offset, &ip->id); + delta = get_unsigned_delta(src_tvb, &offset, hf_vj_ip_id_delta, tree); else - ip->id = htons (ntohs (ip->id) + 1); - - /* Compute ip packet length and the buffer length needed */ - if((len = tvb_length(src_tvb) - offset - CRC_LEN) < ZERO) { + delta = 1; + if(!pinfo->fd->flags.visited) + ip->id = htons(ntohs(ip->id) + delta); + + /* Compute IP packet length and the buffer length needed */ + len = tvb_reported_length_remaining(src_tvb, offset); + if(len < ZERO) { + /* + * This shouldn't happen, as we *were* able to fetch stuff right before + * offset. + */ + if(check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "VJ compressed TCP (not enough data available)"); comp->flags |= SLF_TOSS; return VJ_ERROR; } - len += hdrlen; - ip->tot_len = htons(len); - /* Compute IP check sum */ - ip->cksum = ZERO; - ip->cksum = ip_csum((guint8 *)ip, lo_nibble(ip->ihl_version) * 4); - - /* Store the reconstructed header in frame data area */ - buf_hdr = g_mem_chunk_alloc(vj_header_memchunk); - buf_hdr->offset = offset; /* Offset in tvbuff is also stored */ - data_ptr = buf_hdr->data; - memcpy(data_ptr, ip, IP_HDR_LEN); - data_ptr += IP_HDR_LEN; - if(lo_nibble(ip->ihl_version) > 5) { - memcpy(data_ptr, cs->cs_ipopt, (lo_nibble(ip->ihl_version) - 5) * 4); - data_ptr += (lo_nibble(ip->ihl_version) - 5) * 4; - } - memcpy(data_ptr, thp, TCP_HDR_LEN); - data_ptr += TCP_HDR_LEN; - if(TCP_OFFSET(thp) > 5) - memcpy(data_ptr, cs->cs_tcpopt, (TCP_OFFSET(thp) - 5) * 4); - p_add_proto_data(fd, proto_vj, buf_hdr); - - return VJ_OK; -} - -/* For VJ compressed packet check if it is malformed */ -static gint -vjc_check(tvbuff_t *src_tvb, slcompress *comp) -{ - guint8 conn_index = ZERO; - guint8 offset = ZERO; - gint changes = ZERO; - - g_assert(src_tvb); - g_assert(comp); - if(tvb_length(src_tvb) < 3){ - comp->flags |= SLF_TOSS; - return VJ_ERROR; + if(!pinfo->fd->flags.visited) { + len += hdrlen; + ip->tot_len = htons(len); + /* Compute IP check sum */ + ip->cksum = ZERO; + ip->cksum = ip_csum((guint8 *)ip, lo_nibble(ip->ihl_version) * 4); + + /* Store the reconstructed header in frame data area */ + buf_hdr = g_mem_chunk_alloc(vj_header_memchunk); + buf_hdr->offset = offset; /* Offset in tvbuff is also stored */ + data_ptr = buf_hdr->data; + memcpy(data_ptr, ip, IP_HDR_LEN); + data_ptr += IP_HDR_LEN; + if(lo_nibble(ip->ihl_version) > 5) { + memcpy(data_ptr, cs->cs_ipopt, (lo_nibble(ip->ihl_version) - 5) * 4); + data_ptr += (lo_nibble(ip->ihl_version) - 5) * 4; + } + memcpy(data_ptr, thp, TCP_HDR_LEN); + data_ptr += TCP_HDR_LEN; + if(TCP_OFFSET(thp) > 5) + memcpy(data_ptr, cs->cs_tcpopt, (TCP_OFFSET(thp) - 5) * 4); + p_add_proto_data(pinfo->fd, proto_vj, buf_hdr); } - /* Read the change byte */ - changes = tvb_get_guint8(src_tvb, offset++); + if(tree != NULL && tvb_offset_exists(src_tvb, offset)) + proto_tree_add_text(tree, src_tvb, offset, -1, "TCP payload"); - if(changes & NEW_C){ /* Read conn index */ - conn_index = tvb_get_guint8(src_tvb, offset++); - if(conn_index > comp->rslot_limit) { - comp->flags |= SLF_TOSS; - return VJ_ERROR; - } - comp->flags &= ~SLF_TOSS; - comp->recv_current = conn_index; - } - else { - if(comp->flags & SLF_TOSS) - return VJ_ERROR; - } - return VJ_OK; } -/* Decode the delta of a 32 bit header field */ -static void -decodel(tvbuff_t *tvb, guint32* offset, gint32 *val) +/* + * Get an unsigned delta for a field, and put it into the protocol tree if + * we're building a protocol tree. + */ +static int +get_unsigned_delta(tvbuff_t *tvb, int *offsetp, int hf, proto_tree *tree) { - gint del = tvb_get_guint8(tvb, (*offset)++); + int offset = *offsetp; + int len; + guint16 del; + + len = 1; + del = tvb_get_guint8(tvb, offset++); if(del == ZERO){ - del = tvb_get_ntohs(tvb, *offset); - *offset= *offset + 2; + del = tvb_get_ntohs(tvb, offset); + offset += 2; + len += 2; } - *val = htonl(ntohl(*val) + del); - return; + if(tree != NULL) + proto_tree_add_uint(tree, hf, tvb, *offsetp, len, del); + *offsetp = offset; + return del; } -/* Decode the delta of a 16 bit header field */ -static void -decodes(tvbuff_t *tvb, guint32* offset, gint16 *val) +/* + * Get a signed delta for a field, and put it into the protocol tree if + * we're building a protocol tree. + */ +static int +get_signed_delta(tvbuff_t *tvb, int *offsetp, int hf, proto_tree *tree) { - gint del = tvb_get_guint8(tvb, (*offset)++); + int offset = *offsetp; + int len; + gint16 del; + + len = 1; + del = tvb_get_guint8(tvb, offset++); if(del == ZERO){ - del = tvb_get_ntohs(tvb, *offset); - *offset= *offset + 2; + del = tvb_get_ntohs(tvb, offset); + offset += 2; + len += 2; } - *val = htons(ntohs(*val) + del); - return; + if(tree != NULL) + proto_tree_add_int(tree, hf, tvb, *offsetp, len, del); + *offsetp = offset; + return del; } /* For VJ uncompressed packet check if it is malformed */ static gint -vjuc_check(tvbuff_t *tvb, slcompress *comp) +vjuc_check(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + slcompress *comp) { - guint8 ihl = ZERO; - gint index = ZERO; - - g_assert(comp); - g_assert(tvb); + guint8 ihl; + gint index; if(tvb_length(tvb) < IP_HDR_LEN) { + if(check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "VJ uncompressed TCP (not enough data available)"); + if(tree != NULL) + call_dissector(data_handle, tvb, pinfo, tree); comp->flags |= SLF_TOSS; - index = VJ_ERROR; + return VJ_ERROR; } - else { - /* Get the IP header length */ - ihl = tvb_get_guint8(tvb, 0) & IP_HDR_LEN_MASK; - ihl <<= 2; - /* Get connection index */ - index = tvb_get_guint8(tvb, IP_FIELD_PROTOCOL); + /* Get the IP header length */ + ihl = tvb_get_guint8(tvb, 0) & IP_HDR_LEN_MASK; + ihl <<= 2; + + /* Get connection index */ + index = tvb_get_guint8(tvb, IP_FIELD_PROTOCOL); + if(tree != NULL) + proto_tree_add_uint(tree, hf_vj_connection_number, tvb, + IP_FIELD_PROTOCOL, 1, index); + + /* Check connection number and IP header length field */ + if(ihl < IP_HDR_LEN) { + if(check_col(pinfo->cinfo, COL_INFO)) { + col_add_fstr(pinfo->cinfo, COL_INFO, "VJ uncompressed TCP (header length (%u) < %u)", + ihl, IP_HDR_LEN); + } + comp->flags |= SLF_TOSS; + return VJ_ERROR; + } - /* Check connection number and IP header length field */ - if(ihl < IP_HDR_LEN || index > comp->rslot_limit) { - comp->flags |= SLF_TOSS; - index = VJ_ERROR; + if(index > comp->rslot_limit) { + if(check_col(pinfo->cinfo, COL_INFO)) { + col_add_fstr(pinfo->cinfo, COL_INFO, "VJ uncompressed TCP (index (%u) < highest receive slot ID (%u))", + index, comp->rslot_limit); } + comp->flags |= SLF_TOSS; + return VJ_ERROR; } return index; @@ -584,16 +835,14 @@ vjuc_check(tvbuff_t *tvb, slcompress *comp) /* Setup the decompressed packet tvb for VJ uncompressed packets */ static gint vjuc_tvb_setup(tvbuff_t *tvb, - tvbuff_t **dst_tvb, - slcompress *comp, - frame_data *fd) + tvbuff_t **dst_tvb, + packet_info *pinfo, + slcompress *comp) { - guint8 ihl = ZERO; - gint isize = tvb_length(tvb); - guint8 *buffer = NULL; - - g_assert(comp); - g_assert(tvb); + frame_data *fd = pinfo->fd; + guint8 ihl = ZERO; + gint isize = tvb_length(tvb); + guint8 *buffer = NULL; /* Get the IP header length */ ihl = tvb_get_guint8(tvb, 0) & IP_HDR_LEN_MASK; @@ -602,20 +851,25 @@ vjuc_tvb_setup(tvbuff_t *tvb, /* Copy packet data to a buffer */ buffer = g_malloc(isize); tvb_memcpy(tvb, buffer, 0, isize); - buffer[IP_FIELD_PROTOCOL] = IP_PROTOCOL_TCP; + buffer[IP_FIELD_PROTOCOL] = IP_PROTO_TCP; /* Compute checksum */ - if (ip_csum(buffer, ihl) != ZERO) { + if(ip_csum(buffer, ihl) != ZERO) { g_free(buffer); + if(check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "VJ uncompressed TCP (bad IP checksum)"); comp->flags |= SLF_TOSS; return VJ_ERROR; } + if(check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "VJ uncompressed TCP"); + /* * Form the new tvbuff. * Neither header checksum is recalculated */ - *dst_tvb = tvb_new_real_data(buffer, isize, isize); + *dst_tvb = tvb_new_real_data(buffer, isize, pntohs(&buffer[IP_FIELD_TOT_LEN])); tvb_set_child_real_data_tvbuff(tvb, *dst_tvb); add_new_data_source(fd, *dst_tvb, "VJ Uncompressed"); return VJ_OK; @@ -640,21 +894,21 @@ vjuc_update_state(tvbuff_t *tvb, slcompress *comp, guint8 index) comp->flags &= ~SLF_TOSS; tvb_memcpy(tvb, (guint8 *)&cs->cs_ip, 0, IP_HDR_LEN); tvb_memcpy(tvb, (guint8 *)&cs->cs_tcp, ihl, TCP_HDR_LEN); - if (ihl > IP_HDR_LEN) + if(ihl > IP_HDR_LEN) tvb_memcpy(tvb, cs->cs_ipopt, sizeof(iphdr_type), ihl - IP_HDR_LEN); - if (TCP_OFFSET(&(cs->cs_tcp)) > 5) + if(TCP_OFFSET(&(cs->cs_tcp)) > 5) tvb_memcpy(tvb, cs->cs_tcpopt, ihl + sizeof(tcphdr_type), (TCP_OFFSET(&(cs->cs_tcp)) - 5) * 4); return; } -/* Wraper for in_cksum function */ +/* Wrapper for in_cksum function */ static guint16 ip_csum(const guint8 * ptr, guint32 len) { - vec_t cksum_vec[1]; + vec_t cksum_vec[1]; - cksum_vec[0].ptr = ptr; - cksum_vec[0].len = len; - return in_cksum(&cksum_vec[0], 1); + cksum_vec[0].ptr = ptr; + cksum_vec[0].len = len; + return in_cksum(&cksum_vec[0], 1); } |