summaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-sctp.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
commite4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch)
tree68cb5ef9081156392f1dd62a00c6ccc1451b93df /epan/dissectors/packet-sctp.c
parentInitial commit. (diff)
downloadwireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz
wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'epan/dissectors/packet-sctp.c')
-rw-r--r--epan/dissectors/packet-sctp.c5237
1 files changed, 5237 insertions, 0 deletions
diff --git a/epan/dissectors/packet-sctp.c b/epan/dissectors/packet-sctp.c
new file mode 100644
index 0000000..f9f72a1
--- /dev/null
+++ b/epan/dissectors/packet-sctp.c
@@ -0,0 +1,5237 @@
+/* packet-sctp.c
+ * Routines for Stream Control Transmission Protocol dissection
+ * Copyright 2000-2012 Michael Tuexen <tuexen [AT] fh-muenster.de>
+ * Copyright 2011-2021 Thomas Dreibholz <dreibh [AT] iem.uni-due.de>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+/*
+ * It should be compliant to
+ * - RFC 2960
+ * - RFC 3309
+ * - RFC 3758
+ * - RFC 4460
+ * - RFC 4895
+ * - RFC 4960
+ * - RFC 5061
+ * - RFC 6525
+ * - RFC 7053
+ * - https://tools.ietf.org/html/draft-stewart-sctp-pktdrprep-02
+ * - https://www.ietf.org/archive/id/draft-ietf-tsvwg-sctp-zero-checksum-01.html
+ *
+ * Still to do (so stay tuned)
+ * - error checking mode
+ * * padding errors
+ * * length errors
+ * * bundling errors
+ * * value errors
+ *
+ * Reassembly added 2006 by Robin Seggelmann
+ * TSN Tracking by Luis E. G. Ontanon (Feb 2007)
+ * Copyright 2009, Varun Notibala <nbvarun [AT] gmail.com>
+ *
+ * PPID types updated by Thomas Dreibholz (Feb 2011)
+ */
+
+#include "config.h"
+
+#include "ws_symbol_export.h"
+
+#include <epan/packet.h>
+#include <epan/capture_dissectors.h>
+#include <epan/prefs.h>
+#include <epan/exceptions.h>
+#include <epan/exported_pdu.h>
+#include <epan/ipproto.h>
+#include <epan/addr_resolv.h>
+#include <epan/sctpppids.h>
+#include <epan/uat.h>
+#include <epan/expert.h>
+#include <epan/conversation_table.h>
+#include <epan/show_exception.h>
+#include <epan/decode_as.h>
+#include <epan/proto_data.h>
+
+#include <wsutil/crc32.h>
+#include <wsutil/adler32.h>
+#include <wsutil/utf8_entities.h>
+#include <wsutil/str_util.h>
+#include <wsutil/ws_roundup.h>
+
+#include "packet-sctp.h"
+
+#define LT(x, y) ((gint32)((x) - (y)) < 0)
+
+#define UDP_TUNNELING_PORT 9899
+
+#define MAX_NUMBER_OF_PPIDS 2
+/** This is a valid PPID, but we use it to mark the end of the list */
+#define LAST_PPID 0xffffffff
+
+void proto_register_sctp(void);
+void proto_reg_handoff_sctp(void);
+
+/* Initialize the protocol and registered fields */
+static int proto_sctp = -1;
+static int hf_port = -1;
+static int hf_source_port = -1;
+static int hf_destination_port = -1;
+static int hf_verification_tag = -1;
+static int hf_checksum = -1;
+static int hf_checksum_adler = -1;
+static int hf_checksum_crc32c = -1;
+static int hf_checksum_status = -1;
+
+static int hf_chunk_type = -1;
+static int hf_chunk_flags = -1;
+static int hf_chunk_bit_1 = -1;
+static int hf_chunk_bit_2 = -1;
+static int hf_chunk_length = -1;
+static int hf_chunk_padding = -1;
+static int hf_chunk_value = -1;
+
+static int hf_initiate_tag = -1;
+static int hf_init_chunk_initiate_tag = -1;
+static int hf_init_chunk_adv_rec_window_credit = -1;
+static int hf_init_chunk_number_of_outbound_streams = -1;
+static int hf_init_chunk_number_of_inbound_streams = -1;
+static int hf_init_chunk_initial_tsn = -1;
+
+static int hf_initack_chunk_initiate_tag = -1;
+static int hf_initack_chunk_adv_rec_window_credit = -1;
+static int hf_initack_chunk_number_of_outbound_streams = -1;
+static int hf_initack_chunk_number_of_inbound_streams = -1;
+static int hf_initack_chunk_initial_tsn = -1;
+
+static int hf_data_chunk_tsn = -1;
+static int hf_data_chunk_tsn_raw = -1;
+static int hf_data_chunk_stream_id = -1;
+static int hf_data_chunk_stream_seq_number = -1;
+static int hf_data_chunk_payload_proto_id = -1;
+static int hf_idata_chunk_reserved = -1;
+static int hf_idata_chunk_mid = -1;
+static int hf_idata_chunk_fsn = -1;
+
+static int hf_data_chunk_e_bit = -1;
+static int hf_data_chunk_b_bit = -1;
+static int hf_data_chunk_u_bit = -1;
+static int hf_data_chunk_i_bit = -1;
+
+static int hf_sack_chunk_ns = -1;
+static int hf_sack_chunk_cumulative_tsn_ack = -1;
+static int hf_sack_chunk_cumulative_tsn_ack_raw = -1;
+static int hf_sack_chunk_adv_rec_window_credit = -1;
+static int hf_sack_chunk_number_of_gap_blocks = -1;
+static int hf_sack_chunk_number_of_dup_tsns = -1;
+static int hf_sack_chunk_gap_block_start = -1;
+static int hf_sack_chunk_gap_block_end = -1;
+static int hf_sack_chunk_gap_block_start_tsn = -1;
+static int hf_sack_chunk_gap_block_end_tsn = -1;
+static int hf_sack_chunk_number_tsns_gap_acked = -1;
+static int hf_sack_chunk_duplicate_tsn = -1;
+
+static int hf_nr_sack_chunk_ns = -1;
+static int hf_nr_sack_chunk_cumulative_tsn_ack = -1;
+static int hf_nr_sack_chunk_adv_rec_window_credit = -1;
+static int hf_nr_sack_chunk_number_of_gap_blocks = -1;
+static int hf_nr_sack_chunk_number_of_nr_gap_blocks = -1;
+static int hf_nr_sack_chunk_number_of_dup_tsns = -1;
+static int hf_nr_sack_chunk_reserved = -1;
+static int hf_nr_sack_chunk_gap_block_start = -1;
+static int hf_nr_sack_chunk_gap_block_end = -1;
+static int hf_nr_sack_chunk_gap_block_start_tsn = -1;
+static int hf_nr_sack_chunk_gap_block_end_tsn = -1;
+static int hf_nr_sack_chunk_number_tsns_gap_acked = -1;
+static int hf_nr_sack_chunk_nr_gap_block_start = -1;
+static int hf_nr_sack_chunk_nr_gap_block_end = -1;
+static int hf_nr_sack_chunk_nr_gap_block_start_tsn = -1;
+static int hf_nr_sack_chunk_nr_gap_block_end_tsn = -1;
+static int hf_nr_sack_chunk_number_tsns_nr_gap_acked = -1;
+static int hf_nr_sack_chunk_duplicate_tsn = -1;
+
+static int hf_shutdown_chunk_cumulative_tsn_ack = -1;
+static int hf_cookie = -1;
+static int hf_cwr_chunk_lowest_tsn = -1;
+
+static int hf_ecne_chunk_lowest_tsn = -1;
+static int hf_abort_chunk_t_bit = -1;
+static int hf_shutdown_complete_chunk_t_bit = -1;
+
+static int hf_parameter_type = -1;
+static int hf_parameter_length = -1;
+static int hf_parameter_value = -1;
+static int hf_parameter_padding = -1;
+static int hf_parameter_bit_1 = -1;
+static int hf_parameter_bit_2 = -1;
+static int hf_ipv4_address = -1;
+static int hf_ipv6_address = -1;
+static int hf_heartbeat_info = -1;
+static int hf_state_cookie = -1;
+static int hf_cookie_preservative_increment = -1;
+static int hf_hostname = -1;
+static int hf_supported_address_type = -1;
+static int hf_stream_reset_req_seq_nr = -1;
+static int hf_stream_reset_rsp_seq_nr = -1;
+static int hf_senders_last_assigned_tsn = -1;
+static int hf_senders_next_tsn = -1;
+static int hf_receivers_next_tsn = -1;
+static int hf_stream_reset_rsp_result = -1;
+static int hf_stream_reset_sid = -1;
+static int hf_add_outgoing_streams_number_streams = -1;
+static int hf_add_outgoing_streams_reserved = -1;
+static int hf_add_incoming_streams_number_streams = -1;
+static int hf_add_incoming_streams_reserved = -1;
+
+static int hf_zero_checksum_edmid = -1;
+
+static int hf_random_number = -1;
+static int hf_chunks_to_auth = -1;
+static int hf_hmac_id = -1;
+static int hf_hmac = -1;
+static int hf_shared_key_id = -1;
+static int hf_supported_chunk_type = -1;
+
+static int hf_cause_code = -1;
+static int hf_cause_length = -1;
+static int hf_cause_padding = -1;
+static int hf_cause_info = -1;
+
+static int hf_cause_stream_identifier = -1;
+static int hf_cause_reserved = -1;
+
+static int hf_cause_number_of_missing_parameters = -1;
+static int hf_cause_missing_parameter_type = -1;
+
+static int hf_cause_measure_of_staleness = -1;
+
+static int hf_cause_tsn = -1;
+
+static int hf_forward_tsn_chunk_tsn = -1;
+static int hf_forward_tsn_chunk_sid = -1;
+static int hf_forward_tsn_chunk_ssn = -1;
+
+static int hf_i_forward_tsn_chunk_tsn = -1;
+static int hf_i_forward_tsn_chunk_sid = -1;
+static int hf_i_forward_tsn_chunk_flags = -1;
+static int hf_i_forward_tsn_chunk_res = -1;
+static int hf_i_forward_tsn_chunk_u_bit = -1;
+static int hf_i_forward_tsn_chunk_mid = -1;
+
+static int hf_asconf_ack_seq_nr = -1;
+static int hf_asconf_seq_nr = -1;
+static int hf_correlation_id = -1;
+
+static int hf_adap_indication = -1;
+
+static int hf_pktdrop_chunk_m_bit = -1;
+static int hf_pktdrop_chunk_b_bit = -1;
+static int hf_pktdrop_chunk_t_bit = -1;
+static int hf_pktdrop_chunk_bandwidth = -1;
+static int hf_pktdrop_chunk_queuesize = -1;
+static int hf_pktdrop_chunk_truncated_length = -1;
+static int hf_pktdrop_chunk_reserved = -1;
+static int hf_pktdrop_chunk_data_field = -1;
+
+static int hf_pad_chunk_padding_data = -1;
+
+static int hf_sctp_reassembled_in = -1;
+static int hf_sctp_duplicate = -1;
+static int hf_sctp_fragments = -1;
+static int hf_sctp_fragment = -1;
+
+static int hf_sctp_retransmission = -1;
+static int hf_sctp_retransmitted = -1;
+static int hf_sctp_retransmitted_count = -1;
+static int hf_sctp_data_rtt = -1;
+static int hf_sctp_sack_rtt = -1;
+static int hf_sctp_rto = -1;
+static int hf_sctp_ack_tsn = -1;
+static int hf_sctp_ack_frame = -1;
+static int hf_sctp_acked = -1;
+static int hf_sctp_retransmitted_after_ack = -1;
+
+static int hf_sctp_assoc_index = -1;
+
+static dissector_table_t sctp_port_dissector_table;
+static dissector_table_t sctp_ppi_dissector_table;
+static heur_dissector_list_t sctp_heur_subdissector_list;
+static int sctp_tap = -1;
+static int exported_pdu_tap = -1;
+
+/* Initialize the subtree pointers */
+static gint ett_sctp = -1;
+static gint ett_sctp_chunk = -1;
+static gint ett_sctp_chunk_parameter = -1;
+static gint ett_sctp_chunk_cause = -1;
+static gint ett_sctp_chunk_type = -1;
+static gint ett_sctp_data_chunk_flags = -1;
+static gint ett_sctp_sack_chunk_flags = -1;
+static gint ett_sctp_nr_sack_chunk_flags = -1;
+static gint ett_sctp_abort_chunk_flags = -1;
+static gint ett_sctp_shutdown_complete_chunk_flags = -1;
+static gint ett_sctp_pktdrop_chunk_flags = -1;
+static gint ett_sctp_parameter_type= -1;
+static gint ett_sctp_sack_chunk_gap_block = -1;
+static gint ett_sctp_sack_chunk_gap_block_start = -1;
+static gint ett_sctp_sack_chunk_gap_block_end = -1;
+static gint ett_sctp_nr_sack_chunk_gap_block = -1;
+static gint ett_sctp_nr_sack_chunk_gap_block_start = -1;
+static gint ett_sctp_nr_sack_chunk_gap_block_end = -1;
+static gint ett_sctp_nr_sack_chunk_nr_gap_block = -1;
+static gint ett_sctp_nr_sack_chunk_nr_gap_block_start = -1;
+static gint ett_sctp_nr_sack_chunk_nr_gap_block_end = -1;
+static gint ett_sctp_unrecognized_parameter_parameter = -1;
+static gint ett_sctp_i_forward_tsn_chunk_flags = -1;
+
+static gint ett_sctp_fragments = -1;
+static gint ett_sctp_fragment = -1;
+
+static gint ett_sctp_tsn = -1;
+static gint ett_sctp_ack = -1;
+static gint ett_sctp_acked = -1;
+static gint ett_sctp_tsn_retransmission = -1;
+static gint ett_sctp_tsn_retransmitted_count = -1;
+static gint ett_sctp_tsn_retransmitted = -1;
+
+static expert_field ei_sctp_sack_chunk_adv_rec_window_credit = EI_INIT;
+static expert_field ei_sctp_nr_sack_chunk_number_tsns_gap_acked_100 = EI_INIT;
+static expert_field ei_sctp_parameter_length = EI_INIT;
+static expert_field ei_sctp_bad_sctp_checksum = EI_INIT;
+static expert_field ei_sctp_tsn_retransmitted_more_than_twice = EI_INIT;
+static expert_field ei_sctp_parameter_padding = EI_INIT;
+static expert_field ei_sctp_retransmitted_after_ack = EI_INIT;
+static expert_field ei_sctp_nr_sack_chunk_number_tsns_nr_gap_acked_100 = EI_INIT;
+static expert_field ei_sctp_sack_chunk_gap_block_out_of_order = EI_INIT;
+static expert_field ei_sctp_chunk_length_bad = EI_INIT;
+static expert_field ei_sctp_tsn_retransmitted = EI_INIT;
+static expert_field ei_sctp_sack_chunk_gap_block_malformed = EI_INIT;
+static expert_field ei_sctp_sack_chunk_number_tsns_gap_acked_100 = EI_INIT;
+
+WS_DLL_PUBLIC_DEF const value_string chunk_type_values[] = {
+ { SCTP_DATA_CHUNK_ID, "DATA" },
+ { SCTP_INIT_CHUNK_ID, "INIT" },
+ { SCTP_INIT_ACK_CHUNK_ID, "INIT_ACK" },
+ { SCTP_SACK_CHUNK_ID, "SACK" },
+ { SCTP_HEARTBEAT_CHUNK_ID, "HEARTBEAT" },
+ { SCTP_HEARTBEAT_ACK_CHUNK_ID, "HEARTBEAT_ACK" },
+ { SCTP_ABORT_CHUNK_ID, "ABORT" },
+ { SCTP_SHUTDOWN_CHUNK_ID, "SHUTDOWN" },
+ { SCTP_SHUTDOWN_ACK_CHUNK_ID, "SHUTDOWN_ACK" },
+ { SCTP_ERROR_CHUNK_ID, "ERROR" },
+ { SCTP_COOKIE_ECHO_CHUNK_ID, "COOKIE_ECHO" },
+ { SCTP_COOKIE_ACK_CHUNK_ID, "COOKIE_ACK" },
+ { SCTP_ECNE_CHUNK_ID, "ECNE" },
+ { SCTP_CWR_CHUNK_ID, "CWR" },
+ { SCTP_SHUTDOWN_COMPLETE_CHUNK_ID, "SHUTDOWN_COMPLETE" },
+ { SCTP_AUTH_CHUNK_ID, "AUTH" },
+ { SCTP_NR_SACK_CHUNK_ID, "NR_SACK" },
+ { SCTP_I_DATA_CHUNK_ID, "I_DATA" },
+ { SCTP_ASCONF_ACK_CHUNK_ID, "ASCONF_ACK" },
+ { SCTP_PKTDROP_CHUNK_ID, "PKTDROP" },
+ { SCTP_RE_CONFIG_CHUNK_ID, "RE_CONFIG" },
+ { SCTP_PAD_CHUNK_ID, "PAD" },
+ { SCTP_FORWARD_TSN_CHUNK_ID, "FORWARD_TSN" },
+ { SCTP_ASCONF_CHUNK_ID, "ASCONF" },
+ { SCTP_I_FORWARD_TSN_CHUNK_ID, "I_FORWARD_TSN" },
+ { SCTP_IETF_EXT, "IETF_EXTENSION" },
+ { 0, NULL } };
+
+
+#define CHUNK_TYPE_LENGTH 1
+#define CHUNK_FLAGS_LENGTH 1
+#define CHUNK_LENGTH_LENGTH 2
+#define CHUNK_HEADER_LENGTH (CHUNK_TYPE_LENGTH + \
+ CHUNK_FLAGS_LENGTH + \
+ CHUNK_LENGTH_LENGTH)
+#define CHUNK_HEADER_OFFSET 0
+#define CHUNK_TYPE_OFFSET CHUNK_HEADER_OFFSET
+#define CHUNK_FLAGS_OFFSET (CHUNK_TYPE_OFFSET + CHUNK_TYPE_LENGTH)
+#define CHUNK_LENGTH_OFFSET (CHUNK_FLAGS_OFFSET + CHUNK_FLAGS_LENGTH)
+#define CHUNK_VALUE_OFFSET (CHUNK_LENGTH_OFFSET + CHUNK_LENGTH_LENGTH)
+
+#define PARAMETER_TYPE_LENGTH 2
+#define PARAMETER_LENGTH_LENGTH 2
+#define PARAMETER_HEADER_LENGTH (PARAMETER_TYPE_LENGTH + PARAMETER_LENGTH_LENGTH)
+
+#define PARAMETER_HEADER_OFFSET 0
+#define PARAMETER_TYPE_OFFSET PARAMETER_HEADER_OFFSET
+#define PARAMETER_LENGTH_OFFSET (PARAMETER_TYPE_OFFSET + PARAMETER_TYPE_LENGTH)
+#define PARAMETER_VALUE_OFFSET (PARAMETER_LENGTH_OFFSET + PARAMETER_LENGTH_LENGTH)
+
+#define SOURCE_PORT_LENGTH 2
+#define DESTINATION_PORT_LENGTH 2
+#define VERIFICATION_TAG_LENGTH 4
+#define CHECKSUM_LENGTH 4
+#define COMMON_HEADER_LENGTH (SOURCE_PORT_LENGTH + \
+ DESTINATION_PORT_LENGTH + \
+ VERIFICATION_TAG_LENGTH + \
+ CHECKSUM_LENGTH)
+#define SOURCE_PORT_OFFSET 0
+#define DESTINATION_PORT_OFFSET (SOURCE_PORT_OFFSET + SOURCE_PORT_LENGTH)
+#define VERIFICATION_TAG_OFFSET (DESTINATION_PORT_OFFSET + DESTINATION_PORT_LENGTH)
+#define CHECKSUM_OFFSET (VERIFICATION_TAG_OFFSET + VERIFICATION_TAG_LENGTH)
+
+#define SCTP_CHECKSUM_NONE 0
+#define SCTP_CHECKSUM_ADLER32 1
+#define SCTP_CHECKSUM_CRC32C 2
+#define SCTP_CHECKSUM_AUTOMATIC 3
+
+#define FORWARD_STREAM 0
+#define BACKWARD_STREAM 1
+#define FORWARD_ADD_FORWARD_VTAG 2
+#define BACKWARD_ADD_FORWARD_VTAG 3
+#define BACKWARD_ADD_BACKWARD_VTAG 4
+#define ASSOC_NOT_FOUND 5
+
+/* Default values for preferences */
+static gboolean show_port_numbers = TRUE;
+static gint sctp_checksum = SCTP_CHECKSUM_NONE;
+static gboolean enable_tsn_analysis = TRUE;
+static gboolean enable_association_indexing = FALSE;
+static gboolean enable_ulp_dissection = TRUE;
+static gboolean use_reassembly = TRUE;
+static gboolean show_always_control_chunks = TRUE;
+static gboolean show_relative_tsns = TRUE;
+
+/* Data types and functions for generation/handling of chunk types for chunk statistics */
+
+static const value_string chunk_enabled[] = {
+ {0,"Show"},
+ {1,"Hide"},
+ {0, NULL}
+};
+typedef struct _type_field_t {
+ guint type_id;
+ gchar* type_name;
+ guint type_enable;
+} type_field_t;
+
+static type_field_t* type_fields = NULL;
+static guint num_type_fields = 0;
+
+typedef struct _assoc_info_t {
+ guint16 assoc_index;
+ guint16 direction;
+ gboolean vtag_reflected;
+ guint16 sport;
+ guint16 dport;
+ guint32 verification_tag1;
+ guint32 verification_tag2;
+ guint32 initiate_tag;
+} assoc_info_t;
+
+typedef struct _infodata_t {
+ guint16 assoc_index;
+ guint16 direction;
+} infodata_t;
+
+static wmem_list_t *assoc_info_list = NULL;
+static guint num_assocs = 0;
+
+UAT_CSTRING_CB_DEF(type_fields, type_name, type_field_t)
+UAT_VS_DEF(type_fields, type_enable, type_field_t, guint, 0, "Show")
+UAT_DEC_CB_DEF(type_fields, type_id, type_field_t)
+
+static void *sctp_chunk_type_copy_cb(void* n, const void* o, size_t siz _U_)
+{
+ type_field_t* new_rec = (type_field_t*)n;
+ const type_field_t* old_rec = (const type_field_t*)o;
+ new_rec->type_name = g_strdup(old_rec->type_name);
+
+ return new_rec;
+}
+
+static void
+sctp_chunk_type_free_cb(void* r)
+{
+ type_field_t* rec = (type_field_t*)r;
+ g_free(rec->type_name);
+}
+
+static bool
+sctp_chunk_type_update_cb(void *r, char **err)
+{
+ type_field_t *rec = (type_field_t *)r;
+ char c;
+ if (rec->type_name == NULL) {
+ *err = g_strdup("Header name can't be empty");
+ return FALSE;
+ }
+
+ g_strstrip(rec->type_name);
+ if (rec->type_name[0] == 0) {
+ *err = g_strdup("Header name can't be empty");
+ return FALSE;
+ }
+
+ /* Check for invalid characters (to avoid asserting out when
+ * registering the field).
+ */
+ c = proto_check_field_name(rec->type_name);
+ if (c) {
+ *err = ws_strdup_printf("Header name can't contain '%c'", c);
+ return FALSE;
+ }
+
+ *err = NULL;
+ return TRUE;
+}
+
+static dissector_handle_t sctp_handle;
+
+static struct _sctp_info sctp_info;
+
+#define RETURN_DIRECTION(direction) \
+ do { \
+ /*ws_warning("Returning %d at %d: a-itag=0x%x, a-vtag1=0x%x, a-vtag2=0x%x, b-itag=0x%x, b-vtag1=0x%x, b-vtag2=0x%x", \
+ direction, __LINE__, a->initiate_tag, a->verification_tag1, a->verification_tag2, b->initiate_tag, b->verification_tag1, b->verification_tag2);*/ \
+ return direction; \
+ } while (0)
+static gint sctp_assoc_vtag_cmp(const assoc_info_t *a, const assoc_info_t *b)
+{
+ if (a == NULL || b == NULL)
+ RETURN_DIRECTION(ASSOC_NOT_FOUND);
+
+ if ((a->sport == b->sport) &&
+ (a->dport == b->dport) &&
+ (b->verification_tag2 != 0) &&
+ (a->initiate_tag == b->verification_tag2) &&
+ (a->initiate_tag == b->initiate_tag))
+ RETURN_DIRECTION(FORWARD_STREAM);
+
+ if ((a->sport == b->sport) &&
+ (a->dport == b->dport) &&
+ (a->verification_tag1 == b->verification_tag1) &&
+ (a->initiate_tag == b->initiate_tag))
+ RETURN_DIRECTION(FORWARD_STREAM);
+
+ if ((a->sport == b->sport) &&
+ (a->dport == b->dport) &&
+ (a->verification_tag1 == b->verification_tag1) &&
+ (a->verification_tag1 == 0 && a->initiate_tag != 0) &&
+ (a->initiate_tag != b->initiate_tag ))
+ RETURN_DIRECTION(ASSOC_NOT_FOUND); /* two INITs that belong to different assocs */
+
+ /* assoc known*/
+ if ((a->sport == b->sport) &&
+ (a->dport == b->dport) &&
+ (a->verification_tag1 == b->verification_tag1) &&
+ ((a->verification_tag1 != 0 ||
+ (b->verification_tag2 != 0))))
+ RETURN_DIRECTION(FORWARD_STREAM);
+
+ /* ABORT, vtag reflected */
+ if ((a->sport == b->sport) &&
+ (a->dport == b->dport) &&
+ (a->verification_tag2 == b->verification_tag2) &&
+ (a->verification_tag1 == 0 && b->verification_tag1 != 0))
+ RETURN_DIRECTION(FORWARD_STREAM);
+
+ if ((a->sport == b->dport) &&
+ (a->dport == b->sport) &&
+ (a->verification_tag1 == b->verification_tag2) &&
+ (a->verification_tag1 != 0))
+ RETURN_DIRECTION(BACKWARD_STREAM);
+
+ if ((a->sport == b->dport) &&
+ (a->dport == b->sport) &&
+ (a->verification_tag2 == b->verification_tag1) &&
+ (a->verification_tag2 != 0))
+ RETURN_DIRECTION(BACKWARD_STREAM);
+
+ if ((a->sport == b->dport) &&
+ (a->dport == b->sport) &&
+ (a->verification_tag1 == b->initiate_tag) &&
+ (a->verification_tag2 == 0))
+ RETURN_DIRECTION(BACKWARD_ADD_BACKWARD_VTAG);
+
+ /* ABORT, vtag reflected */
+ if ((a->sport == b->dport) &&
+ (a->dport == b->sport) &&
+ (a->verification_tag2 == b->verification_tag1) &&
+ (a->verification_tag1 == 0 && b->verification_tag2 != 0))
+ RETURN_DIRECTION(BACKWARD_STREAM);
+
+ /*forward stream verification tag can be added*/
+ if ((a->sport == b->sport) &&
+ (a->dport == b->dport) &&
+ (a->verification_tag1 != 0) &&
+ (b->verification_tag1 == 0) &&
+ (b->verification_tag2 !=0))
+ RETURN_DIRECTION(FORWARD_ADD_FORWARD_VTAG);
+
+ if ((a->sport == b->dport) &&
+ (a->dport == b->sport) &&
+ (a->verification_tag1 == b->verification_tag2) &&
+ (b->verification_tag1 == 0))
+ RETURN_DIRECTION(BACKWARD_ADD_FORWARD_VTAG);
+
+ /*backward stream verification tag can be added */
+ if ((a->sport == b->dport) &&
+ (a->dport == b->sport) &&
+ (a->verification_tag1 != 0) &&
+ (b->verification_tag1 != 0) &&
+ (b->verification_tag2 == 0))
+ RETURN_DIRECTION(BACKWARD_ADD_BACKWARD_VTAG);
+
+ RETURN_DIRECTION(ASSOC_NOT_FOUND);
+}
+#undef RETURN_DIRECTION
+
+static infodata_t
+find_assoc_index(assoc_info_t* tmpinfo, gboolean visited)
+{
+ assoc_info_t *info = NULL;
+ wmem_list_frame_t *elem;
+ gboolean cmp = FALSE;
+ infodata_t inf;
+ inf.assoc_index = -1;
+ inf.direction = 1;
+
+ if (assoc_info_list == NULL) {
+ assoc_info_list = wmem_list_new(wmem_file_scope());
+ }
+
+ for (elem = wmem_list_head(assoc_info_list); elem; elem = wmem_list_frame_next(elem))
+ {
+ info = (assoc_info_t*) wmem_list_frame_data(elem);
+
+ if (!visited) {
+ cmp = sctp_assoc_vtag_cmp(tmpinfo, info);
+ if (cmp < ASSOC_NOT_FOUND) {
+ switch (cmp)
+ {
+ case FORWARD_ADD_FORWARD_VTAG:
+ case BACKWARD_ADD_FORWARD_VTAG:
+ info->verification_tag1 = tmpinfo->verification_tag1;
+ break;
+ case BACKWARD_ADD_BACKWARD_VTAG:
+ info->verification_tag2 = tmpinfo->verification_tag1;
+ info->direction = 1;
+ inf.assoc_index = info->assoc_index;
+ inf.direction = 2;
+ return inf;
+ case BACKWARD_STREAM:
+ inf.assoc_index = info->assoc_index;
+ inf.direction = 2;
+ return inf;
+ }
+ if (cmp == FORWARD_STREAM || cmp == FORWARD_ADD_FORWARD_VTAG) {
+ info->direction = 1;
+ } else {
+ info->direction = 2;
+ }
+ inf.assoc_index = info->assoc_index;
+ inf.direction = info->direction;
+ return inf;
+ }
+ } else {
+ if ((tmpinfo->initiate_tag != 0 && tmpinfo->initiate_tag == info->initiate_tag) ||
+ (tmpinfo->verification_tag1 != 0 && tmpinfo->verification_tag1 == info->verification_tag1) ||
+ (tmpinfo->verification_tag2 != 0 && tmpinfo->verification_tag2 == info->verification_tag2)) {
+ inf.assoc_index = info->assoc_index;
+ inf.direction = info->direction;
+ return inf;
+ } else if ((tmpinfo->verification_tag1 != 0 && tmpinfo->verification_tag1 == info->verification_tag2) ||
+ (tmpinfo->verification_tag2 != 0 && tmpinfo->verification_tag2 == info->verification_tag1) ||
+ (tmpinfo->verification_tag1 == 0 && tmpinfo->initiate_tag != 0 &&
+ tmpinfo->initiate_tag == info->verification_tag1)) {
+ inf.assoc_index = info->assoc_index;
+ if (info->direction == 1)
+ inf.direction = 2;
+ else
+ inf.direction = 1;
+ return inf;
+ }
+ }
+ }
+
+ if (!elem && !visited) {
+ info = wmem_new0(wmem_file_scope(), assoc_info_t);
+ info->assoc_index = num_assocs;
+ info->sport = tmpinfo->sport;
+ info->dport = tmpinfo->dport;
+ info->verification_tag1 = tmpinfo->verification_tag1;
+ info->verification_tag2 = tmpinfo->verification_tag2;
+ info->initiate_tag = tmpinfo->initiate_tag;
+ num_assocs++;
+ wmem_list_prepend(assoc_info_list, info);
+ inf.assoc_index = info->assoc_index;
+ inf.direction = 1;
+ }
+
+ return inf;
+}
+
+static void
+sctp_src_prompt(packet_info *pinfo, gchar *result)
+{
+ guint32 port = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, hf_source_port, pinfo->curr_layer_num));
+
+ snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "source (%s%u)", UTF8_RIGHTWARDS_ARROW, port);
+}
+
+static gpointer
+sctp_src_value(packet_info *pinfo)
+{
+ return p_get_proto_data(pinfo->pool, pinfo, hf_source_port, pinfo->curr_layer_num);
+}
+
+static void
+sctp_dst_prompt(packet_info *pinfo, gchar *result)
+{
+ guint32 port = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, hf_destination_port, pinfo->curr_layer_num));
+
+ snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "destination (%s%u)", UTF8_RIGHTWARDS_ARROW, port);
+}
+
+static gpointer
+sctp_dst_value(packet_info *pinfo)
+{
+ return p_get_proto_data(pinfo->pool, pinfo, hf_destination_port, pinfo->curr_layer_num);
+}
+
+static void
+sctp_both_prompt(packet_info *pinfo, gchar *result)
+{
+ guint32 srcport = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, hf_source_port, pinfo->curr_layer_num)),
+ destport = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, hf_destination_port, pinfo->curr_layer_num));
+
+ snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "both (%u%s%u)", srcport, UTF8_LEFT_RIGHT_ARROW, destport);
+}
+
+static void
+sctp_ppi_prompt1(packet_info *pinfo _U_, gchar* result)
+{
+ guint32 ppid = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, proto_sctp, 0));
+
+ if (ppid == LAST_PPID) {
+ snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "PPID (none)");
+ } else {
+ snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "PPID (%d)", ppid);
+ }
+}
+
+static void
+sctp_ppi_prompt2(packet_info *pinfo _U_, gchar* result)
+{
+ guint32 ppid = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, proto_sctp, 1));
+
+ if (ppid == LAST_PPID) {
+ snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "PPID (none)");
+ } else {
+ snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "PPID (%d)", ppid);
+ }
+}
+
+static gpointer
+sctp_ppi_value1(packet_info *pinfo)
+{
+ return p_get_proto_data(pinfo->pool, pinfo, proto_sctp, 0);
+}
+
+static gpointer
+sctp_ppi_value2(packet_info *pinfo)
+{
+ return p_get_proto_data(pinfo->pool, pinfo, proto_sctp, 1);
+}
+
+static const char* sctp_conv_get_filter_type(conv_item_t* conv, conv_filter_type_e filter)
+{
+ if (filter == CONV_FT_SRC_PORT)
+ return "sctp.srcport";
+
+ if (filter == CONV_FT_DST_PORT)
+ return "sctp.dstport";
+
+ if (filter == CONV_FT_ANY_PORT)
+ return "sctp.port";
+
+ if(!conv) {
+ return CONV_FILTER_INVALID;
+ }
+
+ if (filter == CONV_FT_SRC_ADDRESS) {
+ if (conv->src_address.type == AT_IPv4)
+ return "ip.src";
+ if (conv->src_address.type == AT_IPv6)
+ return "ipv6.src";
+ }
+
+ if (filter == CONV_FT_DST_ADDRESS) {
+ if (conv->dst_address.type == AT_IPv4)
+ return "ip.dst";
+ if (conv->dst_address.type == AT_IPv6)
+ return "ipv6.dst";
+ }
+
+ if (filter == CONV_FT_ANY_ADDRESS) {
+ if (conv->src_address.type == AT_IPv4)
+ return "ip.addr";
+ if (conv->src_address.type == AT_IPv6)
+ return "ipv6.addr";
+ }
+
+ return CONV_FILTER_INVALID;
+}
+
+static ct_dissector_info_t sctp_ct_dissector_info = {&sctp_conv_get_filter_type};
+
+static tap_packet_status
+sctp_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip, tap_flags_t flags)
+{
+ conv_hash_t *hash = (conv_hash_t*) pct;
+ hash->flags = flags;
+
+ const struct _sctp_info *sctphdr=(const struct _sctp_info *)vip;
+
+ add_conversation_table_data(hash, &sctphdr->ip_src, &sctphdr->ip_dst,
+ sctphdr->sport, sctphdr->dport, 1, pinfo->fd->pkt_len, &pinfo->rel_ts, &pinfo->abs_ts, &sctp_ct_dissector_info, CONVERSATION_SCTP);
+
+
+ return TAP_PACKET_REDRAW;
+}
+
+static const char* sctp_endpoint_get_filter_type(endpoint_item_t* endpoint, conv_filter_type_e filter)
+{
+ if (filter == CONV_FT_SRC_PORT)
+ return "sctp.srcport";
+
+ if (filter == CONV_FT_DST_PORT)
+ return "sctp.dstport";
+
+ if (filter == CONV_FT_ANY_PORT)
+ return "sctp.port";
+
+ if(!endpoint) {
+ return CONV_FILTER_INVALID;
+ }
+
+ if (filter == CONV_FT_SRC_ADDRESS) {
+ if (endpoint->myaddress.type == AT_IPv4)
+ return "ip.src";
+ if (endpoint->myaddress.type == AT_IPv6)
+ return "ipv6.src";
+ }
+
+ if (filter == CONV_FT_DST_ADDRESS) {
+ if (endpoint->myaddress.type == AT_IPv4)
+ return "ip.dst";
+ if (endpoint->myaddress.type == AT_IPv6)
+ return "ipv6.dst";
+ }
+
+ if (filter == CONV_FT_ANY_ADDRESS) {
+ if (endpoint->myaddress.type == AT_IPv4)
+ return "ip.addr";
+ if (endpoint->myaddress.type == AT_IPv6)
+ return "ipv6.addr";
+ }
+
+ return CONV_FILTER_INVALID;
+}
+
+static et_dissector_info_t sctp_endpoint_dissector_info = {&sctp_endpoint_get_filter_type};
+
+static tap_packet_status
+sctp_endpoint_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip, tap_flags_t flags)
+{
+ conv_hash_t *hash = (conv_hash_t*) pit;
+ hash->flags = flags;
+
+ const struct _sctp_info *sctphdr=(const struct _sctp_info *)vip;
+
+ /* Take two "add" passes per packet, adding for each direction, ensures that all
+ packets are counted properly (even if address is sending to itself)
+ XXX - this could probably be done more efficiently inside endpoint_table */
+ add_endpoint_table_data(hash, &sctphdr->ip_src, sctphdr->sport, TRUE, 1, pinfo->fd->pkt_len, &sctp_endpoint_dissector_info, ENDPOINT_SCTP);
+ add_endpoint_table_data(hash, &sctphdr->ip_dst, sctphdr->dport, FALSE, 1, pinfo->fd->pkt_len, &sctp_endpoint_dissector_info, ENDPOINT_SCTP);
+
+ return TAP_PACKET_REDRAW;
+}
+
+static unsigned int
+sctp_adler32(tvbuff_t *tvb, unsigned int len)
+{
+ const guint8 *buf = tvb_get_ptr(tvb, 0, len);
+ guint32 result = 1;
+
+ result = update_adler32(result, buf, SOURCE_PORT_LENGTH + DESTINATION_PORT_LENGTH + VERIFICATION_TAG_LENGTH);
+ /* handle four 0 bytes as checksum */
+ result = update_adler32(result, "\0\0\0\0", 4);
+ result = update_adler32(result, buf+COMMON_HEADER_LENGTH, len-COMMON_HEADER_LENGTH);
+
+ return result;
+}
+
+static guint32
+sctp_crc32c(tvbuff_t *tvb, unsigned int len)
+{
+ const guint8 *buf = tvb_get_ptr(tvb, 0, len);
+ guint32 crc32,
+ zero = 0;
+ guint32 result;
+
+ /* CRC for header */
+ crc32 = crc32c_calculate_no_swap(buf, SOURCE_PORT_LENGTH + DESTINATION_PORT_LENGTH + VERIFICATION_TAG_LENGTH, CRC32C_PRELOAD);
+
+ /* handle four 0 bytes as checksum */
+ crc32 = crc32c_calculate_no_swap(&zero, 4, crc32);
+
+ /* CRC for the rest of the packet */
+ crc32 = crc32c_calculate_no_swap(&buf[COMMON_HEADER_LENGTH], len-COMMON_HEADER_LENGTH, crc32);
+
+ result = CRC32C_SWAP(crc32);
+
+ return ( ~result );
+}
+
+/*
+ * Routines for dissecting parameters
+ */
+
+typedef struct _sctp_half_assoc_t sctp_half_assoc_t;
+
+static void dissect_parameter(tvbuff_t *, packet_info *, proto_tree *, proto_item *, gboolean, gboolean);
+
+static void dissect_parameters(tvbuff_t *, packet_info *, proto_tree *, proto_item *, gboolean);
+
+static void dissect_error_cause(tvbuff_t *, packet_info *, proto_tree *);
+
+static void dissect_error_causes(tvbuff_t *, packet_info *, proto_tree *);
+
+static gboolean dissect_data_chunk(tvbuff_t*, guint16, packet_info*, proto_tree*, proto_tree*, proto_item*, proto_item*, sctp_half_assoc_t*, gboolean);
+
+static void dissect_sctp_packet(tvbuff_t *, packet_info *, proto_tree *, gboolean);
+
+
+/* TSN ANALYSIS CODE */
+
+struct _sctp_half_assoc_t {
+ guint32 spt;
+ guint32 dpt;
+ guint32 vtag;
+
+ gboolean started;
+
+ guint32 first_tsn; /* start */
+ guint32 cumm_ack; /* rel */
+ wmem_tree_t *tsns; /* sctp_tsn_t* by rel_tsn */
+ wmem_tree_t *tsn_acks; /* sctp_tsn_t* by ctsn_frame */
+
+ struct _sctp_half_assoc_t *peer;
+};
+
+
+typedef struct _retransmit_t {
+ guint32 framenum;
+ nstime_t ts;
+ struct _retransmit_t *next;
+} retransmit_t;
+
+typedef struct _sctp_tsn_t {
+ guint32 tsn;
+ struct {
+ guint32 framenum;
+ nstime_t ts;
+ } first_transmit;
+ struct {
+ guint32 framenum;
+ nstime_t ts;
+ } ack;
+ retransmit_t *retransmit;
+ guint32 retransmit_count;
+ struct _sctp_tsn_t *next;
+} sctp_tsn_t;
+
+
+static wmem_tree_key_t*
+make_address_key(wmem_allocator_t *pool, guint32 spt, guint32 dpt, address *addr)
+{
+ wmem_tree_key_t *k = (wmem_tree_key_t *)wmem_alloc(pool, sizeof(wmem_tree_key_t)*6);
+
+ k[0].length = 1; k[0].key = (guint32*)wmem_memdup(pool, &spt,sizeof(spt));
+ k[1].length = 1; k[1].key = (guint32*)wmem_memdup(pool, &dpt,sizeof(dpt));
+ k[2].length = 1; k[2].key = (guint32*)(void *)&(addr->type);
+ k[3].length = 1; k[3].key = (guint32*)(void *)&(addr->len);
+
+ k[4].length = ((addr->len/4)+1);
+ k[4].key = (guint32*)wmem_alloc0(pool, ((addr->len/4)+1)*4);
+ if (addr->len) memcpy(k[4].key, addr->data, addr->len);
+
+ k[5].length = 0; k[5].key = NULL;
+
+ return k;
+}
+
+static wmem_tree_key_t *
+make_dir_key(wmem_allocator_t *pool, guint32 spt, guint32 dpt, guint32 vtag)
+{
+ wmem_tree_key_t *k = (wmem_tree_key_t *)wmem_alloc(pool, sizeof(wmem_tree_key_t)*4);
+
+ k[0].length = 1; k[0].key = (guint32*)wmem_memdup(pool, &spt,sizeof(spt));
+ k[1].length = 1; k[1].key = (guint32*)wmem_memdup(pool, &dpt,sizeof(dpt));
+ k[2].length = 1; k[2].key = (guint32*)wmem_memdup(pool, &vtag,sizeof(vtag));
+ k[3].length = 0; k[3].key = NULL;
+
+ return k;
+}
+
+
+
+static wmem_tree_t *dirs_by_ptvtag; /* sctp_half_assoc_t* */
+static wmem_tree_t *dirs_by_ptaddr; /* sctp_half_assoc_t**, it may contain a null pointer */
+
+static sctp_half_assoc_t *
+get_half_assoc(packet_info *pinfo, guint32 spt, guint32 dpt, guint32 vtag)
+{
+ sctp_half_assoc_t *ha;
+ sctp_half_assoc_t **hb;
+ wmem_tree_key_t *k;
+
+ if (!enable_tsn_analysis || !vtag || pinfo->flags.in_error_pkt)
+ return NULL;
+
+ /* look for the current half_assoc by spt, dpt and vtag */
+
+ k = make_dir_key(pinfo->pool, spt, dpt, vtag);
+ if (( ha = (sctp_half_assoc_t *)wmem_tree_lookup32_array(dirs_by_ptvtag, k) )) {
+ /* found, if it has been already matched we're done */
+ if (ha->peer) return ha;
+ } else {
+ /* not found, make a new one and add it to the table */
+ ha = wmem_new0(wmem_file_scope(), sctp_half_assoc_t);
+ ha->spt = spt;
+ ha->dpt = dpt;
+ ha->vtag = vtag;
+ ha->tsns = wmem_tree_new(wmem_file_scope());
+ ha->tsn_acks = wmem_tree_new(wmem_file_scope());
+ ha->started = FALSE;
+ ha->first_tsn= 0;
+ ha->cumm_ack= 0;
+
+ /* add this half to the table indexed by ports and vtag */
+ wmem_tree_insert32_array(dirs_by_ptvtag, k, ha);
+ }
+
+ /* at this point we have an unmatched half, look for its other half using the ports and IP address */
+ k = make_address_key(pinfo->pool, dpt, spt, &(pinfo->dst));
+
+ if (( hb = (sctp_half_assoc_t **)wmem_tree_lookup32_array(dirs_by_ptaddr, k) )) {
+ /*the table contains a pointer to a pointer to a half */
+ if (! *hb) {
+ /* if there is no half pointed by this, add the current half to the table */
+ *hb = ha;
+ } else {
+ /* there's a half pointed by this, assume it's our peer and clear the table's pointer */
+ ha->peer = *hb;
+ (*hb)->peer = ha;
+ *hb = NULL;
+ }
+ } else {
+ /* we found no entry in the table: add one (using reversed ports and src addresss) so that it can be matched later */
+ *(hb = (sctp_half_assoc_t **)wmem_alloc(wmem_file_scope(), sizeof(void*))) = ha;
+ k = make_address_key(pinfo->pool, spt, dpt, &(pinfo->src));
+ wmem_tree_insert32_array(dirs_by_ptaddr, k, hb);
+ }
+
+ return ha;
+}
+
+/* Limit the number of retransmissions we track (to limit memory usage--and
+ * tree size--in pathological cases, for example zero window probing forever).
+ */
+#define MAX_RETRANS_TRACKED_PER_TSN 100
+
+static void
+tsn_tree(sctp_tsn_t *t, proto_item *tsn_item, packet_info *pinfo,
+ tvbuff_t *tvb, guint32 framenum)
+{
+ proto_item *pi;
+ proto_tree *pt;
+ proto_tree *tsn_tree_pt = proto_item_add_subtree(tsn_item, ett_sctp_tsn);
+
+ if (t->first_transmit.framenum != framenum) {
+ nstime_t rto;
+
+ pi = proto_tree_add_uint(tsn_tree_pt, hf_sctp_retransmission, tvb, 0, 0, t->first_transmit.framenum);
+ pt = proto_item_add_subtree(pi, ett_sctp_tsn_retransmission);
+ proto_item_set_generated(pi);
+ expert_add_info(pinfo, pi, &ei_sctp_tsn_retransmitted);
+
+ nstime_delta( &rto, &pinfo->abs_ts, &(t->first_transmit.ts) );
+ pi = proto_tree_add_time(pt, hf_sctp_rto, tvb, 0, 0, &rto);
+ proto_item_set_generated(pi);
+
+ /* Detect reneged acks */
+ /* XXX what if the frames aren't sorted by time? */
+ if (t->ack.framenum && t->ack.framenum < framenum)
+ {
+ pi = proto_tree_add_uint_format(pt, hf_sctp_retransmitted_after_ack, tvb, 0, 0, t->ack.framenum,
+ "This TSN was acked (in frame %u) prior to this retransmission (reneged ack?)",
+ t->ack.framenum);
+ proto_item_set_generated(pi);
+ expert_add_info(pinfo, pi, &ei_sctp_retransmitted_after_ack);
+ }
+ } else if (t->retransmit) {
+ retransmit_t **r;
+ nstime_t rto;
+ char ds[64];
+
+ if (t->retransmit_count > MAX_RETRANS_TRACKED_PER_TSN)
+ snprintf(ds, sizeof(ds), " (only %d displayed)", MAX_RETRANS_TRACKED_PER_TSN);
+ else
+ ds[0] = 0;
+
+ pi = proto_tree_add_uint_format(tsn_tree_pt,
+ hf_sctp_retransmitted_count,
+ tvb, 0, 0, t->retransmit_count,
+ "This TSN was retransmitted %u time%s%s",
+ t->retransmit_count,
+ plurality(t->retransmit_count, "", "s"),
+ ds);
+ proto_item_set_generated(pi);
+
+ if (t->retransmit_count > 2)
+ expert_add_info(pinfo, pi, &ei_sctp_tsn_retransmitted_more_than_twice);
+
+ pt = proto_item_add_subtree(pi, ett_sctp_tsn_retransmitted_count);
+
+ r = &t->retransmit;
+ while (*r) {
+ nstime_delta(&rto, &((*r)->ts), &pinfo->abs_ts);
+ pi = proto_tree_add_uint_format(pt,
+ hf_sctp_retransmitted,
+ tvb, 0, 0,
+ (*r)->framenum,
+ "This TSN was retransmitted in frame %u (%s seconds after this frame)",
+ (*r)->framenum,
+ rel_time_to_secs_str(pinfo->pool, &rto));
+ proto_item_set_generated(pi);
+ r = &(*r)->next;
+ }
+ }
+
+ if (t->ack.framenum) {
+ nstime_t rtt;
+
+ pi = proto_tree_add_uint(tsn_tree_pt, hf_sctp_acked, tvb, 0 , 0, t->ack.framenum);
+ proto_item_set_generated(pi);
+ pt = proto_item_add_subtree(pi, ett_sctp_ack);
+
+ nstime_delta( &rtt, &(t->ack.ts), &(t->first_transmit.ts) );
+ pi = proto_tree_add_time(pt, hf_sctp_data_rtt, tvb, 0, 0, &rtt);
+ proto_item_set_generated(pi);
+ }
+}
+
+#define RELTSN(tsn) (((tsn) < h->first_tsn) ? (tsn + (0xffffffff - (h->first_tsn)) + 1) : (tsn - h->first_tsn))
+
+/* Returns TRUE if the tsn is a retransmission (we've seen it before), FALSE
+ * otherwise.
+ */
+static gboolean
+sctp_tsn(packet_info *pinfo, tvbuff_t *tvb, proto_item *tsn_item,
+ sctp_half_assoc_t *h, guint32 tsn)
+{
+ sctp_tsn_t *t;
+ guint32 framenum;
+ guint32 reltsn;
+ gboolean is_retransmission = FALSE;
+
+ /* no half assoc? nothing to do!*/
+ if (!h)
+ return(is_retransmission);
+
+
+ framenum = pinfo->num;
+
+ /* If we're dissecting for a read filter in the GUI [tshark assigns
+ * frame numbers before running the read filter], don't do the TSN
+ * analysis. (We can't anyway because we don't have a valid frame
+ * number...)
+ *
+ * Without this check if you load a capture file in the
+ * GUI while using a read filter, every SCTP TSN is marked as a
+ * retransmission of that in frame 0.
+ */
+ if (framenum == 0)
+ return(is_retransmission);
+
+ /* we have not seen any tsn yet in this half assoc set the ground */
+ if (! h->started) {
+ h->first_tsn = tsn;
+ h->started = TRUE;
+ }
+
+
+ reltsn = RELTSN(tsn);
+
+ /* printf("%.3d REL TSN: %p->%p [%u] %u \n",framenum,h,h->peer,tsn,reltsn); */
+
+ /* look for this tsn in this half's tsn table */
+ if (! (t = (sctp_tsn_t *)wmem_tree_lookup32(h->tsns,reltsn) )) {
+ /* no tsn found, create a new one */
+ t = wmem_new0(wmem_file_scope(), sctp_tsn_t);
+ t->tsn = tsn;
+
+ t->first_transmit.framenum = framenum;
+ t->first_transmit.ts = pinfo->abs_ts;
+
+ wmem_tree_insert32(h->tsns,reltsn,t);
+ }
+
+ is_retransmission = (t->first_transmit.framenum != framenum);
+
+ if ( (! pinfo->fd->visited ) && is_retransmission ) {
+ retransmit_t **r;
+ int i;
+
+ t->retransmit_count++;
+ r = &t->retransmit;
+ i = 0;
+ while (*r && i < MAX_RETRANS_TRACKED_PER_TSN) {
+ r = &(*r)->next;
+ i++;
+ }
+
+ if (i <= MAX_RETRANS_TRACKED_PER_TSN) {
+ *r = wmem_new0(wmem_file_scope(), retransmit_t);
+ (*r)->framenum = framenum;
+ (*r)->ts = pinfo->abs_ts;
+ }
+ }
+
+ tsn_tree(t, tsn_item, pinfo, tvb, framenum);
+
+ return(is_retransmission);
+}
+
+static void
+ack_tree(sctp_tsn_t *t, proto_tree *acks_tree,
+ tvbuff_t *tvb, packet_info *pinfo)
+{
+ proto_item *pi;
+ proto_tree *pt;
+ nstime_t rtt;
+ guint framenum = pinfo->num;
+
+ if ( t->ack.framenum == framenum ) {
+ nstime_delta( &rtt, &(t->ack.ts), &(t->first_transmit.ts) );
+
+ pi = proto_tree_add_uint(acks_tree, hf_sctp_ack_tsn, tvb, 0 , 0, t->tsn);
+ proto_item_set_generated(pi);
+
+ pt = proto_item_add_subtree(pi, ett_sctp_acked);
+
+ pi = proto_tree_add_uint(pt, hf_sctp_ack_frame, tvb, 0 , 0, t->first_transmit.framenum);
+ proto_item_set_generated(pi);
+
+ pi = proto_tree_add_time(pt, hf_sctp_sack_rtt, tvb, 0, 0, &rtt);
+ proto_item_set_generated(pi);
+ }
+}
+
+static void
+sctp_ack(packet_info *pinfo, tvbuff_t *tvb, proto_tree *acks_tree,
+ sctp_half_assoc_t *h, guint32 reltsn)
+{
+ sctp_tsn_t *t;
+ guint32 framenum;
+
+
+ if (!h || !h->peer)
+ return;
+
+ framenum = pinfo->num;
+
+ /* printf("%.6d ACK: %p->%p [%u] \n",framenum,h,h->peer,reltsn); */
+
+ t = (sctp_tsn_t *)wmem_tree_lookup32(h->peer->tsns,reltsn);
+
+ if (t) {
+ if (! t->ack.framenum) {
+ sctp_tsn_t *t2;
+
+ t->ack.framenum = framenum;
+ t->ack.ts = pinfo->abs_ts;
+
+ if (( t2 = (sctp_tsn_t *)wmem_tree_lookup32(h->peer->tsn_acks, framenum) )) {
+ for(;t2->next;t2 = t2->next)
+ ;
+
+ t2->next = t;
+ } else {
+ wmem_tree_insert32(h->peer->tsn_acks, framenum,t);
+ }
+ }
+
+ if ( t->ack.framenum == framenum)
+ ack_tree(t, acks_tree, tvb, pinfo);
+
+ } /* else {
+ proto_tree_add_debug_text(acks_tree, tvb, 0 , 0, "Assoc: %p vs %p ?? %ld",h,h->peer,tsn);
+ } */
+}
+
+#define RELTSNACK(tsn) (((tsn) < h->peer->first_tsn) ? ((tsn) + (0xffffffff - (h->peer->first_tsn)) + 1) : ((tsn) - h->peer->first_tsn))
+static void
+sctp_ack_block(packet_info *pinfo, sctp_half_assoc_t *h, tvbuff_t *tvb,
+ proto_item *acks_tree, const guint32 *tsn_start_ptr,
+ guint32 tsn_end)
+{
+ sctp_tsn_t *t;
+ guint32 framenum;
+ guint32 rel_start;
+ guint32 rel_end;
+
+
+ if ( !h || !h->peer || ! h->peer->started )
+ return;
+
+ framenum = pinfo->num;
+ rel_end = RELTSNACK(tsn_end);
+
+ if (tsn_start_ptr) {
+ rel_start = RELTSNACK(*tsn_start_ptr);
+ /* printf("%.3d BACK: %p->%p [%u-%u]\n",framenum,h,h->peer,rel_start,rel_end); */
+ } else {
+ rel_start = h->peer->cumm_ack;
+ /* printf("%.3d CACK: %p->%p [%u-%u]\n",framenum,h,h->peer,rel_start,rel_end); */
+ }
+
+
+ if ((t = (sctp_tsn_t *)wmem_tree_lookup32(h->peer->tsn_acks, framenum))) {
+ for(;t;t = t->next) {
+ guint32 tsn = t->tsn;
+
+ tsn -= h->peer->first_tsn;
+
+ if (t->ack.framenum == framenum && ( (!tsn_start_ptr) || rel_start <= tsn) && tsn <= rel_end)
+ ack_tree(t, acks_tree, tvb, pinfo);
+ }
+
+ return;
+ }
+
+ if (PINFO_FD_VISITED(pinfo) || rel_end < rel_start || rel_end - rel_start > 0xffff0000 ) return;
+
+ if (! tsn_start_ptr )
+ h->peer->cumm_ack = rel_end + 1;
+
+ if (rel_start <= rel_end && rel_end - rel_start < 5000 ) {
+ guint32 rel_tsn, i;
+ for (i=0; i <= rel_end-rel_start; i++) {
+ rel_tsn = (guint32) (i+rel_start);
+ sctp_ack(pinfo, tvb, acks_tree, h, rel_tsn);
+ }
+ }
+}
+
+/* END TSN ANALYSIS CODE */
+
+
+
+
+#define HEARTBEAT_INFO_PARAMETER_INFO_OFFSET PARAMETER_VALUE_OFFSET
+
+static void
+dissect_heartbeat_info_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree, proto_item *parameter_item)
+{
+ guint16 heartbeat_info_length;
+
+ heartbeat_info_length = tvb_get_ntohs(parameter_tvb, PARAMETER_LENGTH_OFFSET) - PARAMETER_HEADER_LENGTH;
+ if (heartbeat_info_length > 0)
+ proto_tree_add_item(parameter_tree, hf_heartbeat_info, parameter_tvb, HEARTBEAT_INFO_PARAMETER_INFO_OFFSET, heartbeat_info_length, ENC_NA);
+ proto_item_append_text(parameter_item, " (Information: %u byte%s)", heartbeat_info_length, plurality(heartbeat_info_length, "", "s"));
+}
+
+#define IPV4_ADDRESS_LENGTH 4
+#define IPV4_ADDRESS_OFFSET PARAMETER_VALUE_OFFSET
+
+static void
+dissect_ipv4_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree, proto_item *parameter_item, proto_item *additional_item, gboolean dissecting_init_init_ack_chunk)
+{
+ if (parameter_tree) {
+ proto_tree_add_item(parameter_tree, hf_ipv4_address, parameter_tvb, IPV4_ADDRESS_OFFSET, IPV4_ADDRESS_LENGTH, ENC_BIG_ENDIAN);
+ proto_item_append_text(parameter_item, " (Address: %s)", tvb_ip_to_str(wmem_packet_scope(), parameter_tvb, IPV4_ADDRESS_OFFSET));
+ if (additional_item)
+ proto_item_append_text(additional_item, "%s", tvb_ip_to_str(wmem_packet_scope(), parameter_tvb, IPV4_ADDRESS_OFFSET));
+ }
+ if (dissecting_init_init_ack_chunk) {
+ if (sctp_info.number_of_tvbs < MAXIMUM_NUMBER_OF_TVBS)
+ sctp_info.tvb[sctp_info.number_of_tvbs++] = parameter_tvb;
+ else
+ sctp_info.incomplete = TRUE;
+ }
+}
+
+#define IPV6_ADDRESS_LENGTH 16
+#define IPV6_ADDRESS_OFFSET PARAMETER_VALUE_OFFSET
+
+static void
+dissect_ipv6_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree, proto_item *parameter_item, proto_item *additional_item, gboolean dissecting_init_init_ack_chunk)
+{
+ if (parameter_tree) {
+ proto_tree_add_item(parameter_tree, hf_ipv6_address, parameter_tvb, IPV6_ADDRESS_OFFSET, IPV6_ADDRESS_LENGTH, ENC_NA);
+ proto_item_append_text(parameter_item, " (Address: %s)", tvb_ip6_to_str(wmem_packet_scope(), parameter_tvb, IPV6_ADDRESS_OFFSET));
+ if (additional_item)
+ proto_item_append_text(additional_item, "%s", tvb_ip6_to_str(wmem_packet_scope(), parameter_tvb, IPV6_ADDRESS_OFFSET));
+ }
+ if (dissecting_init_init_ack_chunk) {
+ if (sctp_info.number_of_tvbs < MAXIMUM_NUMBER_OF_TVBS)
+ sctp_info.tvb[sctp_info.number_of_tvbs++] = parameter_tvb;
+ else
+ sctp_info.incomplete = TRUE;
+ }
+}
+
+#define STATE_COOKIE_PARAMETER_COOKIE_OFFSET PARAMETER_VALUE_OFFSET
+
+static void
+dissect_state_cookie_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree, proto_item *parameter_item)
+{
+ guint16 state_cookie_length;
+
+ state_cookie_length = tvb_get_ntohs(parameter_tvb, PARAMETER_LENGTH_OFFSET) - PARAMETER_HEADER_LENGTH;
+ if (state_cookie_length > 0)
+ proto_tree_add_item(parameter_tree, hf_state_cookie, parameter_tvb, STATE_COOKIE_PARAMETER_COOKIE_OFFSET, state_cookie_length, ENC_NA);
+ proto_item_append_text(parameter_item, " (Cookie length: %u byte%s)", state_cookie_length, plurality(state_cookie_length, "", "s"));
+}
+
+static void
+dissect_unrecognized_parameters_parameter(tvbuff_t *parameter_tvb, packet_info *pinfo, proto_tree *parameter_tree)
+{
+ /* FIXME: Does it contain one or more parameters? */
+ dissect_parameter(tvb_new_subset_remaining(parameter_tvb, PARAMETER_VALUE_OFFSET), pinfo, parameter_tree, NULL, FALSE, FALSE);
+}
+
+#define COOKIE_PRESERVATIVE_PARAMETER_INCR_LENGTH 4
+#define COOKIE_PRESERVATIVE_PARAMETER_INCR_OFFSET PARAMETER_VALUE_OFFSET
+
+static void
+dissect_cookie_preservative_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree, proto_item *parameter_item)
+{
+ proto_tree_add_item(parameter_tree, hf_cookie_preservative_increment, parameter_tvb, COOKIE_PRESERVATIVE_PARAMETER_INCR_OFFSET, COOKIE_PRESERVATIVE_PARAMETER_INCR_LENGTH, ENC_BIG_ENDIAN);
+ proto_item_append_text(parameter_item, " (Increment :%u msec)", tvb_get_ntohl(parameter_tvb, COOKIE_PRESERVATIVE_PARAMETER_INCR_OFFSET));
+}
+
+#define HOSTNAME_OFFSET PARAMETER_VALUE_OFFSET
+
+static void
+dissect_hostname_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree, proto_item *parameter_item, proto_item *additional_item)
+{
+ gchar *hostname;
+ guint16 hostname_length;
+
+ hostname_length = tvb_get_ntohs(parameter_tvb, PARAMETER_LENGTH_OFFSET) - PARAMETER_HEADER_LENGTH;
+ proto_tree_add_item_ret_display_string(parameter_tree, hf_hostname, parameter_tvb, HOSTNAME_OFFSET, hostname_length, ENC_ASCII, wmem_packet_scope(), &hostname);
+ if (hostname_length > 1) {
+ proto_item_append_text(parameter_item, " (Hostname: %s)", hostname);
+ if (additional_item != NULL) {
+ proto_item_append_text(additional_item, " (Hostname: %s)", hostname);
+ }
+ }
+}
+
+#define IPv4_ADDRESS_TYPE 5
+#define IPv6_ADDRESS_TYPE 6
+#define HOSTNAME_ADDRESS_TYPE 11
+
+static const value_string address_types_values[] = {
+ { IPv4_ADDRESS_TYPE, "IPv4 address" },
+ { IPv6_ADDRESS_TYPE, "IPv6 address" },
+ { HOSTNAME_ADDRESS_TYPE, "Hostname address" },
+ { 0, NULL }
+};
+
+#define SUPPORTED_ADDRESS_TYPE_PARAMETER_ADDRESS_TYPE_LENGTH 2
+
+static void
+dissect_supported_address_types_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree, proto_item *parameter_item)
+{
+ guint16 addr_type, number_of_addr_types, addr_type_number;
+ guint offset;
+
+ number_of_addr_types = (tvb_get_ntohs(parameter_tvb, PARAMETER_LENGTH_OFFSET) - PARAMETER_HEADER_LENGTH)
+ / SUPPORTED_ADDRESS_TYPE_PARAMETER_ADDRESS_TYPE_LENGTH;
+
+ offset = PARAMETER_VALUE_OFFSET;
+ proto_item_append_text(parameter_item, " (Supported types: ");
+ for(addr_type_number = 0; addr_type_number < number_of_addr_types; addr_type_number++) {
+ proto_tree_add_item(parameter_tree, hf_supported_address_type, parameter_tvb, offset, SUPPORTED_ADDRESS_TYPE_PARAMETER_ADDRESS_TYPE_LENGTH, ENC_BIG_ENDIAN);
+ addr_type = tvb_get_ntohs(parameter_tvb, offset);
+ switch (addr_type) {
+ case IPv4_ADDRESS_TYPE:
+ proto_item_append_text(parameter_item, "IPv4");
+ break;
+ case IPv6_ADDRESS_TYPE:
+ proto_item_append_text(parameter_item, "IPv6");
+ break;
+ case HOSTNAME_ADDRESS_TYPE:
+ proto_item_append_text(parameter_item, "hostname");
+ break;
+ default:
+ proto_item_append_text(parameter_item, "%u", addr_type);
+ }
+ if (addr_type_number < (number_of_addr_types-1))
+ proto_item_append_text(parameter_item, ", ");
+ offset += SUPPORTED_ADDRESS_TYPE_PARAMETER_ADDRESS_TYPE_LENGTH;
+ }
+ proto_item_append_text(parameter_item, ")");
+}
+
+#define STREAM_RESET_SEQ_NR_LENGTH 4
+#define SENDERS_LAST_ASSIGNED_TSN_LENGTH 4
+#define SID_LENGTH 2
+
+#define STREAM_RESET_REQ_SEQ_NR_OFFSET PARAMETER_VALUE_OFFSET
+#define STREAM_RESET_REQ_RSP_SEQ_NR_OFFSET (PARAMETER_VALUE_OFFSET + STREAM_RESET_SEQ_NR_LENGTH)
+#define SENDERS_LAST_ASSIGNED_TSN_OFFSET (STREAM_RESET_REQ_RSP_SEQ_NR_OFFSET + STREAM_RESET_SEQ_NR_LENGTH)
+
+static void
+dissect_outgoing_ssn_reset_request_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree, proto_item *parameter_item _U_)
+{
+ guint length, number_of_sids, sid_number, sid_offset;
+
+ proto_tree_add_item(parameter_tree, hf_stream_reset_req_seq_nr, parameter_tvb, STREAM_RESET_REQ_SEQ_NR_OFFSET, STREAM_RESET_SEQ_NR_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(parameter_tree, hf_stream_reset_rsp_seq_nr, parameter_tvb, STREAM_RESET_REQ_RSP_SEQ_NR_OFFSET, STREAM_RESET_SEQ_NR_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(parameter_tree, hf_senders_last_assigned_tsn, parameter_tvb, SENDERS_LAST_ASSIGNED_TSN_OFFSET, SENDERS_LAST_ASSIGNED_TSN_LENGTH, ENC_BIG_ENDIAN);
+
+ length = tvb_get_ntohs(parameter_tvb, PARAMETER_LENGTH_OFFSET);
+ sid_offset = SENDERS_LAST_ASSIGNED_TSN_OFFSET + SENDERS_LAST_ASSIGNED_TSN_LENGTH;
+ if (length > PARAMETER_HEADER_LENGTH + STREAM_RESET_SEQ_NR_LENGTH + STREAM_RESET_SEQ_NR_LENGTH + SENDERS_LAST_ASSIGNED_TSN_LENGTH) {
+ number_of_sids = (length - (PARAMETER_HEADER_LENGTH + STREAM_RESET_SEQ_NR_LENGTH + STREAM_RESET_SEQ_NR_LENGTH + SENDERS_LAST_ASSIGNED_TSN_LENGTH)) / SID_LENGTH;
+ for(sid_number = 0; sid_number < number_of_sids; sid_number++) {
+ proto_tree_add_item(parameter_tree, hf_stream_reset_sid, parameter_tvb, sid_offset, SID_LENGTH, ENC_BIG_ENDIAN);
+ sid_offset += SID_LENGTH;
+ }
+ }
+}
+
+static void
+dissect_incoming_ssn_reset_request_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree, proto_item *parameter_item _U_)
+{
+ guint length, number_of_sids, sid_number, sid_offset;
+
+ proto_tree_add_item(parameter_tree, hf_stream_reset_req_seq_nr, parameter_tvb, STREAM_RESET_REQ_SEQ_NR_OFFSET, STREAM_RESET_SEQ_NR_LENGTH, ENC_BIG_ENDIAN);
+
+ length = tvb_get_ntohs(parameter_tvb, PARAMETER_LENGTH_OFFSET);
+ sid_offset = STREAM_RESET_REQ_SEQ_NR_OFFSET + STREAM_RESET_SEQ_NR_LENGTH;
+ if (length > PARAMETER_HEADER_LENGTH + STREAM_RESET_SEQ_NR_LENGTH) {
+ number_of_sids = (length - (PARAMETER_HEADER_LENGTH + STREAM_RESET_SEQ_NR_LENGTH)) / SID_LENGTH;
+ for(sid_number = 0; sid_number < number_of_sids; sid_number++) {
+ proto_tree_add_item(parameter_tree, hf_stream_reset_sid, parameter_tvb, sid_offset, SID_LENGTH, ENC_BIG_ENDIAN);
+ sid_offset += SID_LENGTH;
+ }
+ }
+}
+
+#define STREAM_RESET_REQ_SEQ_NR_OFFSET PARAMETER_VALUE_OFFSET
+
+static void
+dissect_ssn_tsn_reset_request_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree, proto_item *parameter_item _U_)
+{
+ proto_tree_add_item(parameter_tree, hf_stream_reset_req_seq_nr, parameter_tvb, STREAM_RESET_REQ_SEQ_NR_OFFSET, STREAM_RESET_SEQ_NR_LENGTH, ENC_BIG_ENDIAN);
+}
+
+#define STREAM_RESET_RSP_RESULT_LENGTH 4
+#define SENDERS_NEXT_TSN_LENGTH 4
+#define RECEIVERS_NEXT_TSN_LENGTH 4
+
+#define STREAM_RESET_RSP_RSP_SEQ_NR_OFFSET PARAMETER_VALUE_OFFSET
+#define STREAM_RESET_RSP_RESULT_OFFSET (STREAM_RESET_RSP_RSP_SEQ_NR_OFFSET + STREAM_RESET_SEQ_NR_LENGTH)
+#define SENDERS_NEXT_TSN_OFFSET (STREAM_RESET_RSP_RESULT_OFFSET + STREAM_RESET_RSP_RESULT_LENGTH)
+#define RECEIVERS_NEXT_TSN_OFFSET (SENDERS_NEXT_TSN_OFFSET + SENDERS_NEXT_TSN_LENGTH)
+
+static const value_string stream_reset_result_values[] = {
+ { 0, "Nothing to do" },
+ { 1, "Performed" },
+ { 2, "Denied" },
+ { 3, "Error - Wrong SSN" },
+ { 4, "Error - Request already in progress" },
+ { 5, "Error - Bad sequence number" },
+ { 6, "In progress" },
+ { 0, NULL }
+};
+
+
+static void
+dissect_re_configuration_response_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree, proto_item *parameter_item _U_)
+{
+ guint length;
+
+ length = tvb_get_ntohs(parameter_tvb, PARAMETER_LENGTH_OFFSET);
+
+ proto_tree_add_item(parameter_tree, hf_stream_reset_rsp_seq_nr, parameter_tvb, STREAM_RESET_RSP_RSP_SEQ_NR_OFFSET, STREAM_RESET_SEQ_NR_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(parameter_tree, hf_stream_reset_rsp_result, parameter_tvb, STREAM_RESET_RSP_RESULT_OFFSET, STREAM_RESET_RSP_RESULT_LENGTH, ENC_BIG_ENDIAN);
+ if (length >= PARAMETER_HEADER_LENGTH + STREAM_RESET_SEQ_NR_LENGTH + STREAM_RESET_RSP_RESULT_LENGTH + SENDERS_NEXT_TSN_LENGTH)
+ proto_tree_add_item(parameter_tree, hf_senders_next_tsn, parameter_tvb, SENDERS_NEXT_TSN_OFFSET, SENDERS_NEXT_TSN_LENGTH, ENC_BIG_ENDIAN);
+ if (length >= PARAMETER_HEADER_LENGTH + STREAM_RESET_SEQ_NR_LENGTH + STREAM_RESET_RSP_RESULT_LENGTH + SENDERS_NEXT_TSN_LENGTH + RECEIVERS_NEXT_TSN_LENGTH)
+ proto_tree_add_item(parameter_tree, hf_receivers_next_tsn, parameter_tvb, RECEIVERS_NEXT_TSN_OFFSET, RECEIVERS_NEXT_TSN_LENGTH, ENC_BIG_ENDIAN);
+}
+
+#define ADD_OUTGOING_STREAM_REQ_SEQ_NR_LENGTH 4
+#define ADD_OUTGOING_STREAM_REQ_NUM_STREAMS_LENGTH 2
+#define ADD_OUTGOING_STREAM_REQ_RESERVED_LENGTH 2
+
+#define ADD_OUTGOING_STREAM_REQ_SEQ_NR_OFFSET PARAMETER_VALUE_OFFSET
+#define ADD_OUTGOING_STREAM_REQ_NUM_STREAMS_OFFSET (ADD_OUTGOING_STREAM_REQ_SEQ_NR_OFFSET + ADD_OUTGOING_STREAM_REQ_SEQ_NR_LENGTH)
+#define ADD_OUTGOING_STREAM_REQ_RESERVED_OFFSET (ADD_OUTGOING_STREAM_REQ_NUM_STREAMS_OFFSET + ADD_OUTGOING_STREAM_REQ_NUM_STREAMS_LENGTH)
+
+static void
+dissect_add_outgoing_streams_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree, proto_item *parameter_item _U_)
+{
+ /* guint length; */
+
+ /* length = tvb_get_ntohs(parameter_tvb, PARAMETER_LENGTH_OFFSET); */
+
+ proto_tree_add_item(parameter_tree, hf_stream_reset_req_seq_nr, parameter_tvb, ADD_OUTGOING_STREAM_REQ_SEQ_NR_OFFSET, ADD_OUTGOING_STREAM_REQ_SEQ_NR_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(parameter_tree, hf_add_outgoing_streams_number_streams, parameter_tvb, ADD_OUTGOING_STREAM_REQ_NUM_STREAMS_OFFSET, ADD_OUTGOING_STREAM_REQ_NUM_STREAMS_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(parameter_tree, hf_add_outgoing_streams_reserved, parameter_tvb, ADD_OUTGOING_STREAM_REQ_RESERVED_OFFSET, ADD_OUTGOING_STREAM_REQ_RESERVED_LENGTH, ENC_BIG_ENDIAN);
+}
+
+#define ADD_INCOMING_STREAM_REQ_SEQ_NR_LENGTH 4
+#define ADD_INCOMING_STREAM_REQ_NUM_STREAMS_LENGTH 2
+#define ADD_INCOMING_STREAM_REQ_RESERVED_LENGTH 2
+
+#define ADD_INCOMING_STREAM_REQ_SEQ_NR_OFFSET PARAMETER_VALUE_OFFSET
+#define ADD_INCOMING_STREAM_REQ_NUM_STREAMS_OFFSET (ADD_INCOMING_STREAM_REQ_SEQ_NR_OFFSET + ADD_OUTGOING_STREAM_REQ_SEQ_NR_LENGTH)
+#define ADD_INCOMING_STREAM_REQ_RESERVED_OFFSET (ADD_INCOMING_STREAM_REQ_NUM_STREAMS_OFFSET + ADD_OUTGOING_STREAM_REQ_NUM_STREAMS_LENGTH)
+
+static void
+dissect_add_incoming_streams_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree, proto_item *parameter_item _U_)
+{
+ /* guint length; */
+
+ /* length = tvb_get_ntohs(parameter_tvb, PARAMETER_LENGTH_OFFSET); */
+
+ proto_tree_add_item(parameter_tree, hf_stream_reset_req_seq_nr, parameter_tvb, ADD_INCOMING_STREAM_REQ_SEQ_NR_OFFSET, ADD_OUTGOING_STREAM_REQ_SEQ_NR_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(parameter_tree, hf_add_incoming_streams_number_streams, parameter_tvb, ADD_INCOMING_STREAM_REQ_NUM_STREAMS_OFFSET, ADD_INCOMING_STREAM_REQ_NUM_STREAMS_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(parameter_tree, hf_add_incoming_streams_reserved, parameter_tvb, ADD_INCOMING_STREAM_REQ_RESERVED_OFFSET, ADD_INCOMING_STREAM_REQ_RESERVED_LENGTH, ENC_BIG_ENDIAN);
+}
+
+static void
+dissect_ecn_parameter(tvbuff_t *parameter_tvb _U_)
+{
+}
+
+#define ZERO_CHECKSUM_PARAMETER_EDMID_LENGTH 4
+#define ZERO_CHECKSUM_PARAMETER_EDMID_OFFSET PARAMETER_VALUE_OFFSET
+
+static const value_string edmid_values[] = {
+ { 1, "SCTP over DTLS" },
+ { 0, NULL }
+};
+
+static void
+dissect_zero_checksum_acceptable_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree, proto_item *parameter_item)
+{
+ proto_tree_add_item(parameter_tree, hf_zero_checksum_edmid, parameter_tvb, ZERO_CHECKSUM_PARAMETER_EDMID_OFFSET, ZERO_CHECKSUM_PARAMETER_EDMID_LENGTH, ENC_BIG_ENDIAN);
+ proto_item_append_text(parameter_item, " (EDMID: %s)", val_to_str_const(tvb_get_ntohl(parameter_tvb, ZERO_CHECKSUM_PARAMETER_EDMID_OFFSET), edmid_values, "Unknown"));
+}
+
+#define RANDOM_NUMBER_OFFSET PARAMETER_VALUE_OFFSET
+
+static void
+dissect_random_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree)
+{
+ gint32 number_length;
+
+ number_length = tvb_get_ntohs(parameter_tvb, PARAMETER_LENGTH_OFFSET) - PARAMETER_HEADER_LENGTH;
+ if (number_length > 0)
+ proto_tree_add_item(parameter_tree, hf_random_number, parameter_tvb, RANDOM_NUMBER_OFFSET, number_length, ENC_NA);
+}
+
+static void
+dissect_chunks_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree, proto_item *parameter_item)
+{
+ guint16 number_of_chunks;
+ guint16 chunk_number, offset;
+
+ proto_item_append_text(parameter_item, " (Chunk types to be authenticated: ");
+ number_of_chunks = tvb_get_ntohs(parameter_tvb, PARAMETER_LENGTH_OFFSET) - PARAMETER_HEADER_LENGTH;
+ for(chunk_number = 0, offset = PARAMETER_VALUE_OFFSET; chunk_number < number_of_chunks; chunk_number++, offset += CHUNK_TYPE_LENGTH) {
+ proto_tree_add_item(parameter_tree, hf_chunks_to_auth, parameter_tvb, offset, CHUNK_TYPE_LENGTH, ENC_BIG_ENDIAN);
+ proto_item_append_text(parameter_item, "%s", val_to_str_const(tvb_get_guint8(parameter_tvb, offset), chunk_type_values, "Unknown"));
+ if (chunk_number < (number_of_chunks - 1))
+ proto_item_append_text(parameter_item, ", ");
+ }
+ proto_item_append_text(parameter_item, ")");
+}
+
+static const value_string hmac_id_values[] = {
+ { 0x0000, "Reserved" },
+ { 0x0001, "SHA-1" },
+ { 0x0002, "Reserved" },
+ { 0x0003, "SHA-256" },
+ { 0, NULL } };
+
+#define HMAC_ID_LENGTH 2
+
+static void
+dissect_hmac_algo_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree, proto_item *parameter_item)
+{
+ guint16 number_of_ids;
+ guint16 id_number, offset;
+
+ proto_item_append_text(parameter_item, " (Supported HMACs: ");
+ number_of_ids = (tvb_get_ntohs(parameter_tvb, PARAMETER_LENGTH_OFFSET) - PARAMETER_HEADER_LENGTH) / HMAC_ID_LENGTH;
+ for(id_number = 0, offset = PARAMETER_VALUE_OFFSET; id_number < number_of_ids; id_number++, offset += HMAC_ID_LENGTH) {
+ proto_tree_add_item(parameter_tree, hf_hmac_id, parameter_tvb, offset, HMAC_ID_LENGTH, ENC_BIG_ENDIAN);
+ proto_item_append_text(parameter_item, "%s", val_to_str_const(tvb_get_ntohs(parameter_tvb, offset), hmac_id_values, "Unknown"));
+ if (id_number < (number_of_ids - 1))
+ proto_item_append_text(parameter_item, ", ");
+ }
+ proto_item_append_text(parameter_item, ")");
+}
+
+static void
+dissect_supported_extensions_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree, proto_item *parameter_item)
+{
+ guint16 number_of_types;
+ guint16 type_number, offset;
+
+ proto_item_append_text(parameter_item, " (Supported types: ");
+ number_of_types = (tvb_get_ntohs(parameter_tvb, PARAMETER_LENGTH_OFFSET) - PARAMETER_HEADER_LENGTH) / CHUNK_TYPE_LENGTH;
+ for(type_number = 0, offset = PARAMETER_VALUE_OFFSET; type_number < number_of_types; type_number++, offset += CHUNK_TYPE_LENGTH) {
+ proto_tree_add_item(parameter_tree, hf_supported_chunk_type, parameter_tvb, offset, CHUNK_TYPE_LENGTH, ENC_BIG_ENDIAN);
+ proto_item_append_text(parameter_item, "%s", val_to_str_const(tvb_get_guint8(parameter_tvb, offset), chunk_type_values, "Unknown"));
+ if (type_number < (number_of_types - 1))
+ proto_item_append_text(parameter_item, ", ");
+ }
+ proto_item_append_text(parameter_item, ")");
+}
+
+static void
+dissect_forward_tsn_supported_parameter(tvbuff_t *parameter_tvb _U_)
+{
+}
+
+#define CORRELATION_ID_LENGTH 4
+#define CORRELATION_ID_OFFSET PARAMETER_VALUE_OFFSET
+#define ADDRESS_PARAMETER_OFFSET (CORRELATION_ID_OFFSET + CORRELATION_ID_LENGTH)
+
+static void
+dissect_add_ip_address_parameter(tvbuff_t *parameter_tvb, packet_info *pinfo, proto_tree *parameter_tree, proto_item *parameter_item)
+{
+ guint16 address_length;
+ tvbuff_t *address_tvb;
+
+ address_length = tvb_get_ntohs(parameter_tvb, PARAMETER_LENGTH_OFFSET) - PARAMETER_HEADER_LENGTH - CORRELATION_ID_LENGTH;
+
+ proto_tree_add_item(parameter_tree, hf_correlation_id, parameter_tvb, CORRELATION_ID_OFFSET, CORRELATION_ID_LENGTH, ENC_BIG_ENDIAN);
+ address_tvb = tvb_new_subset_length_caplen(parameter_tvb, ADDRESS_PARAMETER_OFFSET,
+ MIN(address_length, tvb_captured_length_remaining(parameter_tvb, ADDRESS_PARAMETER_OFFSET)),
+ MIN(address_length, tvb_reported_length_remaining(parameter_tvb, ADDRESS_PARAMETER_OFFSET)));
+ proto_item_append_text(parameter_item, " (Address: ");
+ dissect_parameter(address_tvb, pinfo, parameter_tree, parameter_item, FALSE, FALSE);
+ proto_item_append_text(parameter_item, ", correlation ID: %u)", tvb_get_ntohl(parameter_tvb, CORRELATION_ID_OFFSET));
+}
+
+static void
+dissect_del_ip_address_parameter(tvbuff_t *parameter_tvb, packet_info *pinfo, proto_tree *parameter_tree, proto_item *parameter_item)
+{
+ guint16 address_length;
+ tvbuff_t *address_tvb;
+
+ address_length = tvb_get_ntohs(parameter_tvb, PARAMETER_LENGTH_OFFSET) - PARAMETER_HEADER_LENGTH - CORRELATION_ID_LENGTH;
+
+ proto_tree_add_item(parameter_tree, hf_correlation_id, parameter_tvb, CORRELATION_ID_OFFSET, CORRELATION_ID_LENGTH, ENC_BIG_ENDIAN);
+ address_tvb = tvb_new_subset_length_caplen(parameter_tvb, ADDRESS_PARAMETER_OFFSET,
+ MIN(address_length, tvb_captured_length_remaining(parameter_tvb, ADDRESS_PARAMETER_OFFSET)),
+ MIN(address_length, tvb_reported_length_remaining(parameter_tvb, ADDRESS_PARAMETER_OFFSET)));
+ proto_item_append_text(parameter_item, " (Address: ");
+ dissect_parameter(address_tvb, pinfo, parameter_tree, parameter_item, FALSE, FALSE);
+ proto_item_append_text(parameter_item, ", correlation ID: %u)", tvb_get_ntohl(parameter_tvb, CORRELATION_ID_OFFSET));
+}
+
+#define ERROR_CAUSE_IND_CASUES_OFFSET (CORRELATION_ID_OFFSET + CORRELATION_ID_LENGTH)
+
+static void
+dissect_error_cause_indication_parameter(tvbuff_t *parameter_tvb, packet_info *pinfo, proto_tree *parameter_tree)
+{
+ guint16 causes_length;
+ tvbuff_t *causes_tvb;
+
+ proto_tree_add_item(parameter_tree, hf_correlation_id, parameter_tvb, CORRELATION_ID_OFFSET, CORRELATION_ID_LENGTH, ENC_BIG_ENDIAN);
+ causes_length = tvb_get_ntohs(parameter_tvb, PARAMETER_LENGTH_OFFSET) - PARAMETER_HEADER_LENGTH - CORRELATION_ID_LENGTH;
+ causes_tvb = tvb_new_subset_length_caplen(parameter_tvb, ERROR_CAUSE_IND_CASUES_OFFSET,
+ MIN(causes_length, tvb_captured_length_remaining(parameter_tvb, ERROR_CAUSE_IND_CASUES_OFFSET)),
+ MIN(causes_length, tvb_reported_length_remaining(parameter_tvb, ERROR_CAUSE_IND_CASUES_OFFSET)));
+ dissect_error_causes(causes_tvb, pinfo, parameter_tree);
+}
+
+static void
+dissect_set_primary_address_parameter(tvbuff_t *parameter_tvb, packet_info *pinfo, proto_tree *parameter_tree, proto_item *parameter_item)
+{
+ guint16 address_length;
+ tvbuff_t *address_tvb;
+
+ address_length = tvb_get_ntohs(parameter_tvb, PARAMETER_LENGTH_OFFSET) - PARAMETER_HEADER_LENGTH - CORRELATION_ID_LENGTH;
+
+ proto_tree_add_item(parameter_tree, hf_correlation_id, parameter_tvb, CORRELATION_ID_OFFSET, CORRELATION_ID_LENGTH, ENC_BIG_ENDIAN);
+ address_tvb = tvb_new_subset_length_caplen(parameter_tvb, ADDRESS_PARAMETER_OFFSET,
+ MIN(address_length, tvb_captured_length_remaining(parameter_tvb, ADDRESS_PARAMETER_OFFSET)),
+ MIN(address_length, tvb_reported_length_remaining(parameter_tvb, ADDRESS_PARAMETER_OFFSET)));
+ proto_item_append_text(parameter_item, " (Address: ");
+ dissect_parameter(address_tvb, pinfo, parameter_tree, parameter_item, FALSE, FALSE);
+ proto_item_append_text(parameter_item, ", correlation ID: %u)", tvb_get_ntohl(parameter_tvb, CORRELATION_ID_OFFSET));
+}
+
+static void
+dissect_success_report_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree, proto_item *parameter_item)
+{
+ proto_tree_add_item(parameter_tree, hf_correlation_id, parameter_tvb, CORRELATION_ID_OFFSET, CORRELATION_ID_LENGTH, ENC_BIG_ENDIAN);
+ proto_item_append_text(parameter_item, " (Correlation ID: %u)", tvb_get_ntohl(parameter_tvb, CORRELATION_ID_OFFSET));
+}
+
+#define ADAP_INDICATION_LENGTH 4
+#define ADAP_INDICATION_OFFSET PARAMETER_VALUE_OFFSET
+
+static void
+dissect_adap_indication_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree, proto_item *parameter_item)
+{
+ proto_tree_add_item(parameter_tree, hf_adap_indication, parameter_tvb, ADAP_INDICATION_OFFSET, ADAP_INDICATION_LENGTH, ENC_BIG_ENDIAN);
+ proto_item_append_text(parameter_item, " (Indication: %u)", tvb_get_ntohl(parameter_tvb, ADAP_INDICATION_OFFSET));
+}
+
+static void
+dissect_unknown_parameter(tvbuff_t *parameter_tvb, proto_tree *parameter_tree, proto_item *parameter_item)
+{
+ guint16 type, parameter_value_length;
+
+ type = tvb_get_ntohs(parameter_tvb, PARAMETER_TYPE_OFFSET);
+ parameter_value_length = tvb_get_ntohs(parameter_tvb, PARAMETER_LENGTH_OFFSET) - PARAMETER_HEADER_LENGTH;
+
+ if (parameter_value_length > 0)
+ proto_tree_add_item(parameter_tree, hf_parameter_value, parameter_tvb, PARAMETER_VALUE_OFFSET, parameter_value_length, ENC_NA);
+
+ proto_item_append_text(parameter_item, " (Type %u, value length: %u byte%s)", type, parameter_value_length, plurality(parameter_value_length, "", "s"));
+}
+
+#define HEARTBEAT_INFO_PARAMETER_ID 0x0001
+#define IPV4ADDRESS_PARAMETER_ID 0x0005
+#define IPV6ADDRESS_PARAMETER_ID 0x0006
+#define STATE_COOKIE_PARAMETER_ID 0x0007
+#define UNREC_PARA_PARAMETER_ID 0x0008
+#define COOKIE_PRESERVATIVE_PARAMETER_ID 0x0009
+#define HOSTNAME_ADDRESS_PARAMETER_ID 0x000b
+#define SUPPORTED_ADDRESS_TYPES_PARAMETER_ID 0x000c
+#define OUTGOING_SSN_RESET_REQUEST_PARAMETER_ID 0x000d
+#define INCOMING_SSN_RESET_REQUEST_PARAMETER_ID 0x000e
+#define SSN_TSN_RESET_REQUEST_PARAMETER_ID 0x000f
+#define RE_CONFIGURATION_RESPONSE_PARAMETER_ID 0x0010
+#define ADD_OUTGOING_STREAMS_REQUEST_PARAMETER_ID 0x0011
+#define ADD_INCOMING_STREAMS_REQUEST_PARAMETER_ID 0x0012
+#define ECN_PARAMETER_ID 0x8000
+#define ZERO_CHECKSUM_ACCEPTABLE_PARAMETER_ID 0x8001
+#define RANDOM_PARAMETER_ID 0x8002
+#define CHUNKS_PARAMETER_ID 0x8003
+#define HMAC_ALGO_PARAMETER_ID 0x8004
+#define SUPPORTED_EXTENSIONS_PARAMETER_ID 0x8008
+#define FORWARD_TSN_SUPPORTED_PARAMETER_ID 0xC000
+#define ADD_IP_ADDRESS_PARAMETER_ID 0xC001
+#define DEL_IP_ADDRESS_PARAMETER_ID 0xC002
+#define ERROR_CAUSE_INDICATION_PARAMETER_ID 0xC003
+#define SET_PRIMARY_ADDRESS_PARAMETER_ID 0xC004
+#define SUCCESS_REPORT_PARAMETER_ID 0xC005
+#define ADAP_LAYER_INDICATION_PARAMETER_ID 0xC006
+
+static const value_string parameter_identifier_values[] = {
+ { HEARTBEAT_INFO_PARAMETER_ID, "Heartbeat info" },
+ { IPV4ADDRESS_PARAMETER_ID, "IPv4 address" },
+ { IPV6ADDRESS_PARAMETER_ID, "IPv6 address" },
+ { STATE_COOKIE_PARAMETER_ID, "State cookie" },
+ { UNREC_PARA_PARAMETER_ID, "Unrecognized parameter" },
+ { COOKIE_PRESERVATIVE_PARAMETER_ID, "Cookie preservative" },
+ { HOSTNAME_ADDRESS_PARAMETER_ID, "Hostname address" },
+ { OUTGOING_SSN_RESET_REQUEST_PARAMETER_ID, "Outgoing SSN reset request" },
+ { INCOMING_SSN_RESET_REQUEST_PARAMETER_ID, "Incoming SSN reset request" },
+ { SSN_TSN_RESET_REQUEST_PARAMETER_ID, "SSN/TSN reset request" },
+ { RE_CONFIGURATION_RESPONSE_PARAMETER_ID, "Re-configuration response" },
+ { ADD_OUTGOING_STREAMS_REQUEST_PARAMETER_ID, "Add outgoing streams request" },
+ { ADD_INCOMING_STREAMS_REQUEST_PARAMETER_ID, "Add incoming streams request" },
+ { SUPPORTED_ADDRESS_TYPES_PARAMETER_ID, "Supported address types" },
+ { ECN_PARAMETER_ID, "ECN" },
+ { ZERO_CHECKSUM_ACCEPTABLE_PARAMETER_ID, "Zero checksum acceptable" },
+ { RANDOM_PARAMETER_ID, "Random" },
+ { CHUNKS_PARAMETER_ID, "Authenticated Chunk list" },
+ { HMAC_ALGO_PARAMETER_ID, "Requested HMAC Algorithm" },
+ { SUPPORTED_EXTENSIONS_PARAMETER_ID, "Supported Extensions" },
+ { FORWARD_TSN_SUPPORTED_PARAMETER_ID, "Forward TSN supported" },
+ { ADD_IP_ADDRESS_PARAMETER_ID, "Add IP address" },
+ { DEL_IP_ADDRESS_PARAMETER_ID, "Delete IP address" },
+ { ERROR_CAUSE_INDICATION_PARAMETER_ID, "Error cause indication" },
+ { SET_PRIMARY_ADDRESS_PARAMETER_ID, "Set primary address" },
+ { SUCCESS_REPORT_PARAMETER_ID, "Success report" },
+ { ADAP_LAYER_INDICATION_PARAMETER_ID, "Adaptation Layer Indication" },
+ { 0, NULL } };
+
+#define SCTP_PARAMETER_BIT_1 0x8000
+#define SCTP_PARAMETER_BIT_2 0x4000
+
+static const true_false_string sctp_parameter_bit_1_value = {
+ "Skip parameter and continue processing of the chunk",
+ "Stop processing of chunk"
+};
+
+static const true_false_string sctp_parameter_bit_2_value = {
+ "Do report",
+ "Do not report"
+};
+
+static void
+dissect_parameter(tvbuff_t *parameter_tvb, packet_info *pinfo,
+ proto_tree *chunk_tree, proto_item *additional_item,
+ gboolean dissecting_init_init_ack_chunk,
+ gboolean final_parameter)
+{
+ guint16 type, length, padding_length, reported_length;
+ proto_item *parameter_item, *type_item;
+ proto_tree *parameter_tree, *type_tree;
+
+ type = tvb_get_ntohs(parameter_tvb, PARAMETER_TYPE_OFFSET);
+ length = tvb_get_ntohs(parameter_tvb, PARAMETER_LENGTH_OFFSET);
+ reported_length = tvb_reported_length(parameter_tvb);
+ padding_length = reported_length - length;
+
+ parameter_tree = proto_tree_add_subtree_format(chunk_tree, parameter_tvb, PARAMETER_HEADER_OFFSET, -1,
+ ett_sctp_chunk_parameter, &parameter_item, "%s parameter",
+ val_to_str_const(type, parameter_identifier_values, "Unknown"));
+ if (final_parameter) {
+ if (padding_length > 0) {
+ expert_add_info(pinfo, parameter_item, &ei_sctp_parameter_padding);
+ }
+ } else {
+ if (reported_length % 4) {
+ expert_add_info_format(pinfo, parameter_item, &ei_sctp_parameter_length, "Parameter length is not padded to a multiple of 4 bytes (length=%d)", reported_length);
+ }
+ }
+
+ if (!(chunk_tree || (dissecting_init_init_ack_chunk && (type == IPV4ADDRESS_PARAMETER_ID || type == IPV6ADDRESS_PARAMETER_ID))))
+ return;
+
+ if (chunk_tree) {
+ type_item = proto_tree_add_item(parameter_tree, hf_parameter_type, parameter_tvb, PARAMETER_TYPE_OFFSET, PARAMETER_TYPE_LENGTH, ENC_BIG_ENDIAN);
+ type_tree = proto_item_add_subtree(type_item, ett_sctp_parameter_type);
+ proto_tree_add_item(type_tree, hf_parameter_bit_1, parameter_tvb, PARAMETER_TYPE_OFFSET, PARAMETER_TYPE_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(type_tree, hf_parameter_bit_2, parameter_tvb, PARAMETER_TYPE_OFFSET, PARAMETER_TYPE_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(parameter_tree, hf_parameter_length, parameter_tvb, PARAMETER_LENGTH_OFFSET, PARAMETER_LENGTH_LENGTH, ENC_BIG_ENDIAN);
+ /* XXX - add expert info if length is bogus? */
+ } else {
+ parameter_item = NULL;
+ parameter_tree = NULL;
+ }
+
+ switch(type) {
+ case HEARTBEAT_INFO_PARAMETER_ID:
+ dissect_heartbeat_info_parameter(parameter_tvb, parameter_tree, parameter_item);
+ break;
+ case IPV4ADDRESS_PARAMETER_ID:
+ dissect_ipv4_parameter(parameter_tvb, parameter_tree, parameter_item, additional_item, dissecting_init_init_ack_chunk);
+ break;
+ case IPV6ADDRESS_PARAMETER_ID:
+ dissect_ipv6_parameter(parameter_tvb, parameter_tree, parameter_item, additional_item, dissecting_init_init_ack_chunk);
+ break;
+ case STATE_COOKIE_PARAMETER_ID:
+ dissect_state_cookie_parameter(parameter_tvb, parameter_tree, parameter_item);
+ break;
+ case UNREC_PARA_PARAMETER_ID:
+ dissect_unrecognized_parameters_parameter(parameter_tvb, pinfo, parameter_tree);
+ break;
+ case COOKIE_PRESERVATIVE_PARAMETER_ID:
+ dissect_cookie_preservative_parameter(parameter_tvb, parameter_tree, parameter_item);
+ break;
+ case HOSTNAME_ADDRESS_PARAMETER_ID:
+ dissect_hostname_parameter(parameter_tvb, parameter_tree, parameter_item, additional_item);
+ break;
+ case SUPPORTED_ADDRESS_TYPES_PARAMETER_ID:
+ dissect_supported_address_types_parameter(parameter_tvb, parameter_tree, parameter_item);
+ break;
+ case OUTGOING_SSN_RESET_REQUEST_PARAMETER_ID:
+ dissect_outgoing_ssn_reset_request_parameter(parameter_tvb, parameter_tree, parameter_item);
+ break;
+ case INCOMING_SSN_RESET_REQUEST_PARAMETER_ID:
+ dissect_incoming_ssn_reset_request_parameter(parameter_tvb, parameter_tree, parameter_item);
+ break;
+ case SSN_TSN_RESET_REQUEST_PARAMETER_ID:
+ dissect_ssn_tsn_reset_request_parameter(parameter_tvb, parameter_tree, parameter_item);
+ break;
+ case RE_CONFIGURATION_RESPONSE_PARAMETER_ID:
+ dissect_re_configuration_response_parameter(parameter_tvb, parameter_tree, parameter_item);
+ break;
+ case ADD_OUTGOING_STREAMS_REQUEST_PARAMETER_ID:
+ dissect_add_outgoing_streams_parameter(parameter_tvb, parameter_tree, parameter_item);
+ break;
+ case ADD_INCOMING_STREAMS_REQUEST_PARAMETER_ID:
+ dissect_add_incoming_streams_parameter(parameter_tvb, parameter_tree, parameter_item);
+ break;
+ case ECN_PARAMETER_ID:
+ dissect_ecn_parameter(parameter_tvb);
+ break;
+ case ZERO_CHECKSUM_ACCEPTABLE_PARAMETER_ID:
+ dissect_zero_checksum_acceptable_parameter(parameter_tvb, parameter_tree, parameter_item);
+ break;
+ case RANDOM_PARAMETER_ID:
+ dissect_random_parameter(parameter_tvb, parameter_tree);
+ break;
+ case CHUNKS_PARAMETER_ID:
+ dissect_chunks_parameter(parameter_tvb, parameter_tree, parameter_item);
+ break;
+ case HMAC_ALGO_PARAMETER_ID:
+ dissect_hmac_algo_parameter(parameter_tvb, parameter_tree, parameter_item);
+ break;
+ case SUPPORTED_EXTENSIONS_PARAMETER_ID:
+ dissect_supported_extensions_parameter(parameter_tvb, parameter_tree, parameter_item);
+ break;
+ case FORWARD_TSN_SUPPORTED_PARAMETER_ID:
+ dissect_forward_tsn_supported_parameter(parameter_tvb);
+ break;
+ case ADD_IP_ADDRESS_PARAMETER_ID:
+ dissect_add_ip_address_parameter(parameter_tvb, pinfo, parameter_tree, parameter_item);
+ break;
+ case DEL_IP_ADDRESS_PARAMETER_ID:
+ dissect_del_ip_address_parameter(parameter_tvb, pinfo, parameter_tree, parameter_item);
+ break;
+ case ERROR_CAUSE_INDICATION_PARAMETER_ID:
+ dissect_error_cause_indication_parameter(parameter_tvb, pinfo, parameter_tree);
+ break;
+ case SET_PRIMARY_ADDRESS_PARAMETER_ID:
+ dissect_set_primary_address_parameter(parameter_tvb, pinfo, parameter_tree, parameter_item);
+ break;
+ case SUCCESS_REPORT_PARAMETER_ID:
+ dissect_success_report_parameter(parameter_tvb, parameter_tree, parameter_item);
+ break;
+ case ADAP_LAYER_INDICATION_PARAMETER_ID:
+ dissect_adap_indication_parameter(parameter_tvb, parameter_tree, parameter_item);
+ break;
+ default:
+ dissect_unknown_parameter(parameter_tvb, parameter_tree, parameter_item);
+ break;
+ }
+
+ if (padding_length > 0) {
+ proto_tree_add_item(parameter_tree, hf_parameter_padding, parameter_tvb, PARAMETER_HEADER_OFFSET + length, padding_length, ENC_NA);
+ }
+}
+
+static void
+dissect_parameters(tvbuff_t *parameters_tvb, packet_info *pinfo, proto_tree *tree, proto_item *additional_item, gboolean dissecting_init_init_ack_chunk)
+{
+ gint offset, length, total_length, remaining_length;
+ tvbuff_t *parameter_tvb;
+ gboolean final_parameter;
+
+ offset = 0;
+ remaining_length = tvb_reported_length_remaining(parameters_tvb, offset);
+ while (remaining_length > 0) {
+ if ((offset > 0) && additional_item)
+ proto_item_append_text(additional_item, " ");
+
+ length = tvb_get_ntohs(parameters_tvb, offset + PARAMETER_LENGTH_OFFSET);
+ total_length = WS_ROUNDUP_4(length);
+
+ /* If we have less bytes than we need, throw an exception while dissecting
+ * the parameter--not when generating the parameter_tvb below.
+ */
+ total_length = MIN(total_length, remaining_length);
+
+ /* create a tvb for the parameter including the padding bytes */
+ parameter_tvb = tvb_new_subset_length_caplen(parameters_tvb, offset, MIN(total_length, tvb_captured_length_remaining(parameters_tvb, offset)), total_length);
+ /* get rid of the handled parameter */
+ offset += total_length;
+ remaining_length = tvb_reported_length_remaining(parameters_tvb, offset);
+ if (remaining_length > 0) {
+ final_parameter = FALSE;
+ } else {
+ final_parameter = TRUE;
+ }
+ dissect_parameter(parameter_tvb, pinfo, tree, additional_item, dissecting_init_init_ack_chunk, final_parameter);
+ }
+}
+
+
+/*
+ * Code to handle error causes for ABORT and ERROR chunks
+ */
+
+
+#define CAUSE_CODE_LENGTH 2
+#define CAUSE_LENGTH_LENGTH 2
+#define CAUSE_HEADER_LENGTH (CAUSE_CODE_LENGTH + CAUSE_LENGTH_LENGTH)
+
+#define CAUSE_HEADER_OFFSET 0
+#define CAUSE_CODE_OFFSET CAUSE_HEADER_OFFSET
+#define CAUSE_LENGTH_OFFSET (CAUSE_CODE_OFFSET + CAUSE_CODE_LENGTH)
+#define CAUSE_INFO_OFFSET (CAUSE_LENGTH_OFFSET + CAUSE_LENGTH_LENGTH)
+
+
+#define CAUSE_STREAM_IDENTIFIER_LENGTH 2
+#define CAUSE_RESERVED_LENGTH 2
+#define CAUSE_STREAM_IDENTIFIER_OFFSET CAUSE_INFO_OFFSET
+#define CAUSE_RESERVED_OFFSET (CAUSE_STREAM_IDENTIFIER_OFFSET + CAUSE_STREAM_IDENTIFIER_LENGTH)
+
+static void
+dissect_invalid_stream_identifier_cause(tvbuff_t *cause_tvb, proto_tree *cause_tree, proto_item *cause_item)
+{
+ proto_tree_add_item(cause_tree, hf_cause_stream_identifier, cause_tvb, CAUSE_STREAM_IDENTIFIER_OFFSET, CAUSE_STREAM_IDENTIFIER_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(cause_tree, hf_cause_reserved, cause_tvb, CAUSE_RESERVED_OFFSET, CAUSE_RESERVED_LENGTH, ENC_BIG_ENDIAN);
+ proto_item_append_text(cause_item, " (SID: %u)", tvb_get_ntohs(cause_tvb, CAUSE_STREAM_IDENTIFIER_OFFSET));
+}
+
+#define CAUSE_NUMBER_OF_MISSING_PARAMETERS_LENGTH 4
+#define CAUSE_MISSING_PARAMETER_TYPE_LENGTH 2
+
+#define CAUSE_NUMBER_OF_MISSING_PARAMETERS_OFFSET CAUSE_INFO_OFFSET
+#define CAUSE_FIRST_MISSING_PARAMETER_TYPE_OFFSET (CAUSE_NUMBER_OF_MISSING_PARAMETERS_OFFSET + \
+ CAUSE_NUMBER_OF_MISSING_PARAMETERS_LENGTH )
+
+static void
+dissect_missing_mandatory_parameters_cause(tvbuff_t *cause_tvb, proto_tree *cause_tree)
+{
+ guint32 number_of_missing_parameters, missing_parameter_number;
+ guint offset;
+
+ number_of_missing_parameters = tvb_get_ntohl(cause_tvb, CAUSE_NUMBER_OF_MISSING_PARAMETERS_OFFSET);
+ proto_tree_add_item(cause_tree, hf_cause_number_of_missing_parameters, cause_tvb, CAUSE_NUMBER_OF_MISSING_PARAMETERS_OFFSET, CAUSE_NUMBER_OF_MISSING_PARAMETERS_LENGTH, ENC_BIG_ENDIAN);
+ offset = CAUSE_FIRST_MISSING_PARAMETER_TYPE_OFFSET;
+ for(missing_parameter_number = 0; missing_parameter_number < number_of_missing_parameters; missing_parameter_number++) {
+ proto_tree_add_item(cause_tree, hf_cause_missing_parameter_type, cause_tvb, offset, CAUSE_MISSING_PARAMETER_TYPE_LENGTH, ENC_BIG_ENDIAN);
+ offset += CAUSE_MISSING_PARAMETER_TYPE_LENGTH;
+ }
+}
+
+#define CAUSE_MEASURE_OF_STALENESS_LENGTH 4
+#define CAUSE_MEASURE_OF_STALENESS_OFFSET CAUSE_INFO_OFFSET
+
+static void
+dissect_stale_cookie_error_cause(tvbuff_t *cause_tvb, proto_tree *cause_tree, proto_item *cause_item)
+{
+ proto_tree_add_item(cause_tree, hf_cause_measure_of_staleness, cause_tvb, CAUSE_MEASURE_OF_STALENESS_OFFSET, CAUSE_MEASURE_OF_STALENESS_LENGTH, ENC_BIG_ENDIAN);
+ proto_item_append_text(cause_item, " (Measure: %u usec)", tvb_get_ntohl(cause_tvb, CAUSE_MEASURE_OF_STALENESS_OFFSET));
+}
+
+static void
+dissect_out_of_resource_cause(tvbuff_t *cause_tvb _U_)
+{
+}
+
+static void
+dissect_unresolvable_address_cause(tvbuff_t *cause_tvb, packet_info *pinfo, proto_tree *cause_tree, proto_item *cause_item)
+{
+ guint16 parameter_length;
+ tvbuff_t *parameter_tvb;
+
+ parameter_length = tvb_get_ntohs(cause_tvb, CAUSE_LENGTH_OFFSET) - CAUSE_HEADER_LENGTH;
+ parameter_tvb = tvb_new_subset_length_caplen(cause_tvb, CAUSE_INFO_OFFSET,
+ MIN(parameter_length, tvb_captured_length_remaining(cause_tvb, CAUSE_INFO_OFFSET)),
+ MIN(parameter_length, tvb_reported_length_remaining(cause_tvb, CAUSE_INFO_OFFSET)));
+ dissect_parameter(parameter_tvb, pinfo, cause_tree, cause_item, FALSE, TRUE);
+}
+
+static gboolean
+dissect_sctp_chunk(tvbuff_t *chunk_tvb, packet_info *pinfo, proto_tree *tree, proto_tree *sctp_tree, sctp_half_assoc_t *assoc, gboolean useinfo);
+
+static void
+dissect_unrecognized_chunk_type_cause(tvbuff_t *cause_tvb, packet_info *pinfo, proto_tree *cause_tree, proto_item *cause_item)
+{
+ guint16 chunk_length;
+ guint8 unrecognized_type;
+ tvbuff_t *unrecognized_chunk_tvb;
+
+ chunk_length = tvb_get_ntohs(cause_tvb, CAUSE_LENGTH_OFFSET) - CAUSE_HEADER_LENGTH;
+ unrecognized_chunk_tvb = tvb_new_subset_length_caplen(cause_tvb, CAUSE_INFO_OFFSET,
+ MIN(chunk_length, tvb_captured_length_remaining(cause_tvb, CAUSE_INFO_OFFSET)),
+ MIN(chunk_length, tvb_reported_length_remaining(cause_tvb, CAUSE_INFO_OFFSET)));
+ dissect_sctp_chunk(unrecognized_chunk_tvb, pinfo, cause_tree,cause_tree, NULL, FALSE);
+ unrecognized_type = tvb_get_guint8(unrecognized_chunk_tvb, CHUNK_TYPE_OFFSET);
+ proto_item_append_text(cause_item, " (Type: %u (%s))", unrecognized_type, val_to_str_const(unrecognized_type, chunk_type_values, "unknown"));
+}
+
+static void
+dissect_invalid_mandatory_parameter_cause(tvbuff_t *cause_tvb _U_)
+{
+}
+
+static void
+dissect_unrecognized_parameters_cause(tvbuff_t *cause_tvb, packet_info *pinfo, proto_tree *cause_tree)
+{
+ guint16 cause_info_length;
+ tvbuff_t *unrecognized_parameters_tvb;
+
+ cause_info_length = tvb_get_ntohs(cause_tvb, CAUSE_LENGTH_OFFSET) - CAUSE_HEADER_LENGTH;
+
+ unrecognized_parameters_tvb = tvb_new_subset_length_caplen(cause_tvb, CAUSE_INFO_OFFSET,
+ MIN(cause_info_length, tvb_captured_length_remaining(cause_tvb, CAUSE_INFO_OFFSET)),
+ MIN(cause_info_length, tvb_reported_length_remaining(cause_tvb, CAUSE_INFO_OFFSET)));
+ dissect_parameters(unrecognized_parameters_tvb, pinfo, cause_tree, NULL, FALSE);
+}
+
+#define CAUSE_TSN_LENGTH 4
+#define CAUSE_TSN_OFFSET CAUSE_INFO_OFFSET
+
+static void
+dissect_no_user_data_cause(tvbuff_t *cause_tvb, proto_tree *cause_tree, proto_item *cause_item)
+{
+ proto_tree_add_item(cause_tree, hf_cause_tsn, cause_tvb, CAUSE_TSN_OFFSET, CAUSE_TSN_LENGTH, ENC_BIG_ENDIAN);
+ proto_item_append_text(cause_item, " (TSN: %u)", tvb_get_ntohl(cause_tvb, CAUSE_TSN_OFFSET));
+}
+
+static void
+dissect_cookie_received_while_shutting_down_cause(tvbuff_t *cause_tvb _U_)
+{
+}
+
+static void
+dissect_restart_with_new_address_cause(tvbuff_t *cause_tvb, packet_info *pinfo, proto_tree *cause_tree, proto_item *cause_item)
+{
+ guint16 cause_info_length;
+ tvbuff_t *parameter_tvb;
+
+ cause_info_length = tvb_get_ntohs(cause_tvb, CAUSE_LENGTH_OFFSET) - CAUSE_HEADER_LENGTH;
+ parameter_tvb = tvb_new_subset_length_caplen(cause_tvb, CAUSE_INFO_OFFSET,
+ MIN(cause_info_length, tvb_captured_length_remaining(cause_tvb, CAUSE_INFO_OFFSET)),
+ MIN(cause_info_length, tvb_reported_length_remaining(cause_tvb, CAUSE_INFO_OFFSET)));
+ proto_item_append_text(cause_item, " (New addresses: ");
+ dissect_parameters(parameter_tvb, pinfo, cause_tree, cause_item, FALSE);
+ proto_item_append_text(cause_item, ")");
+}
+
+static void
+dissect_user_initiated_abort_cause(tvbuff_t *cause_tvb, proto_tree *cause_tree)
+{
+ guint16 cause_info_length;
+
+ cause_info_length = tvb_get_ntohs(cause_tvb, CAUSE_LENGTH_OFFSET) - CAUSE_HEADER_LENGTH;
+ if (cause_info_length > 0)
+ proto_tree_add_item(cause_tree, hf_cause_info, cause_tvb, CAUSE_INFO_OFFSET, cause_info_length, ENC_NA);
+}
+
+static void
+dissect_protocol_violation_cause(tvbuff_t *cause_tvb, proto_tree *cause_tree)
+{
+ guint16 cause_info_length;
+
+ cause_info_length = tvb_get_ntohs(cause_tvb, CAUSE_LENGTH_OFFSET) - CAUSE_HEADER_LENGTH;
+ if (cause_info_length > 0)
+ proto_tree_add_item(cause_tree, hf_cause_info, cause_tvb, CAUSE_INFO_OFFSET, cause_info_length, ENC_NA);
+}
+
+static void
+dissect_delete_last_address_cause(tvbuff_t *cause_tvb, packet_info *pinfo, proto_tree *cause_tree, proto_item *cause_item)
+{
+ guint16 cause_info_length;
+ tvbuff_t *parameter_tvb;
+
+ cause_info_length = tvb_get_ntohs(cause_tvb, CAUSE_LENGTH_OFFSET) - CAUSE_HEADER_LENGTH;
+ parameter_tvb = tvb_new_subset_length_caplen(cause_tvb, CAUSE_INFO_OFFSET,
+ MIN(cause_info_length, tvb_captured_length_remaining(cause_tvb, CAUSE_INFO_OFFSET)),
+ MIN(cause_info_length, tvb_reported_length_remaining(cause_tvb, CAUSE_INFO_OFFSET)));
+ proto_item_append_text(cause_item, " (Last address: ");
+ dissect_parameter(parameter_tvb, pinfo, cause_tree, cause_item, FALSE, FALSE);
+ proto_item_append_text(cause_item, ")");
+}
+
+static void
+dissect_resource_outage_cause(tvbuff_t *cause_tvb, packet_info *pinfo, proto_tree *cause_tree)
+{
+ guint16 cause_info_length;
+ tvbuff_t *parameter_tvb;
+
+ cause_info_length = tvb_get_ntohs(cause_tvb, CAUSE_LENGTH_OFFSET) - CAUSE_HEADER_LENGTH;
+ parameter_tvb = tvb_new_subset_length_caplen(cause_tvb, CAUSE_INFO_OFFSET,
+ MIN(cause_info_length, tvb_captured_length_remaining(cause_tvb, CAUSE_INFO_OFFSET)),
+ MIN(cause_info_length, tvb_reported_length_remaining(cause_tvb, CAUSE_INFO_OFFSET)));
+ dissect_parameter(parameter_tvb, pinfo, cause_tree, NULL, FALSE, FALSE);
+}
+
+static void
+dissect_delete_source_address_cause(tvbuff_t *cause_tvb, packet_info *pinfo, proto_tree *cause_tree, proto_item *cause_item)
+{
+ guint16 cause_info_length;
+ tvbuff_t *parameter_tvb;
+
+ cause_info_length = tvb_get_ntohs(cause_tvb, CAUSE_LENGTH_OFFSET) - CAUSE_HEADER_LENGTH;
+ parameter_tvb = tvb_new_subset_length_caplen(cause_tvb, CAUSE_INFO_OFFSET,
+ MIN(cause_info_length, tvb_captured_length_remaining(cause_tvb, CAUSE_INFO_OFFSET)),
+ MIN(cause_info_length, tvb_reported_length_remaining(cause_tvb, CAUSE_INFO_OFFSET)));
+ proto_item_append_text(cause_item, " (Deleted address: ");
+ dissect_parameter(parameter_tvb, pinfo, cause_tree, cause_item, FALSE, FALSE);
+ proto_item_append_text(cause_item, ")");
+}
+
+static void
+dissect_request_refused_cause(tvbuff_t *cause_tvb, packet_info *pinfo, proto_tree *cause_tree)
+{
+ guint16 cause_info_length;
+ tvbuff_t *parameter_tvb;
+
+ cause_info_length = tvb_get_ntohs(cause_tvb, CAUSE_LENGTH_OFFSET) - CAUSE_HEADER_LENGTH;
+ parameter_tvb = tvb_new_subset_length_caplen(cause_tvb, CAUSE_INFO_OFFSET,
+ MIN(cause_info_length, tvb_captured_length_remaining(cause_tvb, CAUSE_INFO_OFFSET)),
+ MIN(cause_info_length, tvb_reported_length_remaining(cause_tvb, CAUSE_INFO_OFFSET)));
+ dissect_parameter(parameter_tvb, pinfo, cause_tree, NULL, FALSE, FALSE);
+}
+
+static void
+dissect_unsupported_hmac_id_cause(tvbuff_t *cause_tvb, packet_info *pinfo _U_, proto_tree *cause_tree)
+{
+ proto_tree_add_item(cause_tree, hf_hmac_id, cause_tvb, CAUSE_INFO_OFFSET, HMAC_ID_LENGTH, ENC_BIG_ENDIAN);
+}
+
+static void
+dissect_unknown_cause(tvbuff_t *cause_tvb, proto_tree *cause_tree, proto_item *cause_item)
+{
+ guint16 cause_info_length;
+
+ cause_info_length = tvb_get_ntohs(cause_tvb, CAUSE_LENGTH_OFFSET) - CAUSE_HEADER_LENGTH;
+ if (cause_info_length > 0)
+ proto_tree_add_item(cause_tree, hf_cause_info, cause_tvb, CAUSE_INFO_OFFSET, cause_info_length, ENC_NA);
+ proto_item_append_text(cause_item, " (Code: %u, information length: %u byte%s)", tvb_get_ntohs(cause_tvb, CAUSE_CODE_OFFSET), cause_info_length, plurality(cause_info_length, "", "s"));
+}
+
+#define INVALID_STREAM_IDENTIFIER 0x01
+#define MISSING_MANDATORY_PARAMETERS 0x02
+#define STALE_COOKIE_ERROR 0x03
+#define OUT_OF_RESOURCE 0x04
+#define UNRESOLVABLE_ADDRESS 0x05
+#define UNRECOGNIZED_CHUNK_TYPE 0x06
+#define INVALID_MANDATORY_PARAMETER 0x07
+#define UNRECOGNIZED_PARAMETERS 0x08
+#define NO_USER_DATA 0x09
+#define COOKIE_RECEIVED_WHILE_SHUTTING_DOWN 0x0a
+#define RESTART_WITH_NEW_ADDRESSES 0x0b
+#define USER_INITIATED_ABORT 0x0c
+#define PROTOCOL_VIOLATION 0x0d
+#define REQUEST_TO_DELETE_LAST_ADDRESS 0x00a0
+#define OPERATION_REFUSED_DUE_TO_RESOURCE_SHORTAGE 0x00a1
+#define REQUEST_TO_DELETE_SOURCE_ADDRESS 0x00a2
+#define ABORT_DUE_TO_ILLEGAL_ASCONF 0x00a3
+#define REQUEST_REFUSED 0x00a4
+#define UNSUPPORTED_HMAC_ID 0x0105
+
+static const value_string cause_code_values[] = {
+ { INVALID_STREAM_IDENTIFIER, "Invalid stream identifier" },
+ { MISSING_MANDATORY_PARAMETERS, "Missing mandatory parameter" },
+ { STALE_COOKIE_ERROR, "Stale cookie error" },
+ { OUT_OF_RESOURCE, "Out of resource" },
+ { UNRESOLVABLE_ADDRESS, "Unresolvable address" },
+ { UNRECOGNIZED_CHUNK_TYPE, "Unrecognized chunk type" },
+ { INVALID_MANDATORY_PARAMETER, "Invalid mandatory parameter" },
+ { UNRECOGNIZED_PARAMETERS, "Unrecognized parameters" },
+ { NO_USER_DATA, "No user data" },
+ { COOKIE_RECEIVED_WHILE_SHUTTING_DOWN, "Cookie received while shutting down" },
+ { RESTART_WITH_NEW_ADDRESSES, "Restart of an association with new addresses" },
+ { USER_INITIATED_ABORT, "User initiated ABORT" },
+ { PROTOCOL_VIOLATION, "Protocol violation" },
+ { REQUEST_TO_DELETE_LAST_ADDRESS, "Request to delete last address" },
+ { OPERATION_REFUSED_DUE_TO_RESOURCE_SHORTAGE, "Operation refused due to resource shortage" },
+ { REQUEST_TO_DELETE_SOURCE_ADDRESS, "Request to delete source address" },
+ { ABORT_DUE_TO_ILLEGAL_ASCONF, "Association Aborted due to illegal ASCONF-ACK" },
+ { REQUEST_REFUSED, "Request refused - no authorization" },
+ { UNSUPPORTED_HMAC_ID, "Unsupported HMAC identifier" },
+ { 0, NULL } };
+
+
+static void
+dissect_error_cause(tvbuff_t *cause_tvb, packet_info *pinfo, proto_tree *chunk_tree)
+{
+ guint16 code, length, padding_length;
+ proto_item *cause_item;
+ proto_tree *cause_tree;
+
+ code = tvb_get_ntohs(cause_tvb, CAUSE_CODE_OFFSET);
+ length = tvb_get_ntohs(cause_tvb, CAUSE_LENGTH_OFFSET);
+ padding_length = tvb_reported_length(cause_tvb) - length;
+
+ cause_tree = proto_tree_add_subtree_format(chunk_tree, cause_tvb, CAUSE_HEADER_OFFSET, -1,
+ ett_sctp_chunk_cause, &cause_item, "%s cause", val_to_str_const(code, cause_code_values, "Unknown"));
+
+ proto_tree_add_item(cause_tree, hf_cause_code, cause_tvb, CAUSE_CODE_OFFSET, CAUSE_CODE_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(cause_tree, hf_cause_length, cause_tvb, CAUSE_LENGTH_OFFSET, CAUSE_LENGTH_LENGTH, ENC_BIG_ENDIAN);
+ /* XXX - add expert info if length is bogus? */
+
+ switch(code) {
+ case INVALID_STREAM_IDENTIFIER:
+ dissect_invalid_stream_identifier_cause(cause_tvb, cause_tree, cause_item);
+ break;
+ case MISSING_MANDATORY_PARAMETERS:
+ dissect_missing_mandatory_parameters_cause(cause_tvb, cause_tree);
+ break;
+ case STALE_COOKIE_ERROR:
+ dissect_stale_cookie_error_cause(cause_tvb, cause_tree, cause_item);
+ break;
+ case OUT_OF_RESOURCE:
+ dissect_out_of_resource_cause(cause_tvb);
+ break;
+ case UNRESOLVABLE_ADDRESS:
+ dissect_unresolvable_address_cause(cause_tvb, pinfo, cause_tree, cause_item);
+ break;
+ case UNRECOGNIZED_CHUNK_TYPE:
+ dissect_unrecognized_chunk_type_cause(cause_tvb, pinfo, cause_tree, cause_item);
+ break;
+ case INVALID_MANDATORY_PARAMETER:
+ dissect_invalid_mandatory_parameter_cause(cause_tvb);
+ break;
+ case UNRECOGNIZED_PARAMETERS:
+ dissect_unrecognized_parameters_cause(cause_tvb, pinfo, cause_tree);
+ break;
+ case NO_USER_DATA:
+ dissect_no_user_data_cause(cause_tvb, cause_tree, cause_item);
+ break;
+ case COOKIE_RECEIVED_WHILE_SHUTTING_DOWN:
+ dissect_cookie_received_while_shutting_down_cause(cause_tvb);
+ break;
+ case RESTART_WITH_NEW_ADDRESSES:
+ dissect_restart_with_new_address_cause(cause_tvb, pinfo, cause_tree, cause_item);
+ break;
+ case USER_INITIATED_ABORT:
+ dissect_user_initiated_abort_cause(cause_tvb, cause_tree);
+ break;
+ case PROTOCOL_VIOLATION:
+ dissect_protocol_violation_cause(cause_tvb, cause_tree);
+ break;
+ case REQUEST_TO_DELETE_LAST_ADDRESS:
+ dissect_delete_last_address_cause(cause_tvb, pinfo, cause_tree, cause_item);
+ break;
+ case OPERATION_REFUSED_DUE_TO_RESOURCE_SHORTAGE:
+ dissect_resource_outage_cause(cause_tvb, pinfo, cause_tree);
+ break;
+ case REQUEST_TO_DELETE_SOURCE_ADDRESS:
+ dissect_delete_source_address_cause(cause_tvb, pinfo, cause_tree, cause_item);
+ break;
+ case REQUEST_REFUSED:
+ dissect_request_refused_cause(cause_tvb, pinfo, cause_tree);
+ break;
+ case UNSUPPORTED_HMAC_ID:
+ dissect_unsupported_hmac_id_cause(cause_tvb, pinfo, cause_tree);
+ break;
+ default:
+ dissect_unknown_cause(cause_tvb, cause_tree, cause_item);
+ break;
+ }
+
+ if (padding_length > 0)
+ proto_tree_add_item(cause_tree, hf_cause_padding, cause_tvb, CAUSE_HEADER_OFFSET + length, padding_length, ENC_NA);
+}
+
+static void
+dissect_error_causes(tvbuff_t *causes_tvb, packet_info *pinfo, proto_tree *tree)
+{
+ gint offset, length, total_length, remaining_length;
+ tvbuff_t *cause_tvb;
+
+ offset = 0;
+ while((remaining_length = tvb_reported_length_remaining(causes_tvb, offset))) {
+ length = tvb_get_ntohs(causes_tvb, offset + CAUSE_LENGTH_OFFSET);
+ total_length = WS_ROUNDUP_4(length);
+
+ /* If we have less bytes than we need, throw an exception while dissecting
+ * the cause--not when generating the causes_tvb below.
+ */
+ total_length = MIN(total_length, remaining_length);
+
+ /* create a tvb for the parameter including the padding bytes */
+ cause_tvb = tvb_new_subset_length_caplen(causes_tvb, offset, MIN(total_length, tvb_captured_length_remaining(causes_tvb, offset)), total_length);
+
+ dissect_error_cause(cause_tvb, pinfo, tree);
+
+ /* get rid of the handled cause */
+ offset += total_length;
+ }
+}
+
+
+/*
+ * Code to actually dissect the packets
+*/
+
+static gboolean try_heuristic_first = FALSE;
+
+static gboolean
+dissect_payload(tvbuff_t *payload_tvb, packet_info *pinfo, proto_tree *tree, guint32 ppi)
+{
+ guint32 low_port, high_port;
+ gboolean try_ppi, try_low_port, try_high_port;
+ heur_dtbl_entry_t *hdtbl_entry;
+
+ if (enable_ulp_dissection) {
+
+ /* XXX - we ignore port numbers of 0, as some dissectors use a port
+ number of 0 to disable the port. */
+ if (pinfo->srcport > pinfo->destport) {
+ low_port = pinfo->destport;
+ high_port = pinfo->srcport;
+ } else {
+ low_port = pinfo->srcport;
+ high_port = pinfo->destport;
+ }
+
+ try_ppi = FALSE;
+ if (dissector_is_uint_changed(sctp_ppi_dissector_table, ppi)) {
+ if (dissector_try_uint_new(sctp_ppi_dissector_table, ppi, payload_tvb, pinfo, tree, TRUE, GUINT_TO_POINTER(ppi))) {
+ return TRUE;
+ }
+ } else {
+ /* The default; try it later */
+ try_ppi = TRUE;
+ }
+
+ try_low_port = FALSE;
+ if (low_port != 0) {
+ if (dissector_is_uint_changed(sctp_port_dissector_table, low_port)) {
+ if (dissector_try_uint_new(sctp_port_dissector_table, low_port, payload_tvb, pinfo, tree, TRUE, GUINT_TO_POINTER(ppi))) {
+ return TRUE;
+ }
+ } else {
+ /* The default; try it later */
+ try_low_port = TRUE;
+ }
+ }
+
+ try_high_port = FALSE;
+ if (high_port != 0) {
+ if (dissector_is_uint_changed(sctp_port_dissector_table, high_port)) {
+ if (dissector_try_uint_new(sctp_port_dissector_table, high_port, payload_tvb, pinfo, tree, TRUE, GUINT_TO_POINTER(ppi))) {
+ return TRUE;
+ }
+ } else {
+ /* The default; try it later */
+ try_high_port = TRUE;
+ }
+ }
+
+ if (try_heuristic_first) {
+ /* do lookup with the heuristic subdissector table */
+ if (dissector_try_heuristic(sctp_heur_subdissector_list, payload_tvb, pinfo, tree, &hdtbl_entry, GUINT_TO_POINTER(ppi)))
+ return TRUE;
+ }
+
+ /* Do lookups with the subdissector table.
+ First try the PPI.
+
+ When trying port numbers, we try the port number with the lower value
+ first, followed by the port number with the higher value. This means
+ that, for packets where a dissector is registered for *both* port
+ numbers, and where there's no match on the PPI:
+
+ 1) we pick the same dissector for traffic going in both directions;
+
+ 2) we prefer the port number that's more likely to be the right
+ one (as that prefers well-known ports to reserved ports);
+
+ although there is, of course, no guarantee that any such strategy
+ will always pick the right port number. */
+ if (try_ppi &&
+ dissector_try_uint_new(sctp_ppi_dissector_table, ppi, payload_tvb, pinfo, tree, TRUE, GUINT_TO_POINTER(ppi)))
+ return TRUE;
+
+ if (try_low_port &&
+ dissector_try_uint_new(sctp_port_dissector_table, low_port, payload_tvb, pinfo, tree, TRUE, GUINT_TO_POINTER(ppi)))
+ return TRUE;
+ if (try_high_port &&
+ dissector_try_uint_new(sctp_port_dissector_table, high_port, payload_tvb, pinfo, tree, TRUE, GUINT_TO_POINTER(ppi)))
+ return TRUE;
+
+ if (!try_heuristic_first) {
+ /* do lookup with the heuristic subdissector table */
+ if (dissector_try_heuristic(sctp_heur_subdissector_list, payload_tvb, pinfo, tree, &hdtbl_entry, GUINT_TO_POINTER(ppi)))
+ return TRUE;
+ }
+ }
+ /* Oh, well, we don't know this; dissect it as data. */
+ call_data_dissector(payload_tvb, pinfo, tree);
+ return TRUE;
+}
+
+#define DATA_CHUNK_TSN_LENGTH 4
+#define DATA_CHUNK_STREAM_ID_LENGTH 2
+#define DATA_CHUNK_STREAM_SEQ_NUMBER_LENGTH 2
+#define DATA_CHUNK_PAYLOAD_PROTOCOL_ID_LENGTH 4
+#define I_DATA_CHUNK_RESERVED_LENGTH 2
+#define I_DATA_CHUNK_MID_LENGTH 4
+#define I_DATA_CHUNK_PAYLOAD_PROTOCOL_ID_LENGTH 4
+#define I_DATA_CHUNK_FSN_LENGTH 4
+
+#define DATA_CHUNK_TSN_OFFSET (CHUNK_VALUE_OFFSET + 0)
+#define DATA_CHUNK_STREAM_ID_OFFSET (DATA_CHUNK_TSN_OFFSET + DATA_CHUNK_TSN_LENGTH)
+#define DATA_CHUNK_STREAM_SEQ_NUMBER_OFFSET (DATA_CHUNK_STREAM_ID_OFFSET + \
+ DATA_CHUNK_STREAM_ID_LENGTH)
+#define DATA_CHUNK_PAYLOAD_PROTOCOL_ID_OFFSET (DATA_CHUNK_STREAM_SEQ_NUMBER_OFFSET + \
+ DATA_CHUNK_STREAM_SEQ_NUMBER_LENGTH)
+#define DATA_CHUNK_PAYLOAD_OFFSET (DATA_CHUNK_PAYLOAD_PROTOCOL_ID_OFFSET + \
+ DATA_CHUNK_PAYLOAD_PROTOCOL_ID_LENGTH)
+#define I_DATA_CHUNK_RESERVED_OFFSET (DATA_CHUNK_STREAM_ID_OFFSET + \
+ DATA_CHUNK_STREAM_ID_LENGTH)
+#define I_DATA_CHUNK_MID_OFFSET (I_DATA_CHUNK_RESERVED_OFFSET + \
+ I_DATA_CHUNK_RESERVED_LENGTH)
+#define I_DATA_CHUNK_PAYLOAD_PROTOCOL_ID_OFFSET (I_DATA_CHUNK_MID_OFFSET + \
+ I_DATA_CHUNK_MID_LENGTH)
+#define I_DATA_CHUNK_FSN_OFFSET (I_DATA_CHUNK_MID_OFFSET + \
+ I_DATA_CHUNK_MID_LENGTH)
+#define I_DATA_CHUNK_PAYLOAD_OFFSET (I_DATA_CHUNK_PAYLOAD_PROTOCOL_ID_OFFSET + \
+ I_DATA_CHUNK_PAYLOAD_PROTOCOL_ID_LENGTH)
+
+#define DATA_CHUNK_HEADER_LENGTH (CHUNK_HEADER_LENGTH + \
+ DATA_CHUNK_TSN_LENGTH + \
+ DATA_CHUNK_STREAM_ID_LENGTH + \
+ DATA_CHUNK_STREAM_SEQ_NUMBER_LENGTH + \
+ DATA_CHUNK_PAYLOAD_PROTOCOL_ID_LENGTH)
+#define I_DATA_CHUNK_HEADER_LENGTH (CHUNK_HEADER_LENGTH + \
+ DATA_CHUNK_TSN_LENGTH + \
+ DATA_CHUNK_STREAM_ID_LENGTH + \
+ I_DATA_CHUNK_RESERVED_LENGTH + \
+ I_DATA_CHUNK_MID_LENGTH +\
+ I_DATA_CHUNK_PAYLOAD_PROTOCOL_ID_LENGTH)
+
+#define SCTP_DATA_CHUNK_E_BIT 0x01
+#define SCTP_DATA_CHUNK_B_BIT 0x02
+#define SCTP_DATA_CHUNK_U_BIT 0x04
+#define SCTP_DATA_CHUNK_I_BIT 0x08
+
+/* table to hold fragmented SCTP messages */
+static GHashTable *frag_table = NULL;
+
+
+typedef struct _frag_key {
+ guint16 sport;
+ guint16 dport;
+ guint32 verification_tag;
+ guint16 stream_id;
+ guint32 stream_seq_num;
+ guint8 u_bit;
+} frag_key;
+
+
+static gint
+frag_equal(gconstpointer k1, gconstpointer k2)
+{
+ const frag_key *key1 = (const frag_key *) k1;
+ const frag_key *key2 = (const frag_key *) k2;
+
+ return ( (key1->sport == key2->sport) &&
+ (key1->dport == key2->dport) &&
+ (key1->verification_tag == key2->verification_tag) &&
+ (key1->stream_id == key2->stream_id) &&
+ (key1->stream_seq_num == key2->stream_seq_num) &&
+ (key1->u_bit == key2->u_bit)
+ ? TRUE : FALSE);
+}
+
+
+static guint
+frag_hash(gconstpointer k)
+{
+ const frag_key *key = (const frag_key *) k;
+
+ return key->sport ^ key->dport ^ key->verification_tag ^
+ key->stream_id ^ key->stream_seq_num ^ key->u_bit;
+}
+
+
+static void
+frag_free_msgs(sctp_frag_msg *msg)
+{
+ sctp_frag_be *beginend;
+ sctp_fragment *fragment;
+
+ /* free all begins */
+ while (msg->begins) {
+ beginend = msg->begins;
+ msg->begins = msg->begins->next;
+ g_free(beginend);
+ }
+
+ /* free all ends */
+ while (msg->ends) {
+ beginend = msg->ends;
+ msg->ends = msg->ends->next;
+ g_free(beginend);
+ }
+
+ /* free all fragments */
+ while (msg->fragments) {
+ fragment = msg->fragments;
+ msg->fragments = msg->fragments->next;
+ g_free(fragment->data);
+ g_free(fragment);
+ }
+
+ /* msg->messages is wmem_ allocated, no need to free it */
+
+ g_free(msg);
+}
+
+static void
+sctp_init(void)
+{
+ frag_table = g_hash_table_new_full(frag_hash, frag_equal,
+ (GDestroyNotify)g_free, (GDestroyNotify)frag_free_msgs);
+ num_assocs = 0;
+ assoc_info_list = NULL;
+}
+
+static void
+sctp_cleanup(void)
+{
+ g_hash_table_destroy(frag_table);
+}
+
+
+static sctp_frag_msg*
+find_message(guint16 stream_id, guint32 stream_seq_num, guint8 u_bit)
+{
+ frag_key key;
+
+ key.sport = sctp_info.sport;
+ key.dport = sctp_info.dport;
+ key.verification_tag = sctp_info.verification_tag;
+ key.stream_id = stream_id;
+ key.stream_seq_num = stream_seq_num;
+ key.u_bit = u_bit;
+
+ return (sctp_frag_msg *)g_hash_table_lookup(frag_table, &key);
+}
+
+
+static sctp_fragment*
+find_fragment(guint32 tsn, guint16 stream_id, guint32 stream_seq_num, guint8 u_bit)
+{
+ sctp_frag_msg *msg;
+ sctp_fragment *next_fragment;
+
+ msg = find_message(stream_id, stream_seq_num, u_bit);
+
+ if (msg) {
+ next_fragment = msg->fragments;
+ while (next_fragment) {
+ if (next_fragment->tsn == tsn)
+ return next_fragment;
+ next_fragment = next_fragment->next;
+ }
+ }
+
+ return NULL;
+}
+
+
+static sctp_fragment *
+add_fragment(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 tsn,
+ guint16 stream_id, guint32 stream_seq_num, guint8 b_bit, guint8 e_bit,
+ guint8 u_bit, guint32 ppi, gboolean is_idata)
+{
+ sctp_frag_msg *msg;
+ sctp_fragment *fragment, *last_fragment;
+ sctp_frag_be *beginend, *last_beginend;
+ frag_key *key;
+
+ /* Don't try to do reassembly on error packets */
+ if (pinfo->flags.in_error_pkt)
+ return NULL;
+
+ /* lookup message. if not found, create it */
+ msg = find_message(stream_id, stream_seq_num, u_bit);
+
+ if (!msg) {
+ msg = g_new(sctp_frag_msg, 1);
+ msg->begins = NULL;
+ msg->ends = NULL;
+ msg->fragments = NULL;
+ msg->messages = NULL;
+ msg->next = NULL;
+ if (is_idata)
+ if (b_bit)
+ msg->ppi = ppi;
+ else
+ msg->ppi = 0;
+ else
+ msg->ppi = ppi;
+
+ key = g_new(frag_key, 1);
+ key->sport = sctp_info.sport;
+ key->dport = sctp_info.dport;
+ key->verification_tag = sctp_info.verification_tag;
+ key->stream_id = stream_id;
+ key->stream_seq_num = stream_seq_num;
+ key->u_bit = u_bit;
+
+ g_hash_table_insert(frag_table, key, msg);
+ } else {
+ if (b_bit)
+ msg->ppi = ppi;
+ }
+
+ /* lookup segment. if not found, create it */
+ fragment = find_fragment(tsn, stream_id, stream_seq_num, u_bit);
+
+ if (fragment) {
+ /* this fragment is already known.
+ * compare frame number to check if it's a duplicate
+ */
+ if (fragment->frame_num == pinfo->num) {
+ return fragment;
+ } else {
+ /* There already is a fragment having the same ports, v_tag,
+ * stream id, stream_seq_num and tsn but it appeared in a different
+ * frame, so it must be a duplicate fragment. Maybe a retransmission?
+ * Mark it as duplicate and return NULL.
+ *
+ * Note: This won't happen if TSN analysis is on: the caller will have
+ * detected the retransmission and not pass it to the reassembly code.
+ */
+ col_append_str(pinfo->cinfo, COL_INFO, "(Duplicate Message Fragment) ");
+
+ proto_tree_add_uint(tree, hf_sctp_duplicate, tvb, 0, 0, fragment->frame_num);
+ return NULL;
+ }
+ }
+
+ /* There is no point in storing a fragment with no data in it */
+ if (tvb_captured_length(tvb) == 0)
+ return NULL;
+
+ /* create new fragment */
+ fragment = g_new(sctp_fragment, 1);
+ fragment->frame_num = pinfo->num;
+ fragment->tsn = tsn;
+ fragment->len = tvb_captured_length(tvb);
+ fragment->ppi = msg->ppi;
+ fragment->next = NULL;
+ fragment->data = (unsigned char *)g_malloc (fragment->len);
+ tvb_memcpy(tvb, fragment->data, 0, fragment->len);
+
+ /* add new fragment to linked list. sort ascending by tsn */
+ if (!msg->fragments)
+ msg->fragments = fragment;
+ else {
+ if (msg->fragments->tsn > fragment->tsn) {
+ fragment->next = msg->fragments;
+ msg->fragments = fragment;
+ } else {
+ last_fragment = msg->fragments;
+ while (last_fragment->next &&
+ last_fragment->next->tsn < fragment->tsn)
+ last_fragment = last_fragment->next;
+
+ fragment->next = last_fragment->next;
+ last_fragment->next = fragment;
+ }
+ }
+
+
+ /* save begin or end if necessary */
+ if (b_bit && !e_bit) {
+ beginend = g_new(sctp_frag_be, 1);
+ beginend->fragment = fragment;
+ beginend->next = NULL;
+
+ /* add begin to linked list. sort descending by tsn */
+ if (!msg->begins)
+ msg->begins = beginend;
+ else {
+ if (msg->begins->fragment->tsn < beginend->fragment->tsn) {
+ beginend->next = msg->begins;
+ msg->begins = beginend;
+ } else {
+ last_beginend = msg->begins;
+ while (last_beginend->next &&
+ last_beginend->next->fragment->tsn > beginend->fragment->tsn)
+ last_beginend = last_beginend->next;
+
+ beginend->next = last_beginend->next;
+ last_beginend->next = beginend;
+ }
+ }
+
+ }
+
+ if (!b_bit && e_bit) {
+ beginend = g_new(sctp_frag_be, 1);
+ beginend->fragment = fragment;
+ beginend->next = NULL;
+
+ /* add end to linked list. sort ascending by tsn */
+ if (!msg->ends)
+ msg->ends = beginend;
+ else {
+ if (msg->ends->fragment->tsn > beginend->fragment->tsn) {
+ beginend->next = msg->ends;
+ msg->ends = beginend;
+ } else {
+ last_beginend = msg->ends;
+ while (last_beginend->next &&
+ last_beginend->next->fragment->tsn < beginend->fragment->tsn)
+ last_beginend = last_beginend->next;
+
+ beginend->next = last_beginend->next;
+ last_beginend->next = beginend;
+ }
+ }
+
+ }
+
+ return fragment;
+}
+
+static tvbuff_t*
+fragment_reassembly(tvbuff_t *tvb, sctp_fragment *fragment,
+ packet_info *pinfo, proto_tree *tree, guint16 stream_id,
+ guint32 stream_seq_num, guint8 u_bit)
+{
+ sctp_frag_msg *msg;
+ sctp_complete_msg *message, *last_message;
+ sctp_fragment *frag_i, *last_frag, *first_frag;
+ sctp_frag_be *begin, *end, *beginend;
+ guint32 len, offset = 0;
+ tvbuff_t *new_tvb = NULL;
+ proto_item *item;
+ proto_tree *ptree;
+
+ msg = find_message(stream_id, stream_seq_num, u_bit);
+
+ if (!msg) {
+ /* no message, we can't do anything */
+ return NULL;
+ }
+
+ /* check if fragment is part of an already reassembled message */
+ for (message = msg->messages;
+ message &&
+ !(message->begin <= fragment->tsn && message->end >= fragment->tsn) &&
+ !(message->begin > message->end &&
+ (message->begin <= fragment->tsn || message->end >= fragment->tsn));
+ message = message->next);
+
+ if (message) {
+ /* we found the reassembled message this fragment belongs to */
+ if (fragment == message->reassembled_in) {
+
+ /* this is the last fragment, create data source */
+ new_tvb = tvb_new_child_real_data(tvb, message->data, message->len, message->len);
+ add_new_data_source(pinfo, new_tvb, "Reassembled SCTP Message");
+
+ /* display reassembly info */
+ item = proto_tree_add_item(tree, hf_sctp_fragments, tvb, 0, -1, ENC_NA);
+ ptree = proto_item_add_subtree(item, ett_sctp_fragments);
+ proto_item_append_text(item, " (%u bytes, %u fragments): ",
+ message->len, message->end - message->begin + 1);
+
+ if (message->begin > message->end) {
+ for (frag_i = find_fragment(message->begin, stream_id, stream_seq_num, u_bit);
+ frag_i;
+ frag_i = frag_i->next) {
+
+ proto_tree_add_uint_format(ptree, hf_sctp_fragment, new_tvb, offset, frag_i->len,
+ frag_i->frame_num, "Frame: %u, payload: %u-%u (%u bytes)",
+ frag_i->frame_num, offset, offset + frag_i->len - 1, frag_i->len);
+ offset += frag_i->len;
+
+ mark_frame_as_depended_upon(pinfo->fd, frag_i->frame_num);
+ }
+
+ for (frag_i = msg->fragments;
+ frag_i && frag_i->tsn <= message->end;
+ frag_i = frag_i->next) {
+
+ proto_tree_add_uint_format(ptree, hf_sctp_fragment, new_tvb, offset, frag_i->len,
+ frag_i->frame_num, "Frame: %u, payload: %u-%u (%u bytes)",
+ frag_i->frame_num, offset, offset + frag_i->len - 1, frag_i->len);
+ offset += frag_i->len;
+
+ mark_frame_as_depended_upon(pinfo->fd, frag_i->frame_num);
+ }
+ } else {
+ for (frag_i = find_fragment(message->begin, stream_id, stream_seq_num, u_bit);
+ frag_i && frag_i->tsn <= message->end;
+ frag_i = frag_i->next) {
+
+ proto_tree_add_uint_format(ptree, hf_sctp_fragment, new_tvb, offset, frag_i->len,
+ frag_i->frame_num, "Frame: %u, payload: %u-%u (%u bytes)",
+ frag_i->frame_num, offset, offset + frag_i->len - 1, frag_i->len);
+ offset += frag_i->len;
+
+ mark_frame_as_depended_upon(pinfo->fd, frag_i->frame_num);
+ }
+ }
+
+ return new_tvb;
+ }
+
+ /* this is not the last fragment,
+ * so let the user know the frame where the reassembly is
+ */
+ col_append_str(pinfo->cinfo, COL_INFO, "(Message Fragment) ");
+
+ proto_tree_add_uint(tree, hf_sctp_reassembled_in, tvb, 0, 0, message->reassembled_in->frame_num);
+ return NULL;
+ }
+
+ /* this fragment has not been reassembled, yet
+ * check now if we can reassemble it
+ * at first look for the first and last tsn of the msg
+ */
+ for (begin = msg->begins;
+ begin && begin->fragment->tsn > fragment->tsn;
+ begin = begin->next);
+
+ /* in case begin still is null, set it to first (highest) begin
+ * maybe the message tsn restart at 0 in between
+ */
+ if (!begin)
+ begin = msg->begins;
+
+ for (end = msg->ends;
+ end && end->fragment->tsn < fragment->tsn;
+ end = end->next);
+
+ /* in case end still is null, set it to first (lowest) end
+ * maybe the message tsn restart at 0 in between
+ */
+ if (!end)
+ end = msg->ends;
+
+ if (!begin || !end || !msg->fragments ||
+ (begin->fragment->tsn > end->fragment->tsn && msg->fragments->tsn)) {
+ /* begin and end have not been collected, yet
+ * or there might be a tsn restart but the first fragment hasn't a tsn of 0
+ * just mark as fragment
+ */
+
+ col_append_str(pinfo->cinfo, COL_INFO, "(Message Fragment) ");
+
+ return NULL;
+ }
+
+ /* we found possible begin and end
+ * look for the first fragment and then try to get to the end
+ */
+ first_frag = begin->fragment;
+
+ /* while looking if all fragments are there
+ * we can calculate the overall length that
+ * we need in case of success
+ */
+ len = first_frag->len;
+
+ /* check if begin is past end
+ * this can happen if there has been a tsn restart
+ * or we just got the wrong begin and end
+ * so give it a try
+ */
+ if (begin->fragment->tsn > end->fragment->tsn) {
+ for (last_frag = first_frag, frag_i = first_frag->next;
+ frag_i && frag_i->tsn == (last_frag->tsn + 1);
+ last_frag = frag_i, frag_i = frag_i->next) len += frag_i->len;
+
+ /* check if we reached the last possible tsn
+ * if yes, restart and continue
+ */
+ if ((last_frag->tsn + 1)) {
+ /* there are just fragments missing */
+ col_append_str(pinfo->cinfo, COL_INFO, "(Message Fragment) ");
+
+ return NULL;
+ }
+
+ /* we got all fragments until the last possible tsn
+ * and the first is 0 if we got here
+ */
+
+ len += msg->fragments->len;
+ for (last_frag = msg->fragments, frag_i = last_frag->next;
+ frag_i && frag_i->tsn < end->fragment->tsn && frag_i->tsn == (last_frag->tsn + 1);
+ last_frag = frag_i, frag_i = frag_i->next) len += frag_i->len;
+
+ } else {
+ for (last_frag = first_frag, frag_i = first_frag->next;
+ frag_i && frag_i->tsn < end->fragment->tsn && frag_i->tsn == (last_frag->tsn + 1);
+ last_frag = frag_i, frag_i = frag_i->next) len += frag_i->len;
+ }
+
+ if (!frag_i || frag_i != end->fragment || frag_i->tsn != (last_frag->tsn + 1)) {
+ /* we need more fragments. just mark as fragment */
+ col_append_str(pinfo->cinfo, COL_INFO, "(Message Fragment) ");
+
+ return NULL;
+ }
+
+ /* ok, this message is complete, we can reassemble it
+ * but at first don't forget to add the length of the last fragment
+ */
+ len += frag_i->len;
+
+ message = wmem_new(wmem_file_scope(), sctp_complete_msg);
+ message->begin = begin->fragment->tsn;
+ message->end = end->fragment->tsn;
+ message->reassembled_in = fragment;
+ message->len = len;
+ message->data = (unsigned char *)wmem_alloc(wmem_file_scope(), len);
+ message->next = NULL;
+
+ /* now copy all fragments */
+ if (begin->fragment->tsn > end->fragment->tsn) {
+ /* a tsn restart has occurred */
+ for (frag_i = first_frag;
+ frag_i;
+ frag_i = frag_i->next) {
+
+ if (frag_i->len && frag_i->data)
+ memcpy(message->data + offset, frag_i->data, frag_i->len);
+ offset += frag_i->len;
+
+ /* release fragment data */
+ g_free(frag_i->data);
+ frag_i->data = NULL;
+ }
+
+ for (frag_i = msg->fragments;
+ frag_i && frag_i->tsn <= end->fragment->tsn;
+ frag_i = frag_i->next) {
+
+ if (frag_i->len && frag_i->data)
+ memcpy(message->data + offset, frag_i->data, frag_i->len);
+ offset += frag_i->len;
+
+ /* release fragment data */
+ g_free(frag_i->data);
+ frag_i->data = NULL;
+ }
+
+ } else {
+ for (frag_i = first_frag;
+ frag_i && frag_i->tsn <= end->fragment->tsn;
+ frag_i = frag_i->next) {
+
+ if (frag_i->len && frag_i->data)
+ memcpy(message->data + offset, frag_i->data, frag_i->len);
+ offset += frag_i->len;
+
+ /* release fragment data */
+ g_free(frag_i->data);
+ frag_i->data = NULL;
+ }
+ }
+
+ /* save message */
+ if (!msg->messages) {
+ msg->messages = message;
+ } else {
+ for (last_message = msg->messages;
+ last_message->next;
+ last_message = last_message->next);
+ last_message->next = message;
+ }
+
+ /* remove begin and end from list */
+ if (msg->begins == begin) {
+ msg->begins = begin->next;
+ } else {
+ for (beginend = msg->begins;
+ beginend && beginend->next != begin;
+ beginend = beginend->next);
+ if (beginend && beginend->next == begin)
+ beginend->next = begin->next;
+ }
+ g_free(begin);
+
+ if (msg->ends == end) {
+ msg->ends = end->next;
+ } else {
+ for (beginend = msg->ends;
+ beginend && beginend->next != end;
+ beginend = beginend->next);
+ if (beginend && beginend->next == end)
+ beginend->next = end->next;
+ }
+ g_free(end);
+
+ /* create data source */
+ new_tvb = tvb_new_child_real_data(tvb, message->data, len, len);
+ add_new_data_source(pinfo, new_tvb, "Reassembled SCTP Message");
+
+ /* display reassembly info */
+ item = proto_tree_add_item(tree, hf_sctp_fragments, tvb, 0, -1, ENC_NA);
+ ptree = proto_item_add_subtree(item, ett_sctp_fragments);
+ proto_item_append_text(item, " (%u bytes, %u fragments): ",
+ message->len, message->end - message->begin + 1);
+
+ if (message->begin > message->end) {
+ for (frag_i = find_fragment(message->begin, stream_id, stream_seq_num, u_bit);
+ frag_i;
+ frag_i = frag_i->next) {
+
+ proto_tree_add_uint_format(ptree, hf_sctp_fragment, new_tvb, offset, frag_i->len,
+ frag_i->frame_num, "Frame: %u, payload: %u-%u (%u bytes)",
+ frag_i->frame_num, offset, offset + frag_i->len - 1, frag_i->len);
+ offset += frag_i->len;
+
+ mark_frame_as_depended_upon(pinfo->fd, frag_i->frame_num);
+ }
+
+ for (frag_i = msg->fragments;
+ frag_i && frag_i->tsn <= message->end;
+ frag_i = frag_i->next) {
+
+ proto_tree_add_uint_format(ptree, hf_sctp_fragment, new_tvb, offset, frag_i->len,
+ frag_i->frame_num, "Frame: %u, payload: %u-%u (%u bytes)",
+ frag_i->frame_num, offset, offset + frag_i->len - 1, frag_i->len);
+ offset += frag_i->len;
+
+ mark_frame_as_depended_upon(pinfo->fd, frag_i->frame_num);
+ }
+ } else {
+ for (frag_i = find_fragment(message->begin, stream_id, stream_seq_num, u_bit);
+ frag_i && frag_i->tsn <= message->end;
+ frag_i = frag_i->next) {
+
+ proto_tree_add_uint_format(ptree, hf_sctp_fragment, new_tvb, offset, frag_i->len,
+ frag_i->frame_num, "Frame: %u, payload: %u-%u (%u bytes)",
+ frag_i->frame_num, offset, offset + frag_i->len - 1, frag_i->len);
+ offset += frag_i->len;
+
+ mark_frame_as_depended_upon(pinfo->fd, frag_i->frame_num);
+ }
+ }
+
+ /* it's not fragmented anymore */
+ pinfo->fragmented = FALSE;
+
+ return new_tvb;
+}
+
+static exp_pdu_data_t*
+create_exp_pdu_table(packet_info *pinfo, tvbuff_t *tvb, const char *table_name, guint32 table_value)
+{
+ exp_pdu_data_item_t exp_pdu_data_table_value = {
+ exp_pdu_data_dissector_table_num_value_size,
+ exp_pdu_data_dissector_table_num_value_populate_data,
+ GUINT_TO_POINTER(table_value)
+ };
+
+ const exp_pdu_data_item_t *sctp_exp_pdu_items[] = {
+ &exp_pdu_data_src_ip,
+ &exp_pdu_data_dst_ip,
+ &exp_pdu_data_port_type,
+ &exp_pdu_data_src_port,
+ &exp_pdu_data_dst_port,
+ &exp_pdu_data_orig_frame_num,
+ &exp_pdu_data_table_value,
+ NULL
+ };
+
+ exp_pdu_data_t *exp_pdu_data = export_pdu_create_tags(pinfo, table_name, EXP_PDU_TAG_DISSECTOR_TABLE_NAME, sctp_exp_pdu_items);
+
+ exp_pdu_data->tvb_captured_length = tvb_captured_length(tvb);
+ exp_pdu_data->tvb_reported_length = tvb_reported_length(tvb);
+ exp_pdu_data->pdu_tvb = tvb;
+
+ return exp_pdu_data;
+}
+
+static exp_pdu_data_t*
+create_exp_pdu_proto_name(packet_info *pinfo, tvbuff_t *tvb, const gchar *proto_name)
+{
+ exp_pdu_data_t *exp_pdu_data = export_pdu_create_common_tags(pinfo, proto_name, EXP_PDU_TAG_DISSECTOR_NAME);
+
+ exp_pdu_data->tvb_captured_length = tvb_captured_length(tvb);
+ exp_pdu_data->tvb_reported_length = tvb_reported_length(tvb);
+ exp_pdu_data->pdu_tvb = tvb;
+
+ return exp_pdu_data;
+}
+
+static void
+export_sctp_data_chunk(packet_info *pinfo, tvbuff_t *tvb, guint32 payload_proto_id,
+ const wmem_list_frame_t *frame)
+{
+ const gchar *proto_name = NULL;
+ exp_pdu_data_t *exp_pdu_data = NULL;
+
+ if (!have_tap_listener(exported_pdu_tap)) {
+ /* Do nothing when there is no export pdu tap listener */
+ return;
+ }
+
+ if (enable_ulp_dissection && frame) {
+ /* get the proto_name of the next frame */
+ proto_name = proto_get_protocol_filter_name(GPOINTER_TO_UINT(wmem_list_frame_data(frame)));
+ }
+
+ if (proto_name && (strcmp(proto_name, "data") != 0)) {
+ /* when we have proto_name and it is not "data" export using the proto_name */
+ exp_pdu_data = create_exp_pdu_proto_name(pinfo, tvb, proto_name);
+ } else if (payload_proto_id != 0) {
+ exp_pdu_data = create_exp_pdu_table(pinfo, tvb, "sctp.ppi", payload_proto_id);
+ } else if (pinfo->destport != 0) {
+ exp_pdu_data = create_exp_pdu_table(pinfo, tvb, "sctp.port", pinfo->destport);
+ } else if (pinfo->srcport != 0) {
+ exp_pdu_data = create_exp_pdu_table(pinfo, tvb, "sctp.port", pinfo->srcport);
+ } else {
+ /* do not export anything when none of the above fileds are available */
+ return;
+ }
+
+ tap_queue_packet(exported_pdu_tap, pinfo, exp_pdu_data);
+}
+
+static gboolean
+dissect_fragmented_payload(tvbuff_t *payload_tvb, packet_info *pinfo, proto_tree *tree,
+ proto_tree *chunk_tree, guint32 tsn, guint32 ppi, guint16 stream_id,
+ guint32 stream_seq_num, guint8 b_bit, guint8 e_bit, guint8 u_bit, gboolean is_idata)
+{
+ sctp_fragment *fragment;
+ tvbuff_t *new_tvb = NULL;
+
+ /*
+ * If this is a short frame, then we can't, and don't, do
+ * reassembly on it. We just give up.
+ */
+ if (tvb_reported_length(payload_tvb) > tvb_captured_length(payload_tvb))
+ return TRUE;
+
+ /* add fragment to list of known fragments. returns NULL if segment is a duplicate */
+ fragment = add_fragment(payload_tvb, pinfo, chunk_tree, tsn, stream_id, stream_seq_num, b_bit, e_bit, u_bit, ppi, is_idata);
+
+ if (fragment)
+ new_tvb = fragment_reassembly(payload_tvb, fragment, pinfo, chunk_tree, stream_id, stream_seq_num, u_bit);
+
+ /* pass reassembled data to next dissector, if possible */
+ if (new_tvb){
+ gboolean retval;
+ wmem_list_frame_t *cur = wmem_list_tail(pinfo->layers);
+
+ retval = dissect_payload(new_tvb, pinfo, tree, ppi);
+ export_sctp_data_chunk(pinfo, new_tvb, ppi, wmem_list_frame_next(cur));
+ return retval;
+ }
+
+ /* no reassembly done, do nothing */
+ return TRUE;
+}
+
+static const true_false_string sctp_data_chunk_e_bit_value = {
+ "Last segment",
+ "Not the last segment"
+};
+
+static const true_false_string sctp_data_chunk_b_bit_value = {
+ "First segment",
+ "Subsequent segment"
+};
+
+static const true_false_string sctp_data_chunk_u_bit_value = {
+ "Unordered delivery",
+ "Ordered delivery"
+};
+
+static const true_false_string sctp_data_chunk_i_bit_value = {
+ "Send SACK immediately",
+ "Possibly delay SACK"
+};
+
+static gboolean
+dissect_data_chunk(tvbuff_t *chunk_tvb,
+ guint16 chunk_length,
+ packet_info *pinfo,
+ proto_tree *tree,
+ proto_tree *chunk_tree,
+ proto_item *chunk_item,
+ proto_item *flags_item,
+ sctp_half_assoc_t *ha,
+ gboolean is_idata)
+{
+ guint number_of_ppid;
+ volatile guint32 payload_proto_id;
+ tvbuff_t *payload_tvb;
+ proto_tree *flags_tree;
+ guint8 oct, e_bit, b_bit, u_bit;
+ guint16 stream_id;
+ guint32 tsn, rawtsn, ppid, stream_seq_num = 0;
+ proto_item *tsn_item = NULL;
+ gboolean call_subdissector = FALSE;
+ gboolean is_retransmission;
+ guint16 header_length;
+ guint16 payload_offset;
+
+ static int* const chunk_flags[] = {
+ &hf_data_chunk_i_bit,
+ &hf_data_chunk_u_bit,
+ &hf_data_chunk_b_bit,
+ &hf_data_chunk_e_bit,
+ NULL
+ };
+
+ if (is_idata) {
+ if (chunk_length < I_DATA_CHUNK_HEADER_LENGTH) {
+ proto_item_append_text(chunk_item, ", bogus chunk length %u < %u)", chunk_length, I_DATA_CHUNK_HEADER_LENGTH);
+ return TRUE;
+ }
+ payload_proto_id = tvb_get_ntohl(chunk_tvb, I_DATA_CHUNK_PAYLOAD_PROTOCOL_ID_OFFSET);
+ } else {
+ if (chunk_length < DATA_CHUNK_HEADER_LENGTH) {
+ proto_item_append_text(chunk_item, ", bogus chunk length %u < %u)", chunk_length, DATA_CHUNK_HEADER_LENGTH);
+ return TRUE;
+ }
+ payload_proto_id = tvb_get_ntohl(chunk_tvb, DATA_CHUNK_PAYLOAD_PROTOCOL_ID_OFFSET);
+ }
+
+ /* insert the PPID in the pinfo structure if it is not already there and there is still room */
+ for(number_of_ppid = 0; number_of_ppid < MAX_NUMBER_OF_PPIDS; number_of_ppid++) {
+ void *tmp = p_get_proto_data(pinfo->pool, pinfo, proto_sctp, number_of_ppid);
+ ppid = GPOINTER_TO_UINT(tmp);
+ if ((ppid == LAST_PPID) || (ppid == payload_proto_id))
+ break;
+ }
+ if ((number_of_ppid < MAX_NUMBER_OF_PPIDS) && (ppid == LAST_PPID))
+ p_add_proto_data(pinfo->pool, pinfo, proto_sctp, number_of_ppid, GUINT_TO_POINTER(payload_proto_id));
+
+ oct = tvb_get_guint8(chunk_tvb, CHUNK_FLAGS_OFFSET);
+ e_bit = oct & SCTP_DATA_CHUNK_E_BIT;
+ b_bit = oct & SCTP_DATA_CHUNK_B_BIT;
+ u_bit = oct & SCTP_DATA_CHUNK_U_BIT;
+
+ tsn = rawtsn = tvb_get_ntohl(chunk_tvb, DATA_CHUNK_TSN_OFFSET);
+ if ((show_relative_tsns) && (ha)) {
+ if (!ha->started) {
+ ha->first_tsn = tsn;
+ ha->started = TRUE;
+ }
+ tsn -= ha->first_tsn;
+ }
+
+ col_append_fstr(pinfo->cinfo, COL_INFO, "(TSN=%" PRIu32 ") ", tsn);
+
+ if (chunk_tree) {
+ if (is_idata)
+ proto_item_set_len(chunk_item, I_DATA_CHUNK_HEADER_LENGTH);
+ else
+ proto_item_set_len(chunk_item, DATA_CHUNK_HEADER_LENGTH);
+
+ flags_tree = proto_item_add_subtree(flags_item, ett_sctp_data_chunk_flags);
+ proto_tree_add_bitmask_list(flags_tree, chunk_tvb, CHUNK_FLAGS_OFFSET, CHUNK_FLAGS_LENGTH, chunk_flags, ENC_NA);
+ if((show_relative_tsns) && (ha)) {
+ tsn_item = proto_tree_add_uint(chunk_tree, hf_data_chunk_tsn, chunk_tvb, DATA_CHUNK_TSN_OFFSET, DATA_CHUNK_TSN_LENGTH, tsn);
+ proto_tree_add_item(chunk_tree, hf_data_chunk_tsn_raw, chunk_tvb, DATA_CHUNK_TSN_OFFSET, DATA_CHUNK_TSN_LENGTH, ENC_BIG_ENDIAN);
+ }
+ else {
+ tsn_item = proto_tree_add_item(chunk_tree, hf_data_chunk_tsn_raw, chunk_tvb, DATA_CHUNK_TSN_OFFSET, DATA_CHUNK_TSN_LENGTH, ENC_BIG_ENDIAN);
+ }
+ proto_tree_add_item(chunk_tree, hf_data_chunk_stream_id, chunk_tvb, DATA_CHUNK_STREAM_ID_OFFSET, DATA_CHUNK_STREAM_ID_LENGTH, ENC_BIG_ENDIAN);
+ if (is_idata) {
+ proto_tree_add_item(chunk_tree, hf_idata_chunk_reserved, chunk_tvb, I_DATA_CHUNK_RESERVED_OFFSET, I_DATA_CHUNK_RESERVED_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(chunk_tree, hf_idata_chunk_mid, chunk_tvb, I_DATA_CHUNK_MID_OFFSET, I_DATA_CHUNK_MID_LENGTH, ENC_BIG_ENDIAN);
+ if (b_bit)
+ proto_tree_add_item(chunk_tree, hf_data_chunk_payload_proto_id, chunk_tvb, I_DATA_CHUNK_PAYLOAD_PROTOCOL_ID_OFFSET, I_DATA_CHUNK_PAYLOAD_PROTOCOL_ID_LENGTH, ENC_BIG_ENDIAN);
+ else
+ proto_tree_add_item(chunk_tree, hf_idata_chunk_fsn, chunk_tvb, I_DATA_CHUNK_FSN_OFFSET, I_DATA_CHUNK_FSN_LENGTH, ENC_BIG_ENDIAN);
+ } else {
+ proto_tree_add_item(chunk_tree, hf_data_chunk_stream_seq_number, chunk_tvb, DATA_CHUNK_STREAM_SEQ_NUMBER_OFFSET, DATA_CHUNK_STREAM_SEQ_NUMBER_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(chunk_tree, hf_data_chunk_payload_proto_id, chunk_tvb, DATA_CHUNK_PAYLOAD_PROTOCOL_ID_OFFSET, DATA_CHUNK_PAYLOAD_PROTOCOL_ID_LENGTH, ENC_BIG_ENDIAN);
+ }
+ proto_item_append_text(chunk_item, " (%s, ", (u_bit) ? "unordered" : "ordered");
+ if (b_bit) {
+ if (e_bit)
+ proto_item_append_text(chunk_item, "complete");
+ else
+ proto_item_append_text(chunk_item, "first");
+ } else {
+ if (e_bit)
+ proto_item_append_text(chunk_item, "last");
+ else
+ proto_item_append_text(chunk_item, "middle");
+ }
+
+ if (is_idata) {
+ if (b_bit)
+ proto_item_append_text(chunk_item, " segment, TSN: %u, SID: %u, MID: %u, payload length: %u byte%s)",
+ tsn,
+ tvb_get_ntohs(chunk_tvb, DATA_CHUNK_STREAM_ID_OFFSET),
+ tvb_get_ntohl(chunk_tvb, I_DATA_CHUNK_MID_OFFSET),
+ chunk_length - I_DATA_CHUNK_HEADER_LENGTH, plurality(chunk_length - I_DATA_CHUNK_HEADER_LENGTH, "", "s"));
+ else
+ proto_item_append_text(chunk_item, " segment, TSN: %u, SID: %u, MID: %u, FSN: %u, payload length: %u byte%s)",
+ tsn,
+ tvb_get_ntohs(chunk_tvb, DATA_CHUNK_STREAM_ID_OFFSET),
+ tvb_get_ntohl(chunk_tvb, I_DATA_CHUNK_MID_OFFSET),
+ tvb_get_ntohl(chunk_tvb, I_DATA_CHUNK_FSN_OFFSET),
+ chunk_length - I_DATA_CHUNK_HEADER_LENGTH, plurality(chunk_length - I_DATA_CHUNK_HEADER_LENGTH, "", "s"));
+ } else
+ proto_item_append_text(chunk_item, " segment, TSN: %u, SID: %u, SSN: %u, PPID: %u, payload length: %u byte%s)",
+ tsn,
+ tvb_get_ntohs(chunk_tvb, DATA_CHUNK_STREAM_ID_OFFSET),
+ tvb_get_ntohs(chunk_tvb, DATA_CHUNK_STREAM_SEQ_NUMBER_OFFSET),
+ payload_proto_id,
+ chunk_length - DATA_CHUNK_HEADER_LENGTH, plurality(chunk_length - DATA_CHUNK_HEADER_LENGTH, "", "s"));
+ }
+
+ is_retransmission = sctp_tsn(pinfo, chunk_tvb, tsn_item, ha, rawtsn);
+
+ if (is_idata) {
+ header_length = I_DATA_CHUNK_HEADER_LENGTH;
+ payload_offset = I_DATA_CHUNK_PAYLOAD_OFFSET;
+ } else {
+ header_length = DATA_CHUNK_HEADER_LENGTH;
+ payload_offset = DATA_CHUNK_PAYLOAD_OFFSET;
+ }
+ payload_tvb = tvb_new_subset_length_caplen(chunk_tvb, payload_offset,
+ MIN(chunk_length - header_length, tvb_captured_length_remaining(chunk_tvb, payload_offset)),
+ MIN(chunk_length - header_length, tvb_reported_length_remaining(chunk_tvb, payload_offset)));
+
+ /* Is this a fragment? */
+ if (b_bit && e_bit) {
+ /* No - just call the subdissector. */
+ if (!is_retransmission)
+ call_subdissector = TRUE;
+ } else {
+ /* Yes. */
+ pinfo->fragmented = TRUE;
+
+ /* if reassembly is off just mark as fragment for next dissector and proceed */
+ if (!use_reassembly)
+ {
+ /* Don't pass on non-first fragments since the next dissector will
+ * almost certainly not understand the data.
+ */
+ if (b_bit) {
+ if (!is_retransmission)
+ call_subdissector = TRUE;
+ } else
+ return FALSE;
+ }
+
+ }
+
+ if (call_subdissector) {
+ /* This isn't a fragment or reassembly is off and it's the first fragment */
+
+ volatile gboolean retval = FALSE;
+ wmem_list_frame_t *cur = wmem_list_tail(pinfo->layers);
+
+ TRY {
+ retval = dissect_payload(payload_tvb, pinfo, tree, payload_proto_id);
+ }
+ CATCH_NONFATAL_ERRORS {
+ /*
+ * Somebody threw an exception that means that there was a problem
+ * dissecting the payload; that means that a dissector was found,
+ * so we don't need to dissect the payload as data or update the
+ * protocol or info columns.
+ *
+ * Just show the exception and then continue dissecting chunks.
+ */
+ show_exception(payload_tvb, pinfo, tree, EXCEPT_CODE, GET_MESSAGE);
+ }
+ ENDTRY;
+
+ export_sctp_data_chunk(pinfo, payload_tvb, payload_proto_id, wmem_list_frame_next(cur));
+ return retval;
+
+ } else if (is_retransmission) {
+ col_append_str(pinfo->cinfo, COL_INFO, "(retransmission) ");
+ return FALSE;
+ } else {
+
+ /* The logic above should ensure this... */
+ DISSECTOR_ASSERT(use_reassembly);
+
+ stream_id = tvb_get_ntohs(chunk_tvb, DATA_CHUNK_STREAM_ID_OFFSET);
+ if (is_idata) {
+ /* The stream_seq_num variable is used to hold the MID, the tsn variable holds the FSN*/
+ stream_seq_num = tvb_get_ntohl(chunk_tvb, I_DATA_CHUNK_MID_OFFSET);
+ if (b_bit) {
+ tsn = 0;
+ } else {
+ tsn = tvb_get_ntohl(chunk_tvb, I_DATA_CHUNK_FSN_OFFSET);
+ payload_proto_id = 0;
+ }
+ } else {
+ /* if unordered set stream_seq_num to 0 for easier handling */
+ if (u_bit)
+ stream_seq_num = 0;
+ else
+ stream_seq_num = tvb_get_ntohs(chunk_tvb, DATA_CHUNK_STREAM_SEQ_NUMBER_OFFSET);
+ }
+ /* start reassembly */
+ return dissect_fragmented_payload(payload_tvb, pinfo, tree, chunk_tree, tsn, payload_proto_id, stream_id, stream_seq_num, b_bit, e_bit, u_bit, is_idata);
+ }
+
+}
+
+#define INIT_CHUNK_INITIATE_TAG_LENGTH 4
+#define INIT_CHUNK_ADV_REC_WINDOW_CREDIT_LENGTH 4
+#define INIT_CHUNK_NUMBER_OF_OUTBOUND_STREAMS_LENGTH 2
+#define INIT_CHUNK_NUMBER_OF_INBOUND_STREAMS_LENGTH 2
+#define INIT_CHUNK_INITIAL_TSN_LENGTH 4
+#define INIT_CHUNK_FIXED_PARAMTERS_LENGTH (CHUNK_HEADER_LENGTH + \
+ INIT_CHUNK_INITIATE_TAG_LENGTH + \
+ INIT_CHUNK_ADV_REC_WINDOW_CREDIT_LENGTH + \
+ INIT_CHUNK_NUMBER_OF_OUTBOUND_STREAMS_LENGTH + \
+ INIT_CHUNK_NUMBER_OF_INBOUND_STREAMS_LENGTH + \
+ INIT_CHUNK_INITIAL_TSN_LENGTH)
+
+#define INIT_CHUNK_INITIATE_TAG_OFFSET CHUNK_VALUE_OFFSET
+#define INIT_CHUNK_ADV_REC_WINDOW_CREDIT_OFFSET (INIT_CHUNK_INITIATE_TAG_OFFSET + \
+ INIT_CHUNK_INITIATE_TAG_LENGTH )
+#define INIT_CHUNK_NUMBER_OF_OUTBOUND_STREAMS_OFFSET (INIT_CHUNK_ADV_REC_WINDOW_CREDIT_OFFSET + \
+ INIT_CHUNK_ADV_REC_WINDOW_CREDIT_LENGTH )
+#define INIT_CHUNK_NUMBER_OF_INBOUND_STREAMS_OFFSET (INIT_CHUNK_NUMBER_OF_OUTBOUND_STREAMS_OFFSET + \
+ INIT_CHUNK_NUMBER_OF_OUTBOUND_STREAMS_LENGTH )
+#define INIT_CHUNK_INITIAL_TSN_OFFSET (INIT_CHUNK_NUMBER_OF_INBOUND_STREAMS_OFFSET + \
+ INIT_CHUNK_NUMBER_OF_INBOUND_STREAMS_LENGTH )
+#define INIT_CHUNK_VARIABLE_LENGTH_PARAMETER_OFFSET (INIT_CHUNK_INITIAL_TSN_OFFSET + \
+ INIT_CHUNK_INITIAL_TSN_LENGTH )
+
+static void
+dissect_init_chunk(tvbuff_t *chunk_tvb, guint16 chunk_length, packet_info *pinfo, proto_tree *chunk_tree, proto_item *chunk_item)
+{
+ tvbuff_t *parameters_tvb;
+ proto_item *hidden_item;
+
+ if (chunk_length < INIT_CHUNK_FIXED_PARAMTERS_LENGTH) {
+ proto_item_append_text(chunk_item, ", bogus chunk length %u < %u)", chunk_length, INIT_CHUNK_FIXED_PARAMTERS_LENGTH);
+ return;
+ }
+
+ if (chunk_tree) {
+ /* handle fixed parameters */
+ proto_tree_add_item(chunk_tree, hf_init_chunk_initiate_tag, chunk_tvb, INIT_CHUNK_INITIATE_TAG_OFFSET, INIT_CHUNK_INITIATE_TAG_LENGTH, ENC_BIG_ENDIAN);
+ hidden_item = proto_tree_add_item(chunk_tree, hf_initiate_tag, chunk_tvb, INIT_CHUNK_INITIATE_TAG_OFFSET, INIT_CHUNK_INITIATE_TAG_LENGTH, ENC_BIG_ENDIAN);
+ proto_item_set_hidden(hidden_item);
+ proto_tree_add_item(chunk_tree, hf_init_chunk_adv_rec_window_credit, chunk_tvb, INIT_CHUNK_ADV_REC_WINDOW_CREDIT_OFFSET, INIT_CHUNK_ADV_REC_WINDOW_CREDIT_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(chunk_tree, hf_init_chunk_number_of_outbound_streams, chunk_tvb, INIT_CHUNK_NUMBER_OF_OUTBOUND_STREAMS_OFFSET, INIT_CHUNK_NUMBER_OF_OUTBOUND_STREAMS_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(chunk_tree, hf_init_chunk_number_of_inbound_streams, chunk_tvb, INIT_CHUNK_NUMBER_OF_INBOUND_STREAMS_OFFSET, INIT_CHUNK_NUMBER_OF_INBOUND_STREAMS_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(chunk_tree, hf_init_chunk_initial_tsn, chunk_tvb, INIT_CHUNK_INITIAL_TSN_OFFSET, INIT_CHUNK_INITIAL_TSN_LENGTH, ENC_BIG_ENDIAN);
+
+ proto_item_append_text(chunk_item, " (Outbound streams: %u, inbound streams: %u)",
+ tvb_get_ntohs(chunk_tvb, INIT_CHUNK_NUMBER_OF_OUTBOUND_STREAMS_OFFSET),
+ tvb_get_ntohs(chunk_tvb, INIT_CHUNK_NUMBER_OF_INBOUND_STREAMS_OFFSET));
+ }
+
+ /* handle variable parameters */
+ chunk_length -= INIT_CHUNK_FIXED_PARAMTERS_LENGTH;
+ parameters_tvb = tvb_new_subset_length_caplen(chunk_tvb, INIT_CHUNK_VARIABLE_LENGTH_PARAMETER_OFFSET,
+ MIN(chunk_length, tvb_captured_length_remaining(chunk_tvb, INIT_CHUNK_VARIABLE_LENGTH_PARAMETER_OFFSET)),
+ MIN(chunk_length, tvb_reported_length_remaining(chunk_tvb, INIT_CHUNK_VARIABLE_LENGTH_PARAMETER_OFFSET)));
+ dissect_parameters(parameters_tvb, pinfo, chunk_tree, NULL, TRUE);
+}
+
+static void
+dissect_init_ack_chunk(tvbuff_t *chunk_tvb, guint16 chunk_length, packet_info *pinfo, proto_tree *chunk_tree, proto_item *chunk_item)
+{
+ tvbuff_t *parameters_tvb;
+ proto_item *hidden_item;
+
+ if (chunk_length < INIT_CHUNK_FIXED_PARAMTERS_LENGTH) {
+ proto_item_append_text(chunk_item, ", bogus chunk length %u < %u)",
+ chunk_length,
+ INIT_CHUNK_FIXED_PARAMTERS_LENGTH);
+ return;
+ }
+ if (chunk_tree) {
+ /* handle fixed parameters */
+ proto_tree_add_item(chunk_tree, hf_initack_chunk_initiate_tag, chunk_tvb, INIT_CHUNK_INITIATE_TAG_OFFSET, INIT_CHUNK_INITIATE_TAG_LENGTH, ENC_BIG_ENDIAN);
+ hidden_item = proto_tree_add_item(chunk_tree, hf_initiate_tag, chunk_tvb, INIT_CHUNK_INITIATE_TAG_OFFSET, INIT_CHUNK_INITIATE_TAG_LENGTH, ENC_BIG_ENDIAN);
+ proto_item_set_hidden(hidden_item);
+ proto_tree_add_item(chunk_tree, hf_initack_chunk_adv_rec_window_credit, chunk_tvb, INIT_CHUNK_ADV_REC_WINDOW_CREDIT_OFFSET, INIT_CHUNK_ADV_REC_WINDOW_CREDIT_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(chunk_tree, hf_initack_chunk_number_of_outbound_streams, chunk_tvb, INIT_CHUNK_NUMBER_OF_OUTBOUND_STREAMS_OFFSET, INIT_CHUNK_NUMBER_OF_OUTBOUND_STREAMS_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(chunk_tree, hf_initack_chunk_number_of_inbound_streams, chunk_tvb, INIT_CHUNK_NUMBER_OF_INBOUND_STREAMS_OFFSET, INIT_CHUNK_NUMBER_OF_INBOUND_STREAMS_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(chunk_tree, hf_initack_chunk_initial_tsn, chunk_tvb, INIT_CHUNK_INITIAL_TSN_OFFSET, INIT_CHUNK_INITIAL_TSN_LENGTH, ENC_BIG_ENDIAN);
+
+ proto_item_append_text(chunk_item, " (Outbound streams: %u, inbound streams: %u)",
+ tvb_get_ntohs(chunk_tvb, INIT_CHUNK_NUMBER_OF_OUTBOUND_STREAMS_OFFSET),
+ tvb_get_ntohs(chunk_tvb, INIT_CHUNK_NUMBER_OF_INBOUND_STREAMS_OFFSET));
+ }
+ /* handle variable paramters */
+ chunk_length -= INIT_CHUNK_FIXED_PARAMTERS_LENGTH;
+ parameters_tvb = tvb_new_subset_length_caplen(chunk_tvb, INIT_CHUNK_VARIABLE_LENGTH_PARAMETER_OFFSET,
+ MIN(chunk_length, tvb_captured_length_remaining(chunk_tvb, INIT_CHUNK_VARIABLE_LENGTH_PARAMETER_OFFSET)),
+ MIN(chunk_length, tvb_reported_length_remaining(chunk_tvb, INIT_CHUNK_VARIABLE_LENGTH_PARAMETER_OFFSET)));
+ dissect_parameters(parameters_tvb, pinfo, chunk_tree, NULL, TRUE);
+}
+
+#define SCTP_SACK_CHUNK_NS_BIT 0x01
+#define SACK_CHUNK_CUMULATIVE_TSN_ACK_LENGTH 4
+#define SACK_CHUNK_ADV_REC_WINDOW_CREDIT_LENGTH 4
+#define SACK_CHUNK_NUMBER_OF_GAP_BLOCKS_LENGTH 2
+#define SACK_CHUNK_NUMBER_OF_DUP_TSNS_LENGTH 2
+#define SACK_CHUNK_GAP_BLOCK_LENGTH 4
+#define SACK_CHUNK_GAP_BLOCK_START_LENGTH 2
+#define SACK_CHUNK_GAP_BLOCK_END_LENGTH 2
+#define SACK_CHUNK_DUP_TSN_LENGTH 4
+
+#define SACK_CHUNK_CUMULATIVE_TSN_ACK_OFFSET (CHUNK_VALUE_OFFSET + 0)
+#define SACK_CHUNK_ADV_REC_WINDOW_CREDIT_OFFSET (SACK_CHUNK_CUMULATIVE_TSN_ACK_OFFSET + \
+ SACK_CHUNK_CUMULATIVE_TSN_ACK_LENGTH)
+#define SACK_CHUNK_NUMBER_OF_GAP_BLOCKS_OFFSET (SACK_CHUNK_ADV_REC_WINDOW_CREDIT_OFFSET + \
+ SACK_CHUNK_ADV_REC_WINDOW_CREDIT_LENGTH)
+#define SACK_CHUNK_NUMBER_OF_DUP_TSNS_OFFSET (SACK_CHUNK_NUMBER_OF_GAP_BLOCKS_OFFSET + \
+ SACK_CHUNK_NUMBER_OF_GAP_BLOCKS_LENGTH)
+#define SACK_CHUNK_GAP_BLOCK_OFFSET (SACK_CHUNK_NUMBER_OF_DUP_TSNS_OFFSET + \
+ SACK_CHUNK_NUMBER_OF_DUP_TSNS_LENGTH)
+
+
+static void
+dissect_sack_chunk(packet_info *pinfo, tvbuff_t *chunk_tvb, proto_tree *chunk_tree, proto_item *chunk_item, proto_item *flags_item, sctp_half_assoc_t *ha)
+{
+ guint16 number_of_gap_blocks, number_of_dup_tsns;
+ guint16 gap_block_number, dup_tsn_number, start, end;
+ gint gap_block_offset, dup_tsn_offset;
+ guint32 cum_tsn_ack;
+ proto_tree *block_tree;
+ proto_tree *flags_tree;
+ proto_item *ctsa_item;
+ proto_item *a_rwnd_item;
+ proto_tree *acks_tree;
+ guint32 tsns_gap_acked = 0;
+ guint32 a_rwnd;
+ guint16 last_end;
+
+ cum_tsn_ack = tvb_get_ntohl(chunk_tvb, SACK_CHUNK_CUMULATIVE_TSN_ACK_OFFSET);
+ if((show_relative_tsns) && (ha) && (ha->peer)) {
+ cum_tsn_ack -= ha->peer->first_tsn;
+ }
+
+ flags_tree = proto_item_add_subtree(flags_item, ett_sctp_sack_chunk_flags);
+ proto_tree_add_item(flags_tree, hf_sack_chunk_ns, chunk_tvb, CHUNK_FLAGS_OFFSET, CHUNK_FLAGS_LENGTH, ENC_BIG_ENDIAN);
+ if((show_relative_tsns) && (ha) && (ha->peer)) {
+ ctsa_item = proto_tree_add_uint(chunk_tree, hf_sack_chunk_cumulative_tsn_ack, chunk_tvb, SACK_CHUNK_CUMULATIVE_TSN_ACK_OFFSET, SACK_CHUNK_CUMULATIVE_TSN_ACK_LENGTH, cum_tsn_ack);
+ proto_tree_add_item(chunk_tree, hf_sack_chunk_cumulative_tsn_ack_raw, chunk_tvb, SACK_CHUNK_CUMULATIVE_TSN_ACK_OFFSET, SACK_CHUNK_CUMULATIVE_TSN_ACK_LENGTH, ENC_BIG_ENDIAN);
+ }
+ else {
+ ctsa_item = proto_tree_add_item(chunk_tree, hf_sack_chunk_cumulative_tsn_ack_raw, chunk_tvb, SACK_CHUNK_CUMULATIVE_TSN_ACK_OFFSET, SACK_CHUNK_CUMULATIVE_TSN_ACK_LENGTH, ENC_BIG_ENDIAN);
+ }
+ a_rwnd_item = proto_tree_add_item(chunk_tree, hf_sack_chunk_adv_rec_window_credit, chunk_tvb, SACK_CHUNK_ADV_REC_WINDOW_CREDIT_OFFSET, SACK_CHUNK_ADV_REC_WINDOW_CREDIT_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(chunk_tree, hf_sack_chunk_number_of_gap_blocks, chunk_tvb, SACK_CHUNK_NUMBER_OF_GAP_BLOCKS_OFFSET, SACK_CHUNK_NUMBER_OF_GAP_BLOCKS_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(chunk_tree, hf_sack_chunk_number_of_dup_tsns, chunk_tvb, SACK_CHUNK_NUMBER_OF_DUP_TSNS_OFFSET, SACK_CHUNK_NUMBER_OF_DUP_TSNS_LENGTH, ENC_BIG_ENDIAN);
+
+ a_rwnd = tvb_get_ntohl(chunk_tvb, SACK_CHUNK_ADV_REC_WINDOW_CREDIT_OFFSET);
+ if (a_rwnd == 0)
+ expert_add_info(pinfo, a_rwnd_item, &ei_sctp_sack_chunk_adv_rec_window_credit);
+
+
+ /* handle the gap acknowledgement blocks */
+ number_of_gap_blocks = tvb_get_ntohs(chunk_tvb, SACK_CHUNK_NUMBER_OF_GAP_BLOCKS_OFFSET);
+ gap_block_offset = SACK_CHUNK_GAP_BLOCK_OFFSET;
+
+ acks_tree = proto_item_add_subtree(ctsa_item,ett_sctp_ack);
+ sctp_ack_block(pinfo, ha, chunk_tvb, acks_tree, NULL, cum_tsn_ack);
+
+ last_end = 0;
+ for(gap_block_number = 0; gap_block_number < number_of_gap_blocks; gap_block_number++) {
+ proto_item *pi;
+ proto_tree *pt;
+ guint32 tsn_start;
+
+ start = tvb_get_ntohs(chunk_tvb, gap_block_offset);
+ end = tvb_get_ntohs(chunk_tvb, gap_block_offset + SACK_CHUNK_GAP_BLOCK_START_LENGTH);
+ tsn_start = cum_tsn_ack + start;
+
+ block_tree = proto_tree_add_subtree_format(chunk_tree, chunk_tvb, gap_block_offset, SACK_CHUNK_GAP_BLOCK_LENGTH,
+ ett_sctp_sack_chunk_gap_block, NULL, "Gap Acknowledgement for TSN %u to %u", cum_tsn_ack + start, cum_tsn_ack + end);
+
+ pi = proto_tree_add_item(block_tree, hf_sack_chunk_gap_block_start, chunk_tvb, gap_block_offset, SACK_CHUNK_GAP_BLOCK_START_LENGTH, ENC_BIG_ENDIAN);
+ pt = proto_item_add_subtree(pi, ett_sctp_sack_chunk_gap_block_start);
+ pi = proto_tree_add_uint(pt, hf_sack_chunk_gap_block_start_tsn,
+ chunk_tvb, gap_block_offset,SACK_CHUNK_GAP_BLOCK_START_LENGTH, cum_tsn_ack + start);
+ proto_item_set_generated(pi);
+
+ pi = proto_tree_add_item(block_tree, hf_sack_chunk_gap_block_end, chunk_tvb, gap_block_offset + SACK_CHUNK_GAP_BLOCK_START_LENGTH, SACK_CHUNK_GAP_BLOCK_END_LENGTH, ENC_BIG_ENDIAN);
+ pt = proto_item_add_subtree(pi, ett_sctp_sack_chunk_gap_block_end);
+ pi = proto_tree_add_uint(pt, hf_sack_chunk_gap_block_end_tsn, chunk_tvb,
+ gap_block_offset + SACK_CHUNK_GAP_BLOCK_START_LENGTH, SACK_CHUNK_GAP_BLOCK_END_LENGTH, cum_tsn_ack + end);
+ proto_item_set_generated(pi);
+
+ sctp_ack_block(pinfo, ha, chunk_tvb, block_tree, &tsn_start, cum_tsn_ack + end);
+ gap_block_offset += SACK_CHUNK_GAP_BLOCK_LENGTH;
+
+ tsns_gap_acked += (end+1 - start);
+
+ /* Check validity */
+ if (start > end) {
+ expert_add_info(pinfo, pi, &ei_sctp_sack_chunk_gap_block_malformed);
+ }
+ if (last_end > start) {
+ expert_add_info(pinfo, pi, &ei_sctp_sack_chunk_gap_block_out_of_order);
+ }
+ last_end = end;
+ }
+
+ if(last_end == 0) {
+ /* No GapAck -> only show CumAck */
+ col_append_fstr(pinfo->cinfo, COL_INFO,
+ "(Ack=%" PRIu32 ", Arwnd=%" PRIu32 ") ",
+ cum_tsn_ack, a_rwnd);
+ }
+ else {
+ /* Show CumAck + highest GapAck */
+ col_append_fstr(pinfo->cinfo, COL_INFO,
+ "(Ack=%" PRIu32 "+%" PRIu32 ", Arwnd=%" PRIu32 ") ",
+ cum_tsn_ack, last_end, a_rwnd);
+ }
+
+ if (tsns_gap_acked) {
+ proto_item *pi;
+
+ pi = proto_tree_add_uint(chunk_tree, hf_sack_chunk_number_tsns_gap_acked, chunk_tvb, 0, 0, tsns_gap_acked);
+ proto_item_set_generated(pi);
+
+ /* If there are a huge number of GAP ACKs, warn the user. 100 is a random
+ * number: it could be tuned.
+ */
+ if (tsns_gap_acked > 100)
+ expert_add_info(pinfo, pi, &ei_sctp_sack_chunk_number_tsns_gap_acked_100);
+
+ }
+
+
+ /* handle the duplicate TSNs */
+ number_of_dup_tsns = tvb_get_ntohs(chunk_tvb, SACK_CHUNK_NUMBER_OF_DUP_TSNS_OFFSET);
+ dup_tsn_offset = SACK_CHUNK_GAP_BLOCK_OFFSET + number_of_gap_blocks * SACK_CHUNK_GAP_BLOCK_LENGTH;
+ for(dup_tsn_number = 0; dup_tsn_number < number_of_dup_tsns; dup_tsn_number++) {
+ proto_tree_add_item(chunk_tree, hf_sack_chunk_duplicate_tsn, chunk_tvb, dup_tsn_offset, SACK_CHUNK_DUP_TSN_LENGTH, ENC_BIG_ENDIAN);
+ dup_tsn_offset += SACK_CHUNK_DUP_TSN_LENGTH;
+ }
+
+ proto_item_append_text(chunk_item, " (Cumulative TSN: %u, a_rwnd: %u, gaps: %u, duplicate TSNs: %u)",
+ tvb_get_ntohl(chunk_tvb, SACK_CHUNK_CUMULATIVE_TSN_ACK_OFFSET),
+ a_rwnd,
+ number_of_gap_blocks, number_of_dup_tsns);
+}
+
+/* NE: Dissect nr-sack chunk */
+#define SCTP_NR_SACK_CHUNK_NS_BIT 0x01
+#define NR_SACK_CHUNK_CUMULATIVE_TSN_ACK_LENGTH 4
+#define NR_SACK_CHUNK_ADV_REC_WINDOW_CREDIT_LENGTH 4
+#define NR_SACK_CHUNK_NUMBER_OF_GAP_BLOCKS_LENGTH 2
+#define NR_SACK_CHUNK_NUMBER_OF_NR_GAP_BLOCKS_LENGTH 2
+#define NR_SACK_CHUNK_NUMBER_OF_DUP_TSNS_LENGTH 2
+#define NR_SACK_CHUNK_RESERVED_LENGTH 2
+#define NR_SACK_CHUNK_GAP_BLOCK_LENGTH 4
+#define NR_SACK_CHUNK_GAP_BLOCK_START_LENGTH 2
+#define NR_SACK_CHUNK_GAP_BLOCK_END_LENGTH 2
+#define NR_SACK_CHUNK_NR_GAP_BLOCK_LENGTH 4
+#define NR_SACK_CHUNK_NR_GAP_BLOCK_START_LENGTH 2
+#define NR_SACK_CHUNK_NR_GAP_BLOCK_END_LENGTH 2
+#define NR_SACK_CHUNK_DUP_TSN_LENGTH 4
+
+#define NR_SACK_CHUNK_CUMULATIVE_TSN_ACK_OFFSET (CHUNK_VALUE_OFFSET + 0)
+#define NR_SACK_CHUNK_ADV_REC_WINDOW_CREDIT_OFFSET (NR_SACK_CHUNK_CUMULATIVE_TSN_ACK_OFFSET + \
+ NR_SACK_CHUNK_CUMULATIVE_TSN_ACK_LENGTH)
+#define NR_SACK_CHUNK_NUMBER_OF_GAP_BLOCKS_OFFSET (NR_SACK_CHUNK_ADV_REC_WINDOW_CREDIT_OFFSET + \
+ NR_SACK_CHUNK_ADV_REC_WINDOW_CREDIT_LENGTH)
+#define NR_SACK_CHUNK_NUMBER_OF_NR_GAP_BLOCKS_OFFSET (NR_SACK_CHUNK_NUMBER_OF_GAP_BLOCKS_OFFSET + \
+ NR_SACK_CHUNK_NUMBER_OF_GAP_BLOCKS_LENGTH)
+#define NR_SACK_CHUNK_NUMBER_OF_DUP_TSNS_OFFSET (NR_SACK_CHUNK_NUMBER_OF_NR_GAP_BLOCKS_OFFSET + \
+ NR_SACK_CHUNK_NUMBER_OF_NR_GAP_BLOCKS_LENGTH)
+#define NR_SACK_CHUNK_RESERVED_OFFSET (NR_SACK_CHUNK_NUMBER_OF_DUP_TSNS_OFFSET + \
+ NR_SACK_CHUNK_NUMBER_OF_DUP_TSNS_LENGTH)
+#define NR_SACK_CHUNK_GAP_BLOCK_OFFSET (NR_SACK_CHUNK_RESERVED_OFFSET + \
+ NR_SACK_CHUNK_RESERVED_LENGTH)
+
+static void
+dissect_nr_sack_chunk(packet_info *pinfo, tvbuff_t *chunk_tvb, proto_tree *chunk_tree, proto_item *chunk_item, proto_item *flags_item, sctp_half_assoc_t *ha)
+{
+ guint16 number_of_gap_blocks, number_of_dup_tsns;
+ guint16 number_of_nr_gap_blocks;
+ guint16 gap_block_number, nr_gap_block_number, dup_tsn_number, start, end;
+ gint gap_block_offset, nr_gap_block_offset, dup_tsn_offset;
+ guint32 cum_tsn_ack;
+ proto_tree *block_tree;
+ proto_tree *flags_tree;
+ proto_item *ctsa_item;
+ proto_tree *acks_tree;
+ guint32 tsns_gap_acked = 0;
+ guint32 tsns_nr_gap_acked = 0;
+ guint16 last_end;
+
+ flags_tree = proto_item_add_subtree(flags_item, ett_sctp_nr_sack_chunk_flags);
+ proto_tree_add_item(flags_tree, hf_nr_sack_chunk_ns, chunk_tvb, CHUNK_FLAGS_OFFSET, CHUNK_FLAGS_LENGTH, ENC_BIG_ENDIAN);
+
+ ctsa_item = proto_tree_add_item(chunk_tree, hf_nr_sack_chunk_cumulative_tsn_ack, chunk_tvb, NR_SACK_CHUNK_CUMULATIVE_TSN_ACK_OFFSET, NR_SACK_CHUNK_CUMULATIVE_TSN_ACK_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(chunk_tree, hf_nr_sack_chunk_adv_rec_window_credit, chunk_tvb, NR_SACK_CHUNK_ADV_REC_WINDOW_CREDIT_OFFSET, NR_SACK_CHUNK_ADV_REC_WINDOW_CREDIT_LENGTH, ENC_BIG_ENDIAN);
+
+ proto_tree_add_item(chunk_tree, hf_nr_sack_chunk_number_of_gap_blocks, chunk_tvb, NR_SACK_CHUNK_NUMBER_OF_GAP_BLOCKS_OFFSET, NR_SACK_CHUNK_NUMBER_OF_GAP_BLOCKS_LENGTH, ENC_BIG_ENDIAN);
+
+ proto_tree_add_item(chunk_tree, hf_nr_sack_chunk_number_of_nr_gap_blocks, chunk_tvb, NR_SACK_CHUNK_NUMBER_OF_NR_GAP_BLOCKS_OFFSET, NR_SACK_CHUNK_NUMBER_OF_NR_GAP_BLOCKS_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(chunk_tree, hf_nr_sack_chunk_number_of_dup_tsns, chunk_tvb, NR_SACK_CHUNK_NUMBER_OF_DUP_TSNS_OFFSET, NR_SACK_CHUNK_NUMBER_OF_DUP_TSNS_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(chunk_tree, hf_nr_sack_chunk_reserved, chunk_tvb, NR_SACK_CHUNK_RESERVED_OFFSET, NR_SACK_CHUNK_RESERVED_LENGTH, ENC_BIG_ENDIAN);
+
+
+ number_of_gap_blocks = tvb_get_ntohs(chunk_tvb, NR_SACK_CHUNK_NUMBER_OF_GAP_BLOCKS_OFFSET);
+ gap_block_offset = NR_SACK_CHUNK_GAP_BLOCK_OFFSET;
+ cum_tsn_ack = tvb_get_ntohl(chunk_tvb, NR_SACK_CHUNK_CUMULATIVE_TSN_ACK_OFFSET);
+
+ acks_tree = proto_item_add_subtree(ctsa_item,ett_sctp_ack);
+ sctp_ack_block(pinfo, ha, chunk_tvb, acks_tree, NULL, cum_tsn_ack);
+
+ last_end = 0;
+ for(gap_block_number = 0; gap_block_number < number_of_gap_blocks; gap_block_number++) {
+ proto_item *pi;
+ proto_tree *pt;
+ guint32 tsn_start;
+
+ start = tvb_get_ntohs(chunk_tvb, gap_block_offset);
+ end = tvb_get_ntohs(chunk_tvb, gap_block_offset + NR_SACK_CHUNK_GAP_BLOCK_START_LENGTH);
+ tsn_start = cum_tsn_ack + start;
+
+ block_tree = proto_tree_add_subtree_format(chunk_tree, chunk_tvb, gap_block_offset, NR_SACK_CHUNK_GAP_BLOCK_LENGTH,
+ ett_sctp_nr_sack_chunk_gap_block, NULL, "Gap Acknowledgement for TSN %u to %u", cum_tsn_ack + start, cum_tsn_ack + end);
+
+ pi = proto_tree_add_item(block_tree, hf_nr_sack_chunk_gap_block_start, chunk_tvb, gap_block_offset, NR_SACK_CHUNK_GAP_BLOCK_START_LENGTH, ENC_BIG_ENDIAN);
+ pt = proto_item_add_subtree(pi, ett_sctp_nr_sack_chunk_gap_block_start);
+ pi = proto_tree_add_uint(pt, hf_nr_sack_chunk_gap_block_start_tsn,
+ chunk_tvb, gap_block_offset,NR_SACK_CHUNK_GAP_BLOCK_START_LENGTH, cum_tsn_ack + start);
+ proto_item_set_generated(pi);
+
+ pi = proto_tree_add_item(block_tree, hf_nr_sack_chunk_gap_block_end, chunk_tvb, gap_block_offset + NR_SACK_CHUNK_GAP_BLOCK_START_LENGTH, NR_SACK_CHUNK_GAP_BLOCK_END_LENGTH, ENC_BIG_ENDIAN);
+ pt = proto_item_add_subtree(pi, ett_sctp_nr_sack_chunk_gap_block_end);
+ pi = proto_tree_add_uint(pt, hf_nr_sack_chunk_gap_block_end_tsn, chunk_tvb,
+ gap_block_offset + NR_SACK_CHUNK_GAP_BLOCK_START_LENGTH, NR_SACK_CHUNK_GAP_BLOCK_END_LENGTH, cum_tsn_ack + end);
+ proto_item_set_generated(pi);
+
+ sctp_ack_block(pinfo, ha, chunk_tvb, block_tree, &tsn_start, cum_tsn_ack + end);
+ gap_block_offset += NR_SACK_CHUNK_GAP_BLOCK_LENGTH;
+ tsns_gap_acked += (end - start) + 1;
+
+ /* Check validity */
+ if (start > end) {
+ expert_add_info(pinfo, pi, &ei_sctp_sack_chunk_gap_block_malformed);
+ }
+ if (last_end > start) {
+ expert_add_info(pinfo, pi, &ei_sctp_sack_chunk_gap_block_out_of_order);
+ }
+ last_end = end;
+ }
+
+ if (tsns_gap_acked) {
+ proto_item *pi;
+
+ pi = proto_tree_add_uint(chunk_tree, hf_nr_sack_chunk_number_tsns_gap_acked, chunk_tvb, 0, 0, tsns_gap_acked);
+ proto_item_set_generated(pi);
+
+ /* If there are a huge number of GAP ACKs, warn the user. 100 is a random
+ * number: it could be tuned.
+ */
+ if (tsns_gap_acked > 100)
+ expert_add_info(pinfo, pi, &ei_sctp_nr_sack_chunk_number_tsns_gap_acked_100);
+
+ }
+
+ /* NE: handle the nr-sack chunk's nr-gap blocks */
+ number_of_nr_gap_blocks = tvb_get_ntohs(chunk_tvb, NR_SACK_CHUNK_NUMBER_OF_NR_GAP_BLOCKS_OFFSET);
+ nr_gap_block_offset = gap_block_offset;
+
+ last_end = 0;
+ for(nr_gap_block_number = 0; nr_gap_block_number < number_of_nr_gap_blocks; nr_gap_block_number++) {
+ proto_item *pi;
+ proto_tree *pt;
+ /*guint32 tsn_start;*/
+
+ start = tvb_get_ntohs(chunk_tvb, nr_gap_block_offset);
+ end = tvb_get_ntohs(chunk_tvb, nr_gap_block_offset + NR_SACK_CHUNK_NR_GAP_BLOCK_START_LENGTH);
+ /*tsn_start = cum_tsn_ack + start;*/
+
+ block_tree = proto_tree_add_subtree_format(chunk_tree, chunk_tvb, nr_gap_block_offset, NR_SACK_CHUNK_NR_GAP_BLOCK_LENGTH,
+ ett_sctp_nr_sack_chunk_nr_gap_block, NULL, "NR-Gap Acknowledgement for TSN %u to %u", cum_tsn_ack + start, cum_tsn_ack + end);
+
+ pi = proto_tree_add_item(block_tree, hf_nr_sack_chunk_nr_gap_block_start, chunk_tvb, nr_gap_block_offset, NR_SACK_CHUNK_NR_GAP_BLOCK_START_LENGTH, ENC_BIG_ENDIAN);
+ pt = proto_item_add_subtree(pi, ett_sctp_nr_sack_chunk_nr_gap_block_start);
+ pi = proto_tree_add_uint(pt, hf_nr_sack_chunk_nr_gap_block_start_tsn,
+ chunk_tvb, nr_gap_block_offset, NR_SACK_CHUNK_NR_GAP_BLOCK_START_LENGTH, cum_tsn_ack + start);
+ proto_item_set_generated(pi);
+
+ pi = proto_tree_add_item(block_tree, hf_nr_sack_chunk_nr_gap_block_end, chunk_tvb, nr_gap_block_offset + NR_SACK_CHUNK_NR_GAP_BLOCK_START_LENGTH, NR_SACK_CHUNK_NR_GAP_BLOCK_END_LENGTH, ENC_BIG_ENDIAN);
+ pt = proto_item_add_subtree(pi, ett_sctp_nr_sack_chunk_nr_gap_block_end);
+ pi = proto_tree_add_uint(pt, hf_nr_sack_chunk_nr_gap_block_end_tsn, chunk_tvb,
+ nr_gap_block_offset + NR_SACK_CHUNK_NR_GAP_BLOCK_START_LENGTH, NR_SACK_CHUNK_NR_GAP_BLOCK_END_LENGTH, cum_tsn_ack + end);
+ proto_item_set_generated(pi);
+
+ /* sctp_ack_block(pinfo, ha, chunk_tvb, block_tree, &tsn_start, cum_tsn_ack + end); */
+ nr_gap_block_offset += NR_SACK_CHUNK_NR_GAP_BLOCK_LENGTH;
+ tsns_nr_gap_acked += (end - start) + 1;
+
+ /* Check validity */
+ if (start > end) {
+ expert_add_info(pinfo, pi, &ei_sctp_sack_chunk_gap_block_malformed);
+ }
+ if (last_end > start) {
+ expert_add_info(pinfo, pi, &ei_sctp_sack_chunk_gap_block_out_of_order);
+ }
+ last_end = end;
+ }
+
+ if (tsns_nr_gap_acked) {
+ proto_item *pi;
+
+ pi = proto_tree_add_uint(chunk_tree, hf_nr_sack_chunk_number_tsns_nr_gap_acked, chunk_tvb, 0, 0, tsns_nr_gap_acked);
+ proto_item_set_generated(pi);
+
+ /* If there are a huge number of GAP ACKs, warn the user. 100 is a random
+ * number: it could be tuned.
+ */
+ if (tsns_nr_gap_acked > 100)
+ expert_add_info(pinfo, pi, &ei_sctp_nr_sack_chunk_number_tsns_nr_gap_acked_100);
+ }
+
+ /* handle the duplicate TSNs */
+ number_of_dup_tsns = tvb_get_ntohs(chunk_tvb, NR_SACK_CHUNK_NUMBER_OF_DUP_TSNS_OFFSET);
+ dup_tsn_offset = NR_SACK_CHUNK_GAP_BLOCK_OFFSET + number_of_gap_blocks * NR_SACK_CHUNK_GAP_BLOCK_LENGTH
+ + number_of_nr_gap_blocks * NR_SACK_CHUNK_NR_GAP_BLOCK_LENGTH;
+
+
+ for(dup_tsn_number = 0; dup_tsn_number < number_of_dup_tsns; dup_tsn_number++) {
+ proto_tree_add_item(chunk_tree, hf_nr_sack_chunk_duplicate_tsn, chunk_tvb, dup_tsn_offset, NR_SACK_CHUNK_DUP_TSN_LENGTH, ENC_BIG_ENDIAN);
+ dup_tsn_offset += NR_SACK_CHUNK_DUP_TSN_LENGTH;
+ }
+
+ proto_item_append_text(chunk_item, " (Cumulative TSN: %u, a_rwnd: %u, gaps: %u, nr-gaps: %u, duplicate TSNs: %u)",
+ tvb_get_ntohl(chunk_tvb, NR_SACK_CHUNK_CUMULATIVE_TSN_ACK_OFFSET),
+ tvb_get_ntohl(chunk_tvb, NR_SACK_CHUNK_ADV_REC_WINDOW_CREDIT_OFFSET),
+ number_of_gap_blocks, number_of_nr_gap_blocks, number_of_dup_tsns);
+}
+
+#define HEARTBEAT_CHUNK_INFO_OFFSET CHUNK_VALUE_OFFSET
+
+static void
+dissect_heartbeat_chunk(tvbuff_t *chunk_tvb, guint16 chunk_length, packet_info *pinfo, proto_tree *chunk_tree, proto_item *chunk_item)
+{
+ tvbuff_t *parameter_tvb;
+
+ if (chunk_tree) {
+ proto_item_append_text(chunk_item, " (Information: %u byte%s)", chunk_length - CHUNK_HEADER_LENGTH, plurality(chunk_length - CHUNK_HEADER_LENGTH, "", "s"));
+ parameter_tvb = tvb_new_subset_length_caplen(chunk_tvb, HEARTBEAT_CHUNK_INFO_OFFSET,
+ MIN(chunk_length - CHUNK_HEADER_LENGTH, tvb_captured_length_remaining(chunk_tvb, HEARTBEAT_CHUNK_INFO_OFFSET)),
+ MIN(chunk_length - CHUNK_HEADER_LENGTH, tvb_reported_length_remaining(chunk_tvb, HEARTBEAT_CHUNK_INFO_OFFSET)));
+ /* FIXME: Parameters or parameter? */
+ dissect_parameter(parameter_tvb, pinfo, chunk_tree, NULL, FALSE, TRUE);
+ }
+}
+
+#define HEARTBEAT_ACK_CHUNK_INFO_OFFSET CHUNK_VALUE_OFFSET
+
+static void
+dissect_heartbeat_ack_chunk(tvbuff_t *chunk_tvb, guint16 chunk_length, packet_info *pinfo, proto_tree *chunk_tree, proto_item *chunk_item)
+{
+ tvbuff_t *parameter_tvb;
+
+ if (chunk_tree) {
+ proto_item_append_text(chunk_item, " (Information: %u byte%s)", chunk_length - CHUNK_HEADER_LENGTH, plurality(chunk_length - CHUNK_HEADER_LENGTH, "", "s"));
+ parameter_tvb = tvb_new_subset_length_caplen(chunk_tvb, HEARTBEAT_ACK_CHUNK_INFO_OFFSET,
+ MIN(chunk_length - CHUNK_HEADER_LENGTH, tvb_captured_length_remaining(chunk_tvb, HEARTBEAT_ACK_CHUNK_INFO_OFFSET)),
+ MIN(chunk_length - CHUNK_HEADER_LENGTH, tvb_reported_length_remaining(chunk_tvb, HEARTBEAT_ACK_CHUNK_INFO_OFFSET)));
+ /* FIXME: Parameters or parameter? */
+ dissect_parameter(parameter_tvb, pinfo, chunk_tree, NULL, FALSE, TRUE);
+ }
+}
+
+#define ABORT_CHUNK_FIRST_ERROR_CAUSE_OFFSET 4
+#define SCTP_ABORT_CHUNK_T_BIT 0x01
+
+static void
+dissect_abort_chunk(tvbuff_t *chunk_tvb, guint16 chunk_length, packet_info *pinfo, proto_tree *chunk_tree, proto_item *flags_item)
+{
+ tvbuff_t *causes_tvb;
+ proto_tree *flags_tree;
+
+ sctp_info.vtag_reflected =
+ (tvb_get_guint8(chunk_tvb, CHUNK_FLAGS_OFFSET) & SCTP_ABORT_CHUNK_T_BIT) != 0;
+
+ if (chunk_tree) {
+ flags_tree = proto_item_add_subtree(flags_item, ett_sctp_abort_chunk_flags);
+ proto_tree_add_item(flags_tree, hf_abort_chunk_t_bit, chunk_tvb, CHUNK_FLAGS_OFFSET, CHUNK_FLAGS_LENGTH, ENC_BIG_ENDIAN);
+ causes_tvb = tvb_new_subset_length_caplen(chunk_tvb, CHUNK_VALUE_OFFSET,
+ MIN(chunk_length - CHUNK_HEADER_LENGTH, tvb_captured_length_remaining(chunk_tvb, CHUNK_VALUE_OFFSET)),
+ MIN(chunk_length - CHUNK_HEADER_LENGTH, tvb_reported_length_remaining(chunk_tvb, CHUNK_VALUE_OFFSET)));
+ dissect_error_causes(causes_tvb, pinfo, chunk_tree);
+ }
+}
+
+#define SHUTDOWN_CHUNK_CUMULATIVE_TSN_ACK_OFFSET CHUNK_VALUE_OFFSET
+#define SHUTDOWN_CHUNK_CUMULATIVE_TSN_ACK_LENGTH 4
+
+static void
+dissect_shutdown_chunk(tvbuff_t *chunk_tvb, proto_tree *chunk_tree, proto_item *chunk_item)
+{
+ if (chunk_tree) {
+ proto_tree_add_item(chunk_tree, hf_shutdown_chunk_cumulative_tsn_ack, chunk_tvb, SHUTDOWN_CHUNK_CUMULATIVE_TSN_ACK_OFFSET, SHUTDOWN_CHUNK_CUMULATIVE_TSN_ACK_LENGTH, ENC_BIG_ENDIAN);
+ proto_item_append_text(chunk_item, " (Cumulative TSN ack: %u)", tvb_get_ntohl(chunk_tvb, SHUTDOWN_CHUNK_CUMULATIVE_TSN_ACK_OFFSET));
+ }
+}
+
+static void
+dissect_shutdown_ack_chunk(tvbuff_t *chunk_tvb _U_)
+{
+}
+
+#define ERROR_CAUSE_IND_CAUSES_OFFSET CHUNK_VALUE_OFFSET
+
+static void
+dissect_error_chunk(tvbuff_t *chunk_tvb, guint16 chunk_length, packet_info *pinfo, proto_tree *chunk_tree)
+{
+ tvbuff_t *causes_tvb;
+
+ if (chunk_tree) {
+ causes_tvb = tvb_new_subset_length_caplen(chunk_tvb, ERROR_CAUSE_IND_CAUSES_OFFSET,
+ MIN(chunk_length - CHUNK_HEADER_LENGTH, tvb_captured_length_remaining(chunk_tvb, ERROR_CAUSE_IND_CAUSES_OFFSET)),
+ MIN(chunk_length - CHUNK_HEADER_LENGTH, tvb_reported_length_remaining(chunk_tvb, ERROR_CAUSE_IND_CAUSES_OFFSET)));
+ dissect_error_causes(causes_tvb, pinfo, chunk_tree);
+ }
+}
+
+#define COOKIE_OFFSET CHUNK_VALUE_OFFSET
+
+static void
+dissect_cookie_echo_chunk(tvbuff_t *chunk_tvb, guint16 chunk_length, proto_tree *chunk_tree, proto_item *chunk_item)
+{
+ if (chunk_tree) {
+ proto_tree_add_item(chunk_tree, hf_cookie, chunk_tvb, COOKIE_OFFSET, chunk_length - CHUNK_HEADER_LENGTH, ENC_NA);
+ proto_item_append_text(chunk_item, " (Cookie length: %u byte%s)", chunk_length - CHUNK_HEADER_LENGTH, plurality(chunk_length - CHUNK_HEADER_LENGTH, "", "s"));
+ }
+}
+
+static void
+dissect_cookie_ack_chunk(tvbuff_t *chunk_tvb _U_)
+{
+}
+
+#define ECNE_CHUNK_LOWEST_TSN_OFFSET CHUNK_VALUE_OFFSET
+#define ECNE_CHUNK_LOWEST_TSN_LENGTH 4
+
+static void
+dissect_ecne_chunk(tvbuff_t *chunk_tvb, proto_tree *chunk_tree, proto_item *chunk_item)
+{
+ proto_tree_add_item(chunk_tree, hf_ecne_chunk_lowest_tsn, chunk_tvb, ECNE_CHUNK_LOWEST_TSN_OFFSET, ECNE_CHUNK_LOWEST_TSN_LENGTH, ENC_BIG_ENDIAN);
+ proto_item_append_text(chunk_item, " (Lowest TSN: %u)", tvb_get_ntohl(chunk_tvb, ECNE_CHUNK_LOWEST_TSN_OFFSET));
+}
+
+#define CWR_CHUNK_LOWEST_TSN_OFFSET CHUNK_VALUE_OFFSET
+#define CWR_CHUNK_LOWEST_TSN_LENGTH 4
+
+static void
+dissect_cwr_chunk(tvbuff_t *chunk_tvb, proto_tree *chunk_tree, proto_item *chunk_item)
+{
+ proto_tree_add_item(chunk_tree, hf_cwr_chunk_lowest_tsn, chunk_tvb, CWR_CHUNK_LOWEST_TSN_OFFSET, CWR_CHUNK_LOWEST_TSN_LENGTH, ENC_BIG_ENDIAN);
+ proto_item_append_text(chunk_item, " (Lowest TSN: %u)", tvb_get_ntohl(chunk_tvb, CWR_CHUNK_LOWEST_TSN_OFFSET));
+}
+
+#define SCTP_SHUTDOWN_COMPLETE_CHUNK_T_BIT 0x01
+
+
+static const true_false_string sctp_shutdown_complete_chunk_t_bit_value = {
+ "Tag reflected",
+ "Tag not reflected"
+};
+
+
+static void
+dissect_shutdown_complete_chunk(tvbuff_t *chunk_tvb, proto_tree *chunk_tree, proto_item *flags_item)
+{
+ proto_tree *flags_tree;
+
+ sctp_info.vtag_reflected =
+ (tvb_get_guint8(chunk_tvb, CHUNK_FLAGS_OFFSET) & SCTP_SHUTDOWN_COMPLETE_CHUNK_T_BIT) != 0;
+
+ if (chunk_tree) {
+ flags_tree = proto_item_add_subtree(flags_item, ett_sctp_shutdown_complete_chunk_flags);
+ proto_tree_add_item(flags_tree, hf_shutdown_complete_chunk_t_bit, chunk_tvb, CHUNK_FLAGS_OFFSET, CHUNK_FLAGS_LENGTH, ENC_BIG_ENDIAN);
+ }
+}
+
+#define FORWARD_TSN_CHUNK_TSN_LENGTH 4
+#define FORWARD_TSN_CHUNK_SID_LENGTH 2
+#define FORWARD_TSN_CHUNK_SSN_LENGTH 2
+#define FORWARD_TSN_CHUNK_TSN_OFFSET CHUNK_VALUE_OFFSET
+#define FORWARD_TSN_CHUNK_SID_OFFSET 0
+#define FORWARD_TSN_CHUNK_SSN_OFFSET (FORWARD_TSN_CHUNK_SID_OFFSET + FORWARD_TSN_CHUNK_SID_LENGTH)
+
+static void
+dissect_forward_tsn_chunk(tvbuff_t *chunk_tvb, guint16 chunk_length, proto_tree *chunk_tree, proto_item *chunk_item)
+{
+ guint offset;
+ guint16 number_of_affected_streams, affected_stream;
+
+ /* FIXME */
+ if (chunk_length < CHUNK_HEADER_LENGTH + FORWARD_TSN_CHUNK_TSN_LENGTH) {
+ proto_item_append_text(chunk_item, ", bogus chunk length %u < %u)",
+ chunk_length,
+ CHUNK_HEADER_LENGTH + FORWARD_TSN_CHUNK_TSN_LENGTH);
+ return;
+ }
+ if (chunk_tree) {
+ proto_tree_add_item(chunk_tree, hf_forward_tsn_chunk_tsn, chunk_tvb, FORWARD_TSN_CHUNK_TSN_OFFSET, FORWARD_TSN_CHUNK_TSN_LENGTH, ENC_BIG_ENDIAN);
+ number_of_affected_streams = (chunk_length - CHUNK_HEADER_LENGTH - FORWARD_TSN_CHUNK_TSN_LENGTH) /
+ (FORWARD_TSN_CHUNK_SID_LENGTH + FORWARD_TSN_CHUNK_SSN_LENGTH);
+ offset = CHUNK_VALUE_OFFSET + FORWARD_TSN_CHUNK_TSN_LENGTH;
+
+ for(affected_stream = 0; affected_stream < number_of_affected_streams; affected_stream++) {
+ proto_tree_add_item(chunk_tree, hf_forward_tsn_chunk_sid, chunk_tvb, offset + FORWARD_TSN_CHUNK_SID_OFFSET, FORWARD_TSN_CHUNK_SID_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(chunk_tree, hf_forward_tsn_chunk_ssn, chunk_tvb, offset + FORWARD_TSN_CHUNK_SSN_OFFSET, FORWARD_TSN_CHUNK_SSN_LENGTH, ENC_BIG_ENDIAN);
+ offset = offset + (FORWARD_TSN_CHUNK_SID_LENGTH + FORWARD_TSN_CHUNK_SSN_LENGTH);
+ }
+ proto_item_append_text(chunk_item, "(Cumulative TSN: %u)", tvb_get_ntohl(chunk_tvb, FORWARD_TSN_CHUNK_TSN_OFFSET));
+ }
+}
+
+#define I_FORWARD_TSN_CHUNK_TSN_LENGTH 4
+#define I_FORWARD_TSN_CHUNK_SID_LENGTH 2
+#define I_FORWARD_TSN_CHUNK_FLAGS_LENGTH 2
+#define I_FORWARD_TSN_CHUNK_MID_LENGTH 4
+#define I_FORWARD_TSN_CHUNK_TSN_OFFSET CHUNK_VALUE_OFFSET
+#define I_FORWARD_TSN_CHUNK_SID_OFFSET 0
+#define I_FORWARD_TSN_CHUNK_FLAGS_OFFSET (I_FORWARD_TSN_CHUNK_SID_OFFSET + I_FORWARD_TSN_CHUNK_SID_LENGTH)
+#define I_FORWARD_TSN_CHUNK_MID_OFFSET (I_FORWARD_TSN_CHUNK_FLAGS_OFFSET + I_FORWARD_TSN_CHUNK_FLAGS_LENGTH)
+
+#define SCTP_I_FORWARD_TSN_CHUNK_U_BIT 0x0001
+#define SCTP_I_FORWARD_TSN_CHUNK_RES_MASK 0xfffe
+
+static const true_false_string sctp_i_forward_tsn_chunk_u_bit_value = {
+ "Unordered messages",
+ "Ordered messages"
+};
+
+static void
+dissect_i_forward_tsn_chunk(tvbuff_t *chunk_tvb, guint16 chunk_length, proto_tree *chunk_tree, proto_item *chunk_item)
+{
+ guint offset;
+ guint16 number_of_affected_streams, affected_stream;
+ proto_tree *flags_tree;
+ proto_item *flags_item = NULL;
+
+ /* FIXME */
+ if (chunk_length < CHUNK_HEADER_LENGTH + I_FORWARD_TSN_CHUNK_TSN_LENGTH) {
+ proto_item_append_text(chunk_item, ", bogus chunk length %u < %u)",
+ chunk_length,
+ CHUNK_HEADER_LENGTH + I_FORWARD_TSN_CHUNK_TSN_LENGTH);
+ return;
+ }
+ if (chunk_tree) {
+ proto_tree_add_item(chunk_tree, hf_i_forward_tsn_chunk_tsn, chunk_tvb, I_FORWARD_TSN_CHUNK_TSN_OFFSET, I_FORWARD_TSN_CHUNK_TSN_LENGTH, ENC_BIG_ENDIAN);
+ number_of_affected_streams = (chunk_length - CHUNK_HEADER_LENGTH - I_FORWARD_TSN_CHUNK_TSN_LENGTH) /
+ (I_FORWARD_TSN_CHUNK_SID_LENGTH + I_FORWARD_TSN_CHUNK_FLAGS_LENGTH + I_FORWARD_TSN_CHUNK_MID_LENGTH);
+ offset = CHUNK_VALUE_OFFSET + I_FORWARD_TSN_CHUNK_TSN_LENGTH;
+
+ for(affected_stream = 0; affected_stream < number_of_affected_streams; affected_stream++) {
+ proto_tree_add_item(chunk_tree, hf_i_forward_tsn_chunk_sid, chunk_tvb, offset + I_FORWARD_TSN_CHUNK_SID_OFFSET, I_FORWARD_TSN_CHUNK_SID_LENGTH, ENC_BIG_ENDIAN);
+ flags_item = proto_tree_add_item(chunk_tree, hf_i_forward_tsn_chunk_flags, chunk_tvb, offset + I_FORWARD_TSN_CHUNK_FLAGS_OFFSET, I_FORWARD_TSN_CHUNK_FLAGS_LENGTH, ENC_BIG_ENDIAN);
+ flags_tree = proto_item_add_subtree(flags_item, ett_sctp_i_forward_tsn_chunk_flags);
+ proto_tree_add_item(flags_tree, hf_i_forward_tsn_chunk_res, chunk_tvb, offset + I_FORWARD_TSN_CHUNK_FLAGS_OFFSET, I_FORWARD_TSN_CHUNK_FLAGS_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(flags_tree, hf_i_forward_tsn_chunk_u_bit, chunk_tvb, offset + I_FORWARD_TSN_CHUNK_FLAGS_OFFSET, I_FORWARD_TSN_CHUNK_FLAGS_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(chunk_tree, hf_i_forward_tsn_chunk_mid, chunk_tvb, offset + I_FORWARD_TSN_CHUNK_MID_OFFSET, I_FORWARD_TSN_CHUNK_MID_LENGTH, ENC_BIG_ENDIAN);
+ offset += (I_FORWARD_TSN_CHUNK_SID_LENGTH + I_FORWARD_TSN_CHUNK_FLAGS_LENGTH + I_FORWARD_TSN_CHUNK_MID_LENGTH);
+ }
+ proto_item_append_text(chunk_item, "(Cumulative TSN: %u)", tvb_get_ntohl(chunk_tvb, I_FORWARD_TSN_CHUNK_TSN_OFFSET));
+ }
+}
+
+#define RE_CONFIG_PARAMETERS_OFFSET CHUNK_HEADER_LENGTH
+
+static void
+dissect_re_config_chunk(tvbuff_t *chunk_tvb, guint16 chunk_length, packet_info *pinfo, proto_tree *chunk_tree, proto_item *chunk_item _U_)
+{
+ tvbuff_t *parameters_tvb;
+
+ parameters_tvb = tvb_new_subset_length_caplen(chunk_tvb, RE_CONFIG_PARAMETERS_OFFSET,
+ MIN(chunk_length - CHUNK_HEADER_LENGTH, tvb_captured_length_remaining(chunk_tvb, RE_CONFIG_PARAMETERS_OFFSET)),
+ MIN(chunk_length - CHUNK_HEADER_LENGTH, tvb_reported_length_remaining(chunk_tvb, RE_CONFIG_PARAMETERS_OFFSET)));
+ dissect_parameters(parameters_tvb, pinfo, chunk_tree, NULL, FALSE);
+}
+
+#define SHARED_KEY_ID_LENGTH 2
+
+#define SHARED_KEY_ID_OFFSET PARAMETER_VALUE_OFFSET
+#define HMAC_ID_OFFSET (SHARED_KEY_ID_OFFSET + SHARED_KEY_ID_LENGTH)
+#define HMAC_OFFSET (HMAC_ID_OFFSET + HMAC_ID_LENGTH)
+
+static void
+dissect_auth_chunk(tvbuff_t *chunk_tvb, guint16 chunk_length, proto_tree *chunk_tree, proto_item *chunk_item _U_)
+{
+ guint hmac_length;
+
+ hmac_length = chunk_length - CHUNK_HEADER_LENGTH - HMAC_ID_LENGTH - SHARED_KEY_ID_LENGTH;
+ proto_tree_add_item(chunk_tree, hf_shared_key_id, chunk_tvb, SHARED_KEY_ID_OFFSET, SHARED_KEY_ID_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(chunk_tree, hf_hmac_id, chunk_tvb, HMAC_ID_OFFSET, HMAC_ID_LENGTH, ENC_BIG_ENDIAN);
+ if (hmac_length > 0)
+ proto_tree_add_item(chunk_tree, hf_hmac, chunk_tvb, HMAC_OFFSET, hmac_length, ENC_NA);
+}
+
+#define SCTP_SEQUENCE_NUMBER_LENGTH 4
+#define SEQUENCE_NUMBER_OFFSET CHUNK_VALUE_OFFSET
+#define ASCONF_CHUNK_PARAMETERS_OFFSET (SEQUENCE_NUMBER_OFFSET + SCTP_SEQUENCE_NUMBER_LENGTH)
+
+static void
+dissect_asconf_chunk(tvbuff_t *chunk_tvb, guint16 chunk_length, packet_info *pinfo, proto_tree *chunk_tree, proto_item *chunk_item)
+{
+ tvbuff_t *parameters_tvb;
+
+ if (chunk_length < CHUNK_HEADER_LENGTH + SCTP_SEQUENCE_NUMBER_LENGTH) {
+ proto_item_append_text(chunk_item, ", bogus chunk length %u < %u)",
+ chunk_length,
+ CHUNK_HEADER_LENGTH + SCTP_SEQUENCE_NUMBER_LENGTH);
+ return;
+ }
+ if (chunk_tree) {
+ proto_tree_add_item(chunk_tree, hf_asconf_seq_nr, chunk_tvb, SEQUENCE_NUMBER_OFFSET, SCTP_SEQUENCE_NUMBER_LENGTH, ENC_BIG_ENDIAN);
+ }
+ chunk_length -= CHUNK_HEADER_LENGTH + SCTP_SEQUENCE_NUMBER_LENGTH;
+ parameters_tvb = tvb_new_subset_length_caplen(chunk_tvb, ASCONF_CHUNK_PARAMETERS_OFFSET,
+ MIN(chunk_length, tvb_captured_length_remaining(chunk_tvb, ASCONF_CHUNK_PARAMETERS_OFFSET)),
+ MIN(chunk_length, tvb_reported_length_remaining(chunk_tvb, ASCONF_CHUNK_PARAMETERS_OFFSET)));
+ dissect_parameters(parameters_tvb, pinfo, chunk_tree, NULL, FALSE);
+}
+
+#define ASCONF_ACK_CHUNK_PARAMETERS_OFFSET (SEQUENCE_NUMBER_OFFSET + SCTP_SEQUENCE_NUMBER_LENGTH)
+
+static void
+dissect_asconf_ack_chunk(tvbuff_t *chunk_tvb, guint16 chunk_length, packet_info *pinfo, proto_tree *chunk_tree, proto_item *chunk_item)
+{
+ tvbuff_t *parameters_tvb;
+
+ if (chunk_length < CHUNK_HEADER_LENGTH + SCTP_SEQUENCE_NUMBER_LENGTH) {
+ proto_item_append_text(chunk_item, ", bogus chunk length %u < %u)",
+ chunk_length + CHUNK_HEADER_LENGTH,
+ CHUNK_HEADER_LENGTH + SCTP_SEQUENCE_NUMBER_LENGTH);
+ return;
+ }
+ if (chunk_tree) {
+ proto_tree_add_item(chunk_tree, hf_asconf_ack_seq_nr, chunk_tvb, SEQUENCE_NUMBER_OFFSET, SCTP_SEQUENCE_NUMBER_LENGTH, ENC_BIG_ENDIAN);
+ }
+ chunk_length -= CHUNK_HEADER_LENGTH + SCTP_SEQUENCE_NUMBER_LENGTH;
+ parameters_tvb = tvb_new_subset_length_caplen(chunk_tvb, ASCONF_ACK_CHUNK_PARAMETERS_OFFSET,
+ MIN(chunk_length, tvb_captured_length_remaining(chunk_tvb, ASCONF_ACK_CHUNK_PARAMETERS_OFFSET)),
+ MIN(chunk_length, tvb_reported_length_remaining(chunk_tvb, ASCONF_ACK_CHUNK_PARAMETERS_OFFSET)));
+ dissect_parameters(parameters_tvb, pinfo, chunk_tree, NULL, FALSE);
+}
+
+#define PKTDROP_CHUNK_BANDWIDTH_LENGTH 4
+#define PKTDROP_CHUNK_QUEUESIZE_LENGTH 4
+#define PKTDROP_CHUNK_TRUNCATED_SIZE_LENGTH 2
+#define PKTDROP_CHUNK_RESERVED_SIZE_LENGTH 2
+
+#define PKTDROP_CHUNK_HEADER_LENGTH (CHUNK_HEADER_LENGTH + \
+ PKTDROP_CHUNK_BANDWIDTH_LENGTH + \
+ PKTDROP_CHUNK_QUEUESIZE_LENGTH + \
+ PKTDROP_CHUNK_TRUNCATED_SIZE_LENGTH + \
+ PKTDROP_CHUNK_RESERVED_SIZE_LENGTH)
+
+#define PKTDROP_CHUNK_BANDWIDTH_OFFSET CHUNK_VALUE_OFFSET
+#define PKTDROP_CHUNK_QUEUESIZE_OFFSET (PKTDROP_CHUNK_BANDWIDTH_OFFSET + PKTDROP_CHUNK_BANDWIDTH_LENGTH)
+#define PKTDROP_CHUNK_TRUNCATED_SIZE_OFFSET (PKTDROP_CHUNK_QUEUESIZE_OFFSET + PKTDROP_CHUNK_QUEUESIZE_LENGTH)
+#define PKTDROP_CHUNK_RESERVED_SIZE_OFFSET (PKTDROP_CHUNK_TRUNCATED_SIZE_OFFSET + PKTDROP_CHUNK_TRUNCATED_SIZE_LENGTH)
+#define PKTDROP_CHUNK_DATA_FIELD_OFFSET (PKTDROP_CHUNK_RESERVED_SIZE_OFFSET + PKTDROP_CHUNK_RESERVED_SIZE_LENGTH)
+
+#define SCTP_PKTDROP_CHUNK_M_BIT 0x01
+#define SCTP_PKTDROP_CHUNK_B_BIT 0x02
+#define SCTP_PKTDROP_CHUNK_T_BIT 0x04
+
+static const true_false_string sctp_pktdropk_m_bit_value = {
+ "Source is a middlebox",
+ "Source is an endhost"
+};
+
+static const true_false_string sctp_pktdropk_b_bit_value = {
+ "SCTP checksum was incorrect",
+ "SCTP checksum was correct"
+};
+
+static const true_false_string sctp_pktdropk_t_bit_value = {
+ "Packet is truncated",
+ "Packet is not truncated"
+};
+
+static void
+dissect_pktdrop_chunk(tvbuff_t *chunk_tvb, guint16 chunk_length, packet_info *pinfo, proto_tree *chunk_tree, proto_item *chunk_item, proto_item *flags_item)
+{
+ tvbuff_t *data_field_tvb;
+ proto_tree *flags_tree;
+
+ if (chunk_length < PKTDROP_CHUNK_HEADER_LENGTH) {
+ proto_item_append_text(chunk_item, ", bogus chunk length %u < %u)",
+ chunk_length,
+ PKTDROP_CHUNK_HEADER_LENGTH);
+ return;
+ }
+ chunk_length -= PKTDROP_CHUNK_HEADER_LENGTH;
+ data_field_tvb = tvb_new_subset_length_caplen(chunk_tvb, PKTDROP_CHUNK_DATA_FIELD_OFFSET,
+ MIN(chunk_length, tvb_captured_length_remaining(chunk_tvb, PKTDROP_CHUNK_DATA_FIELD_OFFSET)),
+ MIN(chunk_length, tvb_reported_length_remaining(chunk_tvb, PKTDROP_CHUNK_DATA_FIELD_OFFSET)));
+
+ if (chunk_tree) {
+ flags_tree = proto_item_add_subtree(flags_item, ett_sctp_pktdrop_chunk_flags);
+
+ proto_tree_add_item(flags_tree, hf_pktdrop_chunk_m_bit, chunk_tvb, CHUNK_FLAGS_OFFSET, CHUNK_FLAGS_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(flags_tree, hf_pktdrop_chunk_b_bit, chunk_tvb, CHUNK_FLAGS_OFFSET, CHUNK_FLAGS_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(flags_tree, hf_pktdrop_chunk_t_bit, chunk_tvb, CHUNK_FLAGS_OFFSET, CHUNK_FLAGS_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(chunk_tree, hf_pktdrop_chunk_bandwidth, chunk_tvb, PKTDROP_CHUNK_BANDWIDTH_OFFSET, PKTDROP_CHUNK_BANDWIDTH_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(chunk_tree, hf_pktdrop_chunk_queuesize, chunk_tvb, PKTDROP_CHUNK_QUEUESIZE_OFFSET, PKTDROP_CHUNK_QUEUESIZE_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(chunk_tree, hf_pktdrop_chunk_truncated_length, chunk_tvb, PKTDROP_CHUNK_TRUNCATED_SIZE_OFFSET, PKTDROP_CHUNK_TRUNCATED_SIZE_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(chunk_tree, hf_pktdrop_chunk_reserved, chunk_tvb, PKTDROP_CHUNK_RESERVED_SIZE_OFFSET, PKTDROP_CHUNK_RESERVED_SIZE_LENGTH, ENC_BIG_ENDIAN);
+
+ if (chunk_length > 0) {
+ /* XXX - We should dissect what we can and catch the BoundsError when we run out of bytes */
+ if (tvb_get_guint8(chunk_tvb, CHUNK_FLAGS_OFFSET) & SCTP_PKTDROP_CHUNK_T_BIT)
+ proto_tree_add_item(chunk_tree, hf_pktdrop_chunk_data_field, chunk_tvb, PKTDROP_CHUNK_DATA_FIELD_OFFSET, chunk_length, ENC_NA);
+ else {
+ gboolean save_in_error_pkt = pinfo->flags.in_error_pkt;
+ pinfo->flags.in_error_pkt = TRUE;
+
+ dissect_sctp_packet(data_field_tvb, pinfo, chunk_tree, TRUE);
+
+ pinfo->flags.in_error_pkt = save_in_error_pkt;
+ }
+ }
+ }
+}
+
+static void
+dissect_pad_chunk(tvbuff_t *chunk_tvb, guint16 chunk_length, proto_tree *chunk_tree, proto_item *chunk_item)
+{
+ guint16 value_length;
+
+ if (chunk_tree) {
+ value_length = chunk_length - CHUNK_HEADER_LENGTH;
+ proto_tree_add_item(chunk_tree, hf_pad_chunk_padding_data, chunk_tvb, CHUNK_VALUE_OFFSET, value_length, ENC_NA);
+ proto_item_append_text(chunk_item, " (Padding data length: %u byte%s)", value_length, plurality(value_length, "", "s"));
+ }
+}
+
+static void
+dissect_unknown_chunk(tvbuff_t *chunk_tvb, guint16 chunk_length, guint8 chunk_type, proto_tree *chunk_tree, proto_item *chunk_item)
+{
+ guint16 value_length;
+
+ if (chunk_tree) {
+ value_length = chunk_length - CHUNK_HEADER_LENGTH;
+ if (value_length > 0)
+ proto_tree_add_item(chunk_tree, hf_chunk_value, chunk_tvb, CHUNK_VALUE_OFFSET, value_length, ENC_NA);
+ proto_item_append_text(chunk_item, " (Type: %u, value length: %u byte%s)", chunk_type, value_length, plurality(value_length, "", "s"));
+ }
+}
+
+#define SCTP_CHUNK_BIT_1 0x80
+#define SCTP_CHUNK_BIT_2 0x40
+
+static const true_false_string sctp_chunk_bit_1_value = {
+ "Skip chunk and continue processing of the packet",
+ "Stop processing of the packet"
+};
+
+static const true_false_string sctp_chunk_bit_2_value = {
+ "Do report",
+ "Do not report"
+};
+
+
+static gboolean
+dissect_sctp_chunk(tvbuff_t *chunk_tvb,
+ packet_info *pinfo,
+ proto_tree *tree,
+ proto_tree *sctp_tree,
+ sctp_half_assoc_t *ha,
+ gboolean useinfo)
+{
+ guint8 type;
+ guint16 length, padding_length, reported_length;
+ gboolean result;
+ proto_item *flags_item, *chunk_item, *type_item, *length_item;
+ proto_tree *chunk_tree, *type_tree;
+
+ result = FALSE;
+
+ /* first extract the chunk header */
+ type = tvb_get_guint8(chunk_tvb, CHUNK_TYPE_OFFSET);
+ length = tvb_get_ntohs(chunk_tvb, CHUNK_LENGTH_OFFSET);
+ reported_length = tvb_reported_length(chunk_tvb);
+ padding_length = reported_length - length;
+
+ if (useinfo)
+ col_append_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str_const(type, chunk_type_values, "RESERVED"));
+
+ /* create proto_tree stuff */
+ chunk_tree = proto_tree_add_subtree_format(sctp_tree, chunk_tvb, CHUNK_HEADER_OFFSET, reported_length,
+ ett_sctp_chunk, &chunk_item, "%s chunk",
+ val_to_str_const(type, chunk_type_values, "RESERVED"));
+
+ if (tree) {
+ /* then insert the chunk header components into the protocol tree */
+ type_item = proto_tree_add_item(chunk_tree, hf_chunk_type, chunk_tvb, CHUNK_TYPE_OFFSET, CHUNK_TYPE_LENGTH, ENC_BIG_ENDIAN);
+ type_tree = proto_item_add_subtree(type_item, ett_sctp_chunk_type);
+ proto_tree_add_item(type_tree, hf_chunk_bit_1, chunk_tvb, CHUNK_TYPE_OFFSET, CHUNK_TYPE_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(type_tree, hf_chunk_bit_2, chunk_tvb, CHUNK_TYPE_OFFSET, CHUNK_TYPE_LENGTH, ENC_BIG_ENDIAN);
+ flags_item = proto_tree_add_item(chunk_tree, hf_chunk_flags, chunk_tvb, CHUNK_FLAGS_OFFSET, CHUNK_FLAGS_LENGTH, ENC_BIG_ENDIAN);
+ } else {
+ chunk_tree = NULL;
+ chunk_item = NULL;
+ flags_item = NULL;
+ }
+
+ if (length < CHUNK_HEADER_LENGTH) {
+ if (tree) {
+ proto_tree_add_uint_format_value(chunk_tree, hf_chunk_length, chunk_tvb, CHUNK_LENGTH_OFFSET, CHUNK_LENGTH_LENGTH, length,
+ "%u (invalid, should be >= %u)", length, CHUNK_HEADER_LENGTH);
+ proto_item_append_text(chunk_item, ", bogus chunk length %u < %u)", length, CHUNK_HEADER_LENGTH);
+ }
+
+ if (type == SCTP_DATA_CHUNK_ID)
+ result = TRUE;
+ return result;
+ }
+
+ length_item = proto_tree_add_uint(chunk_tree, hf_chunk_length, chunk_tvb, CHUNK_LENGTH_OFFSET, CHUNK_LENGTH_LENGTH, length);
+ if (length > reported_length && !pinfo->flags.in_error_pkt) {
+ expert_add_info_format(pinfo, length_item, &ei_sctp_chunk_length_bad, "Chunk length (%d) is longer than remaining data (%d) in the packet.", length, reported_length);
+ /* We'll almost certainly throw an exception shortly... */
+ }
+
+ /*
+ length -= CHUNK_HEADER_LENGTH;
+ */
+
+ /* now dissect the chunk value */
+ switch(type) {
+ case SCTP_DATA_CHUNK_ID:
+ result = dissect_data_chunk(chunk_tvb, length, pinfo, tree, chunk_tree, chunk_item, flags_item, ha, FALSE);
+ break;
+ case SCTP_I_DATA_CHUNK_ID:
+ result = dissect_data_chunk(chunk_tvb, length, pinfo, tree, chunk_tree, chunk_item, flags_item, ha, TRUE);
+ break;
+ case SCTP_INIT_CHUNK_ID:
+ dissect_init_chunk(chunk_tvb, length, pinfo, chunk_tree, chunk_item);
+ break;
+ case SCTP_INIT_ACK_CHUNK_ID:
+ dissect_init_ack_chunk(chunk_tvb, length, pinfo, chunk_tree, chunk_item);
+ break;
+ case SCTP_SACK_CHUNK_ID:
+ dissect_sack_chunk(pinfo, chunk_tvb, chunk_tree, chunk_item, flags_item, ha);
+ break;
+ case SCTP_HEARTBEAT_CHUNK_ID:
+ dissect_heartbeat_chunk(chunk_tvb, length, pinfo, chunk_tree, chunk_item);
+ break;
+ case SCTP_HEARTBEAT_ACK_CHUNK_ID:
+ dissect_heartbeat_ack_chunk(chunk_tvb, length, pinfo, chunk_tree, chunk_item);
+ break;
+ case SCTP_ABORT_CHUNK_ID:
+ dissect_abort_chunk(chunk_tvb, length, pinfo, chunk_tree, flags_item);
+ break;
+ case SCTP_SHUTDOWN_CHUNK_ID:
+ dissect_shutdown_chunk(chunk_tvb, chunk_tree, chunk_item);
+ break;
+ case SCTP_SHUTDOWN_ACK_CHUNK_ID:
+ dissect_shutdown_ack_chunk(chunk_tvb);
+ break;
+ case SCTP_ERROR_CHUNK_ID:
+ dissect_error_chunk(chunk_tvb, length, pinfo, chunk_tree);
+ break;
+ case SCTP_COOKIE_ECHO_CHUNK_ID:
+ dissect_cookie_echo_chunk(chunk_tvb, length, chunk_tree, chunk_item);
+ break;
+ case SCTP_COOKIE_ACK_CHUNK_ID:
+ dissect_cookie_ack_chunk(chunk_tvb);
+ break;
+ case SCTP_ECNE_CHUNK_ID:
+ dissect_ecne_chunk(chunk_tvb, chunk_tree, chunk_item);
+ break;
+ case SCTP_CWR_CHUNK_ID:
+ dissect_cwr_chunk(chunk_tvb, chunk_tree, chunk_item);
+ break;
+ case SCTP_SHUTDOWN_COMPLETE_CHUNK_ID:
+ dissect_shutdown_complete_chunk(chunk_tvb, chunk_tree, flags_item);
+ break;
+ case SCTP_FORWARD_TSN_CHUNK_ID:
+ dissect_forward_tsn_chunk(chunk_tvb, length, chunk_tree, chunk_item);
+ break;
+ case SCTP_RE_CONFIG_CHUNK_ID:
+ dissect_re_config_chunk(chunk_tvb, length, pinfo, chunk_tree, chunk_item);
+ break;
+ case SCTP_AUTH_CHUNK_ID:
+ dissect_auth_chunk(chunk_tvb, length, chunk_tree, chunk_item);
+ break;
+ case SCTP_NR_SACK_CHUNK_ID:
+ dissect_nr_sack_chunk(pinfo, chunk_tvb, chunk_tree, chunk_item, flags_item, ha);
+ break;
+ case SCTP_ASCONF_ACK_CHUNK_ID:
+ dissect_asconf_ack_chunk(chunk_tvb, length, pinfo, chunk_tree, chunk_item);
+ break;
+ case SCTP_ASCONF_CHUNK_ID:
+ dissect_asconf_chunk(chunk_tvb, length, pinfo, chunk_tree, chunk_item);
+ break;
+ case SCTP_I_FORWARD_TSN_CHUNK_ID:
+ dissect_i_forward_tsn_chunk(chunk_tvb, length, chunk_tree, chunk_item);
+ break;
+ case SCTP_PKTDROP_CHUNK_ID:
+ col_set_writable(pinfo->cinfo, -1, FALSE);
+ dissect_pktdrop_chunk(chunk_tvb, length, pinfo, chunk_tree, chunk_item, flags_item);
+ col_set_writable(pinfo->cinfo, -1, TRUE);
+ break;
+ case SCTP_PAD_CHUNK_ID:
+ dissect_pad_chunk(chunk_tvb, length, chunk_tree, chunk_item);
+ break;
+ default:
+ dissect_unknown_chunk(chunk_tvb, length, type, chunk_tree, chunk_item);
+ break;
+ }
+
+ if (padding_length > 0)
+ proto_tree_add_item(chunk_tree, hf_chunk_padding, chunk_tvb, CHUNK_HEADER_OFFSET + length, padding_length, ENC_NA);
+
+ if (useinfo && ((type == SCTP_DATA_CHUNK_ID) || show_always_control_chunks))
+ col_set_fence(pinfo->cinfo, COL_INFO);
+
+ return result;
+}
+
+static void
+dissect_sctp_chunks(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_item *sctp_item, proto_tree *sctp_tree, sctp_half_assoc_t *ha, gboolean encapsulated)
+{
+ tvbuff_t *chunk_tvb;
+ guint16 length, total_length, remaining_length;
+ gint last_offset, offset;
+ gboolean sctp_item_length_set;
+ assoc_info_t tmpinfo;
+ infodata_t id_dir;
+
+ /* the common header of the datagram is already handled */
+ last_offset = 0;
+ offset = COMMON_HEADER_LENGTH;
+ sctp_item_length_set = FALSE;
+
+ while((remaining_length = tvb_reported_length_remaining(tvb, offset))) {
+ /* extract the chunk length and compute number of padding bytes */
+ length = tvb_get_ntohs(tvb, offset + CHUNK_LENGTH_OFFSET);
+ total_length = WS_ROUNDUP_4(length);
+
+ /* If we have less bytes than we need, throw an exception while dissecting
+ * the chunk--not when generating the chunk_tvb below.
+ */
+ total_length = MIN(total_length, remaining_length);
+
+ /* create a tvb for the chunk including the padding bytes */
+ chunk_tvb = tvb_new_subset_length_caplen(tvb, offset, MIN(total_length, tvb_captured_length_remaining(tvb, offset)), total_length);
+
+ /* save it in the sctp_info structure */
+ if (!encapsulated) {
+ if (sctp_info.number_of_tvbs < MAXIMUM_NUMBER_OF_TVBS)
+ sctp_info.tvb[sctp_info.number_of_tvbs++] = chunk_tvb;
+ else
+ sctp_info.incomplete = TRUE;
+ }
+ if (enable_association_indexing) {
+ tmpinfo.assoc_index = -1;
+ tmpinfo.sport = sctp_info.sport;
+ tmpinfo.dport = sctp_info.dport;
+ tmpinfo.vtag_reflected = FALSE;
+ if (tvb_get_guint8(chunk_tvb, CHUNK_TYPE_OFFSET) == SCTP_ABORT_CHUNK_ID) {
+ if ((tvb_get_guint8(chunk_tvb, CHUNK_FLAGS_OFFSET) & SCTP_ABORT_CHUNK_T_BIT) != 0) {
+ tmpinfo.vtag_reflected = TRUE;
+ }
+ }
+ if (tvb_get_guint8(chunk_tvb, CHUNK_TYPE_OFFSET) == SCTP_SHUTDOWN_COMPLETE_CHUNK_ID) {
+ if ((tvb_get_guint8(chunk_tvb, CHUNK_FLAGS_OFFSET) & SCTP_SHUTDOWN_COMPLETE_CHUNK_T_BIT) != 0) {
+ tmpinfo.vtag_reflected = TRUE;
+ }
+ }
+ if (tmpinfo.vtag_reflected) {
+ tmpinfo.verification_tag2 = sctp_info.verification_tag;
+ tmpinfo.verification_tag1 = 0;
+ }
+ else {
+ tmpinfo.verification_tag1 = sctp_info.verification_tag;
+ tmpinfo.verification_tag2 = 0;
+ }
+ if (tvb_get_guint8(chunk_tvb, CHUNK_TYPE_OFFSET) == SCTP_INIT_CHUNK_ID) {
+ tmpinfo.initiate_tag = tvb_get_ntohl(sctp_info.tvb[0], 4);
+ }
+ else {
+ tmpinfo.initiate_tag = 0;
+ }
+
+ id_dir = find_assoc_index(&tmpinfo, PINFO_FD_VISITED(pinfo));
+ sctp_info.assoc_index = id_dir.assoc_index;
+ sctp_info.direction = id_dir.direction;
+ } else {
+ sctp_info.assoc_index = -1;
+ sctp_info.direction = ASSOC_NOT_FOUND;
+ }
+
+ /* call dissect_sctp_chunk for the actual work */
+ if (dissect_sctp_chunk(chunk_tvb, pinfo, tree, sctp_tree, ha, !encapsulated) && (tree)) {
+ proto_item_set_len(sctp_item, offset - last_offset + DATA_CHUNK_HEADER_LENGTH);
+ sctp_item_length_set = TRUE;
+ offset += total_length;
+ last_offset = offset;
+ if (tvb_reported_length_remaining(tvb, offset) > 0) {
+ sctp_item = proto_tree_add_item(tree, proto_sctp, tvb, offset, -1, ENC_NA);
+ sctp_tree = proto_item_add_subtree(sctp_item, ett_sctp);
+ sctp_item_length_set = FALSE;
+ }
+ } else {
+ /* get rid of the dissected chunk */
+ offset += total_length;
+ }
+ }
+ if (!sctp_item_length_set && (tree)) {
+ proto_item_set_len(sctp_item, offset - last_offset);
+ }
+}
+
+static void
+dissect_sctp_packet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gboolean encapsulated)
+{
+ guint32 checksum = 0, calculated_crc32c = 0, calculated_adler32 = 0;
+ guint16 source_port, destination_port;
+ guint captured_length, reported_length;
+ gboolean crc32c_correct = FALSE, adler32_correct = FALSE;
+ proto_item *sctp_item, *hidden_item;
+ proto_tree *sctp_tree;
+ guint32 vtag;
+ sctp_half_assoc_t *ha = NULL;
+ proto_item *pi, *vt = NULL;
+
+ captured_length = tvb_captured_length(tvb);
+ reported_length = tvb_reported_length(tvb);
+ checksum = tvb_get_ntohl(tvb, CHECKSUM_OFFSET);
+ sctp_info.checksum_zero = (checksum == 0);
+
+ /* Only try to checksum the packet if we have all of it */
+ if ((captured_length == reported_length) &&
+ (captured_length >= COMMON_HEADER_LENGTH)) {
+
+ switch(sctp_checksum) {
+ case SCTP_CHECKSUM_NONE:
+ break;
+ case SCTP_CHECKSUM_ADLER32:
+ calculated_adler32 = sctp_adler32(tvb, captured_length);
+ adler32_correct = (checksum == calculated_adler32);
+ sctp_info.adler32_calculated = TRUE;
+ sctp_info.adler32_correct = adler32_correct;
+ break;
+ case SCTP_CHECKSUM_CRC32C:
+ calculated_crc32c = sctp_crc32c(tvb, captured_length);
+ crc32c_correct = (checksum == calculated_crc32c);
+ sctp_info.crc32c_calculated = TRUE;
+ sctp_info.crc32c_correct = crc32c_correct;
+ break;
+ case SCTP_CHECKSUM_AUTOMATIC:
+ calculated_adler32 = sctp_adler32(tvb, captured_length);
+ adler32_correct = (checksum == calculated_adler32);
+ calculated_crc32c = sctp_crc32c(tvb, captured_length);
+ crc32c_correct = (checksum == calculated_crc32c);
+ sctp_info.adler32_calculated = TRUE;
+ sctp_info.adler32_correct = adler32_correct;
+ sctp_info.crc32c_calculated = TRUE;
+ sctp_info.crc32c_correct = crc32c_correct;
+ break;
+ }
+ }
+
+ source_port = tvb_get_ntohs(tvb, SOURCE_PORT_OFFSET);
+ destination_port = tvb_get_ntohs(tvb, DESTINATION_PORT_OFFSET);
+ vtag = tvb_get_ntohl(tvb,VERIFICATION_TAG_OFFSET);
+
+ ha = get_half_assoc(pinfo, source_port, destination_port, vtag);
+
+ /* In the interest of speed, if "tree" is NULL, don't do any work not
+ necessary to generate protocol tree items. */
+ if (tree) {
+
+ /* create the sctp protocol tree */
+ if (show_port_numbers)
+ sctp_item = proto_tree_add_protocol_format(tree, proto_sctp, tvb, 0, -1,
+ "Stream Control Transmission Protocol, Src Port: %s (%u), Dst Port: %s (%u)",
+ sctp_port_to_display(pinfo->pool, source_port), source_port,
+ sctp_port_to_display(pinfo->pool, destination_port), destination_port);
+ else
+ sctp_item = proto_tree_add_item(tree, proto_sctp, tvb, 0, -1, ENC_NA);
+
+ sctp_tree = proto_item_add_subtree(sctp_item, ett_sctp);
+
+ /* add the components of the common header to the protocol tree */
+ proto_tree_add_item(sctp_tree, hf_source_port, tvb, SOURCE_PORT_OFFSET, SOURCE_PORT_LENGTH, ENC_BIG_ENDIAN);
+ proto_tree_add_item(sctp_tree, hf_destination_port, tvb, DESTINATION_PORT_OFFSET, DESTINATION_PORT_LENGTH, ENC_BIG_ENDIAN);
+ vt = proto_tree_add_item(sctp_tree, hf_verification_tag, tvb, VERIFICATION_TAG_OFFSET, VERIFICATION_TAG_LENGTH, ENC_BIG_ENDIAN);
+ hidden_item = proto_tree_add_item(sctp_tree, hf_port, tvb, SOURCE_PORT_OFFSET, SOURCE_PORT_LENGTH, ENC_BIG_ENDIAN);
+ proto_item_set_hidden(hidden_item);
+ hidden_item = proto_tree_add_item(sctp_tree, hf_port, tvb, DESTINATION_PORT_OFFSET, DESTINATION_PORT_LENGTH, ENC_BIG_ENDIAN);
+ proto_item_set_hidden(hidden_item);
+ } else {
+ sctp_tree = NULL;
+ sctp_item = NULL;
+ }
+
+ if (captured_length == reported_length) {
+ /* We have the whole packet */
+
+ switch(sctp_checksum) {
+ case SCTP_CHECKSUM_NONE:
+ proto_tree_add_checksum(sctp_tree, tvb, CHECKSUM_OFFSET, hf_checksum, hf_checksum_status, &ei_sctp_bad_sctp_checksum, pinfo, 0,
+ ENC_BIG_ENDIAN, PROTO_CHECKSUM_NO_FLAGS);
+ break;
+ case SCTP_CHECKSUM_ADLER32:
+ proto_tree_add_checksum(sctp_tree, tvb, CHECKSUM_OFFSET, hf_checksum_adler, hf_checksum_status, &ei_sctp_bad_sctp_checksum, pinfo, calculated_adler32,
+ ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
+ break;
+ case SCTP_CHECKSUM_CRC32C:
+ proto_tree_add_checksum(sctp_tree, tvb, CHECKSUM_OFFSET, hf_checksum_crc32c, hf_checksum_status, &ei_sctp_bad_sctp_checksum, pinfo, calculated_crc32c,
+ ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
+ break;
+ case SCTP_CHECKSUM_AUTOMATIC:
+ if ((adler32_correct) && !(crc32c_correct))
+ proto_tree_add_checksum(sctp_tree, tvb, CHECKSUM_OFFSET, hf_checksum_adler, hf_checksum_status, &ei_sctp_bad_sctp_checksum, pinfo, calculated_adler32,
+ ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
+ else if ((!adler32_correct) && (crc32c_correct))
+ proto_tree_add_checksum(sctp_tree, tvb, CHECKSUM_OFFSET, hf_checksum_crc32c, hf_checksum_status, &ei_sctp_bad_sctp_checksum, pinfo, calculated_crc32c,
+ ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
+ else {
+ proto_tree_add_checksum(sctp_tree, tvb, CHECKSUM_OFFSET, hf_checksum_adler, hf_checksum_status, &ei_sctp_bad_sctp_checksum, pinfo, calculated_adler32,
+ ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
+ proto_tree_add_checksum(sctp_tree, tvb, CHECKSUM_OFFSET, hf_checksum_crc32c, hf_checksum_status, &ei_sctp_bad_sctp_checksum, pinfo, calculated_crc32c,
+ ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY);
+ }
+ break;
+ }
+ } else {
+ /* We don't have the whole packet so we can't verify the checksum */
+ proto_tree_add_checksum(sctp_tree, tvb, CHECKSUM_OFFSET, hf_checksum, hf_checksum_status, &ei_sctp_bad_sctp_checksum, pinfo, 0,
+ ENC_BIG_ENDIAN, PROTO_CHECKSUM_NO_FLAGS);
+ }
+
+ /* add all chunks of the sctp datagram to the protocol tree */
+ dissect_sctp_chunks(tvb, pinfo, tree, sctp_item, sctp_tree, ha, encapsulated);
+
+ /* add assoc_index and move it behind the verification tag */
+ if (enable_association_indexing) {
+ pi = proto_tree_add_uint(sctp_tree, hf_sctp_assoc_index, tvb, 0 , 0, sctp_info.assoc_index);
+ }
+ else {
+ pi = proto_tree_add_uint_format_value(sctp_tree, hf_sctp_assoc_index, tvb, 0 , 0, sctp_info.assoc_index, "disabled (enable in preferences)");
+ }
+ proto_item_set_generated(pi);
+ proto_tree_move_item(sctp_tree, vt, pi);
+}
+
+static gboolean
+capture_sctp(const guchar *pd _U_, int offset _U_, int len _U_, capture_packet_info_t *cpinfo, const union wtap_pseudo_header *pseudo_header _U_)
+{
+ capture_dissector_increment_count(cpinfo, proto_sctp);
+ return TRUE;
+}
+
+static int
+dissect_sctp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
+{
+ guint16 source_port, destination_port;
+ guint number_of_ppid;
+
+ /* Extract the common header */
+ source_port = tvb_get_ntohs(tvb, SOURCE_PORT_OFFSET);
+ destination_port = tvb_get_ntohs(tvb, DESTINATION_PORT_OFFSET);
+
+ /* update pi structure */
+ pinfo->ptype = PT_SCTP;
+ pinfo->srcport = source_port;
+ pinfo->destport = destination_port;
+
+ /* make entry in the Protocol column on summary display */
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "SCTP");
+
+ /* Clear entries in Info column on summary display */
+ col_clear(pinfo->cinfo, COL_INFO);
+
+
+ for(number_of_ppid = 0; number_of_ppid < MAX_NUMBER_OF_PPIDS; number_of_ppid++) {
+ p_add_proto_data(pinfo->pool, pinfo, proto_sctp, number_of_ppid, GUINT_TO_POINTER(LAST_PPID));
+ }
+
+ /* The tvb array in struct _sctp_info is huge: currently 2k pointers.
+ * We know (by the value of 'number_of_tvbs') which of these entries have
+ * been used, so don't memset() the array. This saves us from zeroing out
+ * 8k (4-byte pointers) or 16k (8-byte pointers) of memory every time we
+ * dissect a packet (saving quite a bit of time!).
+ */
+ sctp_info.incomplete = 0;
+ sctp_info.adler32_calculated = 0;
+ sctp_info.adler32_correct = 0;
+ sctp_info.crc32c_calculated = 0;
+ sctp_info.crc32c_correct = 0;
+ sctp_info.vtag_reflected = 0;
+ sctp_info.number_of_tvbs = 0;
+ sctp_info.verification_tag = tvb_get_ntohl(tvb, VERIFICATION_TAG_OFFSET);
+
+ sctp_info.sport = pinfo->srcport;
+ sctp_info.dport = pinfo->destport;
+ set_address(&sctp_info.ip_src, pinfo->src.type, pinfo->src.len, pinfo->src.data);
+ set_address(&sctp_info.ip_dst, pinfo->dst.type, pinfo->dst.len, pinfo->dst.data);
+
+ p_add_proto_data(pinfo->pool, pinfo, hf_source_port, pinfo->curr_layer_num, GUINT_TO_POINTER(pinfo->srcport));
+ p_add_proto_data(pinfo->pool, pinfo, hf_destination_port, pinfo->curr_layer_num, GUINT_TO_POINTER(pinfo->destport));
+
+ dissect_sctp_packet(tvb, pinfo, tree, FALSE);
+ if (!pinfo->flags.in_error_pkt && sctp_info.number_of_tvbs > 0)
+ tap_queue_packet(sctp_tap, pinfo, &sctp_info);
+
+ return tvb_captured_length(tvb);
+}
+
+/* Register the protocol with Wireshark */
+void
+proto_register_sctp(void)
+{
+
+ /* Setup list of header fields */
+ static hf_register_info hf[] = {
+ { &hf_source_port, { "Source port", "sctp.srcport", FT_UINT16, BASE_PT_SCTP, NULL, 0x0, NULL, HFILL } },
+ { &hf_destination_port, { "Destination port", "sctp.dstport", FT_UINT16, BASE_PT_SCTP, NULL, 0x0, NULL, HFILL } },
+ { &hf_port, { "Port", "sctp.port", FT_UINT16, BASE_PT_SCTP, NULL, 0x0, NULL, HFILL } },
+ { &hf_verification_tag, { "Verification tag", "sctp.verification_tag", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
+ { &hf_checksum, { "Checksum", "sctp.checksum", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
+ { &hf_checksum_adler, { "Checksum (Adler)", "sctp.checksum", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
+ { &hf_checksum_crc32c, { "Checksum (CRC32C)", "sctp.checksum", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
+ { &hf_checksum_status, { "Checksum Status", "sctp.checksum.status", FT_UINT8, BASE_NONE, VALS(proto_checksum_vals), 0x0, NULL, HFILL } },
+ { &hf_chunk_type, { "Chunk type", "sctp.chunk_type", FT_UINT8, BASE_DEC, VALS(chunk_type_values), 0x0, NULL, HFILL } },
+ { &hf_chunk_flags, { "Chunk flags", "sctp.chunk_flags", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } },
+ { &hf_chunk_bit_1, { "Bit", "sctp.chunk_bit_1", FT_BOOLEAN, 8, TFS(&sctp_chunk_bit_1_value), SCTP_CHUNK_BIT_1, NULL, HFILL } },
+ { &hf_chunk_bit_2, { "Bit", "sctp.chunk_bit_2", FT_BOOLEAN, 8, TFS(&sctp_chunk_bit_2_value), SCTP_CHUNK_BIT_2, NULL, HFILL } },
+ { &hf_chunk_length, { "Chunk length", "sctp.chunk_length", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_chunk_padding, { "Chunk padding", "sctp.chunk_padding", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_chunk_value, { "Chunk value", "sctp.chunk_value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_cookie, { "Cookie", "sctp.cookie", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_initiate_tag, { "Initiate tag", "sctp.initiate_tag", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
+ { &hf_init_chunk_initiate_tag, { "Initiate tag", "sctp.init_initiate_tag", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
+ { &hf_init_chunk_adv_rec_window_credit, { "Advertised receiver window credit (a_rwnd)", "sctp.init_credit", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_init_chunk_number_of_outbound_streams, { "Number of outbound streams", "sctp.init_nr_out_streams", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_init_chunk_number_of_inbound_streams, { "Number of inbound streams", "sctp.init_nr_in_streams", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_init_chunk_initial_tsn, { "Initial TSN", "sctp.init_initial_tsn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_initack_chunk_initiate_tag, { "Initiate tag", "sctp.initack_initiate_tag", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
+ { &hf_initack_chunk_adv_rec_window_credit, { "Advertised receiver window credit (a_rwnd)", "sctp.initack_credit", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_initack_chunk_number_of_outbound_streams, { "Number of outbound streams", "sctp.initack_nr_out_streams", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_initack_chunk_number_of_inbound_streams, { "Number of inbound streams", "sctp.initack_nr_in_streams", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_initack_chunk_initial_tsn, { "Initial TSN", "sctp.initack_initial_tsn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_data_chunk_tsn, { "Transmission sequence number (relative)", "sctp.data_tsn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_data_chunk_tsn_raw, { "Transmission sequence number (absolute)", "sctp.data_tsn_raw", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_data_chunk_stream_id, { "Stream identifier", "sctp.data_sid", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
+ { &hf_data_chunk_stream_seq_number, { "Stream sequence number", "sctp.data_ssn", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_data_chunk_payload_proto_id, { "Payload protocol identifier", "sctp.data_payload_proto_id", FT_UINT32, BASE_DEC|BASE_EXT_STRING, &sctpppid_val_ext, 0x0, NULL, HFILL } },
+ { &hf_idata_chunk_reserved, { "Reserved", "sctp.data_reserved", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_idata_chunk_mid, { "Message identifier", "sctp.data_mid", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_idata_chunk_fsn, { "Fragment sequence number", "sctp.data_fsn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_data_chunk_e_bit, { "E-Bit", "sctp.data_e_bit", FT_BOOLEAN, 8, TFS(&sctp_data_chunk_e_bit_value), SCTP_DATA_CHUNK_E_BIT, NULL, HFILL } },
+ { &hf_data_chunk_b_bit, { "B-Bit", "sctp.data_b_bit", FT_BOOLEAN, 8, TFS(&sctp_data_chunk_b_bit_value), SCTP_DATA_CHUNK_B_BIT, NULL, HFILL } },
+ { &hf_data_chunk_u_bit, { "U-Bit", "sctp.data_u_bit", FT_BOOLEAN, 8, TFS(&sctp_data_chunk_u_bit_value), SCTP_DATA_CHUNK_U_BIT, NULL, HFILL } },
+ { &hf_data_chunk_i_bit, { "I-Bit", "sctp.data_i_bit", FT_BOOLEAN, 8, TFS(&sctp_data_chunk_i_bit_value), SCTP_DATA_CHUNK_I_BIT, NULL, HFILL } },
+ { &hf_sack_chunk_ns, { "Nonce sum", "sctp.sack_nonce_sum", FT_UINT8, BASE_DEC, NULL, SCTP_SACK_CHUNK_NS_BIT, NULL, HFILL } },
+ { &hf_sack_chunk_cumulative_tsn_ack, { "Cumulative TSN ACK (relative)", "sctp.sack_cumulative_tsn_ack", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_sack_chunk_cumulative_tsn_ack_raw, { "Cumulative TSN ACK (absolute)", "sctp.sack_cumulative_tsn_ack_raw", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_sack_chunk_adv_rec_window_credit, { "Advertised receiver window credit (a_rwnd)", "sctp.sack_a_rwnd", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_sack_chunk_number_of_gap_blocks, { "Number of gap acknowledgement blocks", "sctp.sack_number_of_gap_blocks", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_sack_chunk_number_of_dup_tsns, { "Number of duplicated TSNs", "sctp.sack_number_of_duplicated_tsns", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_sack_chunk_gap_block_start, { "Start", "sctp.sack_gap_block_start", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_sack_chunk_gap_block_start_tsn, { "Start TSN", "sctp.sack_gap_block_start_tsn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_sack_chunk_gap_block_end, { "End", "sctp.sack_gap_block_end", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_sack_chunk_gap_block_end_tsn, { "End TSN", "sctp.sack_gap_block_end_tsn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_sack_chunk_number_tsns_gap_acked, { "Number of TSNs in gap acknowledgement blocks", "sctp.sack_number_of_tsns_gap_acked", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_sack_chunk_duplicate_tsn, { "Duplicate TSN", "sctp.sack_duplicate_tsn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_nr_sack_chunk_ns, { "Nonce sum", "sctp.nr_sack_nonce_sum", FT_UINT8, BASE_DEC, NULL, SCTP_NR_SACK_CHUNK_NS_BIT, NULL, HFILL } },
+ { &hf_nr_sack_chunk_cumulative_tsn_ack, { "Cumulative TSN ACK", "sctp.nr_sack_cumulative_tsn_ack", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_nr_sack_chunk_adv_rec_window_credit, { "Advertised receiver window credit (a_rwnd)", "sctp.nr_sack_a_rwnd", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_nr_sack_chunk_number_of_gap_blocks, { "Number of gap acknowledgement blocks", "sctp.nr_sack_number_of_gap_blocks", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_nr_sack_chunk_number_of_nr_gap_blocks, { "Number of nr-gap acknowledgement blocks", "sctp.nr_sack_number_of_nr_gap_blocks", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_nr_sack_chunk_number_of_dup_tsns, { "Number of duplicated TSNs", "sctp.nr_sack_number_of_duplicated_tsns", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_nr_sack_chunk_reserved, { "Reserved", "sctp.nr_sack_reserved", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
+ { &hf_nr_sack_chunk_gap_block_start, { "Start", "sctp.nr_sack_gap_block_start", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_nr_sack_chunk_gap_block_start_tsn, { "Start TSN", "sctp.nr_sack_gap_block_start_tsn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_nr_sack_chunk_gap_block_end, { "End", "sctp.nr_sack_gap_block_end", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_nr_sack_chunk_gap_block_end_tsn, { "End TSN", "sctp.nr_sack_gap_block_end_tsn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_nr_sack_chunk_number_tsns_gap_acked, { "Number of TSNs in gap acknowledgement blocks", "sctp.nr_sack_number_of_tsns_gap_acked", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_nr_sack_chunk_nr_gap_block_start, { "Start", "sctp.nr_sack_nr_gap_block_start", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_nr_sack_chunk_nr_gap_block_start_tsn, { "Start TSN", "sctp.nr_sack_nr_gap_block_start_tsn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_nr_sack_chunk_nr_gap_block_end, { "End", "sctp.nr_sack_nr_gap_block_end", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_nr_sack_chunk_nr_gap_block_end_tsn, { "End TSN", "sctp.nr_sack_nr_gap_block_end_tsn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_nr_sack_chunk_number_tsns_nr_gap_acked, { "Number of TSNs in nr-gap acknowledgement blocks","sctp.nr_sack_number_of_tsns_nr_gap_acked", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_nr_sack_chunk_duplicate_tsn, { "Duplicate TSN", "sctp.nr_sack_duplicate_tsn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_shutdown_chunk_cumulative_tsn_ack, { "Cumulative TSN Ack", "sctp.shutdown_cumulative_tsn_ack", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_ecne_chunk_lowest_tsn, { "Lowest TSN", "sctp.ecne_lowest_tsn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_cwr_chunk_lowest_tsn, { "Lowest TSN", "sctp.cwr_lowest_tsn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_shutdown_complete_chunk_t_bit, { "T-Bit", "sctp.shutdown_complete_t_bit", FT_BOOLEAN, 8, TFS(&sctp_shutdown_complete_chunk_t_bit_value), SCTP_SHUTDOWN_COMPLETE_CHUNK_T_BIT, NULL, HFILL } },
+ { &hf_abort_chunk_t_bit, { "T-Bit", "sctp.abort_t_bit", FT_BOOLEAN, 8, TFS(&sctp_shutdown_complete_chunk_t_bit_value), SCTP_ABORT_CHUNK_T_BIT, NULL, HFILL } },
+ { &hf_forward_tsn_chunk_tsn, { "New cumulative TSN", "sctp.forward_tsn_tsn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_forward_tsn_chunk_sid, { "Stream identifier", "sctp.forward_tsn_sid", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_forward_tsn_chunk_ssn, { "Stream sequence number", "sctp.forward_tsn_ssn", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_i_forward_tsn_chunk_tsn, { "New cumulative TSN", "sctp.i_forward_tsn_tsn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_i_forward_tsn_chunk_sid, { "Stream identifier", "sctp.i_forward_tsn_sid", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_i_forward_tsn_chunk_flags, { "Flags", "sctp.i_forward_tsn_flags", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } },
+ { &hf_i_forward_tsn_chunk_res, { "Reserved", "sctp.i_forward_tsn_res", FT_UINT16, BASE_DEC, NULL, SCTP_I_FORWARD_TSN_CHUNK_RES_MASK, NULL, HFILL } },
+ { &hf_i_forward_tsn_chunk_u_bit, { "U-Bit", "sctp.i_forward_tsn_u_bit", FT_BOOLEAN, 16, TFS(&sctp_i_forward_tsn_chunk_u_bit_value), SCTP_I_FORWARD_TSN_CHUNK_U_BIT, NULL, HFILL } },
+ { &hf_i_forward_tsn_chunk_mid, { "Message identifier", "sctp.forward_tsn_mid", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_parameter_type, { "Parameter type", "sctp.parameter_type", FT_UINT16, BASE_HEX, VALS(parameter_identifier_values), 0x0, NULL, HFILL } },
+ { &hf_parameter_length, { "Parameter length", "sctp.parameter_length", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_parameter_value, { "Parameter value", "sctp.parameter_value", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_parameter_padding, { "Parameter padding", "sctp.parameter_padding", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_parameter_bit_1, { "Bit", "sctp.parameter_bit_1", FT_BOOLEAN, 16, TFS(&sctp_parameter_bit_1_value), SCTP_PARAMETER_BIT_1, NULL, HFILL } },
+ { &hf_parameter_bit_2, { "Bit", "sctp.parameter_bit_2", FT_BOOLEAN, 16, TFS(&sctp_parameter_bit_2_value), SCTP_PARAMETER_BIT_2, NULL, HFILL } },
+ { &hf_ipv4_address, { "IP Version 4 address", "sctp.parameter_ipv4_address", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_ipv6_address, { "IP Version 6 address", "sctp.parameter_ipv6_address", FT_IPv6, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_heartbeat_info, { "Heartbeat information", "sctp.parameter_heartbeat_information", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_state_cookie, { "State cookie", "sctp.parameter_state_cookie", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_cookie_preservative_increment, { "Suggested Cookie life-span increment (msec)", "sctp.parameter_cookie_preservative_incr", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_hostname, { "Hostname", "sctp.parameter_hostname", FT_STRINGZ, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_supported_address_type, { "Supported address type", "sctp.parameter_supported_address_type", FT_UINT16, BASE_DEC, VALS(address_types_values), 0x0, NULL, HFILL } },
+ { &hf_stream_reset_req_seq_nr, { "Re-configuration request sequence number", "sctp.parameter_reconfig_request_sequence_number", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_stream_reset_rsp_seq_nr, { "Re-configuration response sequence number", "sctp.parameter_reconfig_response_sequence_number", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_senders_last_assigned_tsn, { "Senders last assigned TSN", "sctp.parameter_senders_last_assigned_tsn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_senders_next_tsn, { "Senders next TSN", "sctp.parameter_senders_next_tsn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_receivers_next_tsn, { "Receivers next TSN", "sctp.parameter_receivers_next_tsn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_stream_reset_rsp_result, { "Result", "sctp.parameter_reconfig_response_result", FT_UINT32, BASE_DEC, VALS(stream_reset_result_values), 0x0, NULL, HFILL } },
+ { &hf_stream_reset_sid, { "Stream Identifier", "sctp.parameter_reconfig_sid", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_add_outgoing_streams_number_streams, { "Number of streams", "sctp.parameter_add_outgoing_streams_number", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_add_outgoing_streams_reserved, { "Reserved", "sctp.parameter_add_outgoing_streams_reserved", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_add_incoming_streams_number_streams, { "Number of streams", "sctp.parameter_add_incoming_streams_number", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_add_incoming_streams_reserved, { "Reserved", "sctp.parameter_add_incoming_streams_reserved", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_asconf_seq_nr, { "Sequence number", "sctp.asconf_seq_nr_number", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
+ { &hf_asconf_ack_seq_nr, { "Sequence number", "sctp.asconf_ack_seq_nr_number", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
+ { &hf_correlation_id, { "Correlation_id", "sctp.correlation_id", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
+ { &hf_adap_indication, { "Indication", "sctp.adaptation_layer_indication", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, HFILL } },
+ { &hf_zero_checksum_edmid, { "Error Detection Method Identifier", "sctp.edmid", FT_UINT32, BASE_DEC, VALS(edmid_values), 0x0, NULL, HFILL } },
+ { &hf_random_number, { "Random number", "sctp.random_number", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_chunks_to_auth, { "Chunk type", "sctp.chunk_type_to_auth", FT_UINT8, BASE_DEC, VALS(chunk_type_values), 0x0, NULL, HFILL } },
+ { &hf_hmac_id, { "HMAC identifier", "sctp.hmac_id", FT_UINT16, BASE_DEC, VALS(hmac_id_values), 0x0, NULL, HFILL } },
+ { &hf_hmac, { "HMAC", "sctp.hmac", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_shared_key_id, { "Shared key identifier", "sctp.shared_key_id", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_supported_chunk_type, { "Supported chunk type", "sctp.supported_chunk_type", FT_UINT8, BASE_DEC, VALS(chunk_type_values), 0x0, NULL, HFILL } },
+ { &hf_cause_code, { "Cause code", "sctp.cause_code", FT_UINT16, BASE_HEX, VALS(cause_code_values), 0x0, NULL, HFILL } },
+ { &hf_cause_length, { "Cause length", "sctp.cause_length", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_cause_info, { "Cause information", "sctp.cause_information", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_cause_padding, { "Cause padding", "sctp.cause_padding", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_cause_stream_identifier, { "Stream identifier", "sctp.cause_stream_identifier", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_cause_reserved, { "Reserved", "sctp.cause_reserved", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_cause_number_of_missing_parameters, { "Number of missing parameters", "sctp.cause_nr_of_missing_parameters", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_cause_missing_parameter_type, { "Missing parameter type", "sctp.cause_missing_parameter_type", FT_UINT16, BASE_HEX, VALS(parameter_identifier_values), 0x0, NULL, HFILL } },
+ { &hf_cause_measure_of_staleness, { "Measure of staleness in usec", "sctp.cause_measure_of_staleness", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_cause_tsn, { "TSN", "sctp.cause_tsn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_pktdrop_chunk_m_bit, { "M-Bit", "sctp.pckdrop_m_bit", FT_BOOLEAN, 8, TFS(&sctp_pktdropk_m_bit_value), SCTP_PKTDROP_CHUNK_M_BIT, NULL, HFILL } },
+ { &hf_pktdrop_chunk_b_bit, { "B-Bit", "sctp.pckdrop_b_bit", FT_BOOLEAN, 8, TFS(&sctp_pktdropk_b_bit_value), SCTP_PKTDROP_CHUNK_B_BIT, NULL, HFILL } },
+ { &hf_pktdrop_chunk_t_bit, { "T-Bit", "sctp.pckdrop_t_bit", FT_BOOLEAN, 8, TFS(&sctp_pktdropk_t_bit_value), SCTP_PKTDROP_CHUNK_T_BIT, NULL, HFILL } },
+ { &hf_pktdrop_chunk_bandwidth, { "Bandwidth", "sctp.pktdrop_bandwidth", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_pktdrop_chunk_queuesize, { "Queuesize", "sctp.pktdrop_queuesize", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_pktdrop_chunk_truncated_length, { "Truncated length", "sctp.pktdrop_truncated_length", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_pktdrop_chunk_reserved, { "Reserved", "sctp.pktdrop_reserved", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_pktdrop_chunk_data_field, { "Data field", "sctp.pktdrop_datafield", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_pad_chunk_padding_data, { "Padding data", "sctp.padding_data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+
+ { &hf_sctp_fragment, { "SCTP Fragment", "sctp.fragment", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_sctp_fragments, { "Reassembled SCTP Fragments", "sctp.fragments", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_sctp_reassembled_in, { "Reassembled Message in frame", "sctp.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_sctp_duplicate, { "Fragment already seen in frame", "sctp.duplicate", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+
+ { &hf_sctp_data_rtt, { "The RTT to SACK was", "sctp.data_rtt", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_sctp_sack_rtt, { "The RTT since DATA was", "sctp.sack_rtt", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_sctp_rto, { "Retransmitted after", "sctp.retransmission_time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_sctp_retransmission, { "This TSN is a retransmission of one in frame", "sctp.retransmission", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_sctp_retransmitted, { "This TSN is retransmitted in frame", "sctp.retransmitted", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_sctp_retransmitted_count, { "TSN was retransmitted this many times", "sctp.retransmitted_count", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_sctp_acked, { "This chunk is acked in frame", "sctp.acked", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_sctp_ack_tsn, { "Acknowledges TSN", "sctp.ack", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
+ { &hf_sctp_ack_frame, { "Acknowledges TSN in frame", "sctp.ack_frame", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_ACK), 0x0, NULL, HFILL } },
+ { &hf_sctp_retransmitted_after_ack, { "Chunk was acked prior to retransmission", "sctp.retransmitted_after_ack", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL } },
+ { &hf_sctp_assoc_index, { "Association index", "sctp.assoc_index", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } }
+
+ };
+
+ /* Setup protocol subtree array */
+ static gint *ett[] = {
+ &ett_sctp,
+ &ett_sctp_chunk,
+ &ett_sctp_chunk_parameter,
+ &ett_sctp_chunk_cause,
+ &ett_sctp_chunk_type,
+ &ett_sctp_data_chunk_flags,
+ &ett_sctp_sack_chunk_flags,
+ &ett_sctp_nr_sack_chunk_flags,
+ &ett_sctp_abort_chunk_flags,
+ &ett_sctp_shutdown_complete_chunk_flags,
+ &ett_sctp_pktdrop_chunk_flags,
+ &ett_sctp_parameter_type,
+ &ett_sctp_sack_chunk_gap_block,
+ &ett_sctp_sack_chunk_gap_block_start,
+ &ett_sctp_sack_chunk_gap_block_end,
+ &ett_sctp_nr_sack_chunk_gap_block,
+ &ett_sctp_nr_sack_chunk_gap_block_start,
+ &ett_sctp_nr_sack_chunk_gap_block_end,
+ &ett_sctp_nr_sack_chunk_nr_gap_block,
+ &ett_sctp_nr_sack_chunk_nr_gap_block_start,
+ &ett_sctp_nr_sack_chunk_nr_gap_block_end,
+ &ett_sctp_unrecognized_parameter_parameter,
+ &ett_sctp_i_forward_tsn_chunk_flags,
+ &ett_sctp_fragments,
+ &ett_sctp_fragment,
+ &ett_sctp_ack,
+ &ett_sctp_acked,
+ &ett_sctp_tsn,
+ &ett_sctp_tsn_retransmission,
+ &ett_sctp_tsn_retransmitted_count,
+ &ett_sctp_tsn_retransmitted
+ };
+
+ static ei_register_info ei[] = {
+ { &ei_sctp_tsn_retransmitted, { "sctp.retransmission.expert", PI_SEQUENCE, PI_NOTE, "Retransmitted TSN", EXPFILL }},
+ { &ei_sctp_retransmitted_after_ack, { "sctp.retransmitted_after_ack.expert", PI_SEQUENCE, PI_WARN, "This TSN was acked prior to this retransmission (reneged ack?).", EXPFILL }},
+ { &ei_sctp_tsn_retransmitted_more_than_twice, { "sctp.retransmission.more_than_twice", PI_SEQUENCE, PI_WARN, "This TSN was retransmitted more than 2 times.", EXPFILL }},
+ { &ei_sctp_parameter_padding, { "sctp.parameter_padding.expert", PI_MALFORMED, PI_NOTE, "The padding of this final parameter should be the padding of the chunk.", EXPFILL }},
+ { &ei_sctp_parameter_length, { "sctp.parameter_length.bad", PI_MALFORMED, PI_ERROR, "Parameter length bad", EXPFILL }},
+ { &ei_sctp_sack_chunk_adv_rec_window_credit, { "sctp.sack_a_rwnd.expert", PI_SEQUENCE, PI_NOTE, "Zero Advertised Receiver Window Credit", EXPFILL }},
+ { &ei_sctp_sack_chunk_gap_block_malformed, { "sctp.sack_gap_block_malformed", PI_PROTOCOL, PI_ERROR, "Malformed gap block.", EXPFILL }},
+ { &ei_sctp_sack_chunk_gap_block_out_of_order, { "sctp.sack_gap_block_out_of_order", PI_PROTOCOL, PI_WARN, "Gap blocks not in strict order.", EXPFILL }},
+ { &ei_sctp_sack_chunk_number_tsns_gap_acked_100, { "sctp.sack_number_of_tsns_gap_acked.100", PI_SEQUENCE, PI_WARN, "More than 100 TSNs were gap-acknowledged in this SACK.", EXPFILL }},
+ { &ei_sctp_nr_sack_chunk_number_tsns_gap_acked_100, { "sctp.nr_sack_number_of_tsns_gap_acked.100", PI_SEQUENCE, PI_WARN, "More than 100 TSNs were gap-acknowledged in this NR-SACK.", EXPFILL }},
+ { &ei_sctp_nr_sack_chunk_number_tsns_nr_gap_acked_100, { "sctp.nr_sack_number_of_tsns_nr_gap_acked.100", PI_SEQUENCE, PI_WARN, "More than 100 TSNs were nr-gap-acknowledged in this NR-SACK.", EXPFILL }},
+ { &ei_sctp_chunk_length_bad, { "sctp.chunk_length.bad", PI_MALFORMED, PI_ERROR, "Chunk length bad", EXPFILL }},
+ { &ei_sctp_bad_sctp_checksum, { "sctp.checksum_bad.expert", PI_CHECKSUM, PI_ERROR, "Bad SCTP checksum.", EXPFILL }},
+ };
+
+ static const enum_val_t sctp_checksum_options[] = {
+ { "none", "None", SCTP_CHECKSUM_NONE },
+ { "adler-32", "Adler 32", SCTP_CHECKSUM_ADLER32 },
+ { "crc-32c", "CRC 32c", SCTP_CHECKSUM_CRC32C },
+ { "automatic", "Automatic", SCTP_CHECKSUM_AUTOMATIC},
+ { NULL, NULL, 0 }
+ };
+
+ /* Decode As handling */
+ static build_valid_func sctp_da_src_values[1] = {sctp_src_value};
+ static build_valid_func sctp_da_dst_values[1] = {sctp_dst_value};
+ static build_valid_func sctp_da_both_values[2] = {sctp_src_value, sctp_dst_value};
+ static decode_as_value_t sctp_da_port_values[3] = {{sctp_src_prompt, 1, sctp_da_src_values}, {sctp_dst_prompt, 1, sctp_da_dst_values}, {sctp_both_prompt, 2, sctp_da_both_values}};
+ static decode_as_t sctp_da_port = {"sctp", "sctp.port", 3, 2, sctp_da_port_values, "SCTP", "port(s) as",
+ decode_as_default_populate_list, decode_as_default_reset, decode_as_default_change, NULL};
+
+ static build_valid_func sctp_da_ppi_build_value1[1] = {sctp_ppi_value1};
+ static build_valid_func sctp_da_ppi_build_value2[1] = {sctp_ppi_value2};
+ static decode_as_value_t sctp_da_ppi_values[2] = {{sctp_ppi_prompt1, 1, sctp_da_ppi_build_value1}, {sctp_ppi_prompt2, 1, sctp_da_ppi_build_value2}};
+ static decode_as_t sctp_da_ppi = {"sctp", "sctp.ppi", 2, 0, sctp_da_ppi_values, "SCTP", NULL,
+ decode_as_default_populate_list, decode_as_default_reset, decode_as_default_change, NULL};
+
+ /* UAT for header fields */
+ static uat_field_t custom_types_uat_fields[] = {
+ UAT_FLD_NONE(type_fields, type_id, "Chunk ID", "IANA chunk type ID"),
+ UAT_FLD_CSTRING(type_fields, type_name, "Type name", "Chunk Type name"),
+ UAT_FLD_VS(type_fields, type_enable, "Visibility", chunk_enabled, "Hide or show the type in the chunk statistics"),
+ UAT_END_FIELDS
+ };
+
+ module_t *sctp_module;
+ expert_module_t* expert_sctp;
+ uat_t* chunk_types_uat;
+
+ chunk_types_uat = uat_new("Chunk types for the statistics dialog",
+ sizeof(type_field_t),
+ "statistics_chunk_types",
+ TRUE,
+ &type_fields,
+ &num_type_fields,
+ 0,
+ NULL,
+ sctp_chunk_type_copy_cb,
+ sctp_chunk_type_update_cb,
+ sctp_chunk_type_free_cb,
+ NULL,
+ NULL,
+ custom_types_uat_fields
+);
+
+ /* Register the protocol name and description */
+ proto_sctp = proto_register_protocol("Stream Control Transmission Protocol", "SCTP", "sctp");
+ sctp_module = prefs_register_protocol(proto_sctp, NULL);
+ prefs_register_bool_preference(sctp_module, "show_port_numbers_in_tree",
+ "Show port numbers in the protocol tree",
+ "Show source and destination port numbers in the protocol tree",
+ &show_port_numbers);
+ prefs_register_bool_preference(sctp_module, "relative_tsns", "Relative TSNs",
+ "Use relative TSNs instead of absolute ones",
+ &show_relative_tsns);
+ prefs_register_enum_preference(sctp_module, "checksum", "Checksum type",
+ "The type of checksum used in SCTP packets",
+ &sctp_checksum, sctp_checksum_options, FALSE);
+ prefs_register_bool_preference(sctp_module, "show_always_control_chunks",
+ "Show always control chunks",
+ "Show always SCTP control chunks in the Info column",
+ &show_always_control_chunks);
+ prefs_register_bool_preference(sctp_module, "try_heuristic_first",
+ "Try heuristic sub-dissectors first",
+ "Try to decode a packet using an heuristic sub-dissector before using a sub-dissector registered to a specific port or PPI",
+ &try_heuristic_first);
+ prefs_register_bool_preference(sctp_module, "reassembly",
+ "Reassemble fragmented SCTP user messages",
+ "Whether fragmented SCTP user messages should be reassembled",
+ &use_reassembly);
+ prefs_register_bool_preference(sctp_module, "tsn_analysis",
+ "Enable TSN analysis",
+ "Match TSNs and their SACKs",
+ &enable_tsn_analysis);
+ prefs_register_bool_preference(sctp_module, "association_index",
+ "Enable Association indexing (Can be CPU intense)",
+ "Match verification tags (CPU intense)",
+ &enable_association_indexing);
+ prefs_register_bool_preference(sctp_module, "ulp_dissection",
+ "Dissect upper layer protocols",
+ "Dissect upper layer protocols",
+ &enable_ulp_dissection);
+ prefs_register_uat_preference_qt(sctp_module, "statistics_chunk_types",
+ "Select the chunk types for the statistics dialog",
+ "Select the chunk types for the statistics dialog",
+ chunk_types_uat);
+
+ /* Required function calls to register the header fields and subtrees used */
+ proto_register_field_array(proto_sctp, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+ expert_sctp = expert_register_protocol(proto_sctp);
+ expert_register_field_array(expert_sctp, ei, array_length(ei));
+
+ sctp_tap = register_tap("sctp");
+ exported_pdu_tap = find_tap_id(EXPORT_PDU_TAP_NAME_LAYER_3);
+ /* subdissector code */
+ sctp_port_dissector_table = register_dissector_table("sctp.port", "SCTP port", proto_sctp, FT_UINT16, BASE_DEC);
+ sctp_ppi_dissector_table = register_dissector_table("sctp.ppi", "SCTP payload protocol identifier", proto_sctp, FT_UINT32, BASE_HEX);
+
+ sctp_handle = register_dissector("sctp", dissect_sctp, proto_sctp);
+ sctp_heur_subdissector_list = register_heur_dissector_list("sctp", proto_sctp);
+
+ register_init_routine(sctp_init);
+ register_cleanup_routine(sctp_cleanup);
+
+ dirs_by_ptvtag = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
+ dirs_by_ptaddr = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
+
+ register_decode_as(&sctp_da_port);
+ register_decode_as(&sctp_da_ppi);
+
+ register_conversation_table(proto_sctp, FALSE, sctp_conversation_packet, sctp_endpoint_packet);
+}
+
+void
+proto_reg_handoff_sctp(void)
+{
+ capture_dissector_handle_t sctp_cap_handle;
+
+ dissector_add_uint("wtap_encap", WTAP_ENCAP_SCTP, sctp_handle);
+ dissector_add_uint("ip.proto", IP_PROTO_SCTP, sctp_handle);
+ dissector_add_uint_with_preference("udp.port", UDP_TUNNELING_PORT, sctp_handle);
+ dissector_add_uint_with_preference("dtls.port", UDP_TUNNELING_PORT, sctp_handle);
+ sctp_cap_handle = create_capture_dissector_handle(capture_sctp, proto_sctp);
+ capture_dissector_add_uint("ip.proto", IP_PROTO_SCTP, sctp_cap_handle);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */