/* packet-rdt.c * * Routines for RDT dissection * RDT = Real Data Transport * * Copyright 2005 * Written by Martin Mathieson * * $Id$ * * Ethereal - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Information sources: * http://www.ox.compsoc.net/~glyn/rdt/ * http://www.eurescom.de/~pub-deliverables/P900-series/P913/D2appendices/p913d2appf.pdf */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include "packet-rdt.h" #include #include static dissector_handle_t rdt_handle; /* RDT header fields */ static int proto_rdt = -1; /* RDT setup fields */ static int hf_rdt_setup = -1; static int hf_rdt_setup_frame = -1; static int hf_rdt_setup_method = -1; static int hf_rdt_stream_id = -1; static int hf_rdt_sequence_number = -1; static int hf_rdt_flags = -1; static int hf_rdt_packet_size = -1; static int hf_rdt_timestamp = -1; static int hf_rdt_unparsed = -1; /* RDT fields defining a sub tree */ static gint ett_rdt = -1; static gint ett_rdt_setup = -1; static void dissect_rdt(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); static void show_setup_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); /* Preferences bool to control whether or not setup info should be shown */ static gboolean global_rdt_show_setup_info = TRUE; /* Memory chunk for storing conversation and per-packet info */ static GMemChunk *rdt_conversations = NULL; #define RETRANSMISSION_REQUEST_STREAM_ID 0 #define AUDIO_STREAM_ID 64 #define VIDEO_STREAM_ID 66 const value_string rdt_stream_id_vals[] = { { RETRANSMISSION_REQUEST_STREAM_ID, "Retransmission Request" }, { AUDIO_STREAM_ID, "Audio" }, { VIDEO_STREAM_ID, "Video" }, { 0, NULL }, }; /* Set up an RDT conversation */ void rdt_add_address(packet_info *pinfo, address *addr, int port, int other_port, gchar *setup_method, guint32 setup_frame_number) { address null_addr; conversation_t* p_conv; struct _rdt_conversation_info *p_conv_data = NULL; /* * If this isn't the first time this packet has been processed, * we've already done this work, so we don't need to do it * again. */ if (pinfo->fd->flags.visited) { return; } SET_ADDRESS(&null_addr, AT_NONE, 0, NULL); /* * Check if the ip address and port combination is not * already registered as a conversation. */ p_conv = find_conversation(setup_frame_number, addr, &null_addr, PT_UDP, port, other_port, NO_ADDR_B | (!other_port ? NO_PORT_B : 0)); /* * If not, create a new conversation. */ if ( !p_conv || p_conv->setup_frame != setup_frame_number) { p_conv = conversation_new(setup_frame_number, addr, &null_addr, PT_UDP, (guint32)port, (guint32)other_port, NO_ADDR2 | (!other_port ? NO_PORT2 : 0)); } /* Set dissector */ conversation_set_dissector(p_conv, rdt_handle); /* * Check if the conversation has data associated with it. */ p_conv_data = conversation_get_proto_data(p_conv, proto_rdt); /* * If not, add a new data item. */ if (!p_conv_data) { /* Create conversation data */ p_conv_data = g_mem_chunk_alloc(rdt_conversations); conversation_add_proto_data(p_conv, proto_rdt, p_conv_data); } /* * Update the conversation data. */ strncpy(p_conv_data->method, setup_method, MAX_RDT_SETUP_METHOD_SIZE); p_conv_data->method[MAX_RDT_SETUP_METHOD_SIZE] = '\0'; p_conv_data->frame_number = setup_frame_number; } static void rdt_init(void) { /* (Re)allocate mem chunk for conversations */ if (rdt_conversations) { g_mem_chunk_destroy(rdt_conversations); } rdt_conversations = g_mem_chunk_new("rdt_conversations", sizeof(struct _rdt_conversation_info), 20 * sizeof(struct _rdt_conversation_info), G_ALLOC_ONLY); } /*********************************/ /* Main dissection function */ /*********************************/ static void dissect_rdt(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { guint offset = 0; proto_item *ti = NULL; proto_tree *rdt_tree = NULL; guint8 stream_id; guint16 sequence_number; guint16 packet_size; guint32 timestamp; guint8 flags; /* Set columns */ if (check_col(pinfo->cinfo, COL_PROTOCOL)) { col_set_str(pinfo->cinfo, COL_PROTOCOL, "RDT" ); } if (check_col(pinfo->cinfo, COL_INFO)) { col_set_str(pinfo->cinfo, COL_INFO, "RealPlayer: "); } /* Build tree (inside guard?) */ /* Create RDT protocol tree */ ti = proto_tree_add_item(tree, proto_rdt, tvb, offset, -1, FALSE); rdt_tree = proto_item_add_subtree(ti, ett_rdt); /* Conversation setup info */ if (global_rdt_show_setup_info) { show_setup_info(tvb, pinfo, rdt_tree); } /* Stream ID */ stream_id = tvb_get_guint8(tvb, offset); proto_tree_add_item(rdt_tree, hf_rdt_stream_id, tvb, offset, 1, FALSE); offset++; if (check_col(pinfo->cinfo, COL_PROTOCOL)) { switch (stream_id) { case AUDIO_STREAM_ID: col_append_str(pinfo->cinfo, COL_INFO, "AUDIO "); break; case VIDEO_STREAM_ID: col_append_str(pinfo->cinfo, COL_INFO, "VIDEO "); break; case RETRANSMISSION_REQUEST_STREAM_ID: col_append_str(pinfo->cinfo, COL_INFO, "Retransmit Request ? "); break; default: col_append_str(pinfo->cinfo, COL_INFO, "Unknown "); break; } } if ((stream_id != AUDIO_STREAM_ID) && (stream_id != VIDEO_STREAM_ID)) { /* Don't know what to do with others... */ proto_tree_add_item(rdt_tree, hf_rdt_unparsed, tvb, offset, -1, FALSE); return; } /* Sequence number */ sequence_number = tvb_get_ntohs(tvb, offset); proto_tree_add_item(rdt_tree, hf_rdt_sequence_number, tvb, offset, 2, FALSE); offset += 2; if (check_col(pinfo->cinfo, COL_PROTOCOL)) { col_append_fstr(pinfo->cinfo, COL_INFO, " seq=%d ", sequence_number); } /* These sequence numbers are short packets: give up */ if ((sequence_number & 0xff00) == 0xff00) { proto_tree_add_item(rdt_tree, hf_rdt_unparsed, tvb, offset, -1, FALSE); return; } /* Packet size (not present if 1st byte's m.s. bit was set) */ if (stream_id & 0x80) { packet_size = tvb_get_ntohs(tvb, offset); proto_tree_add_item(rdt_tree, hf_rdt_packet_size, tvb, offset, 2, FALSE); offset += 2; if (check_col(pinfo->cinfo, COL_PROTOCOL)) { col_append_fstr(pinfo->cinfo, COL_INFO, " size=%d ", packet_size); } } /* Flags */ flags = tvb_get_guint8(tvb, offset); proto_tree_add_item(rdt_tree, hf_rdt_flags, tvb, offset, 1, FALSE); offset++; /* Timestamp */ timestamp = tvb_get_ntohl(tvb, offset); proto_tree_add_item(rdt_tree, hf_rdt_timestamp, tvb, offset, 4, FALSE); offset += 4; if (check_col(pinfo->cinfo, COL_PROTOCOL)) { col_append_fstr(pinfo->cinfo, COL_INFO, " ts=%d ", timestamp); } /* The remaining data is unparsed. */ proto_tree_add_item(rdt_tree, hf_rdt_unparsed, tvb, offset, -1, FALSE); } /* Look for conversation info and display any setup info found */ static void show_setup_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { /* Conversation and current data */ conversation_t *p_conv = NULL; struct _rdt_conversation_info *p_conv_data = NULL; /* Use existing packet info if available */ p_conv_data = p_get_proto_data(pinfo->fd, proto_rdt); if (!p_conv_data) { /* First time, get info from conversation */ p_conv = find_conversation(pinfo->fd->num, &pinfo->net_dst, &pinfo->net_src, pinfo->ptype, pinfo->destport, pinfo->srcport, NO_ADDR_B); if (p_conv) { /* Create space for conversation info */ struct _rdt_conversation_info *p_conv_packet_data; p_conv_data = conversation_get_proto_data(p_conv, proto_rdt); if (p_conv_data) { /* Save this conversation info into packet info */ p_conv_packet_data = g_mem_chunk_alloc(rdt_conversations); strcpy(p_conv_packet_data->method, p_conv_data->method); p_conv_packet_data->frame_number = p_conv_data->frame_number; p_add_proto_data(pinfo->fd, proto_rdt, p_conv_packet_data); } } } /* Create setup info subtree with summary info. */ if (p_conv_data) { proto_tree *rdt_setup_tree; proto_item *ti = proto_tree_add_string_format(tree, hf_rdt_setup, tvb, 0, 0, "", "Stream setup by %s (frame %d)", p_conv_data->method, p_conv_data->frame_number); PROTO_ITEM_SET_GENERATED(ti); rdt_setup_tree = proto_item_add_subtree(ti, ett_rdt_setup); if (rdt_setup_tree) { /* Add details into subtree */ proto_item* item = proto_tree_add_uint(rdt_setup_tree, hf_rdt_setup_frame, tvb, 0, 0, p_conv_data->frame_number); PROTO_ITEM_SET_GENERATED(item); item = proto_tree_add_string(rdt_setup_tree, hf_rdt_setup_method, tvb, 0, 0, p_conv_data->method); PROTO_ITEM_SET_GENERATED(item); } } } void proto_register_rdt(void) { static hf_register_info hf[] = { { &hf_rdt_stream_id, { "StreamID", "rdt.stream_id", FT_UINT8, BASE_DEC, VALS(rdt_stream_id_vals), 0x0, "", HFILL } }, { &hf_rdt_sequence_number, { "Sequence number", "rdt.sequence_number", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rdt_flags, { "Flags", "rdt.flags", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rdt_packet_size, { "Packet size", "rdt.packet_size", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL } }, { &hf_rdt_timestamp, { "Timestamp", "rdt.timestamp", FT_UINT32, BASE_DEC, NULL, 0x0, "Timestamp", HFILL } }, { &hf_rdt_setup, { "Stream setup", "rdt.setup", FT_STRING, BASE_NONE, NULL, 0x0, "Stream setup, method and frame number", HFILL } }, { &hf_rdt_setup_frame, { "Setup frame", "rdt.setup-frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0, "Frame that set up this stream", HFILL } }, { &hf_rdt_setup_method, { "Setup Method", "rdt.setup-method", FT_STRING, BASE_NONE, NULL, 0x0, "Method used to set up this stream", HFILL } }, { &hf_rdt_unparsed, { "Unparsed Data", "rdt.unparsed", FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL } } }; static gint *ett[] = { &ett_rdt, &ett_rdt_setup }; module_t *rdt_module; /* Register protocol and fields */ proto_rdt = proto_register_protocol("Real Data Transport", "RDT", "rdt"); proto_register_field_array(proto_rdt, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); register_dissector("rdt", dissect_rdt, proto_rdt); /* Preference settings */ rdt_module = prefs_register_protocol(proto_rdt, NULL); prefs_register_bool_preference(rdt_module, "show_setup_info", "Show stream setup information", "Where available, show which protocol and frame caused " "this RDT stream to be created", &global_rdt_show_setup_info); register_init_routine( &rdt_init ); } void proto_reg_handoff_rdt(void) { rdt_handle = find_dissector("rdt"); }