Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-saprouter.c
blob4c5fb771e1d1a4029897cd84de872dd3d582ab19
1 /* packet-saprouter.c
2 * Routines for SAP Router dissection
3 * Copyright 2022, Martin Gallo <martin.gallo [AT] gmail.com>
4 * Code contributed by SecureAuth Corp.
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
14 * This is a dissector for the SAP Router protocol.
16 * Some details and example requests can be found in pysap's documentation: https://pysap.readthedocs.io/en/latest/protocols/SAPRouter.html.
19 #include <config.h>
20 #include <stdlib.h>
22 #include <epan/packet.h>
23 #include <epan/prefs.h>
24 #include <epan/expert.h>
25 #include <wsutil/wmem/wmem.h>
26 #include <epan/conversation.h>
28 #include <epan/tap.h>
29 #include <ui/tap-credentials.h>
31 #include "packet-sapni.h"
32 #include "packet-sapsnc.h"
35 /* Define default ports */
36 #define SAPROUTER_PORT_RANGE "3298-3299"
39 * Length of the frame header
41 #define SAPROUTER_HEADER_LEN 8
44 * Offsets of header fields
46 #define SAPROUTER_ROUTE_LENGTH_OFFSET 16
47 #define SAPROUTER_ROUTE_OFFSET_OFFSET 20
49 /* SAP Router Eye Catcher strings */
50 #define SAPROUTER_TYPE_NIPING_STRING "EYECATCHER"
51 #define SAPROUTER_TYPE_ROUTE_STRING "NI_ROUTE"
52 #define SAPROUTER_TYPE_ROUTE_ACCEPT "NI_PONG"
53 #define SAPROUTER_TYPE_ERR_STRING "NI_RTERR"
54 #define SAPROUTER_TYPE_ADMIN_STRING "ROUTER_ADM"
56 /* SAP Router Talk Modes */
57 static const value_string saprouter_talk_mode_vals[] = {
58 { 0, "NI_MSG_IO" },
59 { 1, "NI_RAW_IO" },
60 { 2, "NI_ROUT_IO" },
61 /* NULL */
62 { 0, NULL},
65 /* SAP Router Operation values */
66 static const value_string saprouter_opcode_vals[] = {
67 { 0, "Error information" },
68 { 1, "Version Request" },
69 { 2, "Version Response" },
70 { 5, "Send Handle (5)" }, /* TODO: Check this opcodes */
71 { 6, "Send Handle (6)" }, /* TODO: Check this opcodes */
72 { 8, "Send Handle (8)" }, /* TODO: Check this opcodes */
73 { 70, "SNC request" }, /* TODO: Check this opcodes NiSncOpcode: NISNC_REQ */
74 { 71, "SNC handshake complete" }, /* TODO: Check this opcodes NiSncOpcode: NISNC_ACK */
75 /* NULL */
76 { 0, NULL}
79 /* SAP Router Return Code values (as per SAP Note 63342 https://launchpad.support.sap.com/#/notes/63342) */
80 static const value_string saprouter_return_code_vals[] = {
81 { -1, "NI-internal error (NIEINTERN)" },
82 { -2, "Host name unknown (NIEHOST_UNKNOWN)" },
83 { -3, "Service unknown (NIESERV_UNKNOWN)" },
84 { -4, "Service already used (NIESERV_USED)" },
85 { -5, "Time limit reached (NIETIMEOUT)" },
86 { -6, "Connection to partner broken (NIECONN_BROKEN)" },
87 { -7, "Data range too small (NIETOO_SMALL)" },
88 { -8, "Invalid parameters (NIEINVAL)" },
89 { -9, "Wake-Up (without data) (NIEWAKEUP)" },
90 {-10, "Connection setup failed (NIECONN_REFUSED)" },
91 {-11, "PING/PONG signal received (NIEPING)" },
92 {-12, "Connection to partner via NiRouter not yet set up (NIECONN_PENDING)" },
93 {-13, "Invalid version (NIEVERSION)" },
94 {-14, "Local hostname cannot be found (NIEMYHOSTNAME)" },
95 {-15, "No free port in range (NIENOFREEPORT)" },
96 {-16, "Local hostname invalid (NIEMYHOST_VERIFY)" },
97 {-17, "Error in the SNC shift in the saprouter ==> (NIESNC_FAILURE)" },
98 {-18, "Opcode received (NIEOPCODE)" },
99 {-19, "queue limit reached, next package not accepted (NIEQUE_FULL)" },
100 {-20, "Requested package too large (NIETOO_BIG)" },
101 {-90, "Host name unknown (NIEROUT_HOST_UNKNOWN)" },
102 {-91, "Service unknown (NIEROUT_SERV_UNKNOWN)" },
103 {-92, "Connection setup failed (NIEROUT_CONN_REFUSED)" },
104 {-93, "NI-internal errors (NIEROUT_INTERN)" },
105 {-94, "Connect from source to destination not allowed (NIEROUT_PERM_DENIED)" },
106 {-95, "Connection terminated (NIEROUT_CONN_BROKEN)" },
107 {-96, "Invalid client version (NIEROUT_VERSION)" },
108 {-97, "Connection cancelled by administrator (NIEROUT_CANCELED)" },
109 {-98, "saprouter shutdown (NIEROUT_SHUTDOWN)" },
110 {-99, "Information request refused (NIEROUT_INFO_DENIED)" },
111 {-100, "Max. number of clients reached (NIEROUT_OVERFLOW)" },
112 {-101, "Talkmode not allowed (NIEROUT_MODE_DENIED)" },
113 {-102, "Client not available (NIEROUT_NOCLIENT)" },
114 {-103, "Error in external library (NIEROUT_EXTERN)" },
115 {-104, "Error in the SNC shift (NIEROUT_SNC_FAILURE)" },
116 /* NULL */
117 { 0, NULL}
121 /* SAP Router Admin Command values */
122 static const value_string saprouter_admin_command_vals[] = {
123 { 2, "Information Request" },
124 { 3, "New Route Table Request" },
125 { 4, "Toggle Trace Request" },
126 { 5, "Stop Request" },
127 { 6, "Cancel Route Request" },
128 { 7, "Dump Buffers Request" },
129 { 8, "Flush Buffers Request" },
130 { 9, "Soft Shutdown Request" },
131 { 10, "Set Trace Peer" },
132 { 11, "Clear Trace Peer" },
133 { 12, "Trace Connection" },
134 { 13, "Trace Connection" },
135 { 14, "Hide Error Information Request" },
136 /* NULL */
137 { 0, NULL}
140 static int credentials_tap;
142 static int proto_saprouter;
144 /* General fields */
145 static int hf_saprouter_type;
146 static int hf_saprouter_ni_version;
148 /* Niping messages */
149 static int hf_saprouter_niping_message;
151 /* Route information */
152 static int hf_saprouter_route_version;
153 static int hf_saprouter_entries;
154 static int hf_saprouter_talk_mode;
155 static int hf_saprouter_rest_nodes;
156 static int hf_saprouter_route_length;
157 static int hf_saprouter_route_offset;
158 static int hf_saprouter_route;
159 static int hf_saprouter_route_string;
161 static int hf_saprouter_route_requested_in;
162 static int hf_saprouter_route_accepted_in;
164 /* Route strings */
165 static int hf_saprouter_route_string_hostname;
166 static int hf_saprouter_route_string_service;
167 static int hf_saprouter_route_string_password;
170 /* Error Information/Control Messages */
171 static int hf_saprouter_opcode;
172 static int hf_saprouter_return_code;
173 static int hf_saprouter_unknown;
175 /* Error Information Messages */
176 static int hf_saprouter_error_length;
177 static int hf_saprouter_error_string;
178 static int hf_saprouter_error_eyecatcher;
179 static int hf_saprouter_error_counter;
180 static int hf_saprouter_error_error;
181 static int hf_saprouter_error_return_code;
182 static int hf_saprouter_error_component;
183 static int hf_saprouter_error_release;
184 static int hf_saprouter_error_version;
185 static int hf_saprouter_error_module;
186 static int hf_saprouter_error_line;
187 static int hf_saprouter_error_detail;
188 static int hf_saprouter_error_time;
189 static int hf_saprouter_error_system_call;
190 static int hf_saprouter_error_errorno;
191 static int hf_saprouter_error_errorno_text;
192 static int hf_saprouter_error_error_count;
193 static int hf_saprouter_error_location;
194 static int hf_saprouter_error_unknown; /* TODO: Unknown fields */
196 /* Control Messages */
197 static int hf_saprouter_control_length;
198 static int hf_saprouter_control_string;
199 static int hf_saprouter_control_unknown;
201 /* Admin Messages */
202 static int hf_saprouter_admin_command;
203 static int hf_saprouter_admin_password;
204 static int hf_saprouter_admin_client_count_short;
205 static int hf_saprouter_admin_client_count_int;
206 static int hf_saprouter_admin_client_ids;
207 static int hf_saprouter_admin_client_id;
208 static int hf_saprouter_admin_address_mask;
210 static int ett_saprouter;
212 /* Expert info */
213 static expert_field ei_saprouter_route_password_found;
214 static expert_field ei_saprouter_route_invalid_length;
215 static expert_field ei_saprouter_info_password_found;
216 static expert_field ei_saprouter_invalid_client_ids;
218 /* Global port preference */
219 static range_t *global_saprouter_port_range;
222 /* Global SNC dissection preference */
223 static bool global_saprouter_snc_dissection = true;
225 /* Protocol handle */
226 static dissector_handle_t saprouter_handle;
228 /* Session state information being tracked in a SAP Router conversation */
229 typedef struct saprouter_session_state {
230 bool route_information;
231 unsigned route_requested_in;
232 bool route_accepted;
233 unsigned route_accepted_in;
234 bool route_snc_protected;
235 char *src_hostname; /* Source hostname (first entry in the route string) */
236 uint32_t src_port; /* Source port number */
237 char *src_password; /* Source password XXX: Check if possible */
238 char *dest_hostname; /* Destination hostname (last entry in the route string) */
239 uint32_t dest_port; /* Destination port number */
240 char *dest_password; /* Destination password */
241 } saprouter_session_state;
246 void proto_reg_handoff_saprouter(void);
247 void proto_register_saprouter(void);
250 static uint32_t
251 dissect_serviceport(char *port){
252 uint32_t portnumber = 0;
254 if (g_ascii_isdigit(port[0])){
255 portnumber = (uint32_t)strtoul(port, NULL, 10);
256 } else if ((strlen(port)>5) && g_str_has_prefix(port, "sapdp")){
257 portnumber = 3200 + (uint32_t)strtoul(port+5, NULL, 10);
258 } else if ((strlen(port)>5) && g_str_has_prefix(port, "sapgw")){
259 portnumber = 3300 + (uint32_t)strtoul(port+5, NULL, 10);
260 } else if ((strlen(port)>5) && g_str_has_prefix(port, "sapms")){
261 portnumber = 3600 + (uint32_t)strtoul(port+5, NULL, 10);
263 return (portnumber);
266 static void
267 dissect_routestring(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, uint32_t offset, saprouter_session_state *session_state){
268 int hop = 1;
269 uint32_t len, route_offset, int_port = 0;
270 char *hostname = NULL, *port = NULL, *password = NULL;
271 proto_item *route_hop = NULL, *route_password = NULL;
272 proto_tree *route_hop_tree = NULL;
274 while (tvb_offset_exists(tvb, offset)){
275 route_offset = offset; hostname = port = password = NULL;
277 /* Create the subtree for this route hop */
278 route_hop = proto_tree_add_item(tree, hf_saprouter_route_string, tvb, offset, 0, ENC_NA);
279 route_hop_tree = proto_item_add_subtree(route_hop, ett_saprouter);
280 proto_item_append_text(route_hop, ", nro %d", hop);
282 /* Dissect the hostname string */
283 len = tvb_strsize(tvb, offset);
284 hostname = (char *)tvb_get_string_enc(wmem_file_scope(), tvb, offset, len - 1, ENC_ASCII);
285 proto_tree_add_item(route_hop_tree, hf_saprouter_route_string_hostname, tvb, offset, len, ENC_ASCII|ENC_NA);
286 offset += len;
288 /* Dissect the port string */
289 len = tvb_strsize(tvb, offset);
290 port = (char *)tvb_get_string_enc(pinfo->pool, tvb, offset, len - 1, ENC_ASCII);
291 proto_tree_add_item(route_hop_tree, hf_saprouter_route_string_service, tvb, offset, len, ENC_ASCII|ENC_NA);
292 offset += len;
294 /* Dissect the password string */
295 len = tvb_strsize(tvb, offset);
296 password = (char *)tvb_get_string_enc(wmem_file_scope(), tvb, offset, len - 1, ENC_ASCII);
297 route_password = proto_tree_add_item(route_hop_tree, hf_saprouter_route_string_password, tvb, offset, len, ENC_ASCII|ENC_NA);
299 /* If a password was found, add a expert warning in the security category */
300 if (len > 1){
301 expert_add_info(pinfo, route_password, &ei_saprouter_route_password_found);
303 /* Add the password to the credential tap */
304 tap_credential_t *auth = wmem_new0(pinfo->pool, tap_credential_t);
305 auth->num = pinfo->num;
306 auth->password_hf_id = hf_saprouter_route_string_password;
307 auth->proto = "SAP Router Route String password";
308 auth->username = wmem_strdup(pinfo->pool, TAP_CREDENTIALS_PLACEHOLDER);
309 tap_queue_packet(credentials_tap, pinfo, auth);
311 offset += len;
313 /* Adjust the size of the route hop item now that we know the size */
314 proto_item_set_len(route_hop, offset - route_offset);
316 /* Get the service port in numeric format */
317 int_port = dissect_serviceport(port);
319 /* Add the first hostname/port as source in the conversation state*/
320 if ((hop==1) && !(pinfo->fd->visited)){
321 session_state->src_hostname = hostname;
322 session_state->src_port = int_port;
323 session_state->src_password = password;
325 hop++;
328 if (!(pinfo->fd->visited)) {
329 /* Add the last hostname/port as destination */
330 if (hop!=1){
331 session_state->dest_hostname = hostname;
332 session_state->dest_port = int_port;
333 session_state->dest_password = password;
335 /* Save the status of the conversation state */
336 session_state->route_information = true;
337 session_state->route_accepted = false;
342 static void
343 dissect_errorstring(tvbuff_t *tvb, proto_tree *tree, uint32_t offset)
345 uint32_t len;
347 len = tvb_strsize(tvb, offset);
348 proto_tree_add_item(tree, hf_saprouter_error_eyecatcher, tvb, offset, len, ENC_ASCII|ENC_NA);
349 offset += len;
350 len = tvb_strsize(tvb, offset);
351 proto_tree_add_item(tree, hf_saprouter_error_counter, tvb, offset, len, ENC_ASCII|ENC_NA);
352 offset += len;
353 len = tvb_strsize(tvb, offset);
354 proto_tree_add_item(tree, hf_saprouter_error_error, tvb, offset, len, ENC_ASCII|ENC_NA);
355 offset += len;
356 len = tvb_strsize(tvb, offset);
357 proto_tree_add_item(tree, hf_saprouter_error_return_code, tvb, offset, len, ENC_ASCII|ENC_NA);
358 offset += len;
359 len = tvb_strsize(tvb, offset);
360 proto_tree_add_item(tree, hf_saprouter_error_component, tvb, offset, len, ENC_ASCII|ENC_NA);
361 offset += len;
362 len = tvb_strsize(tvb, offset);
363 proto_tree_add_item(tree, hf_saprouter_error_release, tvb, offset, len, ENC_ASCII|ENC_NA);
364 offset += len;
365 len = tvb_strsize(tvb, offset);
366 proto_tree_add_item(tree, hf_saprouter_error_version, tvb, offset, len, ENC_ASCII|ENC_NA);
367 offset += len;
368 len = tvb_strsize(tvb, offset);
369 proto_tree_add_item(tree, hf_saprouter_error_module, tvb, offset, len, ENC_ASCII|ENC_NA);
370 offset += len;
371 len = tvb_strsize(tvb, offset);
372 proto_tree_add_item(tree, hf_saprouter_error_line, tvb, offset, len, ENC_ASCII|ENC_NA);
373 offset += len;
374 len = tvb_strsize(tvb, offset);
375 proto_tree_add_item(tree, hf_saprouter_error_detail, tvb, offset, len, ENC_ASCII|ENC_NA);
376 offset += len;
377 len = tvb_strsize(tvb, offset);
378 proto_tree_add_item(tree, hf_saprouter_error_time, tvb, offset, len, ENC_ASCII|ENC_NA);
379 offset += len;
380 len = tvb_strsize(tvb, offset);
381 proto_tree_add_item(tree, hf_saprouter_error_system_call, tvb, offset, len, ENC_ASCII|ENC_NA);
382 offset += len;
383 len = tvb_strsize(tvb, offset);
384 proto_tree_add_item(tree, hf_saprouter_error_errorno, tvb, offset, len, ENC_ASCII|ENC_NA);
385 offset += len;
386 len = tvb_strsize(tvb, offset);
387 proto_tree_add_item(tree, hf_saprouter_error_errorno_text, tvb, offset, len, ENC_ASCII|ENC_NA);
388 offset += len;
389 len = tvb_strsize(tvb, offset);
390 proto_tree_add_item(tree, hf_saprouter_error_error_count, tvb, offset, len, ENC_ASCII|ENC_NA);
391 offset += len;
392 len = tvb_strsize(tvb, offset);
393 proto_tree_add_item(tree, hf_saprouter_error_location, tvb, offset, len, ENC_ASCII|ENC_NA);
394 offset += len;
396 len = tvb_strsize(tvb, offset);
397 proto_tree_add_item(tree, hf_saprouter_error_unknown, tvb, offset, len, ENC_ASCII|ENC_NA);
398 offset += len;
399 len = tvb_strsize(tvb, offset);
400 proto_tree_add_item(tree, hf_saprouter_error_unknown, tvb, offset, len, ENC_ASCII|ENC_NA);
401 offset += len;
402 len = tvb_strsize(tvb, offset);
403 proto_tree_add_item(tree, hf_saprouter_error_unknown, tvb, offset, len, ENC_ASCII|ENC_NA);
404 offset += len;
405 len = tvb_strsize(tvb, offset);
406 proto_tree_add_item(tree, hf_saprouter_error_unknown, tvb, offset, len, ENC_ASCII|ENC_NA);
407 offset += len;
409 len = tvb_strsize(tvb, offset);
410 proto_tree_add_item(tree, hf_saprouter_error_eyecatcher, tvb, offset, len, ENC_ASCII|ENC_NA);
414 static tvbuff_t*
415 dissect_saprouter_snc_frame(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, uint32_t offset _U_){
417 /* Call the SNC dissector */
418 if (global_saprouter_snc_dissection == true){
419 return dissect_sapsnc_frame(tvb, pinfo, tree, offset);
422 return NULL;
426 static int
427 dissect_saprouter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
429 tvbuff_t *next_tvb = NULL;
430 uint8_t opcode;
431 uint32_t offset = 0, eyecatcher_length = 0;
432 conversation_t *conversation = NULL;
433 saprouter_session_state *session_state = NULL;
434 proto_item *ti = NULL, *ri = NULL, *ei = NULL, *ci = NULL, *gi = NULL, *admin_password = NULL;
435 proto_tree *saprouter_tree = NULL, *route_tree = NULL, *text_tree = NULL, *clients_tree = NULL;
437 /* Search for a conversation */
438 conversation = find_or_create_conversation(pinfo);
439 session_state = (saprouter_session_state *)conversation_get_proto_data(conversation, proto_saprouter);
440 if (!session_state){
441 session_state = wmem_new(wmem_file_scope(), saprouter_session_state);
442 if (session_state){
443 session_state->route_information = false;
444 session_state->route_requested_in = 0;
445 session_state->route_accepted = false;
446 session_state->route_accepted_in = 0;
447 session_state->route_snc_protected = false;
448 session_state->src_hostname = NULL;
449 session_state->src_port = 0;
450 session_state->src_password = NULL;
451 session_state->dest_hostname = NULL;
452 session_state->dest_port = 0;
453 session_state->dest_password = NULL;
454 conversation_add_proto_data(conversation, proto_saprouter, session_state);
455 } else {
456 /* Unable to establish a conversation, break dissection of the packet */
457 return 0;
461 /* Add the protocol to the column */
462 col_set_str(pinfo->cinfo, COL_PROTOCOL, "SAPROUTER");
464 /* Add the main SAP Router subtree */
465 ti = proto_tree_add_item(tree, proto_saprouter, tvb, offset, -1, ENC_NA);
466 saprouter_tree = proto_item_add_subtree(ti, ett_saprouter);
468 /* Get the 'eye catcher' length */
469 eyecatcher_length = tvb_strsize(tvb, offset);
471 /* Niping message */
472 if (tvb_reported_length_remaining(tvb, offset) >= 10 && tvb_strneql(tvb, offset, SAPROUTER_TYPE_NIPING_STRING, 10) == 0) {
473 col_set_str(pinfo->cinfo, COL_INFO, "Niping message");
475 proto_tree_add_item(saprouter_tree, hf_saprouter_type, tvb, offset, 10, ENC_ASCII|ENC_NA);
476 offset += 10;
477 proto_item_append_text(ti, ", Niping message");
479 if (tvb_reported_length_remaining(tvb, offset)) {
480 proto_tree_add_item(saprouter_tree, hf_saprouter_niping_message, tvb, offset, -1, ENC_NA);
484 /* Admin Message Type */
485 else if (tvb_strneql(tvb, offset, SAPROUTER_TYPE_ADMIN_STRING, eyecatcher_length) == 0) {
486 col_set_str(pinfo->cinfo, COL_INFO, "Admin message");
488 proto_tree_add_item(saprouter_tree, hf_saprouter_type, tvb, offset, eyecatcher_length, ENC_ASCII|ENC_NA);
489 offset += eyecatcher_length;
490 proto_item_append_text(ti, ", Admin message");
492 proto_tree_add_item(saprouter_tree, hf_saprouter_ni_version, tvb, offset, 1, ENC_BIG_ENDIAN);
493 offset++;
495 opcode = tvb_get_uint8(tvb, offset);
496 proto_tree_add_item(saprouter_tree, hf_saprouter_admin_command, tvb, offset, 1, ENC_BIG_ENDIAN);
497 offset++;
499 switch (opcode){
500 case 2:{ /* Info request */
501 offset+=2; /* Skip 2 bytes */
502 /* Check if a password was supplied */
503 if (tvb_offset_exists(tvb, offset) && (tvb_strsize(tvb, offset) > 0)){
504 admin_password = proto_tree_add_item(saprouter_tree, hf_saprouter_admin_password, tvb, offset, tvb_strsize(tvb, offset), ENC_ASCII|ENC_NA);
505 expert_add_info(pinfo, admin_password, &ei_saprouter_info_password_found);
507 /* Add the password to the credential tap */
508 tap_credential_t *auth = wmem_new0(pinfo->pool, tap_credential_t);
509 auth->num = pinfo->num;
510 auth->password_hf_id = hf_saprouter_admin_password;
511 auth->proto = "SAP Router Info Request password";
512 auth->username = wmem_strdup(pinfo->pool, TAP_CREDENTIALS_PLACEHOLDER);
513 tap_queue_packet(credentials_tap, pinfo, auth);
515 break;
517 case 10: /* Set Peer Trace */
518 case 11:{ /* Clear Peer Trace */
519 proto_tree_add_item(saprouter_tree, hf_saprouter_admin_address_mask, tvb, offset, 32, ENC_ASCII|ENC_NA);
520 break;
522 case 6: /* Cancel Route request */
523 case 12: /* Trace Connection */
524 case 13: /* Trace Connection */
526 uint16_t client_count = 0, client_count_actual = 0;
528 /* Retrieve the client count first */
529 if (opcode == 6){
530 offset+=2; /* Skip 2 bytes for Cancel Route request*/
531 client_count = tvb_get_ntohs(tvb, offset);
532 proto_tree_add_item(saprouter_tree, hf_saprouter_admin_client_count_short, tvb, offset, 2, ENC_BIG_ENDIAN);
533 offset+=2;
534 } else {
535 client_count = tvb_get_ntohl(tvb, offset);
536 proto_tree_add_item(saprouter_tree, hf_saprouter_admin_client_count_int, tvb, offset, 4, ENC_BIG_ENDIAN);
537 offset+=4;
540 /* Parse the list of client IDs */
541 ci = proto_tree_add_item(saprouter_tree, hf_saprouter_admin_client_ids, tvb, offset, 4*client_count, ENC_NA);
542 clients_tree = proto_item_add_subtree(ci, ett_saprouter);
543 while (tvb_offset_exists(tvb, offset) && tvb_reported_length_remaining(tvb, offset)>=4){
544 proto_tree_add_item(clients_tree, hf_saprouter_admin_client_id, tvb, offset, 4, ENC_BIG_ENDIAN);
545 offset+=4;
546 client_count_actual+=1;
549 /* Check if the actual count of IDs differes from the reported number */
550 if ((client_count_actual != client_count) || tvb_reported_length_remaining(tvb, offset)>0){
551 expert_add_info(pinfo, clients_tree, &ei_saprouter_invalid_client_ids);
554 break;
556 default: {
557 /* Skip 2 bytes */
558 break;
562 /* Route Message Type */
563 } else if (tvb_strneql(tvb, offset, SAPROUTER_TYPE_ROUTE_STRING, eyecatcher_length) == 0){
564 uint32_t route_length = 0, route_offset = 0;
566 col_set_str(pinfo->cinfo, COL_INFO, "Route message");
568 /* Get the route length/offset */
569 route_length = tvb_get_ntohl(tvb, offset + SAPROUTER_ROUTE_LENGTH_OFFSET);
570 route_offset = offset + SAPROUTER_ROUTE_OFFSET_OFFSET + 4;
572 proto_tree_add_item(saprouter_tree, hf_saprouter_type, tvb, 0, eyecatcher_length, ENC_ASCII|ENC_NA);
573 offset += eyecatcher_length;
574 proto_item_append_text(ti, ", Route message");
575 /* Add the fields */
576 proto_tree_add_item(saprouter_tree, hf_saprouter_route_version, tvb, offset, 1, ENC_BIG_ENDIAN);
577 offset++;
578 proto_tree_add_item(saprouter_tree, hf_saprouter_ni_version, tvb, offset, 1, ENC_BIG_ENDIAN);
579 offset++;
580 proto_tree_add_item(saprouter_tree, hf_saprouter_entries, tvb, offset, 1, ENC_BIG_ENDIAN);
581 offset++;
582 proto_tree_add_item(saprouter_tree, hf_saprouter_talk_mode, tvb, offset, 1, ENC_BIG_ENDIAN);
583 offset+=3; /* There're two unused bytes there */
584 proto_tree_add_item(saprouter_tree, hf_saprouter_rest_nodes, tvb, offset, 1, ENC_BIG_ENDIAN);
585 offset++;
586 proto_tree_add_item(saprouter_tree, hf_saprouter_route_length, tvb, offset, 4, ENC_BIG_ENDIAN);
587 offset+=4;
588 proto_tree_add_item(saprouter_tree, hf_saprouter_route_offset, tvb, offset, 4, ENC_BIG_ENDIAN);
589 offset+=4;
590 /* Add the route tree */
591 if ((uint32_t)tvb_reported_length_remaining(tvb, offset) != route_length){
592 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);
593 route_length = (uint32_t)tvb_reported_length_remaining(tvb, offset);
595 ri = proto_tree_add_item(saprouter_tree, hf_saprouter_route, tvb, offset, route_length, ENC_NA);
596 route_tree = proto_item_add_subtree(ri, ett_saprouter);
598 /* Dissect the route string */
599 dissect_routestring(tvb, pinfo, route_tree, route_offset, session_state);
601 /* If this is the first time we're seeing this packet, mark it as the one where the route was requested */
602 if (!pinfo->fd->visited) {
603 session_state->route_requested_in = pinfo->num;
606 /* Add the route to the colinfo*/
607 if (session_state->src_hostname){
608 col_append_fstr(pinfo->cinfo, COL_INFO, ", Source: Hostname=%s Service Port=%d", session_state->src_hostname, session_state->src_port);
609 if (strlen(session_state->src_password)>0)
610 col_append_fstr(pinfo->cinfo, COL_INFO, " Password=%s", session_state->src_password);
612 if (session_state->dest_hostname){
613 col_append_fstr(pinfo->cinfo, COL_INFO, ", Destination: Hostname=%s Service Port=%d", session_state->dest_hostname, session_state->dest_port);
614 if (strlen(session_state->dest_password)>0)
615 col_append_fstr(pinfo->cinfo, COL_INFO, " Password=%s", session_state->dest_password);
618 if (session_state->route_accepted && session_state->route_accepted_in) {
619 gi = proto_tree_add_uint(saprouter_tree, hf_saprouter_route_accepted_in, tvb, 0, 0, session_state->route_accepted_in);
620 proto_item_set_generated(gi);
623 /* Error Information/Control Message Type */
624 } else if (tvb_strneql(tvb, offset, SAPROUTER_TYPE_ERR_STRING, eyecatcher_length) == 0){
626 /* Extract the opcode if possible to determine the type of message */
627 if (tvb_offset_exists(tvb, offset + 10)) {
628 opcode = tvb_get_uint8(tvb, offset + 10);
629 } else {
630 opcode = 0;
633 col_set_str(pinfo->cinfo, COL_INFO, (opcode==0)? "Error information" : "Control message");
635 uint32_t text_length = 0;
637 proto_item_append_text(ti, (opcode==0)? ", Error information" : ", Control message");
638 /* Add the fields */
639 proto_tree_add_item(saprouter_tree, hf_saprouter_type, tvb, offset, eyecatcher_length, ENC_ASCII|ENC_NA);
640 offset += eyecatcher_length;
641 proto_tree_add_item(saprouter_tree, hf_saprouter_ni_version, tvb, offset, 1, ENC_BIG_ENDIAN);
642 offset++;
643 proto_tree_add_item(saprouter_tree, hf_saprouter_opcode, tvb, offset, 1, ENC_BIG_ENDIAN);
644 offset+=2; /* There's a unused byte there */
645 proto_tree_add_item(saprouter_tree, hf_saprouter_return_code, tvb, offset, 4, ENC_BIG_ENDIAN);
646 offset+=4;
648 text_length = tvb_get_ntohl(tvb, offset);
649 /* Error Information Message */
650 if (opcode == 0){
651 proto_tree_add_item(saprouter_tree, hf_saprouter_error_length, tvb, offset, 4, ENC_BIG_ENDIAN);
652 offset+=4;
653 if ((text_length > 0) && tvb_offset_exists(tvb, offset+text_length)){
654 /* Add the error string tree */
655 ei = proto_tree_add_item(saprouter_tree, hf_saprouter_error_string, tvb, offset, text_length, ENC_NA);
656 text_tree = proto_item_add_subtree(ei, ett_saprouter);
657 dissect_errorstring(tvb, text_tree, offset);
658 offset += text_length;
661 /* Add an unknown int field */
662 proto_tree_add_item(saprouter_tree, hf_saprouter_unknown, tvb, offset, 4, ENC_BIG_ENDIAN);
664 /* Control Message */
665 } else {
666 /* Add the opcode name */
667 proto_item_append_text(ti, ", opcode=%s", val_to_str_const(opcode, saprouter_opcode_vals, "Unknown"));
668 col_append_fstr(pinfo->cinfo, COL_INFO, ", opcode=%s", val_to_str_const(opcode, saprouter_opcode_vals, "Unknown"));
670 proto_tree_add_item(saprouter_tree, hf_saprouter_control_length, tvb, offset, 4, ENC_BIG_ENDIAN);
671 offset+=4;
672 if ((text_length >0) && tvb_offset_exists(tvb, offset+text_length)){
673 /* Add the control string tree */
674 proto_tree_add_item(saprouter_tree, hf_saprouter_control_string, tvb, offset, text_length, ENC_ASCII|ENC_NA);
675 offset += text_length;
678 /* SNC request, mark the conversation as SNC protected and dissect the SNC frame */
679 if (opcode == 70 || opcode == 71){
680 session_state->route_snc_protected = true;
681 dissect_saprouter_snc_frame(tvb, pinfo, tree, offset);
683 /* Other opcodes */
684 } else {
685 proto_tree_add_item(saprouter_tree, hf_saprouter_control_unknown, tvb, offset, 4, ENC_ASCII|ENC_NA);
690 /* Route Acceptance (NI_PONG) Message Type */
691 } else if (tvb_strneql(tvb, offset, SAPROUTER_TYPE_ROUTE_ACCEPT, eyecatcher_length) == 0){
692 /* Route information available */
693 if (session_state->route_information){
694 /* If this is the first time we're seen the packet, mark is as the one where the route was accepted */
695 if (!pinfo->fd->visited) {
696 session_state->route_accepted = true;
697 session_state->route_accepted_in = pinfo->num;
700 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);
701 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);
703 if (session_state->route_requested_in) {
704 gi = proto_tree_add_uint(saprouter_tree, hf_saprouter_route_requested_in, tvb, 0, 0, session_state->route_requested_in);
705 proto_item_set_generated(gi);
709 /* Unknown Message Type */
710 } else {
712 col_set_str(pinfo->cinfo, COL_INFO, "Routed message");
713 proto_item_append_text(ti, ", Routed message");
715 /* If the session is protected with SNC, first dissect the SNC frame
716 * and save the content for further dissection.
718 if (session_state->route_snc_protected) {
719 col_append_str(pinfo->cinfo, COL_INFO, ", SNC protected");
720 proto_item_append_text(ti, ", SNC protected");
721 next_tvb = dissect_saprouter_snc_frame(tvb, pinfo, tree, offset);
723 /* If the session is not protected dissect the entire payload */
724 } else {
725 next_tvb = tvb;
728 /* If the session has information about the route requested */
729 if (session_state->route_information){
731 /* Route accepted */
732 if (session_state->route_accepted){
734 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);
735 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);
737 if (session_state->route_requested_in) {
738 gi = proto_tree_add_uint(saprouter_tree, hf_saprouter_route_requested_in, tvb, 0, 0, session_state->route_requested_in);
739 proto_item_set_generated(gi);
741 if (session_state->route_accepted_in) {
742 gi = proto_tree_add_uint(saprouter_tree, hf_saprouter_route_accepted_in, tvb, 0, 0, session_state->route_accepted_in);
743 proto_item_set_generated(gi);
746 /* Route not accepted but some information available */
747 } else {
748 col_append_str(pinfo->cinfo, COL_INFO, ", to unknown destination");
749 proto_item_append_text(ti, ", to unknown destination");
752 /* Call the dissector in the NI protocol sub-dissectors table
753 * according to the route destination port number. */
754 if (next_tvb) {
755 dissect_sap_protocol_payload(next_tvb, offset, pinfo, tree, 0, session_state->dest_port);
758 } else {
759 /* No route information available */
760 col_append_str(pinfo->cinfo, COL_INFO, ", to unknown destination");
761 proto_item_append_text(ti, ", to unknown destination");
765 return tvb_reported_length(tvb);
768 void
769 proto_register_saprouter(void)
771 static hf_register_info hf[] = {
772 { &hf_saprouter_type,
773 { "Type", "saprouter.type", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
775 /* Niping message */
776 { &hf_saprouter_niping_message,
777 { "Niping message", "saprouter.message", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
779 /* NI Route messages */
780 { &hf_saprouter_route_version,
781 { "Route version", "saprouter.version", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
782 { &hf_saprouter_ni_version,
783 { "NI version", "saprouter.niversion", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
784 { &hf_saprouter_entries,
785 { "Entries", "saprouter.entries", FT_UINT8, BASE_DEC, NULL, 0x0, "Total number of entries", HFILL }},
786 { &hf_saprouter_talk_mode,
787 { "Talk Mode", "saprouter.talkmode", FT_UINT8, BASE_DEC, VALS(saprouter_talk_mode_vals), 0x0, NULL, HFILL }},
788 { &hf_saprouter_rest_nodes,
789 { "Remaining Hops", "saprouter.restnodes", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
790 { &hf_saprouter_route_length,
791 { "Route String Length", "saprouter.routelength", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
792 { &hf_saprouter_route_offset,
793 { "Route String Offset", "saprouter.routeoffset", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
794 { &hf_saprouter_route,
795 { "Route String", "saprouter.routestring", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
796 { &hf_saprouter_route_string,
797 { "Route Hop", "saprouter.routestring", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
798 { &hf_saprouter_route_string_hostname,
799 { "Hostname", "saprouter.routestring.hostname", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
800 { &hf_saprouter_route_string_service,
801 { "Service", "saprouter.routestring.service", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
802 { &hf_saprouter_route_string_password,
803 { "Password", "saprouter.routestring.password", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
805 { &hf_saprouter_route_requested_in,
806 { "Route Requested in", "saprouter.requested_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0, "The route request for this packet is in this packet", HFILL }},
807 { &hf_saprouter_route_accepted_in,
808 { "Route Accepted in", "saprouter.accepted_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0, "The route for this packet was accepted in this packet", HFILL }},
810 /* NI error information / Control messages */
811 { &hf_saprouter_opcode,
812 { "Operation Code", "saprouter.opcode", FT_UINT8, BASE_DEC, VALS(saprouter_opcode_vals), 0x0, NULL, HFILL }},
813 { &hf_saprouter_return_code,
814 { "Return Code", "saprouter.returncode", FT_INT32, BASE_DEC, VALS(saprouter_return_code_vals), 0x0, NULL, HFILL }},
815 { &hf_saprouter_unknown,
816 { "Unknown field", "saprouter.unknown", FT_INT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
818 /* NI Error Information messages */
819 { &hf_saprouter_error_length,
820 { "Error Information Text Length", "saprouter.errorlength", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
821 { &hf_saprouter_error_string,
822 { "Error Information Text", "saprouter.errortext", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
823 { &hf_saprouter_error_eyecatcher,
824 { "Eyecatcher", "saprouter.errortext.eyecatcher", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
825 { &hf_saprouter_error_counter,
826 { "Counter", "saprouter.errortext.counter", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
827 { &hf_saprouter_error_error,
828 { "Error", "saprouter.errortext.error", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
829 { &hf_saprouter_error_return_code,
830 { "Return code", "saprouter.errortext.returncode", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
831 { &hf_saprouter_error_component,
832 { "Component", "saprouter.errortext.component", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
833 { &hf_saprouter_error_release,
834 { "Release", "saprouter.errortext.release", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
835 { &hf_saprouter_error_version,
836 { "Version", "saprouter.errortext.version", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
837 { &hf_saprouter_error_module,
838 { "Module", "saprouter.errortext.module", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
839 { &hf_saprouter_error_line,
840 { "Line", "saprouter.errortext.line", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
841 { &hf_saprouter_error_detail,
842 { "Detail", "saprouter.errortext.detail", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
843 { &hf_saprouter_error_time,
844 { "Time", "saprouter.errortext.time", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
845 { &hf_saprouter_error_system_call,
846 { "System Call", "saprouter.errortext.system_call", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
847 { &hf_saprouter_error_errorno,
848 { "Error Number", "saprouter.errortext.errorno", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
849 { &hf_saprouter_error_errorno_text,
850 { "Error Number Text", "saprouter.errortext.errorno_text", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
851 { &hf_saprouter_error_location,
852 { "Location", "saprouter.errortext.location", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
853 { &hf_saprouter_error_error_count,
854 { "Error Count", "saprouter.errortext.error_count", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
855 { &hf_saprouter_error_unknown,
856 { "Unknown field", "saprouter.errortext.unknown", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
858 /* Control messages */
859 { &hf_saprouter_control_length,
860 { "Control Text Length", "saprouter.controllength", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
861 { &hf_saprouter_control_string,
862 { "Control Text", "saprouter.controltext", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
863 { &hf_saprouter_control_unknown,
864 { "Control Unknown field", "saprouter.controlunknown", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
866 /* Router Admin messages */
867 { &hf_saprouter_admin_command,
868 { "Admin Command", "saprouter.command", FT_UINT8, BASE_DEC, VALS(saprouter_admin_command_vals), 0x0, NULL, HFILL }},
869 { &hf_saprouter_admin_password,
870 { "Admin Command Info Password", "saprouter.password", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
871 { &hf_saprouter_admin_client_count_short,
872 { "Admin Command Client Count", "saprouter.client_count", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }},
873 { &hf_saprouter_admin_client_count_int,
874 { "Admin Command Client Count", "saprouter.client_count", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
875 { &hf_saprouter_admin_client_ids,
876 { "Admin Command Client IDs", "saprouter.client_ids", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
877 { &hf_saprouter_admin_client_id,
878 { "Admin Command Client ID", "saprouter.client_id", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
879 { &hf_saprouter_admin_address_mask,
880 { "Admin Command Address Mask", "saprouter.address_mask", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
883 /* Setup protocol subtree array */
884 static int *ett[] = {
885 &ett_saprouter
888 /* Register the expert info */
889 static ei_register_info ei[] = {
890 { &ei_saprouter_route_password_found, { "saprouter.routestring.password.found", PI_SECURITY, PI_WARN, "Route password found", EXPFILL }},
891 { &ei_saprouter_info_password_found, { "saprouter.password.found", PI_SECURITY, PI_WARN, "Info password found", EXPFILL }},
892 { &ei_saprouter_route_invalid_length, { "saprouter.routestring.routelength.invalid", PI_MALFORMED, PI_WARN, "The route string length is invalid", EXPFILL }},
893 { &ei_saprouter_invalid_client_ids, { "saprouter.client_ids.invalid", PI_MALFORMED, PI_WARN, "Client IDs list is malformed", EXPFILL }},
896 module_t *saprouter_module;
897 expert_module_t* saprouter_expert;
899 /* Register the protocol */
900 proto_saprouter = proto_register_protocol("SAP Router Protocol", "SAPROUTER", "saprouter");
902 proto_register_field_array(proto_saprouter, hf, array_length(hf));
903 proto_register_subtree_array(ett, array_length(ett));
905 saprouter_expert = expert_register_protocol(proto_saprouter);
906 expert_register_field_array(saprouter_expert, ei, array_length(ei));
908 register_dissector("saprouter", dissect_saprouter, proto_saprouter);
910 /* Register the preferences */
911 saprouter_module = prefs_register_protocol(proto_saprouter, proto_reg_handoff_saprouter);
913 range_convert_str(wmem_epan_scope(), &global_saprouter_port_range, SAPROUTER_PORT_RANGE, MAX_TCP_PORT);
914 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);
916 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);
918 /* Register the tap*/
919 credentials_tap = register_tap("credentials");
925 * Helpers for dealing with the port range
927 static void range_delete_callback (uint32_t port, void *ptr _U_)
929 dissector_delete_uint("sapni.port", port, saprouter_handle);
932 static void range_add_callback (uint32_t port, void *ptr _U_)
934 dissector_add_uint("sapni.port", port, saprouter_handle);
939 * Register Hand off for the SAP Router Protocol
941 void
942 proto_reg_handoff_saprouter(void)
944 static bool initialized = false;
945 static range_t *saprouter_port_range;
947 if (!initialized) {
948 saprouter_handle = create_dissector_handle(dissect_saprouter, proto_saprouter);
949 initialized = true;
950 } else {
951 range_foreach(saprouter_port_range, range_delete_callback, NULL);
952 wmem_free(wmem_epan_scope(), saprouter_port_range);
955 saprouter_port_range = range_copy(wmem_epan_scope(), global_saprouter_port_range);
956 range_foreach(saprouter_port_range, range_add_callback, NULL);
961 * Editor modelines - https://www.wireshark.org/tools/modelines.html
963 * Local variables:
964 * c-basic-offset: 8
965 * tab-width: 8
966 * indent-tabs-mode: t
967 * End:
969 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
970 * :indentSize=8:tabSize=8:noTabs=false: