Fix various typos and spelling errors.
[obnox/wireshark/wip.git] / epan / dissectors / packet-iax2.c
index c1ae493ff94a4a23796f7ae5631eef9c63aac2a4..b05d44143c702e351fe3ce3fbd775fde63603f35 100644 (file)
@@ -6,66 +6,96 @@
  * Copyright 2003 Alastair Maw
  *
  * IAX2 is a VoIP protocol for the open source PBX Asterisk. Please see
- * http://www.asterisk.org for more information.
+ * http://www.asterisk.org for more information; see
+ *
+ *   http://www.ietf.org/internet-drafts/draft-guy-iax-04.txt
+ *
+ * for the current Internet-Draft for IAX2.
  *
  * $Id$
  *
- * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@ethereal.com>
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
  * 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.
  *
+ * 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 <epan/emem.h>
+#include <epan/reassemble.h>
+#include <epan/aftypes.h>
+#include <epan/tap.h>
+#include <epan/tap-voip.h>
 
 #include "packet-iax2.h"
-#include "iax2_codec_type.h"
+#include <epan/iax2_codec_type.h>
+
+#define IAX2_PORT               4569
+#define PROTO_TAG_IAX2          "IAX2"
 
-#define IAX2_PORT              4569
-#define PROTO_TAG_IAX2         "IAX2"
+/* enough to hold any address in an address_t */
+#define MAX_ADDRESS 16
+
+/* the maximum number of transfers (of each end) we can deal with per call,
+ * plus one */
+#define IAX_MAX_TRANSFERS 2
 
 /* #define DEBUG_HASHING */
+/* #define DEBUG_DESEGMENT */
 
-/* Ethereal ID of the IAX2 protocol */
+/* Wireshark ID of the IAX2 protocol */
 static int proto_iax2 = -1;
 
-/* The following hf_* variables are used to hold the ethereal IDs of
+/* tap register id */
+static int iax2_tap = -1;
+
+/* protocol tap info */
+static iax2_info_t ii_arr[1];
+static iax2_info_t *iax2_info = ii_arr;
+
+/* The following hf_* variables are used to hold the wireshark 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_callno = -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_absts = -1;
+static int hf_iax2_lateness = -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_dtmf_csub = -1;
 static int hf_iax2_cmd_csub = -1;
 static int hf_iax2_iax_csub = -1;
 static int hf_iax2_voice_csub = -1;
@@ -73,6 +103,15 @@ 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_modem_csub = -1;
+static int hf_iax2_trunk_metacmd = -1;
+static int hf_iax2_trunk_cmddata = -1;
+static int hf_iax2_trunk_cmddata_ts = -1;
+static int hf_iax2_trunk_ts = -1;
+static int hf_iax2_trunk_ncalls = -1;
+static int hf_iax2_trunk_call_len = -1;
+static int hf_iax2_trunk_call_scallno = -1;
+static int hf_iax2_trunk_call_ts = -1;
 
 static int hf_iax2_cap_g723_1 = -1;
 static int hf_iax2_cap_gsm = -1;
@@ -90,62 +129,31 @@ static int hf_iax2_cap_png = -1;
 static int hf_iax2_cap_h261 = -1;
 static int hf_iax2_cap_h263 = -1;
 
+static int hf_iax2_fragments = -1;
+static int hf_iax2_fragment = -1;
+static int hf_iax2_fragment_overlap = -1;
+static int hf_iax2_fragment_overlap_conflict = -1;
+static int hf_iax2_fragment_multiple_tails = -1;
+static int hf_iax2_fragment_too_long_fragment = -1;
+static int hf_iax2_fragment_error = -1;
+static int hf_iax2_reassembled_in = -1;
+static int hf_iax2_reassembled_length = -1;
+
+
+/* hf_iax2_ies is an array of header fields, one per potential Information
+ * Element. It's done this way (rather than having separate variables for each
+ * IE) to make the dissection of information elements clearer and more
+ * orthoganal.
+ *
+ * To add the ability to dissect a new information element, just add an
+ * appropriate entry to hf[] in proto_register_iax2(); dissect_ies() will then
+ * pick it up automatically.
+ */
+static int hf_iax2_ies[256];
+static int hf_iax2_ie_datetime = -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_PROVISIONING = -1;
-static int hf_IAX_IE_AESPROVISIONING = -1;
-static int hf_IAX_IE_DATETIME = -1;
-static int hf_IAX_IE_DEVICETYPE = -1;
-static int hf_IAX_IE_SERVICEIDENT = -1;
-static int hf_IAX_IE_FIRMWAREVER = -1;
-static int hf_IAX_IE_FWBLOCKDESC = -1;
-static int hf_IAX_IE_FWBLOCKDATA = -1;
-static int hf_IAX_IE_PROVVER = -1;
-static int hf_IAX_IE_CALLINGPRES = -1;
-static int hf_IAX_IE_CALLINGTON = -1;
-static int hf_IAX_IE_CALLINGTNS = -1;
-static int hf_IAX_IE_SAMPLINGRATE = -1;
-static int hf_IAX_IE_CAUSECODE = -1;
-static int hf_IAX_IE_ENCRYPTION = -1;
-static int hf_IAX_IE_ENCKEY = -1;
-static int hf_IAX_IE_CODEC_PREFS = -1;
-static int hf_IAX_IE_RR_JITTER = -1;
-static int hf_IAX_IE_RR_LOSS = -1;
-static int hf_IAX_IE_RR_PKTS = -1;
-static int hf_IAX_IE_RR_DELAY = -1;
-static int hf_IAX_IE_RR_DROPPED = -1;
-static int hf_IAX_IE_RR_OOO = -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;
@@ -154,10 +162,29 @@ 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_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 gint ett_iax2_fragment = -1;
+static gint ett_iax2_fragments = -1;
+static gint ett_iax2_trunk_cmddata = -1;
+static gint ett_iax2_trunk_call = -1;
+
+static const fragment_items iax2_fragment_items = {
+  &ett_iax2_fragment,
+  &ett_iax2_fragments,
+  &hf_iax2_fragments,
+  &hf_iax2_fragment,
+  &hf_iax2_fragment_overlap,
+  &hf_iax2_fragment_overlap_conflict,
+  &hf_iax2_fragment_multiple_tails,
+  &hf_iax2_fragment_too_long_fragment,
+  &hf_iax2_fragment_error,
+  &hf_iax2_reassembled_in,
+  &hf_iax2_reassembled_length,
+  "iax2 fragments"
+};
 
 static dissector_handle_t data_handle;
 
@@ -166,17 +193,25 @@ static dissector_table_t iax2_dataformat_dissector_table;
 /* voice/video call subdissectors, AST_FORMAT_* */
 static dissector_table_t iax2_codec_dissector_table;
 
+
+/* IAX2 Meta trunk packet Command data flags */
+#define IAX2_TRUNK_TS 1
+
 /* 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"},
+  {AST_FRAME_DTMF_END, "DTMF End"},
+  {AST_FRAME_VOICE, "Voice"},
+  {AST_FRAME_VIDEO, "Video"},
+  {AST_FRAME_CONTROL, "Control"},
+  {AST_FRAME_NULL, "NULL"},
+  {AST_FRAME_IAX, "IAX"},
+  {AST_FRAME_TEXT, "Text"},
+  {AST_FRAME_IMAGE, "Image"},
+  {AST_FRAME_HTML, "HTML"},
+  {AST_FRAME_CNG, "Comfort Noise"},
+  {AST_FRAME_MODEM, "Modem"},
+  {AST_FRAME_DTMF_BEGIN, "DTMF Begin"},
   {0,NULL}
 };
 
@@ -237,149 +272,164 @@ static const value_string iax_cmd_subclasses[] = {
   {0,NULL}
 };
 
+/* IAX2 to tap-voip call state mapping */
+static const voip_call_state tap_cmd_voip_state[] = {
+  VOIP_NO_STATE,
+  VOIP_COMPLETED, /*HANGUP*/
+  VOIP_RINGING,   /*RING*/
+  VOIP_RINGING,   /*RINGING*/
+  VOIP_IN_CALL,   /*ANSWER*/
+  VOIP_REJECTED,  /*BUSY*/
+  VOIP_UNKNOWN,   /*TKOFFHK*/
+  VOIP_UNKNOWN    /*OFFHOOK*/
+};
+
+
+/* Subclassess for Modem packets */
+static const value_string iax_modem_subclasses[] = {
+  {0, "(0?)"},
+  {1, "T.38"},
+  {2, "V.150"},
+  {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_DEVICETYPE, "Device type"},
-  {IAX_IE_SERVICEIDENT, "Service Identifier"},
-  {IAX_IE_FIRMWAREVER, "Firmware revision"},
-  {IAX_IE_FWBLOCKDESC, "Firmware block description"},
-  {IAX_IE_FWBLOCKDATA, "Firmware block of data"},
-  {IAX_IE_PROVVER, "Provisioning version"},
-  {IAX_IE_CALLINGPRES, "Calling presentation"},
-  {IAX_IE_CALLINGTON, "Calling type of number"},
-  {IAX_IE_CALLINGTNS, "Calling transit network select"},
-  {IAX_IE_SAMPLINGRATE, "Supported sampling rates"},
-  {IAX_IE_CAUSECODE, "Hangup cause"},
-  {IAX_IE_ENCRYPTION, "Encryption format"},
-  {IAX_IE_ENCKEY, "Raw encryption key"},
-  {IAX_IE_CODEC_PREFS, "Codec preferences"},
-  {IAX_IE_RR_JITTER, "Received jitter"},
-  {IAX_IE_RR_LOSS, "Received loss"},
-  {IAX_IE_RR_PKTS, "Received frames"},
-  {IAX_IE_RR_DELAY, "Max playout delay in ms for received frames"},
-  {IAX_IE_RR_DROPPED, "Dropped frames"},
-  {IAX_IE_RR_OOO, "Frames received out of order"},
-  {IAX_IE_DATAFORMAT, "Data call format"},
+  {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_DEVICETYPE,       "Device type"},
+  {IAX_IE_SERVICEIDENT,     "Service Identifier"},
+  {IAX_IE_FIRMWAREVER,      "Firmware revision"},
+  {IAX_IE_FWBLOCKDESC,      "Firmware block description"},
+  {IAX_IE_FWBLOCKDATA,      "Firmware block of data"},
+  {IAX_IE_PROVVER,          "Provisioning version"},
+  {IAX_IE_CALLINGPRES,      "Calling presentation"},
+  {IAX_IE_CALLINGTON,       "Calling type of number"},
+  {IAX_IE_CALLINGTNS,       "Calling transit network select"},
+  {IAX_IE_SAMPLINGRATE,     "Supported sampling rates"},
+  {IAX_IE_CAUSECODE,        "Hangup cause"},
+  {IAX_IE_ENCRYPTION,       "Encryption format"},
+  {IAX_IE_ENCKEY,           "Raw encryption key"},
+  {IAX_IE_CODEC_PREFS,      "Codec preferences"},
+  {IAX_IE_RR_JITTER,        "Received jitter"},
+  {IAX_IE_RR_LOSS,          "Received loss"},
+  {IAX_IE_RR_PKTS,          "Received frames"},
+  {IAX_IE_RR_DELAY,         "Max playout delay in ms for received frames"},
+  {IAX_IE_RR_DROPPED,       "Dropped frames"},
+  {IAX_IE_RR_OOO,           "Frames received out of order"},
+  {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"},
+  {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_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_FULL_PACKET,       "Full packet"},
   {IAX2_MINI_VOICE_PACKET, "Mini voice packet"},
   {IAX2_MINI_VIDEO_PACKET, "Mini video packet"},
-  {IAX2_META_PACKET, "Meta packet"},
+  {IAX2_TRUNK_PACKET,      "Trunk packet"},
   {0,NULL}
 };
-  
+
 static const value_string iax_causecodes[] = {
-  {AST_CAUSE_UNALLOCATED, "Unallocated"},
-  {AST_CAUSE_NO_ROUTE_TRANSIT_NET, "No route transit net"},
-  {AST_CAUSE_NO_ROUTE_DESTINATION, "No route to destination"},
-  {AST_CAUSE_CHANNEL_UNACCEPTABLE, "Channel unacceptable"},
-  {AST_CAUSE_CALL_AWARDED_DELIVERED, "Call awarded delivered"},
-  {AST_CAUSE_NORMAL_CLEARING, "Normal clearing"},
-  {AST_CAUSE_USER_BUSY, "User busy"},
-  {AST_CAUSE_NO_USER_RESPONSE, "No user response"},
-  {AST_CAUSE_NO_ANSWER, "No answer"},
-  {AST_CAUSE_CALL_REJECTED, "Call rejected"},
-  {AST_CAUSE_NUMBER_CHANGED, "Number changed"},
-  {AST_CAUSE_DESTINATION_OUT_OF_ORDER, "Destination out of order"},
-  {AST_CAUSE_INVALID_NUMBER_FORMAT, "Invalid number format"},
-  {AST_CAUSE_FACILITY_REJECTED, "Facility rejected"},
-  {AST_CAUSE_RESPONSE_TO_STATUS_ENQUIRY, "Response to status inquiry"},
-  {AST_CAUSE_NORMAL_UNSPECIFIED, "Normal unspecified"},
-  {AST_CAUSE_NORMAL_CIRCUIT_CONGESTION, "Normal circuit congestion"},
-  {AST_CAUSE_NETWORK_OUT_OF_ORDER, "Network out of order"},
-  {AST_CAUSE_NORMAL_TEMPORARY_FAILURE, "Normal temporary failure"},
-  {AST_CAUSE_SWITCH_CONGESTION, "Switch congestion"},
-  {AST_CAUSE_ACCESS_INFO_DISCARDED, "Access info discarded"},
-  {AST_CAUSE_REQUESTED_CHAN_UNAVAIL, "Requested channel unavailable"},
-  {AST_CAUSE_PRE_EMPTED, "Preempted"},
-  {AST_CAUSE_FACILITY_NOT_SUBSCRIBED, "Facility not subscribed"},
-  {AST_CAUSE_OUTGOING_CALL_BARRED, "Outgoing call barred"},
-  {AST_CAUSE_INCOMING_CALL_BARRED, "Incoming call barred"},
-  {AST_CAUSE_BEARERCAPABILITY_NOTAUTH, "Bearer capability not authorized"},
-  {AST_CAUSE_BEARERCAPABILITY_NOTAVAIL, "Bearer capability not available"},
-  {AST_CAUSE_BEARERCAPABILITY_NOTIMPL, "Bearer capability not implemented"},
-  {AST_CAUSE_CHAN_NOT_IMPLEMENTED, "Channel not implemented"},
-  {AST_CAUSE_FACILITY_NOT_IMPLEMENTED, "Facility not implemented"},
-  {AST_CAUSE_INVALID_CALL_REFERENCE, "Invalid call reference"},
-  {AST_CAUSE_INCOMPATIBLE_DESTINATION, "Incompatible destination"},
-  {AST_CAUSE_INVALID_MSG_UNSPECIFIED, "Invalid message unspecified"},
-  {AST_CAUSE_MANDATORY_IE_MISSING, "Mandatory IE missing"},
-  {AST_CAUSE_MESSAGE_TYPE_NONEXIST, "Message type nonexistent"},
-  {AST_CAUSE_WRONG_MESSAGE, "Wrong message"},
-  {AST_CAUSE_IE_NONEXIST, "IE nonexistent"},
-  {AST_CAUSE_INVALID_IE_CONTENTS, "Invalid IE contents"},
-  {AST_CAUSE_WRONG_CALL_STATE, "Wrong call state"},
-  {AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE, "Recovery on timer expire"},
-  {AST_CAUSE_MANDATORY_IE_LENGTH_ERROR, "Mandatory IE length error"},
-  {AST_CAUSE_PROTOCOL_ERROR, "Protocol error"},
-  {AST_CAUSE_INTERWORKING, "Interworking"},
+  {AST_CAUSE_UNALLOCATED,                 "Unallocated"},
+  {AST_CAUSE_NO_ROUTE_TRANSIT_NET,        "No route transit net"},
+  {AST_CAUSE_NO_ROUTE_DESTINATION,        "No route to destination"},
+  {AST_CAUSE_CHANNEL_UNACCEPTABLE,        "Channel unacceptable"},
+  {AST_CAUSE_CALL_AWARDED_DELIVERED,      "Call awarded delivered"},
+  {AST_CAUSE_NORMAL_CLEARING,             "Normal clearing"},
+  {AST_CAUSE_USER_BUSY,                   "User busy"},
+  {AST_CAUSE_NO_USER_RESPONSE,            "No user response"},
+  {AST_CAUSE_NO_ANSWER,                   "No answer"},
+  {AST_CAUSE_CALL_REJECTED,               "Call rejected"},
+  {AST_CAUSE_NUMBER_CHANGED,              "Number changed"},
+  {AST_CAUSE_DESTINATION_OUT_OF_ORDER,    "Destination out of order"},
+  {AST_CAUSE_INVALID_NUMBER_FORMAT,       "Invalid number format"},
+  {AST_CAUSE_FACILITY_REJECTED,           "Facility rejected"},
+  {AST_CAUSE_RESPONSE_TO_STATUS_ENQUIRY,  "Response to status inquiry"},
+  {AST_CAUSE_NORMAL_UNSPECIFIED,          "Normal unspecified"},
+  {AST_CAUSE_NORMAL_CIRCUIT_CONGESTION,   "Normal circuit congestion"},
+  {AST_CAUSE_NETWORK_OUT_OF_ORDER,        "Network out of order"},
+  {AST_CAUSE_NORMAL_TEMPORARY_FAILURE,    "Normal temporary failure"},
+  {AST_CAUSE_SWITCH_CONGESTION,           "Switch congestion"},
+  {AST_CAUSE_ACCESS_INFO_DISCARDED,       "Access info discarded"},
+  {AST_CAUSE_REQUESTED_CHAN_UNAVAIL,      "Requested channel unavailable"},
+  {AST_CAUSE_PRE_EMPTED,                  "Preempted"},
+  {AST_CAUSE_FACILITY_NOT_SUBSCRIBED,     "Facility not subscribed"},
+  {AST_CAUSE_OUTGOING_CALL_BARRED,        "Outgoing call barred"},
+  {AST_CAUSE_INCOMING_CALL_BARRED,        "Incoming call barred"},
+  {AST_CAUSE_BEARERCAPABILITY_NOTAUTH,    "Bearer capability not authorized"},
+  {AST_CAUSE_BEARERCAPABILITY_NOTAVAIL,   "Bearer capability not available"},
+  {AST_CAUSE_BEARERCAPABILITY_NOTIMPL,    "Bearer capability not implemented"},
+  {AST_CAUSE_CHAN_NOT_IMPLEMENTED,        "Channel not implemented"},
+  {AST_CAUSE_FACILITY_NOT_IMPLEMENTED,    "Facility not implemented"},
+  {AST_CAUSE_INVALID_CALL_REFERENCE,      "Invalid call reference"},
+  {AST_CAUSE_INCOMPATIBLE_DESTINATION,    "Incompatible destination"},
+  {AST_CAUSE_INVALID_MSG_UNSPECIFIED,     "Invalid message unspecified"},
+  {AST_CAUSE_MANDATORY_IE_MISSING,        "Mandatory IE missing"},
+  {AST_CAUSE_MESSAGE_TYPE_NONEXIST,       "Message type nonexistent"},
+  {AST_CAUSE_WRONG_MESSAGE,               "Wrong message"},
+  {AST_CAUSE_IE_NONEXIST,                 "IE nonexistent"},
+  {AST_CAUSE_INVALID_IE_CONTENTS,         "Invalid IE contents"},
+  {AST_CAUSE_WRONG_CALL_STATE,            "Wrong call state"},
+  {AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE,    "Recovery on timer expire"},
+  {AST_CAUSE_MANDATORY_IE_LENGTH_ERROR,   "Mandatory IE length error"},
+  {AST_CAUSE_PROTOCOL_ERROR,              "Protocol error"},
+  {AST_CAUSE_INTERWORKING,                "Interworking"},
   {0, NULL}
 };
 
@@ -415,12 +465,13 @@ typedef struct {
   port_type ptype;
   guint32 port;
   guint32 callno;
+
+  /* this is where addr->data points to. it's put in here for easy freeing */
+  guint8 address_data[MAX_ADDRESS];
 } 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.
@@ -440,13 +491,14 @@ static gchar *key_to_str( const iax_circuit_key *key )
   }
   strp=str[i];
 
-  /* why doesn't address_to_str take a const pointer?
+  /* why doesn't ep_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);
+  /* XXX - is this a case for ep_alloc? */
+  g_snprintf(strp, 80, "{%s:%i,%i}",
+             ep_address_to_str((address *)&key->addr),
+             key->port,
+             key->callno);
   return strp;
 }
 #endif
@@ -459,73 +511,72 @@ static gint iax_circuit_equal(gconstpointer v, gconstpointer w)
   gint result;
 
   result = ( ADDRESSES_EQUAL(&(v1->addr), &(v2->addr)) &&
-            v1->ptype == v2->ptype &&
-            v1->port  == v2->port &&
-            v1->callno== v2->callno);
+             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);
+  g_debug( "+++ Comparing for equality: %s, %s: %u",key_to_str(v1), key_to_str(v2), result);
 #endif
 
-  return result;;
+  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]);
-
+  ADD_ADDRESS_TO_HASH(hash_val, &key->addr);
   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 );
+  g_debug( "+++ 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)
+/* Find, or create, a circuit for the given
+   {address,porttype,port,call} quadruplet
+*/
+static guint iax_circuit_lookup(const address *address_p,
+                                port_type ptype,
+                                guint32 port,
+                                guint32 callno)
 {
   iax_circuit_key key;
   guint32 *circuit_id_p;
 
-  key.addr = *address;
+  key.addr = *address_p;
   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 = se_alloc(sizeof(iax_circuit_key));
+    new_key->addr.type = address_p->type;
+    new_key->addr.len = MIN(address_p->len,MAX_ADDRESS);
+    new_key->addr.data = new_key->address_data;
+    memcpy(new_key->address_data,address_p->data,new_key->addr.len);
     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 = se_alloc(sizeof(iax_circuit_key));
     *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 );
+    g_debug("Created new circuit id %u for node %s", *circuit_id_p, key_to_str(new_key));
 #endif
+  }
 
   return *circuit_id_p;
 }
@@ -533,6 +584,11 @@ static guint iax_circuit_lookup(const address *address,
 
 /* ************************************************************************* */
 
+typedef struct {
+  guint32     current_frag_id; /* invalid unless current_frag_bytes > 0 */
+  guint32     current_frag_bytes;
+  guint32     current_frag_minlen;
+} iax_call_dirdata;
 
 /* This is our per-call data structure, which is attached to both the
  * forward and reverse circuits.
@@ -552,188 +608,215 @@ typedef struct iax_call_data {
   guint32 src_codec, dst_codec;
   guint32 src_vformat, dst_vformat;
 
-  guint forward_circuit_id;
-  guint reverse_circuit_id;
+  /* when a transfer takes place, we'll get a new circuit id; we assume that we
+     don't try to transfer more than IAX_MAX_TRANSFERS times in a call */
+  guint forward_circuit_ids[IAX_MAX_TRANSFERS];
+  guint reverse_circuit_ids[IAX_MAX_TRANSFERS];
+  guint n_forward_circuit_ids;
+  guint n_reverse_circuit_ids;
 
-  guint callno;
-} iax_call_data;
+  /* this is the subdissector for the call */
+  dissector_handle_t subdissector;
 
-static guint callcount = 0;
+  /* the absolute start time of the call */
+  nstime_t start_time;
 
-static GMemChunk *iax_call_datas = NULL;
+  GHashTable *fid_table;
+  GHashTable *fragment_table;
+  iax_call_dirdata dirdata[2];
+} iax_call_data;
 
 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)
+
+/* creates a new CT_IAX2 circuit with a specified circuit id for a call
+ *
+ * typically a call has up to three associated circuits: an original source, an
+ * original destination, and the result of a transfer.
+ *
+ * For each endpoint, a CT_IAX2 circuit is created and added to the call_data
+ * by this function
+ *
+ * 'reversed' should be true if this end is the one which would have _received_
+ * the NEW packet, or it is an endpoint to which the 'destination' is being
+ * transferred.
+ *
+ */
+static circuit_t *iax2_new_circuit_for_call(guint circuit_id, guint framenum, iax_call_data *iax_call,
+                                            gboolean reversed)
+{
+  circuit_t *res;
+
+  if(( reversed && iax_call->n_reverse_circuit_ids >= IAX_MAX_TRANSFERS) ||
+     ( !reversed && iax_call->n_forward_circuit_ids >= IAX_MAX_TRANSFERS)) {
+    g_warning("Too many transfers for iax_call");
+    return NULL;
+  }
+
+  res = circuit_new(CT_IAX2,
+                    circuit_id,
+                    framenum );
+
+  circuit_add_proto_data(res, proto_iax2, iax_call);
+
+  if( reversed )
+    iax_call -> reverse_circuit_ids[iax_call->n_reverse_circuit_ids++] = circuit_id;
+  else
+    iax_call -> forward_circuit_ids[iax_call->n_forward_circuit_ids++] = circuit_id;
+
+  return res;
+}
+
+
+/* returns true if this circuit id is a "forward" circuit for this call: ie, it
+ * is the point which _sent_ the original 'NEW' packet, or a point to which that
+ * end was subsequently transferred */
+static gboolean is_forward_circuit(guint circuit_id,
+                                   const iax_call_data *iax_call)
+{
+  guint i;
+  for(i=0;i<iax_call->n_forward_circuit_ids;i++){
+    if(circuit_id == iax_call->forward_circuit_ids[i])
+      return TRUE;
+  }
+  return FALSE;
+}
+
+/* returns true if this circuit id is a "reverse" circuit for this call: ie, it
+ * is the point which _received_ the original 'NEW' packet, or a point to which that
+ * end was subsequently transferred */
+static gboolean is_reverse_circuit(guint circuit_id,
+                                   const iax_call_data *iax_call)
+{
+  guint i;
+  for(i=0;i<iax_call->n_reverse_circuit_ids;i++){
+    if(circuit_id == iax_call->reverse_circuit_ids[i])
+      return TRUE;
+  }
+  return FALSE;
+}
+
+
+static iax_call_data *iax_lookup_call_from_dest( guint src_circuit_id,
+                                                 guint dst_circuit_id,
+                                                 guint framenum,
+                                                 gboolean *reversed_p)
 {
   circuit_t *dst_circuit;
   iax_call_data * iax_call;
   gboolean reversed = FALSE;
-  
+
   dst_circuit = find_circuit( CT_IAX2,
-                             dst_circuit_id,
-                             framenum );
+                              dst_circuit_id,
+                              framenum );
 
   if( !dst_circuit ) {
 #ifdef DEBUG_HASHING
-    g_message( "++ destination circuit not found, must have missed NEW packet" );
+    g_debug( "++ 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" );
+  g_debug( "++ 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 */
   DISSECTOR_ASSERT(iax_call);
-  
-  if( dst_circuit_id == iax_call -> forward_circuit_id ) {
+
+  if( is_forward_circuit(dst_circuit_id, iax_call )) {
 #ifdef DEBUG_HASHING
-    g_message( "++ destination circuit matches forward_circuit_id of call, "
-              "therefore packet is reversed" );
+    g_debug( "++ 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;
-      
+    if( iax_call -> n_reverse_circuit_ids == 0 ) {
       /* we are going in the reverse direction, and this call
-        doesn't have a reverse circuit associated with it.
-        create one now. */
+         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" );
+      g_debug( "++ 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));
+      iax2_new_circuit_for_call( src_circuit_id, framenum, iax_call, TRUE );
 #ifdef DEBUG_HASHING
-      g_message( "++ done" );
+      g_debug( "++ 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;
+    } else if( !is_reverse_circuit(src_circuit_id, iax_call )) {
+      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_ids[0],
+                 iax_call->reverse_circuit_ids[0]);
       return NULL;
     }
-  } else if ( dst_circuit_id == iax_call -> reverse_circuit_id ) {
+  } else if ( is_reverse_circuit(dst_circuit_id, iax_call)) {
 #ifdef DEBUG_HASHING
-    g_message( "++ destination circuit matches reverse_circuit_id of call, "
-              "therefore packet is forward" );
+    g_debug( "++ 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( !is_forward_circuit(src_circuit_id, iax_call)) {
+      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_ids[0],
+                 iax_call->reverse_circuit_ids[0]);
       if( reversed_p )
         *reversed_p = FALSE;
-      if( circuit_p )
-        *circuit_p = NULL;
       return NULL;
     }
   } else {
     DISSECTOR_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 );
-    DISSECTOR_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)
+
+/* looks up an iax_call for this packet */
+static iax_call_data *iax_lookup_call( packet_info *pinfo,
+                                       guint32 scallno,
+                                       guint32 dcallno,
+                                       gboolean *reversed_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);
+  g_debug( "++ iax_lookup_circuit_details: Looking up circuit for frame %u, "
+             "from {%s:%u:%u} to {%s:%u:%u}", pinfo->fd->num,
+             ep_address_to_str(&pinfo->src),pinfo->srcport,scallno,
+             ep_address_to_str(&pinfo->dst),pinfo->destport,dcallno);
 #endif
 
 
   src_circuit_id = iax_circuit_lookup(&pinfo->src,pinfo->ptype,
-                                     pinfo->srcport,scallno);
+                                      pinfo->srcport,scallno);
 
 
   /* the most reliable indicator of call is the destination callno, if
@@ -741,14 +824,16 @@ static iax_call_data *iax_lookup_circuit_details( packet_info *pinfo,
   if( dcallno != 0 ) {
     guint dst_circuit_id;
 #ifdef DEBUG_HASHING
-    g_message( "++ dcallno non-zero, looking up destination circuit" );
+    g_debug( "++ dcallno non-zero, looking up destination circuit" );
 #endif
 
     dst_circuit_id = iax_circuit_lookup(&pinfo->dst,pinfo->ptype,
-                                       pinfo->destport,dcallno);
+                                        pinfo->destport,dcallno);
 
-    iax_call = iax_lookup_circuit_details_from_dest(src_circuit_id, dst_circuit_id, pinfo->fd->num, &reversed, &src_circuit);
+    iax_call = iax_lookup_call_from_dest(src_circuit_id, dst_circuit_id,
+                                         pinfo->fd->num, &reversed);
   } else {
+    circuit_t *src_circuit;
 
     /* in all other circumstances, the source circuit should already
      * exist: its absense indicates that we missed the all-important NEW
@@ -756,145 +841,177 @@ static iax_call_data *iax_lookup_circuit_details( packet_info *pinfo,
      */
 
     src_circuit = find_circuit( CT_IAX2,
-                           src_circuit_id,
-                           pinfo->fd->num );
+                            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 */
+         iax call data to it; assert this */
       DISSECTOR_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;
+      if( is_forward_circuit(src_circuit_id,iax_call))
+        reversed = FALSE;
+      else if(is_reverse_circuit(src_circuit_id,iax_call))
+        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.
-       */
-       DISSECTOR_ASSERT_NOT_REACHED();
+        /* 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.
+        */
+        DISSECTOR_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' );
+    g_debug( "++ Found call for packet: id %u, reversed=%c", iax_call->forward_circuit_ids[0], reversed?'1':'0' );
   } else {
-    g_message( "++ Call not found. Must have missed the NEW packet?" );
+    g_debug( "++ Call not found. Must have missed the NEW packet?" );
   }
 #endif
-  
+
   return iax_call;
 }
 
+/* initialise the per-direction parts of an iax_call_data structure */
+static void init_dir_data(iax_call_dirdata *dirdata)
+{
+  dirdata -> current_frag_bytes=0;
+  dirdata -> current_frag_minlen=0;
+}
+
 
 /* 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)
+static iax_call_data *iax_new_call( packet_info *pinfo,
+                                    guint32 scallno)
 {
-  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;
+  static const nstime_t millisecond = {0, 1000000};
 
 #ifdef DEBUG_HASHING
-    g_message( "+ new_circuit: Added new circuit for new call %u", call -> callno );
+  g_debug( "+ new_circuit: Handling NEW packet, frame %u", pinfo->fd->num );
 #endif
 
-    circuit_add_proto_data( circuit, proto_iax2, call );
-
-  if( circuit_p )
-    *circuit_p = circuit;
+  circuit_id = iax_circuit_lookup(&pinfo->src,pinfo->ptype,
+                                  pinfo->srcport,scallno);
+
+  call = se_alloc(sizeof(iax_call_data));
+  call -> dataformat = 0;
+  call -> src_codec = 0;
+  call -> dst_codec = 0;
+  call -> n_forward_circuit_ids = 0;
+  call -> n_reverse_circuit_ids = 0;
+  call -> subdissector = NULL;
+  call -> start_time = pinfo->fd->abs_ts;
+  nstime_delta(&call -> start_time, &call -> start_time, &millisecond);
+  call -> fid_table = g_hash_table_new(g_direct_hash, g_direct_equal);
+  init_dir_data(&call->dirdata[0]);
+  init_dir_data(&call->dirdata[1]);
+  call->fragment_table = NULL;
+  fragment_table_init(&(call->fragment_table));
+
+  iax2_new_circuit_for_call(circuit_id,pinfo->fd->num,call,FALSE);
 
   return call;
 }
-    
+
 
 /* ************************************************************************* */
 
 /* per-packet data */
 typedef struct iax_packet_data {
+  gboolean first_time; /* we're dissecting this packet for the first time; so
+                          things like codec and transfer requests should be
+                          propogated into the call data */
   iax_call_data *call_data;
   guint32 codec;
+  gboolean reversed;
+  nstime_t abstime;    /* the absolute time of this packet, based on its
+                        * timestamp and the NEW packet's time (-1 if unknown) */
 } iax_packet_data;
 
-static GMemChunk *iax_packets = NULL;
-
-static iax_packet_data *iax_new_packet_data(iax_call_data *call)
+static iax_packet_data *iax_new_packet_data(iax_call_data *call, gboolean reversed)
 {
-  iax_packet_data *p = g_mem_chunk_alloc(iax_packets);
+  iax_packet_data *p = se_alloc(sizeof(iax_packet_data));
+  p->first_time=TRUE;
   p->call_data=call;
   p->codec=0;
+  p->reversed=reversed;
+  p->abstime.secs=-1;
+  p->abstime.nsecs=-1;
   return p;
 }
 
+static void  iax2_populate_pinfo_from_packet_data(packet_info *pinfo, const iax_packet_data * p)
+{
+  /* info for subdissectors. We always pass on the original forward circuit,
+   * and steal the p2p_dir flag to indicate the direction */
+  if( p->call_data == NULL ) {
+     /* if we missed the NEW packet for this call, call_data will be null. it's
+      * tbd what the best thing to do here is. */
+    pinfo -> ctype = CT_NONE;
+  } else {
+    pinfo -> ctype = CT_IAX2;
+    pinfo -> circuit_id = (guint32)p->call_data->forward_circuit_ids[0];
+    pinfo -> p2p_dir = p->reversed?P2P_DIR_RECV:P2P_DIR_SENT;
+
+    if (check_col (pinfo->cinfo, COL_IF_DIR))
+      col_set_str (pinfo->cinfo, COL_IF_DIR, p->reversed ? "rev" : "fwd" );
+  }
+}
+
 
 /* ************************************************************************* */
 
+/* this is passed up from the IE dissector to the main dissector */
+typedef struct
+{
+  address peer_address;
+  port_type peer_ptype;
+  guint32 peer_port;
+  guint32 peer_callno;
+  guint32 dataformat;
+} iax2_ie_data;
+
+
 static guint32 dissect_fullpacket (tvbuff_t * tvb, guint32 offset,
-                               guint16 scallno,
-                               packet_info * pinfo,
-                               proto_tree * iax2_tree,
-                               proto_tree * main_tree);
+                                   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_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 guint32 dissect_minivideopacket (tvbuff_t * tvb, guint32 offset, 
-                                       guint16 scallno,
-                                       packet_info * pinfo,
-                                       proto_tree * iax2_tree,
-                                       proto_tree * main_tree);
+static guint32 dissect_trunkpacket (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);
+                            packet_info *pinfo, proto_tree *iax2_tree,
+                            proto_tree *tree, guint32 ts, gboolean video,
+                            iax_packet_data *iax_packet);
 
 
 
@@ -910,14 +1027,8 @@ dissect_iax2 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
   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);
-    }
+  col_set_str (pinfo->cinfo, COL_PROTOCOL, PROTO_TAG_IAX2);
+  col_clear(pinfo->cinfo, COL_INFO);
 
   /* add the 'iax2' tree to the main tree */
   if (tree)
@@ -928,8 +1039,8 @@ dissect_iax2 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
 
   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 */
+    /* starting with 0x0000 indicates meta packet which can be either a mini
+     * video packet or a trunk packet */
     offset+=2;
     stmp = tvb_get_ntohs(tvb, offset);
     if( stmp & 0x8000 ) {
@@ -939,7 +1050,7 @@ dissect_iax2 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
       offset += 2;
     }
     else {
-      type = IAX2_META_PACKET;
+      type = IAX2_TRUNK_PACKET;
     }
   } else {
     /* The source call/fullpacket flag is common to both mini and full packets */
@@ -963,19 +1074,35 @@ dissect_iax2 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
       proto_tree_add_item (full_mini_subtree, hf_iax2_scallno, tvb, offset-2, 2, FALSE);
   }
 
+  iax2_info->ptype = type;
+  iax2_info->scallno = 0;
+  iax2_info->dcallno = 0;
+  iax2_info->ftype = 0;
+  iax2_info->csub = 0;
+  iax2_info->payload_len = 0;
+  iax2_info->timestamp = 0;
+  iax2_info->callState = VOIP_NO_STATE;
+  iax2_info->messageName = NULL;
+  iax2_info->callingParty = NULL;
+  iax2_info->calledParty = NULL;
+  iax2_info->payload_data = NULL;
+
   switch( type ) {
     case IAX2_FULL_PACKET:
       len = dissect_fullpacket( tvb, offset, scallno, pinfo, full_mini_subtree, tree );
       break;
     case IAX2_MINI_VOICE_PACKET:
+      iax2_info->messageName = "MINI_VOICE_PACKET";
       len = dissect_minipacket( tvb, offset, scallno, pinfo, full_mini_subtree, tree );
       break;
     case IAX2_MINI_VIDEO_PACKET:
+      iax2_info->messageName = "MINI_VIDEO_PACKET";
       len = dissect_minivideopacket( tvb, offset, scallno, pinfo, full_mini_subtree, tree );
       break;
-    case IAX2_META_PACKET:
+    case IAX2_TRUNK_PACKET:
       /* not implemented yet */
-      len = 0;
+      iax2_info->messageName = "TRUNK_PACKET";
+      len = dissect_trunkpacket( tvb, offset, scallno, pinfo, full_mini_subtree, tree );
       break;
     default:
       len = 0;
@@ -984,332 +1111,256 @@ dissect_iax2 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
   /* 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);
+  tap_queue_packet(iax2_tap, pinfo, iax2_info);
+}
+
+static proto_item *dissect_datetime_ie(tvbuff_t *tvb, guint32 offset, proto_tree *ies_tree)
+{
+  struct tm tm;
+  guint32 ie_val;
+  nstime_t datetime;
+
+  proto_tree_add_item (ies_tree, hf_iax2_ies[IAX_IE_DATETIME], tvb, offset + 2, 4, FALSE);
+  ie_val = tvb_get_ntohl(tvb, offset+2);
+
+  /* who's crazy idea for a time encoding was this? */
+  tm.tm_sec  = (ie_val & 0x1f) << 1;
+  tm.tm_min  = (ie_val>>5) & 0x3f;
+  tm.tm_hour = (ie_val>>11) & 0x1f;
+  tm.tm_mday = (ie_val>>16) & 0x1f;
+  tm.tm_mon  = ((ie_val>>21) & 0xf) - 1;
+  tm.tm_year = ((ie_val>>25) & 0x7f) + 100;
+  tm.tm_isdst= -1; /* there's no info on whether DST was in force; assume it's
+                    * the same as currently */
+
+  datetime.secs = mktime(&tm);
+  datetime.nsecs = 0;
+  return proto_tree_add_time (ies_tree, hf_iax2_ie_datetime, tvb, offset+2, 4, &datetime);
 }
 
 
 /* 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 * iax_tree,
+                            iax2_ie_data *ie_data)
 {
-  proto_tree *sockaddr_tree = NULL;
-  proto_item *sockaddr_item = 0;
-
+  DISSECTOR_ASSERT(ie_data);
 
   while (offset < tvb_reported_length (tvb)) {
 
     int ies_type = tvb_get_guint8(tvb, offset);
     int ies_len = tvb_get_guint8(tvb, offset + 1);
+    guint16 apparent_addr_family;
+
+    /* do non-tree-dependent stuff first */
+    switch(ies_type) {
+      case IAX_IE_DATAFORMAT:
+        if (ies_len != 4) THROW(ReportedBoundsError);
+        ie_data -> dataformat = tvb_get_ntohl(tvb, offset+2);
+        break;
+
+      case IAX_IE_CALLED_NUMBER:
+        iax2_info->calledParty = g_strdup(tvb_format_text(tvb, offset+2, ies_len));
+        break;
+      case IAX_IE_CALLING_NUMBER:
+        iax2_info->callingParty = g_strdup(tvb_format_text(tvb, offset+2, ies_len));
+        break;
+
+      case IAX_IE_APPARENT_ADDR:
+        /* The IAX2 I-D says that the "apparent address" structure
+           "is the same as the linux struct sockaddr_in", without
+           bothering to note that the address family field is in
+           *host* byte order in that structure (the I-D seems to be
+           assuming that "everything is a Vax^Wx86 or x86-64" with
+           the address family field being little-endian).
+
+           This means the address family values are the Linux
+           address family values. */
+        apparent_addr_family = tvb_get_letohs(tvb, offset+2);
+        switch( apparent_addr_family ) {
+          case LINUX_AF_INET:
+            /* IAX is always over UDP */
+            ie_data->peer_ptype = PT_UDP;
+            ie_data->peer_port = tvb_get_ntohs(tvb, offset+4);
+
+            /* the ip address is big-endian, but then so is peer_address.data */
+            SET_ADDRESS(&ie_data->peer_address,AT_IPv4,4,tvb_get_ptr(tvb,offset+6,4));
+            break;
+
+          default:
+            g_warning("Not supported in IAX dissector: peer address family of %u", apparent_addr_family);
+            break;
+        }
+        break;
+    }
+
+
+    /* the rest of this stuff only needs doing if we have an iax_tree */
 
     if( iax_tree ) {
-      proto_item *ti;
+      proto_item *ti, *ie_item = NULL;
       proto_tree *ies_tree;
+      int ie_hf = hf_iax2_ies[ies_type];
 
-      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);
-
+      ti = proto_tree_add_text(iax_tree, tvb, offset, ies_len+2, " " );
 
       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);
+                          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);
 
 
+      /* hf_iax2_ies[] is an array, indexed by IE number, of header-fields, one
+         per IE. Apart from a couple of special cases which require more
+         complex decoding, we can just look up an entry from the array, and add
+         the relevant item.
+      */
+
       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;
-
-             if (ies_len != 4) THROW(ReportedBoundsError);
-             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:
-             if (ies_len != 4) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_FORMAT, tvb,
-                                  offset + 2, ies_len, FALSE);
-             break;
-           case IAX_IE_VERSION:
-             if (ies_len != 2) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_VERSION, tvb,
-                                  offset + 2, ies_len, FALSE);
-             break;
-           case IAX_IE_ADSICPE:
-             if (ies_len != 2) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_ADSICPE, tvb,
-                                  offset + 2, ies_len, FALSE);
-             break;
-           case IAX_IE_AUTHMETHODS:
-             if (ies_len != 2) THROW(ReportedBoundsError);
-             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:
-             if (ies_len != 2) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_REFRESH, tvb,
-                                  offset + 2, ies_len, FALSE);
-             break;
-           case IAX_IE_DPSTATUS:
-             if (ies_len != 2) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_DPSTATUS, tvb,
-                                  offset + 2, ies_len, FALSE);
-             break;
-           case IAX_IE_CALLNO:
-             if (ies_len != 2) THROW(ReportedBoundsError);
-             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:
-             if (ies_len != 2) THROW(ReportedBoundsError);
-             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:
-             if (ies_len != 4) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_TRANSFERID, tvb,
-                                  offset + 2, ies_len, FALSE);
-             break;
-           case IAX_IE_PROVISIONING:
-             proto_tree_add_item (ies_tree, hf_IAX_IE_PROVISIONING, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_AESPROVISIONING:
-             proto_tree_add_item (ies_tree, hf_IAX_IE_AESPROVISIONING, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_DATETIME:
-             if (ies_len != 2) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_DATETIME, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_DEVICETYPE:
-             proto_tree_add_item (ies_tree, hf_IAX_IE_DEVICETYPE, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_SERVICEIDENT:
-             proto_tree_add_item (ies_tree, hf_IAX_IE_SERVICEIDENT, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_FIRMWAREVER:
-             if (ies_len != 2) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_FIRMWAREVER, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_FWBLOCKDESC:
-             if (ies_len != 4) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_FWBLOCKDESC, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_FWBLOCKDATA:
-             proto_tree_add_item (ies_tree, hf_IAX_IE_FWBLOCKDATA, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_PROVVER:
-             if (ies_len != 4) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_PROVVER, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_CALLINGPRES:
-             if (ies_len != 1) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_CALLINGPRES, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_CALLINGTON:
-             if (ies_len != 1) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_CALLINGTON, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_CALLINGTNS:
-             if (ies_len != 2) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_CALLINGTNS, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_SAMPLINGRATE:
-             if (ies_len != 2) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_SAMPLINGRATE, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_CAUSECODE:
-             if (ies_len != 1) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_CAUSECODE, tvb,
-                                  offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_ENCRYPTION:
-             if (ies_len != 2) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_ENCRYPTION, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_ENCKEY:
-             proto_tree_add_item (ies_tree, hf_IAX_IE_ENCKEY, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_CODEC_PREFS:
-             proto_tree_add_item (ies_tree, hf_IAX_IE_CODEC_PREFS, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_RR_JITTER:
-             if (ies_len != 4) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_RR_JITTER, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_RR_LOSS:
-             if (ies_len != 4) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_RR_LOSS, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_RR_PKTS:
-             if (ies_len != 4) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_RR_PKTS, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_RR_DELAY:
-             if (ies_len != 2) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_RR_DELAY, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_RR_DROPPED:
-             if (ies_len != 4) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_RR_DROPPED, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-           case IAX_IE_RR_OOO:
-             if (ies_len != 4) THROW(ReportedBoundsError);
-             proto_tree_add_item (ies_tree, hf_IAX_IE_RR_OOO, tvb,
-                                 offset +2, ies_len, FALSE);
-             break;
-             
-      case IAX_IE_DATAFORMAT:
-       if (ies_len != 4) THROW(ReportedBoundsError);
-       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 );
-       }
+        case IAX_IE_DATETIME:
+          ie_item = dissect_datetime_ie(tvb,offset,ies_tree);
+          break;
+
+
+        case IAX_IE_CAPABILITY:
+        {
+          proto_tree *codec_tree;
+
+          if (ies_len != 4) THROW(ReportedBoundsError);
+
+          ie_item =
+            proto_tree_add_item (ies_tree, ie_hf,
+                                 tvb, offset + 2, ies_len, FALSE);
+          codec_tree =
+            proto_item_add_subtree (ie_item, 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_APPARENT_ADDR:
+        {
+          proto_tree *sockaddr_tree = NULL;
+
+          ie_item = proto_tree_add_text(ies_tree, tvb, offset + 2, 16, "Apparent Address");
+          sockaddr_tree = proto_item_add_subtree(ie_item, ett_iax2_ies_apparent_addr);
+
+          /* The IAX2 I-D says that the "apparent address" structure
+             "is the same as the linux struct sockaddr_in", without
+             bothering to note that the address family field is in
+             *host* byte order in that structure (the I-D seems to be
+             assuming that "everything is a Vax^Wx86 or x86-64" with
+             the address family field being little-endian).
+
+             This means the address family values are the Linux
+             address family values. */
+          apparent_addr_family = tvb_get_letohs(tvb, offset+2);
+          proto_tree_add_uint(sockaddr_tree, hf_IAX_IE_APPARENTADDR_SINFAMILY, tvb, offset + 2, 2, apparent_addr_family);
+
+          switch(  apparent_addr_family ) {
+            case LINUX_AF_INET:
+            {
+              guint32 addr;
+              proto_tree_add_uint(sockaddr_tree, hf_IAX_IE_APPARENTADDR_SINPORT, tvb, offset + 4, 2, ie_data->peer_port);
+              memcpy(&addr, ie_data->peer_address.data, 4);
+              proto_tree_add_ipv4(sockaddr_tree, hf_IAX_IE_APPARENTADDR_SINADDR, tvb, offset + 6, 4, addr);
+              break;
+            }
+          }
+          break;
+        }
+
+        default:
+          if( ie_hf != -1 ) {
+            /* throw an error if the IE isn't the expected length */
+            gint explen = ftype_length(proto_registrar_get_nth(ie_hf)->type);
+            if(explen != 0 && ies_len != explen)
+              THROW(ReportedBoundsError);
+            ie_item = proto_tree_add_item(ies_tree, ie_hf, tvb, offset + 2, ies_len, FALSE);
+          } else {
+            /* we don't understand this ie: add a generic one */
+            guint32 value;
+            const guint8 *ptr;
+            const gchar *ie_name = val_to_str(ies_type, iax_ies_type, "Unknown");
+
+            switch(ies_len) {
+              case 1:
+                value = tvb_get_guint8(tvb, offset + 2);
+                ie_item =
+                  proto_tree_add_uint_format(ies_tree, hf_IAX_IE_UNKNOWN_BYTE,
+                                             tvb, offset+2, 1, value,
+                                             "%s: %#02x", ie_name, value );
+                break;
+
+              case 2:
+                value = tvb_get_ntohs(tvb, offset + 2);
+                ie_item =
+                  proto_tree_add_uint_format(ies_tree, hf_IAX_IE_UNKNOWN_I16,
+                                             tvb, offset+2, 2, value,
+                                             "%s: %#04x", ie_name, value );
+                break;
+
+              case 4:
+                value = tvb_get_ntohl(tvb, offset + 2);
+                ie_item =
+                  proto_tree_add_uint_format(ies_tree, hf_IAX_IE_UNKNOWN_I32,
+                                             tvb, offset+2, 4, value,
+                                             "%s: %#08x", ie_name, value );
+                break;
+
+              default:
+                ptr = tvb_get_ptr(tvb, offset + 2, ies_len);
+                ie_item =
+                  proto_tree_add_string_format(ies_tree, hf_IAX_IE_UNKNOWN_BYTES,
+                                               tvb, offset+2, ies_len, ptr,
+                                               "%s: %s", ie_name, ptr );
+                break;
+            }
+          }
+          break;
       }
+
+      /* by now, we *really* ought to have added an item */
+      DISSECTOR_ASSERT(ie_item != NULL);
+
+      /* Retrieve the text from the item we added, and append it to the main IE
+       * item */
+      if(!PROTO_ITEM_IS_HIDDEN(ti)) {
+        field_info *ie_finfo = PITEM_FINFO(ie_item);
+
+        /* if the representation of the item has already been set, use that;
+           else we have to allocate a block to put the text into */
+        if( ie_finfo && ie_finfo->rep != NULL )
+          proto_item_set_text(ti, "Information Element: %s",
+                              ie_finfo->rep->representation);
+        else {
+          guint8 *ie_val = NULL;
+          ie_val = g_malloc(ITEM_LABEL_LENGTH);
+          proto_item_fill_label(ie_finfo, ie_val);
+          proto_item_set_text(ti, "Information Element: %s",
+                              ie_val);
+          g_free(ie_val);
+        }
       }
     }
+
     offset += ies_len + 2;
   }
   return offset;
@@ -1329,12 +1380,115 @@ static guint32 uncompress_subclass(guint8 csub)
     return (guint32)csub;
 }
 
+/* returns the new offset */
+static guint32 dissect_iax2_command(tvbuff_t * tvb, guint32 offset,
+                                    packet_info * pinfo, proto_tree *tree,
+                                    iax_packet_data *iax_packet)
+{
+  guint8 csub = tvb_get_guint8(tvb, offset);
+  guint8 address_data[MAX_ADDRESS];
+  iax2_ie_data ie_data;
+  iax_call_data *iax_call;
 
+  ie_data.peer_address.type = AT_NONE;
+  ie_data.peer_address.len = 0;
+  ie_data.peer_address.data = address_data;
+  ie_data.peer_ptype = 0;
+  ie_data.peer_port = 0;
+  ie_data.peer_callno = 0;
+  ie_data.dataformat = (guint32)-1;
+  iax_call = iax_packet -> call_data;
+
+  /* add the subclass */
+  proto_tree_add_uint (tree, hf_iax2_iax_csub, tvb, offset, 1, csub);
+  offset++;
+
+  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))
+    return offset;
+
+  offset = dissect_ies(tvb, offset, tree, &ie_data);
+
+  /* if this is a data call, set up a subdissector for the circuit */
+  if(iax_call && ie_data.dataformat != (guint32)-1 && iax_call -> subdissector == NULL) {
+    iax_call -> subdissector = dissector_get_port_handle(iax2_dataformat_dissector_table, ie_data.dataformat );
+    iax_call -> dataformat = ie_data.dataformat;
+  }
+
+  /* if this is a transfer request, record it in the call data */
+  if( csub == IAX_COMMAND_TXREQ && iax_packet -> first_time ) {
+    if( ie_data.peer_address.type != AT_NONE && ie_data.peer_callno != 0 ) {
+      guint tx_circuit = iax_circuit_lookup(&ie_data.peer_address,
+                                            ie_data.peer_ptype,
+                                            ie_data.peer_port,
+                                            ie_data.peer_callno);
+
+#if 0
+      g_debug("found transfer request for call %u->%u, to new id %u",
+                iax_call->forward_circuit_ids[0],
+                iax_call->reverse_circuit_ids[0],
+                tx_circuit);
+#endif
+
+      iax2_new_circuit_for_call(tx_circuit,pinfo->fd->num,iax_call,iax_packet->reversed);
+    }
+  }
+
+  return offset;
+}
+
+static void iax2_add_ts_fields(packet_info * pinfo, proto_tree * iax2_tree, iax_packet_data *iax_packet, guint16 shortts)
+{
+  guint32 longts = shortts;
+  nstime_t ts;
+  proto_item *item;
+
+  if(iax_packet->call_data == NULL) {
+    /* no call info for this frame; perhaps we missed the NEW packet */
+    return;
+  }
+
+  if(iax_packet->abstime.secs == -1) {
+    time_t start_secs = iax_packet->call_data->start_time.secs;
+    gint32 abs_secs = (gint32) (start_secs + longts/1000);
+
+    /* deal with short timestamps by assuming that packets are never more than
+     * 16 seconds late */
+    while(abs_secs < pinfo->fd->abs_ts.secs - 16) {
+      longts += 32768;
+      abs_secs = (gint32) (start_secs + longts/1000);
+    }
+
+    iax_packet->abstime.secs=abs_secs;
+    iax_packet->abstime.nsecs=iax_packet->call_data->start_time.nsecs + (longts % 1000) * 1000000;
+    if(iax_packet->abstime.nsecs >= 1000000000) {
+      iax_packet->abstime.nsecs -= 1000000000;
+      iax_packet->abstime.secs ++;
+    }
+  }
+  iax2_info->timestamp = longts;
+
+  if (iax2_tree) {
+    item = proto_tree_add_time(iax2_tree, hf_iax2_absts, NULL, 0, 0, &iax_packet->abstime);
+    PROTO_ITEM_SET_GENERATED(item);
+
+    ts  = pinfo->fd->abs_ts;
+    nstime_delta(&ts, &ts, &iax_packet->abstime);
+
+    item = proto_tree_add_time(iax2_tree, hf_iax2_lateness, NULL, 0, 0, &ts);
+    PROTO_ITEM_SET_GENERATED(item);
+  }
+}
+
+/* returns the new offset */
 static guint32
-dissect_fullpacket (tvbuff_t * tvb, guint32 offset, 
-                   guint16 scallno,
-                   packet_info * pinfo, proto_tree * iax2_tree,
-                   proto_tree * main_tree)
+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;
@@ -1349,10 +1503,8 @@ dissect_fullpacket (tvbuff_t * tvb, guint32 offset,
   gboolean reversed;
   gboolean rtp_marker;
 
-  circuit_t *circuit;
-
   /*
-   * remove the top bit for retransmission detection 
+   * remove the top bit for retransmission detection
    */
   dcallno = tvb_get_ntohs(tvb, offset);
   retransmission = dcallno & 0x8000;
@@ -1360,6 +1512,10 @@ dissect_fullpacket (tvbuff_t * tvb, guint32 offset,
   ts = tvb_get_ntohl(tvb, offset+2);
   type = tvb_get_guint8(tvb, offset + 8);
   csub = tvb_get_guint8(tvb, offset + 9);
+  iax2_info->ftype = type;
+  iax2_info->csub = csub;
+  iax2_info->scallno = scallno;
+  iax2_info->dcallno = dcallno;
 
   /* see if we've seen this packet before */
   iax_packet = (iax_packet_data *)p_get_proto_data(pinfo->fd,proto_iax2);
@@ -1368,80 +1524,72 @@ dissect_fullpacket (tvbuff_t * tvb, guint32 offset,
 
     if( type == AST_FRAME_IAX && csub == IAX_COMMAND_NEW ) {
       /* NEW packets start a new call */
-      iax_call = iax_new_circuit_details(pinfo,scallno,&circuit);
+      iax_call = iax_new_call(pinfo,scallno);
       reversed = FALSE;
     } else {
-      iax_call = iax_lookup_circuit_details(pinfo, scallno, dcallno,
-                                      &reversed, &circuit);
+      iax_call = iax_lookup_call(pinfo, scallno, dcallno,
+                                 &reversed);
     }
 
-    iax_packet = iax_new_packet_data(iax_call);
+    iax_packet = iax_new_packet_data(iax_call, reversed);
     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;
+    reversed = iax_packet->reversed;
   }
-   
+
+  iax2_populate_pinfo_from_packet_data(pinfo, iax_packet);
+
   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_item(iax2_tree, hf_iax2_retransmission, tvb, offset, 2, FALSE );
+
+      if( iax_call ) {
+        proto_item *item =
+          proto_tree_add_uint (iax2_tree, hf_iax2_callno, tvb, 0, 4,
+                             iax_call->forward_circuit_ids[0] );
+        PROTO_ITEM_SET_GENERATED(item);
+      }
 
       proto_tree_add_uint (iax2_tree, hf_iax2_ts, tvb, offset+2, 4, ts);
+      iax2_add_ts_fields(pinfo, iax2_tree, iax_packet, (guint16)ts);
 
       proto_tree_add_item (iax2_tree, hf_iax2_oseqno, tvb, offset+6, 1,
-                          FALSE);
+                           FALSE);
 
       proto_tree_add_item (iax2_tree, hf_iax2_iseqno, tvb, offset+7, 1,
-                          FALSE);
+                           FALSE);
       packet_type_base = proto_tree_add_uint (iax2_tree, hf_iax2_type, tvb,
-                                             offset+8, 1, type);
+                                              offset+8, 1, type);
 
       /* add the type-specific subtree */
       packet_type_tree = proto_item_add_subtree (packet_type_base, ett_iax2_type);
+  } else {
+    iax2_add_ts_fields(pinfo, iax2_tree, iax_packet, (guint16)ts);
   }
 
+
   /* 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);
+                  val_to_str (type, iax_frame_types, "Unknown (0x%02x)"),
+                  scallno, ts);
   }
+  iax2_info->messageName = val_to_str (type, iax_frame_types, "Unknown (0x%02x)");
 
   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 );
-    }
+    offset=dissect_iax2_command(tvb,offset+9,pinfo,packet_type_tree,iax_packet);
+    iax2_info->messageName = val_to_str (csub, iax_iax_subclasses, "unknown (0x%02x)");
+    iax2_info->callState = csub;
     break;
 
-  case AST_FRAME_DTMF:
-    proto_tree_add_text (packet_type_tree, tvb, offset+9, 1, "DTMF digit: %c", csub);
+  case AST_FRAME_DTMF_BEGIN:
+  case AST_FRAME_DTMF_END:
+    proto_tree_add_item (packet_type_tree, hf_iax2_dtmf_csub, tvb, offset+9, 1, FALSE);
     offset += 10;
 
     if (check_col (pinfo->cinfo, COL_INFO))
@@ -1451,12 +1599,14 @@ dissect_fullpacket (tvbuff_t * tvb, guint32 offset,
   case AST_FRAME_CONTROL:
     /* add the subclass */
     proto_tree_add_uint (packet_type_tree, hf_iax2_cmd_csub, tvb,
-                        offset+9, 1, csub);
+                         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)"));
+                       val_to_str (csub, iax_cmd_subclasses, "unknown (0x%02x)"));
+    iax2_info->messageName = val_to_str (csub, iax_cmd_subclasses, "unknown (0x%02x)");
+    if (csub <= 8) iax2_info->callState = tap_cmd_voip_state[csub];
     break;
 
   case AST_FRAME_VOICE:
@@ -1464,21 +1614,23 @@ dissect_fullpacket (tvbuff_t * tvb, guint32 offset,
     iax_packet -> codec = codec = uncompress_subclass(csub);
 
     if( packet_type_tree ) {
+      proto_item *item;
       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);
+      item = proto_tree_add_uint (packet_type_tree, hf_iax2_voice_codec, tvb, offset+9, 1, codec);
+      PROTO_ITEM_SET_GENERATED(item);
     }
 
     offset += 10;
 
     if( iax_call ) {
       if( reversed ) {
-       iax_call->dst_codec = codec;
+        iax_call->dst_codec = codec;
       } else {
-       iax_call->src_codec = codec;
+        iax_call->src_codec = codec;
       }
     }
 
-    dissect_payload(tvb, offset, pinfo, main_tree, ts, FALSE,iax_packet);
+    dissect_payload(tvb, offset, pinfo, iax2_tree, main_tree, ts, FALSE,iax_packet);
     break;
 
   case AST_FRAME_VIDEO:
@@ -1487,32 +1639,44 @@ dissect_fullpacket (tvbuff_t * tvb, guint32 offset,
     iax_packet -> codec = codec = uncompress_subclass((guint8) (csub & ~40));
 
     if( packet_type_tree ) {
+      proto_item *item;
       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);
+      item = proto_tree_add_uint (packet_type_tree, hf_iax2_video_codec, tvb, offset+9, 1, codec);
+      PROTO_ITEM_SET_GENERATED(item);
     }
 
     offset += 10;
 
-    if( iax_call ) {
+    if( iax_call && iax_packet -> first_time ) {
       if( reversed ) {
-       iax_call->dst_vformat = codec;
+        iax_call->dst_vformat = codec;
       } else {
-       iax_call->src_vformat = codec;
+        iax_call->src_vformat = codec;
       }
     }
 
-    if( rtp_marker && check_col (pinfo->cinfo, COL_INFO))
-      col_append_fstr (pinfo->cinfo, COL_INFO, ", Mark" );
+    if( rtp_marker )
+      col_append_str (pinfo->cinfo, COL_INFO, ", Mark" );
 
 
-    dissect_payload(tvb, offset, pinfo, main_tree, ts, TRUE, iax_packet);
+    dissect_payload(tvb, offset, pinfo, iax2_tree, main_tree, ts, TRUE, iax_packet);
     break;
 
+  case AST_FRAME_MODEM:
+    proto_tree_add_item (packet_type_tree, hf_iax2_modem_csub, tvb, offset+9, 1, FALSE);
+    offset += 10;
 
+    if (check_col (pinfo->cinfo, COL_INFO))
+      col_append_fstr (pinfo->cinfo, COL_INFO, " %s",
+                       val_to_str (csub, iax_modem_subclasses, "unknown (0x%02x)"));
+    break;
+
+  case AST_FRAME_HTML:
+  case AST_FRAME_CNG:
   default:
     proto_tree_add_uint (packet_type_tree, hf_iax2_csub, tvb, offset+9,
-                        1, csub);
+                         1, csub);
     offset += 10;
 
     if (check_col (pinfo->cinfo, COL_INFO))
@@ -1520,12 +1684,16 @@ dissect_fullpacket (tvbuff_t * tvb, guint32 offset,
     break;
   }
 
+  /* next time we come to parse this packet, don't propogate the codec into the
+   * call_data */
+  iax_packet->first_time = FALSE;
+
   return offset;
 }
 
 static iax_packet_data *iax2_get_packet_data_for_minipacket(packet_info * pinfo,
-                                                           guint16 scallno,
-                                                           gboolean video)
+                                                            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);
@@ -1533,33 +1701,35 @@ static iax_packet_data *iax2_get_packet_data_for_minipacket(packet_info * pinfo,
   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);
+    iax_call = iax_lookup_call(pinfo, scallno, 0, &reversed);
 
-    p = iax_new_packet_data(iax_call);
+    p = iax_new_packet_data(iax_call,reversed);
     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 ) 
+     if( video )
         p->codec = reversed ? iax_call -> dst_vformat : iax_call -> src_vformat;
-      else 
+      else
         p->codec = reversed ? iax_call -> dst_codec : iax_call -> src_codec;
     }
   }
+
+  iax2_populate_pinfo_from_packet_data(pinfo, p);
   return p;
 }
 
 
 static guint32 dissect_minivideopacket (tvbuff_t * tvb, guint32 offset,
-                                       guint16 scallno, packet_info * pinfo,
-                                       proto_tree * iax2_tree, proto_tree *main_tree)
+                                        guint16 scallno, packet_info * pinfo,
+                                        proto_tree * iax2_tree, proto_tree *main_tree)
 {
   guint32 ts;
   iax_packet_data *iax_packet;
   gboolean rtp_marker;
+  proto_item *item;
 
   ts = tvb_get_ntohs(tvb, offset);
 
@@ -1567,102 +1737,492 @@ static guint32 dissect_minivideopacket (tvbuff_t * tvb, guint32 offset,
   rtp_marker = ts & 0x8000 ? TRUE:FALSE;
   ts &= ~0x8000;
 
+  iax_packet = iax2_get_packet_data_for_minipacket(pinfo, scallno, TRUE);
 
   if( iax2_tree ) {
+    if( iax_packet->call_data ) {
+      item =
+        proto_tree_add_uint (iax2_tree, hf_iax2_callno, tvb, 0, 4,
+                                    iax_packet->call_data->forward_circuit_ids[0] );
+      PROTO_ITEM_SET_GENERATED(item);
+    }
+
     proto_tree_add_item (iax2_tree, hf_iax2_minividts, tvb, offset, 2, FALSE);
+    iax2_add_ts_fields(pinfo, iax2_tree, iax_packet, (guint16)ts);
     proto_tree_add_item (iax2_tree, hf_iax2_minividmarker, tvb, offset, 2, FALSE);
+  } else {
+    iax2_add_ts_fields(pinfo, iax2_tree, iax_packet, (guint16)ts);
   }
 
   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":"");
+      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, iax2_tree, main_tree, ts, TRUE, iax_packet);
 
-  dissect_payload(tvb, offset, pinfo, main_tree, ts, TRUE, iax_packet);
+  /* next time we come to parse this packet, don't propogate the codec into the
+   * call_data */
+  iax_packet->first_time = FALSE;
 
   return offset;
 }
 
 static guint32
 dissect_minipacket (tvbuff_t * tvb, guint32 offset, guint16 scallno, packet_info * pinfo, proto_tree * iax2_tree,
-                   proto_tree *main_tree)
+                    proto_tree *main_tree)
 {
   guint32 ts;
   iax_packet_data *iax_packet;
+  proto_item *item;
 
   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);
+
+  if( iax2_tree ) {
+    if( iax_packet->call_data ) {
+      item = proto_tree_add_uint (iax2_tree, hf_iax2_callno, tvb, 0, 4,
+                                  iax_packet->call_data->forward_circuit_ids[0] );
+      PROTO_ITEM_SET_GENERATED(item);
+    }
+
+    proto_tree_add_uint (iax2_tree, hf_iax2_minits, tvb, offset, 2, ts);
+    iax2_add_ts_fields(pinfo, iax2_tree, iax_packet,(guint16)ts);
+  } else {
+    iax2_add_ts_fields(pinfo, iax2_tree, iax_packet, (guint16)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);
+      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);
+  dissect_payload(tvb, offset, pinfo, iax2_tree, main_tree, ts, FALSE, iax_packet);
+
 
+  /* next time we come to parse this packet, don't propagate the codec into the
+   * call_data */
+  iax_packet->first_time = FALSE;
 
   return offset;
 }
 
+
+static guint32 dissect_trunkcall_ts (tvbuff_t * tvb, guint32 offset, proto_tree * iax2_tree)
+{
+  proto_item *call_item;
+  proto_tree *call_tree;
+  guint16 datalen, rlen, ts, scallno;
+
+  /*
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |     Data Length (in octets)   |R|     Source Call Number      |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |           time-stamp          |                               |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |
+   |                                       Data                    |
+   :                                                               :
+   |                                                               |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  */
+
+  datalen = tvb_get_ntohs(tvb, offset);
+  scallno = tvb_get_ntohs(tvb, offset + 2);
+  ts = tvb_get_ntohs(tvb, offset + 4);
+
+  rlen = MIN(tvb_length(tvb) - offset - 6, datalen);
+
+  if( iax2_tree ) {
+    call_item = proto_tree_add_text(iax2_tree, tvb, offset, rlen + 6, "Trunk call from %u, ts: %u", scallno, ts);
+    call_tree = proto_item_add_subtree(call_item, ett_iax2_trunk_call);
+
+    proto_tree_add_item(call_tree, hf_iax2_trunk_call_len, tvb, offset, 2, FALSE);
+    proto_tree_add_item(call_tree, hf_iax2_trunk_call_scallno, tvb, offset + 2, 2, FALSE);
+    proto_tree_add_item(call_tree, hf_iax2_trunk_call_ts, tvb, offset + 4, 2, FALSE);
+
+  }
+
+  offset += 6 + rlen;
+
+  return offset;
+}
+
+static guint32 dissect_trunkcall_nots (tvbuff_t * tvb, guint32 offset, proto_tree * iax2_tree)
+{
+  proto_item *call_item;
+  proto_tree *call_tree;
+  guint16 datalen, rlen, scallno;
+
+  /*
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |R|      Source Call Number     |     Data Length (in octets)   |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                                                               |
+   :                             Data                              :
+   |                                                               |
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  */
+
+  scallno = tvb_get_ntohs(tvb, offset);
+  datalen = tvb_get_ntohs(tvb, offset + 2);
+
+  rlen = MIN(tvb_length(tvb) - offset - 4, datalen);
+
+  if( iax2_tree ) {
+    call_item = proto_tree_add_text(iax2_tree, tvb, offset, rlen + 6, "Trunk call from %u", scallno);
+    call_tree = proto_item_add_subtree(call_item, ett_iax2_trunk_call);
+
+    proto_tree_add_item(call_tree, hf_iax2_trunk_call_scallno, tvb, offset, 2, FALSE);
+    proto_tree_add_item(call_tree, hf_iax2_trunk_call_len, tvb, offset + 2, 2, FALSE);
+  }
+
+  offset += 4 + rlen;
+
+  return offset;
+}
+
+
+static guint32 dissect_trunkpacket (tvbuff_t * tvb, guint32 offset,
+                                        guint16 scallno, packet_info * pinfo,
+                                        proto_tree * iax2_tree, proto_tree *main_tree)
+{
+  guint32 ts;
+  guint8 cmddata, trunkts;
+  int ncalls = 0;
+  /*iax_packet_data *iax_packet;*/
+  proto_item *cd, *nc = NULL;
+  proto_tree *field_tree = NULL;
+
+  /* shut the compiler up */
+  scallno = scallno;
+  main_tree = main_tree;
+
+  cmddata = tvb_get_guint8(tvb, offset + 1);
+  trunkts = cmddata & IAX2_TRUNK_TS;
+
+  /* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1   */
+  /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */
+  /* |F|         Meta Indicator      |V|Meta Command | Cmd Data (0)  | */
+  /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */
+  /* |                            time-stamp                         | */
+  /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */
+
+
+  if( iax2_tree ) {
+    /* Meta Command */
+    proto_tree_add_item(iax2_tree, hf_iax2_trunk_metacmd, tvb, offset, 1, FALSE);
+
+    /* Command data */
+    cd = proto_tree_add_uint(iax2_tree, hf_iax2_trunk_cmddata, tvb, offset + 1, 1, cmddata);
+    field_tree = proto_item_add_subtree(cd, ett_iax2_trunk_cmddata);
+    if (trunkts)
+      proto_item_append_text(cd, " (trunk timestamps)");
+
+    /* CD -> Trunk timestamp */
+    proto_tree_add_boolean(field_tree, hf_iax2_trunk_cmddata_ts, tvb, offset + 1, 1, cmddata);
+
+    /* Timestamp */
+    ts = tvb_get_ntohs(tvb, offset);
+    proto_tree_add_item(iax2_tree, hf_iax2_trunk_ts, tvb, offset + 2, 4, FALSE);
+  }
+  
+  offset += 6;
+
+  if( trunkts ) {
+    /* Trunk calls with timestamp */
+    while(tvb_length_remaining(tvb, offset) >= 6) {
+      offset = dissect_trunkcall_ts (tvb, offset, iax2_tree);
+      ncalls++;
+    }
+  }
+  else {
+    /* Trunk calls without timestamp */
+    while(tvb_length_remaining(tvb, offset) >= 4) {
+      offset = dissect_trunkcall_nots (tvb, offset, iax2_tree);
+      ncalls++;
+    }
+  }
+
+  if( iax2_tree ) {
+    /* number of items */
+    nc = proto_tree_add_uint(iax2_tree, hf_iax2_trunk_ncalls, NULL, 0, 0, ncalls);
+    PROTO_ITEM_SET_GENERATED(nc);
+  }
+
+  col_add_fstr (pinfo->cinfo, COL_INFO, "Trunk packet with %d calls", ncalls);
+
+  return offset;
+}
+
+
+static void process_iax_pdu( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+                              gboolean video, iax_packet_data *iax_packet )
+{
+  guint32 codec = iax_packet -> codec;
+  iax_call_data *iax_call = iax_packet -> call_data;
+
+#ifdef DEBUG_DESEGMENT
+  g_debug("calling process_iax_pdu; len = %u", tvb_reported_length(tvb));
+#endif
+
+  if( !video && iax_call && iax_call->subdissector ) {
+    call_dissector(iax_call->subdissector, tvb, pinfo, tree);
+  }else if( codec != 0 && dissector_try_port(iax2_codec_dissector_table, codec, tvb, pinfo, tree )) {
+    /* codec dissector handled our data */
+  }else {
+    /* we don't know how to dissect our data: dissect it as data */
+    call_dissector(data_handle,tvb, pinfo, tree);
+  }
+
+#ifdef DEBUG_DESEGMENT
+  g_debug("called process_iax_pdu; pinfo->desegment_len=%u; pinfo->desegment_offset=%u",
+            pinfo->desegment_len, pinfo->desegment_offset);
+#endif
+}
+
+static void desegment_iax(tvbuff_t *tvb, packet_info *pinfo, proto_tree *iax2_tree,
+                          proto_tree *tree, gboolean video, iax_packet_data *iax_packet )
+{
+
+  iax_call_data *iax_call = iax_packet -> call_data;
+  iax_call_dirdata *dirdata;
+  gpointer value=NULL;
+  guint32 frag_offset=0;
+  fragment_data *fd_head;
+  gboolean must_desegment = FALSE;
+
+  DISSECTOR_ASSERT(iax_call);
+
+  pinfo->can_desegment = 2;
+  pinfo->desegment_offset = 0;
+  pinfo->desegment_len = 0;
+
+#ifdef DEBUG_DESEGMENT
+  g_debug("dissecting packet %u", pinfo->fd->num);
+#endif
+
+  dirdata = &(iax_call->dirdata[!!(iax_packet->reversed)]);
+
+  if((!pinfo->fd->flags.visited && dirdata->current_frag_bytes > 0) ||
+     (value = g_hash_table_lookup(iax_call->fid_table,
+                                  GUINT_TO_POINTER(pinfo->fd->num))) != NULL ) {
+    /* then we are continuing an already-started pdu */
+    guint32 fid;
+    guint32 frag_len = tvb_reported_length( tvb );
+    gboolean complete;
+
+#ifdef DEBUG_DESEGMENT
+    g_debug("visited: %i; c_f_b: %u; hash: %u->%u", pinfo->fd->flags.visited?1:0,
+            dirdata->current_frag_bytes, pinfo->fd->num, fid);
+#endif
+
+    if(!pinfo->fd->flags.visited) {
+      guint32 tot_len;
+      fid = dirdata->current_frag_id;
+      tot_len                      = dirdata->current_frag_minlen;
+      g_hash_table_insert( iax_call->fid_table, GUINT_TO_POINTER(pinfo->fd->num), GUINT_TO_POINTER(fid) );
+      frag_offset                  = dirdata->current_frag_bytes;
+      dirdata->current_frag_bytes += frag_len;
+      complete                     = dirdata->current_frag_bytes > tot_len;
+#ifdef DEBUG_DESEGMENT
+      g_debug("hash: %u->%u; frag_offset: %u; c_f_b: %u; totlen: %u",
+              pinfo->fd->num, fid, frag_offset, dirdata->current_frag_bytes, tot_len );
+#endif
+    } else {
+      fid = GPOINTER_TO_UINT(value);
+      /* these values are unused by fragment_add if pinfo->fd->flags.visited */
+      dirdata->current_frag_bytes = 0;
+      complete = FALSE;
+    }
+
+    /* fragment_add checks for already-added */
+    fd_head = fragment_add( tvb, 0, pinfo, fid,
+                            iax_call->fragment_table,
+                            frag_offset,
+                            frag_len, !complete );
+
+    if(fd_head && (pinfo->fd->num == fd_head->reassembled_in)) {
+      gint32 old_len;
+      tvbuff_t *next_tvb = tvb_new_child_real_data(tvb, fd_head->data, fd_head->datalen, fd_head->datalen);
+      add_new_data_source(pinfo, next_tvb, "Reassembled IAX2");
+
+      process_iax_pdu(next_tvb,pinfo,tree,video,iax_packet);
+
+      /* calculate the amount of data which was available to the higher-level
+         dissector before we added this segment; if the returned offset is
+         within that section, the higher-level dissector was unable to find any
+         pdus; if it's after that, it found one or more complete PDUs.
+      */
+      old_len = (gint32)(tvb_reported_length(next_tvb) - frag_len);
+      if( pinfo->desegment_len &&
+          pinfo->desegment_offset < old_len ) {
+        /* oops, it wasn't actually complete */
+        fragment_set_partial_reassembly(pinfo, fid, iax_call->fragment_table);
+        if(pinfo->desegment_len == DESEGMENT_ONE_MORE_SEGMENT) {
+          /* only one more byte should be enough for a retry */
+          dirdata->current_frag_minlen = fd_head->datalen + 1;
+        } else {
+          dirdata->current_frag_minlen = fd_head->datalen + pinfo->desegment_len;
+        }
+      } else {
+        /* we successfully dissected some data; create the proto tree items for
+         * the fragments, and flag any remaining data for desegmentation */
+
+        proto_item *iax_tree_item, *frag_tree_item;
+        /* this nargery is to insert the fragment tree into the main tree
+         * between the IAX protocol entry and the subdissector entry */
+        show_fragment_tree(fd_head, &iax2_fragment_items, tree, pinfo, next_tvb, &frag_tree_item);
+        iax_tree_item = proto_item_get_parent( proto_tree_get_parent( iax2_tree ));
+        if( frag_tree_item && iax_tree_item )
+          proto_tree_move_item( tree, iax_tree_item, frag_tree_item );
+
+        dirdata->current_frag_minlen = dirdata->current_frag_id = dirdata->current_frag_bytes = 0;
+
+        if( pinfo->desegment_len ) {
+          /* there's a bit of data left to desegment */
+          must_desegment = TRUE;
+          /* make desegment_offset relative to our tvb */
+          pinfo->desegment_offset -= old_len;
+        }
+
+        /* don't add a 'reassembled in' item for this pdu */
+        fd_head = NULL;
+      }
+    }
+  } else {
+    /* This segment was not found in our table, so it doesn't
+       contain a continuation of a higher-level PDU.
+       Call the normal subdissector.
+    */
+
+    process_iax_pdu(tvb,pinfo,tree,video,iax_packet);
+
+    if(pinfo->desegment_len) {
+      /* the higher-level dissector has asked for some more data - ie,
+         the end of this segment does not coincide with the end of a
+         higher-level PDU. */
+      must_desegment = TRUE;
+    }
+
+    fd_head = NULL;
+  }
+
+  /* must_desegment is set if the end of this segment (or the whole of it)
+   * contained the start of a higher-level PDU; we must add whatever is left of
+   * this segment (after pinfo->desegment_offset) to a fragment table for disassembly. */
+  if(must_desegment) {
+    guint32 fid = pinfo->fd->num; /* a new fragment id */
+    guint32 deseg_offset = pinfo->desegment_offset;
+    guint32 frag_len = tvb_reported_length_remaining(tvb,deseg_offset);
+    dirdata->current_frag_id = fid;
+    dirdata->current_frag_bytes = frag_len;
+
+    if(pinfo->desegment_len == DESEGMENT_ONE_MORE_SEGMENT) {
+      /* only one more byte should be enough for a retry */
+      dirdata->current_frag_minlen = frag_len + 1;
+    } else {
+      dirdata->current_frag_minlen = frag_len + pinfo->desegment_len;
+    }
+
+    fd_head = fragment_add(tvb, deseg_offset, pinfo, fid,
+                           iax_call->fragment_table,
+                           0, frag_len, TRUE );
+#ifdef DEBUG_DESEGMENT
+    g_debug("Start offset of undissected bytes: %u; "
+            "Bytes remaining in this segment: %u; min required bytes: %u\n",
+            deseg_offset, frag_len, frag_len + pinfo->desegment_len);
+#endif
+  }
+
+  /* add a 'reassembled in' item if necessary */
+  if( fd_head != NULL ) {
+    guint32 deseg_offset = pinfo->desegment_offset;
+    if( fd_head->reassembled_in != 0 &&
+        !(fd_head->flags & FD_PARTIAL_REASSEMBLY) ) {
+      proto_item *iax_tree_item;
+      iax_tree_item = proto_tree_add_uint( tree, hf_iax2_reassembled_in,
+                                           tvb, deseg_offset, tvb_reported_length_remaining(tvb,deseg_offset),
+                                           fd_head->reassembled_in);
+      PROTO_ITEM_SET_GENERATED(iax_tree_item);
+    } else {
+      /* this fragment is never reassembled */
+      proto_tree_add_text( tree, tvb, deseg_offset, -1,
+                           "IAX2 fragment, unfinished");
+    }
+
+    if( pinfo->desegment_offset == 0 ) {
+      col_set_str(pinfo->cinfo, COL_PROTOCOL, "IAX2");
+      col_set_str(pinfo->cinfo, COL_INFO, "[IAX2 segment of a reassembled PDU]");
+    }
+  }
+
+  pinfo->can_desegment = 0;
+  pinfo->desegment_offset = 0;
+  pinfo->desegment_len = 0;
+}
+
 static void dissect_payload(tvbuff_t *tvb, guint32 offset,
-                           packet_info *pinfo, proto_tree *tree,
-                           guint32 ts, gboolean video,
-                           iax_packet_data *iax_packet)
+                            packet_info *pinfo, proto_tree *iax2_tree,
+                            proto_tree *tree, guint32 ts, gboolean video,
+                            iax_packet_data *iax_packet)
 {
+#if 0
   gboolean out_of_order = FALSE;
+#endif
   tvbuff_t *sub_tvb;
   guint32 codec = iax_packet -> codec;
+  guint32 nbytes;
   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" );
+    col_append_str (pinfo->cinfo, COL_INFO, ", empty frame" );
     return;
   }
 
-  sub_tvb = tvb_new_subset(tvb, offset, -1, -1 );
+  sub_tvb = tvb_new_subset_remaining(tvb, offset);
 
   /* 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)"));
-
+                       val_to_str (iax_call -> dataformat,
+                                   iax_dataformats, "unknown (0x%02x)"));
+#if 0
       if( out_of_order )
-       col_append_fstr (pinfo->cinfo, COL_INFO, " (out-of-order packet)");
+        col_append_str (pinfo->cinfo, COL_INFO, " (out-of-order packet)");
+#endif
     } else {
       col_append_fstr (pinfo->cinfo, COL_INFO, ", %s",
-                      val_to_str (codec, codec_types, "unknown (0x%02x)"));
+                       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;
+  nbytes = tvb_reported_length(sub_tvb);
+  proto_tree_add_text( iax2_tree, sub_tvb, 0, -1,
+                       "IAX2 payload (%u byte%s)", nbytes,
+                       plurality( nbytes, "", "s" ));
 
-  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);
+  iax2_info->payload_len = nbytes;
+  iax2_info->payload_data = tvb_get_ptr(sub_tvb, 0, -1);
+
+  /* pass the rest of the block to a subdissector */
+  if(iax_packet->call_data)
+    desegment_iax( sub_tvb, pinfo, iax2_tree, tree, video, iax_packet );
+  else
+    process_iax_pdu(sub_tvb,pinfo,tree,video,iax_packet);
 }
 
 /*
@@ -1678,35 +2238,29 @@ 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",
+     {"Packet type", "iax2.packet_type", FT_UINT8, BASE_DEC, VALS(iax_packet_types), 0,
+      "Full/minivoice/minivideo/trunk packet",
       HFILL}},
 
+    {&hf_iax2_callno,
+     {"Call identifier", "iax2.call", FT_UINT32, BASE_DEC, NULL, 0,
+      "This is the identifier Wireshark assigns to identify this call. It does not correspond to any real field in the protocol", HFILL }},
 
     {&hf_iax2_scallno,
      {"Source call", "iax2.src_call", FT_UINT16, BASE_DEC, NULL, 0x7FFF,
@@ -1722,25 +2276,31 @@ proto_register_iax2 (void)
     {&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}},
+      "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",
+      "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",
+      "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",
+      "timestamp is the time, in ms after the start of this call, at which this packet was transmitted",
+      HFILL}},
+
+    {&hf_iax2_absts,
+     {"Absolute Time", "iax2.abstime", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0,
+      "The absolute time of this packet (calculated by adding the IAX timestamp to  the start time of this call)",
+      HFILL}},
+
+    {&hf_iax2_lateness,
+     {"Lateness", "iax2.lateness", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
+      "The lateness of this packet compared to its timestamp",
       HFILL}},
 
     {&hf_iax2_minividmarker,
@@ -1750,14 +2310,13 @@ proto_register_iax2 (void)
 
     {&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",
+      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",
+      "iseqno is the sequence no of the last successfully received packet",
       HFILL}},
 
     {&hf_iax2_type,
@@ -1767,358 +2326,447 @@ proto_register_iax2 (void)
       HFILL}},
 
     {&hf_iax2_csub,
-     {"Sub-class", "iax2.subclass", FT_UINT8, BASE_DEC, NULL, 0x0, 
-      "subclass",
+     {"Unknown subclass", "iax2.subclass", FT_UINT8, BASE_DEC, NULL, 0x0,
+      "Subclass of unknown type of full IAX2 frame",
+      HFILL}},
+
+    {&hf_iax2_dtmf_csub,
+     {"DTMF subclass (digit)", "iax2.dtmf.subclass", FT_STRINGZ, BASE_NONE, NULL, 0x0,
+      "DTMF subclass gives the DTMF digit",
       HFILL}},
 
     {&hf_iax2_cmd_csub,
      {"Control subclass", "iax2.control.subclass", FT_UINT8, BASE_DEC,
-      VALS (iax_cmd_subclasses), 0x0, 
+      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,
+     {"IAX subclass", "iax2.iax.subclass", FT_UINT8, BASE_DEC,
       VALS (iax_iax_subclasses),
-      0x0, 
-      "IAX type gives the command number for IAX signalling packets", HFILL}},
+      0x0,
+      "IAX subclass 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",
+     {"Voice Subclass (compressed codec no)", "iax2.voice.subclass", FT_UINT8, BASE_DEC, NULL, 0x0,
+      NULL,
       HFILL}},
 
     {&hf_iax2_voice_codec,
      {"CODEC", "iax2.voice.codec", FT_UINT32, BASE_HEX, VALS (codec_types),
-      0x0, 
+      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)",
+     {"Video Subclass (compressed codec no)", "iax2.video.subclass", FT_UINT8, BASE_DEC, NULL, 0xBF,
+      NULL,
       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, 
+     {"CODEC", "iax2.video.codec", FT_UINT32, BASE_HEX, VALS (codec_types), 0,
       "The codec used to encode video data", HFILL}},
-    
+
+    {&hf_iax2_modem_csub,
+     {"Modem subclass", "iax2.modem.subclass", FT_UINT8, BASE_DEC,
+      VALS (iax_modem_subclasses),
+      0x0,
+      "Modem subclass gives the type of modem", HFILL}},
+
+    {&hf_iax2_trunk_ts,
+     {"Timestamp", "iax2.timestamp", FT_UINT32, BASE_DEC, NULL, 0x0,
+      "timestamp is the time, in ms after the start ofCommand data this call, at which this trunk packet was transmitted",
+      HFILL}},
+
+    {&hf_iax2_trunk_metacmd,
+     {"Meta command", "iax2.trunk.metacmd", FT_UINT8, BASE_DEC, NULL, 0x7F,
+      "Meta command indicates whether or not the Meta Frame is a trunk.", HFILL}},
+
+    {&hf_iax2_trunk_cmddata,
+     {"Command data", "iax2.trunk.cmddata", FT_UINT8, BASE_HEX, NULL, 0x0,
+      "Flags for options that apply to a trunked call", HFILL}},
+
+    {&hf_iax2_trunk_cmddata_ts,
+     {"Trunk timestamps", "iax2.trunk.cmddata.ts", FT_BOOLEAN, 8, NULL, IAX2_TRUNK_TS,
+      "True: calls do each include their own timestamp", HFILL}},
+
+    {&hf_iax2_trunk_call_len,
+     {"Data length", "iax2.trunk.call.len", FT_UINT16, BASE_DEC, NULL, 0x0,
+      "Trunk call data length in octets", HFILL}},
+
+    {&hf_iax2_trunk_call_scallno,
+     {"Source call number", "iax2.trunk.call.scallno", FT_UINT16, BASE_DEC, NULL, 0x7FFF,
+      "Trunk call source call number", HFILL}},
+
+    {&hf_iax2_trunk_call_ts,
+     {"Timestamp", "iax2.trunk.call.ts", 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_trunk_ncalls,
+     {"Number of calls", "iax2.trunk.ncalls", FT_UINT16, BASE_DEC, NULL, 0x0,
+      "Number of calls in this trunk packet",
+      HFILL}},
     /*
      * Decoding for the ies
      */
 
     {&hf_IAX_IE_APPARENTADDR_SINFAMILY,
-     {"Family", "iax2.iax.app_addr.sinfamily", FT_UINT16, BASE_DEC, NULL, 0, "Family", HFILL }},
+     {"Family", "iax2.iax.app_addr.sinfamily", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
     {&hf_IAX_IE_APPARENTADDR_SINPORT,
-     {"Port", "iax2.iax.app_addr.sinport", FT_UINT16, BASE_DEC, NULL, 0, "Port", HFILL }},
+     {"Port", "iax2.iax.app_addr.sinport", FT_UINT16, BASE_DEC, NULL, 0, NULL, 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 }},
+     {"Address", "iax2.iax.app_addr.sinaddr", FT_IPv4, BASE_NONE, NULL, 0, NULL, HFILL }},
 
-    {&hf_IAX_IE_CALLED_NUMBER,
+    {&hf_iax2_ies[IAX_IE_CALLED_NUMBER],
      {"Number/extension being called", "iax2.iax.called_number",
       FT_STRING,
-      BASE_NONE, NULL, 0x0, "", HFILL}},
+      BASE_NONE, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_CALLING_NUMBER,
+    {&hf_iax2_ies[IAX_IE_CALLING_NUMBER],
      {"Calling number", "iax2.iax.calling_number", FT_STRING,
       BASE_NONE, NULL,
-      0x0, "", HFILL}},
+      0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_CALLING_ANI,
+    {&hf_iax2_ies[IAX_IE_CALLING_ANI],
      {"Calling number ANI for billing", "iax2.iax.calling_ani",
       FT_STRING,
-      BASE_NONE, NULL, 0x0, "", HFILL}},
+      BASE_NONE, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_CALLING_NAME,
+    {&hf_iax2_ies[IAX_IE_CALLING_NAME],
      {"Name of caller", "iax2.iax.calling_name", FT_STRING, BASE_NONE,
       NULL,
-      0x0, "", HFILL}},
+      0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_CALLED_CONTEXT,
+    {&hf_iax2_ies[IAX_IE_CALLED_CONTEXT],
      {"Context for number", "iax2.iax.called_context", FT_STRING,
       BASE_NONE,
-      NULL, 0x0, "", HFILL}},
+      NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_USERNAME,
+    {&hf_iax2_ies[IAX_IE_USERNAME],
      {"Username (peer or user) for authentication",
       "iax2.iax.username",
-      FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL}},
+      FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_PASSWORD,
+    {&hf_iax2_ies[IAX_IE_PASSWORD],
      {"Password for authentication", "iax2.iax.password", FT_STRING,
-      BASE_NONE, NULL, 0x0, "", HFILL}},
+      BASE_NONE, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_CAPABILITY,
+    {&hf_iax2_ies[IAX_IE_CAPABILITY],
      {"Actual codec capability", "iax2.iax.capability", FT_UINT32,
       BASE_HEX,
-      NULL, 0x0, "", HFILL}},
+      NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_FORMAT,
+    {&hf_iax2_ies[IAX_IE_FORMAT],
      {"Desired codec format", "iax2.iax.format", FT_UINT32, BASE_HEX,
-      VALS (codec_types), 0x0, "", HFILL}},
+      VALS (codec_types), 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_LANGUAGE,
+    {&hf_iax2_ies[IAX_IE_LANGUAGE],
      {"Desired language", "iax2.iax.language", FT_STRING, BASE_NONE,
       NULL,
-      0x0, "", HFILL}},
+      0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_VERSION,
+    {&hf_iax2_ies[IAX_IE_VERSION],
      {"Protocol version", "iax2.iax.version", FT_UINT16, BASE_HEX, NULL,
       0x0,
-      "", HFILL}},
+      NULL, HFILL}},
 
-    {&hf_IAX_IE_ADSICPE,
+    {&hf_iax2_ies[IAX_IE_ADSICPE],
      {"CPE ADSI capability", "iax2.iax.cpe_adsi", FT_UINT16, BASE_HEX,
       NULL,
-      0x0, "", HFILL}},
+      0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_DNID,
+    {&hf_iax2_ies[IAX_IE_DNID],
      {"Originally dialed DNID", "iax2.iax.dnid", FT_STRING, BASE_NONE,
       NULL,
-      0x0, "", HFILL}},
+      0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_AUTHMETHODS,
+    {&hf_iax2_ies[IAX_IE_AUTHMETHODS],
      {"Authentication method(s)", "iax2.iax.auth.methods", FT_UINT16,
       BASE_HEX,
-      NULL, 0x0, "", HFILL}},
+      NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_CHALLENGE,
+    {&hf_iax2_ies[IAX_IE_CHALLENGE],
      {"Challenge data for MD5/RSA", "iax2.iax.auth.challenge",
       FT_STRING,
-      BASE_NONE, NULL, 0x0, "", HFILL}},
+      BASE_NONE, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_MD5_RESULT,
+    {&hf_iax2_ies[IAX_IE_MD5_RESULT],
      {"MD5 challenge result", "iax2.iax.auth.md5", FT_STRING,
       BASE_NONE, NULL,
-      0x0, "", HFILL}},
+      0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_RSA_RESULT,
+    {&hf_iax2_ies[IAX_IE_RSA_RESULT],
      {"RSA challenge result", "iax2.iax.auth.rsa", FT_STRING,
       BASE_NONE, NULL,
-      0x0, "", HFILL}},
+      0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_REFRESH,
+    {&hf_iax2_ies[IAX_IE_REFRESH],
      {"When to refresh registration", "iax2.iax.refresh", FT_INT16,
       BASE_DEC,
-      NULL, 0x0, "", HFILL}},
+      NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_DPSTATUS,
+    {&hf_iax2_ies[IAX_IE_DPSTATUS],
      {"Dialplan status", "iax2.iax.dialplan_status", FT_UINT16,
       BASE_HEX, NULL,
-      0x0, "", HFILL}},
+      0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_CALLNO,
-     {"Call number of peer", "iax2.iax.call_no", FT_INT16, BASE_DEC,
+    {&hf_iax2_ies[IAX_IE_CALLNO],
+     {"Call number of peer", "iax2.iax.call_no", FT_UINT16, BASE_DEC,
       NULL,
-      0x0, "", HFILL}},
+      0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_CAUSE,
-     {"Cause", "iax2.iax.cause", FT_STRING, BASE_NONE, NULL, 0x0, "",
+    {&hf_iax2_ies[IAX_IE_CAUSE],
+     {"Cause", "iax2.iax.cause", FT_STRING, BASE_NONE, NULL, 0x0, NULL,
       HFILL}},
 
-    {&hf_IAX_IE_IAX_UNKNOWN,
+    {&hf_iax2_ies[IAX_IE_IAX_UNKNOWN],
      {"Unknown IAX command", "iax2.iax.iax_unknown", FT_BYTES,
-      BASE_HEX, NULL,
-      0x0, "", HFILL}},
+      BASE_NONE, NULL,
+      0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_MSGCOUNT,
+    {&hf_iax2_ies[IAX_IE_MSGCOUNT],
      {"How many messages waiting", "iax2.iax.msg_count", FT_INT16,
       BASE_DEC,
-      NULL, 0x0, "", HFILL}},
+      NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_AUTOANSWER,
+    {&hf_iax2_ies[IAX_IE_AUTOANSWER],
      {"Request auto-answering", "iax2.iax.autoanswer", FT_NONE,
       BASE_NONE,
-      NULL, 0x0, "", HFILL}},
+      NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_MUSICONHOLD,
+    {&hf_iax2_ies[IAX_IE_MUSICONHOLD],
      {"Request musiconhold with QUELCH", "iax2.iax.moh", FT_NONE,
       BASE_NONE,
-      NULL, 0x0, "", HFILL}},
+      NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_TRANSFERID,
+    {&hf_iax2_ies[IAX_IE_TRANSFERID],
      {"Transfer Request Identifier", "iax2.iax.transferid", FT_UINT32,
-      BASE_HEX, NULL, 0x0, "", HFILL}},
+      BASE_HEX, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_RDNIS,
+    {&hf_iax2_ies[IAX_IE_RDNIS],
      {"Referring DNIS", "iax2.iax.rdnis", FT_STRING, BASE_NONE, NULL,
-      0x0, "",
+      0x0, NULL,
       HFILL}},
 
-    {&hf_IAX_IE_PROVISIONING,
+    {&hf_iax2_ies[IAX_IE_PROVISIONING],
      {"Provisioning info","iax2.iax.provisioning", FT_STRING, BASE_NONE,
-       NULL, 0x0, "", HFILL}},
+       NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_AESPROVISIONING,
+    {&hf_iax2_ies[IAX_IE_AESPROVISIONING],
      {"AES Provisioning info","iax2.iax.aesprovisioning", FT_STRING, BASE_NONE,
-       NULL, 0x0, "", HFILL}},
+       NULL, 0x0, NULL, HFILL}},
+
+    {&hf_iax2_ies[IAX_IE_DATETIME],
+     {"Date/Time", "iax2.iax.datetime.raw", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_DATETIME,
-     {"Date/Time", "iax2.iax.datetime", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL}},
+    {&hf_iax2_ie_datetime,
+     {"Date/Time", "iax2.iax.datetime", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0x0, NULL, HFILL }},
 
-    {&hf_IAX_IE_DEVICETYPE,
-     {"Device type", "iax2.iax.devicetype", FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL}},
+    {&hf_iax2_ies[IAX_IE_DEVICETYPE],
+     {"Device type", "iax2.iax.devicetype", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_SERVICEIDENT,
-     {"Service identifier", "iax2.iax.serviceident", FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL}},
+    {&hf_iax2_ies[IAX_IE_SERVICEIDENT],
+     {"Service identifier", "iax2.iax.serviceident", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_FIRMWAREVER,
-     {"Firmware version", "iax2.iax.firmwarever", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL}},
+    {&hf_iax2_ies[IAX_IE_FIRMWAREVER],
+     {"Firmware version", "iax2.iax.firmwarever", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_FWBLOCKDESC,
-     {"Firmware block description", "iax2.iax.fwblockdesc", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL}},
+    {&hf_iax2_ies[IAX_IE_FWBLOCKDESC],
+     {"Firmware block description", "iax2.iax.fwblockdesc", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_FWBLOCKDATA,
-     {"Firmware block of data", "iax2.iax.fwblockdata", FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL}},
+    {&hf_iax2_ies[IAX_IE_FWBLOCKDATA],
+     {"Firmware block of data", "iax2.iax.fwblockdata", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_PROVVER,
-     {"Provisioning version", "iax2.iax.provver", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL}},
+    {&hf_iax2_ies[IAX_IE_PROVVER],
+     {"Provisioning version", "iax2.iax.provver", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_CALLINGPRES,
-     {"Calling presentation", "iax2.iax.callingpres", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL}},
+    {&hf_iax2_ies[IAX_IE_CALLINGPRES],
+     {"Calling presentation", "iax2.iax.callingpres", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_CALLINGTON,
-     {"Calling type of number", "iax2.iax.callington", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL}},
+    {&hf_iax2_ies[IAX_IE_CALLINGTON],
+     {"Calling type of number", "iax2.iax.callington", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_CALLINGTNS,
-     {"Calling transit network select", "iax2.iax.callingtns", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL}},
+    {&hf_iax2_ies[IAX_IE_CALLINGTNS],
+     {"Calling transit network select", "iax2.iax.callingtns", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_SAMPLINGRATE,
-     {"Supported sampling rates", "iax2.iax.samplingrate", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL}},
+    {&hf_iax2_ies[IAX_IE_SAMPLINGRATE],
+     {"Supported sampling rates", "iax2.iax.samplingrate", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_CAUSECODE,
+    {&hf_iax2_ies[IAX_IE_CAUSECODE],
      {"Hangup cause", "iax2.iax.causecode", FT_UINT8, BASE_HEX, VALS(iax_causecodes),
-       0x0, "", HFILL}},
+       0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_ENCRYPTION,
-     {"Encryption format", "iax2.iax.encryption", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL}},
+    {&hf_iax2_ies[IAX_IE_ENCRYPTION],
+     {"Encryption format", "iax2.iax.encryption", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_ENCKEY,
-     {"Encryption key", "iax2.iax.enckey", FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL}},
+    {&hf_iax2_ies[IAX_IE_ENCKEY],
+     {"Encryption key", "iax2.iax.enckey", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_CODEC_PREFS,
-     {"Codec negotiation", "iax2.iax.codecprefs", FT_STRING, BASE_NONE, NULL, 0x0, "", HFILL}},
+    {&hf_iax2_ies[IAX_IE_CODEC_PREFS],
+     {"Codec negotiation", "iax2.iax.codecprefs", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_RR_JITTER,
-     {"Received jitter (as in RFC1889)", "iax2.iax.rrjitter", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL}},
+    {&hf_iax2_ies[IAX_IE_RR_JITTER],
+     {"Received jitter (as in RFC1889)", "iax2.iax.rrjitter", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_RR_LOSS,
+    {&hf_iax2_ies[IAX_IE_RR_LOSS],
      {"Received loss (high byte loss pct, low 24 bits loss count, as in rfc1889)", "iax2.iax.rrloss",
-       FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL}},
+       FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_RR_PKTS,
-     {"Total frames received", "iax2.iax.rrpkts", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL}},
+    {&hf_iax2_ies[IAX_IE_RR_PKTS],
+     {"Total frames received", "iax2.iax.rrpkts", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_RR_DELAY,
-     {"Max playout delay in ms for received frames", "iax2.iax.rrdelay", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL}},
+    {&hf_iax2_ies[IAX_IE_RR_DELAY],
+     {"Max playout delay in ms for received frames", "iax2.iax.rrdelay", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_RR_DROPPED,
-     {"Dropped frames (presumably by jitterbuffer)", "iax2.iax.rrdropped", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL}},
+    {&hf_iax2_ies[IAX_IE_RR_DROPPED],
+     {"Dropped frames (presumably by jitterbuffer)", "iax2.iax.rrdropped", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_RR_OOO,
-     {"Frame received out of order", "iax2.iax.rrooo", FT_UINT32, BASE_HEX, NULL, 0x0, "", HFILL}},
+    {&hf_iax2_ies[IAX_IE_RR_OOO],
+     {"Frame received out of order", "iax2.iax.rrooo", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL}},
 
-    {&hf_IAX_IE_DATAFORMAT,
+    {&hf_iax2_ies[IAX_IE_DATAFORMAT],
      {"Data call format", "iax2.iax.dataformat", FT_UINT32, BASE_HEX,
-      VALS(iax_dataformats), 0x0, "", HFILL}},
+      VALS(iax_dataformats), 0x0, NULL, HFILL}},
 
     {&hf_IAX_IE_UNKNOWN_BYTE,
-     {"data", "iax2.iax.unknowndata", FT_UINT8, BASE_HEX, NULL,
+     {"Unknown", "iax2.iax.unknownbyte", 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,
+     {"Unknown", "iax2.iax.unknownshort", 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,
+     {"Unknown", "iax2.iax.unknownlong", 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,
+     {"Unknown", "iax2.iax.unknownstring", FT_STRING, 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 }},
+      TFS(&tfs_supported_not_supported), AST_FORMAT_G723_1,
+      NULL, HFILL }},
 
     {&hf_iax2_cap_gsm,
      {"GSM compression", "iax2.cap.gsm", FT_BOOLEAN, 32,
-       TFS(&supported_strings), AST_FORMAT_GSM, 
-      "GSM compression", HFILL }},
+       TFS(&tfs_supported_not_supported), AST_FORMAT_GSM,
+      NULL, 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 }},
+      TFS(&tfs_supported_not_supported), AST_FORMAT_ULAW,
+      NULL, 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 }},
+       TFS(&tfs_supported_not_supported), AST_FORMAT_ALAW,
+       NULL, HFILL }},
 
     {&hf_iax2_cap_g726,
      {"G.726 compression", "iax2.cap.g726",FT_BOOLEAN, 32,
-      TFS(&supported_strings), AST_FORMAT_G726,
-      "G.726 compression", HFILL }},
+      TFS(&tfs_supported_not_supported), AST_FORMAT_G726,
+      NULL, HFILL }},
 
     {&hf_iax2_cap_adpcm,
      {"ADPCM", "iax2.cap.adpcm", FT_BOOLEAN, 32,
-      TFS(&supported_strings), AST_FORMAT_ADPCM,
-      "ADPCM", HFILL }},
-    
+      TFS(&tfs_supported_not_supported), AST_FORMAT_ADPCM,
+      NULL, 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 }},
+     {"Raw 16-bit Signed Linear (8000 Hz) PCM", "iax2.cap.slinear",
+      FT_BOOLEAN, 32, TFS(&tfs_supported_not_supported), AST_FORMAT_SLINEAR,
+      NULL, 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 }},
+      TFS(&tfs_supported_not_supported), AST_FORMAT_LPC10,
+      NULL, HFILL }},
 
     {&hf_iax2_cap_g729a,
      {"G.729a Audio", "iax2.cap.g729a", FT_BOOLEAN, 32,
-      TFS(&supported_strings), AST_FORMAT_G729A,
-      "G.729a Audio", HFILL }},
+      TFS(&tfs_supported_not_supported), AST_FORMAT_G729A,
+      NULL, HFILL }},
 
     {&hf_iax2_cap_speex,
      {"SPEEX Audio", "iax2.cap.speex", FT_BOOLEAN, 32,
-      TFS(&supported_strings), AST_FORMAT_SPEEX,
-      "SPEEX Audio", HFILL }},
+      TFS(&tfs_supported_not_supported), AST_FORMAT_SPEEX,
+      NULL, 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 }},
+      TFS(&tfs_supported_not_supported), AST_FORMAT_ILBC,
+      NULL, HFILL }},
 
     {&hf_iax2_cap_jpeg,
      {"JPEG images", "iax2.cap.jpeg", FT_BOOLEAN, 32,
-      TFS(&supported_strings), AST_FORMAT_JPEG,
-      "JPEG images", HFILL }},
+      TFS(&tfs_supported_not_supported), AST_FORMAT_JPEG,
+      NULL, HFILL }},
 
     {&hf_iax2_cap_png,
      {"PNG images", "iax2.cap.png", FT_BOOLEAN, 32,
-      TFS(&supported_strings), AST_FORMAT_PNG,
-      "PNG images", HFILL }},
+      TFS(&tfs_supported_not_supported), AST_FORMAT_PNG,
+      NULL, HFILL }},
 
     {&hf_iax2_cap_h261,
      {"H.261 video", "iax2.cap.h261", FT_BOOLEAN, 32,
-      TFS(&supported_strings), AST_FORMAT_H261,
-      "H.261 video", HFILL }},
+      TFS(&tfs_supported_not_supported), AST_FORMAT_H261,
+      NULL, HFILL }},
 
     {&hf_iax2_cap_h263,
      {"H.263 video", "iax2.cap.h263", FT_BOOLEAN, 32,
-      TFS(&supported_strings), AST_FORMAT_H263,
-      "H.263 video", HFILL }}
+      TFS(&tfs_supported_not_supported), AST_FORMAT_H263,
+      NULL, HFILL }},
+
+    /* reassembly stuff */
+    {&hf_iax2_fragments,
+     {"IAX2 Fragments", "iax2.fragments", FT_NONE, BASE_NONE, NULL, 0x0,
+      NULL, HFILL }},
+
+    {&hf_iax2_fragment,
+     {"IAX2 Fragment data", "iax2.fragment", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+      NULL, HFILL }},
+
+    {&hf_iax2_fragment_overlap,
+     {"Fragment overlap", "iax2.fragment.overlap", FT_BOOLEAN, BASE_NONE,
+      NULL, 0x0, "Fragment overlaps with other fragments", HFILL }},
+
+    {&hf_iax2_fragment_overlap_conflict,
+     {"Conflicting data in fragment overlap", "iax2.fragment.overlap.conflict",
+      FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+      "Overlapping fragments contained conflicting data", HFILL }},
+
+    {&hf_iax2_fragment_multiple_tails,
+     {"Multiple tail fragments found", "iax2.fragment.multipletails",
+      FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+      "Several tails were found when defragmenting the packet", HFILL }},
+
+    {&hf_iax2_fragment_too_long_fragment,
+     {"Fragment too long", "iax2.fragment.toolongfragment",
+      FT_BOOLEAN, BASE_NONE, NULL, 0x0,
+      "Fragment contained data past end of packet", HFILL }},
+
+    {&hf_iax2_fragment_error,
+     {"Defragmentation error", "iax2.fragment.error",
+      FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+      "Defragmentation error due to illegal fragments", HFILL }},
+
+    {&hf_iax2_reassembled_in,
+     {"IAX2 fragment, reassembled in frame", "iax2.reassembled_in",
+      FT_FRAMENUM, BASE_NONE, NULL, 0x0,
+      "This IAX2 packet is reassembled in this frame", HFILL }},
+
+    {&hf_iax2_reassembled_length,
+     {"Reassembled IAX2 length", "iax2.reassembled.length",
+      FT_UINT32, BASE_DEC, NULL, 0x0,
+      "The total length of the reassembled payload", HFILL }}
   };
 
   static gint *ett[] = {
@@ -2127,9 +2775,16 @@ proto_register_iax2 (void)
     &ett_iax2_type,
     &ett_iax2_ie,
     &ett_iax2_codecs,
-    &ett_iax2_ies_apparent_addr
+    &ett_iax2_ies_apparent_addr,
+    &ett_iax2_fragment,
+    &ett_iax2_fragments,
+    &ett_iax2_trunk_cmddata,
+    &ett_iax2_trunk_call
   };
 
+  /* initialise the hf_iax2_ies[] array to -1 */
+  memset(hf_iax2_ies,0xff,sizeof(hf_iax2_ies));
+
   proto_iax2 =
     proto_register_protocol ("Inter-Asterisk eXchange v2", "IAX2", "iax2");
   proto_register_field_array (proto_iax2, hf, array_length (hf));
@@ -2141,25 +2796,34 @@ proto_register_iax2 (void)
     "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);
+  iax2_tap = register_tap("IAX2");
 }
 
 void
 proto_reg_handoff_iax2 (void)
 {
+  dissector_handle_t v110_handle = NULL;
+
   dissector_add("udp.port", IAX2_PORT, find_dissector("iax2"));
-  dissector_add("iax2.dataformat", AST_DATAFORMAT_V110, find_dissector("v110"));
+  v110_handle =  find_dissector("v110");
+  if(v110_handle)
+         dissector_add("iax2.dataformat", AST_DATAFORMAT_V110, v110_handle);
   data_handle = find_dissector("data");
 }
 
-
-/* 
- * This sets up the indentation style for this file in emacs.
+/*
+ * Editor modelines
  *
  * Local Variables:
  * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
  * End:
+ *
+ * ex: set shiftwidth=2 tabstop=8 expandtab
+ * :indentSize=2:tabSize=8:noTabs=true:
  */