/* 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) * * ETSI TS 103 097 v1.1.1, v1.2.1 and v1.3.1 * Intelligent Transport Systems (ITS); Security; * Security header and certificate formats * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "packet-e164.h" #include "packet-ieee1609dot2.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 HT_GET(ht) ((ht)&HT_MASK) #define HST_GET(ht) ((ht)&HST_MASK) #define IS_HT_KNOWN(ht) ((ht) <= 0x61) && (ht >= 0x10) && (HST_GET(ht) < 3) && ((HST_GET(ht) == 0) || ((HT_GET(ht) > 0x30) && ((HST_GET(ht) == 1) || ((HST_GET(ht) == 2) && (HT_GET(ht) < 0x43))))) #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 #define SEC_TVB_KEY 0 /* * Variables */ static wmem_map_t *geonw_hashtable; static int proto_geonw; static int proto_btpa; static int proto_btpb; static int geonw_tap; static int btpa_tap; static int btpa_follow_tap; static int btpb_tap; static int btpb_follow_tap; static int hf_geonw_bh; static int hf_geonw_bh_version; static int hf_geonw_bh_next_header; static int hf_geonw_bh_reserved; static int hf_geonw_bh_life_time; static int hf_geonw_bh_lt_mult; static int hf_geonw_bh_lt_base; static int hf_geonw_bh_remain_hop_limit; static int hf_geonw_ch; static int hf_geonw_ch_next_header; static int hf_geonw_ch_reserved1; static int hf_geonw_ch_header_type; //static int hf_geonw_ch_header_subtype; static int hf_geonw_ch_traffic_class; static int hf_geonw_ch_tc_scf; static int hf_geonw_ch_tc_offload; static int hf_geonw_ch_tc_id; static int hf_geonw_ch_flags; static int hf_geonw_ch_flags_mob; static int hf_geonw_ch_flags_reserved; static int hf_geonw_ch_payload_length; static int hf_geonw_ch_max_hop_limit; static int hf_geonw_ch_reserved2; static int hf_geonw_seq_num; static int hf_geonw_reserved; static int hf_geonw_so_pv; static int hf_geonw_so_pv_addr; static int hf_geonw_so_pv_addr_manual; static int hf_geonw_so_pv_addr_type; static int hf_geonw_so_pv_addr_country; static int hf_geonw_so_pv_addr_mid; static int hf_geonw_so_pv_time; static int hf_geonw_so_pv_lat; static int hf_geonw_so_pv_lon; static int hf_geonw_so_pv_pai; static int hf_geonw_so_pv_speed; static int hf_geonw_so_pv_heading; static int hf_geonw_de_pv; static int hf_geonw_de_pv_addr; static int hf_geonw_de_pv_addr_manual; static int hf_geonw_de_pv_addr_type; static int hf_geonw_de_pv_addr_country; static int hf_geonw_de_pv_addr_mid; static int hf_geonw_de_pv_time; static int hf_geonw_de_pv_lat; static int hf_geonw_de_pv_lon; static int hf_geonw_gxc_latitude; static int hf_geonw_gxc_longitude; static int hf_geonw_gxc_radius; static int hf_geonw_gxc_distancea; static int hf_geonw_gxc_distanceb; static int hf_geonw_gxc_angle; static int hf_geonw_gxc_reserved; static int hf_geonw_shb_reserved; static int hf_geonw_lsrq_addr; static int hf_geonw_lsrq_addr_manual; static int hf_geonw_lsrq_addr_type; static int hf_geonw_lsrq_addr_country; static int hf_geonw_lsrq_addr_mid; static int hf_geonw_beacon; static int hf_geonw_guc; static int hf_geonw_gac; static int hf_geonw_gbc; static int hf_geonw_tsb; static int hf_geonw_ls; static int hf_geonw_analysis_flags; static int hf_btpa_dstport; static int hf_btpa_srcport; static int hf_btpa_port; static int hf_btpb_dstport; static int hf_btpb_dstport_info; static int hf_geonw_resp_in; static int hf_geonw_resp_to; static int hf_geonw_no_resp; static int hf_geonw_resptime; static int hf_geonw_dccmco; static int hf_geonw_dccmco_cbr_l_0_hop; static int hf_geonw_dccmco_cbr_l_1_hop; static int hf_geonw_dccmco_output_power; static int hf_geonw_dccmco_reserved; static int ett_geonw; static int ett_geonw_bh; static int ett_geonw_bh_lt; static int ett_geonw_ch; static int ett_geonw_ch_tc; static int ett_geonw_sh; static int ett_geonw_so; static int ett_geonw_so_add; static int ett_geonw_de; static int ett_geonw_de_add; static int ett_geonw_lsrq_add; static int ett_geonw_analysis; static int ett_geonw_dccmco; static int ett_btpa; static int ett_btpb; static int geonw_address_type = -1; static expert_field ei_geonw_nz_reserved; static expert_field ei_geonw_version_err; static expert_field ei_geonw_rhl_lncb; static expert_field ei_geonw_rhl_too_low; static expert_field ei_geonw_mhl_lt_rhl; static expert_field ei_geonw_scc_too_big; static expert_field ei_geonw_analysis_duplicate; static expert_field ei_geonw_resp_not_found; static expert_field ei_geonw_out_of_range; static expert_field ei_geonw_payload_len; static expert_field ei_geonw_intx_too_big; static dissector_table_t geonw_subdissector_table; static dissector_table_t ssp_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, "Topologically-scoped broadcast Single-hop broadcast (SHB)" }, { HTST_TSB_MULT, "Topologically-scoped broadcast Multi-hop broadcast (TSB)" }, { HTST_LS_REQUEST, "Location Service Request" }, { HTST_LS_REPLY, "Location Service Reply" }, { 0x00, NULL} }; static dissector_handle_t ieee1609dot2_handle; 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(pinfo->pool, 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)) tap_queue_packet(btpa_follow_tap, pinfo, next_tvb); 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; uint32_t dst_port; uint32_t dst_info; struct btpbheader *btpbh; btpbh = wmem_new0(pinfo->pool, 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; col_append_fstr(pinfo->cinfo, COL_INFO, " " UTF8_RIGHTWARDS_ARROW " %u", dst_port); 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)) tap_queue_packet(btpb_follow_tap, pinfo, next_tvb); 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(btpb_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 { uint32_t rqst_frame; uint32_t 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; static const char * get_geonw_name(const uint8_t *addr); static const char* geonw_name_resolution_str(const address* addr); static 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 bool geonw_analyze_seq = true; /* * GeoNetworking Address Type */ /* Adapter from ethernet and ipv4 Address Type code */ struct hashgeonw; typedef struct hashgeonw hashgeonw_t; struct hashgeonw { unsigned status; uint8_t addr[8]; char hexaddr[28]; char resolved_name[MAXNAMELEN]; // Node follow up used for duplication detection uint32_t timestamp; uint32_t 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 uint8_t* addrdata, char *buf, int buf_len _U_) { address eth_addr; // Initial or Manual if (addrdata[0] & 0x80) *buf++ = '1'; else *buf++ = '0'; *buf++ = '.'; // Station Type uint32_to_str_buf((addrdata[0] & 0x7C) >> 2, buf, 26); buf += (unsigned) strlen(buf); *buf++ = '.'; // Country Code uint32_to_str_buf(((uint32_t)(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, char *buf, int buf_len _U_) { return _geonw_to_str((const uint8_t *)addr->data, buf, buf_len); } static const char* geonw_col_filter_str(const address* addr _U_, bool is_src) { if (is_src) return "geonw.src_pos.addr"; return "geonw.dst_pos.addr"; } static int geonw_len(void) { return 8; } static unsigned geonw_addr_hash(const void *key) { return wmem_strong_hash((const uint8_t *)key, 8); } static gboolean geonw_addr_cmp(const void *a, const void *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 uint8_t *addr = tp->addr; uint16_t val; char *rname = tp->resolved_name; address eth_addr; uint8_t 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) { uint32_to_str_buf(val, rname, MAXNAMELEN-2); l1 = (uint8_t) strlen(rname); } else { l1 = (uint8_t) g_strlcpy(rname, string, MAXNAMELEN-2); } rname += l1; *rname++ = '.'; // Country Code val = ((uint32_t)(addr[0] & 0x03) << 8) + addr[1]; string = try_val_to_str(val, E164_ISO3166_country_code_short_value); if (string == NULL) { uint32_to_str_buf(val, rname, MAXNAMELEN-12); l2 = (uint8_t) strlen(rname); } else { l2 = (uint8_t) 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); // We could use ether_name_resolution_str: // (void) g_strlcpy(rname, ether_name_resolution_str(ð_addr), MAXNAMELEN-l1-4); tp->status = 1; return tp; } static hashgeonw_t * geonw_hash_new_entry(const uint8_t *addr, bool 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; tp->timestamp = 0; tp->sequence_number = SN_MAX + 1; 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 uint8_t *addr, bool 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 char * get_geonw_name(const uint8_t *addr) { hashgeonw_t *tp; bool resolve = gbl_resolv_flags.network_name; tp = geonw_name_lookup(addr, resolve); return resolve ? tp->resolved_name : tp->hexaddr; } /* get_geonw_name */ const char* geonw_name_resolution_str(const address* addr) { return get_geonw_name((const uint8_t *)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 ... */ 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_conversation_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_conversation_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 */ uint32_t 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_str(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_conversation_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)) { uint32_t 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 */ uint32_t 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 uint32_t timestamp; uint16_t sequence_number; }; /* * Secured geonetworking */ static int hf_geonw_sec; static int hf_sgeonw_version; static int hf_sgeonw_profile; static int hf_sgeonw_hdr; static int hf_sgeonw_pl; static int hf_sgeonw_trl; static int hf_sgeonw_var_len; static int hf_sgeonw_var_len_det; static int hf_sgeonw_var_len_val; static int hf_sgeonw_header_field; static int hf_sgeonw_header_field_type_v1; static int hf_sgeonw_header_field_type_v2; static int hf_sgeonw_opaque; static int hf_sgeonw_payload_field; static int hf_sgeonw_payload_field_type; static int hf_sgeonw_trailer_field; static int hf_sgeonw_trailer_field_type; static int hf_sgeonw_certificate; static int hf_sgeonw_encryption_parameter; static int hf_sgeonw_signature; static int hf_sgeonw_subject_info; static int hf_sgeonw_subject_attribute; static int hf_sgeonw_intx; static int hf_sgeonw_time64; static int hf_sgeonw_conf; static int hf_sgeonw_time32; static int hf_sgeonw_lat; static int hf_sgeonw_lon; static int hf_sgeonw_elev; static int hf_sgeonw_hashedid3; static int hf_sgeonw_hashedid8; static int hf_sgeonw_duration; static int hf_sgeonw_duration_unit; static int hf_sgeonw_duration_value; static int hf_sgeonw_encryption_parameter_nonce; static int hf_sgeonw_msg_id; static int hf_sgeonw_app_id; static int ett_geonw_sec; static int ett_sgeonw_hdr; static int ett_sgeonw_field; static int ett_sgeonw_var_len; static int ett_sgeonw_intx; static int ett_sgeonw_duration; static int ett_sgeonw_encryption_parameter; static int ett_sgeonw_signature; static int ett_sgeonw_subject_info; static int ett_sgeonw_subject_attribute; static int ett_sgeonw_ssp; static expert_field ei_sgeonw_len_unsupported; static expert_field ei_sgeonw_len_too_long; static expert_field ei_sgeonw_subj_info_too_long; static expert_field ei_sgeonw_ssp_too_long; static expert_field ei_sgeonw_bogus; typedef enum { generation_time = 0, generation_time_confidence = 1, expiration = 2, generation_location = 3, request_unrecognized_certificate = 4, message_type = 5, signer_info = 128, recipient_info = 129, encryption_parameters = 130, last_hdr_type = (2<<8)-1 } HeaderFieldType; static const value_string header_field_type_v1_names[] = { { generation_time, "Generation time" }, { generation_time_confidence, "Generation time confidence" }, { expiration, "Expiration" }, { generation_location, "Generation location" }, { request_unrecognized_certificate, "Request unrecognized certificate" }, { message_type, "Message type" }, { signer_info, "Signer info" }, { recipient_info, "Recipient info" }, { encryption_parameters, "Encryption parameters" }, { 0x00, NULL} }; static const value_string header_field_type_v2_names[] = { { generation_time, "Generation time" }, { generation_time_confidence, "Generation time standard deviation" }, { expiration, "Expiration" }, { generation_location, "Generation location" }, { request_unrecognized_certificate, "Request unrecognized certificate" }, { message_type, "ITS Application ID" }, // Change in definition { signer_info, "Signer info" }, { recipient_info, "Encryption parameters" }, // Change in definition { encryption_parameters, "Recipient info" }, // Change in definition { 0x00, NULL} }; typedef enum { unsecured = 0, signed_pl = 1, encrypted = 2, signed_external = 3, signed_and_encrypted = 4, last_pl_type = (2<<8)-1 } PayloadType; static const value_string payload_field_type_names[] = { { unsecured, "Unsecured" }, { signed_pl, "Signed" }, { encrypted, "Encrypted" }, { signed_external, "Signed external" }, { signed_and_encrypted, "Signed and encrypted" }, { 0, NULL }, }; typedef enum { signature = 1, last_trl_type = (2<<8)-1 } TrailerFieldType; static const value_string trailer_field_type_names[] = { { signature, "signature" }, { 0, NULL }, }; static int hf_sgeonw_signer_info; static int hf_sgeonw_signer_info_type; typedef enum { self = 0, certificate_digest_with_ecdsap256 = 1, certificate = 2, certificate_chain = 3, certificate_digest_with_other_algorithm = 4, //reserved(240..255), last_sif_type = (2<<8)-1 } SignerInfoType; static const value_string signer_info_type_names[] = { { self, "Self signed" }, { certificate_digest_with_ecdsap256, "Certificate digest with ecdsap256" }, { certificate, "Certificate" }, { certificate_chain, "Certificate chain" }, { certificate_digest_with_other_algorithm, "Certificate digest with other algorithm" }, { 0, NULL }, }; static int hf_sgeonw_public_key; static int ett_sgeonw_public_key; static int hf_sgeonw_public_key_algorithm; static int hf_sgeonw_ecdsasignature_s; typedef enum { ecdsa_nistp256_with_sha256 = 0, ecies_nistp256 = 1, // reserved(240..255), last_pubkey_algo = (2<<8)-1 } PublicKeyAlgorithm; static const value_string public_key_algorithm_names[] = { { ecdsa_nistp256_with_sha256, "ECDSA nistp256 with SHA256" }, { ecies_nistp256, "ECIES nistp256" }, { 0, NULL }, }; static const int etsits103097_table_2[] = { 32, // ecdsa_nistp256_with_sha256(0) 32 // ecies_nistp256(1) }; static int hf_sgeonw_symmetric_algorithm; typedef enum { aes_128_ccm = 0, // reserved(240..255), last_sym_algo = (2<<8)-1 } SymmetricAlgorithm; static const value_string symmetric_algorithm_names[] = { { aes_128_ccm, "aes_128_ccm" }, { 0, NULL }, }; static const int etsits103097_table_4[] = { 16 // aes_128_ccm(0) }; static int hf_sgeonw_region_type; static int hf_sgeonw_radius; typedef enum { none = 0, circle = 1, rectangle = 2, polygon = 3, id = 4, // reserved(240..255), last_regiontype = (2<<8)-1 } RegionType; static const value_string region_type_names[] = { { none, "none" }, { circle, "circle" }, { rectangle, "rectangle" }, { polygon, "polygon" }, { id, "id" }, { 0, NULL }, }; static int hf_sgeonw_region_dictionary; static int hf_sgeonw_region_identifier; static int hf_sgeonw_local_region; typedef enum { iso_3166_1 = 0, un_stats = 1, last_regiondictionary = (2<<8)-1 } RegionDictionary; static const value_string region_dictionary_names[] = { { iso_3166_1, "Numeric country codes as in ISO 3166-1" }, { un_stats, "Defined by UN Statistics Division" }, { 0, NULL }, }; static int hf_sgeonw_subject_type; typedef enum { enrollment_credential = 0, authorization_ticket = 1, authorization_authority = 2, enrollment_authority = 3, root_ca = 4, crl_signer = 5, last_subjecttype = (2<<8)-1 } SubjectType; static const value_string subject_type_names[] = { { enrollment_credential, "enrollment_credential" }, { authorization_ticket, "authorization_ticket" }, { authorization_authority, "authorization_authority" }, { enrollment_authority, "enrollment_authority" }, { root_ca, "root_ca" }, { crl_signer, "crl_signer" }, { 0, NULL }, }; static int hf_sgeonw_subject_attribute_type_v1; static int hf_sgeonw_subject_attribute_type_v2; typedef enum { verification_key = 0, encryption_key = 1, assurance_level = 2, reconstruction_value = 3, its_aid_list = 32, its_aid_ssp_list = 33, priority_its_aid_list = 34, priority_ssp_list = 35, last_subjectattributetype = (2<<8)-1 } SubjectAttributeType; static const value_string subject_attribute_type_v1_names[] = { { verification_key, "verification_key" }, { encryption_key, "encryption_key" }, { assurance_level, "assurance_level" }, { reconstruction_value, "reconstruction_value" }, { its_aid_list, "its_aid_list" }, { its_aid_ssp_list, "its_aid_ssp_list" }, { priority_its_aid_list, "priority_its_aid_list" }, { priority_ssp_list, "priority_ssp_list" }, { 0, NULL }, }; static const value_string subject_attribute_type_v2_names[] = { { verification_key, "verification_key" }, { encryption_key, "encryption_key" }, { assurance_level, "assurance_level" }, { reconstruction_value, "reconstruction_value" }, { its_aid_list, "its_aid_list" }, { its_aid_ssp_list, "its_aid_ssp_list" }, { 0, NULL }, }; static int hf_sgeonw_validity_restriction_type; typedef enum { time_end = 0, time_start_and_end = 1, time_start_and_duration = 2, region = 3, last_validityrestrictiontype = (2<<8)-1 } ValidityRestrictionType; static const value_string validity_restriction_type_names[] = { { time_end, "time_end" }, { time_start_and_end, "time_start_and_end" }, { time_start_and_duration, "time_start_and_duration" }, { region, "region" }, { 0, NULL }, }; static int hf_sgeonw_eccpoint; static int ett_sgeonw_eccpoint; static int hf_sgeonw_eccpoint_type; static int hf_sgeonw_eccpoint_x; static int hf_sgeonw_eccpoint_y; typedef enum { x_coordinate_only = 0, compressed_lsb_y_0 = 2, compressed_lsb_y_1 = 3, uncompressed = 4, last_eccpointtype = (2<<8)-1 } EccPointType; static const value_string eccpoint_type_names[] = { { x_coordinate_only, "x_coordinate_only" }, { compressed_lsb_y_0, "compressed_lsb_y_0" }, { compressed_lsb_y_1, "compressed_lsb_y_1" }, { uncompressed, "uncompressed" }, { 0, NULL }, }; // Dissects a length and returns the value // The encoding of the length shall use at most 7 bits set to 1. We support only 3... 0xfffffff should be enough though to encode a length static uint32_t dissect_sec_var_len(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree) { uint32_t tmp_val; uint32_t var_len; uint32_t mask; int start = *offset; proto_item *ti; proto_tree *subtree; // Determine length var_len = tvb_get_uint8(tvb, *offset); *offset+=1; mask = 0x80; while(mask && (var_len & mask)) { tmp_val = tvb_get_uint8(tvb, *offset); *offset += 1; var_len = ((var_len & ~mask) << 8) + tmp_val; mask <<= 7; } ti = proto_tree_add_item(tree, hf_sgeonw_var_len, tvb, start, (*offset) - start, ENC_NA); // Length cannot be determined now subtree = proto_item_add_subtree(ti, ett_sgeonw_var_len); proto_tree_add_bits_item(subtree, hf_sgeonw_var_len_det, tvb, start << 3, (*offset) - start, ENC_NA); proto_tree_add_uint_bits_format_value(subtree, hf_sgeonw_var_len_val, tvb, (start << 3) + (*offset) - start, (((*offset) - start) << 3) - ((*offset) - start),var_len,ENC_BIG_ENDIAN,"%u",var_len); // EI Error if !mask (more than 32 bits) if (!mask) expert_add_info(pinfo, ti, &ei_sgeonw_len_unsupported); return var_len; } // IntX static int dissect_sec_intx(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree, int hf, uint32_t *ret) { //uint8_t var_len = 1; uint64_t tmp_val; uint64_t mask; int start = *offset; proto_item *ti; proto_tree *subtree; // Determine length tmp_val = tvb_get_uint8(tvb, *offset); *offset+=1; mask = 0x80; while(mask && (tmp_val & mask)) { tmp_val &= ~mask; tmp_val <<= 8; tmp_val += tvb_get_uint8(tvb, *offset); *offset += 1; mask <<= 7; //var_len++; } ti = proto_tree_add_item(tree, hf_sgeonw_intx, tvb, start, (*offset) - start, ENC_NA); subtree = proto_item_add_subtree(ti, ett_sgeonw_intx); proto_tree_add_bits_item(subtree, hf_sgeonw_var_len_det, tvb, start << 3, (*offset) - start, ENC_NA); if ((hf != hf_sgeonw_app_id) || ((*offset) - start) > 4) { proto_tree_add_uint64_bits_format_value(subtree, hf, tvb, (start << 3) + (*offset) - start, (((*offset) - start) << 3) - ((*offset) - start), tmp_val, ENC_BIG_ENDIAN, "%" PRIu64, tmp_val); } else { proto_tree_add_uint_bits_format_value(subtree, hf, tvb, (start << 3) + (*offset) - start, (((*offset) - start) << 3) - ((*offset) - start), (uint32_t)tmp_val, ENC_BIG_ENDIAN, "%s(%u)", val64_to_str_const(tmp_val, ieee1609dot2_Psid_vals, "Unknown") , (uint32_t)tmp_val); } // ETSI TS 103 097 V1.2.1: The encoding of the length shall use at most 7 bits set to 1. if (!mask) // EI Error if more than 7 expert_add_info(pinfo, ti, &ei_sgeonw_len_too_long); if (ret) { if(tmp_val & 0xffffffff00000000) { expert_add_info(pinfo, ti, &ei_geonw_intx_too_big); } *ret = (uint32_t) tmp_val; } return (*offset) - start; } static int dissect_sec_eccpoint(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree, PublicKeyAlgorithm algorithm) { uint32_t tmp_val; uint32_t param_len; uint32_t start = *offset; proto_item *ti; proto_tree *subtree; int field_size = etsits103097_table_2[algorithm]; ti = proto_tree_add_item(tree, hf_sgeonw_eccpoint, tvb, *offset, 0, ENC_NA); subtree = proto_item_add_subtree(ti, ett_sgeonw_eccpoint); proto_tree_add_item_ret_uint(subtree, hf_sgeonw_eccpoint_type, tvb, *offset, 1, ENC_BIG_ENDIAN, &tmp_val); // Opaque[field_size] proto_tree_add_item(subtree, hf_sgeonw_eccpoint_x, tvb, 1+(*offset), field_size, ENC_NA); *offset += field_size+1; switch(tmp_val) { case x_coordinate_only: case compressed_lsb_y_0: case compressed_lsb_y_1: break; case uncompressed: // Opaque[field_size] proto_tree_add_item(subtree, hf_sgeonw_eccpoint_y, tvb, *offset, field_size, ENC_NA); *offset += field_size; break; default: // Opaque param_len = dissect_sec_var_len(tvb, offset, pinfo, subtree); proto_tree_add_item(subtree, hf_sgeonw_opaque, tvb, *offset, param_len, ENC_NA); *offset += param_len; } proto_item_set_end(ti, tvb, *offset); return (*offset) - start; } static int dissect_sec_publickey(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree) { uint32_t tmp_val; uint32_t param_len; int start = *offset; proto_item *part_item; proto_tree *part_tree; part_item = proto_tree_add_item(tree, hf_sgeonw_public_key, tvb, *offset, 0, ENC_NA); // Length cannot be determined now part_tree = proto_item_add_subtree(part_item, ett_sgeonw_public_key); proto_tree_add_item_ret_uint(part_tree, hf_sgeonw_public_key_algorithm, tvb, *offset, 1, ENC_BIG_ENDIAN, &tmp_val); *offset += 1; switch(tmp_val) { case ecdsa_nistp256_with_sha256: // EccPoint dissect_sec_eccpoint(tvb, offset, pinfo, part_tree, (PublicKeyAlgorithm) tmp_val); break; case ecies_nistp256: // SymAlgo + EccPoint proto_tree_add_item(part_tree, hf_sgeonw_symmetric_algorithm, tvb, *offset, 1, ENC_BIG_ENDIAN); *offset += 1; dissect_sec_eccpoint(tvb, offset, pinfo, part_tree, (PublicKeyAlgorithm) tmp_val); break; default: // Opaque param_len = dissect_sec_var_len(tvb, offset, pinfo, part_tree); proto_tree_add_item(part_tree, hf_sgeonw_opaque, tvb, *offset, param_len, ENC_NA); *offset += param_len; } proto_item_set_end(part_item, tvb, *offset); return (*offset) - start; } static int dissect_sec_encryption_parameters(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree) { uint32_t tmp_val; uint32_t param_len; uint32_t start = *offset; proto_item *part_item; proto_tree *part_tree; part_item = proto_tree_add_item(tree, hf_sgeonw_encryption_parameter, tvb, *offset, 0, ENC_NA); // Length cannot be determined now part_tree = proto_item_add_subtree(part_item, ett_sgeonw_encryption_parameter); proto_tree_add_item_ret_uint(part_tree, hf_sgeonw_symmetric_algorithm, tvb, *offset, 1, ENC_BIG_ENDIAN, &tmp_val); *offset += 1; switch(tmp_val) { case ecdsa_nistp256_with_sha256: // Opaque[12] proto_tree_add_item(part_tree, hf_sgeonw_encryption_parameter_nonce, tvb, *offset, 12, ENC_NA); *offset += 12; break; default: // Opaque param_len = dissect_sec_var_len(tvb, offset, pinfo, part_tree); proto_tree_add_item(part_tree, hf_sgeonw_opaque, tvb, *offset, param_len, ENC_NA); *offset += param_len; } proto_item_set_end(part_item, tvb, *offset); return (*offset) - start; } static int dissect_sec_ecdsasignature(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree, PublicKeyAlgorithm algorithm) { uint32_t start = *offset; int field_size = etsits103097_table_2[algorithm]; dissect_sec_eccpoint(tvb, offset, pinfo, tree, algorithm); proto_tree_add_item(tree, hf_sgeonw_ecdsasignature_s, tvb, *offset, field_size, ENC_NA); *offset += field_size; return (*offset) - start; } static int dissect_sec_signature(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree) { int start = *offset; uint32_t param_len; uint32_t tmp_val; proto_item *part_item; proto_tree *part_tree; part_item = proto_tree_add_item(tree, hf_sgeonw_signature, tvb, *offset, 0, ENC_NA); // Length cannot be determined now part_tree = proto_item_add_subtree(part_item, ett_sgeonw_signature); proto_tree_add_item_ret_uint(part_tree, hf_sgeonw_public_key_algorithm, tvb, *offset, 1, ENC_BIG_ENDIAN, &tmp_val); *offset += 1; switch(tmp_val) { case ecdsa_nistp256_with_sha256: // EcdsaSignature dissect_sec_ecdsasignature(tvb, offset, pinfo, part_tree, ecdsa_nistp256_with_sha256); break; default: // Opaque param_len = dissect_sec_var_len(tvb, offset, pinfo, part_tree); proto_tree_add_item(part_tree, hf_sgeonw_opaque, tvb, *offset, param_len, ENC_NA); *offset += param_len; } proto_item_set_end(part_item, tvb, *offset); return (*offset) - start; } inline static int dissect_sec_2dlocation(tvbuff_t *tvb, int *offset, proto_tree *tree) { proto_tree_add_item(tree, hf_sgeonw_lat, tvb, *offset, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_sgeonw_lon, tvb, 4+*offset, 4, ENC_BIG_ENDIAN); *offset += 8; return 8; } static int dissect_sec_subject_info(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree) { uint32_t param_len; int start = *offset; proto_item *ti; proto_item *part_item; proto_tree *part_tree; part_item = proto_tree_add_item(tree, hf_sgeonw_subject_info, tvb, *offset, 0, ENC_NA); // Length cannot be determined now part_tree = proto_item_add_subtree(part_item, ett_sgeonw_subject_info); proto_tree_add_item(part_tree, hf_sgeonw_subject_type, tvb, *offset, 1, ENC_BIG_ENDIAN); *offset += 1; param_len = dissect_sec_var_len(tvb, offset, pinfo, part_tree); ti = proto_tree_add_item(part_tree, hf_sgeonw_opaque, tvb, *offset, param_len, ENC_NA); // Expert info: shall be at most 255 bytes long if (param_len > 255) expert_add_info(pinfo, ti, &ei_sgeonw_subj_info_too_long); *offset += param_len; proto_item_set_end(part_item, tvb, *offset); return (*offset) - start; } static int dissect_sec_itsaidssp(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree) { int start = *offset; uint32_t param_len; uint32_t appid; proto_item *ti; proto_tree *subtree; dissect_sec_intx(tvb, offset, pinfo, tree, hf_sgeonw_app_id, &appid); param_len = dissect_sec_var_len(tvb, offset, pinfo, tree); ti = proto_tree_add_item(tree, hf_sgeonw_opaque, tvb, *offset, param_len, ENC_NA); // Expert info: shall be at most 31 bytes long if (param_len > 31) { expert_add_info(pinfo, ti, &ei_sgeonw_ssp_too_long); } else { subtree = proto_item_add_subtree(ti, ett_sgeonw_ssp); tvbuff_t *next_tvb = tvb_new_subset_length(tvb, *offset, param_len); dissector_try_uint(ssp_subdissector_table, appid, next_tvb, pinfo, subtree); } *offset += param_len; return (*offset) - start; } static int hf_sgeonw_priority; static int dissect_sec_itsaidpriority(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree) { int start = *offset; dissect_sec_intx(tvb, offset, pinfo, tree, hf_sgeonw_app_id, NULL); proto_tree_add_item(tree, hf_sgeonw_priority, tvb, *offset, 1, ENC_BIG_ENDIAN); *offset += 1; return (*offset) - start; } static int dissect_sec_itsaidpriorityssp(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree) { int start = *offset; uint32_t param_len; proto_item *ti; dissect_sec_intx(tvb, offset, pinfo, tree, hf_sgeonw_app_id, NULL); proto_tree_add_item(tree, hf_sgeonw_priority, tvb, *offset, 1, ENC_BIG_ENDIAN); *offset += 1; param_len = dissect_sec_var_len(tvb, offset, pinfo, tree); ti = proto_tree_add_item(tree, hf_sgeonw_opaque, tvb, *offset, param_len, ENC_NA); // Expert info: shall be at most 31 bytes long if (param_len > 31) expert_add_info(pinfo, ti, &ei_sgeonw_ssp_too_long); *offset += param_len; return (*offset) - start; } static int hf_sgeonw_subject_assurance; static int ett_sgeonw_subject_assurance; static int hf_sgeonw_subject_assurance_assurance; static int hf_sgeonw_subject_assurance_reserved; static int hf_sgeonw_subject_assurance_confidence; static int dissect_sec_subject_attributes(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree, uint8_t version) { int start = *offset; uint32_t tmp_val; uint32_t param_len; proto_item *ti; proto_item *part_item; proto_tree *subtree; part_item = proto_tree_add_item(tree, hf_sgeonw_subject_attribute, tvb, *offset, 0, ENC_NA); subtree = proto_item_add_subtree(part_item, ett_sgeonw_subject_attribute); ti = proto_tree_add_item_ret_uint(subtree, version == 1 ? hf_sgeonw_subject_attribute_type_v1 : hf_sgeonw_subject_attribute_type_v2, tvb, *offset, 1, ENC_BIG_ENDIAN, &tmp_val); *offset += 1; switch (tmp_val) { case verification_key: case encryption_key: dissect_sec_publickey(tvb,offset,pinfo,subtree); break; case assurance_level: ti = proto_tree_add_item(subtree, hf_sgeonw_subject_assurance, tvb, *offset, 1, ENC_NA); subtree = proto_item_add_subtree(ti, ett_sgeonw_subject_assurance); proto_tree_add_item(subtree, hf_sgeonw_subject_assurance_assurance, tvb, *offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(subtree, hf_sgeonw_subject_assurance_reserved, tvb, *offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(subtree, hf_sgeonw_subject_assurance_confidence, tvb, *offset, 1, ENC_BIG_ENDIAN); *offset += 1; break; case reconstruction_value: dissect_sec_eccpoint(tvb,offset,pinfo,subtree,ecdsa_nistp256_with_sha256); // XXX Which algorithm? hack as only one algo defined break; case its_aid_list: tmp_val = dissect_sec_var_len(tvb, offset, pinfo, subtree); while (tmp_val > 0) { param_len = dissect_sec_intx(tvb,offset,pinfo, subtree, hf_sgeonw_app_id, NULL); if(tmp_val < param_len) { expert_add_info(pinfo, ti, &ei_sgeonw_bogus); return *offset - start; } tmp_val -= param_len; } break; case its_aid_ssp_list: tmp_val = dissect_sec_var_len(tvb, offset, pinfo, subtree); while (tmp_val > 0) { param_len = dissect_sec_itsaidssp(tvb, offset, pinfo, subtree); if(tmp_val < param_len) { expert_add_info(pinfo, ti, &ei_sgeonw_bogus); return *offset - start; } tmp_val -= param_len; } break; case priority_its_aid_list: tmp_val = dissect_sec_var_len(tvb, offset, pinfo, subtree); while (tmp_val > 0) { param_len = dissect_sec_itsaidpriority(tvb, offset, pinfo, subtree); if(tmp_val < param_len) { expert_add_info(pinfo, ti, &ei_sgeonw_bogus); return *offset - start; } tmp_val -= param_len; } break; case priority_ssp_list: tmp_val = dissect_sec_var_len(tvb, offset, pinfo, subtree); while (tmp_val > 0) { param_len = dissect_sec_itsaidpriorityssp(tvb, offset, pinfo, subtree); if(tmp_val < param_len) { expert_add_info(pinfo, ti, &ei_sgeonw_bogus); return *offset - start; } tmp_val -= param_len; } break; default: // Opaque param_len = dissect_sec_var_len(tvb, offset, pinfo, subtree); proto_tree_add_item(subtree, hf_sgeonw_opaque, tvb, *offset, param_len, ENC_NA); *offset += param_len; } proto_item_set_end(part_item, tvb, *offset); return (*offset) - start; } static int dissect_sec_circularregion(tvbuff_t *tvb, int *offset, proto_tree *tree) { dissect_sec_2dlocation(tvb, offset, tree); // uint16 proto_tree_add_item(tree, hf_sgeonw_radius, tvb, *offset, 2, ENC_BIG_ENDIAN); *offset += 2; return 10; } static int dissect_sec_rectangularregion(tvbuff_t *tvb, int *offset, proto_tree *tree) { dissect_sec_2dlocation(tvb, offset, tree); dissect_sec_2dlocation(tvb, offset, tree); return 16; } static int dissect_sec_polygonalregion(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree) { int start = *offset; uint32_t param_len; uint32_t tmp_val; tmp_val = dissect_sec_var_len(tvb, offset, pinfo, tree); while (tmp_val) { param_len = dissect_sec_2dlocation(tvb, offset, tree); if(tmp_val < param_len) return *offset - start; tmp_val -= param_len; } return (*offset) - start; } static int dissect_sec_identifiedregion(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree) { int start = *offset; proto_tree_add_item(tree, hf_sgeonw_region_dictionary, tvb, *offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_sgeonw_region_identifier, tvb, *offset, 2, ENC_BIG_ENDIAN); *offset += 3; dissect_sec_intx(tvb, offset, pinfo, tree, hf_sgeonw_local_region, NULL); return (*offset) - start; } static int dissect_sec_geographicregion(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree) { int start = *offset; uint32_t param_len; uint32_t tmp_val; proto_tree_add_item_ret_uint(tree, hf_sgeonw_region_type, tvb, *offset, 1, ENC_BIG_ENDIAN, &tmp_val); *offset += 1; switch (tmp_val) { case none: break; case circle: dissect_sec_circularregion(tvb,offset,tree); break; case rectangle: dissect_sec_rectangularregion(tvb,offset,tree); break; case polygon: dissect_sec_polygonalregion(tvb, offset, pinfo, tree); break; case id: dissect_sec_identifiedregion(tvb, offset, pinfo, tree); break; default: // Opaque param_len = dissect_sec_var_len(tvb, offset, pinfo, tree); proto_tree_add_item(tree, hf_sgeonw_opaque, tvb, *offset, param_len, ENC_NA); *offset += param_len; } return (*offset) - start; } static const value_string sgeonw_duration_unit_names[] = { { 0, "Seconds" }, { 1, "Minutes" }, { 2, "Hours" }, { 3, "60 Hours block" }, { 4, "Years" }, { 0, NULL } }; static int dissect_sec_validity_restrictions(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree) { int start = *offset; uint32_t tmp_val; uint32_t param_len; proto_item *ti; proto_tree *subtree; proto_tree_add_item_ret_uint(tree, hf_sgeonw_validity_restriction_type, tvb, *offset, 1, ENC_BIG_ENDIAN, &tmp_val); *offset += 1; switch (tmp_val) { case time_end: // Time32 proto_tree_add_item(tree, hf_sgeonw_time32, tvb, *offset, 4, ENC_BIG_ENDIAN); *offset += 4; break; case time_start_and_end: // Time32 proto_tree_add_item(tree, hf_sgeonw_time32, tvb, *offset, 4, ENC_BIG_ENDIAN); *offset += 4; // Time32 proto_tree_add_item(tree, hf_sgeonw_time32, tvb, *offset, 4, ENC_BIG_ENDIAN); *offset += 4; break; case time_start_and_duration: // Time32 proto_tree_add_item(tree, hf_sgeonw_time32, tvb, *offset, 4, ENC_BIG_ENDIAN); *offset += 4; // Duration ti = proto_tree_add_item(tree, hf_sgeonw_duration, tvb, *offset, 2, ENC_NA); subtree = proto_item_add_subtree(ti, ett_sgeonw_duration); proto_tree_add_item(subtree, hf_sgeonw_duration_unit, tvb, *offset, 2, ENC_BIG_ENDIAN); proto_tree_add_item(subtree, hf_sgeonw_duration_value, tvb, *offset, 2, ENC_BIG_ENDIAN); *offset += 2; break; case region: dissect_sec_geographicregion(tvb, offset, pinfo, tree); break; default: // Opaque param_len = dissect_sec_var_len(tvb, offset, pinfo, tree); proto_tree_add_item(tree, hf_sgeonw_opaque, tvb, *offset, param_len, ENC_NA); *offset += param_len; } return (*offset) - start; } static int dissect_sec_signer_info(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree, uint8_t version); static int hf_sgeonw_certification_version; static int // NOLINTNEXTLINE(misc-no-recursion) dissect_sec_certificate(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree, uint8_t version) { uint32_t tmp_val; uint32_t param_len; int start = *offset; proto_tree_add_item_ret_uint(tree, hf_sgeonw_certification_version, tvb, *offset, 1, ENC_BIG_ENDIAN, &tmp_val); *offset += 1; if (version == 1) { tmp_val = dissect_sec_var_len(tvb, offset, pinfo, tree); while (tmp_val > 0) { param_len = dissect_sec_signer_info(tvb, offset, pinfo, tree, version); tmp_val -= param_len; } } else { dissect_sec_signer_info(tvb, offset, pinfo, tree, version); } dissect_sec_subject_info(tvb, offset, pinfo, tree); tmp_val = dissect_sec_var_len(tvb, offset, pinfo, tree); while (tmp_val > 0) { param_len = dissect_sec_subject_attributes(tvb, offset, pinfo, tree, version); tmp_val -= param_len; } tmp_val = dissect_sec_var_len(tvb, offset, pinfo, tree); while (tmp_val > 0) { param_len = dissect_sec_validity_restrictions(tvb, offset, pinfo, tree); tmp_val -= param_len; } dissect_sec_signature(tvb, offset, pinfo, tree); return (*offset) - start; } static int // NOLINTNEXTLINE(misc-no-recursion) dissect_sec_signer_info(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree, uint8_t version) { int start = *offset; uint32_t tmp_val; uint32_t param_len; proto_item *ti; proto_tree *subtree; proto_item *tinner; proto_tree *insidetree; increment_dissection_depth(pinfo); tmp_val = tvb_get_uint8(tvb, *offset); if (tmp_val == self) { // No additional data shall be given proto_tree_add_item(tree, hf_sgeonw_signer_info_type, tvb, *offset, 1, ENC_BIG_ENDIAN); *offset += 1; } else { ti = proto_tree_add_item(tree, hf_sgeonw_signer_info, tvb, *offset, 0, ENC_NA); subtree = proto_item_add_subtree(ti, ett_sgeonw_field); proto_tree_add_item(subtree, hf_sgeonw_signer_info_type, tvb, *offset, 1, ENC_BIG_ENDIAN); *offset += 1; switch(tmp_val) { case certificate_digest_with_ecdsap256: // HashedId8 proto_tree_add_item(subtree, hf_sgeonw_hashedid8, tvb, *offset, 8, ENC_NA); *offset += 8; break; case certificate: // Certificate tinner = proto_tree_add_item(subtree, hf_sgeonw_certificate, tvb, *offset, 0, ENC_NA); insidetree = proto_item_add_subtree(tinner, ett_sgeonw_field); dissect_sec_certificate(tvb, offset, pinfo, insidetree, version); proto_item_set_end(tinner, tvb, *offset); break; case certificate_chain: // variable-length vector of type Certificate tmp_val = dissect_sec_var_len(tvb, offset, pinfo, subtree); while (tmp_val > 0) { tinner = proto_tree_add_item(subtree, hf_sgeonw_certificate, tvb, *offset, 0, ENC_NA); insidetree = proto_item_add_subtree(tinner, ett_sgeonw_field); param_len = dissect_sec_certificate(tvb, offset, pinfo, insidetree, version); proto_item_set_end(tinner, tvb, *offset); tmp_val -= param_len; } break; case certificate_digest_with_other_algorithm: // HashedId8 proto_tree_add_item(subtree, hf_sgeonw_public_key_algorithm, tvb, *offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(subtree, hf_sgeonw_hashedid8, tvb, 1+(*offset), 8, ENC_NA); *offset += 9; break; default: // Opaque param_len = dissect_sec_var_len(tvb, offset, pinfo, subtree); proto_tree_add_item(subtree, hf_sgeonw_opaque, tvb, *offset, param_len, ENC_NA); *offset += param_len; } proto_item_set_end(ti, tvb, *offset); } decrement_dissection_depth(pinfo); return (*offset) - start; } static int hf_sgeonw_encrypted_key; static int hf_sgeonw_auth_tag; // This structure defines how to transmit an EciesNistP256-encrypted symmetric key as defined in IEEE // Std 1363a-2004. // EciesNistP256EncryptedKey structure shall be preceded by an according // EncryptionParameters structure. static int dissect_sec_eciesnistp256entryptedkey(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree, PublicKeyAlgorithm pub_alg, SymmetricAlgorithm symm_alg, uint8_t version) { int start = *offset; uint8_t symm_key_len = etsits103097_table_4[symm_alg]; dissect_sec_eccpoint(tvb, offset, pinfo, tree, pub_alg); proto_tree_add_item(tree, hf_sgeonw_encrypted_key, tvb, *offset, symm_key_len, ENC_NA); *offset += symm_key_len; proto_tree_add_item(tree, hf_sgeonw_auth_tag, tvb, *offset, version==1?20:16, ENC_NA); *offset += version==1?20:16; return (*offset) - start; } static int dissect_sec_recipient_info(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *tree, uint8_t version) { int start = *offset; uint32_t tmp_val; uint32_t param_len; proto_tree_add_item(tree, hf_sgeonw_hashedid8, tvb, *offset, 8, ENC_NA); proto_tree_add_item_ret_uint(tree, hf_sgeonw_public_key_algorithm, tvb, 8+*offset, 1, ENC_BIG_ENDIAN, &tmp_val); *offset += 9; switch (tmp_val) { case ecies_nistp256: // EciesNistP256EncryptedKey // XXX SymmetricAlgorithm should be provided by preceding EncryptionParameter... dissect_sec_eciesnistp256entryptedkey(tvb, offset, pinfo, tree, (PublicKeyAlgorithm)tmp_val, aes_128_ccm, version); break; default: // Opaque param_len = dissect_sec_var_len(tvb, offset, pinfo, tree); proto_tree_add_item(tree, hf_sgeonw_opaque, tvb, *offset, param_len, ENC_NA); *offset += param_len; } return (*offset) - start; } static int dissect_sec_payload(tvbuff_t *tvb, int *offset, packet_info *pinfo, proto_tree *part_tree) { int start = *offset; uint32_t tmp_val; uint32_t param_len; proto_tree *field_tree; proto_item *ti; tmp_val = tvb_get_uint8(tvb, *offset); if (tmp_val == signed_external) { proto_tree_add_item(part_tree, hf_sgeonw_payload_field_type, tvb, *offset, 1, ENC_BIG_ENDIAN); *offset += 1; } else { ti = proto_tree_add_item(part_tree, hf_sgeonw_payload_field, tvb, *offset, 0, ENC_NA); field_tree = proto_item_add_subtree(ti, ett_sgeonw_field); proto_tree_add_item(field_tree, hf_sgeonw_payload_field_type, tvb, *offset, 1, ENC_BIG_ENDIAN); *offset += 1; switch(tmp_val) { case unsecured: case signed_pl: param_len = dissect_sec_var_len(tvb, offset, pinfo, field_tree); if (param_len) { tvbuff_t *next_tvb = tvb_new_subset_length(tvb, *offset, param_len); p_add_proto_data(pinfo->pool, pinfo, proto_geonw, SEC_TVB_KEY, next_tvb); } *offset += param_len; break; case encrypted: case signed_and_encrypted: param_len = dissect_sec_var_len(tvb, offset, pinfo, field_tree); proto_tree_add_item(field_tree, hf_sgeonw_opaque, tvb, *offset, param_len, ENC_NA); *offset += param_len; break; default: // Opaque param_len = dissect_sec_var_len(tvb, offset, pinfo, field_tree); proto_tree_add_item(field_tree, hf_sgeonw_opaque, tvb, *offset, param_len, ENC_NA); *offset += param_len; } proto_item_set_end(ti, tvb, *offset); } return (*offset) - start; } static int dissect_secured_message(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, void *data _U_) { uint32_t msg_id; // Or Application ID, depending on version uint8_t version; uint32_t var_len; proto_item *ti; uint32_t tmp_val; uint32_t param_len; proto_item *secmsg_item; proto_item *part_item; proto_tree *part_tree; proto_tree *field_tree; int sec_start = offset; // Secured message subtree secmsg_item = proto_tree_add_item(tree, hf_geonw_sec, tvb, offset, 0, ENC_NA); // Length cannot be determined now proto_tree *secmsg_tree = proto_item_add_subtree(secmsg_item, ett_geonw_sec); version = tvb_get_uint8(tvb, offset); if (version == 3) { tvbuff_t *next_tvb = tvb_new_subset_remaining(tvb, offset); call_dissector(ieee1609dot2_handle, next_tvb, pinfo, secmsg_tree); // If unsecure or only signed, content is in private data return tvb_captured_length(tvb); } proto_tree_add_item(secmsg_tree, hf_sgeonw_version, tvb, offset, 1, ENC_BIG_ENDIAN); offset+=1; if ((version < 1) || (version > 2)) return 1; if (version == 1) { proto_tree_add_item_ret_uint(secmsg_tree, hf_sgeonw_profile, tvb, offset, 1, ENC_BIG_ENDIAN, &tmp_val); offset += 1; } // Header Fields part_item = proto_tree_add_item(secmsg_tree, hf_sgeonw_hdr, tvb, offset, 0, ENC_NA); // Length cannot be determined now part_tree = proto_item_add_subtree(part_item, ett_sgeonw_hdr); var_len = dissect_sec_var_len(tvb, &offset, pinfo, part_tree); while (var_len > 0) { uint32_t start = offset; ti = proto_tree_add_item(part_tree, hf_sgeonw_header_field, tvb, offset, 0, ENC_NA); field_tree = proto_item_add_subtree(ti, ett_sgeonw_field); proto_tree_add_item_ret_uint(field_tree, version==1?hf_sgeonw_header_field_type_v1:hf_sgeonw_header_field_type_v2, tvb, offset, 1, ENC_BIG_ENDIAN, &tmp_val); offset += 1; switch(tmp_val) { case generation_time: // Time64 proto_tree_add_item(field_tree, hf_sgeonw_time64, tvb, offset, 8, ENC_BIG_ENDIAN); offset += 8; break; case generation_time_confidence: // Time64WithStandardDeviation proto_tree_add_item(field_tree, hf_sgeonw_time64, tvb, offset, 8, ENC_BIG_ENDIAN); proto_tree_add_item(field_tree, hf_sgeonw_conf, tvb, offset+8, 1, ENC_BIG_ENDIAN); offset += 9; break; case expiration: // Time32 proto_tree_add_item(field_tree, hf_sgeonw_time32, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; break; case generation_location: // ThreeDLocation proto_tree_add_item(field_tree, hf_sgeonw_lat, tvb, offset, 4, ENC_BIG_ENDIAN); proto_tree_add_item(field_tree, hf_sgeonw_lon, tvb, offset+4, 4, ENC_BIG_ENDIAN); proto_tree_add_item(field_tree, hf_sgeonw_elev, tvb, offset+8, 2, ENC_BIG_ENDIAN); offset += 10; break; case request_unrecognized_certificate: // HashedId3 param_len = dissect_sec_var_len(tvb, &offset, pinfo, field_tree); proto_item_set_len(ti, (offset-start) + param_len); while (param_len) { proto_tree_add_item(field_tree, hf_sgeonw_hashedid3, tvb, offset, 3, ENC_NA); offset += 3; param_len -= 3; } break; case message_type: if (version == 1) { proto_tree_add_item_ret_uint(field_tree, hf_sgeonw_msg_id, tvb, offset, 2, ENC_BIG_ENDIAN, &msg_id); offset += 2; } else { dissect_sec_intx(tvb, &offset, pinfo, field_tree, hf_sgeonw_app_id, &msg_id); } break; case signer_info: // SignerInfo dissect_sec_signer_info(tvb, &offset, pinfo, field_tree, version); break; case recipient_info: // RecipientInfo param_len = dissect_sec_var_len(tvb, &offset, pinfo, field_tree); proto_item_set_len(ti, (offset-start) + param_len); while (param_len) { param_len -= dissect_sec_recipient_info(tvb, &offset, pinfo, field_tree, version); } offset += param_len; break; case encryption_parameters: // EncryptionParameters dissect_sec_encryption_parameters(tvb, &offset, pinfo, field_tree); break; default: // Opaque param_len = dissect_sec_var_len(tvb, &offset, pinfo, field_tree); proto_item_set_len(ti, (offset-start) + param_len); proto_tree_add_item(field_tree, hf_sgeonw_opaque, tvb, offset, param_len, ENC_NA); offset += param_len; } proto_item_set_end(ti, tvb, offset); /* EI var_len >= (offset-start) */ var_len -= (offset - start); } proto_item_set_end(part_item, tvb, offset); // Payload part_item = proto_tree_add_item(secmsg_tree, hf_sgeonw_pl, tvb, offset, 0, ENC_NA); // Length cannot be determined now part_tree = proto_item_add_subtree(part_item, ett_sgeonw_hdr); // Change in version 2: only one payload element here! if (version == 1) { var_len = dissect_sec_var_len(tvb, &offset, pinfo, part_tree); while (var_len > 0) { uint32_t start = offset; dissect_sec_payload(tvb, &offset, pinfo, part_tree); if (var_len < (offset-start)) return 0; var_len -= (offset - start); } } else { dissect_sec_payload(tvb, &offset, pinfo, part_tree); } proto_item_set_end(part_item, tvb, offset); // Trailer part_item = proto_tree_add_item(secmsg_tree, hf_sgeonw_trl, tvb, offset, 0, ENC_NA); // Length cannot be determined now part_tree = proto_item_add_subtree(part_item, ett_sgeonw_hdr); var_len = dissect_sec_var_len(tvb, &offset, pinfo, part_tree); while (var_len > 0) { uint32_t start = offset; ti = proto_tree_add_item(part_tree, hf_sgeonw_trailer_field, tvb, offset, 0, ENC_NA); field_tree = proto_item_add_subtree(ti, ett_sgeonw_field); proto_tree_add_item_ret_uint(field_tree, hf_sgeonw_trailer_field_type, tvb, offset, 1, ENC_BIG_ENDIAN, &tmp_val); offset += 1; switch(tmp_val) { case signature: // Signature dissect_sec_signature(tvb, &offset, pinfo, field_tree); break; default: // Opaque param_len = dissect_sec_var_len(tvb, &offset, pinfo, field_tree); proto_tree_add_item(field_tree, hf_sgeonw_opaque, tvb, offset, param_len, ENC_NA); offset += param_len; } proto_item_set_end(ti, tvb, offset); var_len -= (offset - start); } proto_item_set_end(part_item, tvb, offset); proto_item_set_end(secmsg_item, tvb, offset); return offset - sec_start; } static int dissect_sgeonw(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void *data _U_) { // Just store the tvbuff for later, as it is embedded inside a secured geonetworking packet p_add_proto_data(pinfo->pool, pinfo, proto_geonw, SEC_TVB_KEY, tvb); return tvb_reported_length(tvb); } // The actual dissector // skip_bh: // 0 - do not skip, complete GNW packet with Basic Header // BH_NH_COMMON_HDR - skip and continue to Common Header // BH_NH_SECURED_PKT - skip and continue to Secured Packet // XXX COL_INFO to be improved static int dissect_geonw_internal(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_, uint8_t skip_bh) { uint8_t bh_next_header; uint32_t ch_next_header; uint32_t header_type; uint32_t rhl; uint32_t tmp_val; int offset = 0; proto_item *ti; proto_item *top_item; proto_item* rhl_ti = NULL; int hdr_len = 0; uint32_t payload_len = 0; uint32_t reserved; uint32_t timestamp; uint32_t sequence_number = SN_MAX + 1; struct geonwheader *geonwh; int32_t latlon; geonwh = wmem_new0(pinfo->pool, struct geonwheader); col_set_str(pinfo->cinfo, COL_PROTOCOL, "GEONW"); /* Clear out stuff in the info column */ col_clear(pinfo->cinfo,COL_INFO); if (!skip_bh) { bh_next_header = tvb_get_uint8(tvb, 0) & 0x0f; hdr_len = BH_LEN; } else { bh_next_header = skip_bh; } if (bh_next_header == BH_NH_COMMON_HDR) { header_type = tvb_get_uint8(tvb, hdr_len + 1); hdr_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; } } top_item = proto_tree_add_item(tree, proto_geonw, tvb, 0, hdr_len, ENC_NA); proto_tree *geonw_tree = proto_item_add_subtree(top_item, ett_geonw); if (!skip_bh) { // 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 or 1 if (tmp_val > 1) { col_add_fstr(pinfo->cinfo, COL_INFO, "Bogus GeoNetworking version (%u, must be less than 2)", 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; 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) { dissect_secured_message(tvb, offset, pinfo, geonw_tree, NULL); tvbuff_t *next_tvb = (tvbuff_t*)p_get_proto_data(pinfo->pool, pinfo, proto_geonw, SEC_TVB_KEY); if (next_tvb) { tvb = next_tvb; bh_next_header = BH_NH_COMMON_HDR; offset = 0; header_type = tvb_get_uint8(tvb, 1); hdr_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; } } } 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; if (!skip_bh) { /* 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 GNW datagram isn't * obviously bogus, adjust the length of this tvbuff to include only * the GNW 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; offset += 1; if (!skip_bh) { // 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); } } // 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; // Stop here if header_type unknown if (!IS_HT_KNOWN(header_type)) { // Update top level tree item proto_item_set_end(top_item, tvb, offset); return tvb_reported_length(tvb); } 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: // 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; 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; 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 && (geonwh->gnw_ver==0) && !(pinfo->fd->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 uint8_t *)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) && (((uint64_t)timestamp - (uint64_t)tp->timestamp) <= (uint64_t)TST_MAX/2)) || ((tp->timestamp > timestamp) && (((uint64_t)tp->timestamp - (uint64_t)timestamp) > (uint64_t)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) && (((uint64_t)timestamp - (uint64_t)tp->timestamp) <= (uint64_t)TST_MAX/2)) || ((tp->timestamp > timestamp) && (((uint64_t)tp->timestamp - (uint64_t)timestamp) > (uint64_t)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] "); } } } } // XXX Implement DPD if version == 1 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, "Longitude 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); } 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, "Longitude out of range (%f)", (float)latlon/10000000); } offset += 4; break; case HTST_TSB_SINGLE: // Reserved 32 bits // See usage in 636-4 subpart 2 for ITS-5G reserved = tvb_get_uint32(tvb, offset, ENC_BIG_ENDIAN); if (reserved) { ti = proto_tree_add_item(geonw_sh_tree, hf_geonw_dccmco, tvb, offset, 4, ENC_NA); proto_tree *dccmco = proto_item_add_subtree(ti, ett_geonw_dccmco); proto_tree_add_item(dccmco, hf_geonw_dccmco_cbr_l_0_hop, tvb, offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(dccmco, hf_geonw_dccmco_cbr_l_1_hop, tvb, offset+1, 1, ENC_BIG_ENDIAN); proto_tree_add_item(dccmco, hf_geonw_dccmco_output_power, tvb, offset+2, 1, ENC_BIG_ENDIAN); proto_tree_add_item(dccmco, hf_geonw_dccmco_reserved, tvb, offset+2, 1, ENC_BIG_ENDIAN); proto_tree_add_item(dccmco, hf_geonw_shb_reserved, tvb, offset+3, 1, ENC_BIG_ENDIAN); } else { proto_tree_add_item(geonw_sh_tree, hf_geonw_shb_reserved, tvb, offset, 4, ENC_BIG_ENDIAN); } 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, "Longitude 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: } proto_item_set_end(top_item, tvb, offset); 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_, char* result) { uint32_t port = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, hf_btpa_srcport, pinfo->curr_layer_num)); snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "source (%u%s)", port, UTF8_RIGHTWARDS_ARROW); } static void * 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, char *result) { uint32_t port = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, hf_btpa_dstport, pinfo->curr_layer_num)); snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "destination (%s%u)", UTF8_RIGHTWARDS_ARROW, port); } static void * 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, char *result) { uint32_t 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)); 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, char *result) { uint32_t port = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, hf_btpb_dstport, pinfo->curr_layer_num)); snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "destination (%s%u)", UTF8_RIGHTWARDS_ARROW, port); } static void * 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 int *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_with_description("btpa.payload", "BTP-A payload fallback", 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", "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); btpa_tap = register_tap("btpa"); btpa_follow_tap = register_tap("btpa_follow"); } void proto_reg_handoff_btpa(void) { dissector_add_uint("geonw.ch.nh", 1, btpa_handle); find_dissector_add_dependency("gnw", proto_btpa); } 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 int *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_with_description("btpb.payload", "BTP-B payload fallback", 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", "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); btpb_tap = register_tap("btpb"); btpb_follow_tap = register_tap("btpb_follow"); } void proto_reg_handoff_btpb(void) { dissector_add_uint("geonw.ch.nh", 2, btpb_handle); find_dissector_add_dependency("gnw", proto_btpb); } // Display functions static void display_latitude( char *result, int32_t hexver ) { snprintf( result, ITEM_LABEL_LENGTH, "%ud%u'%.2f\"%c (%d)", abs(hexver)/10000000, abs(hexver%10000000)*6/1000000, abs(hexver*6%1000000)*6./100000., hexver>=0?'N':'S', hexver); } static void display_longitude( char *result, int32_t hexver ) { snprintf( result, ITEM_LABEL_LENGTH, "%ud%u'%.2f\"%c (%d)", abs(hexver)/10000000, abs(hexver%10000000)*6/1000000, abs(hexver*6%1000000)*6./100000., hexver>=0?'E':'W', hexver); } static void display_speed( char *result, int32_t hexver ) { snprintf( result, ITEM_LABEL_LENGTH, "%.2f m/s", hexver/100.); } static void display_heading( char *result, uint32_t hexver ) { snprintf( result, ITEM_LABEL_LENGTH, "%.1f degrees", hexver/10.); } static void display_elevation( char *result, int32_t hexver ) { // 0x0000 to 0xEFFF: positive numbers with a range from 0 to +6 143,9 meters. All numbers above +6 143,9 are // also represented by 0xEFFF. // 0xF001 to 0xFFFF: negative numbers with a range from -409,5 to -0,1 meters. All numbers below -409,5 are // also represented by 0xF001. // 0xF000: an unknown elevation if (hexver == -4096) snprintf( result, ITEM_LABEL_LENGTH, "Unknown (%4x)", hexver); else snprintf( result, ITEM_LABEL_LENGTH, "%.1fm", hexver/10.); } static int dissect_geonw(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data) { return dissect_geonw_internal(tvb, pinfo, tree, data, 0); } static int dissect_geonw_comm(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data) { return dissect_geonw_internal(tvb, pinfo, tree, data, BH_NH_COMMON_HDR); } static int dissect_geonw_sec(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data) { return dissect_geonw_internal(tvb, pinfo, tree, data, BH_NH_SECURED_PKT); } void proto_register_geonw(void) { static const value_string bh_next_header_names[] = { { 0, "ANY" }, { 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[] = { { 0, "ANY" }, { 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 traffic_classes_its_g5_names[] = { { 0, "ITS-G5 Access Category Voice" }, { 1, "ITS-G5 Access Category Video" }, { 2, "ITS-G5 Access Category Best effort" }, { 3, "ITS-G5 Access Category Background" }, { 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, { "Traffic class ID", "geonw.ch.tc.id", FT_UINT8, BASE_DEC, VALS(traffic_classes_its_g5_names), 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_DEC, 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, UNS(&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), 0x7FFF, NULL, HFILL }}, { &hf_geonw_so_pv_heading, { "Heading", "geonw.src_pos.hdg", FT_UINT16, BASE_CUSTOM, CF_FUNC(display_heading), 0x00, NULL, HFILL }}, // Decentralized Congestion Control - Multi Channel Operation { &hf_geonw_dccmco, { "Decentralized Congestion Control - Multi Channel Operation", "geonw.dccmco", FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL }}, { &hf_geonw_dccmco_cbr_l_0_hop, { "Local channel busy ratio", "geonw.cbr_l0hop", FT_UINT8, BASE_DEC, NULL, 0x80, NULL, HFILL }}, { &hf_geonw_dccmco_cbr_l_1_hop, { "Max neighbouring CBR", "geonw.cbr_l1hop", FT_UINT8, BASE_DEC, NULL, 0x80, NULL, HFILL }}, { &hf_geonw_dccmco_output_power, { "Output power", "geonw.outpower", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, UNS(&units_dbm), 0xf8, NULL, HFILL }}, { &hf_geonw_dccmco_reserved, { "Reserved", "geonw.dccmco.reserved", FT_UINT8, BASE_DEC, NULL, 0x07, 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, UNS(&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, UNS(&units_meters), 0x00, NULL, HFILL }}, { &hf_geonw_gxc_distancea, { "Distance a", "geonw.gxc.distancea", FT_UINT16, BASE_DEC|BASE_UNIT_STRING, UNS(&units_meters), 0x00, NULL, HFILL }}, { &hf_geonw_gxc_distanceb, { "Distance b", "geonw.gxc.distanceb", FT_UINT16, BASE_DEC|BASE_UNIT_STRING, UNS(&units_meters), 0x00, NULL, HFILL }}, { &hf_geonw_gxc_angle, { "Angle", "geonw.gxc.angle", FT_UINT16, BASE_DEC|BASE_UNIT_STRING, UNS(&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.tsb", 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 }}, // Secures packets { &hf_geonw_sec, { "Secured Packet", "geonw.sec", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_version, { "Version", "geonw.sec.version", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_profile, { "Profile", "geonw.sec.profile", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_hdr, { "Header fields", "geonw.sec.hdr", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_pl, { "Payload fields", "geonw.sec.pl", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_trl, { "Trailer fields", "geonw.sec.trl", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_public_key, { "Public key", "geonw.sec.pub_key", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_certificate, { "Certificate", "geonw.sec.certif", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_var_len, { "Var length", "geonw.sec.var_len", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_var_len_det, { "Var length determinant", "geonw.sec.var_len.det", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_var_len_val, { "Var length value", "geonw.sec.var_len.value", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_intx, { "IntX", "geonw.sec.intx", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_header_field, { "Header field", "geonw.sec.hdr_field", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_payload_field, { "Payload field", "geonw.sec.pl_field", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_trailer_field, { "Trailer field", "geonw.sec.trl_field", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_signer_info, { "Signer info", "geonw.sec.signer_info", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_eccpoint, { "ECC Point", "geonw.sec.eccpoint", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_duration, { "Duration", "geonw.sec.duration", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_subject_assurance, { "Subject assurance", "geonw.sec.subj_assur", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_encryption_parameter, { "Encryption parameter", "geonw.sec.encrypt_param", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_signature, { "Signature", "geonw.sec.signature", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_subject_info, { "Subject info", "geonw.sec.subj_info", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_subject_attribute, { "Subject attribute", "geonw.sec.subj_attr", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_opaque, { "Opaque", "geonw.sec.opaque", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_encrypted_key, { "Encrypted key", "geonw.sec.enc_key", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_auth_tag, { "Authentication tag", "geonw.sec.auth_tag", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_ecdsasignature_s, { "s", "geonw.sec.signature.s", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_eccpoint_x, { "x", "geonw.sec.eccpoint.x", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_eccpoint_y, { "y", "geonw.sec.eccpoint.y", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_hashedid8, { "Hashed ID 8", "geonw.sec.hashedid8", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_encryption_parameter_nonce, { "Nonce", "geonw.sec.nonce", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_header_field_type_v1, { "Header field type", "geonw.sec.hdr_fld_type", FT_UINT8, BASE_DEC, VALS(header_field_type_v1_names), 0x0, NULL, HFILL }}, { &hf_sgeonw_header_field_type_v2, { "Header field type", "geonw.sec.hdr_fld_type", FT_UINT8, BASE_DEC, VALS(header_field_type_v2_names), 0x0, NULL, HFILL }}, { &hf_sgeonw_payload_field_type, { "Payload field type", "geonw.sec.pl_fld_type", FT_UINT8, BASE_DEC, VALS(payload_field_type_names), 0x0, NULL, HFILL }}, { &hf_sgeonw_trailer_field_type, { "Trailer field type", "geonw.sec.trl_fld_type", FT_UINT8, BASE_DEC, VALS(trailer_field_type_names), 0x0, NULL, HFILL }}, { &hf_sgeonw_public_key_algorithm, { "Public key algorithm", "geonw.sec.pubkeyalgo", FT_UINT8, BASE_DEC, VALS(public_key_algorithm_names), 0x0, NULL, HFILL }}, { &hf_sgeonw_eccpoint_type, { "EccPoint type", "geonw.sec.eccpoint_type", FT_UINT8, BASE_DEC, VALS(eccpoint_type_names), 0x0, NULL, HFILL }}, { &hf_sgeonw_signer_info_type, { "Signer info type", "geonw.sec.signer_info_type", FT_UINT8, BASE_DEC, VALS(signer_info_type_names), 0x0, NULL, HFILL }}, { &hf_sgeonw_validity_restriction_type, { "Validity restriction type", "geonw.sec.val_rest_type", FT_UINT8, BASE_DEC, VALS(validity_restriction_type_names), 0x0, NULL, HFILL }}, { &hf_sgeonw_subject_type, { "Subject type", "geonw.sec.subject_type", FT_UINT8, BASE_DEC, VALS(subject_type_names), 0x0, NULL, HFILL }}, { &hf_sgeonw_subject_attribute_type_v1, { "Subject attribute", "geonw.sec.subject_attr", FT_UINT8, BASE_DEC, VALS(subject_attribute_type_v1_names), 0x0, NULL, HFILL }}, { &hf_sgeonw_subject_attribute_type_v2, { "Subject attribute", "geonw.sec.subject_attr", FT_UINT8, BASE_DEC, VALS(subject_attribute_type_v2_names), 0x0, NULL, HFILL }}, { &hf_sgeonw_symmetric_algorithm, { "Symmetric algorithm", "geonw.sec.symalgo", FT_UINT8, BASE_DEC, VALS(symmetric_algorithm_names), 0x0, NULL, HFILL }}, { &hf_sgeonw_region_type, { "Region type", "geonw.sec.regiontype", FT_UINT8, BASE_DEC, VALS(region_type_names), 0x0, NULL, HFILL }}, { &hf_sgeonw_region_dictionary, { "Region dictionary", "geonw.sec.regiondict", FT_UINT8, BASE_DEC, VALS(region_dictionary_names), 0x0, NULL, HFILL }}, { &hf_sgeonw_region_identifier, { "Region identifier", "geonw.sec.regionid", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_local_region, { "Local region", "geonw.sec.local_region", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_certification_version, { "Certification version", "geonw.sec.certif.version", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_time64, { "Time64", "geonw.sec.time64", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_conf, { "Confidence", "geonw.sec.confidence", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_time32, { "Time32", "geonw.sec.time32", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_lat, { "Latitude", "geonw.sec.lat", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_lon, { "Longitude", "geonw.sec.lon", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_elev, { "Elevation", "geonw.sec.elev", FT_INT16, BASE_CUSTOM, CF_FUNC(display_elevation), 0x0, NULL, HFILL }}, { &hf_sgeonw_hashedid3, { "Hashed ID 3", "geonw.sec.hashedid3", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_duration_unit, { "Unit", "geonw.sec.duration.unit", FT_UINT16, BASE_DEC, VALS(sgeonw_duration_unit_names), 0xe000, NULL, HFILL }}, { &hf_sgeonw_duration_value, { "Value", "geonw.sec.duration.value", FT_UINT16, BASE_DEC, NULL, 0x1fff, NULL, HFILL }}, { &hf_sgeonw_radius, { "Radius", "geonw.sec.radius", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_priority, { "Priority", "geonw.sec.priority", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_subject_assurance_assurance, { "Subject assurance", "geonw.sec.subj_assur.assurance", FT_UINT8, BASE_DEC, NULL, 0xe0, NULL, HFILL }}, { &hf_sgeonw_subject_assurance_reserved, { "Reserved", "geonw.sec.subj_assur.reserved", FT_UINT8, BASE_DEC, NULL, 0x1c, NULL, HFILL }}, { &hf_sgeonw_subject_assurance_confidence, { "Subject assurance", "geonw.sec.subj_assur.confidence", FT_UINT8, BASE_DEC, NULL, 0x03, NULL, HFILL }}, { &hf_sgeonw_msg_id, { "Message ID", "geonw.sec.msg_id", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_sgeonw_app_id, { "Application ID", "geonw.sec.app_id", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, 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 }}, { &ei_sgeonw_len_unsupported, { "geonw.sec.len_unsup", PI_MALFORMED, PI_ERROR, "Length not supported", EXPFILL }}, { &ei_sgeonw_len_too_long, { "geonw.sec.bogus_len", PI_MALFORMED, PI_ERROR, "Length of int shall be at most 7 bits long", EXPFILL }}, { &ei_sgeonw_subj_info_too_long, { "geonw.sec.bogus_sinfo", PI_MALFORMED, PI_ERROR, "Subject info length shall be at most 255", EXPFILL }}, { &ei_sgeonw_ssp_too_long, { "geonw.sec.bogus_ssp", PI_MALFORMED, PI_ERROR, "Service specific permission length shall be at most 31", EXPFILL }}, { &ei_sgeonw_bogus, { "geonw.sec.bogus", PI_MALFORMED, PI_ERROR, "Malformed message (check length)", EXPFILL }}, { &ei_geonw_intx_too_big, { "geonw.intx_too_big", PI_MALFORMED, PI_ERROR, "IntX value exceeds 32 bits", EXPFILL }}, }; static int *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, &ett_geonw_dccmco, &ett_geonw_sec, // Secured packet &ett_sgeonw_hdr, // Parts (header, payload or trailer) subtree &ett_sgeonw_field, // Field subtree &ett_sgeonw_var_len, // Variable length subtree &ett_sgeonw_intx, &ett_sgeonw_duration, &ett_sgeonw_eccpoint, &ett_sgeonw_subject_assurance, &ett_sgeonw_public_key, &ett_sgeonw_encryption_parameter, &ett_sgeonw_signature, &ett_sgeonw_subject_info, &ett_sgeonw_subject_attribute, &ett_sgeonw_ssp, }; 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); register_dissector("gnw.comm", dissect_geonw_comm, proto_geonw); register_dissector("gnw.sec", dissect_geonw_sec, 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); ssp_subdissector_table = register_dissector_table("geonw.ssp", "ATS-AID/PSID based dissector for Service Specific Permissions (SSP)", proto_geonw, FT_UINT32, 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); geonw_tap = register_tap("geonw"); } void proto_reg_handoff_geonw(void) { dissector_handle_t sgeonw_handle_; // This is a minimal dissector that just stores the tvbuff for later use; // not useful from outside a dissector table, so not using register_dissector() sgeonw_handle_ = create_dissector_handle(dissect_sgeonw, 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); ieee1609dot2_handle = find_dissector_add_dependency("ieee1609dot2.data", proto_geonw); dissector_add_uint("ieee1609dot2.psid", psid_den_basic_services, sgeonw_handle_); dissector_add_uint("ieee1609dot2.psid", psid_ca_basic_services, sgeonw_handle_); dissector_add_uint("ieee1609dot2.psid", psid_traffic_light_manoeuver_service, sgeonw_handle_); dissector_add_uint("ieee1609dot2.psid", psid_road_and_lane_topology_service, sgeonw_handle_); dissector_add_uint("ieee1609dot2.psid", psid_infrastructure_to_vehicle_information_service, sgeonw_handle_); dissector_add_uint("ieee1609dot2.psid", psid_traffic_light_control_requests_service, sgeonw_handle_); dissector_add_uint("ieee1609dot2.psid", psid_geonetworking_management_communications, sgeonw_handle_); dissector_add_uint("ieee1609dot2.psid", psid_traffic_light_control_status_service, sgeonw_handle_); dissector_add_uint("ieee1609dot2.psid", psid_collective_perception_service, sgeonw_handle_); } /* * 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: */