wireshark/epan/dissectors/packet-iax2.c
Guy Harris a82419745d Add a hack to set "reversed" and "circuit" in code paths where it wasn't
getting set.  The hack needs to be replaced by code that does the right
thing.

Fix indentation.

svn path=/trunk/; revision=13374
2005-02-10 08:07:44 +00:00

1879 lines
56 KiB
C

/*
* 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
if( reversed_p )
*reversed_p = FALSE;
if( circuit_p )
*circuit_p = NULL;
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);
if( reversed_p )
*reversed_p = FALSE;
if( circuit_p )
*circuit_p = NULL;
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);
if( reversed_p )
*reversed_p = FALSE;
if( circuit_p )
*circuit_p = NULL;
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;
/*
* XXX - the code needs to set "circuit" and "reversed" somehow here,
* by determining them based on values in "iax_call" or "iax_packet".
* I leave that as an exercise for somebody who wants to maintain
* this code.
*/
circuit = NULL;
reversed = FALSE;
}
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( iax_call ) {
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:
*/