diff options
author | Gilbert Ramirez <gram@alumni.rice.edu> | 2004-07-18 18:06:47 +0000 |
---|---|---|
committer | Gilbert Ramirez <gram@alumni.rice.edu> | 2004-07-18 18:06:47 +0000 |
commit | 669db206cb1f270046ad400fff7655e20c63e723 (patch) | |
tree | 4eff24a2e16c8963e497e1fc575f35e6af59bd26 /epan/dissectors/packet-vj.c | |
parent | ae46c27a38700af669ef907491081f09df6f6b2c (diff) |
Move dissectors to epan/dissectors directory.
Also move ncp222.py, x11-fields, process-x11-fields.pl,
make-reg-dotc, and make-reg-dotc.py.
Adjust #include lines in files that include packet-*.h
files.
svn path=/trunk/; revision=11410
Diffstat (limited to 'epan/dissectors/packet-vj.c')
-rw-r--r-- | epan/dissectors/packet-vj.c | 900 |
1 files changed, 900 insertions, 0 deletions
diff --git a/epan/dissectors/packet-vj.c b/epan/dissectors/packet-vj.c new file mode 100644 index 0000000000..30bbe0cbca --- /dev/null +++ b/epan/dissectors/packet-vj.c @@ -0,0 +1,900 @@ +/* packet-vj.c + * Routines for Van Jacobson header decompression. + * + * $Id$ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * + * This file created by Irfan Khan <ikhan@qualcomm.com> + * Copyright (c) 2001 by QUALCOMM, Incorporated. + * All Rights reserved. + * + * Routines to compress and uncompress TCP packets (for transmission + * over low speed serial lines). + * + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: + * - Initial distribution. + * + * + * modified for KA9Q Internet Software Package by + * Katie Stevens (dkstevens@ucdavis.edu) + * University of California, Davis + * Computing Services + * - 01-31-90 initial adaptation (from 1.19) + * PPP.05 02-15-90 [ks] + * PPP.08 05-02-90 [ks] use PPP protocol field to signal compression + * PPP.15 09-90 [ks] improve mbuf handling + * PPP.16 11-02 [karn] substantially rewritten to use NOS facilities + * + * - Feb 1991 Bill_Simpson@um.cc.umich.edu + * variable number of conversation slots + * allow zero or one slots + * separate routines + * status display + * - Jul 1994 Dmitry Gorodchanin + * Fixes for memory leaks. + * - Oct 1994 Dmitry Gorodchanin + * Modularization. + * - Jan 1995 Bjorn Ekwall + * Use ip_fast_csum from ip.h + * - July 1995 Christos A. Polyzols + * Spotted bug in tcp option checking + * - Sep 2001 Irfan Khan + * Rewrite to make the code work for ethereal. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <glib.h> +#include <string.h> +#include <epan/packet.h> +#include "prefs.h" +#include "packet-ppp.h" +#include "ppptypes.h" +#include "ipproto.h" +#include "in_cksum.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_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_FIELD_HDR_LEN 12 /* Data offset field in TCP hdr */ +#define TCP_HDR_LEN 20 /* Minimum TCP header length */ +#define TCP_MAX_OPT_LEN 44 /* Max length of TCP options */ +#define TCP_SIMUL_CONV_MAX 256 /* Max number of simul. TCP conversations */ +#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 /* 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 */ +#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U)/* unidirectional data */ +#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U) + +/* Function return values */ +#define VJ_OK 0 +#define VJ_ERROR -1 + +/* Define for 0 */ +#define ZERO 0 + +/* 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 */ + +/* IP and TCP header types */ +typedef struct { + guint8 ihl_version; + guint8 tos; + guint16 tot_len; + guint16 id; + guint16 frag_off; + guint8 ttl; + guint8 proto; + guint16 cksum; + guint32 src; + guint32 dst; +} iphdr_type; + +typedef struct { + guint16 srcport; + guint16 dstport; + guint32 seq; + guint32 ack_seq; + guint8 off_x2; + guint8 flags; + guint16 window; + guint16 cksum; + guint16 urg_ptr; +} tcphdr_type; + +#define TCP_OFFSET(th) (((th)->off_x2 & 0xf0) >> 4) + +/* State per active tcp conversation */ +typedef struct cstate { + iphdr_type cs_ip; + tcphdr_type cs_tcp; + guint8 cs_ipopt[IP_MAX_OPT_LEN]; + guint8 cs_tcpopt[TCP_MAX_OPT_LEN]; + guint32 flags; +#define SLF_TOSS 0x00000001 /* tossing rcvd frames until id received */ +} cstate; + +/* All the state data for one serial line */ +typedef struct { + cstate rstate[TCP_SIMUL_CONV_MAX]; /* receive connection states (array) */ + guint8 recv_current; /* most recent rcvd id */ +} slcompress; + +/* 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; + +/* State repository (Full Duplex) */ +#define RX_TX_STATE_COUNT 2 +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 { + int offset; /* uppermost bit is "can't dissect" flag */ + guint8 data[VJ_DATA_SIZE]; +} vj_header_t; + +/* Function prototypes */ +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(void); +static void vj_init(void); +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; + int i; + gint conn_index; + cstate *cs = NULL; + guint8 ihl; + guint8 thl; + guint8 *buffer; + tvbuff_t *next_tvb; + gint isize = tvb_length(tvb); + gint ipsize; + + 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(pinfo->p2p_dir == P2P_DIR_UNKNOWN) { + /* Direction of the traffic unknown - can't update state */ + comp = NULL; + } else { + /* Get state for that direction */ + comp = rx_tx_state[pinfo->p2p_dir]; + } + + /* + * Check to make sure we can fetch the connection index. + */ + if(!tvb_bytes_exist(tvb, IP_FIELD_PROTOCOL, 1)) { + /* + * We don't. We can't even mark a connection as non-decompressable, + * as we don't know which connection this is. Mark them all as + * non-decompressable. + */ + 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); + if(comp != NULL) { + for(i = 0; i < TCP_SIMUL_CONV_MAX; i++) + comp->rstate[i].flags |= SLF_TOSS; + } + return; + } + + /* Get connection index */ + conn_index = tvb_get_guint8(tvb, IP_FIELD_PROTOCOL); + if(tree != NULL) + proto_tree_add_uint(vj_tree, hf_vj_connection_number, tvb, + IP_FIELD_PROTOCOL, 1, conn_index); + + /* + * Update the current connection, and get a pointer to its state. + */ + if(comp != NULL) { + comp->recv_current = conn_index; + cs = &comp->rstate[conn_index]; + } + + /* Get the IP header length */ + ihl = tvb_get_guint8(tvb, 0) & IP_HDR_LEN_MASK; + ihl <<= 2; + + /* Check IP header length */ + if(ihl < IP_HDR_LEN) { + if(check_col(pinfo->cinfo, COL_INFO)) { + col_add_fstr(pinfo->cinfo, COL_INFO, "VJ uncompressed TCP (IP header length (%u) < %u)", + ihl, IP_HDR_LEN); + } + if(cs != NULL) + cs->flags |= SLF_TOSS; + return; + } + + /* Make sure we have the full IP header */ + if(isize < ihl) { + 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); + if(cs != NULL) + cs->flags |= SLF_TOSS; + return; + } + + if(check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "VJ uncompressed TCP"); + + /* + * Copy packet data to a buffer, and replace the connection index with + * the protocol type (which is always TCP), to give the actual IP header. + */ + buffer = tvb_memdup(tvb, 0, isize); + buffer[IP_FIELD_PROTOCOL] = IP_PROTO_TCP; + + /* Check IP checksum */ + if(ip_csum(buffer, ihl) != ZERO) { + /* + * Checksum invalid - don't update state, and don't decompress + * any subsequent compressed packets in this direction. + */ + if(cs != NULL) + cs->flags |= SLF_TOSS; + cs = NULL; /* disable state updates */ + } else { + /* Do we have the TCP header length in the tvbuff? */ + if(!tvb_bytes_exist(tvb, ihl + TCP_FIELD_HDR_LEN, 1)) { + /* We don't, so we can't provide enough data for decompression */ + if(check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "VJ uncompressed TCP (not enough data available)"); + if(cs != NULL) + cs->flags |= SLF_TOSS; + cs = NULL; /* disable state updates */ + } else { + /* Get the TCP header length */ + thl = tvb_get_guint8(tvb, ihl + TCP_FIELD_HDR_LEN); + thl = ((thl & 0xf0) >> 4) * 4; + + /* Check TCP header length */ + if(thl < TCP_HDR_LEN) { + if(check_col(pinfo->cinfo, COL_INFO)) { + col_add_fstr(pinfo->cinfo, COL_INFO, "VJ uncompressed TCP (TCP header length (%u) < %u)", + thl, TCP_HDR_LEN); + } + if(cs != NULL) + cs->flags |= SLF_TOSS; + cs = NULL; /* disable state updates */ + } else { + /* Make sure we have the full TCP header */ + if(isize < thl) { + if(check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "VJ uncompressed TCP (not enough data available)"); + if(cs != NULL) + cs->flags |= SLF_TOSS; + cs = NULL; /* disable state updates */ + } + } + } + } + + /* + * If packet seen for first time, update state if we have state and can + * update it. + */ + if(!pinfo->fd->flags.visited) { + if(cs != NULL) { + cs->flags &= ~SLF_TOSS; + memcpy(&cs->cs_ip, &buffer[0], IP_HDR_LEN); + memcpy(&cs->cs_tcp, &buffer[ihl], TCP_HDR_LEN); + if(ihl > IP_HDR_LEN) + memcpy(cs->cs_ipopt, &buffer[sizeof(iphdr_type)], ihl - IP_HDR_LEN); + if(TCP_OFFSET(&(cs->cs_tcp)) > 5) + memcpy(cs->cs_tcpopt, &buffer[ihl + sizeof(tcphdr_type)], + (TCP_OFFSET(&(cs->cs_tcp)) - 5) * 4); + } + } + + /* + * Set up tvbuff containing packet with protocol type. + * Neither header checksum is recalculated. + * + * Use the length field from the IP header as the reported length; + * use the minimum of that and the number of bytes we got from + * the tvbuff as the actual length, just in case the tvbuff we were + * handed includes part or all of the FCS (because the FCS preference + * for the PPP dissector doesn't match the FCS size in this session). + */ + ipsize = pntohs(&buffer[IP_FIELD_TOT_LEN]); + if (ipsize < isize) + isize = ipsize; + next_tvb = tvb_new_real_data(buffer, isize, ipsize); + tvb_set_child_real_data_tvbuff(tvb, next_tvb); + add_new_data_source(pinfo, next_tvb, "VJ Uncompressed"); + + /* + * Call IP dissector. + */ + 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_ERROR; + + 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: Compressed data"); + vj_tree = proto_item_add_subtree(ti, ett_vj); + } + + if(!ppp_vj_decomp || pinfo->p2p_dir == P2P_DIR_UNKNOWN) { + /* + * VJ decompression turned off, so we shouldn't decompress, or + * direction of the traffic unknown, so we can't decompress. + */ + comp = NULL; + } else { + /* Get state for that direction */ + comp = rx_tx_state[pinfo->p2p_dir]; + } + + /* Process the compressed data header */ + if(vjc_process(tvb, pinfo, vj_tree, comp) == VJ_ERROR) + return; + + /* Decompression possible - 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); +} + +/* 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); +} + +void +proto_reg_handoff_vj(void) +{ + dissector_handle_t vjc_handle; + dissector_handle_t vjuc_handle; + + vjc_handle = create_dissector_handle(dissect_vjc, proto_vj); + dissector_add("ppp.protocol", PPP_VJC_COMP, vjc_handle); + + vjuc_handle = create_dissector_handle(dissect_vjuc, proto_vj); + dissector_add("ppp.protocol", PPP_VJC_UNCOMP, vjuc_handle); + + ip_handle = find_dissector("ip"); + data_handle = find_dissector("data"); +} + +/* Initialization function */ +static void +vj_init(void) +{ + gint i = ZERO; + slcompress *pslc = NULL; + + if(vj_header_memchunk != NULL) + g_mem_chunk_destroy(vj_header_memchunk); + vj_header_memchunk = g_mem_chunk_new("vj header store", sizeof (vj_header_t), + sizeof (vj_header_t) * VJ_ATOM_COUNT, + G_ALLOC_ONLY); + for(i = 0; i < RX_TX_STATE_COUNT; i++) { + if((pslc = rx_tx_state[i]) != NULL) + g_free(pslc); + rx_tx_state[i] = slhc_init(); + } + return; +} + +/* Initialization routine for VJ decompression */ +static slcompress * +slhc_init(void) +{ + slcompress *comp = g_malloc(sizeof(slcompress)); + int i; + + memset(comp, ZERO, sizeof(slcompress)); + + /* + * Initialize the state; there is no current connection, and + * we have no header data for any of the connections, as we + * haven't yet seen an uncompressed frame. + */ + comp->recv_current = TCP_SIMUL_CONV_MAX - 1; + for (i = 0; i < TCP_SIMUL_CONV_MAX; i++) + comp->rstate[i].flags |= SLF_TOSS; + return comp; +} + +/* Setup the decompressed packet tvb for VJ compressed packets */ +static gint +vjc_tvb_setup(tvbuff_t *src_tvb, + tvbuff_t **dst_tvb, + packet_info *pinfo) +{ + vj_header_t *hdr_buf; + guint8 offset; + guint8 *data_ptr; + 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(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 (previous data bad or missing)"); + 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; + 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); + 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, g_ntohs(ip->tot_len)); + tvb_set_child_real_data_tvbuff(src_tvb, *dst_tvb); + add_new_data_source(pinfo, *dst_tvb, "VJ Decompressed"); + return VJ_OK; +} + +/* + * For VJ compressed packet: + * + * check if it is malformed; + * dissect the relevant fields; + * update the decompressor state on the first pass. + */ +static gint +vjc_process(tvbuff_t *src_tvb, packet_info *pinfo, proto_tree *tree, + slcompress *comp) +{ + int offset = ZERO; + int i; + gint changes; + proto_item *ti; + proto_tree *changes_tree; + guint8 conn_index; + cstate *cs = NULL; + iphdr_type *ip = NULL; + tcphdr_type *thp = NULL; + guint16 tcp_cksum; + gint hdrlen = ZERO; + guint16 word; + int delta; + gint len; + vj_header_t *buf_hdr; + guint8 *data_ptr; + + if(tvb_length(src_tvb) < 3){ + /* + * We don't even have enough data for the change byte, so we can't + * determine which connection this is; mark all connections as + * non-decompressible. + */ + 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); + if(comp != NULL) { + for(i = 0; i < TCP_SIMUL_CONV_MAX; i++) + comp->rstate[i].flags |= SLF_TOSS; + } + return VJ_ERROR; + } + + /* Read the change byte */ + 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(comp != NULL) + comp->recv_current = conn_index; + } + + if(!pinfo->fd->flags.visited) { + /* + * This is the first time this frame has been seen, so we need + * state information to decompress it. If that information isn't + * available, don't use the state information, and don't update it, + * either. + */ + if(comp != NULL && !(comp->rstate[comp->recv_current].flags & SLF_TOSS)) { + cs = &comp->rstate[comp->recv_current]; + thp = &cs->cs_tcp; + ip = &cs->cs_ip; + } + } + + /* Build TCP and IP headers */ + 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(cs != NULL) { + hdrlen = lo_nibble(ip->ihl_version) * 4 + TCP_OFFSET(thp) * 4; + thp->cksum = g_htons(tcp_cksum); + } + offset += 2; + if(cs != NULL) { + 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 */ + if(cs != NULL) { + word = g_ntohs(ip->tot_len) - hdrlen; + thp->ack_seq = g_htonl(g_ntohl(thp->ack_seq) + word); + thp->seq = g_htonl(g_ntohl(thp->seq) + word); + } + break; + case SPECIAL_D: /* Unidirectional data */ + if(cs != NULL) + thp->seq = g_htonl(g_ntohl(thp->seq) + g_ntohs(ip->tot_len) - hdrlen); + break; + default: + if(changes & NEW_U){ + delta = get_unsigned_delta(src_tvb, &offset, hf_vj_urp, tree); + if(cs != NULL) { + thp->urg_ptr = delta; + thp->flags |= TCP_URG_BIT; + } + } else { + if(cs != NULL) + thp->flags &= ~TCP_URG_BIT; + } + if(changes & NEW_W) { + delta = get_signed_delta(src_tvb, &offset, hf_vj_win_delta, tree); + if(cs != NULL) + thp->window = g_htons(g_ntohs(thp->window) + delta); + } + if(changes & NEW_A) { + delta = get_unsigned_delta(src_tvb, &offset, hf_vj_ack_delta, tree); + if(cs != NULL) + thp->ack_seq = g_htonl(g_ntohl(thp->ack_seq) + delta); + } + if(changes & NEW_S) { + delta = get_unsigned_delta(src_tvb, &offset, hf_vj_seq_delta, tree); + if(cs != NULL) + thp->seq = g_htonl(g_ntohl(thp->seq) + delta); + } + break; + } + if(changes & NEW_I) + delta = get_unsigned_delta(src_tvb, &offset, hf_vj_ip_id_delta, tree); + else + delta = 1; + if(cs != NULL) + ip->id = g_htons(g_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)"); + if(cs != NULL) + cs->flags |= SLF_TOSS; + return VJ_ERROR; + } + + /* Show the TCP payload */ + if(tree != NULL && tvb_offset_exists(src_tvb, offset)) + proto_tree_add_text(tree, src_tvb, offset, -1, "TCP payload"); + + /* Nothing more to do if we don't have any compression state */ + if(comp == NULL) { + /* 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)"); + return VJ_ERROR; + } + + if(cs != NULL) { + len += hdrlen; + ip->tot_len = g_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); + } + + return VJ_OK; +} + +/* + * 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) +{ + 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 += 2; + len += 2; + } + if(tree != NULL) + proto_tree_add_uint(tree, hf, tvb, *offsetp, len, del); + *offsetp = offset; + return del; +} + +/* + * 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) +{ + 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 += 2; + len += 2; + } + if(tree != NULL) + proto_tree_add_int(tree, hf, tvb, *offsetp, len, del); + *offsetp = offset; + return del; +} + +/* Wrapper for in_cksum function */ +static guint16 +ip_csum(const guint8 * ptr, guint32 len) +{ + vec_t cksum_vec[1]; + + cksum_vec[0].ptr = ptr; + cksum_vec[0].len = len; + return in_cksum(&cksum_vec[0], 1); +} |