GSM A DTAP: add UMTS EVS to supported codecs list IE
[metze/wireshark/wip.git] / epan / dissectors / packet-mgcp.c
index dc353f7727b12892c3b68b2cd66c7e1db9771b66..2d7b601120fe331373bd6f5c73efd4343fa3d895 100644 (file)
@@ -7,7 +7,7 @@
  * NCS 1.0: PacketCable Network-Based Call Signaling Protocol Specification,
  *          PKT-SP-EC-MGCP-I09-040113, January 13, 2004, Cable Television
  *          Laboratories, Inc., http://www.PacketCable.com/
- * NCS 1.5: PKT-SP-NCS1.5-I03-070412, April 12, 2007 Cable Television
+ * NCS 1.5: PKT-SP-NCS1.5-I04-120412, April 12, 2012 Cable Television
  *          Laboratories, Inc., http://www.PacketCable.com/
  * www.iana.org/assignments/mgcp-localconnectionoptions
  *
  * By Gerald Combs <gerald@wireshark.org>
  * Copyright 1999 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "config.h"
 #include <epan/conversation.h>
 #include <epan/tap.h>
 #include <epan/rtd_table.h>
+#include <epan/expert.h>
 #include "packet-mgcp.h"
 
+#include <wsutil/strtoi.h>
 
 #define TCP_PORT_MGCP_GATEWAY 2427
 #define UDP_PORT_MGCP_GATEWAY 2427
@@ -101,6 +91,31 @@ static int hf_mgcp_param_localconnoptions_rdir = -1;
 static int hf_mgcp_param_localconnoptions_rsh = -1;
 static int hf_mgcp_param_localconnoptions_mp = -1;
 static int hf_mgcp_param_localconnoptions_fxr = -1;
+static int hf_mgcp_param_localvoicemetrics = -1;
+static int hf_mgcp_param_remotevoicemetrics = -1;
+static int hf_mgcp_param_voicemetrics_nlr = -1;
+static int hf_mgcp_param_voicemetrics_jdr = -1;
+static int hf_mgcp_param_voicemetrics_bld = -1;
+static int hf_mgcp_param_voicemetrics_gld = -1;
+static int hf_mgcp_param_voicemetrics_bd = -1;
+static int hf_mgcp_param_voicemetrics_gd = -1;
+static int hf_mgcp_param_voicemetrics_rtd = -1;
+static int hf_mgcp_param_voicemetrics_esd = -1;
+static int hf_mgcp_param_voicemetrics_sl = -1;
+static int hf_mgcp_param_voicemetrics_nl = -1;
+static int hf_mgcp_param_voicemetrics_rerl = -1;
+static int hf_mgcp_param_voicemetrics_gmn = -1;
+static int hf_mgcp_param_voicemetrics_nsr = -1;
+static int hf_mgcp_param_voicemetrics_xsr = -1;
+static int hf_mgcp_param_voicemetrics_mlq = -1;
+static int hf_mgcp_param_voicemetrics_mcq = -1;
+static int hf_mgcp_param_voicemetrics_plc = -1;
+static int hf_mgcp_param_voicemetrics_jba = -1;
+static int hf_mgcp_param_voicemetrics_jbr = -1;
+static int hf_mgcp_param_voicemetrics_jbn = -1;
+static int hf_mgcp_param_voicemetrics_jbm = -1;
+static int hf_mgcp_param_voicemetrics_jbs = -1;
+static int hf_mgcp_param_voicemetrics_iaj = -1;
 static int hf_mgcp_param_connectionmode = -1;
 static int hf_mgcp_param_reqevents = -1;
 static int hf_mgcp_param_restartmethod = -1;
@@ -133,6 +148,7 @@ static int hf_mgcp_param_maxmgcpdatagram = -1;
 static int hf_mgcp_param_packagelist = -1;
 static int hf_mgcp_param_extension = -1;
 static int hf_mgcp_param_extension_critical = -1;
+static int hf_mgcp_param_resourceid = -1;
 static int hf_mgcp_param_invalid = -1;
 static int hf_mgcp_messagecount = -1;
 static int hf_mgcp_dup = -1;
@@ -143,6 +159,8 @@ static int hf_mgcp_rsp_dup_frame = -1;
 static int hf_mgcp_unknown_parameter = -1;
 static int hf_mgcp_malformed_parameter = -1;
 
+static expert_field ei_mgcp_rsp_rspcode_invalid = EI_INIT;
+
 static const value_string mgcp_return_code_vals[] = {
        {000, "Response Acknowledgement"},
        {100, "The transaction is currently being executed.  An actual completion message will follow on later."},
@@ -229,6 +247,8 @@ static int ett_mgcp = -1;
 static int ett_mgcp_param = -1;
 static int ett_mgcp_param_connectionparam = -1;
 static int ett_mgcp_param_localconnectionoptions = -1;
+static int ett_mgcp_param_localvoicemetrics = -1;
+static int ett_mgcp_param_remotevoicemetrics = -1;
 
 /*
  * Define the tap for mgcp
@@ -276,7 +296,12 @@ static void dissect_mgcp_connectionparams(proto_tree *parent_tree, tvbuff_t *tvb
 static void dissect_mgcp_localconnectionoptions(proto_tree *parent_tree, tvbuff_t *tvb,
                                                gint offset, gint param_type_len,
                                                gint param_val_len);
-
+static void dissect_mgcp_localvoicemetrics(proto_tree *parent_tree, tvbuff_t *tvb,
+                                               gint offset, gint param_type_len,
+                                               gint param_val_len);
+static void dissect_mgcp_remotevoicemetrics(proto_tree *parent_tree, tvbuff_t *tvb,
+                                               gint offset, gint param_type_len,
+                                               gint param_val_len);
 
 static void mgcp_raw_text_add(tvbuff_t *tvb, proto_tree *tree);
 
@@ -401,7 +426,7 @@ typedef struct _mgcp_call_info_key
        conversation_t *conversation;
 } mgcp_call_info_key;
 
-static GHashTable *mgcp_calls;
+static wmem_map_t *mgcp_calls;
 
 /* Compare 2 keys */
 static gint mgcp_call_equal(gconstpointer k1, gconstpointer k2)
@@ -470,7 +495,7 @@ static int dissect_mgcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, voi
                sectionlen = tvb_find_dot_line(tvb, tvb_sectionbegin, -1, &tvb_sectionend);
                if (sectionlen != -1)
                {
-                       dissect_mgcp_message(tvb_new_subset(tvb, tvb_sectionbegin,
+                       dissect_mgcp_message(tvb_new_subset_length_caplen(tvb, tvb_sectionbegin,
                                                sectionlen, sectionlen),
                                        pinfo, tree, mgcp_tree, ti);
                        tvb_sectionbegin = tvb_sectionend;
@@ -573,7 +598,7 @@ static void dissect_mgcp_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *
                sectionlen = tvb_find_line_end(tvb, 0, -1, &tvb_sectionend, FALSE);
                if (sectionlen > 0)
                {
-                       dissect_mgcp_firstline(tvb_new_subset(tvb, tvb_sectionbegin,
+                       dissect_mgcp_firstline(tvb_new_subset_length_caplen(tvb, tvb_sectionbegin,
                                               sectionlen, sectionlen), pinfo,
                                               mgcp_tree, mi);
                }
@@ -586,7 +611,7 @@ static void dissect_mgcp_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *
                                                        &tvb_sectionend);
                        if (sectionlen > 0)
                        {
-                               dissect_mgcp_params(tvb_new_subset(tvb, tvb_sectionbegin, sectionlen, sectionlen),
+                               dissect_mgcp_params(tvb_new_subset_length_caplen(tvb, tvb_sectionbegin, sectionlen, sectionlen),
                                                                   mgcp_tree, mi);
                        }
                }
@@ -633,18 +658,6 @@ static void mgcp_raw_text_add(tvbuff_t *tvb, proto_tree *tree)
        } while (tvb_offset_exists(tvb, tvb_lineend));
 }
 
-/* Discard and init any state we've saved */
-static void mgcp_init_protocol(void)
-{
-       mgcp_calls = g_hash_table_new(mgcp_call_hash, mgcp_call_equal);
-}
-
-static void mgcp_cleanup_protocol(void)
-{
-       g_hash_table_destroy(mgcp_calls);
-}
-
-
 /*
  * is_mgcp_verb - A function for determining whether there is a
  *                MGCP verb at offset in tvb
@@ -795,7 +808,7 @@ static gint tvb_parse_param(tvbuff_t* tvb, gint offset, gint len, int** hf, mgcp
 
        if (len > 0)
        {
-               tempchar = tvb_get_guint8(tvb, tvb_current_offset);
+               tempchar = (guint8)g_ascii_toupper(tvb_get_guint8(tvb, tvb_current_offset));
 
                switch (tempchar)
                {
@@ -856,6 +869,26 @@ static gint tvb_parse_param(tvbuff_t* tvb, gint offset, gint len, int** hf, mgcp
                                        *hf = &hf_mgcp_param_requestid;
                                        tvb_current_offset--;
                                }
+                               /* XRM/MCR */
+                               else
+                               if (len > (tvb_current_offset - offset) &&
+                                  (tempchar = (guint8)g_ascii_toupper(tvb_get_guint8(tvb,tvb_current_offset))) == 'R')
+                               {
+                                       /* Move past 'R' */
+                                       tvb_current_offset += 3;
+                                       if (len > (tvb_current_offset - offset) &&
+                                               (tempchar = (guint8)g_ascii_toupper(tvb_get_guint8(tvb,tvb_current_offset))) == 'R')
+                                       {
+                                               *hf = &hf_mgcp_param_remotevoicemetrics;
+                                       }
+                                       else
+                                       if (len > (tvb_current_offset - offset) &&
+                                          (tempchar = (guint8)g_ascii_toupper(tvb_get_guint8(tvb,tvb_current_offset))) == 'L')
+                                       {
+                                               *hf = &hf_mgcp_param_localvoicemetrics;
+                                       }
+                                       tvb_current_offset -= 4;
+                               }
 
                                /* X+...: or X-....: are vendor extension parameters */
                                else
@@ -900,7 +933,7 @@ static gint tvb_parse_param(tvbuff_t* tvb, gint offset, gint len, int** hf, mgcp
                        case 'M':
                                tvb_current_offset++;
                                if (len > (tvb_current_offset - offset) &&
-                                  (tempchar = tvb_get_guint8(tvb, tvb_current_offset)) == ':')
+                                  (tempchar = (guint8)g_ascii_toupper(tvb_get_guint8(tvb, tvb_current_offset))) == ':')
                                {
                                        *hf = &hf_mgcp_param_connectionmode;
                                        tvb_current_offset--;
@@ -914,7 +947,7 @@ static gint tvb_parse_param(tvbuff_t* tvb, gint offset, gint len, int** hf, mgcp
                        case 'R':
                                tvb_current_offset++;
                                if (len > (tvb_current_offset - offset) &&
-                                   (tempchar = tvb_get_guint8(tvb, tvb_current_offset)) == ':')
+                                   (tempchar = (guint8)g_ascii_toupper(tvb_get_guint8(tvb, tvb_current_offset))) == ':')
                                {
                                        *hf = &hf_mgcp_param_reqevents;
                                        tvb_current_offset--;
@@ -942,6 +975,18 @@ static gint tvb_parse_param(tvbuff_t* tvb, gint offset, gint len, int** hf, mgcp
                        case 'D':
                                if (tvb_get_guint8(tvb, tvb_current_offset+1) != ':')
                                {
+                                       if (len > (tvb_current_offset + 5 - offset) &&
+                                               (g_ascii_toupper(tvb_get_guint8(tvb, tvb_current_offset + 1) == 'Q')) &&
+                                               (                tvb_get_guint8(tvb, tvb_current_offset + 2) == '-' ) &&
+                                               (g_ascii_toupper(tvb_get_guint8(tvb, tvb_current_offset + 3) == 'R')) &&
+                                               (g_ascii_toupper(tvb_get_guint8(tvb, tvb_current_offset + 4) == 'I')) &&
+                                               (                tvb_get_guint8(tvb, tvb_current_offset + 5) == ':' )
+                                       ) {
+                                               tvb_current_offset+=4;
+                                               *hf = &hf_mgcp_param_resourceid;
+                                               break;
+                                       }
+
                                        *hf = &hf_mgcp_param_invalid;
                                        break;
                                }
@@ -960,7 +1005,7 @@ static gint tvb_parse_param(tvbuff_t* tvb, gint offset, gint len, int** hf, mgcp
                        case 'P':
                                tvb_current_offset++;
                                if (len > (tvb_current_offset - offset) &&
-                                   (tempchar = tvb_get_guint8(tvb, tvb_current_offset)) == ':')
+                                   (tempchar = (guint8)g_ascii_toupper(tvb_get_guint8(tvb, tvb_current_offset))) == ':')
                                {
                                        *hf = &hf_mgcp_param_connectionparam;
                                        tvb_current_offset--;
@@ -974,7 +1019,7 @@ static gint tvb_parse_param(tvbuff_t* tvb, gint offset, gint len, int** hf, mgcp
                        case 'E':
                                tvb_current_offset++;
                                if (len > (tvb_current_offset - offset) &&
-                                   (tempchar = tvb_get_guint8(tvb, tvb_current_offset)) == ':')
+                                   (tempchar = (guint8)g_ascii_toupper(tvb_get_guint8(tvb, tvb_current_offset))) == ':')
                                {
                                        *hf = &hf_mgcp_param_reasoncode;
                                        tvb_current_offset--;
@@ -988,7 +1033,7 @@ static gint tvb_parse_param(tvbuff_t* tvb, gint offset, gint len, int** hf, mgcp
                        case 'Z':
                                tvb_current_offset++;
                                if (len > (tvb_current_offset - offset) &&
-                                   (tempchar = tvb_get_guint8(tvb, tvb_current_offset)) == ':')
+                                   (tempchar = (guint8)g_ascii_toupper(tvb_get_guint8(tvb, tvb_current_offset))) == ':')
                                {
                                        *hf = &hf_mgcp_param_specificendpoint;
                                        tvb_current_offset--;
@@ -1062,7 +1107,8 @@ static gint tvb_parse_param(tvbuff_t* tvb, gint offset, gint len, int** hf, mgcp
 
        /* For these types, show the whole line */
        if ((*hf == &hf_mgcp_param_invalid) ||
-           (*hf == &hf_mgcp_param_extension) || (*hf == &hf_mgcp_param_extension_critical))
+           (*hf == &hf_mgcp_param_extension) || (*hf == &hf_mgcp_param_extension_critical) ||
+           (*hf == &hf_mgcp_param_localvoicemetrics) || (*hf == &hf_mgcp_param_remotevoicemetrics))
        {
                returnvalue = offset;
        }
@@ -1101,9 +1147,9 @@ static void dissect_mgcp_firstline(tvbuff_t *tvb, packet_info *pinfo, proto_tree
        mgcp_call_info_key *new_mgcp_call_key = NULL;
        mgcp_call_t *mgcp_call = NULL;
        nstime_t delta;
-       gint rspcode = 0;
        const gchar *verb_description = "";
        char code_with_verb[64] = "";  /* To fit "<4-letter-code> (<longest-verb>)" */
+       proto_item* pi;
 
        static address null_address = ADDRESS_INIT_NONE;
        tvb_previous_offset = 0;
@@ -1156,11 +1202,13 @@ static void dissect_mgcp_firstline(tvbuff_t *tvb, packet_info *pinfo, proto_tree
                                else
                                if (is_mgcp_rspcode(tvb, tvb_previous_offset, tvb_current_len))
                                {
+                                       gboolean rspcode_valid;
                                        mgcp_type = MGCP_RESPONSE;
-                                       rspcode = atoi(code);
-                                       mi->rspcode = rspcode;
-                                       proto_tree_add_uint(tree, hf_mgcp_rsp_rspcode, tvb,
-                                                           tvb_previous_offset, tokenlen, rspcode);
+                                       rspcode_valid = ws_strtou32(code, NULL, &mi->rspcode);
+                                       pi = proto_tree_add_uint(tree, hf_mgcp_rsp_rspcode, tvb,
+                                                           tvb_previous_offset, tokenlen, mi->rspcode);
+                                       if (!rspcode_valid)
+                                               expert_add_info(pinfo, pi, &ei_mgcp_rsp_rspcode_invalid);
                                }
                                else
                                {
@@ -1253,9 +1301,7 @@ static void dissect_mgcp_firstline(tvbuff_t *tvb, packet_info *pinfo, proto_tree
                                   to which the call was sent. */
                                if (pinfo->ptype == PT_TCP)
                                {
-                                       conversation = find_conversation(pinfo->num, &pinfo->src,
-                                                                        &pinfo->dst, pinfo->ptype, pinfo->srcport,
-                                                                        pinfo->destport, 0);
+                                       conversation = find_conversation_pinfo(pinfo, 0);
                                }
                                else
                                {
@@ -1265,7 +1311,7 @@ static void dissect_mgcp_firstline(tvbuff_t *tvb, packet_info *pinfo, proto_tree
                                         * if you do that.
                                         */
                                        conversation = find_conversation(pinfo->num, &null_address,
-                                                                        &pinfo->dst, pinfo->ptype, pinfo->srcport,
+                                                                        &pinfo->dst, conversation_pt_to_endpoint_type(pinfo->ptype), pinfo->srcport,
                                                                         pinfo->destport, 0);
                                }
                                if (conversation != NULL)
@@ -1274,7 +1320,7 @@ static void dissect_mgcp_firstline(tvbuff_t *tvb, packet_info *pinfo, proto_tree
                                           matching conversation is available. */
                                        mgcp_call_key.transid = mi->transid;
                                        mgcp_call_key.conversation = conversation;
-                                       mgcp_call = (mgcp_call_t *)g_hash_table_lookup(mgcp_calls, &mgcp_call_key);
+                                       mgcp_call = (mgcp_call_t *)wmem_map_lookup(mgcp_calls, &mgcp_call_key);
                                        if (mgcp_call)
                                        {
                                                /* Indicate the frame to which this is a reply. */
@@ -1355,9 +1401,7 @@ static void dissect_mgcp_firstline(tvbuff_t *tvb, packet_info *pinfo, proto_tree
                                 */
                                if (pinfo->ptype == PT_TCP)
                                {
-                                       conversation = find_conversation(pinfo->num, &pinfo->src,
-                                                                        &pinfo->dst, pinfo->ptype, pinfo->srcport,
-                                                                        pinfo->destport, 0);
+                                       conversation = find_conversation_pinfo(pinfo, 0);
                                }
                                else
                                {
@@ -1368,7 +1412,7 @@ static void dissect_mgcp_firstline(tvbuff_t *tvb, packet_info *pinfo, proto_tree
                                         * if you do that.
                                         */
                                        conversation = find_conversation(pinfo->num, &pinfo->src,
-                                                                        &null_address, pinfo->ptype, pinfo->srcport,
+                                                                        &null_address, conversation_pt_to_endpoint_type(pinfo->ptype), pinfo->srcport,
                                                                         pinfo->destport, 0);
                                }
                                if (conversation == NULL)
@@ -1377,13 +1421,13 @@ static void dissect_mgcp_firstline(tvbuff_t *tvb, packet_info *pinfo, proto_tree
                                        if (pinfo->ptype == PT_TCP)
                                        {
                                                conversation = conversation_new(pinfo->num, &pinfo->src,
-                                                                               &pinfo->dst, pinfo->ptype, pinfo->srcport,
+                                                                               &pinfo->dst, ENDPOINT_TCP, pinfo->srcport,
                                                                                pinfo->destport, 0);
                                        }
                                        else
                                        {
                                                conversation = conversation_new(pinfo->num, &pinfo->src,
-                                                                               &null_address, pinfo->ptype, pinfo->srcport,
+                                                                               &null_address, conversation_pt_to_endpoint_type(pinfo->ptype), pinfo->srcport,
                                                                                pinfo->destport, 0);
                                        }
                                }
@@ -1393,7 +1437,7 @@ static void dissect_mgcp_firstline(tvbuff_t *tvb, packet_info *pinfo, proto_tree
                                mgcp_call_key.conversation = conversation;
 
                                /* Look up the request */
-                               mgcp_call = (mgcp_call_t *)g_hash_table_lookup(mgcp_calls, &mgcp_call_key);
+                               mgcp_call = (mgcp_call_t *)wmem_map_lookup(mgcp_calls, &mgcp_call_key);
                                if (mgcp_call != NULL)
                                {
                                        /* We've seen a request with this TRANSID, with the same
@@ -1437,7 +1481,7 @@ static void dissect_mgcp_firstline(tvbuff_t *tvb, packet_info *pinfo, proto_tree
                                        g_strlcpy(mgcp_call->code, mi->code, 5);
 
                                        /* Store it */
-                                       g_hash_table_insert(mgcp_calls, new_mgcp_call_key, mgcp_call);
+                                       wmem_map_insert(mgcp_calls, new_mgcp_call_key, mgcp_call);
                                }
                                if (mgcp_call->rsp_num)
                                {
@@ -1515,6 +1559,20 @@ static void dissect_mgcp_params(tvbuff_t *tvb, proto_tree *tree, mgcp_info_t* mi
                                                        tvb_tokenbegin - tvb_linebegin, tokenlen);
                                }
                                else
+                               if (*my_param == hf_mgcp_param_localvoicemetrics)
+                               {
+                                       tokenlen = tvb_find_line_end(tvb,tvb_tokenbegin,-1,&tvb_lineend,FALSE);
+                                       dissect_mgcp_localvoicemetrics(mgcp_param_tree, tvb, tvb_linebegin,
+                                                                tvb_tokenbegin - tvb_linebegin, tokenlen);
+                               }
+                               else
+                               if (*my_param == hf_mgcp_param_remotevoicemetrics)
+                               {
+                                       tokenlen = tvb_find_line_end(tvb,tvb_tokenbegin,-1,&tvb_lineend,FALSE);
+                                       dissect_mgcp_remotevoicemetrics(mgcp_param_tree, tvb, tvb_linebegin,
+                                                                tvb_tokenbegin - tvb_linebegin, tokenlen);
+                               }
+                               else
                                {
                                        tokenlen = tvb_find_line_end(tvb, tvb_tokenbegin, -1, &tvb_lineend, FALSE);
                                        proto_tree_add_string(mgcp_param_tree, *my_param, tvb,
@@ -1794,7 +1852,153 @@ dissect_mgcp_localconnectionoptions(proto_tree *parent_tree, tvbuff_t *tvb, gint
                                proto_tree_add_string(tree, hf_mgcp_unknown_parameter, tvb, offset, tokenlen, tokens[i]);
                        }
                }
-               else
+       }
+}
+
+/* Dissect the Local Voice Metrics option */
+static void
+dissect_mgcp_localvoicemetrics(proto_tree *parent_tree, tvbuff_t *tvb, gint offset, gint param_type_len, gint param_val_len)
+{
+       proto_tree *tree = parent_tree;
+       proto_item *item = NULL;
+
+       gchar *tokenline = NULL;
+       gchar **tokens = NULL;
+       gchar **typval = NULL;
+       guint i = 0;
+       guint tokenlen = 0;
+       int hf_string = -1;
+
+       if (parent_tree)
+       {
+       item = proto_tree_add_item(parent_tree, hf_mgcp_param_localvoicemetrics, tvb, offset, param_type_len+param_val_len, ENC_ASCII|ENC_NA);
+               tree = proto_item_add_subtree(item, ett_mgcp_param_localvoicemetrics);
+       }
+
+       /* The XRM/LVM: line */
+       offset += 9; /* skip the XRM/LVM: */
+       tokenline = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, param_val_len - 9, ENC_ASCII);
+
+       /* Split into type=value pairs separated by comma and WSP */
+       tokens = wmem_strsplit(wmem_packet_scope(), tokenline, ",", -1);
+       for (i = 0; tokens[i] != NULL; i++)
+       {
+
+               tokenlen = (int)strlen(tokens[i]);
+               typval = wmem_strsplit(wmem_packet_scope(), tokens[i], "=", 2);
+               if ((typval[0] != NULL) && (typval[1] != NULL))
+               {
+                       if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "NLR"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_nlr;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "JDR"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_jdr;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "BLD"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_bld;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "GLD"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_gld;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "BD"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_bd;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "GD"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_gd;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "RTD"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_rtd;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "ESD"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_esd;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "SL"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_sl;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "NL"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_nl;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "RERL"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_rerl;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "GMN"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_gmn;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "NSR"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_nsr;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "XSR"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_xsr;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "MLQ"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_mlq;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "MCQ"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_mcq;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "PLC"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_plc;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "JBA"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_jba;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "JBR"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_jbr;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "JBN"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_jbn;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "JBM"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_jbm;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "JBS"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_jbs;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "IAJ"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_iaj;
+                       }
+                       else
+                       {
+                               hf_string = -1;
+                       }
+
+                       /* Add item */
+                       if (tree)
+                       {
+                               if (hf_string != -1)
+                               {
+                                       proto_tree_add_string(tree, hf_string, tvb, offset, tokenlen, g_strstrip(typval[1]));
+                               }
+                               else
+                               {
+                                       proto_tree_add_string(tree, hf_mgcp_unknown_parameter, tvb, offset, tokenlen, tokens[i]);
+                               }
+                       }
+               }
+               else if (tree)
                {
                        proto_tree_add_string(tree, hf_mgcp_malformed_parameter, tvb, offset, tokenlen, tokens[i]);
                }
@@ -1802,7 +2006,155 @@ dissect_mgcp_localconnectionoptions(proto_tree *parent_tree, tvbuff_t *tvb, gint
        }
 }
 
+/* Dissect the Remote Voice Metrics option */
+static void
+dissect_mgcp_remotevoicemetrics(proto_tree *parent_tree, tvbuff_t *tvb, gint offset, gint param_type_len, gint param_val_len)
+{
+       proto_tree *tree = parent_tree;
+       proto_item *item = NULL;
 
+       gchar *tokenline = NULL;
+       gchar **tokens = NULL;
+       gchar **typval = NULL;
+       guint i = 0;
+       guint tokenlen = 0;
+       int hf_string = -1;
+
+       if (parent_tree)
+       {
+       item = proto_tree_add_item(parent_tree, hf_mgcp_param_remotevoicemetrics, tvb, offset, param_type_len+param_val_len, ENC_ASCII|ENC_NA);
+               tree = proto_item_add_subtree(item, ett_mgcp_param_remotevoicemetrics);
+       }
+
+       /* The XRM/RVM: line */
+       offset += 9; /* skip the XRM/RVM: */
+       tokenline = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, param_val_len - 9, ENC_ASCII);
+
+       /* Split into type=value pairs separated by comma and WSP */
+       tokens = wmem_strsplit(wmem_packet_scope(), tokenline, ",", -1);
+       for (i = 0; tokens[i] != NULL; i++)
+       {
+               tokenlen = (int)strlen(tokens[i]);
+               typval = wmem_strsplit(wmem_packet_scope(), tokens[i], "=", 2);
+               if ((typval[0] != NULL) && (typval[1] != NULL))
+               {
+                       if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "NLR"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_nlr;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "JDR"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_jdr;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "BLD"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_bld;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "GLD"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_gld;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "BD"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_bd;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "GD"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_gd;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "RTD"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_rtd;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "ESD"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_esd;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "SL"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_sl;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "NL"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_nl;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "RERL"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_rerl;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "GMN"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_gmn;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "NSR"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_nsr;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "XSR"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_xsr;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "MLQ"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_mlq;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "MCQ"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_mcq;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "PLC"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_plc;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "JBA"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_jba;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "JBR"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_jbr;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "JBN"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_jbn;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "JBM"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_jbm;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "JBS"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_jbs;
+                       }
+                       else if (!g_ascii_strcasecmp(g_strstrip(typval[0]), "IAJ"))
+                       {
+                               hf_string = hf_mgcp_param_voicemetrics_iaj;
+                       }
+                       else
+                       {
+                               hf_string = -1;
+                       }
+
+                       /* Add item */
+                       if (tree)
+                       {
+                               if (hf_string != -1)
+                               {
+                                       proto_tree_add_string(tree, hf_string, tvb, offset, tokenlen, g_strstrip(typval[1]));
+                               }
+                               else
+                               {
+                                       proto_tree_add_string(tree, hf_mgcp_unknown_parameter, tvb, offset, tokenlen, tokens[i]);
+                               }
+                       }
+               }
+               else if (tree)
+               {
+                       proto_tree_add_string(tree, hf_mgcp_malformed_parameter, tvb, offset, tokenlen, tokens[i]);
+               }
+               offset += tokenlen + 1; /* 1 extra for the delimiter */
+       }
+}
 
 /*
  * tvb_find_null_line - Returns the length from offset to the first null
@@ -1996,6 +2348,8 @@ void proto_reg_handoff_mgcp(void);
 
 void proto_register_mgcp(void)
 {
+       expert_module_t* expert_mgcp;
+
        static hf_register_info hf[] =
                {
                        { &hf_mgcp_req,
@@ -2133,6 +2487,81 @@ void proto_register_mgcp(void)
                        { &hf_mgcp_param_localconnoptions_fxr,
                          { "FXR (fxr/fx)", "mgcp.param.localconnectionoptions.fxr", FT_STRING, BASE_NONE, NULL, 0x0,
                            "FXR", HFILL }},
+                       { &hf_mgcp_param_localvoicemetrics,
+                         { "LocalVoiceMetrics (XRM/LVM)", "mgcp.param.localvoicemetrics", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Local Voice Metrics", HFILL }},
+                       { &hf_mgcp_param_remotevoicemetrics,
+                         { "RemoteVoiceMetrics (XRM/RVM)", "mgcp.param.remotevoicemetrics", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Remote Voice Metrics", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_nlr,
+                         { "Network packet loss rate(NLR)", "mgcp.param.voicemetrics.nlr", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics NLR", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_jdr,
+                         { "Jitter buffer discard rate(JDR)", "mgcp.param.voicemetrics.jdr", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics JDR", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_bld,
+                         { "Burst loss density(BLD)", "mgcp.param.voicemetrics.bld", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics BLD", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_gld,
+                         { "Gap loss density(GLD)", "mgcp.param.voicemetrics.gld", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics GLD", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_bd,
+                         { "Burst duration(BD)", "mgcp.param.voicemetrics.bd", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics BD", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_gd,
+                         { "Gap duration(GD)", "mgcp.param.voicemetrics.gd", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics GD", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_rtd,
+                         { "Round trip network delay(RTD)", "mgcp.param.voicemetrics.rtd", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics RTD", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_esd,
+                         { "End system delay(ESD)", "mgcp.param.voicemetrics.esd", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics ESD", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_sl,
+                         { "Signal level(SL)", "mgcp.param.voicemetrics.sl", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics SL", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_nl,
+                         { "Noise level(NL)", "mgcp.param.voicemetrics.nl", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metricsx NL", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_rerl,
+                         { "Residual echo return loss(RERL)", "mgcp.param.voicemetrics.rerl", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics ERL", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_gmn,
+                         { "Minimum gap threshold(GMN)", "mgcp.param.voicemetrics.gmn", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics GMN", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_nsr,
+                         { "R factor(NSR)", "mgcp.param.voicemetrics.nsr", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics NSR", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_xsr,
+                         { "External R factor(XSR)", "mgcp.param.voicemetrics.xsr", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics XSR", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_mlq,
+                         { "Estimated MOS-LQ(MLQ)", "mgcp.param.voicemetrics.mlq", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics MLQ", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_mcq,
+                         { "Estimated MOS-CQ(MCQ)", "mgcp.param.voicemetrics.mcq", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics MCQ", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_plc,
+                         { "Packet loss concealment type(PLC)", "mgcp.param.voicemetrics.plc", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics PLC", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_jba,
+                         { "Jitter Buffer Adaptive(JBA)", "mgcp.param.voicemetrics.jba", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics JBA", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_jbr,
+                         { "Jitter Buffer Rate(JBR)", "mgcp.param.voicemetrics.jbr", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics JBR", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_jbn,
+                         { "Nominal jitter buffer delay(JBN)", "mgcp.param.voicemetrics.jbn", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics JBN", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_jbm,
+                         { "Maximum jitter buffer delay(JBM)", "mgcp.param.voicemetrics.jbm", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics JBM", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_jbs,
+                         { "Absolute maximum jitter buffer delay(JBS)", "mgcp.param.voicemetrics.jbs", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics JBS", HFILL }},
+                       { &hf_mgcp_param_voicemetrics_iaj,
+                         { "Inter-arrival Jitter(IAJ)", "mgcp.param.voicemetrics.iaj", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Voice Metrics IAJ", HFILL }},
                        { &hf_mgcp_param_connectionmode,
                          { "ConnectionMode (M)", "mgcp.param.connectionmode", FT_STRING, BASE_NONE, NULL, 0x0,
                            "Connection Mode", HFILL }},
@@ -2229,6 +2658,9 @@ void proto_register_mgcp(void)
                        { &hf_mgcp_param_extension_critical,
                          { "Extension Parameter (critical)", "mgcp.param.extensioncritical", FT_STRING, BASE_NONE, NULL, 0x0,
                            "Critical Extension Parameter", HFILL }},
+                       { &hf_mgcp_param_resourceid,
+                         { "ResourceIdentifier (DQ-RI)", "mgcp.param.resourceid", FT_STRING, BASE_NONE, NULL, 0x0,
+                           "Resource Identifier", HFILL }},
                        { &hf_mgcp_param_invalid,
                          { "Invalid Parameter", "mgcp.param.invalid", FT_STRING, BASE_NONE, NULL, 0x0,
                            NULL, HFILL }},
@@ -2263,17 +2695,24 @@ void proto_register_mgcp(void)
                        &ett_mgcp,
                        &ett_mgcp_param,
                        &ett_mgcp_param_connectionparam,
-                       &ett_mgcp_param_localconnectionoptions
+                       &ett_mgcp_param_localconnectionoptions,
+                       &ett_mgcp_param_localvoicemetrics,
+                       &ett_mgcp_param_remotevoicemetrics
                };
 
+       static ei_register_info ei[] = {
+               { &ei_mgcp_rsp_rspcode_invalid, { "mgcp.rsp.rspcode.invalid", PI_MALFORMED, PI_ERROR,
+               "RSP code must be a string containing an integer", EXPFILL }}
+       };
+
        module_t *mgcp_module;
 
        /* Register protocol */
        proto_mgcp = proto_register_protocol("Media Gateway Control Protocol", "MGCP", "mgcp");
        proto_register_field_array(proto_mgcp, hf, array_length(hf));
        proto_register_subtree_array(ett, array_length(ett));
-       register_init_routine(&mgcp_init_protocol);
-       register_cleanup_routine(&mgcp_cleanup_protocol);
+
+       mgcp_calls = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), mgcp_call_hash, mgcp_call_equal);
 
        mgcp_handle = register_dissector("mgcp", dissect_mgcp, proto_mgcp);
 
@@ -2324,6 +2763,10 @@ void proto_register_mgcp(void)
        mgcp_tap = register_tap("mgcp");
 
        register_rtd_table(proto_mgcp, NULL, 1, NUM_TIMESTATS, mgcp_mesage_type, mgcpstat_packet, NULL);
+
+       expert_mgcp = expert_register_protocol(proto_mgcp);
+       expert_register_field_array(expert_mgcp, ei, array_length(ei));
+
 }
 
 /* The registration hand-off routine */
@@ -2362,6 +2805,7 @@ void proto_reg_handoff_mgcp(void)
        callagent_tcp_port = global_mgcp_callagent_tcp_port;
        callagent_udp_port = global_mgcp_callagent_udp_port;
 
+       /* Names of port preferences too specific to add "auto" preference here */
        dissector_add_uint("tcp.port", global_mgcp_gateway_tcp_port,   mgcp_tpkt_handle);
        dissector_add_uint("udp.port", global_mgcp_gateway_udp_port,   mgcp_handle);
        dissector_add_uint("tcp.port", global_mgcp_callagent_tcp_port, mgcp_tpkt_handle);