Welcome! Log In Create A New Profile

Advanced

[RFC] Wireshark dissector for SPOP

Posted by Anonymous User 
Anonymous User
[RFC] Wireshark dissector for SPOP
November 05, 2017 09:40AM
/*
* Wireshark dissector for SPOP
*
* Copyright 2017 Daniela Sonnenschein <[email protected]>
*
* 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_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_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 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_BOOLEAN_TRUE 0x08

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_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, NULL, 0x0, NULL, HFILL }
},
{ &hf_spop_action_args,
{ "ACTION-ARGS", "spop.payload.action.args",
FT_NONE, BASE_NONE, NULL, 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_BOOLEAN_TRUE, 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)
{
size_t length;
uint64_t string_length;
proto_tree *item;

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);

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)
{
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);
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;

/* <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);

length = dissect_string(tvb, tree, offset);
length += dissect_typed_data(tvb, tree, offset + length);

proto_item_set_len(item, length);

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);

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);

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 nbargs;
proto_tree *item;

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);
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;

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);
}
Frederic Lecaille
Re: [RFC] Wireshark dissector for SPOP
November 09, 2017 04:20PM
On 11/05/2017 09:27 AM, My.Card.God@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  [email protected]@..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.
Anonymous User
Aw: Re: [RFC] Wireshark dissector for SPOP
November 12, 2017 07:50PM
/*
* Wireshark dissector for SPOP
*
* Copyright 2017 Daniela Sonnenschein <[email protected]>
*
* 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);
}
Sorry, only registered users may post in this forum.

Click here to login