aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-iax2.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/dissectors/packet-iax2.c')
-rw-r--r--epan/dissectors/packet-iax2.c1855
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:
+ */