SAPRouter: Added SAP Router as main dissector

This commit is contained in:
Martin Gallo 2022-11-21 15:01:59 +00:00 committed by A Wireshark GitLab Utility
parent 2128053e7c
commit 82b5201f79
5 changed files with 975 additions and 1 deletions

View File

@ -73,6 +73,7 @@ SAP Enqueue Server (SAPEnqueue)
SAP HANA SQL Command Network Protocol (SAPHDB)
SAP Internet Graphic Server (SAP IGS)
SAP Network Interface (SAPNI)
SAP Router (SAPROUTER)
World of Warcraft World (WOWW) display filters have been changed to be more internally consistent.
Support for almost all WoW 1.12 messages has been added.
Management Component Transport Protocol (MCTP)

View File

@ -1772,6 +1772,7 @@ set(DISSECTOR_SRC
${CMAKE_CURRENT_SOURCE_DIR}/packet-saphdb.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-sapigs.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-sapni.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-saprouter.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-sasp.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-sbus.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-sbc.c

View File

@ -27,6 +27,8 @@
#include <epan/conversation.h>
#include <wsutil/wmem/wmem.h>
#include "packet-sapni.h"
/*
* Define default ports. The right range should be 32NN and 4NNNN, but as port numbers are proprietary and not
@ -88,7 +90,7 @@ get_sap_protocol_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset _U_,
* heuristics as a first try as some protocols uses the same TCP ports
* (e.g. 3200/tcp for Enqueue Server and Diag).
*/
static void
void
dissect_sap_protocol_payload(tvbuff_t *tvb, guint32 offset, packet_info *pinfo, proto_tree *tree, guint16 sport, guint16 dport){
guint16 low_port = 0, high_port = 0;
tvbuff_t *next_tvb = NULL;

View File

@ -0,0 +1,21 @@
/* packet-sapni.h
* Routines for SAP NI (Network Interface) dissection
* Copyright 2022, Martin Gallo <martin.gallo [AT] gmail.com>
* Code contributed by SecureAuth Corp.
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef __PACKET_SAPPROTOCOL_H__
#define __PACKET_SAPPROTOCOL_H__
#include <epan/packet.h>
extern void
dissect_sap_protocol_payload(tvbuff_t *tvb, guint32 offset, packet_info *pinfo, proto_tree *tree, guint16 sport, guint16 dport);
#endif /* __PACKET_SAPPROTOCOL_H__ */

View File

@ -0,0 +1,949 @@
/* packet-saprouter.c
* Routines for SAP Router dissection
* Copyright 2022, Martin Gallo <martin.gallo [AT] gmail.com>
* Code contributed by SecureAuth Corp.
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
/*
* This is a dissector for the SAP Router protocol.
*
* Some details and example requests can be found in pysap's documentation: https://pysap.readthedocs.io/en/latest/protocols/SAPRouter.html.
*/
#include <config.h>
#include <stdlib.h>
#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/expert.h>
#include <wsutil/wmem/wmem.h>
#include <epan/conversation.h>
#include "packet-sapni.h"
/* Define default ports */
#define SAPROUTER_PORT_RANGE "3298-3299"
/*
* Length of the frame header
*/
#define SAPROUTER_HEADER_LEN 8
/*
* Offsets of header fields
*/
#define SAPROUTER_ROUTE_LENGTH_OFFSET 16
#define SAPROUTER_ROUTE_OFFSET_OFFSET 20
/* SAP Router Eye Catcher strings */
#define SAPROUTER_TYPE_NIPING_STRING "EYECATCHER"
#define SAPROUTER_TYPE_ROUTE_STRING "NI_ROUTE"
#define SAPROUTER_TYPE_ROUTE_ACCEPT "NI_PONG"
#define SAPROUTER_TYPE_ERR_STRING "NI_RTERR"
#define SAPROUTER_TYPE_ADMIN_STRING "ROUTER_ADM"
/* SAP Router Talk Modes */
static const value_string saprouter_talk_mode_vals[] = {
{ 0, "NI_MSG_IO" },
{ 1, "NI_RAW_IO" },
{ 2, "NI_ROUT_IO" },
/* NULL */
{ 0, NULL},
};
/* SAP Router Operation values */
static const value_string saprouter_opcode_vals[] = {
{ 0, "Error information" },
{ 1, "Version Request" },
{ 2, "Version Response" },
{ 5, "Send Handle (5)" }, /* TODO: Check this opcodes */
{ 6, "Send Handle (6)" }, /* TODO: Check this opcodes */
{ 8, "Send Handle (8)" }, /* TODO: Check this opcodes */
{ 70, "SNC request" }, /* TODO: Check this opcodes NiSncOpcode: NISNC_REQ */
{ 71, "SNC handshake complete" }, /* TODO: Check this opcodes NiSncOpcode: NISNC_ACK */
/* NULL */
{ 0, NULL}
};
/* SAP Router Return Code values (as per SAP Note 63342 https://launchpad.support.sap.com/#/notes/63342) */
static const value_string saprouter_return_code_vals[] = {
{ -1, "NI-internal error (NIEINTERN)" },
{ -2, "Host name unknown (NIEHOST_UNKNOWN)" },
{ -3, "Service unknown (NIESERV_UNKNOWN)" },
{ -4, "Service already used (NIESERV_USED)" },
{ -5, "Time limit reached (NIETIMEOUT)" },
{ -6, "Connection to partner broken (NIECONN_BROKEN)" },
{ -7, "Data range too small (NIETOO_SMALL)" },
{ -8, "Invalid parameters (NIEINVAL)" },
{ -9, "Wake-Up (without data) (NIEWAKEUP)" },
{-10, "Connection setup failed (NIECONN_REFUSED)" },
{-11, "PING/PONG signal received (NIEPING)" },
{-12, "Connection to partner via NiRouter not yet set up (NIECONN_PENDING)" },
{-13, "Invalid version (NIEVERSION)" },
{-14, "Local hostname cannot be found (NIEMYHOSTNAME)" },
{-15, "No free port in range (NIENOFREEPORT)" },
{-16, "Local hostname invalid (NIEMYHOST_VERIFY)" },
{-17, "Error in the SNC shift in the saprouter ==> (NIESNC_FAILURE)" },
{-18, "Opcode received (NIEOPCODE)" },
{-19, "queue limit reached, next package not accepted (NIEQUE_FULL)" },
{-20, "Requested package too large (NIETOO_BIG)" },
{-90, "Host name unknown (NIEROUT_HOST_UNKNOWN)" },
{-91, "Service unknown (NIEROUT_SERV_UNKNOWN)" },
{-92, "Connection setup failed (NIEROUT_CONN_REFUSED)" },
{-93, "NI-internal errors (NIEROUT_INTERN)" },
{-94, "Connect from source to destination not allowed (NIEROUT_PERM_DENIED)" },
{-95, "Connection terminated (NIEROUT_CONN_BROKEN)" },
{-96, "Invalid client version (NIEROUT_VERSION)" },
{-97, "Connection cancelled by administrator (NIEROUT_CANCELED)" },
{-98, "saprouter shutdown (NIEROUT_SHUTDOWN)" },
{-99, "Information request refused (NIEROUT_INFO_DENIED)" },
{-100, "Max. number of clients reached (NIEROUT_OVERFLOW)" },
{-101, "Talkmode not allowed (NIEROUT_MODE_DENIED)" },
{-102, "Client not available (NIEROUT_NOCLIENT)" },
{-103, "Error in external library (NIEROUT_EXTERN)" },
{-104, "Error in the SNC shift (NIEROUT_SNC_FAILURE)" },
/* NULL */
{ 0, NULL}
};
/* SAP Router Admin Command values */
static const value_string saprouter_admin_command_vals[] = {
{ 2, "Information Request" },
{ 3, "New Route Table Request" },
{ 4, "Toggle Trace Request" },
{ 5, "Stop Request" },
{ 6, "Cancel Route Request" },
{ 7, "Dump Buffers Request" },
{ 8, "Flush Buffers Request" },
{ 9, "Soft Shutdown Request" },
{ 10, "Set Trace Peer" },
{ 11, "Clear Trace Peer" },
{ 12, "Trace Connection" },
{ 13, "Trace Connection" },
{ 14, "Hide Error Information Request" },
/* NULL */
{ 0, NULL}
};
static int proto_saprouter = -1;
/* General fields */
static int hf_saprouter_type = -1;
static int hf_saprouter_ni_version = -1;
/* Niping messages */
static int hf_saprouter_niping_message = -1;
/* Route information */
static int hf_saprouter_route_version = -1;
static int hf_saprouter_entries = -1;
static int hf_saprouter_talk_mode = -1;
static int hf_saprouter_rest_nodes = -1;
static int hf_saprouter_route_length = -1;
static int hf_saprouter_route_offset = -1;
static int hf_saprouter_route = -1;
static int hf_saprouter_route_string = -1;
static int hf_saprouter_route_requested_in = -1;
static int hf_saprouter_route_accepted_in = -1;
/* Route strings */
static int hf_saprouter_route_string_hostname = -1;
static int hf_saprouter_route_string_service = -1;
static int hf_saprouter_route_string_password = -1;
/* Error Information/Control Messages */
static int hf_saprouter_opcode = -1;
static int hf_saprouter_return_code = -1;
static int hf_saprouter_unknown = -1;
/* Error Information Messages */
static int hf_saprouter_error_length = -1;
static int hf_saprouter_error_string = -1;
static int hf_saprouter_error_eyecatcher = -1;
static int hf_saprouter_error_counter = -1;
static int hf_saprouter_error_error = -1;
static int hf_saprouter_error_return_code= -1;
static int hf_saprouter_error_component = -1;
static int hf_saprouter_error_release = -1;
static int hf_saprouter_error_version = -1;
static int hf_saprouter_error_module = -1;
static int hf_saprouter_error_line = -1;
static int hf_saprouter_error_detail= -1;
static int hf_saprouter_error_time = -1;
static int hf_saprouter_error_system_call = -1;
static int hf_saprouter_error_errorno = -1;
static int hf_saprouter_error_errorno_text = -1;
static int hf_saprouter_error_error_count = -1;
static int hf_saprouter_error_location= -1;
static int hf_saprouter_error_unknown= -1; /* TODO: Unknown fields */
/* Control Messages */
static int hf_saprouter_control_length = -1;
static int hf_saprouter_control_string = -1;
static int hf_saprouter_control_unknown = -1;
/* Admin Messages */
static int hf_saprouter_admin_command = -1;
static int hf_saprouter_admin_password = -1;
static int hf_saprouter_admin_client_count_short = -1;
static int hf_saprouter_admin_client_count_int = -1;
static int hf_saprouter_admin_client_ids = -1;
static int hf_saprouter_admin_client_id = -1;
static int hf_saprouter_admin_address_mask = -1;
static gint ett_saprouter = -1;
/* Expert info */
static expert_field ei_saprouter_route_password_found = EI_INIT;
static expert_field ei_saprouter_route_invalid_length = EI_INIT;
static expert_field ei_saprouter_info_password_found = EI_INIT;
static expert_field ei_saprouter_invalid_client_ids = EI_INIT;
/* Global port preference */
static range_t *global_saprouter_port_range;
/* Global SNC dissection preference */
static gboolean global_saprouter_snc_dissection = TRUE;
/* Protocol handle */
static dissector_handle_t saprouter_handle;
/* Session state information being tracked in a SAP Router conversation */
typedef struct saprouter_session_state {
gboolean route_information;
guint route_requested_in;
gboolean route_accepted;
guint route_accepted_in;
gboolean route_snc_protected;
gchar *src_hostname; /* Source hostname (first entry in the route string) */
guint32 src_port; /* Source port number */
gchar *src_password; /* Source password XXX: Check if possible */
gchar *dest_hostname; /* Destination hostname (last entry in the route string) */
guint32 dest_port; /* Destination port number */
gchar *dest_password; /* Destination password */
} saprouter_session_state;
/*
*
*/
void proto_reg_handoff_saprouter(void);
void proto_register_saprouter(void);
static guint32
dissect_serviceport(gchar *port){
guint32 portnumber = 0;
if (g_ascii_isdigit(port[0])){
portnumber = (guint32)strtoul(port, NULL, 10);
} else if ((strlen(port)>5) && g_str_has_prefix(port, "sapdp")){
portnumber = 3200 + (guint32)strtoul(port+5, NULL, 10);
} else if ((strlen(port)>5) && g_str_has_prefix(port, "sapgw")){
portnumber = 3300 + (guint32)strtoul(port+5, NULL, 10);
} else if ((strlen(port)>5) && g_str_has_prefix(port, "sapms")){
portnumber = 3600 + (guint32)strtoul(port+5, NULL, 10);
}
return (portnumber);
}
static void
dissect_routestring(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset, saprouter_session_state *session_state){
int hop = 1;
guint32 len, route_offset, int_port = 0;
gchar *hostname = NULL, *port = NULL, *password = NULL;
proto_item *route_hop = NULL, *route_password = NULL;
proto_tree *route_hop_tree = NULL;
while (tvb_offset_exists(tvb, offset)){
route_offset = offset; hostname = port = password = NULL;
/* Create the subtree for this route hop */
route_hop = proto_tree_add_item(tree, hf_saprouter_route_string, tvb, offset, 0, ENC_NA);
route_hop_tree = proto_item_add_subtree(route_hop, ett_saprouter);
proto_item_append_text(route_hop, ", nro %d", hop);
/* Dissect the hostname string */
len = tvb_strsize(tvb, offset);
hostname = (gchar *)tvb_get_string_enc(wmem_file_scope(), tvb, offset, len - 1, ENC_ASCII);
proto_tree_add_item(route_hop_tree, hf_saprouter_route_string_hostname, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
/* Dissect the port string */
len = tvb_strsize(tvb, offset);
port = (gchar *)tvb_get_string_enc(pinfo->pool, tvb, offset, len - 1, ENC_ASCII);
proto_tree_add_item(route_hop_tree, hf_saprouter_route_string_service, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
/* Dissect the password string */
len = tvb_strsize(tvb, offset);
password = (gchar *)tvb_get_string_enc(wmem_file_scope(), tvb, offset, len - 1, ENC_ASCII);
route_password = proto_tree_add_item(route_hop_tree, hf_saprouter_route_string_password, tvb, offset, len, ENC_ASCII|ENC_NA);
/* If a password was found, add a expert warning in the security category */
if (len > 1){
expert_add_info(pinfo, route_password, &ei_saprouter_route_password_found);
}
offset += len;
/* Adjust the size of the route hop item now that we know the size */
proto_item_set_len(route_hop, offset - route_offset);
/* Get the service port in numeric format */
int_port = dissect_serviceport(port);
/* Add the first hostname/port as source in the conversation state*/
if ((hop==1) && !(pinfo->fd->visited)){
session_state->src_hostname = hostname;
session_state->src_port = int_port;
session_state->src_password = password;
}
hop++;
}
if (!(pinfo->fd->visited)) {
/* Add the last hostname/port as destination */
if (hop!=1){
session_state->dest_hostname = hostname;
session_state->dest_port = int_port;
session_state->dest_password = password;
}
/* Save the status of the conversation state */
session_state->route_information = TRUE;
session_state->route_accepted = FALSE;
}
}
static void
dissect_errorstring(tvbuff_t *tvb, proto_tree *tree, guint32 offset)
{
guint32 len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_eyecatcher, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_counter, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_error, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_return_code, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_component, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_release, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_version, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_module, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_line, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_detail, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_time, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_system_call, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_errorno, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_errorno_text, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_error_count, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_location, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_unknown, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_unknown, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_unknown, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_unknown, tvb, offset, len, ENC_ASCII|ENC_NA);
offset += len;
len = tvb_strsize(tvb, offset);
proto_tree_add_item(tree, hf_saprouter_error_eyecatcher, tvb, offset, len, ENC_ASCII|ENC_NA);
}
static tvbuff_t*
dissect_saprouter_snc_frame(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, guint32 offset _U_){
/* Call the SNC dissector
* TODO: This will be enabled when the SNC dissector is added
if (global_saprouter_snc_dissection == TRUE){
return dissect_sapsnc_frame(tvb, pinfo, tree, offset);
}
*/
return NULL;
}
static int
dissect_saprouter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
tvbuff_t *next_tvb = NULL;
guint8 opcode = 0;
guint32 offset = 0, eyecatcher_length = 0;
conversation_t *conversation = NULL;
saprouter_session_state *session_state = NULL;
proto_item *ti = NULL, *ri = NULL, *ei = NULL, *ci = NULL, *gi = NULL, *admin_password = NULL;
proto_tree *saprouter_tree = NULL, *route_tree = NULL, *text_tree = NULL, *clients_tree = NULL;
/* Search for a conversation */
conversation = find_or_create_conversation(pinfo);
session_state = (saprouter_session_state *)conversation_get_proto_data(conversation, proto_saprouter);
if (!session_state){
session_state = wmem_new(wmem_file_scope(), saprouter_session_state);
if (session_state){
session_state->route_information = FALSE;
session_state->route_requested_in = 0;
session_state->route_accepted = FALSE;
session_state->route_accepted_in = 0;
session_state->route_snc_protected = FALSE;
session_state->src_hostname = NULL;
session_state->src_port = 0;
session_state->src_password = NULL;
session_state->dest_hostname = NULL;
session_state->dest_port = 0;
session_state->dest_password = NULL;
conversation_add_proto_data(conversation, proto_saprouter, session_state);
} else {
/* Unable to establish a conversation, break dissection of the packet */
return 0;
}
}
/* Add the protocol to the column */
col_add_str(pinfo->cinfo, COL_PROTOCOL, "SAPROUTER");
/* Add the main SAP Router subtree */
ti = proto_tree_add_item(tree, proto_saprouter, tvb, offset, -1, ENC_NA);
saprouter_tree = proto_item_add_subtree(ti, ett_saprouter);
/* Get the 'eye catcher' length */
eyecatcher_length = tvb_strsize(tvb, offset);
/* Niping message */
if (tvb_reported_length_remaining(tvb, offset) >= 10 && tvb_strneql(tvb, offset, SAPROUTER_TYPE_NIPING_STRING, 10) == 0) {
col_set_str(pinfo->cinfo, COL_INFO, "Niping message");
proto_tree_add_item(saprouter_tree, hf_saprouter_type, tvb, offset, 10, ENC_ASCII|ENC_NA);
offset += 10;
proto_item_append_text(ti, ", Niping message");
if (tvb_reported_length_remaining(tvb, offset)) {
proto_tree_add_item(saprouter_tree, hf_saprouter_niping_message, tvb, offset, -1, ENC_NA);
}
}
/* Admin Message Type */
else if (tvb_strneql(tvb, offset, SAPROUTER_TYPE_ADMIN_STRING, eyecatcher_length) == 0) {
col_set_str(pinfo->cinfo, COL_INFO, "Admin message");
proto_tree_add_item(saprouter_tree, hf_saprouter_type, tvb, offset, eyecatcher_length, ENC_ASCII|ENC_NA);
offset += eyecatcher_length;
proto_item_append_text(ti, ", Admin message");
proto_tree_add_item(saprouter_tree, hf_saprouter_ni_version, tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
opcode = tvb_get_guint8(tvb, offset);
proto_tree_add_item(saprouter_tree, hf_saprouter_admin_command, tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
switch (opcode){
case 2:{ /* Info request */
offset+=2; /* Skip 2 bytes */
/* Check if a password was supplied */
if (tvb_offset_exists(tvb, offset) && (tvb_strsize(tvb, offset) > 0)){
admin_password = proto_tree_add_item(saprouter_tree, hf_saprouter_admin_password, tvb, offset, tvb_strsize(tvb, offset), ENC_ASCII|ENC_NA);
expert_add_info(pinfo, admin_password, &ei_saprouter_info_password_found);
}
break;
}
case 10: /* Set Peer Trace */
case 11:{ /* Clear Peer Trace */
proto_tree_add_item(saprouter_tree, hf_saprouter_admin_address_mask, tvb, offset, 32, ENC_ASCII|ENC_NA);
break;
}
case 6: /* Cancel Route request */
case 12: /* Trace Connection */
case 13: /* Trace Connection */
{
guint16 client_count = 0, client_count_actual = 0;
/* Retrieve the client count first */
if (opcode == 6){
offset+=2; /* Skip 2 bytes for Cancel Route request*/
client_count = tvb_get_ntohs(tvb, offset);
proto_tree_add_item(saprouter_tree, hf_saprouter_admin_client_count_short, tvb, offset, 2, ENC_BIG_ENDIAN);
offset+=2;
} else {
client_count = tvb_get_ntohl(tvb, offset);
proto_tree_add_item(saprouter_tree, hf_saprouter_admin_client_count_int, tvb, offset, 4, ENC_BIG_ENDIAN);
offset+=4;
}
/* Parse the list of client IDs */
ci = proto_tree_add_item(saprouter_tree, hf_saprouter_admin_client_ids, tvb, offset, 4*client_count, ENC_NA);
clients_tree = proto_item_add_subtree(ci, ett_saprouter);
while (tvb_offset_exists(tvb, offset) && tvb_reported_length_remaining(tvb, offset)>=4){
proto_tree_add_item(clients_tree, hf_saprouter_admin_client_id, tvb, offset, 4, ENC_BIG_ENDIAN);
offset+=4;
client_count_actual+=1;
}
/* Check if the actual count of IDs differes from the reported number */
if ((client_count_actual != client_count) || tvb_reported_length_remaining(tvb, offset)>0){
expert_add_info(pinfo, clients_tree, &ei_saprouter_invalid_client_ids);
}
break;
}
default: {
/* Skip 2 bytes */
break;
}
}
/* Route Message Type */
} else if (tvb_strneql(tvb, offset, SAPROUTER_TYPE_ROUTE_STRING, eyecatcher_length) == 0){
guint32 route_length = 0, route_offset = 0;
col_set_str(pinfo->cinfo, COL_INFO, "Route message");
/* Get the route length/offset */
route_length = tvb_get_ntohl(tvb, offset + SAPROUTER_ROUTE_LENGTH_OFFSET);
route_offset = offset + SAPROUTER_ROUTE_OFFSET_OFFSET + 4;
proto_tree_add_item(saprouter_tree, hf_saprouter_type, tvb, 0, eyecatcher_length, ENC_ASCII|ENC_NA);
offset += eyecatcher_length;
proto_item_append_text(ti, ", Route message");
/* Add the fields */
proto_tree_add_item(saprouter_tree, hf_saprouter_route_version, tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
proto_tree_add_item(saprouter_tree, hf_saprouter_ni_version, tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
proto_tree_add_item(saprouter_tree, hf_saprouter_entries, tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
proto_tree_add_item(saprouter_tree, hf_saprouter_talk_mode, tvb, offset, 1, ENC_BIG_ENDIAN);
offset+=3; /* There're two unused bytes there */
proto_tree_add_item(saprouter_tree, hf_saprouter_rest_nodes, tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
proto_tree_add_item(saprouter_tree, hf_saprouter_route_length, tvb, offset, 4, ENC_BIG_ENDIAN);
offset+=4;
proto_tree_add_item(saprouter_tree, hf_saprouter_route_offset, tvb, offset, 4, ENC_BIG_ENDIAN);
offset+=4;
/* Add the route tree */
if ((guint32)tvb_reported_length_remaining(tvb, offset) != route_length){
expert_add_info_format(pinfo, saprouter_tree, &ei_saprouter_route_invalid_length, "Route string length is invalid (remaining=%d, route_length=%d)", tvb_reported_length_remaining(tvb, offset), route_length);
route_length = (guint32)tvb_reported_length_remaining(tvb, offset);
}
ri = proto_tree_add_item(saprouter_tree, hf_saprouter_route, tvb, offset, route_length, ENC_NA);
route_tree = proto_item_add_subtree(ri, ett_saprouter);
/* Dissect the route string */
dissect_routestring(tvb, pinfo, route_tree, route_offset, session_state);
/* If this is the first time we're seeing this packet, mark it as the one where the route was requested */
if (!pinfo->fd->visited) {
session_state->route_requested_in = pinfo->num;
}
/* Add the route to the colinfo*/
if (session_state->src_hostname){
col_append_fstr(pinfo->cinfo, COL_INFO, ", Source: Hostname=%s Service Port=%d", session_state->src_hostname, session_state->src_port);
if (strlen(session_state->src_password)>0)
col_append_fstr(pinfo->cinfo, COL_INFO, " Password=%s", session_state->src_password);
}
if (session_state->dest_hostname){
col_append_fstr(pinfo->cinfo, COL_INFO, ", Destination: Hostname=%s Service Port=%d", session_state->dest_hostname, session_state->dest_port);
if (strlen(session_state->dest_password)>0)
col_append_fstr(pinfo->cinfo, COL_INFO, " Password=%s", session_state->dest_password);
}
if (session_state->route_accepted && session_state->route_accepted_in) {
gi = proto_tree_add_uint(saprouter_tree, hf_saprouter_route_accepted_in, tvb, 0, 0, session_state->route_accepted_in);
proto_item_set_generated(gi);
}
/* Error Information/Control Message Type */
} else if (tvb_strneql(tvb, offset, SAPROUTER_TYPE_ERR_STRING, eyecatcher_length) == 0){
/* Extract the opcode if possible to determine the type of message */
if (tvb_offset_exists(tvb, offset + 10)) {
opcode = tvb_get_guint8(tvb, offset + 10);
} else {
opcode = 0;
}
col_set_str(pinfo->cinfo, COL_INFO, (opcode==0)? "Error information" : "Control message");
guint32 text_length = 0;
proto_item_append_text(ti, (opcode==0)? ", Error information" : ", Control message");
/* Add the fields */
proto_tree_add_item(saprouter_tree, hf_saprouter_type, tvb, offset, eyecatcher_length, ENC_ASCII|ENC_NA);
offset += eyecatcher_length;
proto_tree_add_item(saprouter_tree, hf_saprouter_ni_version, tvb, offset, 1, ENC_BIG_ENDIAN);
offset++;
proto_tree_add_item(saprouter_tree, hf_saprouter_opcode, tvb, offset, 1, ENC_BIG_ENDIAN);
offset+=2; /* There's a unused byte there */
proto_tree_add_item(saprouter_tree, hf_saprouter_return_code, tvb, offset, 4, ENC_BIG_ENDIAN);
offset+=4;
text_length = tvb_get_ntohl(tvb, offset);
/* Error Information Message */
if (opcode == 0){
proto_tree_add_item(saprouter_tree, hf_saprouter_error_length, tvb, offset, 4, ENC_BIG_ENDIAN);
offset+=4;
if ((text_length > 0) && tvb_offset_exists(tvb, offset+text_length)){
/* Add the error string tree */
ei = proto_tree_add_item(saprouter_tree, hf_saprouter_error_string, tvb, offset, text_length, ENC_NA);
text_tree = proto_item_add_subtree(ei, ett_saprouter);
dissect_errorstring(tvb, text_tree, offset);
offset += text_length;
}
/* Add an unknown int field */
proto_tree_add_item(saprouter_tree, hf_saprouter_unknown, tvb, offset, 4, ENC_BIG_ENDIAN);
/* Control Message */
} else {
/* Add the opcode name */
proto_item_append_text(ti, ", opcode=%s", val_to_str(opcode, saprouter_opcode_vals, "Unknown"));
col_append_fstr(pinfo->cinfo, COL_INFO, ", opcode=%s", val_to_str(opcode, saprouter_opcode_vals, "Unknown"));
proto_tree_add_item(saprouter_tree, hf_saprouter_control_length, tvb, offset, 4, ENC_BIG_ENDIAN);
offset+=4;
if ((text_length >0) && tvb_offset_exists(tvb, offset+text_length)){
/* Add the control string tree */
proto_tree_add_item(saprouter_tree, hf_saprouter_control_string, tvb, offset, text_length, ENC_ASCII|ENC_NA);
offset += text_length;
}
/* SNC request, mark the conversation as SNC protected and dissect the SNC frame */
if (opcode == 70 || opcode == 71){
session_state->route_snc_protected = TRUE;
dissect_saprouter_snc_frame(tvb, pinfo, tree, offset);
/* Other opcodes */
} else {
proto_tree_add_item(saprouter_tree, hf_saprouter_control_unknown, tvb, offset, 4, ENC_ASCII|ENC_NA);
}
}
/* Route Acceptance (NI_PONG) Message Type */
} else if (tvb_strneql(tvb, offset, SAPROUTER_TYPE_ROUTE_ACCEPT, eyecatcher_length) == 0){
/* Route information available */
if (session_state->route_information){
/* If this is the first time we're seen the packet, mark is as the one where the route was accepted */
if (!pinfo->fd->visited) {
session_state->route_accepted = TRUE;
session_state->route_accepted_in = pinfo->num;
}
col_append_fstr(pinfo->cinfo, COL_INFO, ", from %s:%d to %s:%d", session_state->src_hostname, session_state->src_port, session_state->dest_hostname, session_state->dest_port);
proto_item_append_text(ti, ", from %s:%d to %s:%d", session_state->src_hostname, session_state->src_port, session_state->dest_hostname, session_state->dest_port);
if (session_state->route_requested_in) {
gi = proto_tree_add_uint(saprouter_tree, hf_saprouter_route_requested_in, tvb, 0, 0, session_state->route_requested_in);
proto_item_set_generated(gi);
}
}
/* Uknown Message Type */
} else {
col_add_fstr(pinfo->cinfo, COL_INFO, "Routed message");
proto_item_append_text(ti, ", Routed message");
/* If the session is protected with SNC, first dissect the SNC frame
* and save the content for further dissection.
*/
if (session_state->route_snc_protected) {
col_append_fstr(pinfo->cinfo, COL_INFO, ", SNC protected");
proto_item_append_text(ti, ", SNC protected");
next_tvb = dissect_saprouter_snc_frame(tvb, pinfo, tree, offset);
/* If the session is not protected dissect the entire payload */
} else {
next_tvb = tvb;
}
/* If the session has information about the route requested */
if (session_state->route_information){
/* Route accepted */
if (session_state->route_accepted){
col_append_fstr(pinfo->cinfo, COL_INFO, ", from %s:%d to %s:%d ", session_state->src_hostname, session_state->src_port, session_state->dest_hostname, session_state->dest_port);
proto_item_append_text(ti, ", from %s:%d to %s:%d ", session_state->src_hostname, session_state->src_port, session_state->dest_hostname, session_state->dest_port);
if (session_state->route_requested_in) {
gi = proto_tree_add_uint(saprouter_tree, hf_saprouter_route_requested_in, tvb, 0, 0, session_state->route_requested_in);
proto_item_set_generated(gi);
}
if (session_state->route_accepted_in) {
gi = proto_tree_add_uint(saprouter_tree, hf_saprouter_route_accepted_in, tvb, 0, 0, session_state->route_accepted_in);
proto_item_set_generated(gi);
}
/* Route not accepted but some information available */
} else {
col_append_fstr(pinfo->cinfo, COL_INFO, ", to unknown destination");
proto_item_append_text(ti, ", to unknown destination");
}
/* Call the dissector in the NI protocol sub-dissectors table
* according to the route destination port number. */
if (next_tvb) {
dissect_sap_protocol_payload(next_tvb, offset, pinfo, tree, 0, session_state->dest_port);
}
} else {
/* No route information available */
col_append_fstr(pinfo->cinfo, COL_INFO, ", to unknown destination");
proto_item_append_text(ti, ", to unknown destination");
}
}
return tvb_reported_length(tvb);
}
void
proto_register_saprouter(void)
{
static hf_register_info hf[] = {
{ &hf_saprouter_type,
{ "Type", "saprouter.type", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
/* Niping message */
{ &hf_saprouter_niping_message,
{ "Niping message", "saprouter.message", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
/* NI Route messages */
{ &hf_saprouter_route_version,
{ "Route version", "saprouter.version", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_ni_version,
{ "NI version", "saprouter.niversion", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_entries,
{ "Entries", "saprouter.entries", FT_UINT8, BASE_DEC, NULL, 0x0, "Total number of entries", HFILL }},
{ &hf_saprouter_talk_mode,
{ "Talk Mode", "saprouter.talkmode", FT_UINT8, BASE_DEC, VALS(saprouter_talk_mode_vals), 0x0, NULL, HFILL }},
{ &hf_saprouter_rest_nodes,
{ "Remaining Hops", "saprouter.restnodes", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_route_length,
{ "Route String Length", "saprouter.routelength", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_route_offset,
{ "Route String Offset", "saprouter.routeoffset", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_route,
{ "Route String", "saprouter.routestring", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_route_string,
{ "Route Hop", "saprouter.routestring", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_route_string_hostname,
{ "Hostname", "saprouter.routestring.hostname", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_route_string_service,
{ "Service", "saprouter.routestring.service", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_route_string_password,
{ "Password", "saprouter.routestring.password", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_route_requested_in,
{ "Route Requested in", "saprouter.requested_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0, "The route request for this packet is in this packet", HFILL }},
{ &hf_saprouter_route_accepted_in,
{ "Route Accepted in", "saprouter.accepted_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0, "The route for this packet was accepted in this packet", HFILL }},
/* NI error information / Control messages */
{ &hf_saprouter_opcode,
{ "Operation Code", "saprouter.opcode", FT_UINT8, BASE_DEC, VALS(saprouter_opcode_vals), 0x0, NULL, HFILL }},
{ &hf_saprouter_return_code,
{ "Return Code", "saprouter.returncode", FT_INT32, BASE_DEC, VALS(saprouter_return_code_vals), 0x0, NULL, HFILL }},
{ &hf_saprouter_unknown,
{ "Unknown field", "saprouter.unknown", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
/* NI Error Information messages */
{ &hf_saprouter_error_length,
{ "Error Information Text Length", "saprouter.errorlength", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_error_string,
{ "Error Information Text", "saprouter.errortext", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_error_eyecatcher,
{ "Eyecatcher", "saprouter.errortext.eyecatcher", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_error_counter,
{ "Counter", "saprouter.errortext.counter", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_error_error,
{ "Error", "saprouter.errortext.error", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_error_return_code,
{ "Return code", "saprouter.errortext.returncode", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_error_component,
{ "Component", "saprouter.errortext.component", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_error_release,
{ "Release", "saprouter.errortext.release", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_error_version,
{ "Version", "saprouter.errortext.version", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_error_module,
{ "Module", "saprouter.errortext.module", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_error_line,
{ "Line", "saprouter.errortext.line", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_error_detail,
{ "Detail", "saprouter.errortext.detail", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_error_time,
{ "Time", "saprouter.errortext.time", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_error_system_call,
{ "System Call", "saprouter.errortext.system_call", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_error_errorno,
{ "Error Number", "saprouter.errortext.errorno", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_error_errorno_text,
{ "Error Number Text", "saprouter.errortext.errorno_text", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_error_location,
{ "Location", "saprouter.errortext.location", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_error_error_count,
{ "Error Count", "saprouter.errortext.error_count", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_error_unknown,
{ "Unknown field", "saprouter.errortext.unknown", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
/* Control messages */
{ &hf_saprouter_control_length,
{ "Control Text Length", "saprouter.controllength", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_control_string,
{ "Control Text", "saprouter.controltext", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_control_unknown,
{ "Control Unknown field", "saprouter.controlunknown", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
/* Router Admin messages */
{ &hf_saprouter_admin_command,
{ "Admin Command", "saprouter.command", FT_UINT8, BASE_DEC, VALS(saprouter_admin_command_vals), 0x0, NULL, HFILL }},
{ &hf_saprouter_admin_password,
{ "Admin Command Info Password", "saprouter.password", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_admin_client_count_short,
{ "Admin Command Client Count", "saprouter.client_count", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_admin_client_count_int,
{ "Admin Command Client Count", "saprouter.client_count", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_admin_client_ids,
{ "Admin Command Client IDs", "saprouter.client_ids", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_admin_client_id,
{ "Admin Command Client ID", "saprouter.client_id", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
{ &hf_saprouter_admin_address_mask,
{ "Admin Command Address Mask", "saprouter.address_mask", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
};
/* Setup protocol subtree array */
static gint *ett[] = {
&ett_saprouter
};
/* Register the expert info */
static ei_register_info ei[] = {
{ &ei_saprouter_route_password_found, { "saprouter.routestring.password", PI_SECURITY, PI_WARN, "Route password found", EXPFILL }},
{ &ei_saprouter_info_password_found, { "saprouter.password", PI_SECURITY, PI_WARN, "Info password found", EXPFILL }},
{ &ei_saprouter_route_invalid_length, { "saprouter.routestring.routelength.invalid", PI_MALFORMED, PI_WARN, "The route string length is invalid", EXPFILL }},
{ &ei_saprouter_invalid_client_ids, { "saprouter.client_ids.invalid", PI_MALFORMED, PI_WARN, "Client IDs list is malformed", EXPFILL }},
};
module_t *saprouter_module;
expert_module_t* saprouter_expert;
/* Register the protocol */
proto_saprouter = proto_register_protocol("SAP Router Protocol", "SAPROUTER", "saprouter");
proto_register_field_array(proto_saprouter, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
saprouter_expert = expert_register_protocol(proto_saprouter);
expert_register_field_array(saprouter_expert, ei, array_length(ei));
register_dissector("saprouter", dissect_saprouter, proto_saprouter);
/* Register the preferences */
saprouter_module = prefs_register_protocol(proto_saprouter, proto_reg_handoff_saprouter);
range_convert_str(wmem_epan_scope(), &global_saprouter_port_range, SAPROUTER_PORT_RANGE, MAX_TCP_PORT);
prefs_register_range_preference(saprouter_module, "tcp_ports", "SAP Router Protocol TCP port numbers", "Port numbers used for SAP Router Protocol (default " SAPROUTER_PORT_RANGE ")", &global_saprouter_port_range, MAX_TCP_PORT);
prefs_register_bool_preference(saprouter_module, "snc_dissection", "Dissect SAP SNC frames", "Whether the SAP Router Protocol dissector should call the SAP SNC dissector for SNC frames", &global_saprouter_snc_dissection);
}
/**
* Helpers for dealing with the port range
*/
static void range_delete_callback (guint32 port, gpointer ptr _U_)
{
dissector_delete_uint("sapni.port", port, saprouter_handle);
}
static void range_add_callback (guint32 port, gpointer ptr _U_)
{
dissector_add_uint("sapni.port", port, saprouter_handle);
}
/**
* Register Hand off for the SAP Router Protocol
*/
void
proto_reg_handoff_saprouter(void)
{
static gboolean initialized = FALSE;
static range_t *saprouter_port_range;
if (!initialized) {
saprouter_handle = create_dissector_handle(dissect_saprouter, proto_saprouter);
initialized = TRUE;
} else {
range_foreach(saprouter_port_range, range_delete_callback, NULL);
wmem_free(wmem_epan_scope(), saprouter_port_range);
}
saprouter_port_range = range_copy(wmem_epan_scope(), global_saprouter_port_range);
range_foreach(saprouter_port_range, range_add_callback, NULL);
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: t
* End:
*
* vi: set shiftwidth=8 tabstop=8 noexpandtab:
* :indentSize=8:tabSize=8:noTabs=false:
*/