wiretap: Add a phone log reader.
Change-Id: I0b290df4783616f1eb15e6ad35fbd6d2b4c3dbdd Reviewed-on: https://code.wireshark.org/review/33865 Petri-Dish: Anders Broman <a.broman58@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
parent
bfeb46b1cf
commit
d8ad7a6863
|
@ -124,6 +124,7 @@ set(CLEAN_ASN1_DISSECTOR_SRC
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/packet-lcsap.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/packet-ldap.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/packet-lnpdqp.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/packet-log3gpp.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/packet-logotypecertextn.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/packet-lpp.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/packet-lppa.c
|
||||
|
|
|
@ -0,0 +1,801 @@
|
|||
/* packet-log3gpp.c
|
||||
* Routines for dissecting phone logs containing 3GPP protocol messages.
|
||||
* Copyright 2008, Vincent Helfre
|
||||
*
|
||||
* Wireshark - Network traffic analyzer
|
||||
* By Gerald Combs <gerald@wireshark.org>
|
||||
* Copyright 1998 Gerald Combs
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <epan/packet.h>
|
||||
#include <epan/prefs.h>
|
||||
#include <epan/proto_data.h>
|
||||
|
||||
#include <wsutil/strtoi.h>
|
||||
|
||||
#include "packet-mac-lte.h"
|
||||
#include "packet-pdcp-lte.h"
|
||||
#include "packet-rlc-lte.h"
|
||||
|
||||
#define FD1 0
|
||||
#define REL8 1
|
||||
|
||||
void proto_register_log3gpp(void);
|
||||
void proto_reg_handoff_log3gpp(void);
|
||||
|
||||
/* Protocol and registered fields. */
|
||||
static int proto_log3gpp = -1;
|
||||
|
||||
|
||||
static int hf_log3gpp_timestamp = -1;
|
||||
static int hf_log3gpp_protocol = -1;
|
||||
static int hf_log3gpp_direction = -1;
|
||||
static int hf_log3gpp_dissector_option = -1;
|
||||
static int hf_log3gpp_unparsed_data = -1;
|
||||
static int hf_log3gpp_dissected_length = -1;
|
||||
|
||||
/* Protocol subtree. */
|
||||
static int ett_log3gpp = -1;
|
||||
|
||||
/* Variables used to select a version for RRC and NAS */
|
||||
static int lte_rrc_prot_version = REL8;
|
||||
static int nas_eps_prot_version = REL8;
|
||||
|
||||
static const enum_val_t lte_rrc_dissector_version[] = {
|
||||
{"FD1", "FD1", FD1},
|
||||
{"Rel8 dec 2008", "Rel8 dec 2008", REL8}, /* Add new dissector version after */
|
||||
{NULL, NULL, -1}
|
||||
};
|
||||
|
||||
static const enum_val_t nas_eps_dissector_version[] = {
|
||||
{"FD1", "FD1", FD1},
|
||||
{"Rel8 dec 2008", "Rel8 dec 2008", REL8}, /* Add new dissector version after */
|
||||
{NULL, NULL, -1}
|
||||
};
|
||||
|
||||
typedef enum packet_direction_t
|
||||
{
|
||||
UPLINK,
|
||||
DOWNLINK
|
||||
} packet_direction_t;
|
||||
|
||||
static const value_string direction_vals[] = {
|
||||
{ 0, "Uplink" },
|
||||
{ 1, "Downlink" },
|
||||
{ 0, NULL },
|
||||
};
|
||||
/* Pseudo header functions*/
|
||||
typedef gboolean (*pseudo_hdr_func_ptr_t) (char *, packet_info *pinfo, guint16, packet_direction_t);
|
||||
|
||||
static gboolean lte_mac_pseudo_hdr(char *, packet_info *pinfo, guint16, guint8);
|
||||
static gboolean lte_rlc_pseudo_hdr(char *, packet_info *pinfo, guint16, guint8);
|
||||
static gboolean lte_pdcp_pseudo_hdr(char *, packet_info *pinfo, guint16, guint8);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char * protocol_name;
|
||||
const char * ul_dissector_name;
|
||||
const char * dl_dissector_name;
|
||||
dissector_handle_t ul_dissector_handle;
|
||||
dissector_handle_t dl_dissector_handle;
|
||||
pseudo_hdr_func_ptr_t hdr_process;
|
||||
} lookup_dissector_element_t;
|
||||
|
||||
/* Look up table for protocol name /dissector: should be in alphabetic order!!!
|
||||
the purpose is to match a protocol name with a dissector,
|
||||
and to store the dissector handle the first time to avoid looking it up every time.
|
||||
This table should contain all 3GPP specified protocols */
|
||||
lookup_dissector_element_t dissector_lookup_table[] = {
|
||||
{"DATA","data","data",0,0,NULL},
|
||||
{"GAN.TCP","umatcp","umatcp",0,0,NULL},
|
||||
{"GAN.UDP","umaudp","umaudp",0,0,NULL},
|
||||
{"GSM.CCCH","gsm_a_ccch","gsm_a_ccch",0,0,NULL},
|
||||
{"GSM.SACCH","gsm_a_sacch","gsm_a_sacch",0,0,NULL},
|
||||
{"GTP","gtp","gtp",0,0,NULL},
|
||||
{"LLC","llcgprs","llcgprs",0,0,NULL},
|
||||
{"LTE-MAC","mac-lte","mac-lte",0,0, (pseudo_hdr_func_ptr_t)lte_mac_pseudo_hdr},
|
||||
{"LTE-PDCP","pdcp-lte","pdcp-lte",0,0, (pseudo_hdr_func_ptr_t)lte_pdcp_pseudo_hdr},
|
||||
{"LTE-RLC","rlc-lte","rlc-lte",0,0, (pseudo_hdr_func_ptr_t)lte_rlc_pseudo_hdr},
|
||||
{"LTE-RRC.BCCH.BCH",0,0,0,0,NULL}, /* Dissector set according to preferences (depending on the release) */
|
||||
{"LTE-RRC.BCCH.DL.SCH",0,0,0,0,NULL}, /* Dissector set according to preferences (depending on the release) */
|
||||
{"LTE-RRC.CCCH",0,0,0,0,NULL}, /* Dissector set according to preferences (depending on the release) */
|
||||
{"LTE-RRC.DCCH",0,0,0,0,NULL}, /* Dissector set according to preferences (depending on the release) */
|
||||
{"LTE-RRC.PCCH",0,0,0,0,NULL}, /* Dissector set according to preferences (depending on the release) */
|
||||
{"NAS","gsm_a_dtap","gsm_a_dtap",0,0,NULL},
|
||||
{"NAS-EPS",0,0,0,0,NULL}, /* Dissector set according to preferences (depending on the release) */
|
||||
{"RRC.BCCH.BCH","rrc.bcch.bch","rrc.bcch.bch",0,0,NULL},
|
||||
{"RRC.BCCH.FACH","rrc.bcch.fach","rrc.bcch.fach",0,0,NULL},
|
||||
{"RRC.CCCH","rrc.ul.ccch","rrc.dl.ccch",0,0,NULL},
|
||||
{"RRC.DCCH","rrc.ul.dcch","rrc.dl.dcch",0,0,NULL},
|
||||
{"RRC.MCCH","rrc.mcch","rrc.mcch",0,0,NULL},
|
||||
{"RRC.MSCH","rrc.msch","rrc.msch",0,0,NULL},
|
||||
{"RRC.PCCH","rrc.pcch","rrc.pcch",0,0,NULL},
|
||||
{"RRC.SHCCH","rrc.ul.shcch","rrc.dl.shcch",0,0,NULL},
|
||||
{"RRC.SI.MIB","rrc.si.mib","rrc.si.mib",0,0,NULL},
|
||||
{"RRC.SI.SB1","rrc.sb1","rrc.sb1",0,0,NULL},
|
||||
{"RRC.SI.SB2","rrc.sb2","rrc.sb2",0,0,NULL},
|
||||
{"RRC.SI.SIB1","rrc.si.sib1","rrc.si.sib1",0,0,NULL},
|
||||
{"RRC.SI.SIB10","rrc.si.sib10","rrc.si.sib10",0,0,NULL},
|
||||
{"RRC.SI.SIB11","rrc.si.sib11","rrc.si.sib11",0,0,NULL},
|
||||
{"RRC.SI.SIB11bis","rrc.si.sib11bis","rrc.si.sib11bis",0,0,NULL},
|
||||
{"RRC.SI.SIB12","rrc.si.sib12","rrc.si.sib12",0,0,NULL},
|
||||
{"RRC.SI.SIB13","rrc.si.sib13","rrc.si.sib13",0,0,NULL },
|
||||
{"RRC.SI.SIB13-1","rrc.si.sib13-1","rrc.si.sib13-1",0,0,NULL},
|
||||
{"RRC.SI.SIB13-2","rrc.si.sib13-2","rrc.si.sib13-2",0,0,NULL},
|
||||
{"RRC.SI.SIB13-3","rrc.si.sib13-3","rrc.si.sib13-3",0,0,NULL },
|
||||
{"RRC.SI.SIB13-4","rrc.si.sib13-4","rrc.si.sib13-4",0,0,NULL},
|
||||
{"RRC.SI.SIB14","rrc.si.sib14","rrc.si.sib14",0,0,NULL},
|
||||
{"RRC.SI.SIB15","rrc.si.sib15","rrc.si.sib15",0,0,NULL},
|
||||
{"RRC.SI.SIB15bis","rrc.si.sib15bis","rrc.si.sib15bis",0,0,NULL},
|
||||
{"RRC.SI.SIB15-1","rrc.si.sib15-1","rrc.si.sib15-1",0,0,NULL},
|
||||
{"RRC.SI.SIB15-1bis","rrc.si.sib15-1bis","rrc.si.sib15-1bis",0,0,NULL },
|
||||
{"RRC.SI.SIB15-2","rrc.si.sib15-2","rrc.si.sib15-2",0,0,NULL},
|
||||
{"RRC.SI.SIB15-2bis","rrc.si.sib15-2bis","rrc.si.sib15-2bis",0,0,NULL},
|
||||
{"RRC.SI.SIB15-3","rrc.si.sib15-3","rrc.si.sib15-3",0,0,NULL},
|
||||
{"RRC.SI.SIB15-3bis","rrc.si.sib15-3bis","rrc.si.sib15-3bis",0,0,NULL},
|
||||
{"RRC.SI.SIB15-4","rrc.si.sib15-4","rrc.si.sib15-4",0,0,NULL},
|
||||
{"RRC.SI.SIB15-5","rrc.si.sib15-5","rrc.si.sib15-5",0,0,NULL},
|
||||
{"RRC.SI.SIB15-6","rrc.si.sib15-6","rrc.si.sib15-6",0,0,NULL},
|
||||
{"RRC.SI.SIB15-7","rrc.si.sib15-7","rrc.si.sib15-7",0,0,NULL},
|
||||
{"RRC.SI.SIB15-8","rrc.si.sib15-8","rrc.si.sib15-8",0,0,NULL},
|
||||
{"RRC.SI.SIB18","rrc.si.sib18","rrc.si.sib18",0,0,NULL},
|
||||
{"RRC.SI.SIB17","rrc.si.sib17","rrc.si.sib17",0,0,NULL},
|
||||
{"RRC.SI.SIB18","rrc.si.sib18","rrc.si.sib18",0,0,NULL},
|
||||
{"RRC.SI.SIB2","rrc.si.sib2","rrc.si.sib2",0,0,NULL},
|
||||
{"RRC.SI.SIB3","rrc.si.sib3","rrc.si.sib3",0,0,NULL},
|
||||
{"RRC.SI.SIB4","rrc.si.sib4","rrc.si.sib4",0,0,NULL},
|
||||
{"RRC.SI.SIB5","rrc.si.sib5","rrc.si.sib5",0,0,NULL},
|
||||
{"RRC.SI.SIB5bis","rrc.si.sib5bis","rrc.si.sib5bis",0,0,NULL},
|
||||
{"RRC.SI.SIB6","rrc.si.sib6","rrc.si.sib6",0,0,NULL},
|
||||
{"RRC.SI.SIB7","rrc.si.sib7","rrc.si.sib7",0,0,NULL},
|
||||
{"RRC.SI.SIB8","rrc.si.sib8","rrc.si.sib8",0,0,NULL},
|
||||
{"RRC.SI.SIB9","rrc.si.sib9","rrc.si.sib9",0,0,NULL},
|
||||
{"SNDCP","sndcp","sndcp",0,0,NULL},
|
||||
{"SNDCPXID","sndcpxid","sndcpxid",0,0,NULL}
|
||||
};
|
||||
|
||||
|
||||
static int
|
||||
dissector_element_compare(const void *protocol_name, const void *element)
|
||||
{
|
||||
return strcmp((const char *)protocol_name, ((const lookup_dissector_element_t *) element)->protocol_name);
|
||||
}
|
||||
|
||||
static dissector_handle_t
|
||||
look_for_dissector(char* protocol_name, packet_direction_t direction, pseudo_hdr_func_ptr_t* func_ptr _U_)
|
||||
{
|
||||
lookup_dissector_element_t* element_ptr;
|
||||
dissector_handle_t dissector_handle = NULL;
|
||||
|
||||
element_ptr = (lookup_dissector_element_t*)bsearch((void*)protocol_name,
|
||||
(void*)dissector_lookup_table,
|
||||
sizeof(dissector_lookup_table) / sizeof(lookup_dissector_element_t),
|
||||
sizeof(lookup_dissector_element_t),
|
||||
dissector_element_compare);
|
||||
if (element_ptr != NULL) {
|
||||
if (direction == UPLINK)
|
||||
{
|
||||
if (element_ptr->ul_dissector_handle == 0)
|
||||
{
|
||||
element_ptr->ul_dissector_handle = find_dissector(element_ptr->ul_dissector_name);
|
||||
}
|
||||
dissector_handle = element_ptr->ul_dissector_handle;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (element_ptr->dl_dissector_handle == 0)
|
||||
{
|
||||
element_ptr->dl_dissector_handle = find_dissector(element_ptr->dl_dissector_name);
|
||||
}
|
||||
dissector_handle = element_ptr->dl_dissector_handle;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return dissector_handle;
|
||||
}
|
||||
|
||||
static void
|
||||
update_dissector_name(const char* protocol_name, packet_direction_t direction, const char* dissector_name)
|
||||
{
|
||||
lookup_dissector_element_t* element_ptr;
|
||||
|
||||
element_ptr = (lookup_dissector_element_t*)bsearch((void*)protocol_name,
|
||||
(void*)dissector_lookup_table,
|
||||
sizeof(dissector_lookup_table) / sizeof(lookup_dissector_element_t),
|
||||
sizeof(lookup_dissector_element_t),
|
||||
dissector_element_compare);
|
||||
if (element_ptr != NULL) {
|
||||
if (direction == UPLINK)
|
||||
{
|
||||
element_ptr->ul_dissector_handle = 0;
|
||||
element_ptr->ul_dissector_name = dissector_name;
|
||||
}
|
||||
else
|
||||
{
|
||||
element_ptr->dl_dissector_handle = 0;
|
||||
element_ptr->dl_dissector_name = dissector_name;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* pseudo header functions: used for the dissectors that needs pseudo headers */
|
||||
/******************************************************************************/
|
||||
|
||||
|
||||
/* In the optional string, MAC info should be set as follow (M = mandatory, O = optional):
|
||||
* Radio type (M): "FDD" or "TDD"
|
||||
* RNTI type (M): "NO_RNTI" or "P_RNTI" or "RA_RNTI" or "C_RNTI" or "SI_RNT" followed by rnti value in decimal format
|
||||
* subframe number (M): "SFN" followed by the subframe number in decimal format
|
||||
*/
|
||||
static gboolean
|
||||
lte_mac_pseudo_hdr(char* option_str, packet_info* pinfo, guint16 length, guint8 direction)
|
||||
{
|
||||
struct mac_lte_info* p_mac_lte_info;
|
||||
char* par_opt_field;
|
||||
char option[30];
|
||||
static int proto_mac_lte = 0;
|
||||
|
||||
/* look up for protocol handle */
|
||||
if (proto_mac_lte == 0)
|
||||
{
|
||||
proto_mac_lte = proto_get_id_by_filter_name("mac-lte");
|
||||
}
|
||||
|
||||
/* Need to copy the string in a local buffer since strtok will modify it */
|
||||
g_strlcpy(option, option_str, 30);
|
||||
|
||||
/* Only need to set info once per session. */
|
||||
p_mac_lte_info = (struct mac_lte_info*)p_get_proto_data(wmem_file_scope(), pinfo, proto_mac_lte, 0);
|
||||
if (p_mac_lte_info != NULL)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Allocate & zero struct */
|
||||
p_mac_lte_info = (struct mac_lte_info*) wmem_new0(wmem_packet_scope(), mac_lte_info);
|
||||
|
||||
/* First mandatory parameter */
|
||||
par_opt_field = strtok(option, " ");
|
||||
if (strcmp(par_opt_field, "FDD") == 0)
|
||||
{
|
||||
p_mac_lte_info->radioType = FDD_RADIO;
|
||||
}
|
||||
else if (strcmp(par_opt_field, "TDD") == 0)
|
||||
{
|
||||
p_mac_lte_info->radioType = TDD_RADIO;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Second mandatory parameter */
|
||||
par_opt_field = strtok(NULL, " ");
|
||||
if (strcmp(par_opt_field, "NO_RNTI") == 0)
|
||||
{
|
||||
p_mac_lte_info->rntiType = NO_RNTI;
|
||||
}
|
||||
else if (strcmp(par_opt_field, "P_RNTI") == 0)
|
||||
{
|
||||
p_mac_lte_info->rntiType = P_RNTI;
|
||||
}
|
||||
else if (strcmp(par_opt_field, "RA_RNTI") == 0)
|
||||
{
|
||||
p_mac_lte_info->rntiType = RA_RNTI;
|
||||
}
|
||||
else if (strcmp(par_opt_field, "C_RNTI") == 0)
|
||||
{
|
||||
p_mac_lte_info->rntiType = C_RNTI;
|
||||
}
|
||||
else if (strcmp(par_opt_field, "SI_RNTI") == 0)
|
||||
{
|
||||
p_mac_lte_info->rntiType = SI_RNTI;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
/* Get the associated rnti value */
|
||||
par_opt_field = strtok(NULL, " ");
|
||||
if (par_opt_field)
|
||||
{
|
||||
ws_strtoi16(par_opt_field, NULL, &p_mac_lte_info->rnti);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* First optional parameter */
|
||||
p_mac_lte_info->subframeNumber = 0;
|
||||
par_opt_field = strtok(NULL, " ");
|
||||
|
||||
if (strcmp(par_opt_field, "SFN") == 0)
|
||||
{
|
||||
par_opt_field = strtok(NULL, " ");
|
||||
if (par_opt_field != NULL)
|
||||
{
|
||||
ws_strtoi16(par_opt_field, NULL, &p_mac_lte_info->subframeNumber);
|
||||
}
|
||||
}
|
||||
p_mac_lte_info->direction = direction;
|
||||
p_mac_lte_info->length = length;
|
||||
|
||||
/* Store info in packet */
|
||||
p_add_proto_data(wmem_file_scope(), pinfo, proto_mac_lte, 0, p_mac_lte_info);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* In the optional string, RLC info should be set as follow (M = mandatory, O = optional):
|
||||
* Channel type (M): "SRB" or "DRB" followed by the RB ID
|
||||
* RLC mode (M): "TM" or "UM" or "AM" or "NA"
|
||||
* UM Sequence nb length (O): "SN_5b" or "SN_10b"
|
||||
*/
|
||||
|
||||
static gboolean
|
||||
lte_rlc_pseudo_hdr(char* option_str, packet_info* pinfo, guint16 length, guint8 direction)
|
||||
{
|
||||
struct rlc_lte_info* p_rlc_lte_info;
|
||||
char* par_opt_field;
|
||||
char option[30];
|
||||
static int proto_rlc_lte = 0;
|
||||
|
||||
/* look up for protocol handle */
|
||||
if (proto_rlc_lte == 0)
|
||||
{
|
||||
proto_rlc_lte = proto_get_id_by_filter_name("rlc-lte");
|
||||
}
|
||||
g_strlcpy(option, option_str, 30);
|
||||
|
||||
/* Only need to set info once per session. */
|
||||
p_rlc_lte_info = (struct rlc_lte_info*)p_get_proto_data(wmem_file_scope(), pinfo, proto_rlc_lte, 0);
|
||||
if (p_rlc_lte_info != NULL)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Allocate & zero struct */
|
||||
p_rlc_lte_info = (struct rlc_lte_info*) wmem_new0(wmem_packet_scope(), rlc_lte_info);
|
||||
/* First mandatory parameter */
|
||||
par_opt_field = strtok(option, " ");
|
||||
|
||||
if (strcmp(par_opt_field, "SRB") == 0)
|
||||
{
|
||||
p_rlc_lte_info->channelType = CHANNEL_TYPE_SRB;
|
||||
}
|
||||
else if (strcmp(par_opt_field, "DRB") == 0)
|
||||
{
|
||||
p_rlc_lte_info->channelType = CHANNEL_TYPE_DRB;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
/* Fill in the RB ID */
|
||||
par_opt_field = strtok(NULL, " ");
|
||||
ws_strtou16(par_opt_field, NULL, &p_rlc_lte_info->channelId);
|
||||
|
||||
/* Second mandatory parameter */
|
||||
par_opt_field = strtok(NULL, " ");
|
||||
if (strcmp(par_opt_field, "TM") == 0)
|
||||
{
|
||||
p_rlc_lte_info->rlcMode = RLC_TM_MODE;
|
||||
}
|
||||
else if (strcmp(par_opt_field, "UM") == 0)
|
||||
{
|
||||
p_rlc_lte_info->rlcMode = RLC_UM_MODE;
|
||||
}
|
||||
else if (strcmp(par_opt_field, "AM") == 0)
|
||||
{
|
||||
p_rlc_lte_info->rlcMode = RLC_AM_MODE;
|
||||
}
|
||||
else if (strcmp(par_opt_field, "NA") == 0)
|
||||
{
|
||||
p_rlc_lte_info->rlcMode = RLC_PREDEF;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* First optional parameter */
|
||||
par_opt_field = strtok(NULL, " ");
|
||||
if (par_opt_field != NULL)
|
||||
{
|
||||
if (strcmp(par_opt_field, "SN_5b") == 0)
|
||||
{
|
||||
p_rlc_lte_info->sequenceNumberLength = UM_SN_LENGTH_5_BITS;
|
||||
}
|
||||
else if (strcmp(par_opt_field, "SN_10b") == 0)
|
||||
{
|
||||
p_rlc_lte_info->sequenceNumberLength = UM_SN_LENGTH_10_BITS;
|
||||
}
|
||||
}
|
||||
p_rlc_lte_info->direction = direction;
|
||||
p_rlc_lte_info->priority = 0;
|
||||
p_rlc_lte_info->ueid = 0;
|
||||
p_rlc_lte_info->pduLength = length;
|
||||
|
||||
/* Store info in packet */
|
||||
p_add_proto_data(wmem_file_scope(), pinfo, proto_rlc_lte, 0, p_rlc_lte_info);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* In the optional string, PDCP info should be set as follow (M = mandatory, O = optional):
|
||||
* Plane: "SRB" or "DRB"
|
||||
* Sequence number length: "SN_7b" or "SN_12b"
|
||||
*/
|
||||
static gboolean
|
||||
lte_pdcp_pseudo_hdr(char* option_str, packet_info* pinfo, guint16 length _U_, guint8 direction _U_)
|
||||
{
|
||||
struct pdcp_lte_info* p_pdcp_lte_info;
|
||||
char* par_opt_field;
|
||||
char option[30];
|
||||
static int proto_pdcp_lte = 0;
|
||||
|
||||
/* look up for protocol handle */
|
||||
if (proto_pdcp_lte == 0)
|
||||
{
|
||||
proto_pdcp_lte = proto_get_id_by_filter_name("pdcp-lte");
|
||||
}
|
||||
g_strlcpy(option, option_str, 30);
|
||||
|
||||
/* Only need to set info once per session. */
|
||||
p_pdcp_lte_info = (struct pdcp_lte_info*)p_get_proto_data(wmem_file_scope(), pinfo, proto_pdcp_lte, 0);
|
||||
if (p_pdcp_lte_info != NULL)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Allocate & zero struct */
|
||||
p_pdcp_lte_info = (struct pdcp_lte_info*) wmem_new0(wmem_packet_scope(), pdcp_lte_info);
|
||||
/* First mandatory parameter */
|
||||
par_opt_field = strtok(option, " ");
|
||||
if (strcmp(par_opt_field, "SRB") == 0)
|
||||
{
|
||||
p_pdcp_lte_info->plane = SIGNALING_PLANE;
|
||||
}
|
||||
else if (strcmp(par_opt_field, "DRB") == 0)
|
||||
{
|
||||
p_pdcp_lte_info->plane = USER_PLANE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
/* Second mandatory parameter */
|
||||
par_opt_field = strtok(NULL, " ");
|
||||
if (strcmp(par_opt_field, "SN_7b") == 0)
|
||||
{
|
||||
p_pdcp_lte_info->seqnum_length = PDCP_SN_LENGTH_7_BITS;
|
||||
}
|
||||
else if (strcmp(par_opt_field, "SN_12b") == 0)
|
||||
{
|
||||
p_pdcp_lte_info->seqnum_length = PDCP_SN_LENGTH_12_BITS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
p_pdcp_lte_info->no_header_pdu = 0;
|
||||
p_pdcp_lte_info->rohc.rohc_compression = 0;
|
||||
|
||||
/* Store info in packet */
|
||||
p_add_proto_data(wmem_file_scope(), pinfo, proto_pdcp_lte, 0, p_pdcp_lte_info);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************/
|
||||
/* Main dissection function. */
|
||||
/*****************************************/
|
||||
static int
|
||||
dissect_log3gpp(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data _U_)
|
||||
{
|
||||
proto_tree* prot3gpp_tree = NULL;
|
||||
proto_item* ti = NULL;
|
||||
gint offset = 0;
|
||||
gint protocol_name_start;
|
||||
gint protocol_name_length;
|
||||
gint protocol_option_start;
|
||||
gint protocol_option_length;
|
||||
gint timestamp_start;
|
||||
gint timestamp_length;
|
||||
packet_direction_t direction;
|
||||
tvbuff_t* next_tvb;
|
||||
dissector_handle_t protocol_handle = 0;
|
||||
int sub_dissector_result = 0;
|
||||
char* protocol_name;
|
||||
char* protocol_option;
|
||||
gboolean is_hex_data;
|
||||
|
||||
/* Clear Info */
|
||||
col_clear(pinfo->cinfo, COL_INFO);
|
||||
|
||||
/* Create root (protocol) tree. */
|
||||
ti = proto_tree_add_item(tree, proto_log3gpp, tvb, offset, -1, FALSE);
|
||||
prot3gpp_tree = proto_item_add_subtree(ti, ett_log3gpp);
|
||||
|
||||
/*********************************************************************/
|
||||
/* Note that these are the fields of the stub header as written out */
|
||||
/* by the wiretap module */
|
||||
|
||||
/* Timestamp in file */
|
||||
timestamp_start = offset;
|
||||
timestamp_length = tvb_strsize(tvb, offset);
|
||||
if (prot3gpp_tree) {
|
||||
proto_tree_add_double_format_value(prot3gpp_tree, hf_log3gpp_timestamp, tvb,
|
||||
offset, timestamp_length,
|
||||
g_ascii_strtod(tvb_format_text(tvb, offset, timestamp_length), NULL),
|
||||
"%s", tvb_format_text(tvb, offset, timestamp_length - 1));
|
||||
}
|
||||
offset += timestamp_length;
|
||||
|
||||
|
||||
/* protocol name */
|
||||
protocol_name_start = offset;
|
||||
protocol_name_length = tvb_strsize(tvb, offset);
|
||||
if (prot3gpp_tree) {
|
||||
proto_tree_add_item(prot3gpp_tree, hf_log3gpp_protocol, tvb, offset, protocol_name_length, ENC_ASCII | ENC_NA);
|
||||
}
|
||||
offset += protocol_name_length;
|
||||
|
||||
/* Direction */
|
||||
direction = (packet_direction_t)tvb_get_guint8(tvb, offset);
|
||||
if (prot3gpp_tree) {
|
||||
proto_tree_add_item(prot3gpp_tree, hf_log3gpp_direction, tvb, offset, 1, ENC_BIG_ENDIAN);
|
||||
}
|
||||
offset++;
|
||||
|
||||
/* protocol option */
|
||||
protocol_option_start = offset;
|
||||
protocol_option_length = tvb_strsize(tvb, offset);
|
||||
if (prot3gpp_tree) {
|
||||
proto_tree_add_item(prot3gpp_tree, hf_log3gpp_dissector_option, tvb, offset, protocol_option_length, ENC_ASCII | ENC_NA);
|
||||
}
|
||||
offset += protocol_option_length;
|
||||
|
||||
if (prot3gpp_tree)
|
||||
{
|
||||
/* Set selection length of prot3gpp tree */
|
||||
proto_item_set_len(prot3gpp_tree, offset);
|
||||
}
|
||||
|
||||
/* Add useful details to protocol tree label */
|
||||
protocol_name = (char*)tvb_get_string_enc(wmem_packet_scope(), tvb, protocol_name_start, protocol_name_length, ENC_UTF_8 | ENC_NA);
|
||||
/* Set Protocol */
|
||||
col_set_str(pinfo->cinfo, COL_PROTOCOL, protocol_name);
|
||||
/* To know whether the data following is row byte stream or text data */
|
||||
is_hex_data = strcmp(protocol_name, "TXT");
|
||||
|
||||
proto_item_append_text(ti, " t=%s %c prot=%s",
|
||||
tvb_get_string_enc(wmem_packet_scope(), tvb, timestamp_start, timestamp_length, ENC_UTF_8 | ENC_NA),
|
||||
(direction == 0) ? 'U' : 'D',
|
||||
protocol_name);
|
||||
|
||||
if (is_hex_data)
|
||||
{
|
||||
/* We might need to prepend pseudo header for the dissector */
|
||||
pseudo_hdr_func_ptr_t func_ptr = NULL;
|
||||
|
||||
/* Look up for the optional information */
|
||||
protocol_option = (char*)tvb_get_string_enc(wmem_packet_scope(), tvb, protocol_option_start, protocol_option_length, ENC_UTF_8 | ENC_NA);
|
||||
|
||||
/* look up for the right dissector handle */
|
||||
protocol_handle = look_for_dissector(protocol_name, direction, &func_ptr);
|
||||
|
||||
/***********************************************************************/
|
||||
/* Now hand off to the dissector of intended packet encapsulation type */
|
||||
|
||||
/* Try appropriate dissector, if one has been selected */
|
||||
if (protocol_handle != 0)
|
||||
{
|
||||
/* Dissect the remainder of the frame using chosen protocol handle */
|
||||
next_tvb = tvb_new_subset_length_caplen(tvb, offset, -1, tvb_reported_length(tvb) - offset);
|
||||
|
||||
/* This part is optional, only for dissector that need pseudo header information */
|
||||
if (func_ptr != NULL && strlen(protocol_option) != 0)
|
||||
{
|
||||
if (func_ptr(protocol_option, pinfo, offset, direction) == 0)
|
||||
{
|
||||
/* There was an error, return */
|
||||
return tvb_reported_length(tvb);
|
||||
}
|
||||
}
|
||||
sub_dissector_result = call_dissector(protocol_handle, next_tvb, pinfo, tree);
|
||||
}
|
||||
}
|
||||
|
||||
if (protocol_handle == 0 || sub_dissector_result == 0)
|
||||
{
|
||||
/* Could get here because:
|
||||
- desired protocol is unavailable (probably disabled), OR
|
||||
- protocol rejected our data
|
||||
Show remaining bytes as unparsed data */
|
||||
proto_tree_add_item(prot3gpp_tree, hf_log3gpp_unparsed_data, tvb, offset, -1, ENC_NA);
|
||||
|
||||
if (!is_hex_data)
|
||||
{
|
||||
col_add_fstr(pinfo->cinfo, COL_INFO,
|
||||
"%s",
|
||||
tvb_get_string_enc(wmem_packet_scope(), tvb, offset, tvb_reported_length(tvb) - offset, ENC_UTF_8 | ENC_NA));
|
||||
}
|
||||
else
|
||||
{
|
||||
col_add_fstr(pinfo->cinfo, COL_INFO,
|
||||
"Not dissected ( t=%s %c prot=%s)",
|
||||
tvb_get_string_enc(wmem_packet_scope(), tvb, timestamp_start, timestamp_length, ENC_UTF_8 | ENC_NA),
|
||||
(direction == 0) ? 'U' : 'D',
|
||||
tvb_get_string_enc(wmem_packet_scope(), tvb, protocol_name_start, protocol_name_length, ENC_UTF_8 | ENC_NA));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Show number of dissected bytes */
|
||||
proto_item* ti_local = proto_tree_add_uint(prot3gpp_tree,
|
||||
hf_log3gpp_dissected_length,
|
||||
tvb, 0, 0, tvb_reported_length(tvb) - offset);
|
||||
PROTO_ITEM_SET_GENERATED(ti_local);
|
||||
}
|
||||
return tvb_reported_length(tvb);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* Associate this protocol with the log3gpp file encapsulation type. */
|
||||
/******************************************************************************/
|
||||
void proto_reg_handoff_log3gpp(void)
|
||||
{
|
||||
gboolean init = FALSE;
|
||||
|
||||
if (init == FALSE)
|
||||
{
|
||||
dissector_handle_t log3gpp_handle;
|
||||
|
||||
log3gpp_handle = find_dissector("prot3gpp");
|
||||
dissector_add_uint("wtap_encap", WTAP_ENCAP_LOG_3GPP, log3gpp_handle);
|
||||
init = TRUE;
|
||||
}
|
||||
if (lte_rrc_prot_version == REL8)
|
||||
{
|
||||
update_dissector_name("LTE-RRC.BCCH.BCH", UPLINK, "lte-rrc.bcch.bch");
|
||||
update_dissector_name("LTE-RRC.BCCH.BCH", DOWNLINK, "lte-rrc.bcch.bch");
|
||||
update_dissector_name("LTE-RRC.BCCH.DL.SCH", UPLINK, "lte-rrc.bcch.dl.sch");
|
||||
update_dissector_name("LTE-RRC.BCCH.DL.SCH", DOWNLINK, "lte-rrc.bcch.dl.sch");
|
||||
update_dissector_name("LTE-RRC.CCCH", UPLINK, "lte-rrc.ul.ccch");
|
||||
update_dissector_name("LTE-RRC.CCCH", DOWNLINK, "lte-rrc.dl.ccch");
|
||||
update_dissector_name("LTE-RRC.DCCH", UPLINK, "lte-rrc.ul.dcch");
|
||||
update_dissector_name("LTE-RRC.DCCH", DOWNLINK, "lte-rrc.dl.dcch");
|
||||
update_dissector_name("LTE-RRC.PCCH", UPLINK, "lte-rrc.pcch");
|
||||
update_dissector_name("LTE-RRC.PCCH", DOWNLINK, "lte-rrc.pcch");
|
||||
}
|
||||
else if (lte_rrc_prot_version == FD1)
|
||||
{
|
||||
update_dissector_name("LTE-RRC.BCCH.BCH", UPLINK, "lte-rrc-fd1.bcch.bch");
|
||||
update_dissector_name("LTE-RRC.BCCH.BCH", DOWNLINK, "lte-rrc-fd1.bcch.bch");
|
||||
update_dissector_name("LTE-RRC.BCCH.DL.SCH", UPLINK, "lte-rrc-fd1.bcch.dl.sch");
|
||||
update_dissector_name("LTE-RRC.BCCH.DL.SCH", DOWNLINK, "lte-rrc-fd1.bcch.dl.sch");
|
||||
update_dissector_name("LTE-RRC.CCCH", UPLINK, "lte-rrc-fd1.ul.ccch");
|
||||
update_dissector_name("LTE-RRC.CCCH", DOWNLINK, "lte-rrc-fd1.dl.ccch");
|
||||
update_dissector_name("LTE-RRC.DCCH", UPLINK, "lte-rrc-fd1.ul.dcch");
|
||||
update_dissector_name("LTE-RRC.DCCH", DOWNLINK, "lte-rrc-fd1.dl.dcch");
|
||||
update_dissector_name("LTE-RRC.PCCH", UPLINK, "lte-rrc-fd1.pcch");
|
||||
update_dissector_name("LTE-RRC.PCCH", DOWNLINK, "lte-rrc-fd1.pcch");
|
||||
}
|
||||
if (nas_eps_prot_version == REL8)
|
||||
{
|
||||
update_dissector_name("NAS-EPS", UPLINK, "nas-eps");
|
||||
update_dissector_name("NAS-EPS", DOWNLINK, "nas-eps");
|
||||
}
|
||||
else if (nas_eps_prot_version == FD1)
|
||||
{
|
||||
update_dissector_name("NAS-EPS", UPLINK, "nas-eps-fd1");
|
||||
update_dissector_name("NAS-EPS", DOWNLINK, "nas-eps-fd1");
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************/
|
||||
/* Register the protocol */
|
||||
/****************************************/
|
||||
void proto_register_log3gpp(void)
|
||||
{
|
||||
module_t *log3gpp_module;
|
||||
static hf_register_info hf[] =
|
||||
{
|
||||
{ &hf_log3gpp_timestamp,
|
||||
{ "Timestamp",
|
||||
"log3gpp.timestamp", FT_DOUBLE, BASE_NONE, NULL, 0x0,
|
||||
"File timestamp", HFILL
|
||||
}
|
||||
},
|
||||
{ &hf_log3gpp_protocol,
|
||||
{ "3GPP protocol",
|
||||
"log3gpp.protocol", FT_STRING, BASE_NONE, NULL, 0x0,
|
||||
"Original 3GPP protocol name", HFILL
|
||||
}
|
||||
},
|
||||
{ &hf_log3gpp_dissector_option,
|
||||
{ "option",
|
||||
"log3gpp.option", FT_STRING, BASE_NONE, NULL, 0x0,
|
||||
"Protocol option", HFILL
|
||||
}
|
||||
},
|
||||
{ &hf_log3gpp_direction,
|
||||
{ "Direction",
|
||||
"log3gpp.direction", FT_UINT8, BASE_DEC, VALS(direction_vals), 0x0,
|
||||
"Frame direction (Uplink or Downlink)", HFILL
|
||||
}
|
||||
},
|
||||
{ &hf_log3gpp_unparsed_data,
|
||||
{ "Unparsed protocol data",
|
||||
"log3gpp.unparsed_data", FT_BYTES, BASE_NONE, NULL, 0x0,
|
||||
"Unparsed 3GPP protocol data", HFILL
|
||||
}
|
||||
},
|
||||
{ &hf_log3gpp_dissected_length,
|
||||
{ "Dissected length",
|
||||
"log3gpp.dissected-length", FT_UINT16, BASE_DEC, NULL, 0x0,
|
||||
"Number of bytes dissected by subdissector(s)", HFILL
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
static gint *ett[] =
|
||||
{
|
||||
&ett_log3gpp
|
||||
};
|
||||
|
||||
/* Register protocol. */
|
||||
proto_log3gpp = proto_register_protocol("3GPP log packet",
|
||||
"LOG3GPP",
|
||||
"log3gpp");
|
||||
proto_register_field_array(proto_log3gpp, hf, array_length(hf));
|
||||
proto_register_subtree_array(ett, array_length(ett));
|
||||
|
||||
log3gpp_module = prefs_register_protocol(proto_log3gpp, proto_reg_handoff_log3gpp);
|
||||
prefs_register_enum_preference(log3gpp_module,
|
||||
"rrc_release_version",
|
||||
"Select the release version of LTE RRC protocol",
|
||||
"There might be plugins corresponding to different version of the specification "
|
||||
"If they are present they should be listed here.",
|
||||
<e_rrc_prot_version,
|
||||
lte_rrc_dissector_version,
|
||||
FALSE);
|
||||
|
||||
prefs_register_enum_preference(log3gpp_module,
|
||||
"nas_eps_release_version",
|
||||
"Select the release version of NAS EPS protocol",
|
||||
"There might be plugins corresponding to different version of the specification "
|
||||
"If they are present they should be listed here.",
|
||||
&nas_eps_prot_version,
|
||||
nas_eps_dissector_version,
|
||||
FALSE);
|
||||
|
||||
/* Allow dissector to find be found by name. */
|
||||
register_dissector("prot3gpp", dissect_log3gpp, proto_log3gpp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Editor modelines - http://www.wireshark.org/tools/modelines.html
|
||||
*
|
||||
* Local variables:
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 8
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*
|
||||
* vi: set shiftwidth=4 tabstop=8 expandtab:
|
||||
* :indentSize=4:tabSize=8:noTabs=true:
|
||||
*/
|
|
@ -49,6 +49,7 @@ set(WIRETAP_NONGENERATED_FILES
|
|||
k12.c
|
||||
lanalyzer.c
|
||||
libpcap.c
|
||||
log3gpp.c
|
||||
logcat.c
|
||||
logcat_text.c
|
||||
merge.c
|
||||
|
|
|
@ -76,6 +76,8 @@
|
|||
#include "rfc7468.h"
|
||||
#include "ruby_marshal.h"
|
||||
#include "systemd_journal.h"
|
||||
#include "log3gpp.h"
|
||||
|
||||
|
||||
/*
|
||||
* Add an extension, and all compressed versions thereof if requested,
|
||||
|
@ -421,8 +423,10 @@ static const struct open_info open_info_base[] = {
|
|||
{ "Ixia IxVeriWave .vwr Raw Capture", OPEN_INFO_HEURISTIC, vwr_open, "vwr", NULL, NULL },
|
||||
{ "CAM Inspector file", OPEN_INFO_HEURISTIC, camins_open, "camins", NULL, NULL },
|
||||
{ "JavaScript Object Notation", OPEN_INFO_HEURISTIC, json_open, "json", NULL, NULL },
|
||||
{ "Ruby Marshal Object", OPEN_INFO_HEURISTIC, ruby_marshal_open, "", NULL, NULL },
|
||||
{ "Systemd Journal", OPEN_INFO_HEURISTIC, systemd_journal_open, "log;jnl;journal", NULL, NULL }
|
||||
{ "Ruby Marshal Object", OPEN_INFO_HEURISTIC, ruby_marshal_open, "", NULL, NULL },
|
||||
{ "Systemd Journal", OPEN_INFO_HEURISTIC, systemd_journal_open, "log;jnl;journal", NULL, NULL },
|
||||
{ "3gpp phone log", OPEN_INFO_MAGIC, log3gpp_open, "log", NULL, NULL },
|
||||
|
||||
};
|
||||
|
||||
/* this is only used to build the dynamic array on load, do NOT use this
|
||||
|
@ -1652,6 +1656,11 @@ static const struct file_type_subtype_info dump_open_table_base[] = {
|
|||
/* WTAP_FILE_TYPE_SUBTYPE_SYSTEMD_JOURNAL */
|
||||
{ "systemd journal export", "systemd journal", NULL, NULL,
|
||||
FALSE, FALSE, 0,
|
||||
NULL, NULL, NULL },
|
||||
|
||||
/* WTAP_FILE_TYPE_SUBTYPE_LOG_3GPP */
|
||||
{ "3GPP Log", "3gpp_log", "*.log", NULL,
|
||||
TRUE, FALSE, 0,
|
||||
NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,897 @@
|
|||
/* log3gpp.c
|
||||
* Routines encapsulating/dumping 3gpp protocol logs.
|
||||
* The purpose of this format is to be able to log the 3GPP protocol stack on a mobile phone.
|
||||
* Copyright 2008, Vincent Helfre
|
||||
*
|
||||
* Wireshark - Network traffic analyzer
|
||||
* By Gerald Combs <gerald@wireshark.org>
|
||||
* Copyright 1998 Gerald Combs
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "wtap-int.h"
|
||||
#include "file_wrappers.h"
|
||||
|
||||
#include "log3gpp.h"
|
||||
|
||||
#define MAX_FIRST_LINE_LENGTH 200
|
||||
#define MAX_TIMESTAMP_LINE_LENGTH 100
|
||||
#define MAX_LINE_LENGTH 65536
|
||||
#define MAX_TIMESTAMP_LEN 32
|
||||
#define MAX_SECONDS_CHARS 16
|
||||
#define MAX_SUBSECOND_DECIMALS 4
|
||||
#define MAX_PROTOCOL_NAME 64
|
||||
#define MAX_PROTOCOL_PAR_STRING 64
|
||||
|
||||
/* 's' or 'r' of a packet as read from .out file */
|
||||
typedef enum packet_direction_t
|
||||
{
|
||||
uplink,
|
||||
downlink
|
||||
} packet_direction_t;
|
||||
|
||||
typedef struct {
|
||||
time_t start_secs;
|
||||
guint32 start_usecs;
|
||||
} log3gpp_t;
|
||||
|
||||
gint first_packet_offset;
|
||||
gchar firstline[MAX_FIRST_LINE_LENGTH];
|
||||
gchar secondline[MAX_TIMESTAMP_LINE_LENGTH];
|
||||
gint secondline_length = 0;
|
||||
|
||||
/***********************************************************/
|
||||
/* Transient data used for parsing */
|
||||
|
||||
/* 'Magic number' at start of 3gpp log files. */
|
||||
static const gchar log3gpp_magic[] = "3GPP protocols transcript";
|
||||
|
||||
/* Protocol name of the packet that the packet was captured at */
|
||||
static gchar protocol_name[MAX_PROTOCOL_NAME+1];
|
||||
|
||||
/* Optional string parameter giving info required for the protocol dissector */
|
||||
static gchar protocol_parameters[MAX_PROTOCOL_PAR_STRING+1];
|
||||
/************************************************************/
|
||||
/* Functions called from wiretap core */
|
||||
static gboolean log3gpp_read( wtap* wth, wtap_rec* rec, Buffer* buf,
|
||||
int* err, gchar** err_info, gint64* data_offset);
|
||||
static gboolean log3gpp_seek_read(struct wtap *wth, gint64 seek_off,
|
||||
wtap_rec *rec,
|
||||
Buffer *buf,
|
||||
int *err, gchar **err_info);
|
||||
|
||||
/************************************************************/
|
||||
/* Private helper functions */
|
||||
static gboolean read_new_line(FILE_T fh, gint* length,
|
||||
gchar* buf, size_t bufsize, int* err,
|
||||
gchar** err_info);
|
||||
|
||||
static gboolean parse_line(char* linebuff, gint line_length, gint *seconds, gint *useconds,
|
||||
long *data_offset,
|
||||
gint *data_chars,
|
||||
packet_direction_t *direction,
|
||||
gboolean *is_text_data);
|
||||
static int write_stub_header(guchar *frame_buffer, char *timestamp_string,
|
||||
packet_direction_t direction);
|
||||
static guchar hex_from_char(gchar c);
|
||||
/*not used static gchar char_from_hex(guchar hex);*/
|
||||
|
||||
static gboolean get_file_time_stamp(gchar* linebuff, time_t *secs, guint32 *usecs);
|
||||
|
||||
|
||||
/***************************************************************************/
|
||||
/* Free log3gpp-specific capture info from file that was open for reading */
|
||||
/***************************************************************************/
|
||||
void log3gpp_close(wtap* wth)
|
||||
{
|
||||
log3gpp_t* log3gpp = (log3gpp_t*)wth->priv;
|
||||
/* Also free this capture info */
|
||||
g_free(log3gpp);
|
||||
wth->priv = NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
log3gpp_dump(wtap_dumper* wdh _U_, const wtap_rec* rec _U_,
|
||||
const guchar* buf _U_, int* err _U_, gchar** err_info _U_)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/******************************************************/
|
||||
/* Close a file we've been writing to. */
|
||||
/******************************************************/
|
||||
static gboolean
|
||||
log3gpp_dump_finish(wtap_dumper* wdh _U_, int* err _U_)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/********************************************/
|
||||
/* Open file (for reading) */
|
||||
/********************************************/
|
||||
wtap_open_return_val
|
||||
log3gpp_open(wtap *wth, int *err, gchar **err_info _U_)
|
||||
{
|
||||
time_t timestamp;
|
||||
guint32 usecs;
|
||||
log3gpp_t *log3gpp;
|
||||
wtap_open_return_val retval;
|
||||
/* Buffer to hold a single text line read from the file */
|
||||
static gchar linebuff[MAX_LINE_LENGTH];
|
||||
gint firstline_length = 0;
|
||||
|
||||
/* Clear errno before reading from the file */
|
||||
errno = 0;
|
||||
|
||||
/********************************************************************/
|
||||
/* First line needs to contain at least as many characters as magic */
|
||||
|
||||
/*g_warning("Open file"); */
|
||||
|
||||
if (!read_new_line(wth->fh, &firstline_length, linebuff,
|
||||
sizeof linebuff, err, err_info)) {
|
||||
if (*err != 0 && *err != WTAP_ERR_SHORT_READ) {
|
||||
return WTAP_OPEN_ERROR;
|
||||
}
|
||||
else {
|
||||
return WTAP_OPEN_NOT_MINE;
|
||||
}
|
||||
}
|
||||
|
||||
if (((size_t)firstline_length < strlen(log3gpp_magic)) ||
|
||||
firstline_length >= MAX_FIRST_LINE_LENGTH)
|
||||
{
|
||||
retval = WTAP_OPEN_NOT_MINE;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* This file is not for us if it doesn't match our signature */
|
||||
if (memcmp(log3gpp_magic, linebuff, strlen(log3gpp_magic)) != 0)
|
||||
{
|
||||
retval = WTAP_OPEN_NOT_MINE;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/***********************************************************/
|
||||
/* Second line contains file timestamp */
|
||||
if (!read_new_line(wth->fh, &secondline_length,
|
||||
linebuff, sizeof linebuff, err, err_info)) {
|
||||
if (*err != 0 && *err != WTAP_ERR_SHORT_READ) {
|
||||
return WTAP_OPEN_ERROR;
|
||||
}
|
||||
else {
|
||||
return WTAP_OPEN_NOT_MINE;
|
||||
}
|
||||
}
|
||||
|
||||
first_packet_offset = firstline_length + secondline_length;
|
||||
|
||||
if ((secondline_length >= MAX_TIMESTAMP_LINE_LENGTH) ||
|
||||
(!get_file_time_stamp(linebuff, ×tamp, &usecs)))
|
||||
{
|
||||
/* Give up if file time line wasn't valid */
|
||||
retval = WTAP_OPEN_NOT_MINE;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Allocate struct and fill in timestamp (netmon re used)*/
|
||||
log3gpp = (log3gpp_t *)g_malloc(sizeof(log3gpp_t));
|
||||
log3gpp->start_secs = timestamp;
|
||||
log3gpp->start_usecs = usecs;
|
||||
wth->priv = (void *)log3gpp;
|
||||
|
||||
/************************************************************/
|
||||
/* File is for us. Fill in details so packets can be read */
|
||||
|
||||
/* Set our file type */
|
||||
wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_LOG_3GPP;
|
||||
|
||||
/* Use our own encapsulation to send all packets to our stub dissector */
|
||||
wth->file_encap = WTAP_ENCAP_LOG_3GPP;
|
||||
|
||||
/* Callbacks for reading operations */
|
||||
wth->subtype_read = log3gpp_read;
|
||||
wth->subtype_seek_read = log3gpp_seek_read;
|
||||
wth->subtype_close = log3gpp_close;
|
||||
|
||||
/* Choose microseconds (have 4 decimal places...) */
|
||||
wth->file_tsprec = WTAP_TSPREC_USEC;
|
||||
|
||||
*err = errno;
|
||||
|
||||
retval = WTAP_OPEN_MINE;
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************/
|
||||
/* Read packet function. */
|
||||
/* Look for and read the next usable packet */
|
||||
/* - return TRUE and details if found */
|
||||
/**************************************************/
|
||||
gboolean log3gpp_read(wtap* wth, wtap_rec* rec, Buffer* buf,
|
||||
int* err, gchar** err_info, gint64* data_offset)
|
||||
{
|
||||
gint64 offset = file_tell(wth->fh);
|
||||
static gchar linebuff[MAX_LINE_LENGTH + 1];
|
||||
long dollar_offset;
|
||||
packet_direction_t direction;
|
||||
gboolean is_text_data;
|
||||
log3gpp_t *log3gpp = (log3gpp_t *)wth->priv;
|
||||
|
||||
/* Search for a line containing a usable packet */
|
||||
while (1)
|
||||
{
|
||||
int line_length, seconds, useconds, data_chars;
|
||||
gint64 this_offset = offset;
|
||||
|
||||
/* Are looking for first packet after 2nd line */
|
||||
if (file_tell(wth->fh) == 0)
|
||||
{
|
||||
this_offset += (gint64)first_packet_offset +1+1;
|
||||
}
|
||||
|
||||
/* Clear errno before reading from the file */
|
||||
errno = 0;
|
||||
|
||||
/* Read a new line from file into linebuff */
|
||||
if (!read_new_line(wth->fh, &line_length, linebuff,
|
||||
sizeof linebuff, err, err_info)) {
|
||||
if (*err != 0) {
|
||||
return FALSE; /* error */
|
||||
}
|
||||
/* No more lines can be read, so quit. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Try to parse the line as a frame record */
|
||||
if (parse_line(linebuff, line_length, &seconds, &useconds,
|
||||
&dollar_offset,
|
||||
&data_chars,
|
||||
&direction,
|
||||
&is_text_data))
|
||||
{
|
||||
guchar *frame_buffer;
|
||||
int n;
|
||||
int stub_offset = 0;
|
||||
char timestamp_string[MAX_TIMESTAMP_LEN+1];
|
||||
/*not used gint64 *pkey = NULL;*/
|
||||
|
||||
g_snprintf(timestamp_string, 32, "%d.%04d", seconds, useconds/100);
|
||||
|
||||
/* All packets go to 3GPP protocol stub dissector */
|
||||
rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_LOG_3GPP;
|
||||
rec->rec_type = REC_TYPE_PACKET;
|
||||
rec->presence_flags = WTAP_HAS_TS;
|
||||
|
||||
/* Set data_offset to the beginning of the line we're returning.
|
||||
This will be the seek_off parameter when this frame is re-read.
|
||||
*/
|
||||
*data_offset = this_offset;
|
||||
|
||||
/* Fill in timestamp (capture base + packet offset) */
|
||||
rec->ts.secs = log3gpp->start_secs + seconds;
|
||||
if ((log3gpp->start_usecs + useconds) >= 1000000)
|
||||
{
|
||||
rec->ts.secs++;
|
||||
}
|
||||
rec->ts.nsecs =
|
||||
((log3gpp->start_usecs + useconds) % 1000000) *1000;
|
||||
|
||||
if (!is_text_data)
|
||||
{
|
||||
/* Get buffer pointer ready */
|
||||
ws_buffer_assure_space(buf,
|
||||
strlen(timestamp_string)+1 + /* timestamp */
|
||||
strlen(protocol_name)+1 + /* Protocol name */
|
||||
1 + /* direction */
|
||||
(size_t)(data_chars/2));
|
||||
|
||||
frame_buffer = ws_buffer_start_ptr(buf);
|
||||
/*********************/
|
||||
/* Write stub header */
|
||||
stub_offset = write_stub_header(frame_buffer, timestamp_string,
|
||||
direction);
|
||||
|
||||
/* Binary data length is half bytestring length + stub header */
|
||||
rec->rec_header.packet_header.len = data_chars/2 + stub_offset;
|
||||
rec->rec_header.packet_header.caplen = data_chars/2 + stub_offset;
|
||||
/********************************/
|
||||
/* Copy packet data into buffer */
|
||||
for (n=0; n <= data_chars; n+=2)
|
||||
{
|
||||
frame_buffer[stub_offset + n/2] = (hex_from_char(linebuff[dollar_offset+n]) << 4) |
|
||||
hex_from_char(linebuff[dollar_offset+n+1]);
|
||||
}
|
||||
*err = errno = 0;
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Get buffer pointer ready */
|
||||
ws_buffer_assure_space(buf,
|
||||
strlen(timestamp_string)+1 + /* timestamp */
|
||||
strlen(protocol_name)+1 + /* Protocol name */
|
||||
1 + /* direction */
|
||||
data_chars);
|
||||
frame_buffer = ws_buffer_start_ptr(buf);
|
||||
|
||||
/*********************/
|
||||
/* Write stub header */
|
||||
stub_offset = write_stub_header(frame_buffer, timestamp_string,
|
||||
direction);
|
||||
|
||||
/* Binary data length is bytestring length + stub header */
|
||||
rec->rec_header.packet_header.len = data_chars + stub_offset;
|
||||
rec->rec_header.packet_header.caplen = data_chars + stub_offset;
|
||||
|
||||
/* do not convert the ascii char */
|
||||
memcpy(&frame_buffer[stub_offset],&linebuff[dollar_offset],data_chars);
|
||||
frame_buffer[stub_offset+data_chars-1]= '\0';
|
||||
*err = errno = 0;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* No packet details to return... */
|
||||
*err = errno;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************/
|
||||
/* Read & seek function. */
|
||||
/**************************************************/
|
||||
static gboolean
|
||||
log3gpp_seek_read(wtap *wth, gint64 seek_off,
|
||||
wtap_rec *rec _U_ , Buffer *buf,
|
||||
int *err, gchar **err_info)
|
||||
{
|
||||
long dollar_offset;
|
||||
static gchar linebuff[MAX_LINE_LENGTH + 1];
|
||||
packet_direction_t direction;
|
||||
int seconds, useconds, data_chars;
|
||||
gboolean is_text_data;
|
||||
log3gpp_t* log3gpp = (log3gpp_t*)wth->priv;
|
||||
int length = 0;
|
||||
guchar *frame_buffer;
|
||||
|
||||
/* Reset errno */
|
||||
*err = errno = 0;
|
||||
|
||||
/* Seek to beginning of packet */
|
||||
if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Re-read whole line (this really should succeed) */
|
||||
if (!read_new_line(wth->random_fh, &length, linebuff,
|
||||
sizeof linebuff, err, err_info)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Try to parse this line again (should succeed as re-reading...) */
|
||||
if (parse_line(linebuff, length, &seconds, &useconds,
|
||||
&dollar_offset,
|
||||
&data_chars,
|
||||
&direction,
|
||||
&is_text_data))
|
||||
{
|
||||
int n;
|
||||
int stub_offset = 0;
|
||||
char timestamp_string[32];
|
||||
g_snprintf(timestamp_string, 32, "%d.%04d", seconds, useconds/100);
|
||||
|
||||
/* Make sure all packets go to log3gpp dissector */
|
||||
rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_LOG_3GPP;
|
||||
rec->rec_type = REC_TYPE_PACKET;
|
||||
rec->presence_flags = WTAP_HAS_TS;
|
||||
|
||||
/* Fill in timestamp (capture base + packet offset) */
|
||||
rec->ts.secs = log3gpp->start_secs + seconds;
|
||||
if ((log3gpp->start_usecs + useconds) >= 1000000)
|
||||
{
|
||||
rec->ts.secs++;
|
||||
}
|
||||
rec->ts.nsecs =
|
||||
((log3gpp->start_usecs + useconds) % 1000000) * 1000;
|
||||
|
||||
/*********************/
|
||||
/* Write stub header */
|
||||
ws_buffer_assure_space(buf,
|
||||
strlen(timestamp_string)+1 + /* timestamp */
|
||||
strlen(protocol_name)+1 + /* Protocol name */
|
||||
1 + /* direction */
|
||||
data_chars);
|
||||
frame_buffer = ws_buffer_start_ptr(buf);
|
||||
stub_offset = write_stub_header(frame_buffer, timestamp_string,
|
||||
direction);
|
||||
|
||||
if (!is_text_data)
|
||||
{
|
||||
/********************************/
|
||||
/* Copy packet data into buffer */
|
||||
for (n=0; n <= data_chars; n+=2)
|
||||
{
|
||||
frame_buffer[stub_offset + n/2] = (hex_from_char(linebuff[dollar_offset+n]) << 4) |
|
||||
hex_from_char(linebuff[dollar_offset+n+1]);
|
||||
}
|
||||
*err = errno = 0;
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* do not convert the ascii char */
|
||||
memcpy(&frame_buffer[stub_offset],&linebuff[dollar_offset],data_chars);
|
||||
frame_buffer[stub_offset+data_chars-1] = '\0';
|
||||
*err = errno = 0;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* If get here, must have failed */
|
||||
*err = errno;
|
||||
*err_info = g_strdup_printf("prot 3gpp: seek_read failed to read/parse "
|
||||
"line at position %" G_GINT64_MODIFIER "d",
|
||||
seek_off);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/***************************/
|
||||
/* Dump functions */
|
||||
/***************************/
|
||||
|
||||
/*****************************************************/
|
||||
/* The file that we are writing to has been opened. */
|
||||
/* Set other dump callbacks. */
|
||||
/*****************************************************/
|
||||
gboolean log3gpp_dump_open(wtap_dumper *wdh, gboolean cant_seek _U_, int *err _U_)
|
||||
{
|
||||
/* Fill in other dump callbacks */
|
||||
wdh->subtype_write = log3gpp_dump;
|
||||
wdh->subtype_finish = log3gpp_dump_finish;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************/
|
||||
/* Write a single packet out to the file */
|
||||
/*****************************************/
|
||||
/*
|
||||
static gboolean do_fwrite(const void *data, size_t size, size_t count, FILE *stream, int *err_p)
|
||||
{
|
||||
size_t nwritten;
|
||||
|
||||
nwritten = fwrite(data, size, count, stream);
|
||||
if (nwritten != count) {
|
||||
if (nwritten == 0 && ferror(stream))
|
||||
{
|
||||
*err_p = errno;
|
||||
}
|
||||
else
|
||||
{
|
||||
*err_p = WTAP_ERR_SHORT_WRITE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
/****************************/
|
||||
/* Private helper functions */
|
||||
/****************************/
|
||||
|
||||
/**********************************************************************/
|
||||
/* Read a new line from the file, starting at offset. */
|
||||
/* - writes data to static var linebuff */
|
||||
/* - on return 'offset' will point to the next position to read from */
|
||||
/* - return TRUE if this read is successful */
|
||||
/**********************************************************************/
|
||||
static gboolean
|
||||
read_new_line(FILE_T fh, gint* length,
|
||||
gchar* linebuff, size_t linebuffsize, int* err, gchar** err_info)
|
||||
{
|
||||
/* Read in a line */
|
||||
gint64 pos_before = file_tell(fh);
|
||||
|
||||
if (file_gets(linebuff, (int)linebuffsize - 1, fh) == NULL) {
|
||||
/* No characters found, or error */
|
||||
*err = file_error(fh, err_info);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Set length (avoiding strlen()) and offset.. */
|
||||
*length = (gint)(file_tell(fh) - pos_before);
|
||||
|
||||
/* ...but don't want to include newline in line length */
|
||||
if (*length > 0 && linebuff[*length - 1] == '\n') {
|
||||
linebuff[*length - 1] = '\0';
|
||||
*length = *length - 1;
|
||||
}
|
||||
/* Nor do we want '\r' (as will be written when log is created on windows) */
|
||||
if (*length > 0 && linebuff[*length - 1] == '\r') {
|
||||
linebuff[*length - 1] = '\0';
|
||||
*length = *length - 1;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************/
|
||||
/* Parse a line from buffer, by identifying: */
|
||||
/* - context, port and direction of packet */
|
||||
/* - timestamp */
|
||||
/* - data position and length */
|
||||
/* Return TRUE if this packet looks valid and can be displayed */
|
||||
/**********************************************************************/
|
||||
gboolean parse_line(gchar* linebuff, gint line_length, gint *seconds, gint *useconds,
|
||||
long *data_offset, gint *data_chars,
|
||||
packet_direction_t *direction,
|
||||
gboolean *is_text_data)
|
||||
{
|
||||
int n = 0;
|
||||
/*not used int variant_digits = 0;*/
|
||||
/*not used int variant = 1;*/
|
||||
int protocol_chars = 0;
|
||||
int prot_option_chars = 0;
|
||||
char seconds_buff[MAX_SECONDS_CHARS+1];
|
||||
int seconds_chars;
|
||||
char subsecond_decimals_buff[MAX_SUBSECOND_DECIMALS+1];
|
||||
int subsecond_decimals_chars;
|
||||
|
||||
/*********************************************************************/
|
||||
/* Find and read the timestamp */
|
||||
/*********************************************************************/
|
||||
/* Now scan to the next digit, which should be the start of the timestamp */
|
||||
for (; !g_ascii_isdigit((guchar)linebuff[n]) && (n < line_length); n++);
|
||||
if (n >= line_length)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Seconds */
|
||||
for (seconds_chars = 0;
|
||||
(linebuff[n] != '.') &&
|
||||
(seconds_chars <= MAX_SECONDS_CHARS) &&
|
||||
(n < line_length);
|
||||
n++, seconds_chars++)
|
||||
{
|
||||
if (!g_ascii_isdigit((guchar)linebuff[n]))
|
||||
{
|
||||
/* Found a non-digit before decimal point. Fail */
|
||||
return FALSE;
|
||||
}
|
||||
seconds_buff[seconds_chars] = linebuff[n];
|
||||
}
|
||||
if (seconds_chars > MAX_SECONDS_CHARS || n >= line_length)
|
||||
{
|
||||
/* Didn't fit in buffer. Fail rather than use truncated */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Convert found value into number */
|
||||
seconds_buff[seconds_chars] = '\0';
|
||||
|
||||
/* Already know they are digits, so avoid expense of ws_strtoi32() */
|
||||
int multiplier = 1;
|
||||
*seconds = 0;
|
||||
for (int d = seconds_chars - 1; d >= 0; d--) {
|
||||
*seconds += ((seconds_buff[d] - '0') * multiplier);
|
||||
multiplier *= 10;
|
||||
}
|
||||
|
||||
/* The decimal point must follow the last of the seconds digits */
|
||||
if (linebuff[n] != '.')
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
/* Skip it */
|
||||
n++;
|
||||
|
||||
/* Subsecond decimal digits (expect 4-digit accuracy) */
|
||||
for (subsecond_decimals_chars = 0;
|
||||
(linebuff[n] != ' ') &&
|
||||
(subsecond_decimals_chars <= MAX_SUBSECOND_DECIMALS) &&
|
||||
(n < line_length);
|
||||
n++, subsecond_decimals_chars++)
|
||||
{
|
||||
if (!g_ascii_isdigit((guchar)linebuff[n]))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
|
||||
}
|
||||
if (subsecond_decimals_chars > MAX_SUBSECOND_DECIMALS || n >= line_length)
|
||||
{
|
||||
/* More numbers than expected - give up */
|
||||
return FALSE;
|
||||
}
|
||||
/* Convert found value into microseconds */
|
||||
subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
|
||||
/* Already know they are digits, so avoid expense of ws_strtoi32() */
|
||||
*useconds = ((subsecond_decimals_buff[0] - '0') * 100000) +
|
||||
((subsecond_decimals_buff[1] - '0') * 10000) +
|
||||
((subsecond_decimals_buff[2] - '0') * 1000) +
|
||||
((subsecond_decimals_buff[3] - '0') * 100);
|
||||
|
||||
/* Space character must follow end of timestamp */
|
||||
if (linebuff[n] != ' ')
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
n++;
|
||||
|
||||
/*********************************************************************/
|
||||
/* Find and read protocol name */
|
||||
/*********************************************************************/
|
||||
/* Read context name until find ' ' */
|
||||
for (protocol_chars = 0;
|
||||
(linebuff[n] != ' ') && (protocol_chars < MAX_PROTOCOL_NAME) && (n < line_length);
|
||||
n++, protocol_chars++)
|
||||
{
|
||||
if (!g_ascii_isalnum((guchar)linebuff[n]) && linebuff[n] != '_' && linebuff[n] != '.' && linebuff[n] != '-')
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
protocol_name[protocol_chars] = linebuff[n];
|
||||
}
|
||||
if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length)
|
||||
{
|
||||
/* If doesn't fit, fail rather than truncate */
|
||||
return FALSE;
|
||||
}
|
||||
protocol_name[protocol_chars] = '\0';
|
||||
|
||||
/* Space char must follow protocol name */
|
||||
if (linebuff[n] != ' ')
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
/* Skip it */
|
||||
n++;
|
||||
|
||||
/* Scan ahead to the next space */
|
||||
for (; (!g_ascii_isalnum((guchar)linebuff[n])) && (n < line_length); n++);
|
||||
if (n >= line_length)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
if (strcmp(protocol_name,"TXT") == 0)
|
||||
{
|
||||
*direction = uplink;
|
||||
*data_offset = n;
|
||||
*data_chars = line_length - n;
|
||||
*is_text_data = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Next character gives direction of message (must be 'u' or 'd') */
|
||||
if (linebuff[n] == 'u')
|
||||
{
|
||||
*direction = uplink;
|
||||
}
|
||||
else if (linebuff[n] == 'd')
|
||||
{
|
||||
*direction = downlink;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
n++;
|
||||
|
||||
/* Now skip ahead to find start of data (marked by '$') */
|
||||
for (; (linebuff[n] != '$') && (n <= line_length) && (prot_option_chars <= MAX_PROTOCOL_PAR_STRING);
|
||||
n++,prot_option_chars++)
|
||||
{
|
||||
protocol_parameters[prot_option_chars] = linebuff[n];
|
||||
}
|
||||
protocol_parameters[prot_option_chars] = '\0';
|
||||
if (prot_option_chars == MAX_PROTOCOL_PAR_STRING || n >= line_length)
|
||||
{
|
||||
/* If doesn't fit, fail rather than truncate */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Skip it */
|
||||
n++;
|
||||
|
||||
/* Set offset to data start within line */
|
||||
*data_offset = n;
|
||||
|
||||
/* Set number of chars that comprise the hex string protocol data */
|
||||
*data_chars = line_length - n;
|
||||
|
||||
*is_text_data = FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/* Write the stub info to the data buffer while reading a packet */
|
||||
/*****************************************************************/
|
||||
int write_stub_header(guchar *frame_buffer, char *timestamp_string,
|
||||
packet_direction_t direction)
|
||||
{
|
||||
int stub_offset = 0;
|
||||
|
||||
/* Timestamp within file */
|
||||
g_strlcpy((char*)&frame_buffer[stub_offset], timestamp_string, MAX_TIMESTAMP_LEN+1);
|
||||
stub_offset += (int)(strlen(timestamp_string) + 1);
|
||||
|
||||
/* Protocol name */
|
||||
g_strlcpy((char*)&frame_buffer[stub_offset], protocol_name, MAX_PROTOCOL_NAME+1);
|
||||
stub_offset += (int)(strlen(protocol_name) + 1);
|
||||
|
||||
/* Direction */
|
||||
frame_buffer[stub_offset] = direction;
|
||||
stub_offset++;
|
||||
|
||||
/* Option string (might be string of length 0) */
|
||||
g_strlcpy((char*)&frame_buffer[stub_offset], protocol_parameters,MAX_PROTOCOL_PAR_STRING+1);
|
||||
stub_offset += (int)(strlen(protocol_parameters) + 1);
|
||||
return stub_offset;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************/
|
||||
/* Return hex nibble equivalent of hex string character */
|
||||
/********************************************************/
|
||||
guchar hex_from_char(gchar c)
|
||||
{
|
||||
if ((c >= '0') && (c <= '9'))
|
||||
{
|
||||
return c - '0';
|
||||
}
|
||||
|
||||
if ((c >= 'a') && (c <= 'f'))
|
||||
{
|
||||
return 0x0a + (c - 'a');
|
||||
}
|
||||
|
||||
if ((c >= 'A') && (c <= 'F'))
|
||||
{
|
||||
return 0x0a + (c - 'A');
|
||||
}
|
||||
/* Not a valid hex string character */
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************/
|
||||
/* Return character corresponding to hex nibble value */
|
||||
/********************************************************/
|
||||
/*gchar char_from_hex(guchar hex)
|
||||
{
|
||||
static char hex_lookup[16] =
|
||||
{ '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
|
||||
|
||||
if (hex > 15)
|
||||
{
|
||||
return '?';
|
||||
}
|
||||
|
||||
return hex_lookup[hex];
|
||||
}*/
|
||||
|
||||
/************************************************************************/
|
||||
/* Parse year, month, day, hour, minute, seconds out of formatted line. */
|
||||
/* Set secs and usecs as output */
|
||||
/* Return FALSE if no valid time can be read */
|
||||
/************************************************************************/
|
||||
gboolean get_file_time_stamp(gchar* linebuff, time_t *secs, guint32 *usecs)
|
||||
{
|
||||
int n;
|
||||
struct tm tm;
|
||||
#define MAX_MONTH_LETTERS 9
|
||||
char month[MAX_MONTH_LETTERS+1];
|
||||
|
||||
int day, year, hour, minute, second;
|
||||
int scan_found;
|
||||
|
||||
/* If line longer than expected, file is probably not correctly formatted */
|
||||
if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**************************************************************/
|
||||
/* First is month. Read until get a space following the month */
|
||||
for (n=0; (linebuff[n] != ' ') && (n < MAX_MONTH_LETTERS); n++)
|
||||
{
|
||||
month[n] = linebuff[n];
|
||||
}
|
||||
month[n] = '\0';
|
||||
|
||||
if (strcmp(month, "January" ) == 0) tm.tm_mon = 0;
|
||||
else if (strcmp(month, "February" ) == 0) tm.tm_mon = 1;
|
||||
else if (strcmp(month, "March" ) == 0) tm.tm_mon = 2;
|
||||
else if (strcmp(month, "April" ) == 0) tm.tm_mon = 3;
|
||||
else if (strcmp(month, "May" ) == 0) tm.tm_mon = 4;
|
||||
else if (strcmp(month, "June" ) == 0) tm.tm_mon = 5;
|
||||
else if (strcmp(month, "July" ) == 0) tm.tm_mon = 6;
|
||||
else if (strcmp(month, "August" ) == 0) tm.tm_mon = 7;
|
||||
else if (strcmp(month, "September") == 0) tm.tm_mon = 8;
|
||||
else if (strcmp(month, "October" ) == 0) tm.tm_mon = 9;
|
||||
else if (strcmp(month, "November" ) == 0) tm.tm_mon = 10;
|
||||
else if (strcmp(month, "December" ) == 0) tm.tm_mon = 11;
|
||||
else
|
||||
{
|
||||
/* Give up if not found a properly-formatted date */
|
||||
return FALSE;
|
||||
}
|
||||
/* Skip space char */
|
||||
n++;
|
||||
|
||||
/********************************************************/
|
||||
/* Scan for remaining numerical fields */
|
||||
scan_found = sscanf(linebuff+n, "%d, %d %d:%d:%d.%u",
|
||||
&day, &year, &hour, &minute, &second, usecs);
|
||||
if (scan_found != 6)
|
||||
{
|
||||
/* Give up if not all found */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/******************************************************/
|
||||
/* Fill in remaining fields and return it in a time_t */
|
||||
tm.tm_year = year - 1900;
|
||||
tm.tm_mday = day;
|
||||
tm.tm_hour = hour;
|
||||
tm.tm_min = minute;
|
||||
tm.tm_sec = second;
|
||||
tm.tm_isdst = -1; /* daylight saving time info not known */
|
||||
|
||||
/* Get seconds from this time */
|
||||
*secs = mktime(&tm);
|
||||
|
||||
/* Multiply 4 digits given to get micro-seconds */
|
||||
*usecs = *usecs * 100;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Register with wtap */
|
||||
void wtap_register_phonelog(void) {
|
||||
static struct file_type_subtype_info fi =
|
||||
{ "3GPP Log", "3gpp_log", "*.log", NULL, TRUE, FALSE, 0, NULL, NULL, NULL };
|
||||
|
||||
static struct open_info phonelog_oi =
|
||||
{ "3gpp_log", OPEN_INFO_MAGIC, log3gpp_open, "*.log", NULL, NULL };
|
||||
|
||||
wtap_register_open_info(&phonelog_oi, TRUE);
|
||||
|
||||
encap_3gpp_log = wtap_register_encap_type("3GPP Log","3gpp_log");
|
||||
wf_3gpp_log = wtap_register_file_type_subtypes(&fi, WTAP_FILE_TYPE_SUBTYPE_UNKNOWN);
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* Editor modelines - http://www.wireshark.org/tools/modelines.html
|
||||
*
|
||||
* Local variables:
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 8
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*
|
||||
* vi: set shiftwidth=4 tabstop=8 expandtab:
|
||||
* :indentSize=4:tabSize=8:noTabs=true:
|
||||
*/
|
|
@ -0,0 +1,16 @@
|
|||
/* log_3gpp.h
|
||||
*
|
||||
* Wiretap Library
|
||||
* Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
*/
|
||||
#ifndef __LOG_3GPP_H__
|
||||
#define __LOG_3GPP_H__
|
||||
|
||||
#include <wtap.h>
|
||||
|
||||
wtap_open_return_val log3gpp_open(wtap* wth, int* err, gchar** err_info);
|
||||
|
||||
#endif /* __LOG_3GPP_H__ */
|
|
@ -971,6 +971,10 @@ static struct encap_type_info encap_table_base[] = {
|
|||
|
||||
/* WTAP_ENCAP_IEEE802_15_4_TAP */
|
||||
{ "wpan-tap", "IEEE 802.15.4 Wireless with TAP pseudo-header" },
|
||||
|
||||
/* WTAP_ENCAP_LOG_3GPP */
|
||||
{ "log_3GPP", "3GPP Phone Log" },
|
||||
|
||||
};
|
||||
|
||||
WS_DLL_LOCAL
|
||||
|
|
|
@ -287,6 +287,7 @@ extern "C" {
|
|||
#define WTAP_ENCAP_EBHSCR 204
|
||||
#define WTAP_ENCAP_VPP 205
|
||||
#define WTAP_ENCAP_IEEE802_15_4_TAP 206
|
||||
#define WTAP_ENCAP_LOG_3GPP 207
|
||||
|
||||
/* After adding new item here, please also add new item to encap_table_base array */
|
||||
|
||||
|
@ -382,6 +383,7 @@ extern "C" {
|
|||
#define WTAP_FILE_TYPE_SUBTYPE_RFC7468 82
|
||||
#define WTAP_FILE_TYPE_SUBTYPE_RUBY_MARSHAL 83
|
||||
#define WTAP_FILE_TYPE_SUBTYPE_SYSTEMD_JOURNAL 84
|
||||
#define WTAP_FILE_TYPE_SUBTYPE_LOG_3GPP 85
|
||||
|
||||
#define WTAP_NUM_FILE_TYPES_SUBTYPES wtap_get_num_file_types_subtypes()
|
||||
|
||||
|
|
Loading…
Reference in New Issue