diff options
Diffstat (limited to 'epan/dissectors/packet-iax2.c')
-rw-r--r-- | epan/dissectors/packet-iax2.c | 1855 |
1 files changed, 1855 insertions, 0 deletions
diff --git a/epan/dissectors/packet-iax2.c b/epan/dissectors/packet-iax2.c new file mode 100644 index 0000000000..6277a1922a --- /dev/null +++ b/epan/dissectors/packet-iax2.c @@ -0,0 +1,1855 @@ +/* + * packet-iax2.c + * + * Routines for IAX2 packet disassembly + * By Alastair Maw <asterisk@almaw.com> + * Copyright 2003 Alastair Maw + * + * IAX2 is a VoIP protocol for the open source PBX Asterisk. Please see + * http://www.asterisk.org for more information. + * + * $Id$ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * 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. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <glib.h> + +#include <epan/circuit.h> +#include <epan/packet.h> +#include <epan/to_str.h> + +#include "packet-iax2.h" +#include "iax2_codec_type.h" + +#define IAX2_PORT 4569 +#define PROTO_TAG_IAX2 "IAX2" + +/* #define DEBUG_HASHING */ + +/* Ethereal ID of the IAX2 protocol */ +static int proto_iax2 = -1; + +/* The following hf_* variables are used to hold the ethereal IDs of + * our header fields; they are filled out when we call + * proto_register_field_array() in proto_register_iax2() + */ +static int hf_iax2_packet_type = -1; +static int hf_iax2_retransmission = -1; +static int hf_iax2_scallno = -1; +static int hf_iax2_dcallno = -1; +static int hf_iax2_ts = -1; +static int hf_iax2_minits = -1; +static int hf_iax2_minividts = -1; +static int hf_iax2_minividmarker = -1; +static int hf_iax2_oseqno = -1; +static int hf_iax2_iseqno = -1; +static int hf_iax2_type = -1; +static int hf_iax2_csub = -1; +static int hf_iax2_cmd_csub = -1; +static int hf_iax2_iax_csub = -1; +static int hf_iax2_voice_csub = -1; +static int hf_iax2_voice_codec = -1; +static int hf_iax2_video_csub = -1; +static int hf_iax2_video_codec = -1; +static int hf_iax2_marker = -1; + +static int hf_iax2_cap_g723_1 = -1; +static int hf_iax2_cap_gsm = -1; +static int hf_iax2_cap_ulaw = -1; +static int hf_iax2_cap_alaw = -1; +static int hf_iax2_cap_g726 = -1; +static int hf_iax2_cap_adpcm = -1; +static int hf_iax2_cap_slinear = -1; +static int hf_iax2_cap_lpc10 = -1; +static int hf_iax2_cap_g729a = -1; +static int hf_iax2_cap_speex = -1; +static int hf_iax2_cap_ilbc = -1; +static int hf_iax2_cap_jpeg = -1; +static int hf_iax2_cap_png = -1; +static int hf_iax2_cap_h261 = -1; +static int hf_iax2_cap_h263 = -1; + +static int hf_IAX_IE_APPARENTADDR_SINFAMILY = -1; +static int hf_IAX_IE_APPARENTADDR_SINPORT = -1; +static int hf_IAX_IE_APPARENTADDR_SINADDR = -1; +static int hf_IAX_IE_APPARENTADDR_SINZERO = -1; +static int hf_IAX_IE_CALLED_NUMBER = -1; +static int hf_IAX_IE_CALLING_NUMBER = -1; +static int hf_IAX_IE_CALLING_ANI = -1; +static int hf_IAX_IE_CALLING_NAME = -1; +static int hf_IAX_IE_CALLED_CONTEXT = -1; +static int hf_IAX_IE_USERNAME = -1; +static int hf_IAX_IE_PASSWORD = -1; +static int hf_IAX_IE_CAPABILITY = -1; +static int hf_IAX_IE_FORMAT = -1; +static int hf_IAX_IE_LANGUAGE = -1; +static int hf_IAX_IE_VERSION = -1; +static int hf_IAX_IE_ADSICPE = -1; +static int hf_IAX_IE_DNID = -1; +static int hf_IAX_IE_AUTHMETHODS = -1; +static int hf_IAX_IE_CHALLENGE = -1; +static int hf_IAX_IE_MD5_RESULT = -1; +static int hf_IAX_IE_RSA_RESULT = -1; +static int hf_IAX_IE_REFRESH = -1; +static int hf_IAX_IE_DPSTATUS = -1; +static int hf_IAX_IE_CALLNO = -1; +static int hf_IAX_IE_CAUSE = -1; +static int hf_IAX_IE_IAX_UNKNOWN = -1; +static int hf_IAX_IE_MSGCOUNT = -1; +static int hf_IAX_IE_AUTOANSWER = -1; +static int hf_IAX_IE_MUSICONHOLD = -1; +static int hf_IAX_IE_TRANSFERID = -1; +static int hf_IAX_IE_RDNIS = -1; +static int hf_IAX_IE_DATAFORMAT = -1; +static int hf_IAX_IE_UNKNOWN_BYTE = -1; +static int hf_IAX_IE_UNKNOWN_I16 = -1; +static int hf_IAX_IE_UNKNOWN_I32 = -1; +static int hf_IAX_IE_UNKNOWN_BYTES = -1; + +/* These are the ids of the subtrees that we may be creating */ +static gint ett_iax2 = -1; +static gint ett_iax2_full_mini_subtree = -1; +static gint ett_iax2_type = -1; /* Frame-type specific subtree */ +static gint ett_iax2_ie = -1; /* single IE */ +static gint ett_iax2_codecs = -1; /* capabilities IE */ +static gint ett_iax2_ies_apparent_addr = -1; /* apparent address IE */ + +static dissector_handle_t data_handle; + +/* data-call subdissectors, AST_DATAFORMAT_* */ +static dissector_table_t iax2_dataformat_dissector_table; +/* voice/video call subdissectors, AST_FORMAT_* */ +static dissector_table_t iax2_codec_dissector_table; + +/* IAX2 Full-frame types */ +static const value_string iax_frame_types[] = { + {0, "(0?)"}, + {1, "DTMF"}, + {2, "Voice"}, + {3, "Video"}, + {4, "Control"}, + {5, "NULL"}, + {6, "IAX"}, + {7, "Text"}, + {8, "Image"}, + {0,NULL} +}; + +/* Subclasses for IAX packets */ +static const value_string iax_iax_subclasses[] = { + {0, "(0?)"}, + {1, "NEW"}, + {2, "PING"}, + {3, "PONG"}, + {4, "ACK"}, + {5, "HANGUP"}, + {6, "REJECT"}, + {7, "ACCEPT"}, + {8, "AUTHREQ"}, + {9, "AUTHREP"}, + {10, "INVAL"}, + {11, "LAGRQ"}, + {12, "LAGRP"}, + {13, "REGREQ"}, + {14, "REGAUTH"}, + {15, "REGACK"}, + {16, "REGREJ"}, + {17, "REGREL"}, + {18, "VNAK"}, + {19, "DPREQ"}, + {20, "DPREP"}, + {21, "DIAL"}, + {22, "TXREQ"}, + {23, "TXCNT"}, + {24, "TXACC"}, + {25, "TXREADY"}, + {26, "TXREL"}, + {27, "TXREJ"}, + {28, "QUELCH"}, + {29, "UNQULCH"}, + {30, "POKE"}, + {31, "PAGE"}, + {32, "MWI"}, + {33, "UNSUPPORTED"}, + {34, "TRANSFER"}, + {0,NULL} +}; + +/* Subclassess for Control packets */ +static const value_string iax_cmd_subclasses[] = { + {0, "(0?)"}, + {1, "HANGUP"}, + {2, "RING"}, + {3, "RINGING"}, + {4, "ANSWER"}, + {5, "BUSY"}, + {6, "TKOFFHK"}, + {7, "OFFHOOK"}, + {0xFF, "stop sounds"}, /* sent by app_dial, and not much else */ + {0,NULL} +}; + +/* Information elements */ +static const value_string iax_ies_type[] = { + {IAX_IE_CALLED_NUMBER, "Number/extension being called"}, + {IAX_IE_CALLING_NUMBER, "Calling number"}, + {IAX_IE_CALLING_ANI, "Calling number ANI for billing"}, + {IAX_IE_CALLING_NAME, "Name of caller"}, + {IAX_IE_CALLED_CONTEXT, "Context for number"}, + {IAX_IE_USERNAME, "Username (peer or user) for authentication"}, + {IAX_IE_PASSWORD, "Password for authentication"}, + {IAX_IE_CAPABILITY, "Actual codec capability"}, + {IAX_IE_FORMAT, "Desired codec format"}, + {IAX_IE_LANGUAGE, "Desired language"}, + {IAX_IE_VERSION, "Protocol version"}, + {IAX_IE_ADSICPE, "CPE ADSI capability"}, + {IAX_IE_DNID, "Originally dialed DNID"}, + {IAX_IE_AUTHMETHODS, "Authentication method(s)"}, + {IAX_IE_CHALLENGE, "Challenge data for MD5/RSA"}, + {IAX_IE_MD5_RESULT, "MD5 challenge result"}, + {IAX_IE_RSA_RESULT, "RSA challenge result"}, + {IAX_IE_APPARENT_ADDR, "Apparent address of peer"}, + {IAX_IE_REFRESH, "When to refresh registration"}, + {IAX_IE_DPSTATUS, "Dialplan status"}, + {IAX_IE_CALLNO, "Call number of peer"}, + {IAX_IE_CAUSE, "Cause"}, + {IAX_IE_IAX_UNKNOWN, "Unknown IAX command"}, + {IAX_IE_MSGCOUNT, "How many messages waiting"}, + {IAX_IE_AUTOANSWER, "Request auto-answering"}, + {IAX_IE_MUSICONHOLD, "Request musiconhold with QUELCH"}, + {IAX_IE_TRANSFERID, "Transfer Request Identifier"}, + {IAX_IE_RDNIS, "Referring DNIS"}, + {IAX_IE_PROVISIONING, "Provisioning info"}, + {IAX_IE_AESPROVISIONING, "AES Provisioning info"}, + {IAX_IE_DATETIME,"Date/Time"}, + {IAX_IE_DATAFORMAT, "Data call format"}, + {0,NULL} +}; + +static const value_string codec_types[] = { + {AST_FORMAT_G723_1, "G.723.1 compression"}, + {AST_FORMAT_GSM, "GSM compression"}, + {AST_FORMAT_ULAW, "Raw mu-law data (G.711)"}, + {AST_FORMAT_ALAW, "Raw A-law data (G.711)"}, + {AST_FORMAT_G726, "ADPCM (G.726, 32kbps)"}, + {AST_FORMAT_ADPCM, "ADPCM (IMA)"}, + {AST_FORMAT_SLINEAR, "Raw 16-bit Signed Linear (8000 Hz) PCM"}, + {AST_FORMAT_LPC10, "LPC10, 180 samples/frame"}, + {AST_FORMAT_G729A, "G.729a Audio"}, + {AST_FORMAT_SPEEX, "SpeeX Free Compression"}, + {AST_FORMAT_ILBC, "iLBC Free Compression"}, + {AST_FORMAT_JPEG, "JPEG Images"}, + {AST_FORMAT_PNG, "PNG Images"}, + {AST_FORMAT_H261, "H.261 Video"}, + {AST_FORMAT_H263, "H.263 Video"}, + {0,NULL} +}; + +static const value_string iax_dataformats[] = { + {AST_DATAFORMAT_NULL, "N/A (analogue call?)"}, + {AST_DATAFORMAT_V110, "ITU-T V.110 rate adaption"}, + {AST_DATAFORMAT_H223_H245,"ITU-T H.223/H.245"}, + {0,NULL} +}; + +typedef enum { + IAX2_MINI_VOICE_PACKET, + IAX2_FULL_PACKET, + IAX2_MINI_VIDEO_PACKET, + IAX2_META_PACKET +} packet_type; + +static const value_string iax_packet_types[] = { + {IAX2_FULL_PACKET, "Full packet"}, + {IAX2_MINI_VOICE_PACKET, "Mini voice packet"}, + {IAX2_MINI_VIDEO_PACKET, "Mini video packet"}, + {IAX2_META_PACKET, "Meta packet"}, + {0,NULL} +}; + + +/* ************************************************************************* */ + +/* In order to track IAX calls, we have a hash table which maps + * {addr,port type,port,call} to a unique circuit id. + * + * Each call has two such circuits associated with it (a forward and a + * reverse circuit, where 'forward' is defined as the direction the NEW + * packet went in), and we maintain an iax_call_data structure for each + * call, attached to both circuits with circuit_add_proto_data. + * + * Because {addr,port type,port,call} quadruplets can be reused + * (Asterisk reuses call numbers), circuit ids aren't unique to + * individual calls and we treat NEW packets somewhat specially. When we + * get such a packet, we see if there are any calls with a matching + * circuit id, and make sure that its circuits are marked as ended + * before that packet. + * + * A second complication is that we only know one quadruplet at the time + * the NEW packet is processed: there is therefore cunningness in + * iax_lookup_circuit_details() to look for replies to NEW packets and + * create the reverse circuit. + */ + + +/* start with a hash of {addr,port type,port,call}->{id} */ + +typedef struct { + address addr; + port_type ptype; + guint32 port; + guint32 callno; +} iax_circuit_key; + +/* tables */ +static GHashTable *iax_circuit_hashtab = NULL; +static GMemChunk *iax_circuit_keys = NULL; +static GMemChunk *iax_circuit_vals = NULL; +static guint circuitcount = 0; + +/* the number of keys and values to reserve space for in each memory chunk. + We assume we won't be tracking many calls at once so this is quite low. +*/ +#define IAX_INIT_PACKET_COUNT 10 + +#ifdef DEBUG_HASHING +static gchar *key_to_str( const iax_circuit_key *key ) +{ + static int i=0; + static gchar *strp, str[3][80]; + + i++; + if(i>=3){ + i=0; + } + strp=str[i]; + + /* why doesn't address_to_str take a const pointer? + cast the warnings into oblivion. */ + + sprintf(strp,"{%s:%i,%i}", + address_to_str((address *)&key->addr), + key->port, + key->callno); + return strp; +} +#endif + +/* Hash Functions */ +static gint iax_circuit_equal(gconstpointer v, gconstpointer w) +{ + const iax_circuit_key *v1 = (const iax_circuit_key *)v; + const iax_circuit_key *v2 = (const iax_circuit_key *)w; + gint result; + + result = ( ADDRESSES_EQUAL(&(v1->addr), &(v2->addr)) && + v1->ptype == v2->ptype && + v1->port == v2->port && + v1->callno== v2->callno); +#ifdef DEBUG_HASHING + g_message( "+++ Comparing for equality: %s, %s: %u",key_to_str(v1), key_to_str(v2), result); +#endif + + return result;; +} + +static guint iax_circuit_hash (gconstpointer v) +{ + const iax_circuit_key *key = (const iax_circuit_key *)v; + guint hash_val; + int i; + + hash_val = 0; + for (i = 0; i < key->addr.len; i++) + hash_val += (guint)(key->addr.data[i]); + + hash_val += (guint)(key->ptype); + hash_val += (guint)(key->port); + hash_val += (guint)(key->callno); + +#ifdef DEBUG_HASHING + g_message( "+++ Hashing key: %s, result %#x", key_to_str(key), hash_val ); +#endif + + return (guint) hash_val; +} + +static guint iax_circuit_lookup(const address *address, + port_type ptype, + guint32 port, + guint32 callno) +{ + iax_circuit_key key; + guint32 *circuit_id_p; + + key.addr = *address; + key.ptype = ptype; + key.port = port; + key.callno = callno; + +#ifdef DEBUG_HASHING + g_message( "+++ looking up key: %s", key_to_str(&key)); +#endif + + circuit_id_p = g_hash_table_lookup( iax_circuit_hashtab, &key); + if( ! circuit_id_p ) { + iax_circuit_key *new_key; + + new_key = g_mem_chunk_alloc(iax_circuit_keys); + COPY_ADDRESS(&new_key->addr, address); + new_key->ptype = ptype; + new_key->port = port; + new_key->callno = callno; + + circuit_id_p = g_mem_chunk_alloc(iax_circuit_vals); + *circuit_id_p = ++circuitcount; + + g_hash_table_insert(iax_circuit_hashtab, new_key, circuit_id_p); + } + +#ifdef DEBUG_HASHING + g_message( "+++ Id: %u", *circuit_id_p ); +#endif + + return *circuit_id_p; +} + + +/* ************************************************************************* */ + + +/* This is our per-call data structure, which is attached to both the + * forward and reverse circuits. + */ +typedef struct iax_call_data { + /* For this data, src and dst are relative to the original direction under + which this call is stored. Obviously if the reversed flag is set true by + iax_find_call, src and dst are reversed relative to the direction the + actual source and destination of the data. + + if the codec changes mid-call, we update it here; because we store a codec + number with each packet too, we handle going back to earlier packets + without problem. + */ + + iax_dataformat_t dataformat; + guint32 src_codec, dst_codec; + guint32 src_vformat, dst_vformat; + + guint forward_circuit_id; + guint reverse_circuit_id; + + guint callno; +} iax_call_data; + +static guint callcount = 0; + +static GMemChunk *iax_call_datas = NULL; + +static void iax_init_hash( void ) +{ + if (iax_circuit_hashtab) + g_hash_table_destroy(iax_circuit_hashtab); + + if (iax_circuit_keys) + g_mem_chunk_destroy(iax_circuit_keys); + if (iax_circuit_vals) + g_mem_chunk_destroy(iax_circuit_vals); + if (iax_call_datas) + g_mem_chunk_destroy(iax_call_datas); + + iax_circuit_hashtab = g_hash_table_new(iax_circuit_hash, iax_circuit_equal); + + iax_circuit_keys = g_mem_chunk_create(iax_circuit_key, + 2*IAX_INIT_PACKET_COUNT, + G_ALLOC_ONLY); + iax_circuit_vals = g_mem_chunk_create(iax_circuit_key, + 2*IAX_INIT_PACKET_COUNT, + G_ALLOC_ONLY); + + iax_call_datas = g_mem_chunk_create(iax_call_data, + IAX_INIT_PACKET_COUNT, + G_ALLOC_ONLY); + circuitcount = 0; + callcount = 0; +} + + +static iax_call_data *iax_lookup_circuit_details_from_dest( guint src_circuit_id, + guint dst_circuit_id, + guint framenum, + gboolean *reversed_p, + circuit_t **circuit_p) +{ + circuit_t *dst_circuit; + iax_call_data * iax_call; + gboolean reversed = FALSE; + + dst_circuit = find_circuit( CT_IAX2, + dst_circuit_id, + framenum ); + + if( !dst_circuit ) { +#ifdef DEBUG_HASHING + g_message( "++ destination circuit not found, must have missed NEW packet" ); +#endif + return NULL; + } + +#ifdef DEBUG_HASHING + g_message( "++ found destination circuit" ); +#endif + + iax_call = (iax_call_data *)circuit_get_proto_data(dst_circuit,proto_iax2); + + /* there's no way we can create a CT_IAX2 circuit without adding + iax call data to it; assert this */ + g_assert(iax_call); + + if( dst_circuit_id == iax_call -> forward_circuit_id ) { +#ifdef DEBUG_HASHING + g_message( "++ destination circuit matches forward_circuit_id of call, " + "therefore packet is reversed" ); +#endif + + reversed = TRUE; + + if( iax_call -> reverse_circuit_id == 0 ) { + circuit_t *rev_circuit; + + /* we are going in the reverse direction, and this call + doesn't have a reverse circuit associated with it. + create one now. */ +#ifdef DEBUG_HASHING + g_message( "++ reverse_circuit_id of call is zero, need to create a " + "new reverse circuit for this call" ); +#endif + + iax_call -> reverse_circuit_id = src_circuit_id; + rev_circuit = circuit_new(CT_IAX2, + src_circuit_id, + framenum ); + circuit_add_proto_data(rev_circuit, proto_iax2, iax_call); + + /* we should have already set up a subdissector for the forward + * circuit. we'll need to copy it to the reverse circuit. */ + circuit_set_dissector(rev_circuit, circuit_get_dissector(dst_circuit)); +#ifdef DEBUG_HASHING + g_message( "++ done" ); +#endif + } else if( iax_call -> reverse_circuit_id != src_circuit_id ) { + g_warning( "IAX Packet %u from circuit ids %u->%u" + "conflicts with earlier call with circuit ids %u->%u", + framenum, + src_circuit_id,dst_circuit_id, + iax_call->forward_circuit_id, + iax_call->reverse_circuit_id); + return NULL; + } + } else if ( dst_circuit_id == iax_call -> reverse_circuit_id ) { +#ifdef DEBUG_HASHING + g_message( "++ destination circuit matches reverse_circuit_id of call, " + "therefore packet is forward" ); +#endif + + reversed = FALSE; + if( iax_call -> forward_circuit_id != src_circuit_id ) { + g_warning( "IAX Packet %u from circuit ids %u->%u" + "conflicts with earlier call with circuit ids %u->%u", + framenum, + src_circuit_id,dst_circuit_id, + iax_call->forward_circuit_id, + iax_call->reverse_circuit_id); + return NULL; + } + } else { + g_assert_not_reached(); + } + + + if( circuit_p ) { + /* by now we've created a new circuit if one was necessary, or + bailed out if it looks like a conflict, and we should be able + to look up the source circuit without issue */ + *circuit_p = find_circuit( CT_IAX2, + src_circuit_id, + framenum ); + g_assert(*circuit_p); + } + + if( reversed_p ) + *reversed_p = reversed; + + return iax_call; +} + + + /* looks up a circuit_t and an iax_call for this packet */ +static iax_call_data *iax_lookup_circuit_details( packet_info *pinfo, + guint32 scallno, + guint32 dcallno, + gboolean *reversed_p, + circuit_t **circuit_p) +{ + gboolean reversed = FALSE; + iax_call_data *iax_call = NULL; + guint src_circuit_id; + circuit_t *src_circuit = NULL; + +#ifdef DEBUG_HASHING + g_message( "++ iax_lookup_circuit_details: Looking up circuit for frame %u, " + "from {%s:%u:%u} to {%s:%u:%u}", pinfo->fd->num, + address_to_str(&pinfo->src),pinfo->srcport,scallno, + address_to_str(&pinfo->dst),pinfo->destport,dcallno); +#endif + + + src_circuit_id = iax_circuit_lookup(&pinfo->src,pinfo->ptype, + pinfo->srcport,scallno); + + + /* the most reliable indicator of call is the destination callno, if + we have one */ + if( dcallno != 0 ) { + guint dst_circuit_id; +#ifdef DEBUG_HASHING + g_message( "++ dcallno non-zero, looking up destination circuit" ); +#endif + + dst_circuit_id = iax_circuit_lookup(&pinfo->dst,pinfo->ptype, + pinfo->destport,dcallno); + + iax_call = iax_lookup_circuit_details_from_dest(src_circuit_id, dst_circuit_id, pinfo->fd->num, &reversed, &src_circuit); + } else { + + /* in all other circumstances, the source circuit should already + * exist: its absense indicates that we missed the all-important NEW + * packet. + */ + + src_circuit = find_circuit( CT_IAX2, + src_circuit_id, + pinfo->fd->num ); + + if( src_circuit ) { + iax_call = (iax_call_data *)circuit_get_proto_data(src_circuit,proto_iax2); + + /* there's no way we can create a CT_IAX2 circuit without adding + iax call data to it; assert this */ + g_assert(iax_call); + + if( src_circuit_id == iax_call -> forward_circuit_id ) + reversed = FALSE; + else if ( src_circuit_id == iax_call -> reverse_circuit_id ) + reversed = TRUE; + else { + /* there's also no way we can attach an iax_call_data to a circuit + without the circuit being either the forward or reverse circuit + for that call; assert this too. + */ + g_assert_not_reached(); + } + } + } + + if(src_circuit && iax_call) { + /* info for subdissectors. We always pass on the forward circuit, + * and steal the p2p_dir flag to indicate the direction */ + pinfo -> ctype = CT_IAX2; + pinfo -> circuit_id = (guint32)iax_call->forward_circuit_id; + pinfo -> p2p_dir = reversed?P2P_DIR_RECV:P2P_DIR_SENT; + } + + if(reversed_p) + *reversed_p = reversed; + + if(circuit_p) + *circuit_p = src_circuit; + +#ifdef DEBUG_HASHING + if( iax_call ) { + g_message( "++ Found call for packet: id %u, reversed=%c", iax_call->callno, reversed?'1':'0' ); + } else { + g_message( "++ Call not found. Must have missed the NEW packet?" ); + } +#endif + + return iax_call; +} + + +/* handles a NEW packet by creating a new iax call and forward circuit. + the reverse circuit is not created until the ACK is received and + is created by iax_lookup_circuit_details. */ +static iax_call_data *iax_new_circuit_details( packet_info *pinfo, + guint32 scallno, + circuit_t **circuit_p) +{ + circuit_t *circuit; + iax_call_data *call; + guint circuit_id; + +#ifdef DEBUG_HASHING + g_message( "+ new_circuit: Handling NEW packet, frame %u", pinfo->fd->num ); +#endif + + circuit_id = iax_circuit_lookup(&pinfo->src,pinfo->ptype, + pinfo->srcport,scallno); + + circuit = circuit_new(CT_IAX2, + circuit_id, + pinfo->fd->num ); + + + + call = g_mem_chunk_alloc(iax_call_datas); + call -> dataformat = 0; + call -> src_codec = 0; + call -> dst_codec = 0; + call -> forward_circuit_id = circuit_id; + call -> reverse_circuit_id = 0; + call -> callno = ++callcount; + +#ifdef DEBUG_HASHING + g_message( "+ new_circuit: Added new circuit for new call %u", call -> callno ); +#endif + + circuit_add_proto_data( circuit, proto_iax2, call ); + + if( circuit_p ) + *circuit_p = circuit; + + return call; +} + + +/* ************************************************************************* */ + +/* per-packet data */ +typedef struct iax_packet_data { + iax_call_data *call_data; + guint32 codec; +} iax_packet_data; + +static GMemChunk *iax_packets = NULL; + +static iax_packet_data *iax_new_packet_data(iax_call_data *call) +{ + iax_packet_data *p = g_mem_chunk_alloc(iax_packets); + p->call_data=call; + p->codec=0; + return p; +} + + +/* ************************************************************************* */ + +static guint32 dissect_fullpacket (tvbuff_t * tvb, guint32 offset, + guint16 scallno, + packet_info * pinfo, + proto_tree * iax2_tree, + proto_tree * main_tree); + + +static guint32 dissect_minipacket (tvbuff_t * tvb, guint32 offset, + guint16 scallno, + packet_info * pinfo, + proto_tree * iax2_tree, + proto_tree * main_tree); + +static guint32 dissect_minivideopacket (tvbuff_t * tvb, guint32 offset, + guint16 scallno, + packet_info * pinfo, + proto_tree * iax2_tree, + proto_tree * main_tree); + +static void dissect_payload(tvbuff_t *tvb, guint32 offset, + packet_info *pinfo, proto_tree *tree, + guint32 ts, gboolean video, + iax_packet_data *iax_packet); + + + +static void +dissect_iax2 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree) +{ + proto_item *iax2_item = NULL; + proto_tree *iax2_tree = NULL; + proto_tree *full_mini_subtree = NULL; + guint32 offset = 0, len; + guint16 scallno = 0; + guint16 stmp; + packet_type type; + + /* set up the protocol and info fields in the summary pane */ + if (check_col (pinfo->cinfo, COL_PROTOCOL)) + { + col_set_str (pinfo->cinfo, COL_PROTOCOL, PROTO_TAG_IAX2); + } + if (check_col (pinfo->cinfo, COL_INFO)) + { + col_clear (pinfo->cinfo, COL_INFO); + } + + /* add the 'iax2' tree to the main tree */ + if (tree) + { + iax2_item = proto_tree_add_item (tree, proto_iax2, tvb, offset, -1, FALSE); + iax2_tree = proto_item_add_subtree (iax2_item, ett_iax2); + } + + stmp = tvb_get_ntohs(tvb, offset); + if( stmp == 0 ) { + /* starting with 0x0000 indicates either a mini video packet or a 'meta' + * packet, whatever that means */ + offset+=2; + stmp = tvb_get_ntohs(tvb, offset); + if( stmp & 0x8000 ) { + /* mini video packet */ + type = IAX2_MINI_VIDEO_PACKET; + scallno = stmp & 0x7FFF; + offset += 2; + } + else { + type = IAX2_META_PACKET; + } + } else { + /* The source call/fullpacket flag is common to both mini and full packets */ + scallno = tvb_get_ntohs(tvb, offset); + offset += 2; + if( scallno & 0x8000 ) + type = IAX2_FULL_PACKET; + else { + type = IAX2_MINI_VOICE_PACKET; + } + scallno &= 0x7FFF; + } + + if( tree ) { + proto_item *full_mini_base; + + full_mini_base = proto_tree_add_uint(iax2_tree, hf_iax2_packet_type, tvb, 0, offset, type); + full_mini_subtree = proto_item_add_subtree(full_mini_base, ett_iax2_full_mini_subtree); + + if( scallno != 0 ) + proto_tree_add_item (full_mini_subtree, hf_iax2_scallno, tvb, offset-2, 2, FALSE); + } + + switch( type ) { + case IAX2_FULL_PACKET: + len = dissect_fullpacket( tvb, offset, scallno, pinfo, full_mini_subtree, tree ); + break; + case IAX2_MINI_VOICE_PACKET: + len = dissect_minipacket( tvb, offset, scallno, pinfo, full_mini_subtree, tree ); + break; + case IAX2_MINI_VIDEO_PACKET: + len = dissect_minivideopacket( tvb, offset, scallno, pinfo, full_mini_subtree, tree ); + break; + case IAX2_META_PACKET: + /* not implemented yet */ + len = 0; + break; + default: + len = 0; + } + + /* update the 'length' of the main IAX2 header field so that it covers just the headers, + not the audio data. */ + proto_item_set_len(iax2_item, len); +} + + +/* dissect the information elements in an IAX frame. Returns the updated offset */ +static guint32 dissect_ies (tvbuff_t * tvb, guint32 offset, + proto_tree * iax_tree, + iax_call_data *iax_call_data ) +{ + proto_tree *sockaddr_tree = NULL; + proto_item *sockaddr_item = 0; + + + while (offset < tvb_reported_length (tvb)) { + + int ies_type = tvb_get_guint8(tvb, offset); + int ies_len = tvb_get_guint8(tvb, offset + 1); + + if( iax_tree ) { + proto_item *ti; + proto_tree *ies_tree; + + ti = proto_tree_add_text(iax_tree, tvb, offset, ies_len+2, + "Information Element: %s (0x%02X)", + val_to_str(ies_type, iax_ies_type, + "Unknown information element"), + ies_type); + + + ies_tree = proto_item_add_subtree(ti, ett_iax2_ie); + + proto_tree_add_text(ies_tree, tvb, offset, 1, "IE id: %s (0x%02X)", + val_to_str(ies_type, iax_ies_type, "Unknown"), + ies_type); + + proto_tree_add_text(ies_tree, tvb, offset+1, 1, "Length: %u",ies_len); + + + switch (ies_type) { + case IAX_IE_CALLED_NUMBER: + proto_tree_add_item (ies_tree, hf_IAX_IE_CALLED_NUMBER, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_CALLING_NUMBER: + proto_tree_add_item (ies_tree, hf_IAX_IE_CALLING_NUMBER, + tvb, offset + 2, ies_len, FALSE); + break; + case IAX_IE_CALLING_ANI: + proto_tree_add_item (ies_tree, hf_IAX_IE_CALLING_ANI, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_CALLING_NAME: + proto_tree_add_item (ies_tree, hf_IAX_IE_CALLING_NAME, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_CALLED_CONTEXT: + proto_tree_add_item (ies_tree, hf_IAX_IE_CALLED_CONTEXT, + tvb, offset + 2, ies_len, FALSE); + break; + case IAX_IE_USERNAME: + proto_tree_add_item (ies_tree, hf_IAX_IE_USERNAME, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_PASSWORD: + proto_tree_add_item (ies_tree, hf_IAX_IE_PASSWORD, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_LANGUAGE: + proto_tree_add_item (ies_tree, hf_IAX_IE_LANGUAGE, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_DNID: + proto_tree_add_item (ies_tree, hf_IAX_IE_DNID, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_CHALLENGE: + proto_tree_add_item (ies_tree, hf_IAX_IE_CHALLENGE, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_MD5_RESULT: + proto_tree_add_item (ies_tree, hf_IAX_IE_MD5_RESULT, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_RSA_RESULT: + proto_tree_add_item (ies_tree, hf_IAX_IE_RSA_RESULT, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_RDNIS: + proto_tree_add_item (ies_tree, hf_IAX_IE_RDNIS, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_CAPABILITY: + { + proto_tree *codec_tree; + proto_item *codec_base; + + codec_base = + proto_tree_add_item (ies_tree, hf_IAX_IE_CAPABILITY, + tvb, offset + 2, ies_len, FALSE); + codec_tree = + proto_item_add_subtree (codec_base, ett_iax2_codecs); + + proto_tree_add_item(codec_tree, hf_iax2_cap_g723_1, tvb, offset + 2, ies_len, FALSE ); + proto_tree_add_item(codec_tree, hf_iax2_cap_gsm, tvb, offset + 2, ies_len, FALSE ); + proto_tree_add_item(codec_tree, hf_iax2_cap_ulaw, tvb, offset + 2, ies_len, FALSE ); + proto_tree_add_item(codec_tree, hf_iax2_cap_alaw, tvb, offset + 2, ies_len, FALSE ); + proto_tree_add_item(codec_tree, hf_iax2_cap_g726, tvb, offset + 2, ies_len, FALSE ); + proto_tree_add_item(codec_tree, hf_iax2_cap_adpcm, tvb, offset + 2, ies_len, FALSE ); + proto_tree_add_item(codec_tree, hf_iax2_cap_slinear, tvb, offset + 2, ies_len, FALSE ); + proto_tree_add_item(codec_tree, hf_iax2_cap_lpc10, tvb, offset + 2, ies_len, FALSE ); + proto_tree_add_item(codec_tree, hf_iax2_cap_g729a, tvb, offset + 2, ies_len, FALSE ); + proto_tree_add_item(codec_tree, hf_iax2_cap_speex, tvb, offset + 2, ies_len, FALSE ); + proto_tree_add_item(codec_tree, hf_iax2_cap_ilbc, tvb, offset + 2, ies_len, FALSE ); + proto_tree_add_item(codec_tree, hf_iax2_cap_jpeg, tvb, offset + 2, ies_len, FALSE ); + proto_tree_add_item(codec_tree, hf_iax2_cap_png, tvb, offset + 2, ies_len, FALSE ); + proto_tree_add_item(codec_tree, hf_iax2_cap_h261, tvb, offset + 2, ies_len, FALSE ); + proto_tree_add_item(codec_tree, hf_iax2_cap_h263, tvb, offset + 2, ies_len, FALSE ); + break; + } + case IAX_IE_FORMAT: + proto_tree_add_item (ies_tree, hf_IAX_IE_FORMAT, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_VERSION: + proto_tree_add_item (ies_tree, hf_IAX_IE_VERSION, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_ADSICPE: + proto_tree_add_item (ies_tree, hf_IAX_IE_ADSICPE, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_AUTHMETHODS: + proto_tree_add_item (ies_tree, hf_IAX_IE_AUTHMETHODS, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_APPARENT_ADDR: + sockaddr_item = proto_tree_add_text(ies_tree, tvb, offset + 2, 16, "Apparent Address"); + sockaddr_tree = proto_item_add_subtree(sockaddr_item, ett_iax2_ies_apparent_addr); + proto_tree_add_item(sockaddr_tree, hf_IAX_IE_APPARENTADDR_SINADDR, tvb, offset + 6, 4, FALSE); + proto_tree_add_item(sockaddr_tree, hf_IAX_IE_APPARENTADDR_SINPORT, tvb, offset + 4, 2, FALSE); + break; + case IAX_IE_REFRESH: + proto_tree_add_item (ies_tree, hf_IAX_IE_REFRESH, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_DPSTATUS: + proto_tree_add_item (ies_tree, hf_IAX_IE_DPSTATUS, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_CALLNO: + proto_tree_add_item (ies_tree, hf_IAX_IE_CALLNO, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_CAUSE: + proto_tree_add_item (ies_tree, hf_IAX_IE_CAUSE, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_IAX_UNKNOWN: + proto_tree_add_item (ies_tree, hf_IAX_IE_IAX_UNKNOWN, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_MSGCOUNT: + proto_tree_add_item (ies_tree, hf_IAX_IE_MSGCOUNT, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_AUTOANSWER: + proto_tree_add_item (ies_tree, hf_IAX_IE_AUTOANSWER, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_MUSICONHOLD: + proto_tree_add_item (ies_tree, hf_IAX_IE_MUSICONHOLD, tvb, + offset + 2, ies_len, FALSE); + break; + case IAX_IE_TRANSFERID: + proto_tree_add_item (ies_tree, hf_IAX_IE_TRANSFERID, tvb, + offset + 2, ies_len, FALSE); + break; + + case IAX_IE_DATAFORMAT: + proto_tree_add_item (ies_tree, hf_IAX_IE_DATAFORMAT, tvb, + offset + 2, ies_len, FALSE); + + if( iax_call_data ) + iax_call_data -> dataformat = tvb_get_ntohl(tvb, offset+2); + + break; + + default: + { + switch(ies_len) { + case 1: + proto_tree_add_item( ies_tree, hf_IAX_IE_UNKNOWN_BYTE, tvb, offset+2, ies_len, FALSE ); + break; + + case 2: + proto_tree_add_item( ies_tree, hf_IAX_IE_UNKNOWN_I16, tvb, offset+2, ies_len, FALSE ); + break; + + case 4: + proto_tree_add_item( ies_tree, hf_IAX_IE_UNKNOWN_I32, tvb, offset+2, ies_len, FALSE ); + break; + + default: + proto_tree_add_item( ies_tree, hf_IAX_IE_UNKNOWN_BYTES, tvb, offset+2, ies_len, FALSE ); + } + } + } + } + offset += ies_len + 2; + } + return offset; +} + +static guint32 uncompress_subclass(guint8 csub) +{ + /* If the SC_LOG flag is set, return 2^csub otherwise csub */ + if (csub & 0x80) { + /* special case for 'compressed' -1 */ + if (csub == 0xff) + return (guint32)-1; + else + return 1 << (csub & 0x1F); + } + else + return (guint32)csub; +} + + +static guint32 +dissect_fullpacket (tvbuff_t * tvb, guint32 offset, + guint16 scallno, + packet_info * pinfo, proto_tree * iax2_tree, + proto_tree * main_tree) +{ + guint32 retransmission = 0; + guint16 dcallno; + guint32 ts; + guint8 type; + guint8 csub; + guint32 codec; + + proto_tree *packet_type_tree = NULL; + iax_call_data *iax_call; + iax_packet_data *iax_packet; + gboolean reversed; + gboolean rtp_marker; + + circuit_t *circuit; + + /* + * remove the top bit for retransmission detection + */ + dcallno = tvb_get_ntohs(tvb, offset); + retransmission = dcallno & 0x8000; + dcallno = dcallno & 0x7FFF; + ts = tvb_get_ntohl(tvb, offset+2); + type = tvb_get_guint8(tvb, offset + 8); + csub = tvb_get_guint8(tvb, offset + 9); + + /* see if we've seen this packet before */ + iax_packet = (iax_packet_data *)p_get_proto_data(pinfo->fd,proto_iax2); + if( !iax_packet ) { + /* if not, find or create an iax_call info structure for this IAX session. */ + + if( type == AST_FRAME_IAX && csub == IAX_COMMAND_NEW ) { + /* NEW packets start a new call */ + iax_call = iax_new_circuit_details(pinfo,scallno,&circuit); + reversed = FALSE; + } else { + iax_call = iax_lookup_circuit_details(pinfo, scallno, dcallno, + &reversed, &circuit); + } + + iax_packet = iax_new_packet_data(iax_call); + p_add_proto_data(pinfo->fd,proto_iax2,iax_packet); + } else { + iax_call = iax_packet->call_data; + } + + if( iax2_tree ) { + proto_item *packet_type_base; + + proto_tree_add_item (iax2_tree, hf_iax2_dcallno, tvb, offset, 2, FALSE ); + proto_tree_add_boolean(iax2_tree, hf_iax2_retransmission, tvb, offset, 2, FALSE ); + + proto_tree_add_uint (iax2_tree, hf_iax2_ts, tvb, offset+2, 4, ts); + + proto_tree_add_item (iax2_tree, hf_iax2_oseqno, tvb, offset+6, 1, + FALSE); + + proto_tree_add_item (iax2_tree, hf_iax2_iseqno, tvb, offset+7, 1, + FALSE); + packet_type_base = proto_tree_add_uint (iax2_tree, hf_iax2_type, tvb, + offset+8, 1, type); + + /* add the type-specific subtree */ + packet_type_tree = proto_item_add_subtree (packet_type_base, ett_iax2_type); + } + + /* add frame type to info line */ + if (check_col (pinfo->cinfo, COL_INFO)) { + col_add_fstr (pinfo->cinfo, COL_INFO, "%s, source call# %d, timestamp %ums", + val_to_str (type, iax_frame_types, "Unknown (0x%02x)"), + scallno, ts); + } + + switch( type ) { + case AST_FRAME_IAX: + /* add the subclass */ + proto_tree_add_uint (packet_type_tree, hf_iax2_iax_csub, tvb, + offset+9, 1, csub); + offset += 10; + + if (check_col (pinfo->cinfo, COL_INFO)) + col_append_fstr (pinfo->cinfo, COL_INFO, " %s", + val_to_str (csub, iax_iax_subclasses, "unknown (0x%02x)")); + + if (offset < tvb_reported_length (tvb)) { + offset += dissect_ies(tvb, offset, packet_type_tree, iax_call); + } + + if( csub == IAX_COMMAND_NEW && circuit && iax_call ) { + /* if this is a data call, set up a subdissector for the circuit */ + dissector_handle_t s; + s = dissector_get_port_handle(iax2_dataformat_dissector_table, iax_call -> dataformat ); + circuit_set_dissector( circuit, s ); + } + break; + + case AST_FRAME_DTMF: + proto_tree_add_text (packet_type_tree, tvb, offset+9, 1, "DTMF digit: %c", csub); + offset += 10; + + if (check_col (pinfo->cinfo, COL_INFO)) + col_append_fstr (pinfo->cinfo, COL_INFO, " digit %c", csub ); + break; + + case AST_FRAME_CONTROL: + /* add the subclass */ + proto_tree_add_uint (packet_type_tree, hf_iax2_cmd_csub, tvb, + offset+9, 1, csub); + offset += 10; + + if (check_col (pinfo->cinfo, COL_INFO)) + col_append_fstr (pinfo->cinfo, COL_INFO, " %s", + val_to_str (csub, iax_cmd_subclasses, "unknown (0x%02x)")); + break; + + case AST_FRAME_VOICE: + /* add the codec */ + iax_packet -> codec = codec = uncompress_subclass(csub); + + if( packet_type_tree ) { + proto_tree_add_item (packet_type_tree, hf_iax2_voice_csub, tvb, offset+9, 1, FALSE); + proto_tree_add_uint (packet_type_tree, hf_iax2_voice_codec, tvb, offset+9, 1, codec); + } + + offset += 10; + + if( iax_call ) { + if( reversed ) { + iax_call->dst_codec = codec; + } else { + iax_call->src_codec = codec; + } + } + + dissect_payload(tvb, offset, pinfo, main_tree, ts, FALSE,iax_packet); + break; + + case AST_FRAME_VIDEO: + /* bit 6 of the csub is used to represent the rtp 'marker' bit */ + rtp_marker = csub & 0x40 ? TRUE:FALSE; + iax_packet -> codec = codec = uncompress_subclass((guint8) (csub & ~40)); + + if( packet_type_tree ) { + proto_tree_add_item (packet_type_tree, hf_iax2_video_csub, tvb, offset+9, 1, FALSE); + proto_tree_add_item (packet_type_tree, hf_iax2_marker, tvb, offset+9, 1, FALSE); + proto_tree_add_uint (packet_type_tree, hf_iax2_video_codec, tvb, offset+9, 1, codec); + } + + offset += 10; + + if( iax_call ) { + if( reversed ) { + iax_call->dst_vformat = codec; + } else { + iax_call->src_vformat = codec; + } + } + + if( rtp_marker && check_col (pinfo->cinfo, COL_INFO)) + col_append_fstr (pinfo->cinfo, COL_INFO, ", Mark" ); + + + dissect_payload(tvb, offset, pinfo, main_tree, ts, TRUE, iax_packet); + break; + + + default: + proto_tree_add_uint (packet_type_tree, hf_iax2_csub, tvb, offset+9, + 1, csub); + offset += 10; + + if (check_col (pinfo->cinfo, COL_INFO)) + col_append_fstr (pinfo->cinfo, COL_INFO, " subclass %d", csub ); + break; + } + + return offset; +} + +static iax_packet_data *iax2_get_packet_data_for_minipacket(packet_info * pinfo, + guint16 scallno, + gboolean video) +{ + /* see if we've seen this packet before */ + iax_packet_data *p = (iax_packet_data *)p_get_proto_data(pinfo->fd,proto_iax2); + + if( !p ) { + /* if not, find or create an iax_call info structure for this IAX session. */ + gboolean reversed; + circuit_t *circuit; + iax_call_data *iax_call; + + iax_call = iax_lookup_circuit_details(pinfo, scallno, 0, &reversed, &circuit); + + p = iax_new_packet_data(iax_call); + p_add_proto_data(pinfo->fd,proto_iax2,p); + + /* set the codec for this frame to be whatever the last full frame used */ + if( video ) + p->codec = reversed ? iax_call -> dst_vformat : iax_call -> src_vformat; + else + p->codec = reversed ? iax_call -> dst_codec : iax_call -> src_codec; + } + return p; +} + + +static guint32 dissect_minivideopacket (tvbuff_t * tvb, guint32 offset, + guint16 scallno, packet_info * pinfo, + proto_tree * iax2_tree, proto_tree *main_tree) +{ + guint32 ts; + iax_packet_data *iax_packet; + gboolean rtp_marker; + + ts = tvb_get_ntohs(tvb, offset); + + /* bit 15 of the ts is used to represent the rtp 'marker' bit */ + rtp_marker = ts & 0x8000 ? TRUE:FALSE; + ts &= ~0x8000; + + + if( iax2_tree ) { + proto_tree_add_item (iax2_tree, hf_iax2_minividts, tvb, offset, 2, FALSE); + proto_tree_add_item (iax2_tree, hf_iax2_minividmarker, tvb, offset, 2, FALSE); + } + + offset += 2; + + iax_packet = iax2_get_packet_data_for_minipacket(pinfo, scallno, TRUE); + + if (check_col (pinfo->cinfo, COL_INFO)) + col_add_fstr (pinfo->cinfo, COL_INFO, + "Mini video packet, source call# %d, timestamp %ums%s", + scallno, ts, rtp_marker?", Mark":""); + + + dissect_payload(tvb, offset, pinfo, main_tree, ts, TRUE, iax_packet); + + return offset; +} + +static guint32 +dissect_minipacket (tvbuff_t * tvb, guint32 offset, guint16 scallno, packet_info * pinfo, proto_tree * iax2_tree, + proto_tree *main_tree) +{ + guint32 ts; + iax_packet_data *iax_packet; + + ts = tvb_get_ntohs(tvb, offset); + + iax_packet = iax2_get_packet_data_for_minipacket(pinfo, scallno, FALSE); + + proto_tree_add_uint (iax2_tree, hf_iax2_minits, tvb, offset, 2, + ts); + offset += 2; + + if (check_col (pinfo->cinfo, COL_INFO)) + col_add_fstr (pinfo->cinfo, COL_INFO, + "Mini packet, source call# %d, timestamp %ums", + scallno, ts); + + + /* XXX fix the timestamp logic */ + dissect_payload(tvb, offset, pinfo, main_tree, ts, FALSE, iax_packet); + + + return offset; +} + +static void dissect_payload(tvbuff_t *tvb, guint32 offset, + packet_info *pinfo, proto_tree *tree, + guint32 ts, gboolean video, + iax_packet_data *iax_packet) +{ + gboolean out_of_order = FALSE; + tvbuff_t *sub_tvb; + guint32 codec = iax_packet -> codec; + iax_call_data *iax_call = iax_packet -> call_data; + + /* keep compiler quiet */ + ts = ts; + + if( offset >= tvb_reported_length (tvb)) { + if (check_col (pinfo->cinfo, COL_INFO)) + col_append_fstr (pinfo->cinfo, COL_INFO, ", empty frame" ); + return; + } + + sub_tvb = tvb_new_subset(tvb, offset, -1, -1 ); + + /* XXX shouldn't pass through out-of-order packets. */ + + if (check_col (pinfo->cinfo, COL_INFO)) { + if( !video && iax_call && iax_call -> dataformat != 0 ) { + col_append_fstr (pinfo->cinfo, COL_INFO, ", data, format %s", + val_to_str (iax_call -> dataformat, + iax_dataformats, "unknown (0x%02x)")); + + if( out_of_order ) + col_append_fstr (pinfo->cinfo, COL_INFO, " (out-of-order packet)"); + } else { + col_append_fstr (pinfo->cinfo, COL_INFO, ", %s", + val_to_str (codec, codec_types, "unknown (0x%02x)")); + } + } + + /* pass the rest of the block to a subdissector */ + if( !video && try_circuit_dissector(pinfo->ctype, pinfo->circuit_id, pinfo->fd->num, + sub_tvb, pinfo, tree)) + return; + + if( codec != 0 && dissector_try_port(iax2_codec_dissector_table, codec, sub_tvb, pinfo, tree )) + return; + + /* we don't know how to dissect our data: dissect it as data */ + call_dissector(data_handle,sub_tvb, pinfo, tree); +} + +/* + * Init routines + */ + +/* called at the start of a capture. We should clear out our static, per-capture + * data. + */ + +static void +iax_init_protocol(void) +{ + iax_init_hash(); + + if (iax_packets) + g_mem_chunk_destroy(iax_packets); + iax_packets = g_mem_chunk_create(iax_packet_data,128,G_ALLOC_ONLY); +} + + +void +proto_register_iax2 (void) +{ + /* we use this for displaying which codecs are supported */ + static const true_false_string supported_strings = { + "Supported", + "Not supported" + }; + + /* A header field is something you can search/filter on. + * + * We create a structure to register our fields. It consists of an + * array of hf_register_info structures, each of which are of the format + * {&(field id), {name, abbrev, type, display, strings, bitmask, blurb, HFILL}}. + */ + + static hf_register_info hf[] = { + + {&hf_iax2_packet_type, + {"Packet type", "iax2.type", FT_UINT8, BASE_DEC, VALS(iax_packet_types), 0, + "Full/minivoice/minivideo/meta packet", + HFILL}}, + + + {&hf_iax2_scallno, + {"Source call", "iax2.src_call", FT_UINT16, BASE_DEC, NULL, 0x7FFF, + "src_call holds the number of this call at the packet source pbx", + HFILL}}, + + /* FIXME could this be turned into a FRAMENUM field? */ + {&hf_iax2_dcallno, + {"Destination call", "iax2.dst_call", FT_UINT16, BASE_DEC, NULL, 0x7FFF, + "dst_call holds the number of this call at the packet destination", + HFILL}}, + + {&hf_iax2_retransmission, + {"Retransmission", "iax2.retransmission", FT_BOOLEAN, 16, + NULL, 0x8000, + "retransmission is set if this packet is a retransmission of an earlier " + "failed packet", HFILL}}, + + {&hf_iax2_ts, + {"Timestamp", "iax2.timestamp", FT_UINT32, BASE_DEC, NULL, 0x0, + "timestamp is the time, in ms after the start of this call, at which " + "this packet was transmitted", + HFILL}}, + + {&hf_iax2_minits, + {"Timestamp", "iax2.timestamp", FT_UINT16, BASE_DEC, NULL, 0x0, + "timestamp is the time, in ms after the start of this call, at which " + "this packet was transmitted", + HFILL}}, + + {&hf_iax2_minividts, + {"Timestamp", "iax2.timestamp", FT_UINT16, BASE_DEC, NULL, 0x7FFF, + "timestamp is the time, in ms after the start of this call, at which " + "this packet was transmitted", + HFILL}}, + + {&hf_iax2_minividmarker, + {"Marker", "iax2.video.marker", FT_UINT16, BASE_DEC, NULL, 0x8000, + "RTP end-of-frame marker", + HFILL}}, + + {&hf_iax2_oseqno, + {"Outbound seq.no.", "iax2.oseqno", FT_UINT16, BASE_DEC, NULL, + 0x0, + "oseqno is the sequence no of this packet. The first packet has " + "oseqno==0, and subsequent packets increment the oseqno by 1", + HFILL}}, + + {&hf_iax2_iseqno, + {"Inbound seq.no.", "iax2.iseqno", FT_UINT16, BASE_DEC, NULL, 0x0, + "iseqno is the sequence no of the last successfully recieved packet", + HFILL}}, + + {&hf_iax2_type, + {"Type", "iax2.type", FT_UINT8, BASE_DEC, VALS (iax_frame_types), + 0x0, + "For full IAX2 frames, type is the type of frame", + HFILL}}, + + {&hf_iax2_csub, + {"Sub-class", "iax2.subclass", FT_UINT8, BASE_DEC, NULL, 0x0, + "subclass", + HFILL}}, + + {&hf_iax2_cmd_csub, + {"Control subclass", "iax2.control.subclass", FT_UINT8, BASE_DEC, + VALS (iax_cmd_subclasses), 0x0, + "This gives the command number for a Control packet.", HFILL}}, + + {&hf_iax2_iax_csub, + {"IAX type", "iax2.iax.subclass", FT_UINT8, BASE_DEC, + VALS (iax_iax_subclasses), + 0x0, + "IAX type gives the command number for IAX signalling packets", HFILL}}, + + {&hf_iax2_voice_csub, + {"Sub-class", "iax2.voice.subclass", FT_UINT8, BASE_DEC, NULL, 0x0, + "subclass", + HFILL}}, + + {&hf_iax2_voice_codec, + {"CODEC", "iax2.voice.codec", FT_UINT32, BASE_HEX, VALS (codec_types), + 0x0, + "CODEC gives the codec used to encode audio data", HFILL}}, + + {&hf_iax2_video_csub, + {"Subclass (compressed codec no)", "iax2.video.subclass", FT_UINT8, BASE_DEC, NULL, 0xBF, + "Subclass (compressed codec no)", + HFILL}}, + + {&hf_iax2_marker, + {"Marker", "iax2.video.marker", FT_BOOLEAN, 8, NULL, 0x40, + "RTP end-of-frame marker", + HFILL}}, + + {&hf_iax2_video_codec, + {"CODEC", "iax2.video.codec", FT_UINT32, BASE_HEX, VALS (codec_types), 0, + "The codec used to encode video data", HFILL}}, + + /* + * Decoding for the ies + */ + + {&hf_IAX_IE_APPARENTADDR_SINFAMILY, + {"Family", "iax2.iax.app_addr.sinfamily", FT_UINT16, BASE_DEC, NULL, 0, "Family", HFILL }}, + {&hf_IAX_IE_APPARENTADDR_SINPORT, + {"Port", "iax2.iax.app_addr.sinport", FT_UINT16, BASE_DEC, NULL, 0, "Port", HFILL }}, + {&hf_IAX_IE_APPARENTADDR_SINADDR, + {"Address", "iax2.iax.app_addr.sinaddr", FT_IPv4, BASE_HEX, NULL, 0, "Address", HFILL }}, + {&hf_IAX_IE_APPARENTADDR_SINZERO, + {"Zero", "iax2.iax.app_addr.sinzero", FT_BYTES, BASE_HEX, NULL, 0, "Zero", HFILL }}, + + {&hf_IAX_IE_CALLED_NUMBER, + {"Number/extension being called", "iax2.iax.called_number", + FT_STRING, + BASE_NONE, NULL, 0x0, "", HFILL}}, + + {&hf_IAX_IE_CALLING_NUMBER, + {"Calling number", "iax2.iax.calling_number", FT_STRING, + BASE_NONE, NULL, + 0x0, "", HFILL}}, + + {&hf_IAX_IE_CALLING_ANI, + {"Calling number ANI for billing", "iax2.iax.calling_ani", + FT_STRING, + BASE_NONE, NULL, 0x0, "", HFILL}}, + + {&hf_IAX_IE_CALLING_NAME, + {"Name of caller", "iax2.iax.calling_name", FT_STRING, BASE_NONE, + NULL, + 0x0, "", HFILL}}, + + {&hf_IAX_IE_CALLED_CONTEXT, + {"Context for number", "iax2.iax.called_context", FT_STRING, + BASE_NONE, + NULL, 0x0, "", HFILL}}, + + {&hf_IAX_IE_USERNAME, + {"Username (peer or user) for authentication", + "iax2.iax.username", + FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL}}, + + {&hf_IAX_IE_PASSWORD, + {"Password for authentication", "iax2.iax.password", FT_STRING, + BASE_NONE, NULL, 0x0, "", HFILL}}, + + {&hf_IAX_IE_CAPABILITY, + {"Actual codec capability", "iax2.iax.capability", FT_UINT32, + BASE_HEX, + NULL, 0x0, "", HFILL}}, + + {&hf_IAX_IE_FORMAT, + {"Desired codec format", "iax2.iax.format", FT_UINT32, BASE_HEX, + VALS (codec_types), 0x0, "", HFILL}}, + + {&hf_IAX_IE_LANGUAGE, + {"Desired language", "iax2.iax.language", FT_STRING, BASE_NONE, + NULL, + 0x0, "", HFILL}}, + + {&hf_IAX_IE_VERSION, + {"Protocol version", "iax2.iax.version", FT_UINT16, BASE_HEX, NULL, + 0x0, + "", HFILL}}, + + {&hf_IAX_IE_ADSICPE, + {"CPE ADSI capability", "iax2.iax.cpe_adsi", FT_UINT16, BASE_HEX, + NULL, + 0x0, "", HFILL}}, + + {&hf_IAX_IE_DNID, + {"Originally dialed DNID", "iax2.iax.dnid", FT_STRING, BASE_NONE, + NULL, + 0x0, "", HFILL}}, + + {&hf_IAX_IE_AUTHMETHODS, + {"Authentication method(s)", "iax2.iax.auth.methods", FT_UINT16, + BASE_HEX, + NULL, 0x0, "", HFILL}}, + + {&hf_IAX_IE_CHALLENGE, + {"Challenge data for MD5/RSA", "iax2.iax.auth.challenge", + FT_STRING, + BASE_NONE, NULL, 0x0, "", HFILL}}, + + {&hf_IAX_IE_MD5_RESULT, + {"MD5 challenge result", "iax2.iax.auth.md5", FT_STRING, + BASE_NONE, NULL, + 0x0, "", HFILL}}, + + {&hf_IAX_IE_RSA_RESULT, + {"RSA challenge result", "iax2.iax.auth.rsa", FT_STRING, + BASE_NONE, NULL, + 0x0, "", HFILL}}, + + {&hf_IAX_IE_REFRESH, + {"When to refresh registration", "iax2.iax.refresh", FT_INT16, + BASE_DEC, + NULL, 0x0, "", HFILL}}, + + {&hf_IAX_IE_DPSTATUS, + {"Dialplan status", "iax2.iax.dialplan_status", FT_UINT16, + BASE_HEX, NULL, + 0x0, "", HFILL}}, + + {&hf_IAX_IE_CALLNO, + {"Call number of peer", "iax2.iax.call_no", FT_INT16, BASE_DEC, + NULL, + 0x0, "", HFILL}}, + + {&hf_IAX_IE_CAUSE, + {"Cause", "iax2.iax.cause", FT_STRING, BASE_NONE, NULL, 0x0, "", + HFILL}}, + + {&hf_IAX_IE_IAX_UNKNOWN, + {"Unknown IAX command", "iax2.iax.iax_unknown", FT_BYTES, + BASE_HEX, NULL, + 0x0, "", HFILL}}, + + {&hf_IAX_IE_MSGCOUNT, + {"How many messages waiting", "iax2.iax.msg_count", FT_INT16, + BASE_DEC, + NULL, 0x0, "", HFILL}}, + + {&hf_IAX_IE_AUTOANSWER, + {"Request auto-answering", "iax2.iax.autoanswer", FT_NONE, + BASE_NONE, + NULL, 0x0, "", HFILL}}, + + {&hf_IAX_IE_MUSICONHOLD, + {"Request musiconhold with QUELCH", "iax2.iax.moh", FT_NONE, + BASE_NONE, + NULL, 0x0, "", HFILL}}, + + {&hf_IAX_IE_TRANSFERID, + {"Transfer Request Identifier", "iax2.iax.transferid", FT_UINT32, + BASE_HEX, NULL, 0x0, "", HFILL}}, + + {&hf_IAX_IE_RDNIS, + {"Referring DNIS", "iax2.iax.rdnis", FT_STRING, BASE_NONE, NULL, + 0x0, "", + HFILL}}, + + {&hf_IAX_IE_DATAFORMAT, + {"Data call format", "iax2.iax.dataformat", FT_UINT32, BASE_HEX, + VALS(iax_dataformats), 0x0, "", HFILL}}, + + {&hf_IAX_IE_UNKNOWN_BYTE, + {"data", "iax2.iax.unknowndata", FT_UINT8, BASE_HEX, NULL, + 0x0, "Raw data for unknown IEs", + HFILL}}, + {&hf_IAX_IE_UNKNOWN_I16, + {"data", "iax2.iax.unknowndata", FT_UINT16, BASE_HEX, NULL, + 0x0, "Raw data for unknown IEs", + HFILL}}, + {&hf_IAX_IE_UNKNOWN_I32, + {"data", "iax2.iax.unknowndata", FT_UINT32, BASE_HEX, NULL, + 0x0, "Raw data for unknown IEs", + HFILL}}, + {&hf_IAX_IE_UNKNOWN_BYTES, + {"data", "iax2.iax.unknowndata", FT_BYTES, BASE_NONE, NULL, + 0x0, "Raw data for unknown IEs", + HFILL}}, + + /* capablilites */ + {&hf_iax2_cap_g723_1, + {"G.723.1 compression", "iax2.cap.g723_1", FT_BOOLEAN, 32, + TFS(&supported_strings), AST_FORMAT_G723_1, + "G.723.1 compression", HFILL }}, + + {&hf_iax2_cap_gsm, + {"GSM compression", "iax2.cap.gsm", FT_BOOLEAN, 32, + TFS(&supported_strings), AST_FORMAT_GSM, + "GSM compression", HFILL }}, + + {&hf_iax2_cap_ulaw, + {"Raw mu-law data (G.711)", "iax2.cap.ulaw",FT_BOOLEAN, 32, + TFS(&supported_strings), AST_FORMAT_ULAW, + "Raw mu-law data (G.711)", HFILL }}, + + {&hf_iax2_cap_alaw, + {"Raw A-law data (G.711)", "iax2.cap.alaw",FT_BOOLEAN, 32, + TFS(&supported_strings), AST_FORMAT_ALAW, + "Raw A-law data (G.711)", HFILL }}, + + {&hf_iax2_cap_g726, + {"G.726 compression", "iax2.cap.g726",FT_BOOLEAN, 32, + TFS(&supported_strings), AST_FORMAT_G726, + "G.726 compression", HFILL }}, + + {&hf_iax2_cap_adpcm, + {"ADPCM", "iax2.cap.adpcm", FT_BOOLEAN, 32, + TFS(&supported_strings), AST_FORMAT_ADPCM, + "ADPCM", HFILL }}, + + {&hf_iax2_cap_slinear, + {"Raw 16-bit Signed Linear (8000 Hz) PCM", "iax2.cap.slinear", + FT_BOOLEAN, 32, TFS(&supported_strings), AST_FORMAT_SLINEAR, + "Raw 16-bit Signed Linear (8000 Hz) PCM", HFILL }}, + + {&hf_iax2_cap_lpc10, + {"LPC10, 180 samples/frame", "iax2.cap.lpc10", FT_BOOLEAN, 32, + TFS(&supported_strings), AST_FORMAT_LPC10, + "LPC10, 180 samples/frame", HFILL }}, + + {&hf_iax2_cap_g729a, + {"G.729a Audio", "iax2.cap.g729a", FT_BOOLEAN, 32, + TFS(&supported_strings), AST_FORMAT_G729A, + "G.729a Audio", HFILL }}, + + {&hf_iax2_cap_speex, + {"SPEEX Audio", "iax2.cap.speex", FT_BOOLEAN, 32, + TFS(&supported_strings), AST_FORMAT_SPEEX, + "SPEEX Audio", HFILL }}, + + {&hf_iax2_cap_ilbc, + {"iLBC Free compressed Audio", "iax2.cap.ilbc", FT_BOOLEAN, 32, + TFS(&supported_strings), AST_FORMAT_ILBC, + "iLBC Free compressed Audio", HFILL }}, + + {&hf_iax2_cap_jpeg, + {"JPEG images", "iax2.cap.jpeg", FT_BOOLEAN, 32, + TFS(&supported_strings), AST_FORMAT_JPEG, + "JPEG images", HFILL }}, + + {&hf_iax2_cap_png, + {"PNG images", "iax2.cap.png", FT_BOOLEAN, 32, + TFS(&supported_strings), AST_FORMAT_PNG, + "PNG images", HFILL }}, + + {&hf_iax2_cap_h261, + {"H.261 video", "iax2.cap.h261", FT_BOOLEAN, 32, + TFS(&supported_strings), AST_FORMAT_H261, + "H.261 video", HFILL }}, + + {&hf_iax2_cap_h263, + {"H.263 video", "iax2.cap.h263", FT_BOOLEAN, 32, + TFS(&supported_strings), AST_FORMAT_H263, + "H.263 video", HFILL }} + }; + + static gint *ett[] = { + &ett_iax2, + &ett_iax2_full_mini_subtree, + &ett_iax2_type, + &ett_iax2_ie, + &ett_iax2_codecs, + &ett_iax2_ies_apparent_addr + }; + + proto_iax2 = + proto_register_protocol ("Inter-Asterisk eXchange v2", "IAX2", "iax2"); + proto_register_field_array (proto_iax2, hf, array_length (hf)); + proto_register_subtree_array (ett, array_length (ett)); + + register_dissector("iax2", dissect_iax2, proto_iax2); + + iax2_codec_dissector_table = register_dissector_table( + "iax2.codec","IAX codec number", FT_UINT32, BASE_HEX); + iax2_dataformat_dissector_table = register_dissector_table( + "iax2.dataformat","IAX dataformat number", FT_UINT32, BASE_HEX); + + /* register our init routine to be called at the start of a capture, + to clear out our hash tables etc */ + register_init_routine(&iax_init_protocol); +} + +void +proto_reg_handoff_iax2 (void) +{ + dissector_add("udp.port", IAX2_PORT, find_dissector("iax2")); + dissector_add("iax2.dataformat", AST_DATAFORMAT_V110, find_dissector("v110")); + data_handle = find_dissector("data"); +} + + +/* + * This sets up the indentation style for this file in emacs. + * + * Local Variables: + * c-basic-offset: 2 + * End: + */ |