diff --git a/docbook/release-notes.asciidoc b/docbook/release-notes.asciidoc index 2b3206b32e..56f7f34120 100644 --- a/docbook/release-notes.asciidoc +++ b/docbook/release-notes.asciidoc @@ -96,6 +96,7 @@ Ruby Marshal format [commaize] -- Apple Wireless Direct Link (AWDL) +Basic Transport Protocol (BTP) BLIP Couchbase Mobile (BLIP) CDMA 2000 Cisco Meraki Discovery Protocol (MDP) @@ -105,6 +106,7 @@ E1AP (5G) EVS (3GPP TS 26.445 A.2 EVS RTP) Exablaze trailers General Circuit Services Notification Application Protocol (GCSNA) +GeoNetworking (GeoNw) GLOW Lawo Emberplus Data format GSM-R (User-to-User Information Element usage) HI3CCLinkData diff --git a/epan/dissectors/CMakeLists.txt b/epan/dissectors/CMakeLists.txt index b3f10feefd..06590032ba 100644 --- a/epan/dissectors/CMakeLists.txt +++ b/epan/dissectors/CMakeLists.txt @@ -349,6 +349,7 @@ set(DISSECTOR_PUBLIC_HEADERS packet-fmp.h packet-frame.h packet-ftam.h + packet-geonw.h packet-giop.h packet-gluster.h packet-gmr1_common.h @@ -1035,6 +1036,7 @@ set(DISSECTOR_SRC ${CMAKE_CURRENT_SOURCE_DIR}/packet-gearman.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-ged125.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-geneve.c + ${CMAKE_CURRENT_SOURCE_DIR}/packet-geonw.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-gfp.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-gift.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-giop.c diff --git a/epan/dissectors/packet-geonw.c b/epan/dissectors/packet-geonw.c new file mode 100644 index 0000000000..8163dfd45c --- /dev/null +++ b/epan/dissectors/packet-geonw.c @@ -0,0 +1,2074 @@ +/* packet-geonw.c + * Routines for GeoNetworking and BTP-A/B dissection + * Coyright 2018, C. Guerber + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* + * The GeoNetworking protocol is a network layer protocol that provides packet + * routing in an ad hoc network. It makes use of geographical positions for + * packet transport. GeoNetworking supports the communication among individual + * ITS stations as well as the distribution of packets in geographical areas. + * (Extracted from ETSI EN 302 636-4-1) + * + * The Basic Transport Protocol (BTP) provides an end-to-end, connection-less + * transport service in the ITS ad hoc network. Its main purpose is the + * multiplexing of messages from different processes at the ITS facilities + * layer, e.g. CAM and DENM from the cooperative awareness basic service and + * the distributed environmental notification basic service, for the + * transmission of packets via the GeoNetworking protocol as well as the + * de-multiplexing at the destination. + * (Extracted from ETSI EN 302 636-5-1) + * + * Reference standards: + * ETSI EN 302 636-4-1 v1.2.0 (2013-10) + * Intelligent Transport Systems (ITS); Vehicular Communications; GeoNetworking; + * Part 4: Geographical addressing and forwarding for point-to-point and + * point-to-multipoint communications; + * Sub-part 1: Media-Independent Functionality + * + * ETSI EN 302 636-5-1 v1.2.1 (2014-08) + * Intelligent Transport Systems (ITS); Vehicular Communications; GeoNetworking; + * Part 5: Transport Protocols; + * Sub-part 1: Basic Transport Protocol + * + * ETSI EN 302 636-6-1 v1.2.1 (2014-05) + * Intelligent Transport Systems (ITS); Vehicular Communications; GeoNetworking; + * Part 6: Internet Integration; + * Sub-part 1: Transmission of IPv6 Packets over GeoNetworking Protocols + * + * ETSI TS 103 248 v1.2.1 (2018-08) + * Intelligent Transport Systems (ITS); GeoNetworking; + * Port Numbers for the Basic Transport Protocol (BTP) + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "packet-e164.h" +#include "packet-geonw.h" + +/* + * Prototypes + */ +void proto_reg_handoff_btpb(void); +void proto_register_btpb(void); +void proto_reg_handoff_btpa(void); +void proto_register_btpa(void); +void proto_reg_handoff_geonw(void); +void proto_register_geonw(void); + +/* + * Constants + */ +#define HT_MASK 0xf0 +#define HST_MASK 0x0f + +// Definition of header types See section 8.7.4 table 9 +#define HT_BEACON 0x10 +#define HT_GEOUNICAST 0x20 +#define HT_GEOANYCAST 0x30 +#define HT_GEOBROADCAST 0x40 +#define HT_TSB 0x50 +#define HT_LS 0x60 + +// Area subtypes +#define HST_CIRCULAR 0x00 +#define HST_RECTANGULAR 0x01 +#define HST_ELLIPSOIDAL 0x02 + +// TSB subtype +#define HST_SINGLE_HOP 0x00 +#define HST_MULTI_HOP 0x01 + +// LS subtypes +#define HST_REQUEST 0x00 +#define HST_REPLY 0x01 + +// Types and subtype combined +#define HTST_BEACON (HT_BEACON) +#define HTST_GEOUNICAST (HT_GEOUNICAST) +#define HTST_GAC_CIRCLE (HT_GEOANYCAST|HST_CIRCULAR) +#define HTST_GAC_RECT (HT_GEOANYCAST|HST_RECTANGULAR) +#define HTST_GAC_ELLIPSE (HT_GEOANYCAST|HST_ELLIPSOIDAL) +#define HTST_GBC_CIRCLE (HT_GEOBROADCAST|HST_CIRCULAR) +#define HTST_GBC_RECT (HT_GEOBROADCAST|HST_RECTANGULAR) +#define HTST_GBC_ELLIPSE (HT_GEOBROADCAST|HST_ELLIPSOIDAL) +#define HTST_TSB_SINGLE (HT_TSB|HST_SINGLE_HOP) +#define HTST_TSB_MULT (HT_TSB|HST_MULTI_HOP) +#define HTST_LS_REQUEST (HT_LS|HST_REQUEST) +#define HTST_LS_REPLY (HT_LS|HST_REPLY) + +#define BH_LEN 4 +#define BH_NH_COMMON_HDR 1 +#define BH_NH_SECURED_PKT 2 + +#define CH_LEN 8 +#define CH_NH_BTP_A 1 +#define CH_NH_BTP_B 2 +#define CH_NH_IPV6 3 + +#define GUC_LEN 48 +#define TSB_LEN 28 +#define GAC_LEN 44 +#define GBC_LEN 44 +#define BEACON_LEN 24 +#define LS_REQUEST_LEN 36 +#define LS_REPLY_LEN 48 + +#define TST_MAX 0xffffffff + +/* + * Variables + */ +static wmem_map_t *geonw_hashtable = NULL; + +static int proto_geonw = -1; +static int proto_btpa = -1; +static int proto_btpb = -1; + +static int geonw_tap = -1; +static int btpa_tap = -1; +static int btpa_follow_tap = -1; +static int btpb_tap = -1; +static int btpb_follow_tap = -1; + +static int hf_geonw_bh = -1; +static int hf_geonw_bh_version = -1; +static int hf_geonw_bh_next_header = -1; +static int hf_geonw_bh_reserved = -1; +static int hf_geonw_bh_life_time = -1; +static int hf_geonw_bh_lt_mult = -1; +static int hf_geonw_bh_lt_base = -1; +static int hf_geonw_bh_remain_hop_limit = -1; + +static int hf_geonw_ch = -1; +static int hf_geonw_ch_next_header = -1; +static int hf_geonw_ch_reserved1 = -1; +static int hf_geonw_ch_header_type = -1; +//static int hf_geonw_ch_header_subtype = -1; +static int hf_geonw_ch_traffic_class = -1; +static int hf_geonw_ch_tc_scf = -1; +static int hf_geonw_ch_tc_offload = -1; +static int hf_geonw_ch_tc_id = -1; +static int hf_geonw_ch_flags = -1; +static int hf_geonw_ch_flags_mob = -1; +static int hf_geonw_ch_flags_reserved = -1; +static int hf_geonw_ch_payload_length = -1; +static int hf_geonw_ch_max_hop_limit = -1; +static int hf_geonw_ch_reserved2 = -1; + +static int hf_geonw_seq_num = -1; +static int hf_geonw_reserved = -1; +static int hf_geonw_so_pv = -1; +static int hf_geonw_so_pv_addr = -1; +static int hf_geonw_so_pv_addr_manual = -1; +static int hf_geonw_so_pv_addr_type = -1; +static int hf_geonw_so_pv_addr_country = -1; +static int hf_geonw_so_pv_addr_mid = -1; +static int hf_geonw_so_pv_time = -1; +static int hf_geonw_so_pv_lat = -1; +static int hf_geonw_so_pv_lon = -1; +static int hf_geonw_so_pv_pai = -1; +static int hf_geonw_so_pv_speed = -1; +static int hf_geonw_so_pv_heading = -1; +static int hf_geonw_de_pv = -1; +static int hf_geonw_de_pv_addr = -1; +static int hf_geonw_de_pv_addr_manual = -1; +static int hf_geonw_de_pv_addr_type = -1; +static int hf_geonw_de_pv_addr_country = -1; +static int hf_geonw_de_pv_addr_mid = -1; +static int hf_geonw_de_pv_time = -1; +static int hf_geonw_de_pv_lat = -1; +static int hf_geonw_de_pv_lon = -1; + +static int hf_geonw_gxc_latitude = -1; +static int hf_geonw_gxc_longitude = -1; +static int hf_geonw_gxc_radius = -1; +static int hf_geonw_gxc_distancea = -1; +static int hf_geonw_gxc_distanceb = -1; +static int hf_geonw_gxc_angle = -1; +static int hf_geonw_gxc_reserved = -1; + +static int hf_geonw_shb_reserved = -1; + +static int hf_geonw_lsrq_addr = -1; +static int hf_geonw_lsrq_addr_manual = -1; +static int hf_geonw_lsrq_addr_type = -1; +static int hf_geonw_lsrq_addr_country = -1; +static int hf_geonw_lsrq_addr_mid = -1; + +static int hf_geonw_beacon = -1; +static int hf_geonw_guc = -1; +static int hf_geonw_gac = -1; +static int hf_geonw_gbc = -1; +static int hf_geonw_tsb = -1; +static int hf_geonw_ls = -1; +static int hf_geonw_analysis_flags = -1; + +static int hf_btpa_dstport = -1; +static int hf_btpa_srcport = -1; +static int hf_btpa_port = -1; +static int hf_btpb_dstport = -1; +static int hf_btpb_dstport_info = -1; + +static int hf_geonw_resp_in = -1; +static int hf_geonw_resp_to = -1; +static int hf_geonw_no_resp = -1; +static int hf_geonw_resptime = -1; + +static gint ett_geonw = -1; +static gint ett_geonw_bh = -1; +static gint ett_geonw_bh_lt = -1; +static gint ett_geonw_ch = -1; +static gint ett_geonw_ch_tc = -1; +static gint ett_geonw_sh = -1; +static gint ett_geonw_so = -1; +static gint ett_geonw_so_add = -1; +static gint ett_geonw_de = -1; +static gint ett_geonw_de_add = -1; +static gint ett_geonw_lsrq_add = -1; +static gint ett_geonw_analysis = -1; +static gint ett_btpa = -1; +static gint ett_btpb = -1; + +static int geonw_address_type = -1; + +static expert_field ei_geonw_nz_reserved = EI_INIT; +static expert_field ei_geonw_version_err = EI_INIT; +static expert_field ei_geonw_rhl_lncb = EI_INIT; +static expert_field ei_geonw_rhl_too_low = EI_INIT; +static expert_field ei_geonw_mhl_lt_rhl = EI_INIT; +static expert_field ei_geonw_scc_too_big = EI_INIT; +static expert_field ei_geonw_analysis_duplicate = EI_INIT; +static expert_field ei_geonw_resp_not_found = EI_INIT; +static expert_field ei_geonw_out_of_range = EI_INIT; +static expert_field ei_geonw_payload_len = EI_INIT; + +static dissector_table_t geonw_subdissector_table; +static dissector_table_t btpa_subdissector_table; +static dissector_table_t btpb_subdissector_table; + +static const value_string ch_header_type_names[] = { + { HTST_BEACON, "Beacon" }, + { HTST_GEOUNICAST, "Geo Unicast" }, + { HTST_GAC_CIRCLE, "Geo-scoped Anycast Circular area" }, + { HTST_GAC_RECT, "Geo-scoped Anycast Rectangular area" }, + { HTST_GAC_ELLIPSE, "Geo-scoped Anycast Ellipsoidal area" }, + { HTST_GBC_CIRCLE, "Geo-scoped Broadcast Circular area" }, + { HTST_GBC_RECT, "Geo-scoped Broadcast Rectangular area" }, + { HTST_GBC_ELLIPSE, "Geo-scoped Broadcast Ellipsoidal area" }, + { HTST_TSB_SINGLE, "Topologucally-scoped broadcast Single-hop broadcast (SHB)" }, + { HTST_TSB_MULT, "Topologucally-scoped broadcast Multi-hop broadcast (TSB)" }, + { HTST_LS_REQUEST, "Location Service Request" }, + { HTST_LS_REPLY, "Location Service Reply" }, + { 0x00, NULL} +}; + +static dissector_handle_t geonw_handle; +static dissector_handle_t btpa_handle; +static dissector_handle_t btpb_handle; +static dissector_handle_t ipv6_handle; + +static heur_dissector_list_t btpa_heur_subdissector_list; +static heur_dissector_list_t btpb_heur_subdissector_list; + +/* + * Basic Transport Protocol A dissector + */ +static int +dissect_btpa(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + heur_dtbl_entry_t *hdtbl_entry; + int low_port, high_port; + int dst_port, src_port; + proto_item *hidden_item; + struct btpaheader *btpah; + + btpah = wmem_new0(wmem_packet_scope(), struct btpaheader); + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "BTPA"); + /* Clear out stuff in the info column */ + col_clear(pinfo->cinfo,COL_INFO); + + proto_item *ti = proto_tree_add_item(tree, proto_btpa, tvb, 0, 4, ENC_NA); + proto_tree *btpa_tree = proto_item_add_subtree(ti, ett_btpa); + + proto_tree_add_item_ret_uint(btpa_tree, hf_btpa_dstport, tvb, 0, 2, ENC_BIG_ENDIAN, &dst_port); + proto_tree_add_item_ret_uint(btpa_tree, hf_btpa_srcport, tvb, 2, 2, ENC_BIG_ENDIAN, &src_port); + + pinfo->srcport = src_port; + pinfo->destport = dst_port; + + col_append_ports(pinfo->cinfo, COL_INFO, PT_NONE, pinfo->srcport, pinfo->destport); + + // Add hidden port field + hidden_item = proto_tree_add_item(btpa_tree, hf_btpa_port, tvb, 0, 2, ENC_BIG_ENDIAN); + PROTO_ITEM_SET_HIDDEN(hidden_item); + hidden_item = proto_tree_add_item(btpa_tree, hf_btpa_port, tvb, 2, 2, ENC_BIG_ENDIAN); + PROTO_ITEM_SET_HIDDEN(hidden_item); + + btpah->btp_psrc = src_port; + btpah->btp_pdst = dst_port; + copy_address_shallow(&btpah->gnw_src, &pinfo->src); + copy_address_shallow(&btpah->gnw_dst, &pinfo->dst); + tap_queue_packet(btpa_tap, pinfo, btpah); + + tvbuff_t *next_tvb = tvb_new_subset_remaining(tvb, 4); + + if (have_tap_listener(btpa_follow_tap)) + // XXX Do as in tcp to provide port numbers? + tap_queue_packet(btpa_follow_tap, pinfo, next_tvb); + + // XXX try heuristic first preference? + + if (src_port > dst_port) { + low_port = dst_port; + high_port = src_port; + } else { + low_port = src_port; + high_port = dst_port; + } + + if (dissector_try_uint_new(btpa_subdissector_table, low_port, next_tvb, pinfo, tree, TRUE, NULL)) + return tvb_captured_length(tvb); + + if (dissector_try_uint_new(btpa_subdissector_table, high_port, next_tvb, pinfo, tree, TRUE, NULL)) + return tvb_captured_length(tvb); + + if (dissector_try_heuristic(btpa_heur_subdissector_list, next_tvb, pinfo, tree, &hdtbl_entry, NULL)) + return tvb_captured_length(tvb); + + call_data_dissector(next_tvb, pinfo, tree); + return tvb_captured_length(tvb); +} + +/* + * Basic Transport Protocol B dissector + */ +static int +dissect_btpb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + heur_dtbl_entry_t *hdtbl_entry; + guint32 dst_port; + guint32 dst_info; + struct btpbheader *btpbh; + + btpbh = wmem_new0(wmem_packet_scope(), struct btpbheader); + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "BTPB"); + /* Clear out stuff in the info column */ + col_clear(pinfo->cinfo,COL_INFO); + + proto_item *ti = proto_tree_add_item(tree, proto_btpb, tvb, 0, 4, ENC_NA); + proto_tree *btpb_tree = proto_item_add_subtree(ti, ett_btpb); + + proto_tree_add_item_ret_uint(btpb_tree, hf_btpb_dstport, tvb, 0, 2, ENC_BIG_ENDIAN, &dst_port); + proto_tree_add_item_ret_uint(btpb_tree, hf_btpb_dstport_info, tvb, 2, 2, ENC_BIG_ENDIAN, &dst_info); + + pinfo->destport = dst_port; + + char buf_dst[32]; + ws_snprintf(buf_dst, 32, "%"G_GUINT16_FORMAT, dst_port); + col_append_lstr(pinfo->cinfo, COL_INFO, " " UTF8_RIGHTWARDS_ARROW " ", buf_dst, COL_ADD_LSTR_TERMINATOR); + + // XXX Dissector table for destination port info? + + btpbh->btp_pdst = dst_port; + btpbh->btp_idst = dst_info; + copy_address_shallow(&btpbh->gnw_src, &pinfo->src); + copy_address_shallow(&btpbh->gnw_dst, &pinfo->dst); + tap_queue_packet(btpb_tap, pinfo, btpbh); + + tvbuff_t *next_tvb = tvb_new_subset_remaining(tvb, 4); + + if (have_tap_listener(btpb_follow_tap)) + // XXX Do as in tcp to provide port numbers? + tap_queue_packet(btpb_follow_tap, pinfo, next_tvb); + + // XXX try heuristic first preference? + + if (dissector_try_uint_new(btpb_subdissector_table, dst_port, next_tvb, pinfo, tree, TRUE, NULL)) { + return tvb_captured_length(tvb); + } + if (dissector_try_heuristic(btpa_heur_subdissector_list, next_tvb, pinfo, tree, &hdtbl_entry, NULL)) { + return tvb_captured_length(tvb); + } + + call_data_dissector(next_tvb, pinfo, tree); + return tvb_captured_length(tvb); +} + +/* + * =========================================================================== + * GeoNetworking dissector + * =========================================================================== + */ + +typedef struct _geonw_transaction_t { + guint32 rqst_frame; + guint32 resp_frame; + nstime_t rqst_time; + nstime_t resp_time; +} geonw_transaction_t; + +typedef struct _geonw_conv_info_t { + wmem_stack_t *unmatched_pdus; + wmem_tree_t *matched_pdus; +} geonw_conv_info_t; + +const gchar * get_geonw_name(const guint8 *addr); +const gchar* geonw_name_resolution_str(const address* addr); +int geonw_name_resolution_len(void); + +static geonw_transaction_t *transaction_start(packet_info * pinfo, proto_tree * tree); +static geonw_transaction_t *transaction_end(packet_info * pinfo, proto_tree * tree); + +static gboolean geonw_analyze_seq = TRUE; + +/* + * GeoNetworking Address Type + */ + +/* Adapter from ethernet and ipv4 Address Type code */ +struct hashgeonw; +typedef struct hashgeonw hashgeonw_t; + +struct hashgeonw { + guint status; + guint8 addr[8]; + char hexaddr[28]; + char resolved_name[MAXNAMELEN]; + + // Node follow up used for duplication detection + guint32 timestamp; + guint32 sequence_number; +}; + + +static int +geonw_str_len(const address* addr _U_) +{ + // (0/1)'.'(0..31)'.'(0..1023)'.'{eth} + return 28; +} + +static int +_geonw_to_str(const guint8* addrdata, gchar *buf, int buf_len _U_) +{ + address eth_addr; + + // Initial or Manual + if (addrdata[0] & 0x80) + *buf++ = '1'; + else + *buf++ = '0'; + *buf++ = '.'; + // Station Type + guint32_to_str_buf((addrdata[0] & 0x7C) >> 2, buf, 26); + buf += (unsigned) strlen(buf); + *buf++ = '.'; + // Country Code + guint32_to_str_buf(((guint32)(addrdata[0] & 0x03) << 8) + addrdata[1], buf, 23); // > 23 + buf += (unsigned) strlen(buf); + *buf++ = '.'; + // LL_ADDR + set_address(ð_addr, AT_ETHER, 6, &(addrdata[2])); + ether_to_str(ð_addr, buf, 18); + + return 28; +} + +static int +geonw_to_str(const address* addr, gchar *buf, int buf_len _U_) +{ + return _geonw_to_str((const guint8 *)addr->data, buf, buf_len); +} + +static const char* +geonw_col_filter_str(const address* addr _U_, gboolean is_src) +{ + if (is_src) + return "geonw.src_pos.addr"; + + return "geonw.dst_pos.addr"; +} + +static int +geonw_len(void) +{ + return 8; +} + +static guint +geonw_addr_hash(gconstpointer key) +{ + return wmem_strong_hash((const guint8 *)key, 8); +} + +static gboolean +geonw_addr_cmp(gconstpointer a, gconstpointer b) +{ + return (memcmp(a, b, 8) == 0); +} + +/* + * These two value_string are used for address resolv: + */ +static const value_string itss_type_small_names[] = { + { 0, "unk" }, + { 1, "ped" }, + { 2, "cyc" }, + { 3, "mop" }, + { 4, "mot" }, + { 5, "pas" }, + { 6, "bus" }, + { 7, "ltr" }, + { 8, "htr" }, + { 9, "trl" }, + { 10, "spe" }, + { 11, "trm" }, + { 15, "rsu" }, + { 0, NULL} +}; + +/* Resolve geonetworking address */ +static hashgeonw_t * +geonw_addr_resolve(hashgeonw_t *tp) { + const guint8 *addr = tp->addr; + guint16 val; + char *rname = tp->resolved_name; + address eth_addr; + guint8 l1, l2; + + // Initial or Manual + if (addr[0] & 0x80) + *rname++ = 'm'; + else + *rname++ = 'i'; + *rname++ = '.'; + // Station Type + val = (addr[0] & 0x7C) >> 2; + const char *string = try_val_to_str(val, itss_type_small_names); + if (string == NULL) { + guint32_to_str_buf(val, rname, MAXNAMELEN-2); + l1 = (unsigned) strlen(rname); + } + else { + l1 = (guint8) g_strlcpy(rname, string, MAXNAMELEN-2); + } + rname += l1; + *rname++ = '.'; + // Country Code + val = ((guint32)(addr[0] & 0x03) << 8) + addr[1]; + string = try_val_to_str(val, E164_ISO3166_country_code_short_value); + if (string == NULL) { + guint32_to_str_buf(val, rname, MAXNAMELEN-12); + l2 = (unsigned) strlen(rname); + } + else { + l2 = (guint8) g_strlcpy(rname, string, MAXNAMELEN-l1-3); + } + rname += l2; + l1 += l2; + *rname++ = '.'; + // LL_ADDR + set_address(ð_addr, AT_ETHER, 6, &(addr[2])); + ether_to_str(ð_addr, rname, 18); + // XXX We could use ether_name_resolution_str: + // g_strlcpy(rname, ether_name_resolution_str(ð_addr), MAXNAMELEN-l1-4); + + tp->status = 1; + + return tp; +} + +static hashgeonw_t * +geonw_hash_new_entry(const guint8 *addr, gboolean resolve) +{ + hashgeonw_t *tp; + + tp = wmem_new(wmem_file_scope(), hashgeonw_t); + memcpy(tp->addr, addr, sizeof(tp->addr)); + /* Values returned by bytes_to_hexstr_punct() are *not* null-terminated */ + _geonw_to_str(addr, tp->hexaddr, 28); + tp->resolved_name[0] = '\0'; + tp->status = 0; + + if (resolve) + geonw_addr_resolve(tp); + + wmem_map_insert(geonw_hashtable, tp->addr, tp); + + return tp; +} /* geonw_hash_new_entry */ + +static hashgeonw_t * +geonw_name_lookup(const guint8 *addr, gboolean resolve) +{ + hashgeonw_t *tp; + + tp = (hashgeonw_t *)wmem_map_lookup(geonw_hashtable, addr); + + if (tp == NULL) { + tp = geonw_hash_new_entry(addr, resolve); + } else { + if (resolve && !tp->status) { + geonw_addr_resolve(tp); /* Found but needs to be resolved */ + } + } + + return tp; + +} /* geonw_name_lookup */ + +const gchar * +get_geonw_name(const guint8 *addr) +{ + hashgeonw_t *tp; + gboolean resolve = gbl_resolv_flags.network_name; + + tp = geonw_name_lookup(addr, resolve); + + return resolve ? tp->resolved_name : tp->hexaddr; + +} /* get_geonw_name */ + +const gchar* geonw_name_resolution_str(const address* addr) +{ + return get_geonw_name((const guint8 *)addr->data); +} + +int geonw_name_resolution_len(void) +{ + return MAX_ADDR_STR_LEN; /* XXX - This can be lower */ +} + +/* + * Conversations for GeoNetworking + */ + +/* Adapted from ICMP echo request/reply code */ + +/* GeoNw LS request/reply transaction statistics ... XXX used by GeoNw tap(s) */ +static geonw_transaction_t *transaction_start(packet_info * pinfo, proto_tree * tree) +{ + conversation_t *conversation; + geonw_conv_info_t *geonw_info; + geonw_transaction_t *geonw_trans; + wmem_tree_key_t geonw_key[3]; + proto_item *it; + + /* Handle the conversation tracking */ + conversation = find_conversation(pinfo->num, &pinfo->src, &pinfo->dst, conversation_pt_to_endpoint_type(pinfo->ptype), HT_LS, HT_LS, 0); + if (conversation == NULL) { + /* No, this is a new conversation. */ + conversation = conversation_new(pinfo->num, &pinfo->src, &pinfo->dst, conversation_pt_to_endpoint_type(pinfo->ptype), HT_LS, HT_LS, 0); + } + geonw_info = (geonw_conv_info_t *)conversation_get_proto_data(conversation, proto_geonw); + if (geonw_info == NULL) { + geonw_info = wmem_new(wmem_file_scope(), geonw_conv_info_t); + geonw_info->unmatched_pdus = wmem_stack_new(wmem_file_scope()); + geonw_info->matched_pdus = wmem_tree_new(wmem_file_scope()); + conversation_add_proto_data(conversation, proto_geonw, geonw_info); + } + + if (!PINFO_FD_VISITED(pinfo)) { + /* this is a new request, create a new transaction structure and map it to the + unmatched table + */ + geonw_trans = wmem_new(wmem_file_scope(), geonw_transaction_t); + geonw_trans->rqst_frame = pinfo->num; + geonw_trans->resp_frame = 0; + geonw_trans->rqst_time = pinfo->abs_ts; + nstime_set_zero(&geonw_trans->resp_time); + wmem_stack_push(geonw_info->unmatched_pdus, (void *) geonw_trans); + } else { + /* Already visited this frame */ + guint32 frame_num = pinfo->num; + + geonw_key[0].length = 1; + geonw_key[0].key = &frame_num; + geonw_key[1].length = 0; + geonw_key[1].key = NULL; + + geonw_trans = (geonw_transaction_t *)wmem_tree_lookup32_array(geonw_info->matched_pdus, geonw_key); + } + if (geonw_trans == NULL) { + if (PINFO_FD_VISITED(pinfo)) { + /* No response found - add field and expert info */ + it = proto_tree_add_item(tree, hf_geonw_no_resp, NULL, 0, 0, ENC_NA); + PROTO_ITEM_SET_GENERATED(it); + + col_append_fstr(pinfo->cinfo, COL_INFO, " (no response found!)"); + + /* Expert info. */ + expert_add_info_format(pinfo, it, &ei_geonw_resp_not_found, "No response seen to LS Request"); + } + + return NULL; + } + + /* Print state tracking in the tree */ + if (geonw_trans->resp_frame) { + it = proto_tree_add_uint(tree, hf_geonw_resp_in, NULL, 0, 0, geonw_trans->resp_frame); + PROTO_ITEM_SET_GENERATED(it); + + col_append_frame_number(pinfo, COL_INFO, " (reply in %u)", geonw_trans->resp_frame); + } + + return geonw_trans; + +} /* transaction_start() */ + +static geonw_transaction_t *transaction_end(packet_info * pinfo, proto_tree * tree) +{ + conversation_t *conversation; + geonw_conv_info_t *geonw_info; + geonw_transaction_t *geonw_trans; + wmem_tree_key_t geonw_key[3]; + proto_item *it; + nstime_t ns; + double resp_time; + + conversation = find_conversation(pinfo->num, &pinfo->src, &pinfo->dst, conversation_pt_to_endpoint_type(pinfo->ptype), HT_LS, HT_LS, 0); + if (conversation == NULL) { + return NULL; + } + + geonw_info = (geonw_conv_info_t *)conversation_get_proto_data(conversation, proto_geonw); + if (geonw_info == NULL) { + return NULL; + } + + if (!PINFO_FD_VISITED(pinfo)) { + guint32 frame_num; + + geonw_trans = (geonw_transaction_t *)wmem_stack_peek(geonw_info->unmatched_pdus); + if (geonw_trans == NULL) { + return NULL; + } + + /* we have already seen this response, or an identical one */ + if (geonw_trans->resp_frame != 0) { + return NULL; + } + + geonw_trans->resp_frame = pinfo->num; + + /* we found a match. Add entries to the matched table for both request and reply frames + */ + geonw_key[0].length = 1; + geonw_key[0].key = &frame_num; + geonw_key[1].length = 0; + geonw_key[1].key = NULL; + + frame_num = geonw_trans->rqst_frame; + wmem_tree_insert32_array(geonw_info->matched_pdus, geonw_key, (void *) geonw_trans); + + frame_num = geonw_trans->resp_frame; + wmem_tree_insert32_array(geonw_info->matched_pdus, geonw_key, (void *) geonw_trans); + } else { + /* Already visited this frame */ + guint32 frame_num = pinfo->num; + + geonw_key[0].length = 1; + geonw_key[0].key = &frame_num; + geonw_key[1].length = 0; + geonw_key[1].key = NULL; + + geonw_trans = (geonw_transaction_t *)wmem_tree_lookup32_array(geonw_info->matched_pdus, geonw_key); + + if (geonw_trans == NULL) { + return NULL; + } + } + + + it = proto_tree_add_uint(tree, hf_geonw_resp_to, NULL, 0, 0, geonw_trans->rqst_frame); + PROTO_ITEM_SET_GENERATED(it); + + nstime_delta(&ns, &pinfo->abs_ts, &geonw_trans->rqst_time); + geonw_trans->resp_time = ns; + resp_time = nstime_to_msec(&ns); + it = proto_tree_add_double_format_value(tree, hf_geonw_resptime, NULL, 0, 0, resp_time, "%.3f ms", resp_time); + PROTO_ITEM_SET_GENERATED(it); + + col_append_frame_number(pinfo, COL_INFO, " (request in %d)", geonw_trans->rqst_frame); + + return geonw_trans; + +} /* transaction_end() */ + +// Adapted from TCP sequence number analysis + +// Conversation data +struct geonw_analysis { + // Node follow up used for duplication detection + guint32 timestamp; + guint16 sequence_number; +}; + +// The actual dissector +// XXX COL_INFO to be improved +static int +dissect_geonw(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + guint8 bh_next_header; + guint32 ch_next_header; + guint32 header_type; + guint32 rhl; + guint32 tmp_val; + gint offset = 0; + proto_item *ti; + gint hdr_len = 0; + guint32 payload_len = 0; + int reserved; + guint32 timestamp; + guint32 sequence_number = SN_MAX + 1; + struct geonwheader *geonwh; + gint32 latlon; + + geonwh = wmem_new0(wmem_packet_scope(), struct geonwheader); + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "GEONW"); + /* Clear out stuff in the info column */ + col_clear(pinfo->cinfo,COL_INFO); + + bh_next_header = tvb_get_guint8(tvb, 0) & 0x0f; + header_type = tvb_get_guint8(tvb, 5); + + if (bh_next_header == BH_NH_SECURED_PKT) { + hdr_len = BH_LEN; + } + else { + hdr_len = BH_LEN + CH_LEN; + switch(header_type & HT_MASK) { + case HT_BEACON: + hdr_len += BEACON_LEN; + break; + case HT_GEOUNICAST: + hdr_len += GUC_LEN; + break; + case HT_GEOANYCAST: + hdr_len += GAC_LEN; + break; + case HT_GEOBROADCAST: + hdr_len += GBC_LEN; + break; + case HT_TSB: + hdr_len += TSB_LEN; + break; + case HT_LS: + hdr_len += LS_REQUEST_LEN; + if (header_type == HTST_LS_REPLY) { + hdr_len += (LS_REPLY_LEN - LS_REQUEST_LEN); + } + break; + default: + hdr_len = -1; + } + } + ti = proto_tree_add_item(tree, proto_geonw, tvb, 0, hdr_len, ENC_NA); + proto_tree *geonw_tree = proto_item_add_subtree(ti, ett_geonw); + + // Basic Header subtree + ti = proto_tree_add_item(geonw_tree, hf_geonw_bh, tvb, 0, 4, ENC_NA); + proto_tree *geonw_bh_tree = proto_item_add_subtree(ti, ett_geonw_bh); + + ti = proto_tree_add_item_ret_uint(geonw_bh_tree, hf_geonw_bh_version, tvb, offset, 1, ENC_BIG_ENDIAN, &tmp_val); + geonwh->gnw_ver = tmp_val; + // Shall be 0 + if (tmp_val) { + col_add_fstr(pinfo->cinfo, COL_INFO, + "Bogus GeoNetworking version (%u, must be 0)", tmp_val); + expert_add_info_format(pinfo, ti, &ei_geonw_version_err, "Bogus GeoNetworking version"); + return tvb_captured_length(tvb); + } + proto_tree_add_item(geonw_bh_tree, hf_geonw_bh_next_header, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + // Reserved byte + // Expert info if not zero? + ti = proto_tree_add_item_ret_uint(geonw_bh_tree, hf_geonw_bh_reserved, tvb, offset, 1, ENC_NA, &reserved); + if (reserved) { + expert_add_info(pinfo, ti, &ei_geonw_nz_reserved); + } + offset += 1; + + // Subtree and lt_mult and lt_base + ti = proto_tree_add_item_ret_uint(geonw_bh_tree, hf_geonw_bh_life_time, tvb, offset, 1, ENC_BIG_ENDIAN, &tmp_val); + geonwh->gnw_lt = tmp_val; + proto_tree *geonw_bh_lt_tree = proto_item_add_subtree(ti, ett_geonw_bh_lt); + + proto_tree_add_item(geonw_bh_lt_tree, hf_geonw_bh_lt_mult, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(geonw_bh_lt_tree, hf_geonw_bh_lt_base, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + + proto_item *rhl_ti = proto_tree_add_item_ret_uint(geonw_bh_tree, hf_geonw_bh_remain_hop_limit, tvb, offset, 1, ENC_BIG_ENDIAN, &rhl); + geonwh->gnw_rhl = rhl; + /* + * Flag a low RHL if the next header is not a common header + */ + if (rhl < 5 && bh_next_header != BH_NH_COMMON_HDR) { + expert_add_info_format(pinfo, rhl_ti, &ei_geonw_rhl_too_low, "\"Remain Hop Limit\" only %u", rhl); + } + offset += 1; + + if (bh_next_header == BH_NH_SECURED_PKT) { + // XXX Try to decrypt? + ti = proto_tree_add_item(tree, proto_geonw, tvb, 0, -1, ENC_NA); + return tvb_captured_length(tvb); + } + + if (bh_next_header == BH_NH_COMMON_HDR) { + // Common Header subtree + ti = proto_tree_add_item(geonw_tree, hf_geonw_ch, tvb, offset, 8, ENC_NA); + proto_tree *geonw_ch_tree = proto_item_add_subtree(ti, ett_geonw_ch); + + // Next Header + proto_tree_add_item_ret_uint(geonw_ch_tree, hf_geonw_ch_next_header, tvb, offset, 1, ENC_BIG_ENDIAN, &ch_next_header); + geonwh->gnw_proto = ch_next_header; + // Reserved + ti = proto_tree_add_item_ret_uint(geonw_ch_tree, hf_geonw_ch_reserved1, tvb, offset, 1, ENC_NA, &reserved); + if (reserved) { + expert_add_info(pinfo, ti, &ei_geonw_nz_reserved); + } + offset += 1; + + // HT + proto_tree_add_item_ret_uint(geonw_ch_tree, hf_geonw_ch_header_type, tvb, offset, 1, ENC_BIG_ENDIAN, &header_type); + geonwh->gnw_htype = header_type; + col_add_str(pinfo->cinfo, COL_INFO, val_to_str(header_type, ch_header_type_names, "Unknown (%u)")); + offset += 1; + + /* Now that we know the header type, lets add expert info on RHL + * RHL shall be + * = 1 if parameter Packet transport type in the service primitive + * GN-DATA.request is SHB, or if Header type HT = 1 (BEACON) + * = Value of optional Maximum hop limit parameter from service + * primitive GN-DATA.request + * = Otherwise GN protocol constant itsGnDefaultHopLimit if + * GN-DATA.request parameter Packet transport type is GUC, GBC, GBC + * or TSB + * Flag a low RHL if the packet is not BEACON or SHB. + */ + if (header_type == HTST_BEACON || header_type == HTST_TSB_SINGLE) { + if (rhl > 1) { + expert_add_info_format(pinfo, rhl_ti, &ei_geonw_rhl_lncb, "\"Remain Hop Limit\" != 1 for BEACON or SHB (%u)", rhl); + } + } else if (rhl < 5) { + expert_add_info_format(pinfo, rhl_ti, &ei_geonw_rhl_too_low, "\"Remain Hop Limit\" only %u", rhl); + } + + // TC + ti = proto_tree_add_item_ret_uint(geonw_ch_tree, hf_geonw_ch_traffic_class, tvb, offset, 1, ENC_BIG_ENDIAN, &tmp_val); + geonwh->gnw_tc = tmp_val; + proto_tree *geonw_ch_tc_tree = proto_item_add_subtree(ti, ett_geonw_ch_tc); + + proto_tree_add_item(geonw_ch_tc_tree, hf_geonw_ch_tc_scf, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(geonw_ch_tc_tree, hf_geonw_ch_tc_offload, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(geonw_ch_tc_tree, hf_geonw_ch_tc_id, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + + ti = proto_tree_add_item(geonw_ch_tree, hf_geonw_ch_flags, tvb, offset, 1, ENC_NA); + proto_tree *geonw_ch_flag_tree = proto_item_add_subtree(ti, ett_geonw_ch_tc); + // Flag (itsGnIsMobile) + proto_tree_add_item_ret_uint(geonw_ch_flag_tree, hf_geonw_ch_flags_mob, tvb, offset, 1, ENC_BIG_ENDIAN, &tmp_val); + geonwh->gnw_flags = tmp_val; + ti = proto_tree_add_item_ret_uint(geonw_ch_flag_tree, hf_geonw_ch_flags_reserved, tvb, offset, 1, ENC_BIG_ENDIAN, &reserved); + if (reserved & 0x7f) { + expert_add_info(pinfo, ti, &ei_geonw_nz_reserved); + } + offset += 1; + + // PL (16 bits) + ti = proto_tree_add_item_ret_uint(geonw_ch_tree, hf_geonw_ch_payload_length, tvb, offset, 2, ENC_BIG_ENDIAN, &payload_len); + geonwh->gnw_len = payload_len; + if (hdr_len > 0) { // We know the length of the header + if (payload_len) { + if (((header_type & HT_MASK) == HT_LS) || (header_type == HT_BEACON)) { + expert_add_info(pinfo, ti, &ei_geonw_nz_reserved); + } + else if ((payload_len + (unsigned) hdr_len) > tvb_reported_length(tvb)) { + expert_add_info(pinfo, ti, &ei_geonw_payload_len); + } + else { + /* + * Now that we know that the total length of this IP datagram isn't + * obviously bogus, adjust the length of this tvbuff to include only + * the IP datagram. + */ + set_actual_length(tvb, hdr_len + payload_len); + } + } + else { + set_actual_length(tvb, hdr_len); + } + } + offset += 2; + + // MHL + proto_tree_add_item_ret_uint(geonw_ch_tree, hf_geonw_ch_max_hop_limit, tvb, offset, 1, ENC_BIG_ENDIAN, &tmp_val); + geonwh->gnw_mhl = tmp_val; + // Expert mhl < rhl: packet will be ignored + if (tmp_val < rhl) { + expert_add_info_format(pinfo, rhl_ti, &ei_geonw_mhl_lt_rhl, "Ignored: \"Remain Hop Limit\" > %u (mhl)", tmp_val); + } + offset += 1; + + // Reserved... + ti = proto_tree_add_item_ret_uint(geonw_ch_tree, hf_geonw_ch_reserved2, tvb, offset, 1, ENC_NA, &reserved); + // Expert info if not zero + if (reserved) { + expert_add_info(pinfo, ti, &ei_geonw_nz_reserved); + } + offset += 1; + + geonwh->gnw_sn = SN_MAX+1; + + proto_tree *geonw_sh_tree; + switch(header_type & HT_MASK) { + case HT_BEACON: + ti = proto_tree_add_item(geonw_tree, hf_geonw_beacon, tvb, offset, hdr_len-offset, ENC_NA); + break; + case HT_GEOUNICAST: + ti = proto_tree_add_item(geonw_tree, hf_geonw_guc, tvb, offset, hdr_len-offset, ENC_NA); + break; + case HT_GEOANYCAST: + ti = proto_tree_add_item(geonw_tree, hf_geonw_gac, tvb, offset, hdr_len-offset, ENC_NA); + break; + case HT_GEOBROADCAST: + ti = proto_tree_add_item(geonw_tree, hf_geonw_gbc, tvb, offset, hdr_len-offset, ENC_NA); + break; + case HT_TSB: + ti = proto_tree_add_item(geonw_tree, hf_geonw_tsb, tvb, offset, hdr_len-offset, ENC_NA); + break; + case HT_LS: + ti = proto_tree_add_item(geonw_tree, hf_geonw_ls, tvb, offset, hdr_len-offset, ENC_NA); + break; + default: + // XXX Malformed or expert info? + // Exit if header_type unknown? + return tvb_captured_length(tvb); + } + geonw_sh_tree = proto_item_add_subtree(ti, ett_geonw_sh); + + switch(header_type) { + case HTST_GEOUNICAST: + case HTST_GAC_CIRCLE: + case HTST_GAC_RECT: + case HTST_GAC_ELLIPSE: + case HTST_GBC_CIRCLE: + case HTST_GBC_RECT: + case HTST_GBC_ELLIPSE: + case HTST_TSB_MULT: + case HTST_LS_REQUEST: + case HTST_LS_REPLY: + // Seq num + proto_tree_add_item_ret_uint(geonw_sh_tree, hf_geonw_seq_num, tvb, offset, 2, ENC_BIG_ENDIAN, &sequence_number); + geonwh->gnw_sn = sequence_number; + offset += 2; + // 16 bits reserved + ti = proto_tree_add_item_ret_uint(geonw_sh_tree, hf_geonw_reserved, tvb, offset, 2, ENC_BIG_ENDIAN, &reserved); + // Expert info if not zero? + if (reserved) { + expert_add_info(pinfo, ti, &ei_geonw_nz_reserved); + } + offset += 2; + // XXX Seq num matching? + case HTST_TSB_SINGLE: + case HTST_BEACON: + break; + } + + // Every packet has source address + ti = proto_tree_add_item(geonw_sh_tree, hf_geonw_so_pv, tvb, offset, 24, ENC_NA); + proto_tree *geonw_so_tree = proto_item_add_subtree(ti, ett_geonw_so); + + ti = proto_tree_add_item(geonw_so_tree, hf_geonw_so_pv_addr, tvb, offset, 8, ENC_NA); + proto_tree *geonw_so_add_tree = proto_item_add_subtree(ti, ett_geonw_so); + set_address_tvb(&pinfo->net_src, geonw_address_type, 8, tvb, offset); + copy_address_shallow(&pinfo->src, &pinfo->net_src); + copy_address_shallow(&geonwh->gnw_src, &pinfo->src); + + proto_tree_add_item(geonw_so_add_tree, hf_geonw_so_pv_addr_manual, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(geonw_so_add_tree, hf_geonw_so_pv_addr_type, tvb, offset, 1, ENC_BIG_ENDIAN); + ti = proto_tree_add_item_ret_uint(geonw_so_add_tree, hf_geonw_so_pv_addr_country, tvb, offset, 2, ENC_BIG_ENDIAN, &reserved); + if (reserved > 999) { + expert_add_info(pinfo, ti, &ei_geonw_scc_too_big); + } + offset += 2; + proto_tree_add_item(geonw_so_add_tree, hf_geonw_so_pv_addr_mid, tvb, offset, 6, ENC_NA); + offset += 6; + + ti = proto_tree_add_item_ret_uint(geonw_so_tree, hf_geonw_so_pv_time, tvb, offset, 4, ENC_BIG_ENDIAN, ×tamp); + geonwh->gnw_tst = timestamp; + + // XXX Is it possible to "follow" a station when updating its GN_ADDR? + + if(geonw_analyze_seq && !(pinfo->fd->flags.visited)) { + // Duplication detection uses SN and TST or only TST (see Annex A of ETSI EN 302 636-4-1) + // We rely on address type and hashtable as this shall be done on a per station basis (i.e. not over a conversation) + // We do not try to consider GN_ADDR updates (due to duplicate address detection or anonymous setting) + hashgeonw_t *tp = (hashgeonw_t *)wmem_map_lookup(geonw_hashtable, pinfo->net_src.data); + if (tp == NULL) { + tp = geonw_hash_new_entry((const guint8 *)pinfo->net_src.data, FALSE); + tp->sequence_number = sequence_number; + tp->timestamp = timestamp; + } else { + if ((sequence_number <= SN_MAX) && (tp->sequence_number > SN_MAX)) { + tp->sequence_number = sequence_number; + tp->timestamp = timestamp; + } + else if (sequence_number <= SN_MAX) { + /* + * 1 P is the received GeoNetworking packet + * 2 SN(P) is the sequence number in the received GeoNetworking packet + * 3 SN(SO) is the last received sequence number from source SO + * 4 SN_MAX is the maximum sequence number = 2^16 - 1 + * 5 TST(P) is the timestamp in the received GeoNetworking packet + * 6 TST(SO) is the last received timestamp from source SO + * 7 TST_MAX is the maximum value of the timestamp = 2^32 - 1 + * 8 + * 9 IF (((TST(P) > TST(SO) AND ((TST(P) - TST(SO)) <= TST_MAX/2)) OR + * ((TST(SO) > TST(P)) AND ((TST(SO) - TST(P)) > TST_MAX/2))) THEN + * 10 # TST(P) is greater than TST(SO) + * 11 TST(SO) = TST(P) + * 12 SN(SO) = SN(P) # P is not a duplicate packet + * 13 ELSEIF TST(P) = TST(SO) THEN + * 14 IF (((SN(P) > SN(SO) AND ((SN(P) - SN(SO)) <= SN_MAX/2)) OR + * ((SN(SO) > SN(P)) AND ((SN(SO) - SN(P)) > SN_MAX/2))) THEN + * 15 # SN(P) is greater than SN(SO) + * 16 TST(SO) = TST(P) + * 17 SN(SO) = SN(P) # P is not a duplicate packet + * 18 ELSE + * 19 # SN(P) is not greater than SN(SO) + * 20 # P is a duplicate + * 21 ENDIF + * 22 ELSE + * 23 # TST(P) not greater than TST(SO) + * 24 ENDIF + */ + if (((timestamp > tp->timestamp) && (((guint64)timestamp - (guint64)tp->timestamp) <= (guint64)TST_MAX/2)) || + ((tp->timestamp > timestamp) && (((guint64)tp->timestamp - (guint64)timestamp) > (guint64)TST_MAX/2))) { + // TST(P) is greater than TST(SO) + tp->sequence_number = sequence_number; + tp->timestamp = timestamp; // P is not a duplicate packet + } else if (timestamp == tp->timestamp) { + if (((sequence_number > tp->sequence_number) && ((sequence_number - tp->sequence_number) <= SN_MAX/2)) || + ((tp->sequence_number > sequence_number) && ((tp->sequence_number - sequence_number) > SN_MAX/2))) { + // SN(P) is greater than SN(SO) + tp->timestamp = timestamp; + tp->sequence_number = sequence_number; // P is not a duplicate packet + } else { + // SN(P) is not greater than SN(SO) + // P is a duplicate + ti = proto_tree_add_item(geonw_tree, hf_geonw_analysis_flags, tvb, 0, 0, ENC_NA); + PROTO_ITEM_SET_GENERATED(ti); + expert_add_info(pinfo, ti, &ei_geonw_analysis_duplicate); + col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[Duplicate packet] "); + } + } // else { # TST(P) not greater than TST(SO) } + } + else { + /* + * 1 P is the received GeoNetworking packet + * 2 TST(P) is the timestamp in the received GeoNetworking packet + * 3 TST(SO) is the last received timestamp from source SO + * 4 TS_MAX is the maximum value of the timestamp = 2^32 - 1 + * 5 + * 6 IF (((TST(P) > TST(SO) AND ((TST(P) - TST(SO)) <= TST_MAX/2)) OR + * ((TST(SO) > TST(P)) AND ((TST(SO) - TST(P)) > TST_MAX/2))) THEN + * 7 # TST(P) is greater than TST(SO) + * 8 TST(SO) = TST(P) # P is not a duplicate packet + * 9 ELSE + * 10 # P is a duplicate + * 11 ENDIF + */ + if (((timestamp > tp->timestamp) && (((guint64)timestamp - (guint64)tp->timestamp) <= (guint64)TST_MAX/2)) || + ((tp->timestamp > timestamp) && (((guint64)tp->timestamp - (guint64)timestamp) > (guint64)TST_MAX/2))) { + // TST(P) is greater than TST(SO) + tp->timestamp = timestamp; // P is not a duplicate packet + } else { + // P is a duplicate + ti = proto_tree_add_item(geonw_tree, hf_geonw_analysis_flags, tvb, 0, 0, ENC_NA); + PROTO_ITEM_SET_GENERATED(ti); + expert_add_info(pinfo, ti, &ei_geonw_analysis_duplicate); + col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[Duplicate packet] "); + } + } + } + } + + offset += 4; + ti = proto_tree_add_item_ret_int(geonw_so_tree, hf_geonw_so_pv_lat, tvb, offset, 4, ENC_BIG_ENDIAN, &latlon); + if (latlon < -900000000 || latlon > 900000000) { + expert_add_info_format(pinfo, ti, &ei_geonw_out_of_range, "Latitude out of range (%f)", (float)latlon/10000000); + } + geonwh->gnw_lat = latlon; + offset += 4; + ti = proto_tree_add_item_ret_int(geonw_so_tree, hf_geonw_so_pv_lon, tvb, offset, 4, ENC_BIG_ENDIAN, &latlon); + if (latlon < -1800000000 || latlon > 1800000000) { + expert_add_info_format(pinfo, ti, &ei_geonw_out_of_range, "Latitude out of range (%f)", (float)latlon/10000000); + } + geonwh->gnw_lon = latlon; + offset += 4; + proto_tree_add_item(geonw_so_tree, hf_geonw_so_pv_pai, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(geonw_so_tree, hf_geonw_so_pv_speed, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + ti = proto_tree_add_item_ret_uint(geonw_so_tree, hf_geonw_so_pv_heading, tvb, offset, 2, ENC_BIG_ENDIAN, &tmp_val); + if (tmp_val > 3600) { + expert_add_info_format(pinfo, ti, &ei_geonw_out_of_range, "Out of range [0..360] (%f)", (float)tmp_val/10); + } + offset += 2; + + proto_tree *geonw_de_tree = NULL; + proto_tree *geonw_de_add_tree = NULL; + switch(header_type) { + case HTST_GEOUNICAST: + case HTST_LS_REPLY: + // Destination address + ti = proto_tree_add_item(geonw_sh_tree, hf_geonw_de_pv, tvb, offset, 20, ENC_NA); + geonw_de_tree = proto_item_add_subtree(ti, ett_geonw_de); + + ti = proto_tree_add_item(geonw_de_tree, hf_geonw_de_pv_addr, tvb, offset, 8, ENC_NA); + geonw_de_add_tree = proto_item_add_subtree(ti, ett_geonw_de); + set_address_tvb(&pinfo->net_dst, geonw_address_type, 8, tvb, offset); + copy_address_shallow(&pinfo->dst, &pinfo->net_dst); + copy_address_shallow(&geonwh->gnw_dst, &pinfo->dst); + + if (header_type == HTST_LS_REPLY) { + transaction_end(pinfo, geonw_tree); + } + // XXX else could "find or create conversation" using HTST_GEOUNICAST as port if needed + + proto_tree_add_item(geonw_de_add_tree, hf_geonw_de_pv_addr_manual, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(geonw_de_add_tree, hf_geonw_de_pv_addr_type, tvb, offset, 1, ENC_BIG_ENDIAN); + ti = proto_tree_add_item_ret_uint(geonw_de_add_tree, hf_geonw_de_pv_addr_country, tvb, offset, 2, ENC_BIG_ENDIAN, &reserved); + if (reserved > 999) { + expert_add_info(pinfo, ti, &ei_geonw_scc_too_big); + } + offset += 2; + proto_tree_add_item(geonw_de_add_tree, hf_geonw_de_pv_addr_mid, tvb, offset, 6, ENC_NA); + offset += 6; + + proto_tree_add_item(geonw_de_tree, hf_geonw_de_pv_time, tvb, offset, 4, ENC_BIG_ENDIAN); + offset += 4; + ti = proto_tree_add_item_ret_int(geonw_de_tree, hf_geonw_de_pv_lat, tvb, offset, 4, ENC_BIG_ENDIAN, &latlon); + if (latlon < -900000000 || latlon > 900000000) { + expert_add_info_format(pinfo, ti, &ei_geonw_out_of_range, "Latitude out of range (%f)", (float)latlon/10000000); + } + offset += 4; + ti = proto_tree_add_item_ret_int(geonw_de_tree, hf_geonw_de_pv_lon, tvb, offset, 4, ENC_BIG_ENDIAN, &latlon); + if (latlon < -1800000000 || latlon > 1800000000) { + expert_add_info_format(pinfo, ti, &ei_geonw_out_of_range, "Latitude out of range (%f)", (float)latlon/10000000); + } + offset += 4; + break; + case HTST_TSB_SINGLE: + // Reserved 32 bits + ti = proto_tree_add_item_ret_uint(geonw_sh_tree, hf_geonw_shb_reserved, tvb, offset, 4, ENC_BIG_ENDIAN, &reserved); + if (reserved) { + expert_add_info(pinfo, ti, &ei_geonw_nz_reserved); + } + offset += 4; + break; + case HTST_GAC_CIRCLE: + case HTST_GAC_RECT: + case HTST_GAC_ELLIPSE: + case HTST_GBC_CIRCLE: + case HTST_GBC_RECT: + case HTST_GBC_ELLIPSE: + ti = proto_tree_add_item_ret_int(geonw_sh_tree, hf_geonw_gxc_latitude, tvb, offset, 4, ENC_BIG_ENDIAN, &latlon); + if (latlon < -900000000 || latlon > 900000000) { + expert_add_info_format(pinfo, ti, &ei_geonw_out_of_range, "Latitude out of range (%f)", (float)latlon/10000000); + } + offset += 4; + ti = proto_tree_add_item_ret_int(geonw_sh_tree, hf_geonw_gxc_longitude, tvb, offset, 4, ENC_BIG_ENDIAN, &latlon); + if (latlon < -1800000000 || latlon > 1800000000) { + expert_add_info_format(pinfo, ti, &ei_geonw_out_of_range, "Latitude out of range (%f)", (float)latlon/10000000); + } + offset += 4; + switch(header_type&0x0f) { + case HST_CIRCULAR: + /* + * According to EN 302 363-4-1: + * In case of a circular area (GeoNetworking packet + * sub-type HST = 0), the fields shall be set to the + * following values: + * 1) Distance a is set to the radius r. + * 2) Distance b is set to 0. + * 3) Angle is set to 0. + */ + proto_tree_add_item(geonw_sh_tree, hf_geonw_gxc_radius, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + ti = proto_tree_add_item_ret_uint(geonw_sh_tree, hf_geonw_gxc_distanceb, tvb, offset, 2, ENC_BIG_ENDIAN, &reserved); + if (reserved) { + expert_add_info(pinfo, ti, &ei_geonw_nz_reserved); + } + offset += 2; + ti = proto_tree_add_item_ret_uint(geonw_sh_tree, hf_geonw_gxc_angle, tvb, offset, 2, ENC_BIG_ENDIAN, &reserved); + if (reserved) { + expert_add_info(pinfo, ti, &ei_geonw_nz_reserved); + } + offset += 2; + break; + case HST_RECTANGULAR: + case HST_ELLIPSOIDAL: + proto_tree_add_item(geonw_sh_tree, hf_geonw_gxc_distancea, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + proto_tree_add_item(geonw_sh_tree, hf_geonw_gxc_distanceb, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + ti = proto_tree_add_item_ret_uint(geonw_sh_tree, hf_geonw_gxc_angle, tvb, offset, 2, ENC_BIG_ENDIAN, &tmp_val); + if (tmp_val > 360) { + expert_add_info_format(pinfo, ti, &ei_geonw_out_of_range, "Out of range [0..360] (%f)", (float)tmp_val); + } + offset += 2; + } + ti = proto_tree_add_item_ret_uint(geonw_sh_tree, hf_geonw_gxc_reserved, tvb, offset, 2, ENC_BIG_ENDIAN, &reserved); + if (reserved) { + expert_add_info(pinfo, ti, &ei_geonw_nz_reserved); + } + offset += 2; + break; + case HTST_LS_REQUEST: + // GN_ADDR + ti = proto_tree_add_item(geonw_sh_tree, hf_geonw_lsrq_addr, tvb, offset, 8, ENC_NA); + geonw_de_add_tree = proto_item_add_subtree(ti, ett_geonw_lsrq_add); + set_address_tvb(&pinfo->net_dst, geonw_address_type, 8, tvb, offset); + copy_address_shallow(&pinfo->dst, &pinfo->net_dst); + + proto_tree_add_item(geonw_de_add_tree, hf_geonw_lsrq_addr_manual, tvb, offset, 1, ENC_BIG_ENDIAN); + proto_tree_add_item(geonw_de_add_tree, hf_geonw_lsrq_addr_type, tvb, offset, 1, ENC_BIG_ENDIAN); + ti = proto_tree_add_item_ret_uint(geonw_de_add_tree, hf_geonw_lsrq_addr_country, tvb, offset, 2, ENC_BIG_ENDIAN, &reserved); + if (reserved > 999) { + expert_add_info(pinfo, ti, &ei_geonw_scc_too_big); + } + offset += 2; + proto_tree_add_item(geonw_de_add_tree, hf_geonw_lsrq_addr_mid, tvb, offset, 6, ENC_NA); + offset += 6; + transaction_start(pinfo, geonw_tree); + break; + //case HTST_BEACON: + //case HTST_TSB_MULT: + } + + tap_queue_packet(geonw_tap, pinfo, geonwh); + + if (payload_len) { + // TODO expert info if payload_len different from remaining + tvbuff_t *next_tvb = tvb_new_subset_length(tvb, offset, payload_len); + switch(ch_next_header) { + case CH_NH_BTP_A: + call_dissector(btpa_handle, next_tvb, pinfo, tree); + break; + case CH_NH_BTP_B: + call_dissector(btpb_handle, next_tvb, pinfo, tree); + break; + case CH_NH_IPV6: + call_dissector(ipv6_handle, next_tvb, pinfo, tree); + break; + default: + if (!dissector_try_uint(geonw_subdissector_table, ch_next_header, next_tvb, pinfo, tree)) { + call_data_dissector(next_tvb, pinfo, tree); + } + } + } + + } + + return tvb_captured_length(tvb); +} + +/* + * Decode_as + */ +static void +btpa_src_prompt(packet_info *pinfo _U_, gchar* result) +{ + guint32 port = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, hf_btpa_srcport, pinfo->curr_layer_num)); + + g_snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "source (%u%s)", port, UTF8_RIGHTWARDS_ARROW); +} + +static gpointer +btpa_src_value(packet_info *pinfo _U_) +{ + return p_get_proto_data(pinfo->pool, pinfo, hf_btpa_srcport, pinfo->curr_layer_num); +} + +static void +btpa_dst_prompt(packet_info *pinfo, gchar *result) +{ + guint32 port = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, hf_btpa_dstport, pinfo->curr_layer_num)); + + g_snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "destination (%s%u)", UTF8_RIGHTWARDS_ARROW, port); +} + +static gpointer +btpa_dst_value(packet_info *pinfo) +{ + return p_get_proto_data(pinfo->pool, pinfo, hf_btpa_dstport, pinfo->curr_layer_num); +} + +static void +btpa_both_prompt(packet_info *pinfo, gchar *result) +{ + guint32 srcport = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, hf_btpa_srcport, pinfo->curr_layer_num)), + destport = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, hf_btpa_dstport, pinfo->curr_layer_num)); + g_snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "both (%u%s%u)", srcport, UTF8_LEFT_RIGHT_ARROW, destport); +} + +static void +btpb_dst_prompt(packet_info *pinfo, gchar *result) +{ + guint32 port = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, hf_btpb_dstport, pinfo->curr_layer_num)); + + g_snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "destination (%s%u)", UTF8_RIGHTWARDS_ARROW, port); +} + +static gpointer +btpb_dst_value(packet_info *pinfo) +{ + return p_get_proto_data(pinfo->pool, pinfo, hf_btpb_dstport, pinfo->curr_layer_num); +} + +/* + * Register + */ +void +proto_register_btpa(void) +{ + static hf_register_info hf_btpa[] = { + // BTP A + { &hf_btpa_dstport, + { "Destination Port", "btpa.dstport", + FT_UINT16, BASE_PT_UDP, NULL, 0x0, + NULL, HFILL }}, + + { &hf_btpa_srcport, + { "Source Port", "btpa.srcport", + FT_UINT16, BASE_PT_UDP, NULL, 0x0, + NULL, HFILL }}, + + { &hf_btpa_port, + { "Port", "btpa.port", + FT_UINT16, BASE_PT_UDP, NULL, 0x0, + NULL, HFILL }}, + + }; + static gint *ett[] = { + &ett_btpa, + }; + proto_btpa = proto_register_protocol("BTP-A", "BTPA", "btpa"); + btpa_handle = register_dissector("btpa", dissect_btpa, proto_btpa); + proto_register_field_array(proto_btpa, hf_btpa, array_length(hf_btpa)); + + proto_register_subtree_array(ett, array_length(ett)); + + // Register subdissector table + btpa_subdissector_table = register_dissector_table("btpa.port", + "BTP-A port", proto_btpa, FT_UINT16, BASE_HEX); + + btpa_heur_subdissector_list = register_heur_dissector_list("btpa.payload", proto_btpa); + + // Decode as + static build_valid_func btpa_da_src_values[1] = {btpa_src_value}; + static build_valid_func btpa_da_dst_values[1] = {btpa_dst_value}; + static build_valid_func btpa_da_both_values[2] = {btpa_src_value, btpa_dst_value}; + static decode_as_value_t btpa_da_values[3] = {{btpa_src_prompt, 1, btpa_da_src_values}, {btpa_dst_prompt, 1, btpa_da_dst_values}, {btpa_both_prompt, 2, btpa_da_both_values}}; + static decode_as_t btpa_da = {"btpa", "Transport", "btpa.port", 3, 2, btpa_da_values, "BTP-A", "port(s) as", + decode_as_default_populate_list, decode_as_default_reset, decode_as_default_change, NULL}; + + register_decode_as(&btpa_da); +} + +void +proto_reg_handoff_btpa(void) +{ + dissector_handle_t btpa_handle_; + + btpa_handle_ = create_dissector_handle(dissect_btpa, proto_btpa); + dissector_add_uint("geonw.ch.nh", 1, btpa_handle_); + + find_dissector_add_dependency("gnw", proto_btpa); + + btpa_tap = register_tap("btpa"); + btpa_follow_tap = register_tap("btpa_follow"); +} + +void +proto_register_btpb(void) +{ + static hf_register_info hf_btpb[] = { + // BTP B + { &hf_btpb_dstport, + { "Destination Port", "btpb.dstport", + FT_UINT16, BASE_PT_UDP, NULL, 0x0, + NULL, HFILL }}, + + { &hf_btpb_dstport_info, + { "Destination Port info", "btpb.dstportinf", + FT_UINT16, BASE_HEX, NULL, 0x0, + NULL, HFILL }}, + + }; + static gint *ett[] = { + &ett_btpb, + }; + proto_btpb = proto_register_protocol("BTP-B", "BTPB", "btpb"); + btpb_handle = register_dissector("btpb", dissect_btpb, proto_btpb); + proto_register_field_array(proto_btpb, hf_btpb, array_length(hf_btpb)); + + proto_register_subtree_array(ett, array_length(ett)); + + // Register subdissector table + btpb_subdissector_table = register_dissector_table("btpb.port", + "BTP-B dst port", proto_btpb, FT_UINT16, BASE_HEX); + + btpb_heur_subdissector_list = register_heur_dissector_list("btpb.payload", proto_btpb); + + // Decode as + static build_valid_func btpb_da_build_value[1] = {btpb_dst_value}; + static decode_as_value_t btpb_da_values = {btpb_dst_prompt, 1, btpb_da_build_value}; + static decode_as_t btpb_da = {"btpb", "BTP-B dest. port", "btpb.port", 1, 0, &btpb_da_values, NULL, NULL, + decode_as_default_populate_list, decode_as_default_reset, decode_as_default_change, NULL}; + + register_decode_as(&btpb_da); +} + +void +proto_reg_handoff_btpb(void) +{ + dissector_handle_t btpb_handle_; + + btpb_handle_ = create_dissector_handle(dissect_btpb, proto_btpb); + dissector_add_uint("geonw.ch.nh", 2, btpb_handle_); + + find_dissector_add_dependency("gnw", proto_btpb); + + btpb_tap = register_tap("btpb"); + btpb_follow_tap = register_tap("btpb_follow"); +} + +// Display functions +static void +display_latitude( gchar *result, gint32 hexver ) +{ + g_snprintf( result, ITEM_LABEL_LENGTH, "%ud%u'%.2f\"%c", + abs(hexver)/10000000, + abs(hexver%10000000)*6/1000000, + abs(hexver*6%1000000)*6./100000., + hexver>=0?'N':'S'); +} + +static void +display_longitude( gchar *result, gint32 hexver ) +{ + g_snprintf( result, ITEM_LABEL_LENGTH, "%ud%u'%.2f\"%c", + abs(hexver)/10000000, + abs(hexver%10000000)*6/1000000, + abs(hexver*6%1000000)*6./100000., + hexver>=0?'E':'W'); +} + +static void +display_speed( gchar *result, gint32 hexver ) +{ + g_snprintf( result, ITEM_LABEL_LENGTH, "%.2f m/s", abs(hexver)/100.); +} + +static void +display_heading( gchar *result, guint32 hexver ) +{ + g_snprintf( result, ITEM_LABEL_LENGTH, "%.1f degrees", hexver/10.); +} + +void +proto_register_geonw(void) +{ + static const value_string bh_next_header_names[] = { + { 1, "Common Header" }, + { 2, "Secured Packet" }, + { 0, NULL} + }; + + static const value_string bh_lt_base_names[] = { + { 0, "50 ms" }, + { 1, "1 s" }, + { 2, "10 s" }, + { 3, "100 s"}, + { 0, NULL} + }; + + static const value_string ch_next_header_names[] = { + { CH_NH_BTP_A, "BTP-A Transport protocol" }, + { CH_NH_BTP_B, "BTP-B Transport protocol" }, + { CH_NH_IPV6, "IPv6 header" }, + { 0, NULL} + }; + + static const value_string itss_type_names[] = { + { 0, "Unknown" }, + { 1, "Pedestrian" }, + { 2, "Cyclist" }, + { 3, "Moped" }, + { 4, "Motorcycle" }, + { 5, "Passenger Car" }, + { 6, "Bus" }, + { 7, "Light Truck" }, + { 8, "Heavy Truck" }, + { 9, "Trailer" }, + { 10, "Special Vehicle" }, + { 11, "Tram" }, + { 15, "Road Side Unit" }, + { 0, NULL} + }; + + static hf_register_info hf_geonw[] = { + + { &hf_geonw_bh, + { "Basic Header", "geonw.bh", FT_NONE, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_geonw_bh_version, + { "Version", "geonw.bh.version", + FT_UINT8, BASE_DEC, NULL, 0xF0, + NULL, HFILL }}, + + { &hf_geonw_bh_reserved, + { "Reserved", "geonw.bh.reserved", FT_UINT8, + BASE_HEX, NULL, 0x0, "It SHOULD be set to 0", HFILL }}, + + { &hf_geonw_bh_next_header, + { "Next Header", "geonw.bh.nh", + FT_UINT8, BASE_DEC, VALS(bh_next_header_names), 0x0F, + NULL, HFILL }}, + + { &hf_geonw_bh_life_time, + { "Life Time", "geonw.bh.lt", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL }}, + + { &hf_geonw_bh_lt_mult, + { "Life Time multiplier", "geonw.bh.lt.mult", + FT_UINT8, BASE_DEC, NULL, 0xFC, + NULL, HFILL }}, + + { &hf_geonw_bh_lt_base, + { "Life Time base", "geonw.bh.lt.base", + FT_UINT8, BASE_DEC, VALS(bh_lt_base_names), 0x03, + NULL, HFILL }}, + + { &hf_geonw_bh_remain_hop_limit, + { "Remaining Hop Limit", "geonw.bh.rhl", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL }}, + + { &hf_geonw_ch, + { "Common Header", "geonw.ch", FT_NONE, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_geonw_ch_next_header, + { "Next Header", "geonw.ch.nh", + FT_UINT8, BASE_DEC, VALS(ch_next_header_names), 0xF0, + NULL, HFILL }}, + + { &hf_geonw_ch_reserved1, + { "Reserved", "geonw.ch.reserved1", FT_UINT8, + BASE_HEX, NULL, 0x0F, "It SHOULD be set to 0", HFILL }}, + + { &hf_geonw_ch_header_type, + { "Header type", "geonw.ch.htype", + FT_UINT8, BASE_HEX, VALS(ch_header_type_names), 0x00, + NULL, HFILL }}, + + { &hf_geonw_ch_traffic_class, + { "Traffic class", "geonw.ch.tclass", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL }}, + + { &hf_geonw_ch_tc_scf, + { "Store Carry Forward", "geonw.ch.tc.buffer", + FT_UINT8, BASE_DEC, NULL, 0x80, + NULL, HFILL }}, + + { &hf_geonw_ch_tc_offload, + { "Channel offload", "geonw.ch.tc.offload", + FT_UINT8, BASE_DEC, NULL, 0x40, + NULL, HFILL }}, + + { &hf_geonw_ch_tc_id, + { "Mobility", "geonw.ch.tc.id", + FT_UINT8, BASE_DEC, NULL, 0x3F, + NULL, HFILL }}, + + { &hf_geonw_ch_flags, + { "Flags", "geonw.ch.flags", FT_NONE, + BASE_NONE, NULL, 0x0, NULL, HFILL }}, + + { &hf_geonw_ch_flags_mob, + { "Mobility flag", "geonw.ch.flags.mob", + FT_UINT8, BASE_DEC, NULL, 0x80, + NULL, HFILL }}, + + { &hf_geonw_ch_flags_reserved, + { "Reserved", "geonw.ch.flags.reserved", + FT_UINT8, BASE_DEC, NULL, 0x7F, + NULL, HFILL }}, + + { &hf_geonw_ch_payload_length, + { "Payload length", "geonw.ch.plength", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL }}, + + { &hf_geonw_ch_max_hop_limit, + { "Maximum Hop Limit", "geonw.ch.mhl", + FT_UINT8, BASE_DEC, NULL, 0x00, + NULL, HFILL }}, + + { &hf_geonw_ch_reserved2, + { "Reserved", "geonw.ch.reserved2", FT_UINT8, + BASE_HEX, NULL, 0x00, "It SHOULD be set to 0", HFILL }}, + + { &hf_geonw_seq_num, + { "Sequence number", "geonw.seq_num", + FT_UINT16, BASE_HEX, NULL, 0x00, + NULL, HFILL }}, + + { &hf_geonw_reserved, + { "Reserved", "geonw.reserved", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL }}, + + // Long Position + { &hf_geonw_so_pv, + { "Source position", "geonw.src_pos", + FT_BYTES, BASE_NONE, NULL, 0x00, + NULL, HFILL }}, + + { &hf_geonw_so_pv_addr, + { "GN_ADDR", "geonw.src_pos.addr", + FT_BYTES, BASE_NONE, NULL, 0x00, + NULL, HFILL }}, + + { &hf_geonw_so_pv_addr_manual, + { "Manual", "geonw.src_pos.addr.manual", + FT_UINT8, BASE_DEC, NULL, 0x80, + NULL, HFILL }}, + + { &hf_geonw_so_pv_addr_type, + { "ITS-S type", "geonw.src_pos.addr.type", + FT_UINT8, BASE_DEC, VALS(itss_type_names), 0x7C, + NULL, HFILL }}, + + { &hf_geonw_so_pv_addr_country, + { "ITS-S Country Code", "geonw.src_pos.addr.country", + FT_UINT16, BASE_DEC, VALS(E164_country_code_value), 0x03FF, + NULL, HFILL }}, + + { &hf_geonw_so_pv_addr_mid, + { "MID", "geonw.src_pos.addr.mid", + FT_ETHER, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_geonw_so_pv_time, + { "Timestamp", "geonw.src_pos.tst", + FT_UINT32, BASE_DEC|BASE_UNIT_STRING, &units_milliseconds, 0x00, + NULL, HFILL }}, + + { &hf_geonw_so_pv_lat, + { "Latitude", "geonw.src_pos.lat", + FT_INT32, BASE_CUSTOM, CF_FUNC(display_latitude), 0x00, + NULL, HFILL }}, + + { &hf_geonw_so_pv_lon, + { "Longitude", "geonw.src_pos.long", + FT_INT32, BASE_CUSTOM, CF_FUNC(display_longitude), 0x00, + NULL, HFILL }}, + + { &hf_geonw_so_pv_pai, + { "Position accuracy indicator", "geonw.src_pos.pai", + FT_UINT8, BASE_DEC, NULL, 0x80, + NULL, HFILL }}, + + { &hf_geonw_so_pv_speed, + { "Speed", "geonw.src_pos.speed", + FT_INT16, BASE_CUSTOM, CF_FUNC(display_speed), 0x00, + NULL, HFILL }}, + + { &hf_geonw_so_pv_heading, + { "Heading", "geonw.src_pos.hdg", + FT_UINT16, BASE_CUSTOM, CF_FUNC(display_heading), 0x00, + NULL, HFILL }}, + + // Short Position + { &hf_geonw_de_pv, + { "Destination position", "geonw.dst_pos", + FT_BYTES, BASE_NONE, NULL, 0x00, + NULL, HFILL }}, + + { &hf_geonw_de_pv_addr, + { "GN_ADDR", "geonw.dst_pos.addr", + FT_BYTES, BASE_NONE, NULL, 0x00, + NULL, HFILL }}, + + { &hf_geonw_de_pv_addr_manual, + { "Manual", "geonw.dst_pos.addr.manual", + FT_UINT8, BASE_DEC, NULL, 0x80, + NULL, HFILL }}, + + { &hf_geonw_de_pv_addr_type, + { "ITS-S type", "geonw.dst_pos.addr.type", + FT_UINT8, BASE_DEC, VALS(itss_type_names), 0x7C, + NULL, HFILL }}, + + { &hf_geonw_de_pv_addr_country, + { "ITS-S Country Code", "geonw.dst_pos.addr.country", + FT_UINT16, BASE_DEC, VALS(E164_country_code_value), 0x03FF, + NULL, HFILL }}, + + { &hf_geonw_de_pv_addr_mid, + { "MID", "geonw.dst_pos.addr.mid", + FT_ETHER, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_geonw_de_pv_time, + { "Timestamp", "geonw.dst_pos.tst", + FT_UINT32, BASE_DEC|BASE_UNIT_STRING, &units_milliseconds, 0x00, + NULL, HFILL }}, + + { &hf_geonw_de_pv_lat, + { "Latitude", "geonw.dst_pos.lat", + FT_INT32, BASE_CUSTOM, CF_FUNC(display_latitude), 0x00, + NULL, HFILL }}, + + { &hf_geonw_de_pv_lon, + { "Longitude", "geonw.dst_pos.long", + FT_INT32, BASE_CUSTOM, CF_FUNC(display_longitude), 0x00, + NULL, HFILL }}, + + // GBC/GAC + { &hf_geonw_gxc_latitude, + { "Latitude", "geonw.gxc.latitude", + FT_INT32, BASE_CUSTOM, CF_FUNC(display_latitude), 0x00, + NULL, HFILL }}, + + { &hf_geonw_gxc_longitude, + { "Longitude", "geonw.gxc.longitude", + FT_INT32, BASE_CUSTOM, CF_FUNC(display_longitude), 0x00, + NULL, HFILL }}, + + { &hf_geonw_gxc_radius, + { "Radius r", "geonw.gxc.radius", + FT_UINT16, BASE_DEC|BASE_UNIT_STRING, &units_meters, 0x00, + NULL, HFILL }}, + + { &hf_geonw_gxc_distancea, + { "Distance a", "geonw.gxc.distancea", + FT_UINT16, BASE_DEC|BASE_UNIT_STRING, &units_meters, 0x00, + NULL, HFILL }}, + + { &hf_geonw_gxc_distanceb, + { "Distance b", "geonw.gxc.distanceb", + FT_UINT16, BASE_DEC|BASE_UNIT_STRING, &units_meters, 0x00, + NULL, HFILL }}, + + { &hf_geonw_gxc_angle, + { "Angle", "geonw.gxc.angle", + FT_UINT16, BASE_DEC|BASE_UNIT_STRING, &units_degree_degrees, 0x00, + NULL, HFILL }}, + + { &hf_geonw_gxc_reserved, + { "Reserved", "geonw.gxc.reserved", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL }}, + + // SHB + { &hf_geonw_shb_reserved, + { "Reserved", "geonw.shb.reserved", + FT_UINT32, BASE_DEC, NULL, 0x00, + NULL, HFILL }}, + + // LS Request + { &hf_geonw_lsrq_addr, + { "GN_ADDR", "geonw.ls_req.addr", + FT_BYTES, BASE_NONE, NULL, 0x00, + NULL, HFILL }}, + + { &hf_geonw_lsrq_addr_manual, + { "Manual", "geonw.ls_req.addr.manual", + FT_UINT8, BASE_DEC, NULL, 0x80, + NULL, HFILL }}, + + { &hf_geonw_lsrq_addr_type, + { "ITS-S type", "geonw.ls_req.addr.type", + FT_UINT8, BASE_DEC, VALS(itss_type_names), 0x7C, + NULL, HFILL }}, + + { &hf_geonw_lsrq_addr_country, + { "ITS-S Country Code", "geonw.ls_req.addr.country", + FT_UINT16, BASE_DEC, VALS(E164_country_code_value), 0x03FF, + NULL, HFILL }}, + + { &hf_geonw_lsrq_addr_mid, + { "MID", "geonw.ls_req.addr.mid", + FT_ETHER, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + + { &hf_geonw_beacon, + { "Beacon Packet", "geonw.beacon", FT_NONE, + BASE_NONE, NULL, 0x0, NULL, HFILL }}, + + { &hf_geonw_guc, + { "GeoUniCast Packet", "geonw.guc", FT_NONE, + BASE_NONE, NULL, 0x0, NULL, HFILL }}, + + { &hf_geonw_gac, + { "GeoAnyCast Packet", "geonw.gac", FT_NONE, + BASE_NONE, NULL, 0x0, NULL, HFILL }}, + + { &hf_geonw_gbc, + { "GeoBroadCast Packet", "geonw.gbc", FT_NONE, + BASE_NONE, NULL, 0x0, NULL, HFILL }}, + + { &hf_geonw_tsb, + { "Topologically-Scoped Broadcast Packet", "geonw.gbc", FT_NONE, + BASE_NONE, NULL, 0x0, NULL, HFILL }}, + + { &hf_geonw_ls, + { "Location Service Packet", "geonw.ls", FT_NONE, + BASE_NONE, NULL, 0x0, NULL, HFILL }}, + + { &hf_geonw_resp_in, + { "Response frame", "geonw.resp_in", FT_FRAMENUM, BASE_NONE, + FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0, + "The frame number of the corresponding response", + HFILL}}, + + { &hf_geonw_no_resp, + { "No response seen", "geonw.no_resp", FT_NONE, BASE_NONE, + NULL, 0x0, + "No corresponding response frame was seen", + HFILL}}, + + { &hf_geonw_resp_to, + { "Request frame", "geonw.resp_to", FT_FRAMENUM, BASE_NONE, + FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0, + "The frame number of the corresponding request", HFILL}}, + + { &hf_geonw_resptime, + { "Response time", "geonw.resptime", FT_DOUBLE, BASE_NONE, + NULL, 0x0, + "The time between the request and the response, in ms.", + HFILL}}, + + { &hf_geonw_analysis_flags, + { "GeoNetworking Analysis Flags", "geonw.analysis.flags", FT_NONE, BASE_NONE, NULL, 0x0, + "This frame has some of the GeoNetworking analysis flags set", HFILL }}, + + }; + static ei_register_info ei[] = { + { &ei_geonw_nz_reserved, { "geonw.reserved_not_zero", PI_PROTOCOL, PI_WARN, "Incorrect, should be 0", EXPFILL }}, + { &ei_geonw_version_err, { "geonw.bogus_version", PI_MALFORMED, PI_ERROR, "Bogus GeoNetworking Version", EXPFILL }}, + { &ei_geonw_rhl_lncb, { "geonw.rhl.lncb", PI_SEQUENCE, PI_NOTE, "Remaining Hop Limit", EXPFILL }}, + { &ei_geonw_rhl_too_low, { "geonw.rhl.too_small", PI_SEQUENCE, PI_NOTE, "Remaining Hop Limit", EXPFILL }}, + { &ei_geonw_mhl_lt_rhl, { "geonw.rhl.ht_mhl", PI_SEQUENCE, PI_WARN, "Remaining Hop Limit To Live", EXPFILL }}, + { &ei_geonw_scc_too_big, { "geonw.scc_too_big", PI_MALFORMED, PI_ERROR, "Country code should be less than 1000", EXPFILL }}, + { &ei_geonw_analysis_duplicate, { "geonw.analysis_duplicate", PI_SEQUENCE, PI_NOTE, "Duplicate packet", EXPFILL }}, + { &ei_geonw_resp_not_found, { "geonw.resp_not_found", PI_SEQUENCE, PI_WARN, "Response not found", EXPFILL }}, + { &ei_geonw_out_of_range, { "geonw.position_oor", PI_MALFORMED, PI_WARN, "Position out of range", EXPFILL }}, + { &ei_geonw_payload_len, { "geonw.bogus_geonw_length", PI_PROTOCOL, PI_ERROR, "Bogus GeoNetworking length", EXPFILL }}, + }; + static gint *ett[] = { + &ett_geonw, + &ett_geonw_bh, + &ett_geonw_bh_lt, + &ett_geonw_ch, + &ett_geonw_ch_tc, + &ett_geonw_sh, + &ett_geonw_so, + &ett_geonw_so_add, + &ett_geonw_de, + &ett_geonw_de_add, + &ett_geonw_lsrq_add, + &ett_geonw_analysis, + }; + + expert_module_t* expert_geonw; + module_t *geonw_module; + + proto_geonw = proto_register_protocol("GeoNetworking", "GNW", "gnw"); + + + geonw_handle = register_dissector("gnw", dissect_geonw, proto_geonw); + + proto_register_field_array(proto_geonw, hf_geonw, array_length(hf_geonw)); + proto_register_subtree_array(ett, array_length(ett)); + + expert_geonw = expert_register_protocol(proto_geonw); + expert_register_field_array(expert_geonw, ei, array_length(ei)); + + geonw_subdissector_table = register_dissector_table("geonw.ch.nh", + "GeoNetworking Next Header", proto_geonw, FT_UINT8, BASE_HEX); + + geonw_address_type = address_type_dissector_register("AT_GEONW", "GeoNetworking address", geonw_to_str, geonw_str_len, NULL, geonw_col_filter_str, geonw_len, geonw_name_resolution_str, geonw_name_resolution_len); + + /* Register configuration preferences */ + geonw_module = prefs_register_protocol(proto_geonw, NULL); + prefs_register_bool_preference(geonw_module, "analyze_sequence_numbers", + "Analyze GeoNetworking sequence numbers", + "Make the GeoNetworking dissector analyze GeoNetworking sequence numbers to find and flag duplicate packet (Annex A)", + &geonw_analyze_seq); + + geonw_hashtable = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), geonw_addr_hash, geonw_addr_cmp); +} + +void +proto_reg_handoff_geonw(void) +{ + dissector_handle_t geonw_handle_; + + geonw_handle_ = create_dissector_handle(dissect_geonw, proto_geonw); + + dissector_add_uint_with_preference("ethertype", ETHERTYPE_GEONETWORKING, geonw_handle_); + + // IPv6 over GeoNetworking Protocols + ipv6_handle = find_dissector("ipv6"); + dissector_add_uint("geonw.ch.nh", 3, ipv6_handle); + + geonw_tap = register_tap("geonw"); +} + +/* + * Editor modelines - https://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: + */ diff --git a/epan/dissectors/packet-geonw.h b/epan/dissectors/packet-geonw.h new file mode 100644 index 0000000000..d5242f4c88 --- /dev/null +++ b/epan/dissectors/packet-geonw.h @@ -0,0 +1,66 @@ +/* packet-geonw.h + * Routines for GeoNetworking and BTP-A/B dissection + * Coyright 2018, C. Guerber + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + +#ifndef __PACKET_GEONW_H__ +#define __PACKET_GEONW_H__ + +#define SN_MAX 0xffff + +typedef struct geonwheader +{ + guint8 gnw_ver; /* Version */ + guint8 gnw_lt; /* Life time */ + guint8 gnw_rhl; /* Remaining Hop Limit */ + guint8 gnw_proto; /* Next header */ + guint8 gnw_htype; /* Header type */ + guint8 gnw_tc; /* Traffic class */ + guint8 gnw_flags; /* Flags */ + guint8 gnw_mhl; /* Remaining Hop Limit */ + guint16 gnw_len; /* Payload length */ + guint32 gnw_sn; /* Sequence number or MAX+1 */ + guint32 gnw_tst; /* Sequence number or MAX+1 */ + address gnw_src; /* source address */ + address gnw_dst; /* destination address */ + gint32 gnw_lat; /* Latitude */ + gint32 gnw_lon; /* Longitude */ +} geonwheader; + +typedef struct btpaheader +{ + address gnw_src; /* source address */ + address gnw_dst; /* destination address */ + guint16 btp_psrc; /* Source port */ + guint16 btp_pdst; /* Destination port */ +} btpaheader; + +typedef struct btpbheader +{ + address gnw_src; /* source address */ + address gnw_dst; /* destination address */ + guint16 btp_pdst; /* Destination port */ + guint16 btp_idst; /* Destination info */ +} btpbheader; + +#endif /* __PACKET_GEONW_H__ */ + +/* + * 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: + */