Hi,
I've figured it out, it has been a feature of the dissector code. Perhaps it might be useful for someone developing her own SPOA.
Kind regards,
Danny
Gesendet: Donnerstag, 09. November 2017 um 16:07 Uhr
Von: "Frederic Lecaille" <flecai...@haproxy.com>
An: my.card....@web.de, haproxy@formilux.org
Betreff: Re: [RFC] Wireshark dissector for SPOP
Von: "Frederic Lecaille" <flecai...@haproxy.com>
An: my.card....@web.de, haproxy@formilux.org
Betreff: Re: [RFC] Wireshark dissector for SPOP
On 11/05/2017 09:27 AM, my.card....@web.de wrote:
> Hi all,
Hi,
> I've implemented a very basic wireshark (https://www.wireshark.org)
> dissector for SPOP. I've stumbled over the following issue, that I
> couldn't figure out, yet.
> ACTION-ARGS should be multiple TYPED-DATA items, but the data sent by
> contrib/spoa_sample does not add type information:
> 73335 14123.613537866 127.0.0.1 127.0.0.1 SPOP 89 ACK
> STREAM-ID:35969 FRAME-ID:1[Malformed Packet]
> 0000 00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00 ..............E.
> 0010 00 4b 70 77 40 00 40 06 cc 33 7f 00 00 01 7f 00 .Kpw@.@..3......
> 0020 00 01 30 39 cd 4c e8 1d cd f2 05 90 45 92 80 18 ..09.L......E...
> 0030 01 5e fe 3f 00 00 01 01 08 0a 01 46 c7 f3 01 46 .^.?.......F...F
> 0040 c7 f3 67 01 00 00 00 f2 99 01 01 01 03 01 08 69 ..g............i
> 0050 70 5f 73 63 6f 72 65 03 47 p_score.G
> Is this an implementation or documentation issues? What is haproxy
> expecting here?
> Kind regards,
> Danny
Thank you for sharing this code.
It could be interesting to have such a dissector in the future.
I will have a look at your code asap.
Do not hesitate to update this thread if you managed to fix some issues.
Regards,
Fred.
> Hi all,
Hi,
> I've implemented a very basic wireshark (https://www.wireshark.org)
> dissector for SPOP. I've stumbled over the following issue, that I
> couldn't figure out, yet.
> ACTION-ARGS should be multiple TYPED-DATA items, but the data sent by
> contrib/spoa_sample does not add type information:
> 73335 14123.613537866 127.0.0.1 127.0.0.1 SPOP 89 ACK
> STREAM-ID:35969 FRAME-ID:1[Malformed Packet]
> 0000 00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00 ..............E.
> 0010 00 4b 70 77 40 00 40 06 cc 33 7f 00 00 01 7f 00 .Kpw@.@..3......
> 0020 00 01 30 39 cd 4c e8 1d cd f2 05 90 45 92 80 18 ..09.L......E...
> 0030 01 5e fe 3f 00 00 01 01 08 0a 01 46 c7 f3 01 46 .^.?.......F...F
> 0040 c7 f3 67 01 00 00 00 f2 99 01 01 01 03 01 08 69 ..g............i
> 0050 70 5f 73 63 6f 72 65 03 47 p_score.G
> Is this an implementation or documentation issues? What is haproxy
> expecting here?
> Kind regards,
> Danny
Thank you for sharing this code.
It could be interesting to have such a dissector in the future.
I will have a look at your code asap.
Do not hesitate to update this thread if you managed to fix some issues.
Regards,
Fred.
/* * Wireshark dissector for SPOP * * Copyright 2017 Daniela Sonnenschein <my.card....@web.de> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Please see the protocol specification at * * https://www.haproxy.org/download/1.7/doc/SPOE.txt * */ #include <stdint.h> #include <stdio.h>
#include "config.h" #include <epan/packet.h> #include <epan/prefs.h> #include "packet-tcp.h" #define SPOP_PORT 12345 static int proto_spop = -1; static int hf_spop_frame_length = -1; static int hf_spop_frame_type = -1; static int hf_spop_flags = -1; static int hf_spop_flag_fin = -1; static int hf_spop_frame_id = -1; static int hf_spop_stream_id = -1; static int hf_spop_kvlist = -1; static int hf_spop_key_value = -1; static int hf_spop_key_value_flags = -1; static int hf_spop_messages = -1; static int hf_spop_message = -1; static int hf_spop_nbargs = -1; static int hf_spop_actions = -1; static int hf_spop_action = -1; static int hf_spop_action_type = -1; static int hf_spop_action_args = -1; static int hf_spop_action_scope = -1; static int hf_spop_typed_data = -1; static int hf_spop_boolean = -1; static int hf_spop_integer = -1; static int hf_spop_ipv4 = -1; static int hf_spop_ipv6 = -1; static int hf_spop_string = -1; static int hf_spop_string_length = -1; static int hf_spop_string_value = -1; static int hf_spop_binary = -1; static gint ett_spop = -1; static gint ett_spop_flags = -1; static gint ett_spop_kvlist = -1; static gint ett_spop_key_value = -1; static gint ett_spop_messages = -1; static gint ett_spop_message = -1; static gint ett_spop_actions = -1; static gint ett_spop_action = -1; static gint ett_spop_action_args = -1; static gint ett_spop_string = -1; static gint ett_spop_typed_data = -1; /* FLAGS : 0 1-31 +---+-----------+ | F| | | I| RESERVED | | N| | +--+------------+ */ /* FIN: Indicates that this is the final payload fragment. The first fragment may also be the final fragment. */ #define SPOP_FLAG_FIN 0x01 static const value_string frame_types[] = { { 1, "HAPROXY-HELLO" }, /* Sent by HAproxy when it opens a connection to an agent */ { 2, "HAPROXY-DISCONNECT" }, /* Sent by HAproxy when it want to close the connection or in reply to an AGENT-DISCONNECT frame */ { 3, "NOTIFY" }, /* Sent by HAproxy to pass information to an agent */ { 101, "AGENT-HELLO" }, /* Reply to a HAPROXY-HELLO frame, when the connection is established */ { 102, "AGENT-DISCONNECT" }, /* Sent by an agent just before closing the connection */ { 103, "ACK" }, /* Sent to acknowledge a NOTIFY frame */ { 0, NULL } }; static const value_string action_types[] = { { 1, "set-var" }, /* <1> */ { 2, "unset-var" }, /* <2> */ { 0, NULL } }; static const value_string action_scopes[] = { { 0, "PROCESS" }, /* <0> */ { 1, "SESSION" }, /* <1> */ { 2, "TRANSATION" }, /* <2> */ { 3, "REQUEST" }, /* <3> */ { 4, "RESERVED" }, /* <4> */ { 0, NULL } }; static const value_string data_type[] = { { 0, "NULL" }, /* <0> */ { 1, "BOOL" }, /* <1+FLAG> */ { 2, "INT32" }, /* <2><VALUE:varint> */ { 3, "UINT32" }, /* <3><VALUE:varint> */ { 4, "INT64" }, /* <4><VALUE:varint> */ { 5, "UINT64" }, /* <5><VALUE:varint> */ { 6, "IPV4" }, /* <6><STRUCT IN_ADDR:4 bytes> */ { 7, "IPV6" }, /* <7><STRUCT IN_ADDR6:16 bytes> */ { 8, "String" }, /* <8><LENGTH:varint><BYTES> */ { 9, "Binary" }, /* <9><LENGTH:varint><BYTES> */ { 0, NULL } }; /* For booleans, the value (true or false) is the first bit in the FLAGS bitfield. if this bit is set to 0, then the boolean is evaluated as false, otherwise, the boolean is evaluated as true. */ #define SPOP_KEY_VALUE_FLAGS_MASK 0xf0 #define SPOP_KEY_VALUE_FLAG_BOOLEAN 0x10 void proto_register_spop(void) { static hf_register_info hf[] = { { &hf_spop_frame_length, { "Frame Length", "spop.frame.length", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } }, { &hf_spop_frame_type, { "Frame Type", "spop.frame.type", FT_UINT8, BASE_DEC, VALS(frame_types), 0x0, NULL, HFILL } }, { &hf_spop_flags, { "Flags", "spop.flags", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } }, { &hf_spop_flag_fin, { "FIN Flag", "spop.flags.fin", FT_BOOLEAN, 8, NULL, SPOP_FLAG_FIN, NULL, HFILL } }, { &hf_spop_stream_id, { "Stream ID", "spop.stream.id", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_spop_frame_id, { "Frame ID", "spop.frame.id", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_spop_kvlist, { "KV-LIST", "spop.payload.kv-list", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_spop_key_value, { "KEY-VALUE", "spop.payload.key-value", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_spop_key_value_flags, { "KEY-VALUE-FLAGS", "spop.payload.key-value-flags", FT_UINT8, 4, NULL, SPOP_KEY_VALUE_FLAGS_MASK, NULL, HFILL } }, { &hf_spop_messages, { "MESSAGES", "spop.payload.messages", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_spop_message, { "MESSAGE", "spop.payload.message", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_spop_nbargs, { "NBARGS", "spop.payload.nbargs", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } }, { &hf_spop_actions, { "ACTIONS", "spop.payload.actions", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_spop_action, { "ACTION", "spop.payload.action", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_spop_action_type, { "ACTION-TYPE", "spop.payload.action.type", FT_UINT8, BASE_DEC, VALS(action_types), 0x0, NULL, HFILL } }, { &hf_spop_action_args, { "ACTION-ARGS", "spop.payload.action.args", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_spop_action_scope, { "ACTION-SCOPE", "spop.payload.action.scope", FT_UINT8, BASE_DEC, VALS(action_scopes), 0x0, NULL, HFILL } }, { &hf_spop_typed_data, { "TYPED-DATA", "spop.payload.typed-data", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_spop_boolean, { "BOOLEAN", "spop.value.boolean", FT_BOOLEAN, 8, NULL, SPOP_KEY_VALUE_FLAG_BOOLEAN, NULL, HFILL } }, { &hf_spop_integer, { "INTEGER", "spop.value.integer", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_spop_ipv4, { "IPv4", "spop.value.ipv4", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_spop_ipv6, { "IPv6", "spop.value.ipv6", FT_IPv6, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_spop_string, { "STRING", "spop.value.string", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_spop_string_length, { "LENGTH", "spop.value.string.length", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_spop_string_value, { "VALUE", "spop.value.string.value", FT_STRING, STR_ASCII, NULL, 0x0, NULL, HFILL } }, { &hf_spop_binary, { "BINARY", "spop.value.binary", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } } }; static gint *ett[] = { &ett_spop, &ett_spop_flags, &ett_spop_kvlist, &ett_spop_typed_data, &ett_spop_key_value, &ett_spop_messages, &ett_spop_message, &ett_spop_actions, &ett_spop_action, &ett_spop_action_args, &ett_spop_string, }; proto_spop = proto_register_protocol ( "SPOP Protocol", /* name */ "SPOP", /* short name */ "spop" /* abbrev */ ); proto_register_field_array(proto_spop, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); } /* Variable-length integer (varint) are encoded using Peers encoding: * * 0 <= X < 240 : 1 byte (7.875 bits) [ XXXX XXXX ] * 240 <= X < 2288 : 2 bytes (11 bits) [ 1111 XXXX ] [ 0XXX XXXX ] * 2288 <= X < 264432 : 3 bytes (18 bits) [ 1111 XXXX ] [ 1XXX XXXX ] [ 0XXX XXXX ] * 264432 <= X < 33818864 : 4 bytes (25 bits) [ 1111 XXXX ] [ 1XXX XXXX ]*2 [ 0XXX XXXX ] * 33818864 <= X < 4328786160 : 5 bytes (32 bits) [ 1111 XXXX ] [ 1XXX XXXX ]*3 [ 0XXX XXXX ] * ... */ static int dissect_varint(tvbuff_t *tvb, proto_tree *tree _U_, int id, size_t offset, uint64_t *valuep) { size_t length; uint64_t value = 0; uint8_t tmp; tmp = tvb_get_guint8(tvb, offset); length = 1; if ((tmp & 0xf0) == 0xf0) { value = (tmp & 0x0f); do { tmp = tvb_get_guint8(tvb, offset + length); length++; value <<= 7; value += (tmp & 0x7f); } while (tmp & 0x80); } else /* if (tmp < 240) */ { value = tmp; } { proto_item *ti = proto_tree_add_item(tree, id, tvb, offset, length, ENC_NA); proto_item_append_text(ti, ": %lu (varint %lu octets)", value, length); } if (valuep) *valuep = value; return length; } static int dissect_string(tvbuff_t *tvb, proto_tree *tree _U_, size_t offset, char **strp) { size_t length; uint64_t string_length; proto_tree *item; char *string_value; item = proto_tree_add_item(tree, hf_spop_string, tvb, offset, 0, ENC_NA); tree = proto_item_add_subtree(item, ett_spop_string); length = dissect_varint(tvb, tree, hf_spop_string_length, offset, &string_length); proto_tree_add_item(tree, hf_spop_string_value, tvb, offset + length, string_length, ENC_STRING); proto_item_set_len(item, length + string_length); if ((string_value = (char *)wmem_alloc(wmem_packet_scope(), string_length+1))) { tvb_memcpy(tvb, (guint8 *)string_value, offset + length, string_length); string_value[string_length] = '\0'; proto_item_append_text(item, ": \"%s\"", string_value); } if (strp) *strp = string_value; return length + string_length; } static int dissect_binary(tvbuff_t *tvb, proto_tree *tree _U_, size_t offset) { size_t length; uint64_t binary_length; length = dissect_varint(tvb, tree, hf_spop_integer, offset, &binary_length); proto_tree_add_item(tree, hf_spop_binary, tvb, offset + length, binary_length, ENC_NA); return length + binary_length; } static int dissect_typed_data(tvbuff_t *tvb, proto_tree *tree _U_, size_t offset, char **value) { size_t length = 0; proto_tree *item; item = proto_tree_add_item(tree, hf_spop_typed_data, tvb, offset, 1, ENC_NA); tree = proto_item_add_subtree(item, ett_spop_typed_data); switch (tvb_get_guint8(tvb, offset)) { case 9: /* Binary */ length = dissect_binary(tvb, tree, offset + 1); break; case 8: /* String */ length = dissect_string(tvb, tree, offset + 1, value); break; case 7: /* IPV6 */ proto_tree_add_item(tree, hf_spop_ipv6, tvb, offset + 1, 16, ENC_NA); length = 16; break; case 6: /* IPV4 */ proto_tree_add_item(tree, hf_spop_ipv4, tvb, offset + 1, 4, ENC_NA); length = 4; break; case 5: /* UINT64 */ case 4: /* INT64 */ case 3: /* UINT32 */ case 2: /* INT32 */ length = dissect_varint(tvb, tree, hf_spop_integer, offset + 1, NULL); break; case 1: /* Boolean */ proto_tree_add_item(tree, hf_spop_boolean, tvb, offset, 1, ENC_NA); length = 0; break; case 0: /* NULL */ default: break; } proto_item_set_len(item, length + 1); return length + 1; } static int dissect_key_value(tvbuff_t *tvb, proto_tree *tree _U_, size_t offset) { size_t length; proto_tree *item; char *key = "-", *value = "-"; /* <STRING> <TYPED-DATA> */ item = proto_tree_add_item(tree, hf_spop_key_value, tvb, offset, 0, ENC_NA); tree = proto_item_add_subtree(item, ett_spop_key_value); proto_tree_add_item(tree, hf_spop_key_value_flags, tvb, offset, 1, ENC_NA); length = dissect_string(tvb, tree, offset, &key); length += dissect_typed_data(tvb, tree, offset + length, &value); proto_item_set_len(item, length); proto_item_append_text(item, ": \"%s\" = \"%s\"", key, value); return length; } static int dissect_kvlist(tvbuff_t *tvb, proto_tree *tree _U_, size_t offset) { size_t length; size_t kvlist_length; proto_tree *item; int pairs; /* KV-LIST: [ <KV-NAME> <KV-VALUE> ... ] */ length = tvb_captured_length(tvb); kvlist_length = length - offset; item = proto_tree_add_item(tree, hf_spop_kvlist, tvb, offset, kvlist_length, ENC_NA); tree = proto_item_add_subtree(item, ett_spop_kvlist); for (pairs=0; offset<length; pairs++) offset += dissect_key_value(tvb, tree, offset); proto_item_append_text(item, ": %d key value pairs", pairs); return kvlist_length; } static int dissect_message(tvbuff_t *tvb, proto_tree *tree _U_, size_t offset) { size_t length; proto_tree *item; guint8 nbargs, i; /* <MESSAGE-NAME> <NB-ARGS:1 byte> <KV-LIST> */ item = proto_tree_add_item(tree, hf_spop_message, tvb, offset, 0, ENC_NA); tree = proto_item_add_subtree(item, ett_spop_message); length = dissect_string(tvb, tree, offset, NULL); nbargs = tvb_get_guint8(tvb, offset + length); proto_tree_add_item(tree, hf_spop_nbargs, tvb, offset + length, 1, ENC_NA); length += 1; for (i=0; i<nbargs; i++) length += dissect_kvlist(tvb, tree, offset + length); proto_item_set_len(item, length); return length; } static int dissect_messages(tvbuff_t *tvb, proto_tree *tree _U_, size_t offset) { size_t length; size_t messages_length; proto_tree *item; /* LIST-OF-MESSAGES: [ <MESSAGE-NAME> <NB-ARGS:1 byte> <KV-LIST> ... ] */ length = tvb_captured_length(tvb); messages_length = length - offset; item = proto_tree_add_item(tree, hf_spop_messages, tvb, offset, messages_length, ENC_NA); tree = proto_item_add_subtree(item, ett_spop_messages); while (offset < length) offset += dissect_message(tvb, tree, offset); return messages_length; } static int dissect_action_args(tvbuff_t *tvb, proto_tree *tree _U_, size_t offset, guint8 nbargs) { proto_tree *item; size_t length = 0; int i; /* ACTION-ARGS: [ <TYPED-DATA>... ] */ item = proto_tree_add_item(tree, hf_spop_action_args, tvb, offset, 0, ENC_NA); tree = proto_item_add_subtree(item, ett_spop_action_args); proto_item_append_text(item, ": %d action args", nbargs); for (i=0; i<nbargs; i++) length += dissect_typed_data(tvb, tree, offset + length, NULL); proto_item_set_len(item, length); return length; } static int dissect_action(tvbuff_t *tvb, proto_tree *tree _U_, size_t offset) { size_t length; guint8 type; guint8 nbargs; proto_tree *item, *args; /* An agent must acknowledge each NOTIFY frame by sending the corresponding ACK frame. Actions can be added in these frames to dynamically take action on the processing of a stream. */ item = proto_tree_add_item(tree, hf_spop_action, tvb, offset, 0, ENC_NA); tree = proto_item_add_subtree(item, ett_spop_action); /* ACTION-TYPE:1 byte> <NB-ARGS:1 byte> <ACTION-ARGS> */ proto_tree_add_item(tree, hf_spop_action_type, tvb, offset, 1, ENC_NA); type = tvb_get_guint8(tvb, offset); length = 1; proto_tree_add_item(tree, hf_spop_nbargs, tvb, offset + length, 1, ENC_NA); nbargs = tvb_get_guint8(tvb, offset + length); length += 1; args = proto_tree_add_item(tree, hf_spop_action_args, tvb, offset + length, 0, ENC_NA); tree = proto_item_add_subtree(args, ett_spop_action_args); /* Here is the list of supported actions: */ switch (type) { case 0x01: /* set-var - set the value for an existing variable. 3 arguments must be attached to this action: the variable scope (proc, sess, txn, req or req), the variable name (a string) and its value. ACTION-SET-VAR : <SET-VAR:1 byte><NB-ARGS:1 byte> <VAR-SCOPE:1 byte><VAR-NAME><VAR-VALUE> SET-VAR : <1> NB-ARGS : <3> VAR-SCOPE : <PROCESS> | <SESSION> | <TRANSACTION> | <REQUEST> | <RESPONSE> VAR-NAME : <STRING> VAR-VALUE : <TYPED-DATA> */ if (nbargs == 3) { // VAR-SCOPE proto_tree_add_item(tree, hf_spop_action_scope, tvb, offset + length, 1, ENC_NA); length += 1; // VAR-NAME length += dissect_string(tvb, tree, offset + length, NULL); // VAR-VALUE length += dissect_typed_data(tvb, tree, offset + length, NULL); } break; case 0x02: /* unset-var - unset the value for an existing variable. 2 arguments must be attached to this action: the variable scope (proc, sess, txn, req or req) and the variable name (a string). ACTION-UNSET-VAR : <UNSET-VAR:1 byte><NB-ARGS:1 byte> <VAR-SCOPE:1 byte><VAR-NAME> UNSET-VAR : <2> NB-ARGS : <2> VAR-SCOPE : <PROCESS> | <SESSION> | <TRANSACTION> | <REQUEST> | <RESPONSE> VAR-NAME : <STRING> */ if (nbargs == 2) { // VAR-SCOPE proto_tree_add_item(tree, hf_spop_action_scope, tvb, offset + length, 1, ENC_NA); length += 1; // VAR-NAME length += dissect_string(tvb, tree, offset + length, NULL); } break; /* default: error */ } // length += dissect_action_args(tvb, tree, offset + length, nbargs); proto_item_set_len(item, length); return length; } static int dissect_actions(tvbuff_t *tvb, proto_tree *tree _U_, size_t offset) { size_t length; size_t actions_length; proto_tree *item; int actions; /* LIST-OF-ACTIONS: [ <ACTION> ... ] */ length = tvb_captured_length(tvb); actions_length = length - offset; item = proto_tree_add_item(tree, hf_spop_actions, tvb, offset, actions_length, ENC_NA); tree = proto_item_add_subtree(item, ett_spop_actions); for (actions=0; offset < length; actions++) offset += dissect_action(tvb, tree, offset); proto_item_append_text(item, ": %d actions", actions); return actions_length; } static int dissect_spop_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void *data _U_) { size_t offset = 0; proto_tree *spop_tree; proto_item *item; uint8_t frame_type; uint64_t stream_id; uint64_t frame_id; proto_tree *flags_tree, *ft; col_set_str(pinfo->cinfo, COL_PROTOCOL, "SPOP"); /* Clear out stuff in the info column */ col_clear(pinfo->cinfo, COL_INFO); item = proto_tree_add_item(tree, proto_spop, tvb, 0, -1, ENC_NA); spop_tree = proto_item_add_subtree(item, ett_spop); /* Exchange between HAProxy and agents are made using FRAME packets. All frames must be prefixed with their size encoded on 4 bytes in network byte order: <FRAME-LENGTH:4 bytes> <FRAME> */ proto_tree_add_item(spop_tree, hf_spop_frame_length, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; /* A frame always starts with its type, on one byte, followed by metadata containing flags, on 4 bytes and a two variable-length integer representing the stream identifier and the frame identifier inside the stream: FRAME : <FRAME-TYPE:1 byte> <METADATA> <FRAME-PAYLOAD> METADATA : <FLAGS:4 bytes> <STREAM-ID:varint> <FRAME-ID:varint> */ frame_type = tvb_get_guint8(tvb, offset); proto_tree_add_item(spop_tree, hf_spop_frame_type, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_item_append_text(item, ": %s", val_to_str(frame_type, frame_types, "Unknown (0x%02x)")); ft = proto_tree_add_item(spop_tree, hf_spop_flags, tvb, offset, 4, ENC_BIG_ENDIAN); flags_tree = proto_item_add_subtree(ft, ett_spop_flags); proto_tree_add_item(flags_tree, hf_spop_flag_fin, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 4; offset += dissect_varint(tvb, spop_tree, hf_spop_stream_id, offset, &stream_id); offset += dissect_varint(tvb, spop_tree, hf_spop_frame_id, offset, &frame_id); col_add_fstr(pinfo->cinfo, COL_INFO, /* "FRAME-TYPE: */ "%s STREAM-ID:%lu FRAME-ID:%lu", val_to_str(frame_type, frame_types, "0x%02x"), stream_id, frame_id); /* Then comes the frame payload. Depending on the frame type, the payload can be of three types: a simple key/value list, a list of messages or a list of actions. FRAME-PAYLOAD : <LIST-OF-MESSAGES> | <LIST-OF-ACTIONS> | <KV-LIST> LIST-OF-MESSAGES : [ <MESSAGE-NAME> <NB-ARGS:1 byte> <KV-LIST> ... ] MESSAGE-NAME : <STRING> LIST-OF-ACTIONS : [ <ACTION-TYPE:1 byte> <NB-ARGS:1 byte> <ACTION-ARGS> ... ] ACTION-ARGS : [ <TYPED-DATA>... ] KV-LIST : [ <KV-NAME> <KV-VALUE> ... ] KV-NAME : <STRING> KV-VALUE : <TYPED-DATA> */ switch (frame_type) { case 1: /* "HAPROXY-HELLO" */ case 2: /* "HAPROXY-DISCONNECT" */ case 101: /* "AGENT-HELLO" */ case 102: /* "AGENT-DISCONNECT" */ offset += dissect_kvlist(tvb, spop_tree, offset); break; case 3: /* "NOTIFY" */ offset += dissect_messages(tvb, spop_tree, offset); break; case 103: /* "ACK" */ offset += dissect_actions(tvb, spop_tree, offset); break; default: fprintf(stderr, "%s: What?! %i\n", __func__, frame_type); /* Panic and run! */ break; } return tvb_captured_length(tvb); } /* determine PDU length of spo protocol */ static guint get_spop_frame_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_) { return (guint)tvb_get_ntohl(tvb, offset) + 4; } /* The main dissecting routine */ static int dissect_spop(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) { tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 4, get_spop_frame_len, dissect_spop_frame, data); return tvb_captured_length(tvb); } void proto_reg_handoff_spop(void) { static dissector_handle_t spop_handle; spop_handle = create_dissector_handle(dissect_spop, proto_spop); dissector_add_uint("tcp.port", SPOP_PORT, spop_handle); }