diff --git a/NEWS b/NEWS index 6e236e873a..7b3fc7e987 100644 --- a/NEWS +++ b/NEWS @@ -82,7 +82,8 @@ Wireshark 3.1.1 Release Notes System Exclusive DigiTech (SYSEX DigiTech), Network Controller Sideband Interface (NCSI), NR Positioning Protocol A (NRPPa) TS 38.455, NVM Express over Fabrics for TCP (nvme-tcp), and OsmoTRX - Protocol (GSM Transceiver control and data) + Protocol (GSM Transceiver control and data), and Scalable + service-Oriented MiddlewarE over IP (SOME/IP) Updated Protocol Support diff --git a/docbook/release-notes.adoc b/docbook/release-notes.adoc index 255b7a41d1..ab46876c82 100644 --- a/docbook/release-notes.adoc +++ b/docbook/release-notes.adoc @@ -102,6 +102,7 @@ Network Controller Sideband Interface (NCSI) NR Positioning Protocol A (NRPPa) TS 38.455 NVM Express over Fabrics for TCP (nvme-tcp) OsmoTRX Protocol (GSM Transceiver control and data) +Scalable service-Oriented MiddlewarE over IP (SOME/IP) Linux net_dm (network drop monitor) protocol -- diff --git a/epan/dissectors/CMakeLists.txt b/epan/dissectors/CMakeLists.txt index f359e3e33b..b64f8b1b14 100644 --- a/epan/dissectors/CMakeLists.txt +++ b/epan/dissectors/CMakeLists.txt @@ -1727,6 +1727,8 @@ set(DISSECTOR_SRC ${CMAKE_CURRENT_SOURCE_DIR}/packet-socketcan.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-socks.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-solaredge.c + ${CMAKE_CURRENT_SOURCE_DIR}/packet-someip.c + ${CMAKE_CURRENT_SOURCE_DIR}/packet-someip-sd.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-soupbintcp.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-spdy.c ${CMAKE_CURRENT_SOURCE_DIR}/packet-spice.c diff --git a/epan/dissectors/packet-someip-sd.c b/epan/dissectors/packet-someip-sd.c new file mode 100644 index 0000000000..4e8b278799 --- /dev/null +++ b/epan/dissectors/packet-someip-sd.c @@ -0,0 +1,971 @@ +/* packet-someip.c + * SOME/IP-SD dissector. + * By Dr. Lars Voelker / + * Copyright 2012-2019 Dr. Lars Voelker + * Copyright 2019 Ana Pantar + * Copyright 2019 Guenter Ebermann + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include +#include +#include +#include +#include +#include "packet-udp.h" +#include "packet-someip.h" + + /* + * Dissector for SOME/IP Service Discovery (SOME/IP-SD). + * + * See + * http://www.some-ip.com + */ + +#define SOMEIP_SD_NAME "SOME/IP-SD" +#define SOMEIP_SD_NAME_LONG "SOME/IP Service Discovery Protocol" +#define SOMEIP_SD_NAME_FILTER "someipsd" + +#define SOMEIP_SD_MESSAGEID 0xffff8100 + + /* Header */ +#define SOMEIP_SD_REBOOT_FLAG 0x80 +#define SOMEIP_SD_UNICAST_FLAG 0x40 +#define SOMEIP_SD_EXPL_INIT_EVENT_REQ_FLAG 0x20 +#define SOMEIP_SD_MIN_LENGTH 12 + +/* Entries */ +#define SD_ENTRY_LENGTH 16 + +#define SD_ENTRY_UNKNOWN 0x00 +#define SD_ENTRY_SERVICE 0x01 +#define SD_ENTRY_EVENTGROUP 0x02 +/* TTL>0 */ +#define SD_ENTRY_FIND_SERVICE 0x00 +#define SD_ENTRY_OFFER_SERVICE 0x01 +#define SD_ENTRY_SUBSCRIBE_EVENTGROUP 0x06 +#define SD_ENTRY_SUBSCRIBE_EVENTGROUP_ACK 0x07 +/* TTL=0 */ +#define SD_ENTRY_STOP_OFFER_SERVICE 0x01 +#define SD_ENTRY_STOP_SUBSCRIBE_EVENTGROUP 0x06 +#define SD_ENTRY_SUBSCRIBE_EVENTGROUP_NACK 0x07 + +#define SD_EVENTGROUP_ENTRY_COUNTER_MASK 0x0f +#define SD_EVENTGROUP_ENTRY_RES2_MASK 0x70 +#define SD_ENTRY_INIT_EVENT_REQ_MASK 0x80 + +/* Options */ +#define SD_OPTION_MINLENGTH 3 +#define SD_OPTION_IPV4_LENGTH 12 +#define SD_OPTION_IPV6_LENGTH 24 + +#define SD_OPTION_UNKNOWN 0x00 +#define SD_OPTION_CONFIGURATION 0x01 +#define SD_OPTION_LOADBALANCING 0x02 +#define SD_OPTION_IPV4_ENDPOINT 0x04 +#define SD_OPTION_IPV6_ENDPOINT 0x06 +#define SD_OPTION_IPV4_MULTICAST 0x14 +#define SD_OPTION_IPV6_MULTICAST 0x16 +#define SD_OPTION_IPV4_SD_ENDPOINT 0x24 +#define SD_OPTION_IPV6_SD_ENDPOINT 0x26 + +#define SD_OPTION_L4PROTO_TCP 6 +#define SD_OPTION_L4PROTO_UDP 17 + +/* ID wireshark identifies the dissector by */ +static int proto_someip_sd = -1; + +/* header field */ +static int hf_someip_sd_flags = -1; +static int hf_someip_sd_rebootflag = -1; +static int hf_someip_sd_unicastflag = -1; +static int hf_someip_sd_explicitiniteventflag = -1; +static int hf_someip_sd_reserved = -1; + +static int hf_someip_sd_length_entriesarray = -1; +static int hf_someip_sd_entries = -1; + +static int hf_someip_sd_entry = -1; +static int hf_someip_sd_entry_type = -1; +static int hf_someip_sd_entry_type_offerservice = -1; +static int hf_someip_sd_entry_type_stopofferservice = -1; +static int hf_someip_sd_entry_type_findservice = -1; +static int hf_someip_sd_entry_type_subscribeeventgroup = -1; +static int hf_someip_sd_entry_type_stopsubscribeeventgroup = -1; +static int hf_someip_sd_entry_type_subscribeeventgroupack = -1; +static int hf_someip_sd_entry_type_subscribeeventgroupnack = -1; +static int hf_someip_sd_entry_index1 = -1; +static int hf_someip_sd_entry_index2 = -1; +static int hf_someip_sd_entry_numopt1 = -1; +static int hf_someip_sd_entry_numopt2 = -1; +static int hf_someip_sd_entry_serviceid = -1; +static int hf_someip_sd_entry_instanceid = -1; +static int hf_someip_sd_entry_majorver = -1; +static int hf_someip_sd_entry_ttl = -1; +static int hf_someip_sd_entry_minorver = -1; +static int hf_someip_sd_entry_eventgroupid = -1; +static int hf_someip_sd_entry_reserved = -1; +static int hf_someip_sd_entry_counter = -1; +static int hf_someip_sd_entry_intial_event_flag = -1; +static int hf_someip_sd_entry_reserved2 = -1; + +static int hf_someip_sd_length_optionsarray = -1; +static int hf_someip_sd_options = -1; + +static int hf_someip_sd_option_type = -1; +static int hf_someip_sd_option_length = -1; +static int hf_someip_sd_option_reserved = -1; +static int hf_someip_sd_option_ipv4 = -1; +static int hf_someip_sd_option_ipv6 = -1; +static int hf_someip_sd_option_port = -1; +static int hf_someip_sd_option_proto = -1; +static int hf_someip_sd_option_reserved2 = -1; +static int hf_someip_sd_option_data = -1; +static int hf_someip_sd_option_config_string = -1; +static int hf_someip_sd_option_lb_priority = -1; +static int hf_someip_sd_option_lb_weight = -1; + +/* protocol tree items */ +static gint ett_someip_sd = -1; +static gint ett_someip_sd_flags = -1; +static gint ett_someip_sd_entries = -1; +static gint ett_someip_sd_entry = -1; +static gint ett_someip_sd_options = -1; +static gint ett_someip_sd_option = -1; + +/*** Preferences ***/ +static range_t *someip_ignore_ports_udp = NULL; +static range_t *someip_ignore_ports_tcp = NULL; + +/* SOME/IP-SD Entry Names for TTL>0 */ +static const value_string sd_entry_type_positive[] = { + {SD_ENTRY_FIND_SERVICE, "Find Service"}, + {SD_ENTRY_OFFER_SERVICE, "Offer Service"}, + {SD_ENTRY_SUBSCRIBE_EVENTGROUP, "Subscribe Eventgroup"}, + {SD_ENTRY_SUBSCRIBE_EVENTGROUP_ACK, "Subscribe Eventgroup Ack"}, + {0, NULL} +}; + +/* SOME/IP-SD Entry Names for TTL=0 */ +static const value_string sd_entry_type_negative[] = { + {SD_ENTRY_STOP_OFFER_SERVICE, "Stop Offer Service"}, + {SD_ENTRY_STOP_SUBSCRIBE_EVENTGROUP, "Stop Subscribe Eventgroup"}, + {SD_ENTRY_SUBSCRIBE_EVENTGROUP_NACK, "Subscribe Eventgroup Negative Ack"}, + {0, NULL} +}; + +/* SOME/IP-SD Option Names */ +static const value_string sd_option_type[] = { + {SD_OPTION_UNKNOWN, "Unknown"}, + {SD_OPTION_CONFIGURATION, "Configuration"}, + {SD_OPTION_LOADBALANCING, "Load Balancing"}, + {SD_OPTION_IPV4_ENDPOINT, "IPv4 Endpoint"}, + {SD_OPTION_IPV6_ENDPOINT, "IPv6 Endpoint"}, + {SD_OPTION_IPV4_MULTICAST, "IPv4 Multicast"}, + {SD_OPTION_IPV6_MULTICAST, "IPv6 Multicast"}, + {SD_OPTION_IPV4_SD_ENDPOINT, "IPv4 SD Endpoint"}, + {SD_OPTION_IPV6_SD_ENDPOINT, "IPv6 SD Endpoint"}, + {0, NULL} +}; + +/* L4 Protocol Names for SOME/IP-SD Endpoints */ +static const value_string sd_option_l4protos[] = { + {SD_OPTION_L4PROTO_TCP, "TCP"}, + {SD_OPTION_L4PROTO_UDP, "UDP"}, + {0, NULL} +}; + +static const true_false_string sd_reboot_flag = { + "Session ID did not roll over since last reboot", + "Session ID rolled over since last reboot" +}; + +static const true_false_string sd_unicast_flag = { + "Unicast messages support", + "Unicast messages not supported (deprecated)" +}; + +static const true_false_string sd_eiec_flag = { + "Explicit Initial Event control supported", + "Explicit Initial Event control not supported" +}; + +/*** expert info items ***/ +static expert_field ef_someipsd_message_truncated = EI_INIT; +static expert_field ef_someipsd_entry_array_malformed = EI_INIT; +static expert_field ef_someipsd_entry_array_empty = EI_INIT; +static expert_field ef_someipsd_entry_unknown = EI_INIT; +static expert_field ef_someipsd_option_array_truncated = EI_INIT; +static expert_field ef_someipsd_option_array_bytes_left = EI_INIT; +static expert_field ef_someipsd_option_unknown = EI_INIT; +static expert_field ef_someipsd_option_wrong_length = EI_INIT; +static expert_field ef_someipsd_L4_protocol_unsupported = EI_INIT; + +/*** prototypes ***/ +void proto_register_someip_sd(void); +void proto_reg_handoff_someip_sd(void); + +/************************************** + ******** SOME/IP-SD Dissector ******** + *************************************/ + +static void +dissect_someip_sd_pdu_option_configuration(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint32 offset, guint32 length, int optionnum) { + guint32 offset_orig = offset; + + tree = proto_tree_add_subtree_format(tree, tvb, offset, length, ett_someip_sd_option, NULL, "%d: Configuration Option", optionnum); + + /* Add common fields */ + proto_tree_add_item(tree, hf_someip_sd_option_length, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + + proto_tree_add_item(tree, hf_someip_sd_option_type, tvb, offset, 1, ENC_NA); + offset += 1; + + proto_tree_add_item(tree, hf_someip_sd_option_reserved, tvb, offset, 1, ENC_NA); + offset += 1; + + proto_tree_add_item(tree, hf_someip_sd_option_config_string, tvb, offset, length - offset + offset_orig, ENC_ASCII|ENC_NA); + + /* TODO: splitting the config string would be nice... does DNS have code for this? */ +} + +static void +dissect_someip_sd_pdu_option_loadbalancing(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, guint32 offset, guint32 length, int optionnum) { + tree = proto_tree_add_subtree_format(tree, tvb, offset, length, ett_someip_sd_option, NULL, "%d: Load Balancing Option", optionnum); + + /* Add common fields */ + proto_tree_add_item(tree, hf_someip_sd_option_length, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + + proto_tree_add_item(tree, hf_someip_sd_option_type, tvb, offset, 1, ENC_NA); + offset += 1; + + proto_tree_add_item(tree, hf_someip_sd_option_reserved, tvb, offset, 1, ENC_NA); + offset += 1; + + proto_tree_add_item(tree, hf_someip_sd_option_lb_priority, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + + proto_tree_add_item(tree, hf_someip_sd_option_lb_weight, tvb, offset, 2, ENC_BIG_ENDIAN); +} + +static void +dissect_someip_sd_pdu_option_ipv4(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset, guint32 length, int optionnum) { + guint8 type = 255; + const gchar *description = NULL; + guint32 l4port = 0; + guint32 l4proto = 0; + const gchar *l4protoname = NULL; + const gchar *ipstring = NULL; + + proto_item *ti = NULL; + proto_item *ti_top = NULL; + + type = tvb_get_guint8(tvb, offset + 2); + description = val_to_str(type, sd_option_type, "(Unknown Option: %d)"); + tree = proto_tree_add_subtree_format(tree, tvb, offset, length, ett_someip_sd_option, &ti_top, "%d: %s Option", optionnum, description); + + if (length != SD_OPTION_IPV4_LENGTH) { + expert_add_info(pinfo, ti_top, &ef_someipsd_option_wrong_length); + return; + } + + /* Add common fields */ + proto_tree_add_item(tree, hf_someip_sd_option_length, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + + proto_tree_add_item(tree, hf_someip_sd_option_type, tvb, offset, 1, ENC_NA); + offset += 1; + + proto_tree_add_item(tree, hf_someip_sd_option_reserved, tvb, offset, 1, ENC_NA); + offset += 1; + + proto_tree_add_item(tree, hf_someip_sd_option_ipv4, tvb, offset, 4, ENC_NA); + ipstring = tvb_ip_to_str(tvb, offset); + offset += 4; + + proto_tree_add_item(tree, hf_someip_sd_option_reserved2, tvb, offset, 1, ENC_NA); + offset += 1; + + ti = proto_tree_add_item_ret_uint(tree, hf_someip_sd_option_proto, tvb, offset, 1, ENC_NA, &l4proto); + l4protoname = val_to_str(l4proto, sd_option_l4protos, "Unknown Transport Protocol: %d"); + proto_item_append_text(ti, " (%s)", l4protoname); + + if (type != SD_OPTION_IPV4_ENDPOINT && l4proto == SD_OPTION_L4PROTO_TCP) { + expert_add_info(pinfo, ti_top, &ef_someipsd_L4_protocol_unsupported); + } + offset += 1; + + proto_tree_add_item_ret_uint(tree, hf_someip_sd_option_port, tvb, offset, 2, ENC_BIG_ENDIAN, &l4port); + + proto_item_append_text(ti_top, " (%s:%d (%s))", ipstring, l4port, l4protoname); + + if (l4proto == SD_OPTION_L4PROTO_UDP && !value_is_in_range(someip_ignore_ports_udp, l4port)) { + register_someip_port_udp(l4port); + } + if (l4proto == SD_OPTION_L4PROTO_TCP && !value_is_in_range(someip_ignore_ports_tcp, l4port)) { + register_someip_port_tcp(l4port); + } +} + +static void +dissect_someip_sd_pdu_option_ipv6(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset, guint32 length, int optionnum) { + guint8 type = 255; + const gchar *description = NULL; + guint32 l4port = 0; + guint32 l4proto = 0; + const gchar *l4protoname = NULL; + proto_item *ti = NULL; + proto_item *ti_top = NULL; + + type = tvb_get_guint8(tvb, offset + 2); + description = val_to_str(type, sd_option_type, "(Unknown Option: %d)"); + + tree = proto_tree_add_subtree_format(tree, tvb, offset, length, ett_someip_sd_option, &ti_top, "%d: %s Option", optionnum, description); + + if (length != SD_OPTION_IPV6_LENGTH) { + expert_add_info(pinfo, ti_top, &ef_someipsd_option_wrong_length); + return; + } + + proto_tree_add_item(tree, hf_someip_sd_option_length, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + + proto_tree_add_item(tree, hf_someip_sd_option_type, tvb, offset, 1, ENC_NA); + offset += 1; + + proto_tree_add_item(tree, hf_someip_sd_option_reserved, tvb, offset, 1, ENC_NA); + offset += 1; + + proto_tree_add_item(tree, hf_someip_sd_option_ipv6, tvb, offset, 16, ENC_NA); + offset += 16; + + proto_tree_add_item(tree, hf_someip_sd_option_reserved2, tvb, offset, 1, ENC_NA); + offset += 1; + + ti = proto_tree_add_item_ret_uint(tree, hf_someip_sd_option_proto, tvb, offset, 1, ENC_NA, &l4proto); + l4protoname = val_to_str(l4proto, sd_option_l4protos, "(Unknown Transport Protocol: %d)"); + proto_item_append_text(ti, " (%s)", l4protoname); + + if (type != SD_OPTION_IPV6_ENDPOINT && l4proto == SD_OPTION_L4PROTO_TCP) { + expert_add_info(pinfo, ti_top, &ef_someipsd_L4_protocol_unsupported); + } + offset += 1; + + proto_tree_add_item(tree, hf_someip_sd_option_port, tvb, offset, 2, ENC_BIG_ENDIAN); + + proto_item_append_text(ti_top, " (%s)", l4protoname); + + if (l4proto == SD_OPTION_L4PROTO_UDP && !value_is_in_range(someip_ignore_ports_udp, l4port)) { + register_someip_port_udp(l4port); + } + if (l4proto == SD_OPTION_L4PROTO_TCP && !value_is_in_range(someip_ignore_ports_tcp, l4port)) { + register_someip_port_tcp(l4port); + } +} + +static void +dissect_someip_sd_pdu_option_unknown(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset, guint32 length, int optionnum) { + guint32 len = 0; + proto_item *ti; + + tree = proto_tree_add_subtree_format(tree, tvb, offset, length, ett_someip_sd_option, &ti, "%d: %s Option", optionnum, + val_to_str_const(tvb_get_guint8(tvb, offset + 2), sd_option_type, "Unknown")); + + expert_add_info(pinfo, ti, &ef_someipsd_option_unknown); + + proto_tree_add_item_ret_uint(tree, hf_someip_sd_option_length, tvb, offset, 2, ENC_BIG_ENDIAN, &len); + offset += 2; + + proto_tree_add_item(tree, hf_someip_sd_option_type, tvb, offset, 1, ENC_NA); + offset += 1; + + if (length > 3) { + proto_tree_add_item(tree, hf_someip_sd_option_reserved, tvb, offset, 1, ENC_NA); + offset += 1; + + if (length > 4) { + proto_tree_add_item(tree, hf_someip_sd_option_data, tvb, offset, length - 4, ENC_NA); + } + } +} + +static int +dissect_someip_sd_pdu_options(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_item *ti, guint32 offset, guint32 length) { + guint16 real_length = 0; + guint8 option_type = 0; + int optionnum = 0; + tvbuff_t *subtvb = NULL; + + if (!tvb_bytes_exist(tvb, offset, SD_OPTION_MINLENGTH) || !tvb_bytes_exist(tvb, offset, length)) { + expert_add_info(pinfo, ti, &ef_someipsd_option_array_truncated); + return offset; + } + + while (tvb_bytes_exist(tvb, offset, SD_OPTION_MINLENGTH)) { + real_length = tvb_get_ntohs(tvb, offset) + 3; + option_type = tvb_get_guint8(tvb, offset + 2); + + if (!tvb_bytes_exist(tvb, offset, (gint)real_length)) { + expert_add_info(pinfo, ti, &ef_someipsd_option_array_truncated); + return offset; + } + + subtvb = tvb_new_subset_length(tvb, offset, (gint)real_length); + + switch (option_type) { + case SD_OPTION_CONFIGURATION: + dissect_someip_sd_pdu_option_configuration(subtvb, pinfo, tree, 0, real_length, optionnum); + break; + case SD_OPTION_LOADBALANCING: + dissect_someip_sd_pdu_option_loadbalancing(subtvb, pinfo, tree, 0, real_length, optionnum); + break; + case SD_OPTION_IPV4_ENDPOINT: + case SD_OPTION_IPV4_MULTICAST: + case SD_OPTION_IPV4_SD_ENDPOINT: + dissect_someip_sd_pdu_option_ipv4(subtvb, pinfo, tree, 0, real_length, optionnum); + break; + + case SD_OPTION_IPV6_ENDPOINT: + case SD_OPTION_IPV6_MULTICAST: + case SD_OPTION_IPV6_SD_ENDPOINT: + dissect_someip_sd_pdu_option_ipv6(subtvb, pinfo, tree, 0, real_length, optionnum); + break; + + default: + dissect_someip_sd_pdu_option_unknown(subtvb, pinfo, tree, 0, real_length, optionnum); + break; + } + optionnum++; + offset += real_length; + } + + return offset; +} + +static void +dissect_someip_sd_pdu_entry(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset, guint32 length _U_, guint32 *entry_flags, guint32 *stop_entry_flags) { + guint8 type = 255; + guint32 serviceid = 0; + guint32 instanceid = 0; + guint32 eventgroupid = 0; + guint32 majorver = 0; + guint32 minorver = 0; + guint32 ttl = 0; + + guint32 uniqueid = 0; + guint8 category = SD_ENTRY_UNKNOWN; + + const gchar *description = NULL; + + proto_item *ti; + proto_item *ti_top; + + if (length < SD_ENTRY_LENGTH || !tvb_bytes_exist(tvb, offset, length)) { + return; + } + + /* lets look ahead and find out the type and ttl */ + type = tvb_get_guint8(tvb, offset); + ttl = tvb_get_ntoh24(tvb, offset + 9); + + if (type < 4) { + category = SD_ENTRY_SERVICE; + } else if (type >= 4 && type < 8) { + category = SD_ENTRY_EVENTGROUP; + } else { + ti_top = proto_tree_add_none_format(tree, hf_someip_sd_entry, tvb, offset, SD_ENTRY_LENGTH, "Unknown Entry (Type: %d)", type); + expert_add_info(pinfo, ti_top, &ef_someipsd_entry_unknown); + return; + } + + if (ttl == 0) { + description = val_to_str(type, sd_entry_type_negative, "(Unknown Entry: %d)"); + } else { + description = val_to_str(type, sd_entry_type_positive, "(Unknown Entry: %d)"); + } + + ti_top = proto_tree_add_none_format(tree, hf_someip_sd_entry, tvb, offset, SD_ENTRY_LENGTH, "%s Entry", description); + tree = proto_item_add_subtree(ti_top, ett_someip_sd_entry); + + proto_tree_add_uint_format_value(tree, hf_someip_sd_entry_type, tvb, offset, 1, type, "0x%02x (%s)", type, description); + offset += 1; + + proto_tree_add_item(tree, hf_someip_sd_entry_index1, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + proto_tree_add_item(tree, hf_someip_sd_entry_index2, tvb, offset, 1, ENC_BIG_ENDIAN); + offset += 1; + + proto_tree_add_item(tree, hf_someip_sd_entry_numopt1, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_someip_sd_entry_numopt2, tvb, offset, 1, ENC_NA); + offset += 1; + + ti = proto_tree_add_item_ret_uint(tree, hf_someip_sd_entry_serviceid, tvb, offset, 2, ENC_BIG_ENDIAN, &serviceid); + description = someip_lookup_service_name((guint16)serviceid); + if (description != NULL) { + proto_item_append_text(ti, " (%s)", description); + } + offset += 2; + + proto_tree_add_item_ret_uint(tree, hf_someip_sd_entry_instanceid, tvb, offset, 2, ENC_BIG_ENDIAN, &instanceid); + offset += 2; + + proto_tree_add_item_ret_uint(tree, hf_someip_sd_entry_majorver, tvb, offset, 1, ENC_BIG_ENDIAN, &majorver); + offset += 1; + + proto_tree_add_item(tree, hf_someip_sd_entry_ttl, tvb, offset, 3, ENC_BIG_ENDIAN); + offset += 3; + + /* Add specific fields - i.e. the last line */ + if (category == SD_ENTRY_SERVICE) { + proto_tree_add_item_ret_uint(tree, hf_someip_sd_entry_minorver, tvb, offset, 4, ENC_BIG_ENDIAN, &minorver); + offset += 4; + + proto_item_append_text(ti_top, " (Service ID 0x%04x, Instance ID 0x%04x, Version %u.%u)", serviceid, instanceid, majorver, minorver); + } else if (category == SD_ENTRY_EVENTGROUP) { + proto_tree_add_item(tree, hf_someip_sd_entry_reserved, tvb, offset, 1, ENC_NA); + offset += 1; + + proto_tree_add_item(tree, hf_someip_sd_entry_intial_event_flag, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_someip_sd_entry_reserved2, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_someip_sd_entry_counter, tvb, offset, 1, ENC_NA); + offset += 1; + + ti = proto_tree_add_item_ret_uint(tree, hf_someip_sd_entry_eventgroupid, tvb, offset, 2, ENC_BIG_ENDIAN, &eventgroupid); + description = someip_lookup_eventgroup_name((guint16)serviceid, (guint16)eventgroupid); + if (description != NULL) { + proto_item_append_text(ti, " (%s)", description); + } + offset += 2; + + proto_item_append_text(ti_top, " (Service ID 0x%04x, Instance ID 0x%04x, Eventgroup ID 0x%04x, Version %u)", serviceid, instanceid, eventgroupid, majorver); + } + + /* mark for attaching to info column */ + if (type < 32) { + if (ttl == 0) { + *stop_entry_flags = *stop_entry_flags | (1 << type); + } else { + *entry_flags = *entry_flags | (1 << type); + } + } + + /* lets add some combined filtering term */ + uniqueid = (((guint32)serviceid) << 16) | instanceid; + + ti = NULL; + if (ttl > 0) { + switch (type) { + case SD_ENTRY_FIND_SERVICE: + ti = proto_tree_add_uint_format_value(tree, hf_someip_sd_entry_type_findservice, tvb, offset, SD_ENTRY_LENGTH, uniqueid, "on 0x%08x", uniqueid); + break; + case SD_ENTRY_OFFER_SERVICE: + ti = proto_tree_add_uint_format_value(tree, hf_someip_sd_entry_type_offerservice, tvb, offset, SD_ENTRY_LENGTH, uniqueid, "on 0x%08x", uniqueid); + break; + case SD_ENTRY_SUBSCRIBE_EVENTGROUP: + ti = proto_tree_add_uint_format_value(tree, hf_someip_sd_entry_type_subscribeeventgroup, tvb, offset, SD_ENTRY_LENGTH, uniqueid, "on 0x%08x", uniqueid); + break; + case SD_ENTRY_SUBSCRIBE_EVENTGROUP_ACK: + ti = proto_tree_add_uint_format_value(tree, hf_someip_sd_entry_type_subscribeeventgroupack, tvb, offset, SD_ENTRY_LENGTH, uniqueid, "on 0x%08x", uniqueid); + break; + } + } else { + switch (type) { + case SD_ENTRY_STOP_OFFER_SERVICE: + ti = proto_tree_add_uint_format_value(tree, hf_someip_sd_entry_type_stopofferservice, tvb, offset, SD_ENTRY_LENGTH, uniqueid, "on 0x%08x", uniqueid); + break; + case SD_ENTRY_STOP_SUBSCRIBE_EVENTGROUP: + ti = proto_tree_add_uint_format_value(tree, hf_someip_sd_entry_type_stopsubscribeeventgroup, tvb, offset, SD_ENTRY_LENGTH, uniqueid, "on 0x%08x", uniqueid); + break; + case SD_ENTRY_SUBSCRIBE_EVENTGROUP_NACK: + ti = proto_tree_add_uint_format_value(tree, hf_someip_sd_entry_type_subscribeeventgroupnack, tvb, offset, SD_ENTRY_LENGTH, uniqueid, "on 0x%08x", uniqueid); + break; + } + } + + if (ti != NULL) { + PROTO_ITEM_SET_HIDDEN(ti); + } +} + +static int +dissect_someip_sd_pdu_entries(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_item *ti, guint32 offset, guint32 length) { + guint32 entry_flags = 0; + guint32 stop_entry_flags = 0; + + while (length >= SD_ENTRY_LENGTH) { + dissect_someip_sd_pdu_entry(tvb, pinfo, tree, offset, SD_ENTRY_LENGTH, &entry_flags, &stop_entry_flags); + offset += SD_ENTRY_LENGTH; + length -= SD_ENTRY_LENGTH; + } + + /* Add entry flags */ + if (stop_entry_flags != 0 || entry_flags != 0) { + col_append_str(pinfo->cinfo, COL_INFO, " "); + } + + if (entry_flags & (1 << SD_ENTRY_FIND_SERVICE)) { + col_append_str(pinfo->cinfo, COL_INFO, "[Find]"); + } + + if (stop_entry_flags & (1 << SD_ENTRY_OFFER_SERVICE)) { + col_append_str(pinfo->cinfo, COL_INFO, "[StopOffer]"); + } + + if (entry_flags & (1 << SD_ENTRY_OFFER_SERVICE)) { + col_append_str(pinfo->cinfo, COL_INFO, "[Offer]"); + } + + if (stop_entry_flags & (1 << SD_ENTRY_SUBSCRIBE_EVENTGROUP)) { + col_append_str(pinfo->cinfo, COL_INFO, "[StopSubscribe]"); + } + + if (entry_flags & (1 << SD_ENTRY_SUBSCRIBE_EVENTGROUP)) { + col_append_str(pinfo->cinfo, COL_INFO, "[Subscribe]"); + } + + if (stop_entry_flags & (1 << SD_ENTRY_SUBSCRIBE_EVENTGROUP_ACK)) { + col_append_str(pinfo->cinfo, COL_INFO, "[SubscribeNack]"); + } + + if (entry_flags & (1 << SD_ENTRY_SUBSCRIBE_EVENTGROUP_ACK)) { + col_append_str(pinfo->cinfo, COL_INFO, "[SubscribeAck]"); + } + + if (length != 0) { + expert_add_info(pinfo, ti, &ef_someipsd_entry_array_malformed); + } + + return length; +} + +static int +dissect_someip_sd_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { + guint32 offset = 0; + guint32 length_entriesarray = 0; + guint32 length_optionsarray = 0; + + proto_item *ti = NULL; + proto_item *ti_sd_entries = NULL; + + proto_tree *someip_sd_entries_tree = NULL; + proto_tree *someip_sd_options_tree = NULL; + gboolean stop_parsing_after_entries = FALSE; + guint32 offset_entriesarray; + + static const int * someipsd_flags[] = { + &hf_someip_sd_rebootflag, + &hf_someip_sd_unicastflag, + &hf_someip_sd_explicitiniteventflag, + NULL + }; + + col_set_str(pinfo->cinfo, COL_PROTOCOL, SOMEIP_SD_NAME); + col_set_str(pinfo->cinfo, COL_INFO, SOMEIP_SD_NAME_LONG); + + ti = proto_tree_add_item(tree, proto_someip_sd, tvb, offset, -1, ENC_NA); + tree = proto_item_add_subtree(ti, ett_someip_sd); + + if (!tvb_bytes_exist(tvb, offset, SOMEIP_SD_MIN_LENGTH)) { + expert_add_info(pinfo, ti, &ef_someipsd_message_truncated); + return tvb_reported_length(tvb); + } + + /* add flags */ + proto_tree_add_bitmask(tree, tvb, offset, hf_someip_sd_flags, ett_someip_sd_flags, someipsd_flags, ENC_BIG_ENDIAN); + offset += 1; + + /* add reserved */ + proto_tree_add_item(tree, hf_someip_sd_reserved, tvb, offset, 3, ENC_BIG_ENDIAN); + offset += 3; + + /* add length of entries */ + proto_tree_add_item_ret_uint(tree, hf_someip_sd_length_entriesarray, tvb, offset, 4, ENC_BIG_ENDIAN, &length_entriesarray); + offset += 4; + + if (!tvb_bytes_exist(tvb, offset, length_entriesarray)) { + expert_add_info(pinfo, ti , &ef_someipsd_message_truncated); + return tvb_reported_length(tvb); + } + + if (!tvb_bytes_exist(tvb, offset, length_entriesarray)) { + /* truncated SD message - need to shorten buffer */ + length_entriesarray = tvb_captured_length_remaining(tvb, offset); + expert_add_info(pinfo, ti, &ef_someipsd_message_truncated); + stop_parsing_after_entries = TRUE; + } + + /* preparing entries array but not parsing it yet */ + ti_sd_entries = proto_tree_add_item(tree, hf_someip_sd_entries, tvb, offset, length_entriesarray, ENC_NA); + someip_sd_entries_tree = proto_item_add_subtree(ti_sd_entries, ett_someip_sd_entries); + /* save offset to parse entries later since we need to parse options first */ + offset_entriesarray = offset; + offset += length_entriesarray; + + if (!stop_parsing_after_entries) { + /* make sure we have a length field */ + if (tvb_bytes_exist(tvb, offset, 4)) { + + /* add options length */ + proto_tree_add_item_ret_uint(tree, hf_someip_sd_length_optionsarray, tvb, offset, 4, ENC_BIG_ENDIAN, &length_optionsarray); + offset += 4; + + if (length_optionsarray > 0) { + if (tvb_bytes_exist(tvb, offset, 1)) { + ti = proto_tree_add_item(tree, hf_someip_sd_options, tvb, offset, -1, ENC_NA); + someip_sd_options_tree = proto_item_add_subtree(ti, ett_someip_sd_options); + + /* check, if enough bytes are left for optionsarray */ + if (!tvb_bytes_exist(tvb, offset, length_optionsarray)) { + length_optionsarray = tvb_captured_length_remaining(tvb, offset); + expert_add_info(pinfo, ti, &ef_someipsd_message_truncated); + proto_item_append_text(ti, " (truncated!)"); + } + + /* updating to length we will work with */ + if (length_optionsarray > 0) { + proto_item_set_len(ti, length_optionsarray); + } + + dissect_someip_sd_pdu_options(tvb, pinfo, someip_sd_options_tree, ti, offset, length_optionsarray); + offset += length_optionsarray; + } else { + expert_add_info(pinfo, ti, &ef_someipsd_message_truncated); + } + } + } + } + + /* Possible improvement: it would be nice, if entries referenced options. */ + if (length_entriesarray >= SD_ENTRY_LENGTH) { + offset += dissect_someip_sd_pdu_entries(tvb, pinfo, someip_sd_entries_tree, ti_sd_entries, offset_entriesarray, length_entriesarray); + } else { + expert_add_info(pinfo, ti_sd_entries, &ef_someipsd_entry_array_empty); + } + return offset; +} + +void +proto_register_someip_sd(void) { + module_t *someipsd_module; + + expert_module_t *expert_module_someip_sd; + + /* data fields */ + static hf_register_info hf_sd[] = { + { &hf_someip_sd_flags, + { "Flags", "someipsd.flags", + FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_rebootflag, + { "Reboot Flag", "someipsd.flags.reboot", + FT_BOOLEAN, 8, TFS(&sd_reboot_flag), SOMEIP_SD_REBOOT_FLAG, NULL, HFILL }}, + { &hf_someip_sd_unicastflag, + { "Unicast Flag", "someipsd.flags.unicast", + FT_BOOLEAN, 8, TFS(&sd_unicast_flag), SOMEIP_SD_UNICAST_FLAG, NULL, HFILL }}, + { &hf_someip_sd_explicitiniteventflag, + { "Explicit Initial Events Flag", "someipsd.flags.exp_init_events", + FT_BOOLEAN, 8, TFS(&sd_eiec_flag), SOMEIP_SD_EXPL_INIT_EVENT_REQ_FLAG, NULL, HFILL }}, + { &hf_someip_sd_reserved, + { "Reserved", "someipsd.reserved", + FT_UINT24, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + + { &hf_someip_sd_length_entriesarray, + { "Length of Entries Array", "someipsd.length_entriesarray", + FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_entries, + { "Entries Array", "someipsd.entries", + FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_entry, + { "Entry", "someipsd.entry", + FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_entry_type, + { "Type", "someipsd.entry.type", + FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_entry_index1, + { "Index 1", "someipsd.entry.index1", + FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_entry_index2, + { "Index 2", "someipsd.entry.index2", + FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_entry_numopt1, + { "Number of Opts 1", "someipsd.entry.numopt1", + FT_UINT8, BASE_HEX, NULL, 0xf0, NULL, HFILL }}, + { &hf_someip_sd_entry_numopt2, + { "Number of Opts 2", "someipsd.entry.numopt2", + FT_UINT8, BASE_HEX, NULL, 0x0f, NULL, HFILL }}, + + { &hf_someip_sd_entry_serviceid, + { "Service ID", "someipsd.entry.serviceid", + FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_entry_instanceid, + { "Instance ID", "someipsd.entry.instanceid", + FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_entry_majorver, + { "Major Version", "someipsd.entry.majorver", + FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_entry_ttl, + { "TTL", "someipsd.entry.ttl", + FT_UINT24, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_entry_minorver, + { "Minor Version", "someipsd.entry.minorver", + FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_entry_eventgroupid, + { "Eventgroup ID", "someipsd.entry.eventgroupid", + FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_entry_reserved, + { "Reserved", "someipsd.entry.reserved", + FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } }, + { &hf_someip_sd_entry_counter, + { "Counter", "someipsd.entry.counter", + FT_UINT8, BASE_HEX, NULL, SD_EVENTGROUP_ENTRY_COUNTER_MASK, NULL, HFILL } }, + { &hf_someip_sd_entry_reserved2, + { "Reserved", "someipsd.entry.reserved2", + FT_UINT8, BASE_HEX, NULL, SD_EVENTGROUP_ENTRY_RES2_MASK, NULL, HFILL } }, + { &hf_someip_sd_entry_intial_event_flag, + { "Initial Event Request", "someipsd.entry.initialevents", + FT_BOOLEAN, 8, NULL, SD_ENTRY_INIT_EVENT_REQ_MASK, NULL, HFILL } }, + + { &hf_someip_sd_length_optionsarray, + { "Length of Options Array", "someipsd.length_optionsarray", + FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_options, + { "Options Array", "someipsd.options", + FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + + { &hf_someip_sd_option_type, + { "Type", "someipsd.option.type", + FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_option_length, + { "Length", "someipsd.option.length", + FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_option_reserved, + { "Reserved", "someipsd.option.reserved", + FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_option_ipv4, + { "IPv4 Address", "someipsd.option.ipv4address", + FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_option_ipv6, + { "IPv6 Address", "someipsd.option.ipv6address", + FT_IPv6, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_option_port, + { "Port", "someipsd.option.port", + FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_option_proto, + { "Protocol", "someipsd.option.proto", + FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_option_reserved2, + { "Reserved 2", "someipsd.option.reserved2", + FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } }, + { &hf_someip_sd_option_data, + { "Unknown Data", "someipsd.option.unknown_data", + FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } }, + { &hf_someip_sd_option_config_string, + { "Configuration String", "someipsd.option.config_string", + FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, + { &hf_someip_sd_option_lb_priority, + { "Priority", "someipsd.option.priority", + FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } }, + { &hf_someip_sd_option_lb_weight, + { "Weight", "someipsd.option.weight", + FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } }, + { &hf_someip_sd_entry_type_offerservice, + { "Offer Service", "someipsd.entry.offerservice", + FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_entry_type_stopofferservice, + { "Stop Offer Service", "someipsd.entry.stopofferservice", + FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_entry_type_findservice, + { "Find Service", "someipsd.entry.findservice", + FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + + { &hf_someip_sd_entry_type_subscribeeventgroup, + { "Subscribe Eventgroup", "someipsd.entry.subscribeeventgroup", + FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_entry_type_stopsubscribeeventgroup, + { "Stop Subscribe Eventgroup", "someipsd.entry.stopsubscribeeventgroup", + FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + + { &hf_someip_sd_entry_type_subscribeeventgroupack, + { "Subscribe Eventgroup ACK", "someipsd.entry.subscribeeventgroupack", + FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sd_entry_type_subscribeeventgroupnack, + { "Subscribe Eventgroup NACK", "someipsd.entry.subscribeeventgroupnack", + FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + }; + + static gint *ett_sd[] = { + &ett_someip_sd, + &ett_someip_sd_flags, + &ett_someip_sd_entries, + &ett_someip_sd_entry, + &ett_someip_sd_options, + &ett_someip_sd_option, + }; + + static ei_register_info ei_sd[] = { + { &ef_someipsd_message_truncated,{ "someipsd.message_truncated", PI_MALFORMED, PI_ERROR, "SOME/IP-SD Truncated message!", EXPFILL } }, + { &ef_someipsd_entry_array_malformed,{ "someipsd.entry_array_malformed", PI_MALFORMED, PI_ERROR, "SOME/IP-SD Entry Array length not multiple of 16 bytes!", EXPFILL } }, + { &ef_someipsd_entry_array_empty,{ "someipsd.entry_array_empty", PI_MALFORMED, PI_ERROR, "SOME/IP-SD Empty Entry Array!", EXPFILL } }, + { &ef_someipsd_entry_unknown,{ "someipsd.entry_unknown", PI_MALFORMED, PI_WARN, "SOME/IP-SD Unknown Entry!", EXPFILL } }, + { &ef_someipsd_option_array_truncated,{ "someipsd.option_array_truncated", PI_MALFORMED, PI_ERROR, "SOME/IP-SD Option Array truncated!", EXPFILL } }, + { &ef_someipsd_option_array_bytes_left,{ "someipsd.option_array_bytes_left", PI_MALFORMED, PI_WARN, "SOME/IP-SD Option Array bytes left after parsing options!", EXPFILL } }, + { &ef_someipsd_option_unknown,{ "someipsd.option_unknown", PI_MALFORMED, PI_WARN, "SOME/IP-SD Unknown Option!", EXPFILL } }, + { &ef_someipsd_option_wrong_length,{ "someipsd.option_wrong_length", PI_MALFORMED, PI_ERROR, "SOME/IP-SD Option length is incorrect!", EXPFILL } }, + { &ef_someipsd_L4_protocol_unsupported,{ "someipsd.L4_protocol_unsupported", PI_MALFORMED, PI_ERROR, "SOME/IP-SD Unsupported Layer 4 Protocol!", EXPFILL } }, + }; + + /* Register ETTs */ + proto_someip_sd = proto_register_protocol(SOMEIP_SD_NAME_LONG, SOMEIP_SD_NAME, SOMEIP_SD_NAME_FILTER); + proto_register_field_array(proto_someip_sd, hf_sd, array_length(hf_sd)); + proto_register_subtree_array(ett_sd, array_length(ett_sd)); + expert_module_someip_sd = expert_register_protocol(proto_someip_sd); + expert_register_field_array(expert_module_someip_sd, ei_sd, array_length(ei_sd)); + + /* Register preferences */ + someipsd_module = prefs_register_protocol(proto_someip_sd, &proto_reg_handoff_someip_sd); + + range_convert_str(wmem_epan_scope(), &someip_ignore_ports_udp, "", 65535); + prefs_register_range_preference(someipsd_module, "ports.udp.ignore", "UDP Ports ignored", + "SOME/IP Ignore Port Ranges UDP. These ports are not automatically added by the SOME/IP-SD.", + &someip_ignore_ports_udp, 65535); + + range_convert_str(wmem_epan_scope(), &someip_ignore_ports_tcp, "", 65535); + prefs_register_range_preference(someipsd_module, "ports.tcp.ignore", "TCP Ports ignored", + "SOME/IP Ignore Port Ranges TCP. These ports are not automatically added by the SOME/IP-SD.", + &someip_ignore_ports_tcp, 65535); + } + +void +proto_reg_handoff_someip_sd(void) { + static gboolean initialized = FALSE; + static dissector_handle_t someip_sd_handle = NULL; + + if (!initialized) { + someip_sd_handle = create_dissector_handle(dissect_someip_sd_pdu, proto_someip_sd); + dissector_add_uint("someip.messageid", SOMEIP_SD_MESSAGEID, someip_sd_handle); + initialized = TRUE; + } + + /* nothing to do here right now */ +} + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/epan/dissectors/packet-someip.c b/epan/dissectors/packet-someip.c new file mode 100644 index 0000000000..9bb4d0febc --- /dev/null +++ b/epan/dissectors/packet-someip.c @@ -0,0 +1,3325 @@ +/* packet-someip.c + * SOME/IP dissector. + * By Dr. Lars Voelker / + * Copyright 2012-2019 Dr. Lars Voelker + * Copyright 2019 Ana Pantar + * Copyright 2019 Guenter Ebermann + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include "packet-udp.h" +#include "packet-someip.h" + + /* + * Dissector for SOME/IP, SOME/IP-TP, and SOME/IP Payloads. + * + * See + * http://www.some-ip.com + */ + +#define SOMEIP_NAME "SOME/IP" +#define SOMEIP_NAME_LONG "SOME/IP Protocol" +#define SOMEIP_NAME_FILTER "someip" + +#define SOMEIP_NAME_LONG_MULTIPLE "SOME/IP Protocol (Multiple Payloads)" +#define SOMEIP_NAME_LONG_BROKEN "SOME/IP: Incomplete headers!" +#define SOMEIP_NAME_LONG_TOO_SHORT "SOME/IP: Incomplete SOME/IP payload!" + + /*** Configuration ***/ +#define DATAFILE_SOMEIP_SERVICES "SOMEIP_service_identifiers" +#define DATAFILE_SOMEIP_METHODS "SOMEIP_method_event_identifiers" +#define DATAFILE_SOMEIP_EVENTGROUPS "SOMEIP_eventgroup_identifiers" + +#define DATAFILE_SOMEIP_PARAMETERS "SOMEIP_parameter_list" +#define DATAFILE_SOMEIP_BASE_TYPES "SOMEIP_parameter_base_types" +#define DATAFILE_SOMEIP_ARRAYS "SOMEIP_parameter_arrays" +#define DATAFILE_SOMEIP_STRINGS "SOMEIP_parameter_strings" +#define DATAFILE_SOMEIP_TYPEDEFS "SOMEIP_parameter_typedefs" +#define DATAFILE_SOMEIP_STRUCTS "SOMEIP_parameter_structs" +#define DATAFILE_SOMEIP_UNIONS "SOMEIP_parameter_unions" +#define DATAFILE_SOMEIP_ENUMS "SOMEIP_parameter_enums" + +#define SOMEIP_PAYLOAD_PARAMETER_DATA_TYPE_UNKNOWN 0 +#define SOMEIP_PAYLOAD_PARAMETER_DATA_TYPE_BASE_TYPE 1 +#define SOMEIP_PAYLOAD_PARAMETER_DATA_TYPE_STRING 2 +#define SOMEIP_PAYLOAD_PARAMETER_DATA_TYPE_ARRAY 3 +#define SOMEIP_PAYLOAD_PARAMETER_DATA_TYPE_STRUCT 4 +#define SOMEIP_PAYLOAD_PARAMETER_DATA_TYPE_UNION 5 +#define SOMEIP_PAYLOAD_PARAMETER_DATA_TYPE_TYPEDEF 6 +#define SOMEIP_PAYLOAD_PARAMETER_DATA_TYPE_ENUM 7 + +/*** SOME/IP ***/ +#define SOMEIP_HDR_LEN 16 +#define SOMEIP_HDR_PART1_LEN 8 +#define SOMEIP_HDR_PART2_LEN_INCL_TP 12 +#define SOMEIP_TP_HDR_LEN 4 +#define SOMEIP_PROTOCOL_VERSION 1 + +/* Message Types */ +#define SOMEIP_MSGTYPE_REQUEST 0x00 +#define SOMEIP_MSGTYPE_REQUEST_NO_RESPONSE 0x01 +#define SOMEIP_MSGTYPE_NOTIFICATION 0x02 +#define SOMEIP_MSGTYPE_RESPONSE 0x80 +#define SOMEIP_MSGTYPE_ERROR 0x81 + +#define SOMEIP_MSGTYPE_ACK_MASK 0x40 +#define SOMEIP_MSGTYPE_TP_MASK 0x20 +#define SOMEIP_MSGTYPE_FLAGS_MASK 0x60 +#define SOMEIP_MSGTYPE_NO_FLAGS_MASK 0x9f +#define SOMEIP_MSGTYPE_TP_STRING "SOME/IP-TP segment" +#define SOMEIP_MSGTYPE_ACK_STRING "ACK" + +/* SOME/IP-TP */ +#define SOMEIP_TP_OFFSET_MASK 0xfffffff0 +#define SOMEIP_TP_OFFSET_MASK_FLAGS 0x0000000f +#define SOMEIP_TP_OFFSET_MASK_RESERVED 0x0000000e +#define SOMEIP_TP_OFFSET_MASK_MORE_SEGMENTS 0x00000001 + +/* Return Codes */ +#define SOMEIP_RETCODE_OK 0x00 +#define SOMEIP_RETCODE_NOT_OK 0x01 +#define SOMEIP_RETCODE_UNKNOWN_SERVICE 0x02 +#define SOMEIP_RETCODE_UNKNOWN_METHOD 0x03 +#define SOMEIP_RETCODE_NOT_READY 0x04 +#define SOMEIP_RETCODE_NOT_REACHABLE 0x05 +#define SOMEIP_RETCODE_TIMEOUT 0x06 +#define SOMEIP_RETCODE_WRONG_PROTO_VER 0x07 +#define SOMEIP_RETCODE_WRONG_INTERFACE_VER 0x08 +#define SOMEIP_RETCODE_MALFORMED_MSG 0x09 +#define SOMEIP_RETCODE_WRONG_MESSAGE_TYPE 0x0a + +/* ID wireshark identifies the dissector by */ +static int proto_someip = -1; + +static dissector_handle_t someip_handle_udp = NULL; +static dissector_handle_t someip_handle_tcp = NULL; + +/* header field */ +static int hf_someip_messageid = -1; +static int hf_someip_serviceid = -1; +static int hf_someip_methodid = -1; +static int hf_someip_length = -1; +static int hf_someip_clientid = -1; +static int hf_someip_sessionid = -1; +static int hf_someip_protover = -1; +static int hf_someip_interface_ver = -1; +static int hf_someip_messagetype = -1; +static int hf_someip_messagetype_ack_flag = -1; +static int hf_someip_messagetype_tp_flag = -1; +static int hf_someip_returncode = -1; + +static int hf_someip_tp = -1; +static int hf_someip_tp_offset = -1; +static int hf_someip_tp_flags = -1; +static int hf_someip_tp_reserved = -1; +static int hf_someip_tp_more_segments = -1; + +static int hf_someip_payload = -1; + +/* protocol tree items */ +static gint ett_someip = -1; +static gint ett_someip_msgtype = -1; +static gint ett_someip_tp = -1; +static gint ett_someip_tp_flags = -1; + +/* dissector handling */ +static dissector_table_t someip_dissector_table = NULL; + +/* message reassembly for SOME/IP-TP */ +static int hf_someip_tp_fragments = -1; +static int hf_someip_tp_fragment = -1; +static int hf_someip_tp_fragment_overlap = -1; +static int hf_someip_tp_fragment_overlap_conflicts = -1; +static int hf_someip_tp_fragment_multiple_tails = -1; +static int hf_someip_tp_fragment_too_long_fragment = -1; +static int hf_someip_tp_fragment_error = -1; +static int hf_someip_tp_fragment_count = -1; +static int hf_someip_tp_reassembled_in = -1; +static int hf_someip_tp_reassembled_length = -1; +static int hf_someip_tp_reassembled_data = -1; + +static int hf_payload_unparsed = -1; +static int hf_payload_length_field_8bit = -1; +static int hf_payload_length_field_16bit = -1; +static int hf_payload_length_field_32bit = -1; +static int hf_payload_type_field_8bit = -1; +static int hf_payload_type_field_16bit = -1; +static int hf_payload_type_field_32bit = -1; +static int hf_payload_str_base = -1; +static int hf_payload_str_string = -1; +static int hf_payload_str_struct = -1; + +static gint ett_someip_tp_fragment = -1; +static gint ett_someip_tp_fragments = -1; +static gint ett_someip_payload = -1; +static gint ett_someip_string = -1; +static gint ett_someip_array = -1; +static gint ett_someip_array_dim = -1; +static gint ett_someip_struct = -1; +static gint ett_someip_union = -1; + +static const fragment_items someip_tp_frag_items = { + &ett_someip_tp_fragment, + &ett_someip_tp_fragments, + &hf_someip_tp_fragments, + &hf_someip_tp_fragment, + &hf_someip_tp_fragment_overlap, + &hf_someip_tp_fragment_overlap_conflicts, + &hf_someip_tp_fragment_multiple_tails, + &hf_someip_tp_fragment_too_long_fragment, + &hf_someip_tp_fragment_error, + &hf_someip_tp_fragment_count, + &hf_someip_tp_reassembled_in, + &hf_someip_tp_reassembled_length, + &hf_someip_tp_reassembled_data, + "SOME/IP-TP Segments" +}; + +static reassembly_table someip_tp_reassembly_table; + +static range_t *someip_ports_udp = NULL; +static range_t *someip_ports_tcp = NULL; + +static gboolean someip_tp_reassemble = TRUE; +static gboolean someip_derserializer_activated = FALSE; + + +/* SOME/IP Message Types */ +static const value_string someip_msg_type[] = { + {SOMEIP_MSGTYPE_REQUEST, "Request"}, + {SOMEIP_MSGTYPE_REQUEST_NO_RESPONSE, "Request no response"}, + {SOMEIP_MSGTYPE_NOTIFICATION, "Notification"}, + {SOMEIP_MSGTYPE_RESPONSE, "Response"}, + {SOMEIP_MSGTYPE_ERROR, "Error"}, + {SOMEIP_MSGTYPE_REQUEST | SOMEIP_MSGTYPE_ACK_MASK, "Request Ack"}, + {SOMEIP_MSGTYPE_REQUEST_NO_RESPONSE | SOMEIP_MSGTYPE_ACK_MASK, "Request no response Ack"}, + {SOMEIP_MSGTYPE_NOTIFICATION | SOMEIP_MSGTYPE_ACK_MASK, "Notification Ack"}, + {SOMEIP_MSGTYPE_RESPONSE | SOMEIP_MSGTYPE_ACK_MASK, "Response Ack"}, + {SOMEIP_MSGTYPE_ERROR | SOMEIP_MSGTYPE_ACK_MASK, "Error Ack"}, + {0, NULL} +}; + +/* SOME/IP Return Code */ +static const value_string someip_return_code[] = { + {SOMEIP_RETCODE_OK, "Ok"}, + {SOMEIP_RETCODE_NOT_OK, "Not Ok"}, + {SOMEIP_RETCODE_UNKNOWN_SERVICE, "Unknown Service"}, + {SOMEIP_RETCODE_UNKNOWN_METHOD, "Unknown Method/Event"}, + {SOMEIP_RETCODE_NOT_READY, "Not Ready"}, + {SOMEIP_RETCODE_NOT_REACHABLE, "Not Reachable (internal)"}, + {SOMEIP_RETCODE_TIMEOUT, "Timeout (internal)"}, + {SOMEIP_RETCODE_WRONG_PROTO_VER, "Wrong Protocol Version"}, + {SOMEIP_RETCODE_WRONG_INTERFACE_VER, "Wrong Interface Version"}, + {SOMEIP_RETCODE_MALFORMED_MSG, "Malformed Message"}, + {SOMEIP_RETCODE_WRONG_MESSAGE_TYPE, "Wrong Message Type"}, + {0, NULL} +}; + +/*** expert info items ***/ +static expert_field ef_someip_unknown_version = EI_INIT; +static expert_field ef_someip_message_truncated = EI_INIT; +static expert_field ef_someip_incomplete_headers = EI_INIT; + +static expert_field ef_someip_payload_truncated = EI_INIT; +static expert_field ef_someip_payload_config_error = EI_INIT; +static expert_field ef_someip_payload_alignment_error = EI_INIT; +static expert_field ef_someip_payload_static_array_min_not_max = EI_INIT; +static expert_field ef_someip_payload_dyn_array_not_within_limit = EI_INIT; + +/*** Data Structure for mapping IDs to Names (Services, Methods, ...) ***/ +static GHashTable *data_someip_services = NULL; +static GHashTable *data_someip_methods = NULL; +static GHashTable *data_someip_eventgroups = NULL; + +static GHashTable *data_someip_parameter_list = NULL; +static GHashTable *data_someip_parameter_base_type_list = NULL; +static GHashTable *data_someip_parameter_strings = NULL; +static GHashTable *data_someip_parameter_typedefs = NULL; +static GHashTable *data_someip_parameter_arrays = NULL; +static GHashTable *data_someip_parameter_structs = NULL; +static GHashTable *data_someip_parameter_unions = NULL; +static GHashTable *data_someip_parameter_enums = NULL; + + +/*********************************************** + ********* Preferences / Configuration ********* + ***********************************************/ + +typedef struct _someip_payload_parameter_item { + guint32 pos; + gchar *name; + guint32 data_type; + guint32 id_ref; +} someip_payload_parameter_item_t; + +#define INIT_SOMEIP_PAYLOAD_PARAMETER_ITEM(NAME) \ + (NAME)->pos = 0; \ + (NAME)->name = NULL; \ + (NAME)->data_type = 0; \ + (NAME)->id_ref = 0; + + +typedef struct _someip_payload_parameter_base_type_list { + guint32 id; + gchar *name; + gchar *data_type; + gboolean big_endian; + guint32 bitlength_base_type; + guint32 bitlength_encoded_type; +} someip_payload_parameter_base_type_list_t; + +#define INIT_COMMON_BASE_TYPE_LIST_ITEM(NAME) \ + (NAME)->id = 0; \ + (NAME)->name = NULL; \ + (NAME)->data_type = NULL ; \ + (NAME)->big_endian = TRUE; \ + (NAME)->bitlength_base_type = 0; \ + (NAME)->bitlength_encoded_type = 0; + + +typedef struct _someip_payload_parameter_string { + guint32 id; + gchar *name; + gchar *encoding; + gboolean dynamic_length; + guint32 max_length; + guint32 length_of_length; /* default: 32 */ + gboolean big_endian; + guint32 pad_to; +} someip_payload_parameter_string_t; + +#define INIT_SOMEIP_PAYLOAD_PARAMETER_STRING(NAME) \ + (NAME)->id = 0; \ + (NAME)->name = NULL; \ + (NAME)->encoding = NULL; \ + (NAME)->dynamic_length = FALSE; \ + (NAME)->max_length = 0; \ + (NAME)->length_of_length = 0; \ + (NAME)->big_endian = TRUE; \ + (NAME)->pad_to = 0; + + +typedef struct _someip_payload_parameter_typedef { + guint32 id; + gchar* name; + guint32 data_type; + guint32 id_ref; +} someip_payload_parameter_typedef_t; + +#define INIT_SOMEIP_PAYLOAD_PARAMETER_TYPEDEF(NAME) \ + (NAME)->id = 0; \ + (NAME)->name = NULL; \ + (NAME)->data_type = 0; \ + (NAME)->id_ref = 0; + + +typedef struct _someip_payload_parameter_struct { + guint32 id; + gchar *struct_name; + guint32 length_of_length; /* default: 0 */ + guint32 pad_to; /* default: 0 */ + guint32 num_of_items; + + /* array of items */ + someip_payload_parameter_item_t *items; +} someip_payload_parameter_struct_t; + +#define INIT_SOMEIP_PAYLOAD_PARAMETER_STRUCT(NAME) \ + (NAME)->id = 0; \ + (NAME)->struct_name = NULL; \ + (NAME)->length_of_length = 0; \ + (NAME)->pad_to = 0; \ + (NAME)->num_of_items = 0; + + +typedef struct _someip_payload_parameter_enum_item { + guint64 value; + gchar *name; +} someip_payload_parameter_enum_item_t; + +#define INIT_SOMEIP_PAYLOAD_PARAMETER_ENUM_ITEM(NAME) \ + (NAME)->value = 0; \ + (NAME)->name = NULL; + + +typedef struct _someip_payload_parameter_enum { + guint32 id; + gchar *name; + guint32 data_type; + guint32 id_ref; + guint32 num_of_items; + + someip_payload_parameter_enum_item_t *items; +} someip_payload_parameter_enum_t; + +#define INIT_SOMEIP_PAYLOAD_PARAMETER_ENUM(NAME) \ + (NAME)->id = 0; \ + (NAME)->name = NULL; \ + (NAME)->data_type = 0; \ + (NAME)->id_ref = 0; \ + (NAME)->num_of_items = 0; \ + (NAME)->items = NULL; + +typedef struct _someip_parameter_union_item { + guint32 id; + gchar *name; + guint32 data_type; + guint32 id_ref; +} someip_parameter_union_item_t; + +typedef struct _someip_parameter_union { + guint32 id; + gchar *name; + guint32 length_of_length; /* default: 32 */ + guint32 length_of_type; /* default: 32 */ + guint32 pad_to; /* default: 0 */ + guint32 num_of_items; + + someip_parameter_union_item_t *items; +} someip_parameter_union_t; + +typedef struct _someip_parameter_union_uat { + guint32 id; + gchar *name; + guint32 length_of_length; + guint32 length_of_type; + guint32 pad_to; + guint32 num_of_items; + guint32 type_id; + gchar *type_name; + guint32 data_type; + guint32 id_ref; +} someip_parameter_union_uat_t; + +typedef struct _someip_parameter_enum_uat { + guint32 id; + gchar *name; + guint32 data_type; + guint32 id_ref; + guint32 num_of_items; + guint32 value; + gchar *value_name; +} someip_parameter_enum_uat_t; + +typedef struct _someip_parameter_array_dim { + guint32 num; + guint32 lower_limit; + guint32 upper_limit; + guint32 length_of_length; + guint32 pad_to; +} someip_parameter_array_dim_t; + +typedef struct _someip_parameter_array { + guint32 id; + gchar *name; + guint32 data_type; + guint32 id_ref; + guint32 num_of_dims; + + someip_parameter_array_dim_t *dims; +} someip_parameter_array_t; + +typedef struct _someip_parameter_array_uat { + guint32 id; + gchar *name; + guint32 data_type; + guint32 id_ref; + guint32 num_of_dims; + + guint32 num; + guint32 lower_limit; + guint32 upper_limit; + guint32 length_of_length; + guint32 pad_to; +} someip_parameter_array_uat_t; + +typedef struct _someip_parameter_list { + guint32 service_id; + guint32 method_id; + guint32 version; + guint32 message_type; + + guint32 num_of_items; + + someip_payload_parameter_item_t *items; +} someip_parameter_list_t; + +typedef struct _someip_parameter_list_uat { + guint32 service_id; + guint32 method_id; + guint32 version; + guint32 message_type; + + guint32 num_of_params; + + guint32 pos; + gchar *name; + guint32 data_type; + guint32 id_ref; +} someip_parameter_list_uat_t; + +typedef struct _someip_parameter_struct_uat { + guint32 id; + gchar *struct_name; + guint32 length_of_length; /* default: 0 */ + guint32 pad_to; /* default: 0 */ + + guint32 num_of_items; + + guint32 pos; + gchar *name; + guint32 data_type; + guint32 id_ref; +} someip_parameter_struct_uat_t; + +typedef someip_payload_parameter_base_type_list_t someip_parameter_base_type_list_uat_t; +typedef someip_payload_parameter_string_t someip_parameter_string_uat_t; +typedef someip_payload_parameter_typedef_t someip_parameter_typedef_uat_t; + +typedef struct _generic_one_id_string { + guint id; + gchar *name; +} generic_one_id_string_t; + +typedef struct _generic_two_id_string { + guint id; + guint id2; + gchar *name; +} generic_two_id_string_t; + +static generic_one_id_string_t* someip_service_ident = NULL; +static guint someip_service_ident_num = 0; + +static generic_two_id_string_t* someip_method_ident = NULL; +static guint someip_method_ident_num = 0; + +static generic_two_id_string_t* someip_eventgroup_ident = NULL; +static guint someip_eventgroup_ident_num = 0; + +static someip_parameter_list_uat_t *someip_parameter_list = NULL; +static guint someip_parameter_list_num = 0; + +static someip_parameter_string_uat_t *someip_parameter_strings = NULL; +static guint someip_parameter_strings_num = 0; + +static someip_parameter_typedef_uat_t *someip_parameter_typedefs = NULL; +static guint someip_parameter_typedefs_num = 0; + +static someip_parameter_array_uat_t *someip_parameter_arrays = NULL; +static guint someip_parameter_arrays_num = 0; + +static someip_parameter_struct_uat_t *someip_parameter_structs = NULL; +static guint someip_parameter_structs_num = 0; + +static someip_parameter_union_uat_t *someip_parameter_unions = NULL; +static guint someip_parameter_unions_num = 0; + +static someip_parameter_enum_uat_t *someip_parameter_enums = NULL; +static guint someip_parameter_enums_num = 0; + +static someip_parameter_base_type_list_uat_t *someip_parameter_base_type_list = NULL; +static guint someip_parameter_base_type_list_num = 0; + +void proto_register_someip(void); +void proto_reg_handoff_someip(void); + +/* register a UDP SOME/IP port */ +void +register_someip_port_udp(guint32 portnumber) { + dissector_add_uint("udp.port", portnumber, someip_handle_udp); +} + +/* register a TCP SOME/IP port */ +void +register_someip_port_tcp(guint32 portnumber) { + dissector_add_uint("tcp.port", portnumber, someip_handle_tcp); +} + +/*** UAT Callbacks and Helpers ***/ +static void +someip_free_key(gpointer key) { + wmem_free(wmem_epan_scope(), key); +} + +static void +simple_free(gpointer data _U_) { + /* we need to free because of the g_strdup in post_update*/ + g_free(data); +} + +/* ID -> Name */ +static void * +copy_generic_one_id_string_cb(void* n, const void* o, size_t size _U_) { + generic_one_id_string_t* new_rec = (generic_one_id_string_t*)n; + const generic_one_id_string_t* old_rec = (const generic_one_id_string_t*)o; + + new_rec->name = g_strdup(old_rec->name); + new_rec->id = old_rec->id; + return new_rec; +} + +static gboolean +update_generic_one_identifier_16bit(void *r, char **err) { + generic_one_id_string_t *rec = (generic_one_id_string_t *)r; + + if ( rec->id>0xffff ) { + *err = g_strdup_printf("We currently only support 16 bit identifiers (ID: %i Name: %s)", rec->id, rec->name); + return FALSE; + } + + if (rec->name == NULL || rec->name[0] == 0) { + *err = g_strdup("Name cannot be empty"); + return FALSE; + } + + return TRUE; +} + +static void +free_generic_one_id_string_cb(void*r) { + generic_one_id_string_t* rec = (generic_one_id_string_t*)r; + /* freeing result of g_strdup */ + g_free(rec->name); + rec->name = NULL; +} + +static void +post_update_one_id_string_template_cb(generic_one_id_string_t *data, guint data_num, GHashTable *ht) { + guint i; + int *key = NULL; + + for (i = 0; i < data_num; i++) { + key = wmem_new(wmem_epan_scope(), int); + *key = data[i].id; + + g_hash_table_insert(ht, key, g_strdup(data[i].name)); + } +} + +/* ID/ID2 -> Name */ + +static void * +copy_generic_two_id_string_cb(void* n, const void* o, size_t size _U_) { + generic_two_id_string_t* new_rec = (generic_two_id_string_t*)n; + const generic_two_id_string_t* old_rec = (const generic_two_id_string_t*)o; + + new_rec->name = g_strdup(old_rec->name); + new_rec->id = old_rec->id; + new_rec->id2 = old_rec->id2; + return new_rec; +} + +static gboolean +update_generic_two_identifier_16bit(void *r, char **err) { + generic_two_id_string_t *rec = (generic_two_id_string_t *)r; + + if ( rec->id > 0xffff ) { + *err = g_strdup_printf("We currently only support 16 bit identifiers (ID: %i Name: %s)", rec->id, rec->name); + return FALSE; + } + + if ( rec->id2 > 0xffff ) { + *err = g_strdup_printf("We currently only support 16 bit identifiers (ID: %i ID2: %i Name: %s)", rec->id, rec->id2, rec->name); + return FALSE; + } + + if (rec->name == NULL || rec->name[0] == 0 ) { + *err = g_strdup("Name cannot be empty"); + return FALSE; + } + + return TRUE; +} + +static void +free_generic_two_id_string_cb(void*r) { + generic_two_id_string_t* rec = (generic_two_id_string_t*)r; + + /* freeing result of g_strdup */ + g_free(rec->name); + rec->name = NULL; +} + +static void +post_update_generic_two_id_string_template_cb(generic_two_id_string_t *data, guint data_num, GHashTable *ht) { + guint i; + int *key = NULL; + guint tmp; + guint tmp2; + + for (i = 0; i < data_num; i++) { + key = wmem_new(wmem_epan_scope(), int); + tmp = (data[i].id & 0xffff) << 16; + tmp2 = (data[i].id2 & 0xffff); + + /* the hash table does not know about uint32, so we use int32 */ + *key = (int)(tmp + tmp2); + + g_hash_table_insert(ht, key, g_strdup(data[i].name)); + } +} + +char* +someip_lookup_service_name(guint16 serviceid) { + guint32 tmp = (guint32)serviceid; + + if (data_someip_services == NULL) { + return NULL; + } + + return (char *)g_hash_table_lookup(data_someip_services, &tmp); +} + +static char* +someip_lookup_method_name(guint16 serviceid, guint16 methodid) { + guint32 tmp = (serviceid << 16) + methodid; + + if (data_someip_methods == NULL) { + return NULL; + } + + return (char *)g_hash_table_lookup(data_someip_methods, &tmp); +} + +char* +someip_lookup_eventgroup_name(guint16 serviceid, guint16 eventgroupid) { + guint32 tmp = (serviceid << 16) + eventgroupid; + + if (data_someip_eventgroups == NULL) { + return NULL; + } + + return (char *)g_hash_table_lookup(data_someip_eventgroups, &tmp); +} + +/*** SOME/IP Services ***/ +UAT_HEX_CB_DEF (someip_service_ident, id, generic_one_id_string_t) +UAT_CSTRING_CB_DEF (someip_service_ident, name, generic_one_id_string_t) + +static void +post_update_someip_service_cb(void) { + /* destroy old hash table, if it exists */ + if (data_someip_services) { + g_hash_table_destroy (data_someip_services); + data_someip_services = NULL; + } + + /* create new hash table */ + data_someip_services = g_hash_table_new_full(g_int_hash, g_int_equal, &someip_free_key, &simple_free); + post_update_one_id_string_template_cb(someip_service_ident, someip_service_ident_num, data_someip_services); +} + +/*** SOME/IP Methods/Events/Fields ***/ +UAT_HEX_CB_DEF (someip_method_ident, id, generic_two_id_string_t) +UAT_HEX_CB_DEF (someip_method_ident, id2, generic_two_id_string_t) +UAT_CSTRING_CB_DEF (someip_method_ident, name, generic_two_id_string_t) + +static void +post_update_someip_method_cb(void) { + /* destroy old hash table, if it exists */ + if (data_someip_methods) { + g_hash_table_destroy (data_someip_methods); + data_someip_methods = NULL; + } + + /* create new hash table */ + data_someip_methods = g_hash_table_new_full(g_int_hash, g_int_equal, &someip_free_key, &simple_free); + post_update_generic_two_id_string_template_cb(someip_method_ident, someip_method_ident_num, data_someip_methods); +} + +/*** SOME/IP Eventgroups ***/ +UAT_HEX_CB_DEF (someip_eventgroup_ident, id, generic_two_id_string_t) +UAT_HEX_CB_DEF (someip_eventgroup_ident, id2, generic_two_id_string_t) +UAT_CSTRING_CB_DEF (someip_eventgroup_ident, name, generic_two_id_string_t) + +static void +post_update_someip_eventgroup_cb(void) { + /* destroy old hash table, if it exists */ + if (data_someip_eventgroups) { + g_hash_table_destroy (data_someip_eventgroups); + data_someip_eventgroups = NULL; + } + + /* create new hash table */ + data_someip_eventgroups = g_hash_table_new_full(g_int_hash, g_int_equal, &someip_free_key, &simple_free); + post_update_generic_two_id_string_template_cb(someip_eventgroup_ident, someip_eventgroup_ident_num, data_someip_eventgroups); +} + +static void +someip_payload_free_key(gpointer key) { + wmem_free(wmem_epan_scope(), key); +} + +static void +someip_payload_free_generic_data(gpointer data) { + wmem_free(wmem_epan_scope(), (void *)data); +} + +static gint64 +someip_parameter_key(guint16 serviceid, guint16 methodid, guint8 version, guint8 msgtype) { + gint64 tmp1; + gint64 tmp2; + gint64 tmp3; + gint64 tmp4; + + /* key: + Service-ID [16bit] | Method-ID [16bit] | Version [8bit] | Message-Type [8bit] + */ + + tmp1 = (gint64)(serviceid & 0xffff); + tmp2 = (gint64)(methodid & 0xffff) << 16; + tmp3 = (gint64)(version & 0xff) << 32; + tmp4 = (gint64)(msgtype & 0xff) << 40; + + return (gint64)(tmp1 + tmp2 + tmp3 + tmp4); +} + +static someip_parameter_list_t* +get_parameter_config(guint16 serviceid, guint16 methodid, guint8 version, guint8 msgtype) { + gint64 *key = NULL; + someip_parameter_list_t* tmp = NULL; + + if (data_someip_parameter_list == NULL) { + return NULL; + } + + key = wmem_new(wmem_epan_scope(), gint64); + *key = someip_parameter_key(serviceid, methodid, version, msgtype); + tmp = (someip_parameter_list_t*)g_hash_table_lookup(data_someip_parameter_list, key); + wmem_free(wmem_epan_scope(), key); + + return tmp; +} + +static gpointer +get_generic_config(GHashTable *ht, gint64 id) { + if (ht == NULL) { + return NULL; + } + + return (gpointer)g_hash_table_lookup(ht, &id); +} + +static someip_payload_parameter_base_type_list_t* +get_base_type_config(guint32 id) { + return (someip_payload_parameter_base_type_list_t*)get_generic_config(data_someip_parameter_base_type_list, (gint64)id); +} + +static someip_payload_parameter_string_t* +get_string_config(guint32 id) { + return (someip_payload_parameter_string_t*)get_generic_config(data_someip_parameter_strings, (gint64)id); +} + +static someip_payload_parameter_typedef_t* +get_typedef_config(guint32 id) { + return (someip_payload_parameter_typedef_t*)get_generic_config(data_someip_parameter_typedefs, (gint64)id); +} + +static someip_parameter_array_t* +get_array_config(guint32 id) { + return (someip_parameter_array_t*)get_generic_config(data_someip_parameter_arrays, (gint64)id); +} + +static someip_payload_parameter_struct_t* +get_struct_config(guint32 id) { + return (someip_payload_parameter_struct_t*)get_generic_config(data_someip_parameter_structs, (gint64)id); +} + +static someip_parameter_union_t* +get_union_config(guint32 id) { + return (someip_parameter_union_t*)get_generic_config(data_someip_parameter_unions, (gint64)id); +} + +static someip_payload_parameter_enum_t* +get_enum_config(guint32 id) { + return (someip_payload_parameter_enum_t*)get_generic_config(data_someip_parameter_enums, (gint64)id); +} + +UAT_HEX_CB_DEF(someip_parameter_list, service_id, someip_parameter_list_uat_t) +UAT_HEX_CB_DEF(someip_parameter_list, method_id, someip_parameter_list_uat_t) +UAT_DEC_CB_DEF(someip_parameter_list, version, someip_parameter_list_uat_t) +UAT_HEX_CB_DEF(someip_parameter_list, message_type, someip_parameter_list_uat_t) + +UAT_DEC_CB_DEF(someip_parameter_list, num_of_params, someip_parameter_list_uat_t) + +UAT_DEC_CB_DEF(someip_parameter_list, pos, someip_parameter_list_uat_t) +UAT_CSTRING_CB_DEF(someip_parameter_list, name, someip_parameter_list_uat_t) +UAT_DEC_CB_DEF(someip_parameter_list, data_type, someip_parameter_list_uat_t) +UAT_HEX_CB_DEF(someip_parameter_list, id_ref, someip_parameter_list_uat_t) + +static void * +copy_someip_parameter_list_cb(void* n, const void* o, size_t size _U_) { + someip_parameter_list_uat_t *new_rec = (someip_parameter_list_uat_t*)n; + const someip_parameter_list_uat_t *old_rec = (const someip_parameter_list_uat_t*)o; + + if (old_rec->name) { + new_rec->name = g_strdup(old_rec->name); + } else { + new_rec->name = NULL; + } + + new_rec->service_id = old_rec->service_id; + new_rec->method_id = old_rec->method_id; + new_rec->version = old_rec->version; + new_rec->message_type = old_rec->message_type; + new_rec->num_of_params = old_rec->num_of_params; + new_rec->pos = old_rec->pos; + new_rec->data_type = old_rec->data_type; + new_rec->id_ref = old_rec->id_ref; + + return new_rec; +} + +static gboolean +update_someip_parameter_list(void *r, char **err) { + someip_parameter_list_uat_t *rec = (someip_parameter_list_uat_t *)r; + + if (rec->service_id > 0xffff) { + *err = g_strdup_printf("We currently only support 16 bit Service IDs (Service-ID: %i Name: %s)", rec->service_id, rec->name); + return FALSE; + } + + if (rec->method_id > 0xffff) { + *err = g_strdup_printf("We currently only support 16 bit Method IDs (Service-ID: %i Method-ID: %i Name: %s)", rec->service_id, rec->method_id, rec->name); + return FALSE; + } + + if (rec->version > 0xff) { + *err = g_strdup_printf("We currently only support 8 bit Version (Service-ID: %i Method-ID: %i Version: %d Name: %s)", rec->service_id, rec->method_id, rec->version, rec->name); + return FALSE; + } + + if (rec->message_type > 0xff) { + *err = g_strdup_printf("We currently only support 8 bit Message Type (Service-ID: %i Method-ID: %i Version: %d Message Type: %x Name: %s)", rec->service_id, rec->method_id, rec->version, rec->message_type, rec->name); + return FALSE; + } + + if (rec->name == NULL || rec->name[0] == 0) { + *err = g_strdup_printf("Name cannot be empty"); + return FALSE; + } + + if (rec->pos >= rec->num_of_params) { + *err = g_strdup_printf("Position >= Number of Parameters"); + return FALSE; + } + + return TRUE; +} + +static void +free_someip_parameter_list_cb(void*r) { + someip_parameter_list_uat_t* rec = (someip_parameter_list_uat_t*)r; + if (rec->name) g_free(rec->name); +} + +static void +post_update_someip_parameter_list_read_in_data(someip_parameter_list_uat_t *data, guint data_num, GHashTable *ht) { + guint i = 0; + gint64 *key = NULL; + someip_parameter_list_t *list = NULL; + someip_payload_parameter_item_t *item = NULL; + someip_payload_parameter_item_t *items = NULL; + + if (ht == NULL || data == NULL || data_num == 0) { + return; + } + + for (i = 0; i < data_num; i++) { + + /* the hash table does not know about uint64, so we use int64*/ + key = wmem_new(wmem_epan_scope(), gint64); + *key = someip_parameter_key((guint16)data[i].service_id, (guint16)data[i].method_id, (guint8)data[i].version, (guint8)data[i].message_type); + + list = (someip_parameter_list_t *)g_hash_table_lookup(ht, key); + if (list == NULL) { + + list = wmem_new(wmem_epan_scope(), someip_parameter_list_t); + + list->service_id = data[i].service_id; + list->method_id = data[i].method_id; + list->version = data[i].version; + list->message_type = data[i].message_type; + list->num_of_items = data[i].num_of_params; + + items = (someip_payload_parameter_item_t *)wmem_alloc_array(wmem_epan_scope(), someip_payload_parameter_item_t, data[i].num_of_params); + memset(items, 0, sizeof(someip_payload_parameter_item_t) * data[i].num_of_params); + + list->items = items; + + /* create new entry ... */ + g_hash_table_insert(ht, key, list); + } else { + /* already present, deleting key */ + wmem_free(wmem_epan_scope(), key); + } + + /* and now we add to item array */ + if (data[i].num_of_params == list->num_of_items && data[i].pos < list->num_of_items) { + item = &(list->items[data[i].pos]); + + /* we do not care if we overwrite param */ + item->name = data[i].name; + item->id_ref = data[i].id_ref; + item->pos = data[i].pos; + item->data_type = data[i].data_type; + } + } +} + +static void +post_update_someip_parameter_list_cb(void) { + /* destroy old hash table, if it exists */ + if (data_someip_parameter_list) { + g_hash_table_destroy(data_someip_parameter_list); + data_someip_parameter_list = NULL; + } + + data_someip_parameter_list = g_hash_table_new_full(g_int64_hash, g_int64_equal, &someip_payload_free_key, &someip_payload_free_generic_data); + post_update_someip_parameter_list_read_in_data(someip_parameter_list, someip_parameter_list_num, data_someip_parameter_list); +} + +UAT_HEX_CB_DEF(someip_parameter_enums, id, someip_parameter_enum_uat_t) +UAT_CSTRING_CB_DEF(someip_parameter_enums, name, someip_parameter_enum_uat_t) +UAT_DEC_CB_DEF(someip_parameter_enums, data_type, someip_parameter_enum_uat_t) +UAT_HEX_CB_DEF(someip_parameter_enums, id_ref, someip_parameter_enum_uat_t) +UAT_DEC_CB_DEF(someip_parameter_enums, num_of_items, someip_parameter_enum_uat_t) + +UAT_HEX_CB_DEF(someip_parameter_enums, value, someip_parameter_enum_uat_t) +UAT_CSTRING_CB_DEF(someip_parameter_enums, value_name, someip_parameter_enum_uat_t) + +static void * +copy_someip_parameter_enum_cb(void* n, const void* o, size_t size _U_) { + someip_parameter_enum_uat_t *new_rec = (someip_parameter_enum_uat_t*)n; + const someip_parameter_enum_uat_t *old_rec = (const someip_parameter_enum_uat_t*)o; + + new_rec->id = old_rec->id; + if (old_rec->name) { + new_rec->name = g_strdup(old_rec->name); + } else { + new_rec->name = NULL; + } + new_rec->data_type = old_rec->data_type; + new_rec->id_ref = old_rec->id_ref; + new_rec->num_of_items = old_rec->num_of_items; + + new_rec->value = old_rec->value; + if (old_rec->value_name) { + new_rec->value_name = g_strdup(old_rec->value_name); + } else { + new_rec->value_name = NULL; + } + + return new_rec; +} + +static gboolean +update_someip_parameter_enum(void *r, char **err) { + someip_parameter_enum_uat_t *rec = (someip_parameter_enum_uat_t *)r; + + if (rec->name == NULL || rec->name[0] == 0) { + *err = g_strdup_printf("Name cannot be empty"); + return FALSE; + } + + if (rec->value_name == NULL || rec->value_name[0] == 0) { + *err = g_strdup_printf("Value Name cannot be empty"); + return FALSE; + } + + if (rec->num_of_items == 0) { + *err = g_strdup_printf("Number_of_Items = 0"); + return FALSE; + } + + return TRUE; +} + +static void +free_someip_parameter_enum_cb(void*r) { + someip_parameter_enum_uat_t* rec = (someip_parameter_enum_uat_t*)r; + if (rec->name) g_free(rec->name); + if (rec->value_name) g_free(rec->value_name); +} + +static void +post_update_someip_parameter_enum_read_in_data(someip_parameter_enum_uat_t *data, guint data_num, GHashTable *ht) { + guint i = 0; + guint j = 0; + gint64 *key = NULL; + someip_payload_parameter_enum_t *list = NULL; + someip_payload_parameter_enum_item_t *item = NULL; + + if (ht == NULL || data == NULL || data_num == 0) { + return; + } + + for (i = 0; i < data_num; i++) { + key = wmem_new(wmem_epan_scope(), gint64); + *key = data[i].id; + + list = (someip_payload_parameter_enum_t *)g_hash_table_lookup(ht, key); + if (list == NULL) { + + list = wmem_new(wmem_epan_scope(), someip_payload_parameter_enum_t); + INIT_SOMEIP_PAYLOAD_PARAMETER_ENUM(list) + + list->id = data[i].id; + list->name = data[i].name; + list->data_type = data[i].data_type; + list->id_ref = data[i].id_ref; + list->num_of_items = data[i].num_of_items; + + list->items = (someip_payload_parameter_enum_item_t *)wmem_alloc_array(wmem_epan_scope(), someip_payload_parameter_enum_item_t, list->num_of_items); + memset(list->items, 0, sizeof(someip_payload_parameter_enum_item_t) * list->num_of_items); + + /* create new entry ... */ + g_hash_table_insert(ht, key, list); + } else { + /* dont need it anymore */ + wmem_free(wmem_epan_scope(), key); + } + + /* and now we add to item array */ + if (list->num_of_items > 0 && data[i].num_of_items == list->num_of_items) { + + /* find first empty slot */ + for (j = 0; j < list->num_of_items && list->items[j].name != NULL; j++); + + if (j < list->num_of_items) { + item = &(list->items[j]); + INIT_SOMEIP_PAYLOAD_PARAMETER_ENUM_ITEM(item) + + /* we do not care if we overwrite param */ + item->value = data[i].value; + item->name = data[i].value_name; + } + } + } +} + +static void +post_update_someip_parameter_enum_cb(void) { + /* destroy old hash table, if it exists */ + if (data_someip_parameter_enums) { + g_hash_table_destroy(data_someip_parameter_enums); + data_someip_parameter_enums = NULL; + } + + data_someip_parameter_enums = g_hash_table_new_full(g_int64_hash, g_int64_equal, &someip_payload_free_key, &someip_payload_free_generic_data); + post_update_someip_parameter_enum_read_in_data(someip_parameter_enums, someip_parameter_enums_num, data_someip_parameter_enums); +} + +UAT_HEX_CB_DEF(someip_parameter_arrays, id, someip_parameter_array_uat_t) +UAT_CSTRING_CB_DEF(someip_parameter_arrays, name, someip_parameter_array_uat_t) +UAT_DEC_CB_DEF(someip_parameter_arrays, data_type, someip_parameter_array_uat_t) +UAT_HEX_CB_DEF(someip_parameter_arrays, id_ref, someip_parameter_array_uat_t) +UAT_DEC_CB_DEF(someip_parameter_arrays, num_of_dims, someip_parameter_array_uat_t) + +UAT_DEC_CB_DEF(someip_parameter_arrays, num, someip_parameter_array_uat_t) +UAT_DEC_CB_DEF(someip_parameter_arrays, lower_limit, someip_parameter_array_uat_t) +UAT_DEC_CB_DEF(someip_parameter_arrays, upper_limit, someip_parameter_array_uat_t) +UAT_DEC_CB_DEF(someip_parameter_arrays, length_of_length, someip_parameter_array_uat_t) +UAT_DEC_CB_DEF(someip_parameter_arrays, pad_to, someip_parameter_array_uat_t) + +static void * +copy_someip_parameter_array_cb(void* n, const void* o, size_t size _U_) { + someip_parameter_array_uat_t* new_rec = (someip_parameter_array_uat_t*)n; + const someip_parameter_array_uat_t* old_rec = (const someip_parameter_array_uat_t*)o; + + new_rec->id = old_rec->id; + if (old_rec->name) { + new_rec->name = g_strdup(old_rec->name); + } else { + new_rec->name = NULL; + } + new_rec->data_type = old_rec->data_type; + new_rec->id_ref = old_rec->id_ref; + new_rec->num_of_dims = old_rec->num_of_dims; + + new_rec->num = old_rec->num; + new_rec->lower_limit = old_rec->lower_limit; + new_rec->upper_limit = old_rec->upper_limit; + new_rec->length_of_length = old_rec->length_of_length; + new_rec->pad_to = old_rec->pad_to; + + return new_rec; +} + +static gboolean +update_someip_parameter_array(void *r, char **err) { + someip_parameter_array_uat_t *rec = (someip_parameter_array_uat_t *)r; + + if (rec->name == NULL || rec->name[0] == 0) { + *err = g_strdup_printf("Name cannot be empty"); + return FALSE; + } + + if (rec->num >= rec->num_of_dims) { + *err = g_strdup_printf("Dimension >= Number of Dimensions"); + return FALSE; + } + + return TRUE; +} + +static void +free_someip_parameter_array_cb(void*r) { + someip_parameter_array_uat_t* rec = (someip_parameter_array_uat_t*)r; + if (rec->name) g_free(rec->name); +} + +static void +post_update_someip_parameter_array_read_in_data(someip_parameter_array_uat_t *data, guint data_num, GHashTable *ht) { + guint i = 0; + gint64 *key = NULL; + someip_parameter_array_t *list = NULL; + someip_parameter_array_dim_t *item = NULL; + someip_parameter_array_dim_t *items = NULL; + + if (ht == NULL || data == NULL || data_num == 0) { + return; + } + + for (i = 0; i < data_num; i++) { + key = wmem_new(wmem_epan_scope(), gint64); + *key = data[i].id; + + list = (someip_parameter_array_t *)g_hash_table_lookup(ht, key); + if (list == NULL) { + + list = wmem_new(wmem_epan_scope(), someip_parameter_array_t); + + list->id = data[i].id; + list->name = data[i].name; + list->data_type = data[i].data_type; + list->id_ref = data[i].id_ref; + list->num_of_dims = data[i].num_of_dims; + + items = (someip_parameter_array_dim_t *)wmem_alloc_array(wmem_epan_scope(), someip_parameter_array_dim_t, data[i].num_of_dims); + memset(items, 0, sizeof(someip_parameter_array_dim_t) * data[i].num_of_dims); + + list->dims = items; + + /* create new entry ... */ + g_hash_table_insert(ht, key, list); + } + + /* and now we add to item array */ + if (data[i].num_of_dims == list->num_of_dims && data[i].num < list->num_of_dims) { + item = &(list->dims[data[i].num]); + + /* we do not care if we overwrite param */ + item->num = data[i].num; + item->lower_limit = data[i].lower_limit; + item->upper_limit = data[i].upper_limit; + item->length_of_length = data[i].length_of_length; + item->pad_to = data[i].pad_to; + } + } +} + +static void +post_update_someip_parameter_array_cb(void) { + /* destroy old hash table, if it exists */ + if (data_someip_parameter_arrays) { + g_hash_table_destroy(data_someip_parameter_arrays); + data_someip_parameter_arrays = NULL; + } + + data_someip_parameter_arrays = g_hash_table_new_full(g_int64_hash, g_int64_equal, &someip_payload_free_key, &someip_payload_free_generic_data); + post_update_someip_parameter_array_read_in_data(someip_parameter_arrays, someip_parameter_arrays_num, data_someip_parameter_arrays); +} + +UAT_HEX_CB_DEF(someip_parameter_structs, id, someip_parameter_struct_uat_t) +UAT_CSTRING_CB_DEF(someip_parameter_structs, struct_name, someip_parameter_struct_uat_t) +UAT_DEC_CB_DEF(someip_parameter_structs, length_of_length, someip_parameter_struct_uat_t) +UAT_DEC_CB_DEF(someip_parameter_structs, pad_to, someip_parameter_struct_uat_t) + +UAT_DEC_CB_DEF(someip_parameter_structs, num_of_items, someip_parameter_struct_uat_t) + +UAT_DEC_CB_DEF(someip_parameter_structs, pos, someip_parameter_struct_uat_t) +UAT_CSTRING_CB_DEF(someip_parameter_structs, name, someip_parameter_struct_uat_t) +UAT_DEC_CB_DEF(someip_parameter_structs, data_type, someip_parameter_struct_uat_t) +UAT_HEX_CB_DEF(someip_parameter_structs, id_ref, someip_parameter_struct_uat_t) + +static void * +copy_someip_parameter_struct_cb(void* n, const void* o, size_t size _U_) { + someip_parameter_struct_uat_t *new_rec = (someip_parameter_struct_uat_t*)n; + const someip_parameter_struct_uat_t *old_rec = (const someip_parameter_struct_uat_t*)o; + + new_rec->id = old_rec->id; + + if (old_rec->struct_name) { + new_rec->struct_name = g_strdup(old_rec->struct_name); + } else { + new_rec->struct_name = NULL; + } + + new_rec->length_of_length = old_rec->length_of_length; + new_rec->pad_to = old_rec->pad_to; + + new_rec->num_of_items = old_rec->num_of_items; + + new_rec->pos = old_rec->pos; + + if (old_rec->name) { + new_rec->name = g_strdup(old_rec->name); + } else { + new_rec->name = NULL; + } + + new_rec->data_type = old_rec->data_type; + new_rec->id_ref = old_rec->id_ref; + + return new_rec; +} + +static gboolean +update_someip_parameter_struct(void *r, char **err) { + someip_parameter_struct_uat_t *rec = (someip_parameter_struct_uat_t *)r; + + if (rec->struct_name == NULL || rec->struct_name[0] == 0) { + *err = g_strdup_printf("Struct name cannot be empty"); + return FALSE; + } + + if (rec->name == NULL || rec->name[0] == 0) { + *err = g_strdup_printf("Name cannot be empty"); + return FALSE; + } + + if (rec->pos >= rec->num_of_items) { + *err = g_strdup_printf("Position >= Number of Parameters"); + return FALSE; + } + + return TRUE; +} + +static void +free_someip_parameter_struct_cb(void*r) { + someip_parameter_struct_uat_t* rec = (someip_parameter_struct_uat_t*)r; + if (rec->struct_name) g_free(rec->struct_name); + if (rec->name) g_free(rec->name); +} + +static void +post_update_someip_parameter_struct_read_in_data(someip_parameter_struct_uat_t *data, guint data_num, GHashTable *ht) { + guint i = 0; + gint64 *key = NULL; + someip_payload_parameter_struct_t *list = NULL; + someip_payload_parameter_item_t *item = NULL; + someip_payload_parameter_item_t *items = NULL; + + if (ht == NULL || data == NULL || data_num == 0) { + return; + } + + for (i = 0; i < data_num; i++) { + key = wmem_new(wmem_epan_scope(), gint64); + *key = data[i].id; + + list = (someip_payload_parameter_struct_t *)g_hash_table_lookup(ht, key); + if (list == NULL) { + + list = wmem_new(wmem_epan_scope(), someip_payload_parameter_struct_t); + INIT_SOMEIP_PAYLOAD_PARAMETER_STRUCT(list) + + list->id = data[i].id; + list->struct_name = data[i].struct_name; + list->length_of_length = data[i].length_of_length; + list->pad_to = data[i].pad_to; + list->num_of_items = data[i].num_of_items; + + items = (someip_payload_parameter_item_t *)wmem_alloc_array(wmem_epan_scope(), someip_payload_parameter_item_t, data[i].num_of_items); + memset(items, 0, sizeof(someip_payload_parameter_item_t) * data[i].num_of_items); + + list->items = items; + + /* create new entry ... */ + g_hash_table_insert(ht, key, list); + } + + /* and now we add to item array */ + if (data[i].num_of_items == list->num_of_items && data[i].pos < list->num_of_items) { + item = &(list->items[data[i].pos]); + INIT_SOMEIP_PAYLOAD_PARAMETER_ITEM(item) + + /* we do not care if we overwrite param */ + item->name = data[i].name; + item->id_ref = data[i].id_ref; + item->pos = data[i].pos; + item->data_type = data[i].data_type; + } + } +} + +static void +post_update_someip_parameter_struct_cb(void) { + /* destroy old hash table, if it exists */ + if (data_someip_parameter_structs) { + g_hash_table_destroy(data_someip_parameter_structs); + data_someip_parameter_structs = NULL; + } + + data_someip_parameter_structs = g_hash_table_new_full(g_int64_hash, g_int64_equal, &someip_payload_free_key, &someip_payload_free_generic_data); + post_update_someip_parameter_struct_read_in_data(someip_parameter_structs, someip_parameter_structs_num, data_someip_parameter_structs); +} + +UAT_HEX_CB_DEF(someip_parameter_unions, id, someip_parameter_union_uat_t) +UAT_CSTRING_CB_DEF(someip_parameter_unions, name, someip_parameter_union_uat_t) +UAT_DEC_CB_DEF(someip_parameter_unions, length_of_length, someip_parameter_union_uat_t) +UAT_DEC_CB_DEF(someip_parameter_unions, length_of_type, someip_parameter_union_uat_t) +UAT_DEC_CB_DEF(someip_parameter_unions, pad_to, someip_parameter_union_uat_t) + +UAT_DEC_CB_DEF(someip_parameter_unions, num_of_items, someip_parameter_union_uat_t) + +UAT_DEC_CB_DEF(someip_parameter_unions, type_id, someip_parameter_union_uat_t) +UAT_CSTRING_CB_DEF(someip_parameter_unions, type_name, someip_parameter_union_uat_t) +UAT_DEC_CB_DEF(someip_parameter_unions, data_type, someip_parameter_union_uat_t) +UAT_HEX_CB_DEF(someip_parameter_unions, id_ref, someip_parameter_union_uat_t) + +static void * +copy_someip_parameter_union_cb(void* n, const void* o, size_t size _U_) { + someip_parameter_union_uat_t *new_rec = (someip_parameter_union_uat_t*)n; + const someip_parameter_union_uat_t *old_rec = (const someip_parameter_union_uat_t*)o; + + new_rec->id = old_rec->id; + + if (old_rec->name) { + new_rec->name = g_strdup(old_rec->name); + } else { + new_rec->name = NULL; + } + + new_rec->length_of_length = old_rec->length_of_length; + new_rec->length_of_type = old_rec->length_of_type; + new_rec->pad_to = old_rec->pad_to; + + new_rec->num_of_items = old_rec->num_of_items; + + new_rec->type_id = old_rec->type_id; + + if (old_rec->type_name) { + new_rec->type_name = g_strdup(old_rec->type_name); + } else { + new_rec->type_name = NULL; + } + + new_rec->data_type = old_rec->data_type; + new_rec->id_ref = old_rec->id_ref; + + return new_rec; +} + +static gboolean +update_someip_parameter_union(void *r, char **err) { + someip_parameter_union_uat_t *rec = (someip_parameter_union_uat_t *)r; + + if (rec->name == NULL || rec->name[0] == 0) { + *err = g_strdup_printf("Union name cannot be empty"); + return FALSE; + } + + if (rec->type_name == NULL || rec->type_name[0] == 0) { + *err = g_strdup_printf("Type Name cannot be empty"); + return FALSE; + } + + return TRUE; +} + +static void +free_someip_parameter_union_cb(void*r) { + someip_parameter_union_uat_t *rec = (someip_parameter_union_uat_t*)r; + + if (rec->name) g_free(rec->name); + if (rec->type_name) g_free(rec->type_name); +} + +static void +post_update_someip_parameter_union_read_in_data(someip_parameter_union_uat_t *data, guint data_num, GHashTable *ht) { + guint i = 0; + guint j = 0; + gint64 *key = NULL; + someip_parameter_union_t *list = NULL; + someip_parameter_union_item_t *item = NULL; + + if (ht == NULL || data == NULL || data_num == 0) { + return; + } + + for (i = 0; i < data_num; i++) { + key = wmem_new(wmem_epan_scope(), gint64); + *key = data[i].id; + + list = (someip_parameter_union_t *)g_hash_table_lookup(ht, key); + if (list == NULL) { + + list = wmem_new(wmem_epan_scope(), someip_parameter_union_t); + + list->id = data[i].id; + list->name = data[i].name; + list->length_of_length = data[i].length_of_length; + list->length_of_type = data[i].length_of_type; + list->pad_to = data[i].pad_to; + list->num_of_items = data[i].num_of_items; + + list->items = (someip_parameter_union_item_t *)wmem_alloc_array(wmem_epan_scope(), someip_parameter_union_item_t, list->num_of_items); + memset(list->items, 0, sizeof(someip_parameter_union_item_t) * list->num_of_items); + + /* create new entry ... */ + g_hash_table_insert(ht, key, list); + } else { + /* dont need it anymore */ + wmem_free(wmem_epan_scope(), key); + } + + /* and now we add to item array */ + if (data[i].num_of_items == list->num_of_items) { + + /* find first empty slot */ + for (j = 0; j < list->num_of_items && list->items[j].name != NULL; j++); + + if (j < list->num_of_items) { + item = &(list->items[j]); + + /* we do not care if we overwrite param */ + item->id = data[i].type_id; + item->name = data[i].type_name; + item->data_type = data[i].data_type; + item->id_ref = data[i].id_ref; + } + } + } +} + +static void +post_update_someip_parameter_union_cb(void) { + /* destroy old hash table, if it exists */ + if (data_someip_parameter_unions) { + g_hash_table_destroy(data_someip_parameter_unions); + data_someip_parameter_unions = NULL; + } + + data_someip_parameter_unions = g_hash_table_new_full(g_int64_hash, g_int64_equal, &someip_payload_free_key, &someip_payload_free_generic_data); + post_update_someip_parameter_union_read_in_data(someip_parameter_unions, someip_parameter_unions_num, data_someip_parameter_unions); +} + +UAT_HEX_CB_DEF(someip_parameter_base_type_list, id, someip_parameter_base_type_list_uat_t) +UAT_CSTRING_CB_DEF(someip_parameter_base_type_list, name, someip_parameter_base_type_list_uat_t) +UAT_CSTRING_CB_DEF(someip_parameter_base_type_list, data_type, someip_parameter_base_type_list_uat_t) +UAT_DEC_CB_DEF(someip_parameter_base_type_list, big_endian, someip_parameter_base_type_list_uat_t) +UAT_DEC_CB_DEF(someip_parameter_base_type_list, bitlength_base_type, someip_parameter_base_type_list_uat_t) +UAT_DEC_CB_DEF(someip_parameter_base_type_list, bitlength_encoded_type, someip_parameter_base_type_list_uat_t) + +static void * +copy_someip_parameter_base_type_list_cb(void* n, const void* o, size_t size _U_) { + someip_parameter_base_type_list_uat_t *new_rec = (someip_parameter_base_type_list_uat_t*)n; + const someip_parameter_base_type_list_uat_t *old_rec = (const someip_parameter_base_type_list_uat_t*)o; + + if (old_rec->name) { + new_rec->name = g_strdup(old_rec->name); + } else { + new_rec->name = NULL; + } + + if (old_rec->data_type) { + new_rec->data_type = g_strdup(old_rec->data_type); + } else { + new_rec->data_type = NULL; + } + + new_rec->id = old_rec->id; + new_rec->big_endian = old_rec->big_endian; + new_rec->bitlength_base_type = old_rec->bitlength_base_type; + new_rec->bitlength_encoded_type = old_rec->bitlength_encoded_type; + + return new_rec; +} + +static gboolean +update_someip_parameter_base_type_list(void *r, char **err) { + someip_parameter_base_type_list_uat_t *rec = (someip_parameter_base_type_list_uat_t *)r; + + if (rec->id > 0xffffffff) { + *err = g_strdup_printf("We currently only support 32 bit IDs (%i) Name: %s", rec->id, rec->name); + return FALSE; + } + + if (rec->big_endian != 0 && rec->big_endian != 1) { + *err = g_strdup_printf("Big Endian can be only 0 or 1 but not %d (IDs: %i Name: %s)", rec->big_endian, rec->id, rec->name); + return FALSE; + } + + return TRUE; +} + +static void +free_someip_parameter_base_type_list_cb(void*r) { + someip_parameter_base_type_list_uat_t* rec = (someip_parameter_base_type_list_uat_t*)r; + if (rec->name) g_free(rec->name); + if (rec->data_type) g_free(rec->data_type); +} + +static void +post_update_someip_parameter_base_type_list_cb(void) { + guint i; + gint64* key = NULL; + + /* destroy old hash table, if it exists */ + if (data_someip_parameter_base_type_list) { + g_hash_table_destroy(data_someip_parameter_base_type_list); + data_someip_parameter_base_type_list = NULL; + } + + /* we dont need to free the data as long as we don't alloc it first */ + data_someip_parameter_base_type_list = g_hash_table_new_full(g_int64_hash, g_int64_equal, &someip_payload_free_key, NULL); + + if (data_someip_parameter_base_type_list == NULL || someip_parameter_base_type_list == NULL || someip_parameter_base_type_list_num == 0) { + return; + } + + if (someip_parameter_base_type_list_num > 0) { + for (i = 0; i < someip_parameter_base_type_list_num; i++) { + key = wmem_new(wmem_epan_scope(), gint64); + *key = someip_parameter_base_type_list[i].id; + + g_hash_table_insert(data_someip_parameter_base_type_list, key, &someip_parameter_base_type_list[i]); + } + } +} + +UAT_HEX_CB_DEF(someip_parameter_strings, id, someip_parameter_string_uat_t) +UAT_CSTRING_CB_DEF(someip_parameter_strings, name, someip_parameter_string_uat_t) +UAT_CSTRING_CB_DEF(someip_parameter_strings, encoding, someip_parameter_string_uat_t) +UAT_DEC_CB_DEF(someip_parameter_strings, dynamic_length, someip_parameter_string_uat_t) +UAT_DEC_CB_DEF(someip_parameter_strings, max_length, someip_parameter_string_uat_t) +UAT_DEC_CB_DEF(someip_parameter_strings, length_of_length, someip_parameter_string_uat_t) +UAT_DEC_CB_DEF(someip_parameter_strings, big_endian, someip_parameter_string_uat_t) +UAT_DEC_CB_DEF(someip_parameter_strings, pad_to, someip_parameter_string_uat_t) + +static void * +copy_someip_parameter_string_list_cb(void* n, const void* o, size_t size _U_) { + someip_parameter_string_uat_t* new_rec = (someip_parameter_string_uat_t*)n; + const someip_parameter_string_uat_t* old_rec = (const someip_parameter_string_uat_t*)o; + + if (old_rec->name) { + new_rec->name = g_strdup(old_rec->name); + } else { + new_rec->name = NULL; + } + + if (old_rec->encoding) { + new_rec->encoding = g_strdup(old_rec->encoding); + } else { + new_rec->encoding = NULL; + } + + new_rec->id = old_rec->id; + new_rec->dynamic_length = old_rec->dynamic_length; + new_rec->max_length = old_rec->max_length; + new_rec->length_of_length = old_rec->length_of_length; + new_rec->big_endian = old_rec->big_endian; + new_rec->pad_to = old_rec->pad_to; + + return new_rec; +} + +static gboolean +update_someip_parameter_string_list(void *r, char **err) { + someip_parameter_string_uat_t *rec = (someip_parameter_string_uat_t *)r; + + if (rec->id > 0xffffffff) { + *err = g_strdup_printf("We currently only support 32 bit IDs (%i) Name: %s", rec->id, rec->name); + return FALSE; + } + + if (rec->dynamic_length != 0 && rec->dynamic_length != 1) { + *err = g_strdup_printf("Dynamic Length can be only 0 or 1 but not %d (IDs: %i Name: %s)", rec->dynamic_length, rec->id, rec->name); + return FALSE; + } + + if (rec->big_endian != 0 && rec->big_endian != 1) { + *err = g_strdup_printf("Big Endian can be only 0 or 1 but not %d (IDs: %i Name: %s)", rec->big_endian, rec->id, rec->name); + return FALSE; + } + + if (rec->max_length > 0xffffffff) { + *err = g_strdup_printf("We currently only support 32 bit max_length (%i) Name: %s", rec->max_length, rec->name); + return FALSE; + } + + if (rec->length_of_length != 0 && rec->length_of_length != 8 && rec->length_of_length != 16 && rec->length_of_length != 32) { + *err = g_strdup_printf("length_of_length can be only 0, 8, 16, or 32 but not %d (IDs: %i Name: %s)", rec->length_of_length, rec->id, rec->name); + return FALSE; + } + + + return TRUE; +} + +static void +free_someip_parameter_string_list_cb(void*r) { + someip_parameter_string_uat_t* rec = (someip_parameter_string_uat_t*)r; + if (rec->name) g_free(rec->name); + if (rec->encoding) g_free(rec->encoding); +} + +static void +post_update_someip_parameter_string_list_cb(void) { + guint i; + gint64* key = NULL; + + /* destroy old hash table, if it exists */ + if (data_someip_parameter_strings) { + g_hash_table_destroy(data_someip_parameter_strings); + data_someip_parameter_strings = NULL; + } + + /* we dont need to free the data as long as we don't alloc it first */ + data_someip_parameter_strings = g_hash_table_new_full(g_int64_hash, g_int64_equal, &someip_payload_free_key, NULL); + + if (data_someip_parameter_strings == NULL || someip_parameter_strings == NULL || someip_parameter_strings_num == 0) { + return; + } + + if (someip_parameter_strings_num > 0) { + for (i = 0; i < someip_parameter_strings_num; i++) { + key = wmem_new(wmem_epan_scope(), gint64); + *key = someip_parameter_strings[i].id; + + g_hash_table_insert(data_someip_parameter_strings, key, &someip_parameter_strings[i]); + } + } +} + +UAT_HEX_CB_DEF(someip_parameter_typedefs, id, someip_parameter_typedef_uat_t) +UAT_CSTRING_CB_DEF(someip_parameter_typedefs, name, someip_parameter_typedef_uat_t) +UAT_DEC_CB_DEF(someip_parameter_typedefs, data_type, someip_parameter_typedef_uat_t) +UAT_HEX_CB_DEF(someip_parameter_typedefs, id_ref, someip_parameter_typedef_uat_t) + +static void * +copy_someip_parameter_typedef_list_cb(void* n, const void* o, size_t size _U_) { + someip_parameter_typedef_uat_t *new_rec = (someip_parameter_typedef_uat_t*)n; + const someip_parameter_typedef_uat_t *old_rec = (const someip_parameter_typedef_uat_t*)o; + + if (old_rec->name) { + new_rec->name = g_strdup(old_rec->name); + } else { + new_rec->name = NULL; + } + + new_rec->id = old_rec->id; + new_rec->data_type = old_rec->data_type; + new_rec->id_ref = old_rec->id_ref; + + return new_rec; +} + +static gboolean +update_someip_parameter_typedef_list(void *r, char **err) { + someip_parameter_typedef_uat_t *rec = (someip_parameter_typedef_uat_t *)r; + + if (rec->id > 0xffffffff) { + *err = g_strdup_printf("We currently only support 32 bit IDs (%i) Name: %s", rec->id, rec->name); + return FALSE; + } + + return TRUE; +} + +static void +free_someip_parameter_typedef_list_cb(void*r) { + someip_parameter_typedef_uat_t* rec = (someip_parameter_typedef_uat_t*)r; + if (rec->name) g_free(rec->name); +} + +static void +post_update_someip_parameter_typedef_list_cb(void) { + guint i; + gint64 *key = NULL; + + /* destroy old hash table, if it exists */ + if (data_someip_parameter_typedefs) { + g_hash_table_destroy(data_someip_parameter_typedefs); + data_someip_parameter_typedefs = NULL; + } + + /* we dont need to free the data as long as we don't alloc it first */ + data_someip_parameter_typedefs = g_hash_table_new_full(g_int64_hash, g_int64_equal, &someip_payload_free_key, NULL); + + if (data_someip_parameter_typedefs == NULL || someip_parameter_typedefs == NULL || someip_parameter_typedefs_num == 0) { + return; + } + + if (someip_parameter_typedefs_num > 0) { + for (i = 0; i < someip_parameter_typedefs_num; i++) { + /* key: ID [32bit] */ + key = wmem_new(wmem_epan_scope(), gint64); + *key = someip_parameter_typedefs[i].id; + g_hash_table_insert(data_someip_parameter_typedefs, key, &someip_parameter_typedefs[i]); + } + } +} + +static void +expert_someip_payload_truncated(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, gint offset, gint length) { + proto_tree_add_expert(tree, pinfo, &ef_someip_payload_truncated, tvb, offset, length); + col_append_str(pinfo->cinfo, COL_INFO, " [SOME/IP Payload: Truncated payload!]"); +} + +static void +expert_someip_payload_config_error(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, gint offset, gint length, const char *message) { + proto_tree_add_expert_format(tree, pinfo, &ef_someip_payload_config_error, tvb, offset, length, "SOME/IP Payload: %s", message); + col_append_str(pinfo->cinfo, COL_INFO, " [SOME/IP Payload: Config Error]"); +} + +static void +expert_someip_payload_alignment_error(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, gint offset, gint length) { + proto_tree_add_expert(tree, pinfo, &ef_someip_payload_alignment_error, tvb, offset, length); + col_append_str(pinfo->cinfo, COL_INFO, " [SOME/IP Payload: Alignment problem]"); +} + +/******************************************* + ******** SOME/IP Payload Dissector ******** + *******************************************/ + +static int +dissect_someip_payload_parameter(tvbuff_t* tvb, packet_info* pinfo, proto_tree *tree, gint offset, gint offset_bits, guint8 data_type, guint32 idref, gchar *name); + +/* add a flexible size length field, -1 for error*/ +static gint64 +dissect_someip_payload_length_field(tvbuff_t* tvb, packet_info* pinfo, proto_tree *subtree, gint offset, gint length_of_length_field) { + guint32 tmp = 0; + + switch (length_of_length_field) { + case 8: + proto_tree_add_item_ret_uint(subtree, hf_payload_length_field_8bit, tvb, offset, length_of_length_field / 8, ENC_NA, &tmp); + break; + case 16: + proto_tree_add_item_ret_uint(subtree, hf_payload_length_field_16bit, tvb, offset, length_of_length_field / 8, ENC_BIG_ENDIAN, &tmp); + break; + case 32: + proto_tree_add_item_ret_uint(subtree, hf_payload_length_field_32bit, tvb, offset, length_of_length_field / 8, ENC_BIG_ENDIAN, &tmp); + break; + default: + proto_tree_add_expert_format(subtree, pinfo, &ef_someip_payload_config_error, tvb, offset, 0, + "SOME/IP: Payload: length of length field does not make sense: %d bits", length_of_length_field); + col_append_str(pinfo->cinfo, COL_INFO, " [SOME/IP: Payload Config Error]"); + return -1; + } + + return (gint64)tmp; +} + +/* add a flexible size type field */ +static gint64 +dissect_someip_payload_type_field(tvbuff_t* tvb, packet_info* pinfo, proto_tree *subtree, gint offset, gint length_of_type_field) { + guint32 tmp = 0; + + switch (length_of_type_field) { + case 8: + proto_tree_add_item_ret_uint(subtree, hf_payload_type_field_8bit, tvb, offset, length_of_type_field / 8, ENC_NA, &tmp); + break; + case 16: + proto_tree_add_item_ret_uint(subtree, hf_payload_type_field_16bit, tvb, offset, length_of_type_field / 8, ENC_BIG_ENDIAN, &tmp); + break; + case 32: + proto_tree_add_item_ret_uint(subtree, hf_payload_type_field_32bit, tvb, offset, length_of_type_field / 8, ENC_BIG_ENDIAN, &tmp); + break; + default: + proto_tree_add_expert_format(subtree, pinfo, &ef_someip_payload_config_error, tvb, offset, 0, + "SOME/IP: Payload: length of type field does not make sense: %d bits", length_of_type_field); + col_append_str(pinfo->cinfo, COL_INFO, " [SOME/IP: Payload Config Error]"); + return -1; + } + + return (gint64)tmp; +} + +static gint +dissect_someip_payload_base_type(tvbuff_t* tvb, packet_info* pinfo _U_, proto_tree *tree, gint offset, gint offset_bits, guint8 data_type, guint32 id, gchar* name) { + someip_payload_parameter_base_type_list_t *base_type = NULL; + someip_payload_parameter_enum_t *enum_config = NULL; + + guint32 basetype_id = 0; + guint32 enum_id = 0; + + gint buf_length = -1; + gint param_length = -1; + guint32 bit_length = 0; + + proto_item *ti = NULL; + + guint64 value = 0; + guint8 tmp = 0; + gint tmp_bit_count = 8; + gboolean value_set = FALSE; + + gint32 i = 0; + guint32 j = 0; + gchar *value_name = NULL; + + gint offset_end = 0; + gint offset_end_bits = 0; + + gboolean big_endian = TRUE; + + if (offset_bits < 0) { + return 0; + } + + switch (data_type) { + case SOMEIP_PAYLOAD_PARAMETER_DATA_TYPE_BASE_TYPE: + basetype_id = id; + break; + case SOMEIP_PAYLOAD_PARAMETER_DATA_TYPE_ENUM: + enum_id = id; + enum_config = get_enum_config(enum_id); + if (enum_config == NULL) { + return 0; + } + basetype_id = enum_config->id_ref; + break; + default: + return 0; + } + + base_type = get_base_type_config(basetype_id); + if (base_type == NULL) { + return 0; + } + + big_endian = base_type->big_endian; + + buf_length = tvb_captured_length_remaining(tvb, 0); + + bit_length = base_type->bitlength_encoded_type; + /* +7 to round up, if more than 0 bits */ + param_length = (gint)((offset_bits + bit_length + 7) / 8); + + if (param_length <= buf_length - offset) { + + if (name == NULL) { + ti = proto_tree_add_string_format(tree, hf_payload_str_base, tvb, offset, param_length, base_type->name, "[%s]", base_type->name); + } else { + ti = proto_tree_add_string_format(tree, hf_payload_str_base, tvb, offset, param_length, base_type->name, "%s [%s]", name, base_type->name); + } + + /* Regular (non-shortened!) SOME/IP types! */ + if (offset_bits == 0 && base_type->bitlength_base_type == bit_length && bit_length % 8 == 0) { + if (strcmp(base_type->data_type, "uint8") == 0 && base_type->bitlength_base_type == 8) { + value = tvb_get_guint8(tvb, offset); + value_set = TRUE; + proto_item_append_text(ti, ": %u", (guint8)value); + } else if (strcmp(base_type->data_type, "uint16") == 0 && base_type->bitlength_base_type == 16) { + if (big_endian) { + value = tvb_get_ntohs(tvb, offset); + } else { + value = tvb_get_letohs(tvb, offset); + } + value_set = TRUE; + proto_item_append_text(ti, ": %u", (guint16)value); + } else if (strcmp(base_type->data_type, "uint32") == 0 && base_type->bitlength_base_type == 32) { + if (big_endian) { + value = tvb_get_ntohl(tvb, offset); + } else { + value = tvb_get_letohl(tvb, offset); + } + value_set = TRUE; + proto_item_append_text(ti, ": %u", (guint32)value); + } else if (strcmp(base_type->data_type, "uint64") == 0 && base_type->bitlength_base_type == 64) { + if (big_endian) { + value = tvb_get_ntoh64(tvb, offset); + } else { + value = tvb_get_letoh64(tvb, offset); + } + value_set = TRUE; + proto_item_append_text(ti, ": %" G_GUINT64_FORMAT, value); + } else if (strcmp(base_type->data_type, "int8") == 0 && base_type->bitlength_base_type == 8) { + proto_item_append_text(ti, ": %d", (gint8)tvb_get_guint8(tvb, offset)); + } else if (strcmp(base_type->data_type, "int16") == 0 && base_type->bitlength_base_type == 16) { + if (big_endian) { + proto_item_append_text(ti, ": %d", (gint16)tvb_get_ntohs(tvb, offset)); + } else { + proto_item_append_text(ti, ": %d", (gint16)tvb_get_letohs(tvb, offset)); + } + } else if (strcmp(base_type->data_type, "int32") == 0 && base_type->bitlength_base_type == 32) { + if (big_endian) { + proto_item_append_text(ti, ": %d", (gint32)tvb_get_ntohl(tvb, offset)); + } else { + proto_item_append_text(ti, ": %d", (gint32)tvb_get_letohl(tvb, offset)); + } + } else if (strcmp(base_type->data_type, "int64") == 0 && base_type->bitlength_base_type == 64) { + if (big_endian) { + proto_item_append_text(ti, ": %" G_GINT64_FORMAT, (gint64)tvb_get_ntoh64(tvb, offset)); + } else { + proto_item_append_text(ti, ": %" G_GINT64_FORMAT, (gint64)tvb_get_letoh64(tvb, offset)); + } + } else if (strcmp(base_type->data_type, "float32") == 0 && base_type->bitlength_base_type == 32) { + if (big_endian) { + proto_item_append_text(ti, ": %f", (float)tvb_get_ntohieee_float(tvb, offset)); + } else { + proto_item_append_text(ti, ": %f", (float)tvb_get_letohieee_float(tvb, offset)); + } + } else if (strcmp(base_type->data_type, "float64") == 0 && base_type->bitlength_base_type == 64) { + if (big_endian) { + proto_item_append_text(ti, ": %f", (double)tvb_get_ntohieee_double(tvb, offset)); + } else { + proto_item_append_text(ti, ": %f", (double)tvb_get_letohieee_double(tvb, offset)); + } + } + } else { + /* Shortened datatypes (e.g.CAN over SOME/IP) */ + + offset_end = (gint)((8 * offset + offset_bits + bit_length) / 8); + offset_end_bits = (gint)((8 * offset + offset_bits + bit_length) % 8); + + if (!big_endian) { + /* offset and offset_end need to be included */ + for (i = offset_end; i >= offset; i--) { + + if (i != offset_end || offset_end_bits != 0) { + tmp = tvb_get_guint8(tvb, i); + tmp_bit_count = 8; + + if (i == offset_end) { + tmp = tmp & (0xff >> (8 - offset_end_bits)); + /* don't need to shift value, in the first round */ + tmp_bit_count = 0; + } + + if (i == offset) { + tmp >>= offset_bits; + tmp_bit_count = 8 - offset_bits; + } + + value <<= (guint)tmp_bit_count; + value |= tmp; + } + } + + } else { + /* offset_end needs to be included. */ + for (i = offset; i <= offset_end; i++) { + + /* Do not read the last byte, if you do not need any bit of it. Else we read behind buffer! */ + if (i != offset_end || offset_end_bits != 0) { + tmp = tvb_get_guint8(tvb, i); + tmp_bit_count = 8; + + if (i == offset) { + tmp = tmp & (0xff >> offset_bits); + /* don't need to shift value, in the first round */ + tmp_bit_count = 0; + } + + if (i == offset_end) { + tmp >>= 8 - offset_end_bits; + tmp_bit_count = offset_end_bits; + } + + value <<= (guint)tmp_bit_count; + value |= tmp; + } + } + } + + value_set = TRUE; + proto_item_append_text(ti, ": %ld (0x%lx)", value, value); + + } + } else { + return 0; + } + + if (enum_config != NULL && value_set == TRUE) { + for (j = 0; j < enum_config->num_of_items; j++) { + if (enum_config->items[j].value == value) { + value_name = enum_config->items[j].name; + break; + } + } + if (value_name != NULL) { + proto_item_append_text(ti, " (%s)", value_name); + } + } + + return (gint)bit_length; +} + +static int +dissect_someip_payload_string(tvbuff_t* tvb, packet_info* pinfo, proto_tree *tree, gint offset, gint offset_bits, guint32 id, gchar* name) { + someip_payload_parameter_string_t *config = NULL; + + guint8 *buf = NULL; + guint32 i = 0; + guint32 ret = 0; + + proto_item *ti = NULL; + proto_tree *subtree = NULL; + gint64 tmp = 0; + guint32 length = 0; + gint offset_orig = offset; + gint offset_bits_orig = offset_bits; + + guint str_encoding = 0; + config = get_string_config(id); + + if (config == NULL || offset_bits != 0) { + return 0; + } + + if (tvb_captured_length_remaining(tvb, 0) < (gint)(config->length_of_length >> 3)) { + proto_tree_add_string(tree, hf_payload_str_string, tvb, offset, 0, "ERROR: not enough bytes left for configured string length field!"); + return 0; + }; + + if (config->length_of_length == 0) { + length = config->max_length; + } else { + tmp = dissect_someip_payload_length_field(tvb, pinfo, tree, offset, config->length_of_length); + if (tmp < 0) { + /* error */ + return config->length_of_length / 8; + } + length = (guint32)tmp; + offset += config->length_of_length / 8; + } + + if ((guint32)tvb_captured_length_remaining(tvb, offset) < length) { + proto_tree_add_string(tree, hf_payload_str_string, tvb, offset, 0, "ERROR: not enough bytes left for configured string data!"); + return 0; + } + + if (strcmp(config->encoding, "utf-8") == 0) { + str_encoding = ENC_UTF_8; + } else if (strcmp(config->encoding, "utf-16") == 0) { + str_encoding = ENC_UTF_16; + } else { + str_encoding = ENC_ASCII; + } + + buf = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, length, str_encoding); + + /* sanitizing buffer */ + if (str_encoding == ENC_ASCII || str_encoding == ENC_UTF_8) { + for (i = 0; i < length; i++) { + if (buf[i] > 0x00 && buf[i] < 0x20) { + buf[i] = 0x20; + } + } + } + + ti = proto_tree_add_string_format(tree, hf_payload_str_string, tvb, offset_orig, length + (offset - offset_orig), buf, "%s [%s]: %s", name, config->name, buf); + subtree = proto_item_add_subtree(ti, ett_someip_string); + proto_tree_add_string_format(subtree, hf_payload_str_string, tvb, offset_orig, offset - offset_orig, buf, "string length: %d", length); + offset += length; + + ret = 8 * (offset - offset_orig) + (offset_bits - offset_bits_orig); + + return ret; +} + +static int +dissect_someip_payload_struct(tvbuff_t* tvb, packet_info* pinfo, proto_tree *tree, gint offset_orig, gint offset_bits_orig, guint32 id, gchar* name) { + someip_payload_parameter_item_t* item = NULL; + someip_payload_parameter_struct_t *config = NULL; + + proto_tree *subtree = NULL; + proto_item *ti = NULL; + + gint offset = offset_orig; + gint offset_bits = offset_bits_orig; + gint bits_parsed = 0; + + guint i = 0; + + config = get_struct_config(id); + + if (config == NULL || tree == NULL || tvb == NULL) { + return 0; + } + + ti = proto_tree_add_string_format(tree, hf_payload_str_struct, tvb, offset, 0, config->struct_name, "struct %s [%s]", name, config->struct_name); + subtree = proto_item_add_subtree(ti, ett_someip_struct); + + for (i = 0; i < config->num_of_items; i++) { + item = &(config->items[i]); + bits_parsed = dissect_someip_payload_parameter(tvb, pinfo, subtree, offset, offset_bits, (guint8)item->data_type, item->id_ref, item->name); + offset = (8 * offset + offset_bits + bits_parsed) / 8; + offset_bits = (8 * offset + offset_bits + bits_parsed) % 8; + } + + if (offset_bits == 0) { + proto_item_set_end(ti, tvb, offset); + } else { + proto_item_set_end(ti, tvb, offset + 1); + } + + return 8 * (offset - offset_orig) + (offset_bits - offset_bits_orig); +} + +static int +dissect_someip_payload_typedef(tvbuff_t* tvb, packet_info* pinfo, proto_tree *tree, gint offset, gint offset_bits, guint32 id, gchar* name _U_) { + gint bits_parsed = 0; + someip_payload_parameter_typedef_t *config = NULL; + + config = get_typedef_config(id); + + if (config == NULL) { + return 0; + } + + /* we basically skip over the typedef for now */ + bits_parsed = dissect_someip_payload_parameter(tvb, pinfo, tree, offset, offset_bits, (guint8)config->data_type, config->id_ref, config->name); + + return bits_parsed; +} + +/* returns bytes parsed, length needs to be gint to encode "non-existing" as -1 */ +static int +dissect_someip_payload_array_dim_length(tvbuff_t* tvb, packet_info* pinfo, proto_tree *tree, gint offset_orig, gint *length, gint *lower_limit, gint *upper_limit, + someip_parameter_array_t *config, gint current_dim) { + gint offset = offset_orig; + gint64 tmp = 0; + + *lower_limit = config->dims[current_dim].lower_limit; + *upper_limit = config->dims[current_dim].upper_limit; + + /* length needs to be -1, if we do not have a dynamic length array */ + *length = -1; + + if (config->dims[current_dim].length_of_length > 0) { + /* we are filling the length with number of bytes we found in the packet */ + tmp = dissect_someip_payload_length_field(tvb, pinfo, tree, offset, config->dims[current_dim].length_of_length); + if (tmp < 0) { + /* leave *length = -1 */ + return config->dims[current_dim].length_of_length/8; + } + *length = (gint32)tmp; + offset += config->dims[current_dim].length_of_length/8; + } else { + /* without a length field, the number of elements needs be fixed */ + if (config->dims[current_dim].lower_limit != config->dims[current_dim].upper_limit) { + proto_tree_add_expert_format(tree, pinfo, &ef_someip_payload_static_array_min_not_max, tvb, offset_orig, 0, + "Static array config with Min!=Max (%d, %d)", config->dims[current_dim].lower_limit, config->dims[current_dim].upper_limit); + col_append_str(pinfo->cinfo, COL_INFO, " [SOME/IP Payload: Static array config with Min!=Max!]"); + + return 0; + } + } + + return offset - offset_orig; +} + +/* returns bits parsed, length needs to be gint to encode "non-existing" as -1 */ +static gint +dissect_someip_payload_array_payload(tvbuff_t* tvb, packet_info* pinfo, proto_tree *tree, gint offset_orig, gint length, gint lower_limit, gint upper_limit, + someip_parameter_array_t *config) { + tvbuff_t *subtvb = NULL; + guint32 offset = offset_orig; + guint32 offset_bits = 0; + guint32 bits_parsed = 0; + guint32 ret = 0; + gint count = 0; + + if (length != -1) { + if (length <= tvb_captured_length_remaining(tvb, offset)) { + subtvb = tvb_new_subset_length_caplen(tvb, offset, length, length); + } else { + expert_someip_payload_truncated(tree, pinfo, tvb, offset, tvb_captured_length_remaining(tvb, offset)); + return tvb_captured_length_remaining(tvb, offset); + } + } else { + subtvb = tvb; + } + + /* created subtvb. so we set offset=0 */ + offset = 0; + while ((length == -1 && count < upper_limit) || ((gint)(8 * offset + offset_bits) < 8 * length)) { + bits_parsed = dissect_someip_payload_parameter(subtvb, pinfo, tree, offset, offset_bits, (guint8)config->data_type, config->id_ref, config->name); + if (bits_parsed == 0) { + return 1; + } + offset = (8 * offset + bits_parsed) / 8; + offset_bits = (8 * offset + bits_parsed) % 8; + count++; + } + + if (countupper_limit) { + proto_tree_add_expert_format(tree, pinfo, &ef_someip_payload_dyn_array_not_within_limit, tvb, offset_orig, length, + "Number of items (%d) outside limit %d-%d", count, lower_limit, upper_limit); + col_append_str(pinfo->cinfo, COL_INFO, " [SOME/IP Payload: Dynamic array does not stay between Min and Max values]"); + } + + ret = 8 * offset + offset_bits; + return ret; +} + +/* returns bits parsed */ +static gint +dissect_someip_payload_array_dim(tvbuff_t* tvb, packet_info* pinfo, proto_tree *tree, gint offset_orig, gint length, gint lower_limit, gint upper_limit, someip_parameter_array_t *config, guint current_dim, gchar* name) { + proto_tree *subtree = NULL; + gint sub_length = 0; + gint sub_lower_limit = 0; + gint sub_upper_limit = 0; + gint i = 0; + + gint sub_offset = 0; + gint offset = offset_orig; + gint offset_bits = 0; + gint ret = 0; + gint len_of_len = 0; + + if (config->num_of_dims == current_dim + 1) { + /* only payload left. :) */ + offset_bits += dissect_someip_payload_array_payload(tvb, pinfo, tree, offset, length, lower_limit, upper_limit, config); + } else { + if (length != -1) { + while (offset < offset_orig + (gint)length) { + sub_offset = offset; + offset += dissect_someip_payload_array_dim_length(tvb, pinfo, tree, offset, &sub_length, &sub_lower_limit, &sub_upper_limit, config, current_dim + 1); + len_of_len = offset - sub_offset; + + if (tvb_captured_length_remaining(tvb, offset) < (gint)sub_length) { + expert_someip_payload_truncated(tree, pinfo, tvb, offset, tvb_captured_length_remaining(tvb, offset)); + return 0; + } + + subtree = proto_tree_add_subtree_format(tree, tvb, sub_offset, sub_length + len_of_len, ett_someip_array_dim, NULL, "subarray (dim: %d, limit %d-%d)", current_dim + 1, sub_lower_limit, sub_upper_limit); + + offset_bits += dissect_someip_payload_array_dim(tvb, pinfo, subtree, offset, sub_length, sub_lower_limit, sub_upper_limit, config, current_dim + 1, name); + offset = (8 * offset + offset_bits) / 8; + offset_bits = (8 * offset + offset_bits) % 8; + } + } else { + /* Multi-dim static array */ + sub_lower_limit = config->dims[current_dim].lower_limit; + sub_upper_limit = config->dims[current_dim].upper_limit; + + for (i = 0; i < upper_limit; i++) { + offset += dissect_someip_payload_array_dim(tvb, pinfo, tree, offset, -1, sub_lower_limit, sub_upper_limit, config, current_dim + 1, name); + } + } + } + + ret = 8 * (offset - offset_orig) + (offset_bits - 0); + return ret; +} + +static int +dissect_someip_payload_array(tvbuff_t* tvb, packet_info* pinfo, proto_tree *tree, gint offset_orig, gint offset_bits_orig, guint32 id, gchar* name) { + someip_parameter_array_t *config = NULL; + + proto_tree *subtree; + + gint offset = offset_orig; + gint offset_bits = offset_bits_orig; + + gint length = 0; + gint lower_limit = 0; + gint upper_limit = 0; + + config = get_array_config(id); + + if (config == NULL) { + return 0; + } + + if (config->num_of_dims == 0 || config->dims == NULL) { + expert_someip_payload_config_error(tree, pinfo, tvb, offset, 0, "Array config has not enough dimensions for this array!"); + return 0; + } + + if (offset_bits_orig != 0) { + expert_someip_payload_alignment_error(tree, pinfo, tvb, offset, 0); + return 0; + } + + offset += dissect_someip_payload_array_dim_length(tvb, pinfo, tree, offset_orig, &length, &lower_limit, &upper_limit, config, 0); + + if (length != -1) { + subtree = proto_tree_add_subtree_format(tree, tvb, offset_orig, length + (offset - offset_orig), ett_someip_array, NULL, "array %s (elements limit: %d-%d)", name, lower_limit, upper_limit); + } else { + subtree = proto_tree_add_subtree_format(tree, tvb, offset_orig, length + (offset - offset_orig), ett_someip_array, NULL, "array %s (elements limit: %d)", name, upper_limit); + } + + offset_bits += dissect_someip_payload_array_dim(tvb, pinfo, subtree, offset, length, lower_limit, upper_limit, config, 0, name); + + return 8 * (length + (offset - offset_orig)) + (offset_bits - offset_bits_orig); +} + +static int +dissect_someip_payload_union(tvbuff_t* tvb, packet_info* pinfo, proto_tree *tree, gint offset_orig, gint offset_bits_orig, guint32 id, gchar* name) { + someip_parameter_union_t *config = NULL; + someip_parameter_union_item_t *item = NULL; + proto_tree *subtree = NULL; + + tvbuff_t *subtvb; + gint buf_length = -1; + + gint64 tmp = 0; + guint32 length = 0; + guint32 type = 0; + + guint32 i = 0; + + gint offset = offset_orig; + gint offset_bits = offset_bits_orig; + + config = get_union_config(id); + buf_length = tvb_captured_length_remaining(tvb, 0); + + if (config == NULL) { + expert_someip_payload_config_error(tree, pinfo, tvb, offset, 0, "Union ID not configured"); + return 0; + } + + if (offset_bits_orig != 0) { + expert_someip_payload_alignment_error(tree, pinfo, tvb, offset_orig, 0); + return 0; + } + + if ((config->length_of_length + config->length_of_type) / 8 > (guint)buf_length - offset) { + expert_someip_payload_truncated(tree, pinfo, tvb, offset, tvb_captured_length_remaining(tvb, offset)); + return 0; + } + + subtree = proto_tree_add_subtree_format(tree, tvb, offset_orig, offset - offset_orig + length, ett_someip_union, NULL, "union %s [%s]", name, config->name); + + tmp = dissect_someip_payload_length_field(tvb, pinfo, subtree, offset_orig, config->length_of_length); + if (tmp == -1) { + return 8 * (offset - offset_orig) + (offset_bits - 0); + } else { + length = (guint32)tmp; + } + + tmp = dissect_someip_payload_type_field(tvb, pinfo, subtree, offset_orig + config->length_of_length / 8, config->length_of_type); + if (tmp == -1) { + return 8 * (offset - offset_orig) + (offset_bits - 0); + } else { + type = (guint32)tmp; + } + + offset += (config->length_of_length + config->length_of_type) / 8; + + item = NULL; + for (i = 0; i < config->num_of_items; i++) { + if (config->items[i].id == type && config->items[i].name != NULL) { + item = &(config->items[i]); + } + } + + if (item != NULL) { + subtvb = tvb_new_subset_length_caplen(tvb, offset, length, length); + dissect_someip_payload_parameter(subtvb, pinfo, subtree, 0, 0, (guint8)item->data_type, item->id_ref, item->name); + } else { + expert_someip_payload_config_error(tree, pinfo, tvb, offset, 0, "Union type not configured"); + } + + /* there might be some padding present, if 8*length != bits_parsed */ + return 8 * length + config->length_of_type + config->length_of_length; +} + +static int +dissect_someip_payload_parameter(tvbuff_t* tvb, packet_info* pinfo, proto_tree *tree, gint offset, gint offset_bits, guint8 data_type, guint32 idref, gchar *name) { + gint bits_parsed = 0; + + switch (data_type) { + case SOMEIP_PAYLOAD_PARAMETER_DATA_TYPE_TYPEDEF: + bits_parsed = dissect_someip_payload_typedef(tvb, pinfo, tree, offset, offset_bits, idref, name); + break; + case SOMEIP_PAYLOAD_PARAMETER_DATA_TYPE_BASE_TYPE: + case SOMEIP_PAYLOAD_PARAMETER_DATA_TYPE_ENUM: + bits_parsed = dissect_someip_payload_base_type(tvb, pinfo, tree, offset, offset_bits, data_type, idref, name); + break; + case SOMEIP_PAYLOAD_PARAMETER_DATA_TYPE_STRING: + bits_parsed = dissect_someip_payload_string(tvb, pinfo, tree, offset, offset_bits, idref, name); + break; + case SOMEIP_PAYLOAD_PARAMETER_DATA_TYPE_ARRAY: + bits_parsed = dissect_someip_payload_array(tvb, pinfo, tree, offset, offset_bits, idref, name); + break; + case SOMEIP_PAYLOAD_PARAMETER_DATA_TYPE_STRUCT: + bits_parsed = dissect_someip_payload_struct(tvb, pinfo, tree, offset, offset_bits, idref, name); + break; + case SOMEIP_PAYLOAD_PARAMETER_DATA_TYPE_UNION: + bits_parsed = dissect_someip_payload_union(tvb, pinfo, tree, offset, offset_bits, idref, name); + break; + default: + proto_tree_add_expert_format(tree, pinfo, &ef_someip_payload_config_error, tvb, offset, 0, + "SOME/IP: Payload: item->data_type (0x%x) unknown/not implemented yet! name: %s, id_ref: 0x%x", + data_type, name, idref); + col_append_str(pinfo->cinfo, COL_INFO, " [SOME/IP: Payload Config Error]"); + break; + } + + return bits_parsed; +} + +static void +dissect_someip_payload(tvbuff_t* tvb, packet_info* pinfo, proto_item *ti, guint16 serviceid, guint16 methodid, guint8 version, guint8 msgtype) { + someip_parameter_list_t* paramlist = NULL; + someip_payload_parameter_item_t* item = NULL; + + gint length = -1; + gint offset = 0; + gint offset_bits = 0; + gint bits_parsed = 0; + + proto_tree *tree = NULL; + guint i = 0; + + length = tvb_captured_length_remaining(tvb, 0); + tree = proto_item_add_subtree(ti, ett_someip_payload); + paramlist = get_parameter_config(serviceid, methodid, version, msgtype); + + if (paramlist == NULL) { + return; + } + + for (i = 0; i < paramlist->num_of_items; i++) { + item = &(paramlist->items[i]); + bits_parsed = dissect_someip_payload_parameter(tvb, pinfo, tree, offset, offset_bits, (guint8)item->data_type, item->id_ref, item->name); + offset = (8 * offset + offset_bits + bits_parsed) / 8; + offset_bits = (8 * offset + offset_bits + bits_parsed) % 8; + } + + if (length > offset + 1) { + proto_tree_add_item(tree, hf_payload_unparsed, tvb, offset + 1, length - (offset + 1), ENC_NA); + } +} + +/*********************************** + ******** SOME/IP Dissector ******** + ***********************************/ + +static int +dissect_someip_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { + guint32 offset = 0; + guint32 someip_messageid = 0; + guint32 someip_serviceid = 0; + guint32 someip_methodid = 0; + guint32 someip_sessionid = 0; + guint32 someip_length = 0; + const gchar *service_description = NULL; + const gchar *method_description = NULL; + + guint32 someip_payload_length = 0; + tvbuff_t *subtvb = NULL; + + proto_item *ti = NULL; + proto_item *ti_someip = NULL; + proto_tree *someip_tree = NULL; + proto_tree *msgtype_tree = NULL; + + guint32 protocol_version = 0; + guint32 version = 0; + guint32 msgtype = 0; + gboolean msgtype_ack = FALSE; + gboolean msgtype_tp = FALSE; + guint32 retcode = 0; + int tmp = 0; + + gint tvb_length = tvb_captured_length_remaining(tvb, offset); + + static const int *someip_tp_flags[] = { + &hf_someip_tp_reserved, + &hf_someip_tp_more_segments, + NULL + }; + + col_set_str(pinfo->cinfo, COL_PROTOCOL, SOMEIP_NAME); + col_set_str(pinfo->cinfo, COL_INFO, SOMEIP_NAME_LONG); + ti_someip = proto_tree_add_item(tree, proto_someip, tvb, offset, -1, ENC_NA); + someip_tree = proto_item_add_subtree(ti_someip, ett_someip); + + /* we should never get called with less than 8 bytes */ + if (tvb_length < 8) { + return tvb_length; + } + + /* Message ID = Service ID + Method ID*/ + someip_messageid = tvb_get_ntohl(tvb, 0); + ti = proto_tree_add_uint_format_value(someip_tree, hf_someip_messageid, tvb, offset, 4, someip_messageid, "0x%08x", someip_messageid); + PROTO_ITEM_SET_HIDDEN(ti); + + /* Service ID */ + ti = proto_tree_add_item_ret_uint(someip_tree, hf_someip_serviceid, tvb, offset, 2, ENC_BIG_ENDIAN, &someip_serviceid); + service_description = someip_lookup_service_name(someip_serviceid); + if (service_description != NULL) { + proto_item_append_text(ti, " (%s)", service_description); + } + offset += 2; + + /* Method ID */ + ti = proto_tree_add_item_ret_uint(someip_tree, hf_someip_methodid, tvb, offset, 2, ENC_BIG_ENDIAN, &someip_methodid); + method_description = someip_lookup_method_name(someip_serviceid, someip_methodid); + if (method_description != NULL) { + proto_item_append_text(ti, " (%s)", method_description); + } + offset += 2; + + /* Length */ + proto_tree_add_item_ret_uint(someip_tree, hf_someip_length, tvb, offset, 4, ENC_BIG_ENDIAN, &someip_length); + offset += 4; + + /* this checks if value of the header field */ + if (someip_length < 8) { + expert_add_info_format(pinfo, ti_someip, &ef_someip_incomplete_headers, "%s", "SOME/IP length too short (<8 Bytes)!"); + return tvb_length; + } + + /* Add some additional info to the Protocol line and the Info Column*/ + if (service_description == NULL) { + col_add_fstr(pinfo->cinfo, COL_INFO, "%s (Service ID: 0x%04x, Method ID: 0x%04x, Length: %i)", + SOMEIP_NAME_LONG, someip_serviceid, someip_methodid, someip_length); + } else if (method_description == NULL) { + col_add_fstr(pinfo->cinfo, COL_INFO, "%s (Service ID: 0x%04x (%s), Method ID: 0x%04x, Length: %i)", + SOMEIP_NAME_LONG, someip_serviceid, service_description, someip_methodid, someip_length); + } else { + col_add_fstr(pinfo->cinfo, COL_INFO, "%s (Service ID: 0x%04x (%s), Method ID: 0x%04x (%s), Length: %i)", + SOMEIP_NAME_LONG, someip_serviceid, service_description, someip_methodid, method_description, someip_length); + } + proto_item_append_text(ti_someip, " (Service ID: 0x%04x, Method ID: 0x%04x, Length: %i)", someip_serviceid, someip_methodid, someip_length); + + /* check if we have bytes for the rest of the header */ + if (tvb_length < 0 || offset + 8 > (guint32)tvb_length) { + expert_add_info_format(pinfo, ti_someip, &ef_someip_incomplete_headers, "%s", "SOME/IP not enough buffer bytes for header!"); + return tvb_length; + } + + /* Client ID */ + proto_tree_add_item(someip_tree, hf_someip_clientid, tvb, offset, 2, ENC_BIG_ENDIAN); + offset += 2; + + /* Session ID */ + proto_tree_add_item_ret_uint(someip_tree, hf_someip_sessionid, tvb, offset, 2, ENC_BIG_ENDIAN, &someip_sessionid); + offset += 2; + + /* Protocol Version*/ + ti = proto_tree_add_item_ret_uint(someip_tree, hf_someip_protover, tvb, offset, 1, ENC_BIG_ENDIAN, &protocol_version); + if (protocol_version!=SOMEIP_PROTOCOL_VERSION) { + expert_add_info(pinfo, ti, &ef_someip_unknown_version); + } + offset += 1; + + /* Major Version of Service Interface */ + proto_tree_add_item_ret_uint(someip_tree, hf_someip_interface_ver, tvb, offset, 1, ENC_BIG_ENDIAN, &version); + offset += 1; + + /* Message Type */ + ti = proto_tree_add_item_ret_uint(someip_tree, hf_someip_messagetype, tvb, offset, 1, ENC_BIG_ENDIAN, &msgtype); + msgtype_tree = proto_item_add_subtree(ti, ett_someip_msgtype); + proto_tree_add_item_ret_boolean(msgtype_tree, hf_someip_messagetype_ack_flag, tvb, offset, 1, ENC_BIG_ENDIAN, &msgtype_ack); + proto_tree_add_item_ret_boolean(msgtype_tree, hf_someip_messagetype_tp_flag, tvb, offset, 1, ENC_BIG_ENDIAN, &msgtype_tp); + + proto_item_append_text(ti, " (%s)", val_to_str((~SOMEIP_MSGTYPE_TP_MASK)&msgtype, someip_msg_type, "Unknown Message Type")); + if (msgtype_tp) { + proto_item_append_text(ti, " (%s)", SOMEIP_MSGTYPE_TP_STRING); + } + offset += 1; + + /* Return Code */ + ti = proto_tree_add_item_ret_uint(someip_tree, hf_someip_returncode, tvb, offset, 1, ENC_BIG_ENDIAN, &retcode); + proto_item_append_text(ti, " (%s)", val_to_str(retcode, someip_return_code, "Unknown Return Code")); + offset += 1; + + /* lets figure out what we have for the rest */ + if (((guint32)tvb_length >= (someip_length + 8)) ) { + someip_payload_length = someip_length - SOMEIP_HDR_PART1_LEN; + } else { + someip_payload_length = tvb_length - SOMEIP_HDR_LEN; + expert_add_info(pinfo, ti_someip, &ef_someip_message_truncated); + } + + /* Is this a SOME/IP-TP segment? */ + if (msgtype_tp) { + guint32 tp_offset = 0; + gboolean tp_more_segments = FALSE; + gboolean update_col_info = TRUE; + guint32 segment_key; + fragment_item *someip_tp_head = NULL; + proto_tree *tp_tree = NULL; + + ti = proto_tree_add_item(someip_tree, hf_someip_tp, tvb, offset, someip_payload_length, ENC_NA); + tp_tree = proto_item_add_subtree(ti, ett_someip_tp); + + tp_offset = (tvb_get_ntohl(tvb, offset) & SOMEIP_TP_OFFSET_MASK); + tp_more_segments = ((tvb_get_ntohl(tvb, offset) & SOMEIP_TP_OFFSET_MASK_MORE_SEGMENTS) != 0); + /* Why can I not mask an FT_UINT32 without it being shifted. :( . */ + proto_tree_add_uint(tp_tree, hf_someip_tp_offset, tvb, offset, 4, tp_offset); + proto_tree_add_bitmask_with_flags(tp_tree, tvb, offset+3, hf_someip_tp_flags, ett_someip_tp_flags, someip_tp_flags, ENC_BIG_ENDIAN, BMT_NO_TFS | BMT_NO_INT); + offset += 4; + + proto_tree_add_item(tp_tree, hf_someip_payload, tvb, offset, someip_payload_length - SOMEIP_TP_HDR_LEN, ENC_NA); + + if (someip_tp_reassemble && tvb_bytes_exist(tvb, offset, someip_payload_length - SOMEIP_TP_HDR_LEN)) { + segment_key = someip_messageid ^ (version << 24) ^ (msgtype << 16) ^ someip_sessionid; + someip_tp_head = fragment_add_check(&someip_tp_reassembly_table, tvb, offset, pinfo, segment_key, + NULL, tp_offset, someip_payload_length - SOMEIP_TP_HDR_LEN, tp_more_segments); + subtvb = process_reassembled_data(tvb, offset, pinfo, "Reassembled SOME/IP-TP Segment", + someip_tp_head, &someip_tp_frag_items, &update_col_info, someip_tree); + } + } else { + subtvb = tvb_new_subset_length_caplen(tvb, SOMEIP_HDR_LEN, someip_payload_length, someip_payload_length); + } + + if (subtvb!=NULL) { + tvb_length = tvb_captured_length_remaining(subtvb, 0); + if (tvb_length > 0) { + tmp = dissector_try_uint(someip_dissector_table, someip_messageid, subtvb, pinfo, tree); + + /* if no subdissector was found, the generic payload dissector takes over. */ + if (tmp==0) { + ti = proto_tree_add_item(someip_tree, hf_someip_payload, subtvb, 0, tvb_length, ENC_NA); + + if (someip_derserializer_activated) { + dissect_someip_payload(subtvb, pinfo, ti, (guint16)someip_serviceid, (guint16)someip_methodid, (guint8)version, (guint8)(~SOMEIP_MSGTYPE_TP_MASK)&msgtype); + } + } + } + } + + return SOMEIP_HDR_LEN + someip_payload_length; +} + +static guint +get_someip_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void* data _U_) { + return SOMEIP_HDR_PART1_LEN + (guint)tvb_get_ntohl(tvb, offset + 4); +} + +static int +dissect_someip_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { + tcp_dissect_pdus(tvb, pinfo, tree, TRUE, SOMEIP_HDR_PART1_LEN, get_someip_message_len, dissect_someip_message, data); + return tvb_reported_length(tvb); +} + + +static int +dissect_someip_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { + return udp_dissect_pdus(tvb, pinfo, tree, SOMEIP_HDR_PART1_LEN, NULL, get_someip_message_len, dissect_someip_message, data); +} + +static gboolean +test_someip(packet_info *pinfo _U_, tvbuff_t *tvb, int offset _U_, void *data _U_) +{ + if (tvb_captured_length(tvb) < SOMEIP_HDR_LEN) { + return FALSE; + } + + if (tvb_get_guint32(tvb, 4, ENC_BIG_ENDIAN) < 8) { + return FALSE; + } + + if ((tvb_get_guint8(tvb, 12)) != SOMEIP_PROTOCOL_VERSION) { + return FALSE; + } + + if (!try_val_to_str((tvb_get_guint8(tvb, 14) & ~SOMEIP_MSGTYPE_TP_MASK), someip_msg_type)) { + return FALSE; + } + + return TRUE; +} + +static gboolean +dissect_some_ip_heur_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) +{ + if (test_someip(pinfo, tvb, 0, data)) { + tcp_dissect_pdus(tvb, pinfo, tree, TRUE, SOMEIP_HDR_PART1_LEN, get_someip_message_len, dissect_someip_message, data); + return TRUE; + } + return FALSE; +} + +static gboolean +dissect_some_ip_heur_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) +{ + udp_dissect_pdus(tvb, pinfo, tree, SOMEIP_HDR_PART1_LEN, test_someip, get_someip_message_len, dissect_someip_message, data); + return TRUE; +} + +void +proto_register_someip(void) { + module_t *someip_module; + expert_module_t *expert_module_someip; + + uat_t* someip_service_uat; + uat_t* someip_method_uat; + uat_t* someip_eventgroup_uat; + + uat_t *someip_parameter_base_type_list_uat; + uat_t *someip_parameter_strings_uat; + uat_t *someip_parameter_typedefs_uat; + uat_t *someip_parameter_list_uat; + uat_t *someip_parameter_arrays_uat; + uat_t *someip_parameter_structs_uat; + uat_t *someip_parameter_unions_uat; + uat_t *someip_parameter_enums_uat; + + /* data fields */ + static hf_register_info hf[] = { + { &hf_someip_serviceid, + { "Service ID", "someip.serviceid", + FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_methodid, + { "Method ID", "someip.methodid", + FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_messageid, + { "Message ID", "someip.messageid", + FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_length, + { "Length", "someip.length", + FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_clientid, + { "Client ID", "someip.clientid", + FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_sessionid, + { "Session ID", "someip.sessionid", + FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_protover, + { "SOME/IP Version", "someip.protoversion", + FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_interface_ver, + { "Interface Version", "someip.interfaceversion", + FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_messagetype, + { "Message Type", "someip.messagetype", + FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_messagetype_ack_flag, + { "Message Type Ack Flag", "someip.messagetype.ack", + FT_BOOLEAN, 8, NULL, SOMEIP_MSGTYPE_ACK_MASK, NULL, HFILL }}, + { &hf_someip_messagetype_tp_flag, + { "Message Type TP Flag", "someip.messagetype.tp", + FT_BOOLEAN, 8, NULL, SOMEIP_MSGTYPE_TP_MASK, NULL, HFILL }}, + { &hf_someip_returncode, + { "Return Code", "someip.returncode", + FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, + + { &hf_someip_payload, + { "Payload", "someip.payload", + FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + + { &hf_someip_tp, + { "SOME/IP-TP", "someip.tp", + FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_tp_offset, + { "Offset", "someip.tp.offset", + FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, + { &hf_someip_tp_flags, + { "Flags", "someip.tp.flags", + FT_UINT8, BASE_HEX, NULL, SOMEIP_TP_OFFSET_MASK_FLAGS, NULL, HFILL }}, + { &hf_someip_tp_reserved, + { "Reserved", "someip.tp.flags.reserved", + FT_UINT8, BASE_HEX, NULL, SOMEIP_TP_OFFSET_MASK_RESERVED, NULL, HFILL }}, + { &hf_someip_tp_more_segments, + { "More Segments", "someip.tp.flags.more_segments", + FT_BOOLEAN, 8, NULL, SOMEIP_TP_OFFSET_MASK_MORE_SEGMENTS, NULL, HFILL }}, + + {&hf_someip_tp_fragments, + {"SOME/IP-TP segments", "someip.tp.fragments", + FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_someip_tp_fragment, + {"SOME/IP-TP segment", "someip.tp.fragment", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_someip_tp_fragment_overlap, + {"SOME/IP-TP segment overlap", "someip.tp.fragment.overlap", + FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } }, + {&hf_someip_tp_fragment_overlap_conflicts, + {"SOME/IP-TP segment overlapping with conflicting data", "someip.tp.fragment.overlap.conflicts", + FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } }, + {&hf_someip_tp_fragment_multiple_tails, + {"SOME/IP-TP Message has multiple tail fragments", "someip.tp.fragment.multiple_tails", + FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } }, + {&hf_someip_tp_fragment_too_long_fragment, + {"SOME/IP-TP segment too long", "someip.tp.fragment.too_long_fragment", + FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } }, + {&hf_someip_tp_fragment_error, + {"SOME/IP-TP Message defragmentation error", "someip.tp.fragment.error", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_someip_tp_fragment_count, + {"SOME/IP-TP segment count", "someip.tp.fragment.count", + FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } }, + {&hf_someip_tp_reassembled_in, + {"Reassembled in", "someip.tp.reassembled.in", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_someip_tp_reassembled_length, + {"Reassembled length", "someip.tp.reassembled.length", + FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } }, + + {&hf_someip_tp_reassembled_data, + {"Reassembled data", "someip.tp.reassembled.data", + FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } }, + + { &hf_payload_unparsed, + { "Unparsed Payload", "someip.payload.unparsed", + FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_payload_length_field_8bit, + { "Length", "someip.payload.length", + FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } }, + { &hf_payload_length_field_16bit, + { "Length", "someip.payload.length", + FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } }, + { &hf_payload_length_field_32bit, + { "Length", "someip.payload.length", + FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } }, + { &hf_payload_type_field_8bit, + { "Type", "someip.payload.type", + FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } }, + { &hf_payload_type_field_16bit, + { "Type", "someip.payload.type", + FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } }, + { &hf_payload_type_field_32bit, + { "Type", "someip.payload.type", + FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } }, + + { &hf_payload_str_base, { + "(base)", "someip.payload.base", + FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, + { &hf_payload_str_string, { + "(string)", "someip.payload.string", + FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, + { &hf_payload_str_struct, { + "(struct)", "someip.payload.struct", + FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, + }; + + static gint *ett[] = { + &ett_someip, + &ett_someip_msgtype, + &ett_someip_tp, + &ett_someip_tp_flags, + &ett_someip_tp_fragment, + &ett_someip_tp_fragments, + + &ett_someip_payload, + &ett_someip_string, + &ett_someip_array, + &ett_someip_array_dim, + &ett_someip_struct, + &ett_someip_union, + }; + + + /* UATs for user_data fields */ + static uat_field_t someip_service_uat_fields[] = { + UAT_FLD_HEX(someip_service_ident, id, "Service ID", "ID of the SOME/IP Service (16bit hex without leading 0x)"), + UAT_FLD_CSTRING(someip_service_ident, name, "Service Name", "Name of the SOME/IP Service (string)"), + UAT_END_FIELDS + }; + + static uat_field_t someip_method_uat_fields[] = { + UAT_FLD_HEX(someip_method_ident, id, "Service ID", "ID of the SOME/IP Service (16bit hex without leading 0x)"), + UAT_FLD_HEX(someip_method_ident, id2, "Methods ID", "ID of the SOME/IP Method/Event/Notifier (16bit hex without leading 0x)"), + UAT_FLD_CSTRING(someip_method_ident, name, "Method Name", "Name of the SOME/IP Method/Event/Notifier (string)"), + UAT_END_FIELDS + }; + + static uat_field_t someip_eventgroup_uat_fields[] = { + UAT_FLD_HEX(someip_eventgroup_ident, id, "Service ID", "ID of the SOME/IP Service (16bit hex without leading 0x)"), + UAT_FLD_HEX(someip_eventgroup_ident, id2, "Eventgroup ID", "ID of the SOME/IP Eventgroup (16bit hex without leading 0x)"), + UAT_FLD_CSTRING(someip_eventgroup_ident, name, "Eventgroup Name", "Name of the SOME/IP Service (string)"), + UAT_END_FIELDS + }; + + static uat_field_t someip_parameter_list_uat_fields[] = { + UAT_FLD_HEX(someip_parameter_list, service_id, "Service ID", "ID of the SOME/IP Service (16bit hex without leading 0x)"), + UAT_FLD_HEX(someip_parameter_list, method_id, "Method ID", "ID of the SOME/IP Method/Event/Notifier (16bit hex without leading 0x)"), + UAT_FLD_DEC(someip_parameter_list, version, "Version", "Version of the SOME/IP Service (8bit dec)"), + UAT_FLD_HEX(someip_parameter_list, message_type, "Message Type", "Message Type (8bit hex without leading 0x)"), + + UAT_FLD_DEC(someip_parameter_list, num_of_params, "Number of Parameter", "Number of Parameters (16bit dec)"), + + UAT_FLD_DEC(someip_parameter_list, pos, "Parameter Position", "Position of parameter (16bit dec, starting with 0)"), + UAT_FLD_CSTRING(someip_parameter_list, name, "Parameter Name", "Name of parameter (string)"), + UAT_FLD_DEC(someip_parameter_list, data_type, "Parameter Type", "Type of parameter (1: base, 2: string, 3: array, 4: struct, 5: union, 6: typedef, 7: enum)"), + UAT_FLD_HEX(someip_parameter_list, id_ref, "ID Reference", "ID Reference (32bit hex)"), + UAT_END_FIELDS + }; + + static uat_field_t someip_parameter_array_uat_fields[] = { + UAT_FLD_HEX(someip_parameter_arrays, id, "ID", "ID of SOME/IP array (32bit hex without leading 0x)"), + UAT_FLD_CSTRING(someip_parameter_arrays, name, "Array Name", "Name of array"), + UAT_FLD_DEC(someip_parameter_arrays, data_type, "Parameter Type", "Type of parameter (1: base, 2: string, 3: array, 4: struct, 5: union, 6: typedef, 7: enum)"), + UAT_FLD_HEX(someip_parameter_arrays, id_ref, "ID Reference", "ID Reference (32bit hex)"), + UAT_FLD_DEC(someip_parameter_arrays, num_of_dims, "Number of Items", "Number of Dimensions (16bit dec)"), + + UAT_FLD_DEC(someip_parameter_arrays, num, "Dimension", "Dimension (16bit dec, starting with 0)"), + UAT_FLD_DEC(someip_parameter_arrays, lower_limit, "Lower Limit", "Dimension (32bit dec)"), + UAT_FLD_DEC(someip_parameter_arrays, upper_limit, "Upper Limit", "Dimension (32bit dec)"), + UAT_FLD_DEC(someip_parameter_arrays, length_of_length, "Length of Length Field", "Version of the SOME/IP Service (8bit dec)"), + UAT_FLD_DEC(someip_parameter_arrays, pad_to, "Pad to", "Padding pads to reach allignment (8bit dec)"), + UAT_END_FIELDS + }; + + static uat_field_t someip_parameter_struct_uat_fields[] = { + UAT_FLD_HEX(someip_parameter_structs, id, "ID", "ID of SOME/IP struct (32bit hex without leading 0x)"), + UAT_FLD_CSTRING(someip_parameter_structs, struct_name, "Struct Name", "Name of struct"), + UAT_FLD_DEC(someip_parameter_structs, length_of_length, "Length of Length Field", "Length of the strcuts length field (8bit dec)"), + UAT_FLD_DEC(someip_parameter_structs, pad_to, "Pad to", "Padding pads to reach allignment (8bit dec)"), + + UAT_FLD_DEC(someip_parameter_structs, num_of_items, "Number of Items", "Number of Items (16bit dec)"), + + UAT_FLD_DEC(someip_parameter_structs, pos, "Parameter Position", "Position of parameter (16bit dec, starting with 0)"), + UAT_FLD_CSTRING(someip_parameter_structs, name, "Parameter Name", "Name of parameter (string)"), + UAT_FLD_DEC(someip_parameter_structs, data_type, "Parameter Type", "Type of parameter (1: base, 2: string, 3: array, 4: struct, 5: union, 6: typedef, 7: enum)"), + UAT_FLD_HEX(someip_parameter_structs, id_ref, "ID Reference", "ID Reference (32bit hex)"), + UAT_END_FIELDS + }; + + static uat_field_t someip_parameter_union_uat_fields[] = { + UAT_FLD_HEX(someip_parameter_unions, id, "ID", "ID of SOME/IP union (32bit hex without leading 0x)"), + UAT_FLD_CSTRING(someip_parameter_unions, name, "Union Name", "Name of union"), + UAT_FLD_DEC(someip_parameter_unions, length_of_length, "Length of Length Field", "Length of the unions length field (uint8 dec)"), + UAT_FLD_DEC(someip_parameter_unions, length_of_type, "Length of Type Field", "Length of the unions type field (8bit dec)"), + UAT_FLD_DEC(someip_parameter_unions, pad_to, "Pad to", "Padding pads to reach allignment (8bit dec)"), + + UAT_FLD_DEC(someip_parameter_unions, num_of_items, "Number of Items", "Number of Items (32bit dec)"), + + UAT_FLD_DEC(someip_parameter_unions, type_id, "Type ID", "ID of Type (32bit dec, starting with 0)"), + UAT_FLD_CSTRING(someip_parameter_unions, type_name, "Type Name", "Name of Type (string)"), + UAT_FLD_DEC(someip_parameter_unions, data_type, "Data Type", "Type of payload (1: base, 2: string, 3: array, 4: struct, 5: union, 6: typedef, 7: enum)"), + UAT_FLD_HEX(someip_parameter_unions, id_ref, "ID Reference", "ID Reference (32bit hex)"), + UAT_END_FIELDS + }; + + static uat_field_t someip_parameter_enum_uat_fields[] = { + UAT_FLD_HEX(someip_parameter_enums, id, "ID", "ID of SOME/IP enum (32bit hex without leading 0x)"), + UAT_FLD_CSTRING(someip_parameter_enums, name, "Name", "Name of Enumeration (string)"), + UAT_FLD_DEC(someip_parameter_enums, data_type, "Parameter Type", "Type of parameter (dec)"), + UAT_FLD_HEX(someip_parameter_enums, id_ref, "ID Reference", "ID Reference (32bit hex)"), + UAT_FLD_DEC(someip_parameter_enums, num_of_items, "Number of Items", "Number of Items (32bit dec)"), + + UAT_FLD_HEX(someip_parameter_enums, value, "Value", "Value (64bit uint hex)"), + UAT_FLD_CSTRING(someip_parameter_enums, value_name, "Value Name", "Name (string)"), + UAT_END_FIELDS + }; + + static uat_field_t someip_parameter_base_type_list_uat_fields[] = { + UAT_FLD_HEX(someip_parameter_base_type_list, id, "ID ", "ID (32bit hex)"), + UAT_FLD_CSTRING(someip_parameter_base_type_list, name, "Name", "Name of type (string)"), + UAT_FLD_CSTRING(someip_parameter_base_type_list, data_type, "Data Type", "Data type (string)"), + UAT_FLD_DEC(someip_parameter_base_type_list, big_endian, "Big Endian", "Encoded Big Endian 0=no 1=yes"), + UAT_FLD_DEC(someip_parameter_base_type_list, bitlength_base_type, "Bitlength base type", "Bitlength base type (uint32 dec)"), + UAT_FLD_DEC(someip_parameter_base_type_list, bitlength_encoded_type, "Bitlength enc. type", "Bitlength encoded type (uint32 dec)"), + UAT_END_FIELDS + }; + + static uat_field_t someip_parameter_string_list_uat_fields[] = { + UAT_FLD_HEX(someip_parameter_strings, id, "ID ", "ID (32bit hex)"), + UAT_FLD_CSTRING(someip_parameter_strings, name, "Name", "Name of string (string)"), + UAT_FLD_CSTRING(someip_parameter_strings, encoding, "Encoding", "String Encoding (ascii, utf-8, utf-16)"), + UAT_FLD_DEC(someip_parameter_strings, dynamic_length, "Dynamic Length", "Dynamic length of string 0=no 1=yes"), + UAT_FLD_DEC(someip_parameter_strings, max_length, "Max. Length", "Maximum length/Length (uint32 dec)"), + UAT_FLD_DEC(someip_parameter_strings, length_of_length, "Length of Len Field", "Length of the length field (uint8 dec)"), + UAT_FLD_DEC(someip_parameter_strings, big_endian, "Big Endian", "Encoded Big Endian 0=no 1=yes"), + UAT_FLD_DEC(someip_parameter_strings, pad_to, "Pad to" , "Padding pads to reach allignment (8bit dec)"), + UAT_END_FIELDS + }; + + static uat_field_t someip_parameter_typedef_list_uat_fields[] = { + UAT_FLD_HEX(someip_parameter_typedefs, id, "ID ", "ID (32bit hex)"), + UAT_FLD_CSTRING(someip_parameter_typedefs, name, "Name", "Name of typedef (string)"), + UAT_FLD_DEC(someip_parameter_typedefs, data_type, "Data Type", "Type referenced item (1: base, 2: string, 3: array, 4: struct, 5: union, 6: typedef, 7: enum)"), + UAT_FLD_HEX(someip_parameter_typedefs, id_ref, "ID Reference", "ID Reference (32bit hex)"), + UAT_END_FIELDS + }; + + static ei_register_info ei[] = { + { &ef_someip_unknown_version,{ "someip.unknown_protocol_version", + PI_PROTOCOL, PI_WARN, "SOME/IP Unknown Protocol Version!", EXPFILL } }, + { &ef_someip_message_truncated,{ "someip.message_truncated", + PI_MALFORMED, PI_ERROR, "SOME/IP Truncated message!", EXPFILL } }, + { &ef_someip_incomplete_headers,{ "someip.incomplete_headers", + PI_MALFORMED, PI_ERROR, "SOME/IP Incomplete headers or some bytes left over!", EXPFILL } }, + + { &ef_someip_payload_truncated, {"someip.payload.expert_truncated", + PI_MALFORMED, PI_ERROR, "SOME/IP Payload: Truncated payload!", EXPFILL} }, + { &ef_someip_payload_config_error, {"someip.payload.expert_config_error", + PI_MALFORMED, PI_ERROR, "SOME/IP Payload: Config Error!", EXPFILL} }, + { &ef_someip_payload_alignment_error, {"someip.payload.expert_alignment_error", + PI_MALFORMED, PI_ERROR, "SOME/IP Payload: SOME/IP datatype must be align to a byte!", EXPFILL} }, + { &ef_someip_payload_static_array_min_not_max, {"someip.payload.expert_static_array_min_max", + PI_MALFORMED, PI_ERROR, "SOME/IP Payload: Static array with min!=max!", EXPFILL} }, + { &ef_someip_payload_dyn_array_not_within_limit, {"someip.payload.expert_dyn_array_not_within_limit", + PI_MALFORMED, PI_WARN, "SOME/IP Payload: Dynamic array does not stay between Min and Max values!", EXPFILL} }, + }; + + /* Register ETTs */ + proto_someip = proto_register_protocol(SOMEIP_NAME_LONG, SOMEIP_NAME, SOMEIP_NAME_FILTER); + proto_register_field_array(proto_someip, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + expert_module_someip = expert_register_protocol(proto_someip); + expert_register_field_array(expert_module_someip, ei, array_length(ei)); + + someip_dissector_table = register_dissector_table("someip.messageid", "SOME/IP Message ID", proto_someip, FT_UINT32, BASE_HEX); + + /* init for SOME/IP-TP */ + reassembly_table_init(&someip_tp_reassembly_table, &addresses_ports_reassembly_table_functions); + + /* Register preferences */ + someip_module = prefs_register_protocol(proto_someip, &proto_reg_handoff_someip); + + range_convert_str(wmem_epan_scope(), &someip_ports_udp, "", 65535); + prefs_register_range_preference(someip_module, "ports.udp", "UDP Ports", + "SOME/IP Port Ranges UDP.", + &someip_ports_udp, 65535); + + range_convert_str(wmem_epan_scope(), &someip_ports_tcp, "", 65535); + prefs_register_range_preference(someip_module, "ports.tcp", "TCP Ports", + "SOME/IP Port Ranges TCP.", + &someip_ports_tcp, 65535); + + /* UATs */ + someip_service_uat = uat_new("SOME/IP Services", + sizeof(generic_one_id_string_t), /* record size */ + DATAFILE_SOMEIP_SERVICES, /* filename */ + TRUE, /* from profile */ + (void**) &someip_service_ident, /* data_ptr */ + &someip_service_ident_num, /* numitems_ptr */ + UAT_AFFECTS_DISSECTION, /* but not fields */ + NULL, /* help */ + copy_generic_one_id_string_cb, /* copy callback */ + update_generic_one_identifier_16bit, /* update callback */ + free_generic_one_id_string_cb, /* free callback */ + post_update_someip_service_cb, /* post update callback */ + NULL, /* reset callback */ + someip_service_uat_fields /* UAT field definitions */ + ); + + prefs_register_uat_preference(someip_module, "services", "SOME/IP Services", + "A table to define names of SOME/IP services", someip_service_uat); + + someip_method_uat = uat_new("SOME/IP Methods/Events/Fields", + sizeof(generic_two_id_string_t), /* record size */ + DATAFILE_SOMEIP_METHODS, /* record size */ + TRUE, /* from profile */ + (void**) &someip_method_ident, /* data_ptr */ + &someip_method_ident_num, /* numitems_ptr */ + UAT_AFFECTS_DISSECTION, /* but not fields */ + NULL, /* help */ + copy_generic_two_id_string_cb, /* copy callback */ + update_generic_two_identifier_16bit, /* update callback */ + free_generic_two_id_string_cb, /* free callback */ + post_update_someip_method_cb, /* post update callback */ + NULL, /* reset callback */ + someip_method_uat_fields /* UAT field definitions */ + ); + + prefs_register_uat_preference(someip_module, "methods", "SOME/IP Methods", + "A table to define names of SOME/IP methods", someip_method_uat); + + someip_eventgroup_uat = uat_new("SOME/IP Eventgroups", + sizeof(generic_two_id_string_t), /* record size */ + DATAFILE_SOMEIP_EVENTGROUPS, /* record size */ + TRUE, /* from profile */ + (void**) &someip_eventgroup_ident, /* data_ptr */ + &someip_eventgroup_ident_num, /* numitems_ptr */ + UAT_AFFECTS_DISSECTION, /* but not fields */ + NULL, /* help */ + copy_generic_two_id_string_cb, /* copy callback */ + update_generic_two_identifier_16bit, /* update callback */ + free_generic_two_id_string_cb, /* free callback */ + post_update_someip_eventgroup_cb, /* post update callback */ + NULL, /* reset callback */ + someip_eventgroup_uat_fields /* UAT field definitions */ + ); + + prefs_register_uat_preference(someip_module, "eventgroups", "SOME/IP Eventgroups", + "A table to define names of SOME/IP eventgroups", someip_eventgroup_uat); + + someip_parameter_list_uat = uat_new("SOME/IP Parameter List", + sizeof(someip_parameter_list_uat_t), DATAFILE_SOMEIP_PARAMETERS, TRUE, + (void**)&someip_parameter_list, + &someip_parameter_list_num, + UAT_AFFECTS_DISSECTION, + NULL, /* help */ + copy_someip_parameter_list_cb, + update_someip_parameter_list, + free_someip_parameter_list_cb, + post_update_someip_parameter_list_cb, + NULL, /* reset */ + someip_parameter_list_uat_fields + ); + + prefs_register_bool_preference(someip_module, "reassemble_tp", "Reassemble SOME/IP-TP", + "Reassemble SOME/IP-TP segments", &someip_tp_reassemble); + + prefs_register_bool_preference(someip_module, "payload_dissector_activated", + "Dissect Payload", + "Should the SOME/IP Dissector use the payload dissector?", + &someip_derserializer_activated); + + prefs_register_uat_preference(someip_module, "_someip_parameter_list", "SOME/IP Parameter List", + "A table to define names of SOME/IP parameters", someip_parameter_list_uat); + + someip_parameter_arrays_uat = uat_new("SOME/IP Parameter Arrays", + sizeof(someip_parameter_array_uat_t), DATAFILE_SOMEIP_ARRAYS, TRUE, + (void**)&someip_parameter_arrays, + &someip_parameter_arrays_num, + UAT_AFFECTS_DISSECTION, + NULL, /* help */ + copy_someip_parameter_array_cb, + update_someip_parameter_array, + free_someip_parameter_array_cb, + post_update_someip_parameter_array_cb, + NULL, /* reset */ + someip_parameter_array_uat_fields + ); + + prefs_register_uat_preference(someip_module, "_someip_parameter_arrays", "SOME/IP Parameter Arrays", + "A table to define arrays used by SOME/IP", someip_parameter_arrays_uat); + + someip_parameter_structs_uat = uat_new("SOME/IP Parameter Structs", + sizeof(someip_parameter_struct_uat_t), DATAFILE_SOMEIP_STRUCTS, TRUE, + (void**)&someip_parameter_structs, + &someip_parameter_structs_num, + UAT_AFFECTS_DISSECTION, + NULL, /* help */ + copy_someip_parameter_struct_cb, + update_someip_parameter_struct, + free_someip_parameter_struct_cb, + post_update_someip_parameter_struct_cb, + NULL, /* reset */ + someip_parameter_struct_uat_fields + ); + + prefs_register_uat_preference(someip_module, "_someip_parameter_structs", "SOME/IP Parameter Structs", + "A table to define structs used by SOME/IP", someip_parameter_structs_uat); + + someip_parameter_unions_uat = uat_new("SOME/IP Parameter Unions", + sizeof(someip_parameter_union_uat_t), DATAFILE_SOMEIP_UNIONS, TRUE, + (void**)&someip_parameter_unions, + &someip_parameter_unions_num, + UAT_AFFECTS_DISSECTION, + NULL, /* help */ + copy_someip_parameter_union_cb, + update_someip_parameter_union, + free_someip_parameter_union_cb, + post_update_someip_parameter_union_cb, + NULL, /* reset */ + someip_parameter_union_uat_fields + ); + + prefs_register_uat_preference(someip_module, "_someip_parameter_unions", "SOME/IP Parameter Unions", + "A table to define unions used by SOME/IP", someip_parameter_unions_uat); + + someip_parameter_enums_uat = uat_new("SOME/IP Parameter Enums", + sizeof(someip_parameter_enum_uat_t), DATAFILE_SOMEIP_ENUMS, TRUE, + (void**)&someip_parameter_enums, + &someip_parameter_enums_num, + UAT_AFFECTS_DISSECTION, + NULL, /* help */ + copy_someip_parameter_enum_cb, + update_someip_parameter_enum, + free_someip_parameter_enum_cb, + post_update_someip_parameter_enum_cb, + NULL, /* reset */ + someip_parameter_enum_uat_fields + ); + + prefs_register_uat_preference(someip_module, "_someip_parameter_enums", "SOME/IP Parameter Enums", + "A table to define enumerations used by SOME/IP", someip_parameter_enums_uat); + + someip_parameter_base_type_list_uat = uat_new("SOME/IP Parameter Base Type List", + sizeof(someip_parameter_base_type_list_uat_t), DATAFILE_SOMEIP_BASE_TYPES, TRUE, + (void**)&someip_parameter_base_type_list, + &someip_parameter_base_type_list_num, + UAT_AFFECTS_DISSECTION, + NULL, /* help */ + copy_someip_parameter_base_type_list_cb, + update_someip_parameter_base_type_list, + free_someip_parameter_base_type_list_cb, + post_update_someip_parameter_base_type_list_cb, + NULL, /* reset */ + someip_parameter_base_type_list_uat_fields + ); + + prefs_register_uat_preference(someip_module, "_someip_parameter_base_type_list", "SOME/IP Parameter Base Type List", + "A table to define base types of SOME/IP parameters", someip_parameter_base_type_list_uat); + + someip_parameter_strings_uat = uat_new("SOME/IP Parameter String List", + sizeof(someip_parameter_string_uat_t), DATAFILE_SOMEIP_STRINGS, TRUE, + (void**)&someip_parameter_strings, + &someip_parameter_strings_num, + UAT_AFFECTS_DISSECTION, + NULL, /* help */ + copy_someip_parameter_string_list_cb, + update_someip_parameter_string_list, + free_someip_parameter_string_list_cb, + post_update_someip_parameter_string_list_cb, + NULL, /* reset */ + someip_parameter_string_list_uat_fields + ); + + prefs_register_uat_preference(someip_module, "_someip_parameter_string_list", "SOME/IP Parameter String List", + "A table to define strings parameters", someip_parameter_strings_uat); + + someip_parameter_typedefs_uat = uat_new("SOME/IP Parameter Typedef List", + sizeof(someip_parameter_typedef_uat_t), DATAFILE_SOMEIP_TYPEDEFS, TRUE, + (void**)&someip_parameter_typedefs, + &someip_parameter_typedefs_num, + UAT_AFFECTS_DISSECTION, + NULL, /* help */ + copy_someip_parameter_typedef_list_cb, + update_someip_parameter_typedef_list, + free_someip_parameter_typedef_list_cb, + post_update_someip_parameter_typedef_list_cb, + NULL, /* reset */ + someip_parameter_typedef_list_uat_fields + ); + + prefs_register_uat_preference(someip_module, "_someip_parameter_typedef_list", "SOME/IP Parameter Typedef List", + "A table to define typedefs", someip_parameter_typedefs_uat); +} + +void +proto_reg_handoff_someip(void) { + static gboolean initialized = FALSE; + + if (!initialized) { + someip_handle_udp = create_dissector_handle(dissect_someip_udp, proto_someip); + someip_handle_tcp = create_dissector_handle(dissect_someip_tcp, proto_someip); + + heur_dissector_add("udp", dissect_some_ip_heur_udp, "SOME/IP over UDP Heuristic", "SOME/IP UDP Heuristic", proto_someip, HEURISTIC_DISABLE); + heur_dissector_add("tcp", dissect_some_ip_heur_tcp, "SOME/IP over TCP Heuristic", "SOME/IP TCP Heuristic", proto_someip, HEURISTIC_DISABLE); + + initialized = TRUE; + } else { + /* delete all my ports even the dynamically registered ones */ + dissector_delete_all("udp.port", someip_handle_tcp); + dissector_delete_all("tcp.port", someip_handle_tcp); + } + dissector_add_uint_range("udp.port", someip_ports_udp, someip_handle_udp); + dissector_add_uint_range("tcp.port", someip_ports_tcp, someip_handle_tcp); +} + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/epan/dissectors/packet-someip.h b/epan/dissectors/packet-someip.h new file mode 100644 index 0000000000..41eb7e8536 --- /dev/null +++ b/epan/dissectors/packet-someip.h @@ -0,0 +1,32 @@ +/* packet-someip.h + * Definitions for SOME/IP packet disassembly structures and routines + * By Dr. Lars Voelker / + * Copyright 2012-2019 Dr. Lars Voelker + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* used for SD to add ports dynamically */ +void register_someip_port_udp(guint32 portnumber); +void register_someip_port_tcp(guint32 portnumber); + +/* look up names for SD */ +char* someip_lookup_service_name(guint16 serviceid); +char* someip_lookup_eventgroup_name(guint16 serviceid, guint16 eventgroupid); + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */