forked from osmocom/wireshark
gtp, gtpv2: improve request/reply matching algorithm
GTP tunnel endpoints (MMEs, GSNs...) will eventually reuse sequence number values. When handling long capture files this may lead to wrong request/reply pairs: a message may be considered as a reply to an old request sharing the same reused seq number Add an heuristic to the matching algorithm that involves timestamps: request/reply pair matches only if their timestamps are closer than a configurable threshold. If such value is 0 (default), timestamps are not used and only seq number values are evaluated (i.e. fall-back to old behavior) Note that a wrong match might lead to wrong (gtp-)association/session While at it, extend messagge list explicitly used by the algorithm Change-Id: I021e6e1ce1651a64d24b0664d6e27c9ba39c735c Reviewed-on: https://code.wireshark.org/review/27500 Petri-Dish: Roland Knall <rknall@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Roland Knall <rknall@gmail.com>
This commit is contained in:
parent
14456d0de8
commit
cbffd8c646
|
@ -40,6 +40,8 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <epan/packet.h>
|
||||
#include <epan/conversation.h>
|
||||
#include <epan/prefs.h>
|
||||
|
@ -88,6 +90,8 @@ static dissector_handle_t gtp_handle, gtp_prime_handle;
|
|||
static gboolean g_gtp_over_tcp = TRUE;
|
||||
gboolean g_gtp_session = FALSE;
|
||||
|
||||
static guint pref_pair_matching_max_interval_ms = 0; /* Default: disable */
|
||||
|
||||
static guint g_gtpv0_port = GTPv0_PORT;
|
||||
static guint g_gtpv1c_port = GTPv1C_PORT;
|
||||
static guint g_gtpv1u_port = GTPv1U_PORT;
|
||||
|
@ -3399,6 +3403,8 @@ gtp_sn_equal_matched(gconstpointer k1, gconstpointer k2)
|
|||
{
|
||||
const gtp_msg_hash_t *key1 = (const gtp_msg_hash_t *)k1;
|
||||
const gtp_msg_hash_t *key2 = (const gtp_msg_hash_t *)k2;
|
||||
double diff;
|
||||
nstime_t delta;
|
||||
|
||||
if ( key1->req_frame && key2->req_frame && (key1->req_frame != key2->req_frame) ) {
|
||||
return 0;
|
||||
|
@ -3408,6 +3414,13 @@ gtp_sn_equal_matched(gconstpointer k1, gconstpointer k2)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (pref_pair_matching_max_interval_ms) {
|
||||
nstime_delta(&delta, &key1->req_time, &key2->req_time);
|
||||
diff = fabs(nstime_to_msec(&delta));
|
||||
|
||||
return key1->seq_nr == key2->seq_nr && diff < pref_pair_matching_max_interval_ms;
|
||||
}
|
||||
|
||||
return key1->seq_nr == key2->seq_nr;
|
||||
}
|
||||
|
||||
|
@ -3416,6 +3429,15 @@ gtp_sn_equal_unmatched(gconstpointer k1, gconstpointer k2)
|
|||
{
|
||||
const gtp_msg_hash_t *key1 = (const gtp_msg_hash_t *)k1;
|
||||
const gtp_msg_hash_t *key2 = (const gtp_msg_hash_t *)k2;
|
||||
double diff;
|
||||
nstime_t delta;
|
||||
|
||||
if (pref_pair_matching_max_interval_ms) {
|
||||
nstime_delta(&delta, &key1->req_time, &key2->req_time);
|
||||
diff = fabs(nstime_to_msec(&delta));
|
||||
|
||||
return key1->seq_nr == key2->seq_nr && diff < pref_pair_matching_max_interval_ms;
|
||||
}
|
||||
|
||||
return key1->seq_nr == key2->seq_nr;
|
||||
}
|
||||
|
@ -3425,7 +3447,9 @@ gtp_match_response(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree, gint
|
|||
{
|
||||
gtp_msg_hash_t gcr, *gcrp = NULL;
|
||||
guint32 *session;
|
||||
|
||||
gcr.seq_nr=seq_nr;
|
||||
gcr.req_time = pinfo->abs_ts;
|
||||
|
||||
switch (msgtype) {
|
||||
case GTP_MSG_ECHO_REQ:
|
||||
|
@ -3434,6 +3458,9 @@ gtp_match_response(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree, gint
|
|||
case GTP_MSG_DELETE_PDP_REQ:
|
||||
case GTP_MSG_FORW_RELOC_REQ:
|
||||
case GTP_MSG_DATA_TRANSF_REQ:
|
||||
case GTP_MSG_SGSN_CNTXT_REQ:
|
||||
case GTP_MS_INFO_CNG_NOT_REQ:
|
||||
case GTP_MSG_IDENT_REQ:
|
||||
gcr.is_request=TRUE;
|
||||
gcr.req_frame=pinfo->num;
|
||||
gcr.rep_frame=0;
|
||||
|
@ -3444,6 +3471,9 @@ gtp_match_response(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree, gint
|
|||
case GTP_MSG_DELETE_PDP_RESP:
|
||||
case GTP_MSG_FORW_RELOC_RESP:
|
||||
case GTP_MSG_DATA_TRANSF_RESP:
|
||||
case GTP_MSG_SGSN_CNTXT_RESP:
|
||||
case GTP_MS_INFO_CNG_NOT_RES:
|
||||
case GTP_MSG_IDENT_RESP:
|
||||
gcr.is_request=FALSE;
|
||||
gcr.req_frame=0;
|
||||
gcr.rep_frame=pinfo->num;
|
||||
|
@ -3471,6 +3501,9 @@ gtp_match_response(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree, gint
|
|||
case GTP_MSG_DELETE_PDP_REQ:
|
||||
case GTP_MSG_FORW_RELOC_REQ:
|
||||
case GTP_MSG_DATA_TRANSF_REQ:
|
||||
case GTP_MSG_SGSN_CNTXT_REQ:
|
||||
case GTP_MS_INFO_CNG_NOT_REQ:
|
||||
case GTP_MSG_IDENT_REQ:
|
||||
gcr.seq_nr=seq_nr;
|
||||
|
||||
gcrp=(gtp_msg_hash_t *)g_hash_table_lookup(gtp_info->unmatched, &gcr);
|
||||
|
@ -3496,6 +3529,9 @@ gtp_match_response(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree, gint
|
|||
case GTP_MSG_DELETE_PDP_RESP:
|
||||
case GTP_MSG_FORW_RELOC_RESP:
|
||||
case GTP_MSG_DATA_TRANSF_RESP:
|
||||
case GTP_MSG_SGSN_CNTXT_RESP:
|
||||
case GTP_MS_INFO_CNG_NOT_RES:
|
||||
case GTP_MSG_IDENT_RESP:
|
||||
gcr.seq_nr=seq_nr;
|
||||
gcrp=(gtp_msg_hash_t *)g_hash_table_lookup(gtp_info->unmatched, &gcr);
|
||||
|
||||
|
@ -10280,6 +10316,7 @@ proto_register_gtp(void)
|
|||
&dissect_tpdu_as,
|
||||
gtp_decode_tpdu_as,
|
||||
FALSE);
|
||||
prefs_register_uint_preference(gtp_module, "pair_max_interval", "Max interval allowed in pair matching", "Request/reply pair matches only if their timestamps are closer than that value, in ms (default 0, i.e. don't use timestamps)", 10, &pref_pair_matching_max_interval_ms);
|
||||
|
||||
prefs_register_obsolete_preference(gtp_module, "v0_dissect_cdr_as");
|
||||
prefs_register_obsolete_preference(gtp_module, "v0_check_etsi");
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
#include "config.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <epan/packet.h>
|
||||
#include <epan/conversation.h>
|
||||
#include <epan/to_str.h>
|
||||
|
@ -1037,6 +1039,7 @@ static gint ett_gtpv2_ies[NUM_GTPV2_IES];
|
|||
#define PREF_DECODE_SRVCC_P2C_TRANS_CONT_TARGET_UTRAN 1
|
||||
static gint pref_decode_srvcc_p2c_trans_cont = PREF_DECODE_SRVCC_P2C_TRANS_CONT_NO;
|
||||
|
||||
static guint pref_pair_matching_max_interval_ms = 0; /* Default: disable */
|
||||
|
||||
/* Table 8.1-1: Information Element types for GTPv2 */
|
||||
static const value_string gtpv2_element_type_vals[] = {
|
||||
|
@ -1237,6 +1240,8 @@ gtpv2_sn_equal_matched(gconstpointer k1, gconstpointer k2)
|
|||
{
|
||||
const gtpv2_msg_hash_t *key1 = (const gtpv2_msg_hash_t *)k1;
|
||||
const gtpv2_msg_hash_t *key2 = (const gtpv2_msg_hash_t *)k2;
|
||||
double diff;
|
||||
nstime_t delta;
|
||||
|
||||
if (key1->req_frame && key2->req_frame && (key1->req_frame != key2->req_frame)) {
|
||||
return 0;
|
||||
|
@ -1246,6 +1251,13 @@ gtpv2_sn_equal_matched(gconstpointer k1, gconstpointer k2)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (pref_pair_matching_max_interval_ms) {
|
||||
nstime_delta(&delta, &key1->req_time, &key2->req_time);
|
||||
diff = fabs(nstime_to_msec(&delta));
|
||||
|
||||
return key1->seq_nr == key2->seq_nr && diff < pref_pair_matching_max_interval_ms;
|
||||
}
|
||||
|
||||
return key1->seq_nr == key2->seq_nr;
|
||||
}
|
||||
|
||||
|
@ -1254,6 +1266,15 @@ gtpv2_sn_equal_unmatched(gconstpointer k1, gconstpointer k2)
|
|||
{
|
||||
const gtpv2_msg_hash_t *key1 = (const gtpv2_msg_hash_t *)k1;
|
||||
const gtpv2_msg_hash_t *key2 = (const gtpv2_msg_hash_t *)k2;
|
||||
double diff;
|
||||
nstime_t delta;
|
||||
|
||||
if (pref_pair_matching_max_interval_ms) {
|
||||
nstime_delta(&delta, &key1->req_time, &key2->req_time);
|
||||
diff = fabs(nstime_to_msec(&delta));
|
||||
|
||||
return key1->seq_nr == key2->seq_nr && diff < pref_pair_matching_max_interval_ms;
|
||||
}
|
||||
|
||||
return key1->seq_nr == key2->seq_nr;
|
||||
}
|
||||
|
@ -6936,7 +6957,9 @@ gtpv2_match_response(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree, gin
|
|||
{
|
||||
gtpv2_msg_hash_t gcr, *gcrp = NULL;
|
||||
guint32 *session;
|
||||
|
||||
gcr.seq_nr = seq_nr;
|
||||
gcr.req_time = pinfo->abs_ts;
|
||||
|
||||
switch (msgtype) {
|
||||
case GTPV2_CREATE_SESSION_REQUEST:
|
||||
|
@ -9791,6 +9814,8 @@ void proto_register_gtpv2(void)
|
|||
"This is needed until there's a reliable way to determine the contents of the transparent containers.",
|
||||
&pref_decode_srvcc_p2c_trans_cont, decode_srvcc_ps_to_cs_trans_cont_vals, FALSE);
|
||||
|
||||
prefs_register_uint_preference(gtpv2_module, "pair_max_interval", "Max interval allowed in pair matching", "Request/reply pair matches only if their timestamps are closer than that value, in ms (default 0, i.e. don't use timestamps)", 10, &pref_pair_matching_max_interval_ms);
|
||||
|
||||
proto_register_field_array(proto_gtpv2, hf_gtpv2, array_length(hf_gtpv2));
|
||||
proto_register_subtree_array(ett_gtpv2_array, array_length(ett_gtpv2_array));
|
||||
expert_gtpv2 = expert_register_protocol(proto_gtpv2);
|
||||
|
|
Loading…
Reference in New Issue