handle_export_pdu_conversation(packet_info *pinfo, tvbuff_t *tvb, int src_port, int dst_port, struct tcpinfo *tcpinfo)
{
    if (have_tap_listener(exported_pdu_tap)) {
        conversation_t *conversation = find_conversation(pinfo->num, &pinfo->src, &pinfo->dst, ENDPOINT_TCP, src_port, dst_port, 0);
        if (conversation != NULL)
        {
            dissector_handle_t handle = (dissector_handle_t)wmem_tree_lookup32_le(conversation->dissector_tree, pinfo->num);
            if (handle != NULL)
            {
                exp_pdu_data_item_t exp_pdu_data_dissector_data = {exp_pdu_tcp_dissector_data_size, exp_pdu_tcp_dissector_data_populate_data, NULL};
                const exp_pdu_data_item_t *tcp_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_dissector_data,
                    NULL
                };

                exp_pdu_data_t *exp_pdu_data;

                exp_pdu_data_dissector_data.data = tcpinfo;

                exp_pdu_data = export_pdu_create_tags(pinfo, dissector_handle_get_dissector_name(handle), EXP_PDU_TAG_PROTO_NAME, tcp_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;

                tap_queue_packet(exported_pdu_tap, pinfo, exp_pdu_data);
            }
        }
    }
}

static int tcp_tap = -1;

static int tcp_follow_tap = -1;

static int mptcp_tap = -1;

static int exported_pdu_tap = -1;

static int proto_tcp = -1;

static int proto_ip = -1;

static int proto_icmp = -1;

static int proto_tcp_option_nop = -1;

static int proto_tcp_option_eol = -1;

static int proto_tcp_option_timestamp = -1;

static int proto_tcp_option_mss = -1;

static int proto_tcp_option_wscale = -1;

static int proto_tcp_option_sack_perm = -1;

static int proto_tcp_option_sack = -1;

static int proto_tcp_option_echo = -1;

static int proto_tcp_option_echoreply = -1;

static int proto_tcp_option_cc = -1;

static int proto_tcp_option_cc_new = -1;

static int proto_tcp_option_cc_echo = -1;

static int proto_tcp_option_md5 = -1;

static int proto_tcp_option_scps = -1;

static int proto_tcp_option_snack = -1;

static int proto_tcp_option_scpsrec = -1;

static int proto_tcp_option_scpscor = -1;

static int proto_tcp_option_qs = -1;

static int proto_tcp_option_user_to = -1;

static int proto_tcp_option_tfo = -1;

static int proto_tcp_option_rvbd_probe = -1;

static int proto_tcp_option_rvbd_trpy = -1;

static int proto_tcp_option_exp = -1;

static int proto_tcp_option_unknown = -1;

static int proto_mptcp = -1;

static int hf_tcp_srcport = -1;

static int hf_tcp_dstport = -1;

static int hf_tcp_port = -1;

static int hf_tcp_stream = -1;

static int hf_tcp_seq = -1;

static int hf_tcp_nxtseq = -1;

static int hf_tcp_ack = -1;

static int hf_tcp_hdr_len = -1;

static int hf_tcp_flags = -1;

static int hf_tcp_flags_res = -1;

static int hf_tcp_flags_ns = -1;

static int hf_tcp_flags_cwr = -1;

static int hf_tcp_flags_ecn = -1;

static int hf_tcp_flags_urg = -1;

static int hf_tcp_flags_ack = -1;

static int hf_tcp_flags_push = -1;

static int hf_tcp_flags_reset = -1;

static int hf_tcp_flags_syn = -1;

static int hf_tcp_flags_fin = -1;

static int hf_tcp_flags_str = -1;

static int hf_tcp_window_size_value = -1;

static int hf_tcp_window_size = -1;

static int hf_tcp_window_size_scalefactor = -1;

static int hf_tcp_checksum = -1;

static int hf_tcp_checksum_status = -1;

static int hf_tcp_checksum_calculated = -1;

static int hf_tcp_len = -1;

static int hf_tcp_urgent_pointer = -1;

static int hf_tcp_analysis = -1;

static int hf_tcp_analysis_flags = -1;

static int hf_tcp_analysis_bytes_in_flight = -1;

static int hf_tcp_analysis_push_bytes_sent = -1;

static int hf_tcp_analysis_acks_frame = -1;

static int hf_tcp_analysis_ack_rtt = -1;

static int hf_tcp_analysis_first_rtt = -1;

static int hf_tcp_analysis_rto = -1;

static int hf_tcp_analysis_rto_frame = -1;

static int hf_tcp_analysis_duplicate_ack = -1;

static int hf_tcp_analysis_duplicate_ack_num = -1;

static int hf_tcp_analysis_duplicate_ack_frame = -1;

static int hf_tcp_continuation_to = -1;

static int hf_tcp_pdu_time = -1;

static int hf_tcp_pdu_size = -1;


scan_for_next_pdu(tvbuff_t *tvb, proto_tree *tcp_tree, packet_info *pinfo, int offset, guint32 seq, guint32 nxtseq, wmem_tree_t *multisegment_pdus)
{
    struct tcp_multisegment_pdu *msp=NULL;

    if(!pinfo->fd->visited) {
        msp=(struct tcp_multisegment_pdu *)wmem_tree_lookup32_le(multisegment_pdus, seq-1);
        if(msp) {
            /* If this is a continuation of a PDU started in a
             * previous segment we need to update the last_frame
             * variables.
            */
            if(seq>msp->seq && seq<msp->nxtpdu) {
                msp->last_frame=pinfo->num;
                msp->last_frame_time=pinfo->abs_ts;
                print_pdu_tracking_data(pinfo, tvb, tcp_tree, msp);
            }

            /* If this segment is completely within a previous PDU
             * then we just skip this packet
             */
            if(seq>msp->seq && nxtseq<=msp->nxtpdu) {
                return -1;
            }
            if(seq<msp->nxtpdu && nxtseq>msp->nxtpdu) {
                offset+=msp->nxtpdu-seq;
                return offset;
            }

        }
    } else {
        /* First we try to find the start and transfer time for a PDU.
         * We only print this for the very first segment of a PDU
         * and only for PDUs spanning multiple segments.
         * Se we look for if there was any multisegment PDU started
         * just BEFORE the end of this segment. I.e. either inside this
         * segment or in a previous segment.
         * Since this might also match PDUs that are completely within
         * this segment we also verify that the found PDU does span
         * beyond the end of this segment.
         */
        msp=(struct tcp_multisegment_pdu *)wmem_tree_lookup32_le(multisegment_pdus, nxtseq-1);
        if(msp) {
            if(pinfo->num==msp->first_frame) {
                proto_item *item;
                nstime_t ns;

                item=proto_tree_add_uint(tcp_tree, hf_tcp_pdu_last_frame, tvb, 0, 0, msp->last_frame);
                PROTO_ITEM_SET_GENERATED(item);

                nstime_delta(&ns, &msp->last_frame_time, &pinfo->abs_ts);
                item = proto_tree_add_time(tcp_tree, hf_tcp_pdu_time,
                        tvb, 0, 0, &ns);
                PROTO_ITEM_SET_GENERATED(item);
            }
        }

        /* Second we check if this segment is part of a PDU started
         * prior to the segment (seq-1)
         */
        msp=(struct tcp_multisegment_pdu *)wmem_tree_lookup32_le(multisegment_pdus, seq-1);
        if(msp) {
            /* If this segment is completely within a previous PDU
             * then we just skip this packet
             */
            if(seq>msp->seq && nxtseq<=msp->nxtpdu) {
                print_pdu_tracking_data(pinfo, tvb, tcp_tree, msp);
                return -1;
            }

            if(seq<msp->nxtpdu && nxtseq>msp->nxtpdu) {
                offset+=msp->nxtpdu-seq;
                return offset;
            }
        }

    }
    return offset;
}

TCP: do not use an unknown status when the checksum is 0xffff

Otherwise it triggers an assert when adding the column as the field is
defined as BASE_NONE and not BASE_DEC or BASE_HEX. Thus an unknown value
(not in proto_checksum_vals[)array) cannot be represented.
Mark the checksum as bad even if we process the packet.
Closes #16816

Conflicts:
	epan/dissectors/packet-tcp.c


rvbd_probe_decode_version_type(const guint8 vt, guint8 *ver, guint8 *type)
{
    if (vt & PROBE_VERSION_MASK) {
        *ver = PROBE_VERSION_1;
        *type = vt >> 4;
    } else {
        *ver = PROBE_VERSION_2;
        *type = vt >> 1;
    }
}

static const true_false_string tcp_option_user_to_granularity = {
  "Minutes", "Seconds"
};

static const value_string tcp_option_kind_vs[] = {
    { TCPOPT_EOL, "End of Option List" },
    { TCPOPT_NOP, "No-Operation" },
    { TCPOPT_MSS, "Maximum Segment Size" },
    { TCPOPT_WINDOW, "Window Scale" },
    { TCPOPT_SACK_PERM, "SACK Permitted" },
    { TCPOPT_SACK, "SACK" },
    { TCPOPT_ECHO, "Echo" },
    { TCPOPT_ECHOREPLY, "Echo Reply" },
    { TCPOPT_TIMESTAMP, "Time Stamp Option" },
    { 9, "Partial Order Connection Permitted" },
    { 10, "Partial Order Service Profile" },
    { TCPOPT_CC, "CC" },
    { TCPOPT_CCNEW, "CC.NEW" },
    { TCPOPT_CCECHO, "CC.ECHO" },
    { 14, "TCP Alternate Checksum Request" },
    { 15, "TCP Alternate Checksum Data" },
    { 16, "Skeeter" },
    { 17, "Bubba" },
    { 18, "Trailer Checksum Option" },
    { TCPOPT_MD5, "MD5 Signature Option" },
    { TCPOPT_SCPS, "SCPS Capabilities" },
    { TCPOPT_SNACK, "Selective Negative Acknowledgements" },
    { TCPOPT_RECBOUND, "Record Boundaries" },
    { TCPOPT_CORREXP, "Corruption experienced" },
    { 24, "SNAP" },
    { 25, "Unassigned" },
    { 26, "TCP Compression Filter" },
    { TCPOPT_QS, "Quick-Start Response" },
    { TCPOPT_USER_TO, "User Timeout Option" },
    { 29, "TCP Authentication Option" },
    { TCPOPT_MPTCP, "Multipath TCP" },
    { TCPOPT_TFO, "TCP Fast Open Cookie" },
    { TCPOPT_RVBD_PROBE, "Riverbed Probe" },
    { TCPOPT_RVBD_TRPY, "Riverbed Transparency" },
    { TCPOPT_EXP_FD, "RFC3692-style Experiment 1" },
    { TCPOPT_EXP_FE, "RFC3692-style Experiment 2" },
    { 0, NULL }
};

static const fragment_items tcp_segment_items = {
    &ett_tcp_segment,
    &ett_tcp_segments,
    &hf_tcp_segments,
    &hf_tcp_segment,
    &hf_tcp_segment_overlap,
    &hf_tcp_segment_overlap_conflict,
    &hf_tcp_segment_multiple_tails,
    &hf_tcp_segment_too_long_fragment,
    &hf_tcp_segment_error,
    &hf_tcp_segment_count,
    &hf_tcp_reassembled_in,
    &hf_tcp_reassembled_length,
    &hf_tcp_reassembled_data,
    "Segments"
};


static const char* tcp_host_get_filter_type(hostlist_talker_t* host, conv_filter_type_e filter)
{
    if (filter == CONV_FT_SRC_PORT)
        return "tcp.srcport";

    if (filter == CONV_FT_DST_PORT)
        return "tcp.dstport";

    if (filter == CONV_FT_ANY_PORT)
        return "tcp.port";

    if(!host) {
        return CONV_FILTER_INVALID;
    }

    if (filter == CONV_FT_SRC_ADDRESS) {
        if (host->myaddress.type == AT_IPv4)
            return "ip.src";
        if (host->myaddress.type == AT_IPv6)
            return "ipv6.src";
    }

    if (filter == CONV_FT_DST_ADDRESS) {
        if (host->myaddress.type == AT_IPv4)
            return "ip.dst";
        if (host->myaddress.type == AT_IPv6)
            return "ipv6.dst";
    }

    if (filter == CONV_FT_ANY_ADDRESS) {
        if (host->myaddress.type == AT_IPv4)
            return "ip.addr";
        if (host->myaddress.type == AT_IPv6)
            return "ipv6.addr";
    }

    return CONV_FILTER_INVALID;
}

static int tcp_tap = -1;

static int tcp_follow_tap = -1;

static int mptcp_tap = -1;

static int exported_pdu_tap = -1;

static gboolean tcp_summary_in_tree = TRUE;

static gboolean tcp_check_checksum = FALSE;

static gint tcp_default_window_scaling = (gint)WindowScaling_NotKnown;

static int proto_tcp = -1;

static int proto_ip = -1;

static int proto_icmp = -1;

static int proto_tcp_option_nop = -1;

static int proto_tcp_option_eol = -1;

static int proto_tcp_option_timestamp = -1;

static int proto_tcp_option_mss = -1;

static int proto_tcp_option_wscale = -1;

static int proto_tcp_option_sack_perm = -1;

static int proto_tcp_option_sack = -1;

static int proto_tcp_option_echo = -1;

static int proto_tcp_option_echoreply = -1;

static int proto_tcp_option_cc = -1;

static int proto_tcp_option_cc_new = -1;

static int proto_tcp_option_cc_echo = -1;

static int proto_tcp_option_md5 = -1;

static int proto_tcp_option_scps = -1;

static int proto_tcp_option_snack = -1;

static int proto_tcp_option_scpsrec = -1;

static int proto_tcp_option_scpscor = -1;

static int proto_tcp_option_qs = -1;

static int proto_tcp_option_user_to = -1;

static int proto_tcp_option_tfo = -1;

static int proto_tcp_option_rvbd_probe = -1;

static int proto_tcp_option_rvbd_trpy = -1;

static int proto_tcp_option_exp = -1;

static int proto_tcp_option_unknown = -1;

static int proto_mptcp = -1;

static int hf_tcp_srcport = -1;

static int hf_tcp_dstport = -1;

static int hf_tcp_port = -1;

static int hf_tcp_stream = -1;

static int hf_tcp_seq = -1;

static int hf_tcp_nxtseq = -1;

static int hf_tcp_ack = -1;

static int hf_tcp_hdr_len = -1;

static int hf_tcp_flags = -1;

static int hf_tcp_flags_res = -1;

static int hf_tcp_flags_ns = -1;

static int hf_tcp_flags_cwr = -1;

static int hf_tcp_flags_ecn = -1;

static int hf_tcp_flags_urg = -1;

static int hf_tcp_flags_ack = -1;

static int hf_tcp_flags_push = -1;

static int hf_tcp_flags_reset = -1;

static int hf_tcp_flags_syn = -1;

static int hf_tcp_flags_fin = -1;

static int hf_tcp_flags_str = -1;

static int hf_tcp_window_size_value = -1;

static int hf_tcp_window_size = -1;

static int hf_tcp_window_size_scalefactor = -1;

static int hf_tcp_checksum = -1;

static int hf_tcp_checksum_status = -1;

static int hf_tcp_checksum_calculated = -1;

static int hf_tcp_len = -1;

static int hf_tcp_urgent_pointer = -1;

static int hf_tcp_analysis = -1;

static int hf_tcp_analysis_flags = -1;

static int hf_tcp_analysis_bytes_in_flight = -1;

static int hf_tcp_analysis_push_bytes_sent = -1;

static int hf_tcp_analysis_acks_frame = -1;

static int hf_tcp_analysis_ack_rtt = -1;

static int hf_tcp_analysis_first_rtt = -1;

static int hf_tcp_analysis_rto = -1;

static int hf_tcp_analysis_rto_frame = -1;

static int hf_tcp_analysis_duplicate_ack = -1;

static int hf_tcp_analysis_duplicate_ack_num = -1;

static int hf_tcp_analysis_duplicate_ack_frame = -1;


guint32 get_tcp_stream_count(void)
{
    return tcp_stream_count;
}

void proto_register_tcp(void);

void proto_reg_handoff_tcp(void);

static guint32 tcp_stream_count;

static guint32 mptcp_stream_count;

static void
process_tcp_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
    proto_tree *tree, proto_tree *tcp_tree, int src_port, int dst_port,
    guint32 seq, guint32 nxtseq, gboolean is_tcp_segment,
    struct tcp_analysis *tcpd, struct tcpinfo *tcpinfo);


follow_tcp_tap_listener(void *tapdata, packet_info *pinfo,
                      epan_dissect_t *edt _U_, const void *data)
{
    follow_record_t *follow_record;
    follow_info_t *follow_info = (follow_info_t *)tapdata;
    const tcp_follow_tap_data_t *follow_data = (const tcp_follow_tap_data_t *)data;
    gboolean is_server;
    guint32 sequence = follow_data->tcph->th_seq;
    guint32 length = follow_data->tcph->th_seglen;
    guint32 data_offset = 0;
    guint32 data_length = tvb_captured_length(follow_data->tvb);

    if (follow_data->tcph->th_flags & TH_SYN) {
        sequence++;
    }

    if (follow_info->client_port == 0) {
        follow_info->client_port = pinfo->srcport;
        copy_address(&follow_info->client_ip, &pinfo->src);
        follow_info->server_port = pinfo->destport;
        copy_address(&follow_info->server_ip, &pinfo->dst);
    }

    is_server = !(addresses_equal(&follow_info->client_ip, &pinfo->src) && follow_info->client_port == pinfo->srcport);

   /* Check whether this frame ACKs fragments in flow from the other direction.
    * This happens when frames are not in the capture file, but were actually
    * seen by the receiving host (Fixes bug 592).
    */
    if (follow_info->fragments[!is_server] != NULL) {
        while (check_follow_fragments(follow_info, !is_server, follow_data->tcph->th_ack, pinfo->fd->num));
    }

    /*
     * If this is the first segment of this stream, initialize the next expected
     * sequence number. If there is any data, it will be added below.
     */
    if (follow_info->bytes_written[is_server] == 0 && follow_info->seq[is_server] == 0) {
        follow_info->seq[is_server] = sequence;
    }

    /* We have already seen this src (and received some segments), let's figure
     * out whether this segment extends the stream or overlaps a previous gap. */
    if (LT_SEQ(sequence, follow_info->seq[is_server])) {
        /* This sequence number seems dated, but check the end in case it was a
         * retransmission with more data. */
        guint32 nextseq = sequence + length;
        if (GT_SEQ(nextseq, follow_info->seq[is_server])) {
            /* The begin of the segment was already seen, try to add the
             * remaining data that we have not seen to the payload. */
            data_offset = follow_info->seq[is_server] - sequence;
            if (data_length <= data_offset) {
                data_length = 0;
            } else {
                data_length -= data_offset;
            }

            sequence = follow_info->seq[is_server];
            length = nextseq - follow_info->seq[is_server];
        }
    }
    /*
     * Ignore segments that have no new data (either because it was empty, or
     * because it was fully overlapping with previously received data).
     */
    if (data_length == 0 || LT_SEQ(sequence, follow_info->seq[is_server])) {
        return TAP_PACKET_DONT_REDRAW;
    }

    follow_record = g_new0(follow_record_t, 1);
    follow_record->is_server = is_server;
    follow_record->packet_num = pinfo->fd->num;
    follow_record->seq = sequence;  /* start of fragment, used by check_follow_fragments. */
    follow_record->data = g_byte_array_append(g_byte_array_new(),
                                              tvb_get_ptr(follow_data->tvb, data_offset, data_length),
                                              data_length);

    if (EQ_SEQ(sequence, follow_info->seq[is_server])) {
        /* The segment overlaps or extends the previous end of stream. */
        follow_info->seq[is_server] += length;
        follow_info->bytes_written[is_server] += follow_record->data->len;
        follow_info->payload = g_list_prepend(follow_info->payload, follow_record);

        /* done with the packet, see if it caused a fragment to fit */
        while(check_follow_fragments(follow_info, is_server, 0, pinfo->fd->num));
    } else {
        /* Out of order packet (more preceding segments are expected). */
        follow_info->fragments[is_server] = g_list_append(follow_info->fragments[is_server], follow_record);
    }
    return TAP_PACKET_DONT_REDRAW;
}

void proto_register_tcp(void);

void proto_reg_handoff_tcp(void);

static gboolean tcp_summary_in_tree = TRUE;

static gboolean tcp_check_checksum = FALSE;

static gboolean tcp_no_subdissector_on_error = TRUE;

static gboolean tcp_exp_options_with_magic = TRUE;

static gboolean tcp_display_process_info = FALSE;

static const true_false_string tcp_option_user_to_granularity = {
  "Minutes", "Seconds"
};

static const value_string tcp_option_kind_vs[] = {
    { TCPOPT_EOL, "End of Option List" },
    { TCPOPT_NOP, "No-Operation" },
    { TCPOPT_MSS, "Maximum Segment Size" },
    { TCPOPT_WINDOW, "Window Scale" },
    { TCPOPT_SACK_PERM, "SACK Permitted" },
    { TCPOPT_SACK, "SACK" },
    { TCPOPT_ECHO, "Echo" },
    { TCPOPT_ECHOREPLY, "Echo Reply" },
    { TCPOPT_TIMESTAMP, "Time Stamp Option" },
    { 9, "Partial Order Connection Permitted" },
    { 10, "Partial Order Service Profile" },
    { TCPOPT_CC, "CC" },
    { TCPOPT_CCNEW, "CC.NEW" },
    { TCPOPT_CCECHO, "CC.ECHO" },
    { 14, "TCP Alternate Checksum Request" },
    { 15, "TCP Alternate Checksum Data" },
    { 16, "Skeeter" },
    { 17, "Bubba" },
    { 18, "Trailer Checksum Option" },
    { TCPOPT_MD5, "MD5 Signature Option" },
    { TCPOPT_SCPS, "SCPS Capabilities" },
    { TCPOPT_SNACK, "Selective Negative Acknowledgements" },
    { TCPOPT_RECBOUND, "Record Boundaries" },
    { TCPOPT_CORREXP, "Corruption experienced" },
    { 24, "SNAP" },
    { 25, "Unassigned" },
    { 26, "TCP Compression Filter" },
    { TCPOPT_QS, "Quick-Start Response" },
    { TCPOPT_USER_TO, "User Timeout Option" },
    { 29, "TCP Authentication Option" },
    { TCPOPT_MPTCP, "Multipath TCP" },
    { TCPOPT_TFO, "TCP Fast Open Cookie" },
    { TCPOPT_RVBD_PROBE, "Riverbed Probe" },
    { TCPOPT_RVBD_TRPY, "Riverbed Transparency" },
    { TCPOPT_EXP_FD, "RFC3692-style Experiment 1" },
    { TCPOPT_EXP_FE, "RFC3692-style Experiment 2" },
    { 0, NULL }
};

static const fragment_items tcp_segment_items = {
    &ett_tcp_segment,
    &ett_tcp_segments,
    &hf_tcp_segments,
    &hf_tcp_segment,
    &hf_tcp_segment_overlap,
    &hf_tcp_segment_overlap_conflict,
    &hf_tcp_segment_multiple_tails,
    &hf_tcp_segment_too_long_fragment,
    &hf_tcp_segment_error,
    &hf_tcp_segment_count,
    &hf_tcp_reassembled_in,
    &hf_tcp_reassembled_length,
    &hf_tcp_reassembled_data,
    "Segments"
};


tcp_seq_analysis_packet( void *ptr, packet_info *pinfo, epan_dissect_t *edt _U_, const void *tcp_info)
{
    seq_analysis_info_t *sainfo = (seq_analysis_info_t *) ptr;
    const struct tcpheader *tcph = (const struct tcpheader *)tcp_info;
    const char* flags;
    seq_analysis_item_t *sai = sequence_analysis_create_sai_with_addresses(pinfo, sainfo);

    if (!sai)
        return TAP_PACKET_DONT_REDRAW;

    sai->frame_number = pinfo->num;

    sai->port_src=pinfo->srcport;
    sai->port_dst=pinfo->destport;

    flags = tcp_flags_to_str(NULL, tcph);

    if ((tcph->th_have_seglen)&&(tcph->th_seglen!=0)){
        sai->frame_label = g_strdup_printf("%s - Len: %u",flags, tcph->th_seglen);
    }
    else{
        sai->frame_label = g_strdup(flags);
    }

    wmem_free(NULL, (void*)flags);

    if (tcph->th_flags & TH_ACK)
        sai->comment = g_strdup_printf("Seq = %u Ack = %u",tcph->th_seq, tcph->th_ack);
    else
        sai->comment = g_strdup_printf("Seq = %u",tcph->th_seq);

    sai->line_style = 1;
    sai->conv_num = (guint16) tcph->th_stream;
    sai->display = TRUE;

    g_queue_push_tail(sainfo->items, sai);

    return TAP_PACKET_REDRAW;
}

void proto_register_tcp(void);

void proto_reg_handoff_tcp(void);

static gboolean tcp_summary_in_tree = TRUE;

static gboolean tcp_no_subdissector_on_error = TRUE;

static gboolean tcp_exp_options_with_magic = TRUE;

static const true_false_string tcp_option_user_to_granularity = {
  "Minutes", "Seconds"
};

static const value_string tcp_option_kind_vs[] = {
    { TCPOPT_EOL, "End of Option List" },
    { TCPOPT_NOP, "No-Operation" },
    { TCPOPT_MSS, "Maximum Segment Size" },
    { TCPOPT_WINDOW, "Window Scale" },
    { TCPOPT_SACK_PERM, "SACK Permitted" },
    { TCPOPT_SACK, "SACK" },
    { TCPOPT_ECHO, "Echo" },
    { TCPOPT_ECHOREPLY, "Echo Reply" },
    { TCPOPT_TIMESTAMP, "Time Stamp Option" },
    { 9, "Partial Order Connection Permitted" },
    { 10, "Partial Order Service Profile" },
    { TCPOPT_CC, "CC" },
    { TCPOPT_CCNEW, "CC.NEW" },
    { TCPOPT_CCECHO, "CC.ECHO" },
    { 14, "TCP Alternate Checksum Request" },
    { 15, "TCP Alternate Checksum Data" },
    { 16, "Skeeter" },
    { 17, "Bubba" },
    { 18, "Trailer Checksum Option" },
    { TCPOPT_MD5, "MD5 Signature Option" },
    { TCPOPT_SCPS, "SCPS Capabilities" },
    { TCPOPT_SNACK, "Selective Negative Acknowledgements" },
    { TCPOPT_RECBOUND, "Record Boundaries" },
    { TCPOPT_CORREXP, "Corruption experienced" },
    { 24, "SNAP" },
    { 25, "Unassigned" },
    { 26, "TCP Compression Filter" },
    { TCPOPT_QS, "Quick-Start Response" },
    { TCPOPT_USER_TO, "User Timeout Option" },
    { 29, "TCP Authentication Option" },
    { TCPOPT_MPTCP, "Multipath TCP" },
    { TCPOPT_TFO, "TCP Fast Open Cookie" },
    { TCPOPT_RVBD_PROBE, "Riverbed Probe" },
    { TCPOPT_RVBD_TRPY, "Riverbed Transparency" },
    { TCPOPT_EXP_FD, "RFC3692-style Experiment 1" },
    { TCPOPT_EXP_FE, "RFC3692-style Experiment 2" },
    { 0, NULL }
};

static const fragment_items tcp_segment_items = {
    &ett_tcp_segment,
    &ett_tcp_segments,
    &hf_tcp_segments,
    &hf_tcp_segment,
    &hf_tcp_segment_overlap,
    &hf_tcp_segment_overlap_conflict,
    &hf_tcp_segment_multiple_tails,
    &hf_tcp_segment_too_long_fragment,
    &hf_tcp_segment_error,
    &hf_tcp_segment_count,
    &hf_tcp_reassembled_in,
    &hf_tcp_reassembled_length,
    &hf_tcp_reassembled_data,
    "Segments"
};


dissect_tcpopt_scps(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
    struct tcp_analysis *tcpd;
    proto_tree *field_tree = NULL;
    tcp_flow_t *flow;
    int         direction;
    proto_item *tf = NULL, *item;
    proto_tree *flags_tree = NULL;
    guint8      capvector;
    guint8      connid;
    int         offset = 0, optlen = tvb_reported_length(tvb);

    tcpd = get_tcp_conversation_data(NULL,pinfo);

    /* check direction and get ua lists */
    direction=cmp_address(&pinfo->src, &pinfo->dst);

    /* if the addresses are equal, match the ports instead */
    if(direction==0) {
        direction= (pinfo->srcport > pinfo->destport) ? 1 : -1;
    }

    if(direction>=0)
        flow =&(tcpd->flow1);
    else
        flow =&(tcpd->flow2);

    item = proto_tree_add_item(tree, proto_tcp_option_scps,
                               tvb, offset, -1, ENC_NA);
    field_tree = proto_item_add_subtree(item, ett_tcp_option_scps);

    proto_tree_add_item(field_tree, hf_tcp_option_kind, tvb,
                        offset, 1, ENC_BIG_ENDIAN);
    proto_tree_add_item(field_tree, hf_tcp_option_len, tvb,
                        offset + 1, 1, ENC_BIG_ENDIAN);

    /* If the option length == 4, this is a real SCPS capability option
     * See "CCSDS 714.0-B-2 (CCSDS Recommended Standard for SCPS Transport Protocol
     * (SCPS-TP)" Section 3.2.3 for definition.
     */
    if (optlen == 4) {
        tf = proto_tree_add_item(field_tree, hf_tcp_option_scps_vector, tvb,
                                 offset + 2, 1, ENC_BIG_ENDIAN);
        flags_tree = proto_item_add_subtree(tf, ett_tcp_scpsoption_flags);
        proto_tree_add_item(flags_tree, hf_tcp_scpsoption_flags_bets, tvb,
                            offset + 2, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(flags_tree, hf_tcp_scpsoption_flags_snack1, tvb,
                            offset + 2, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(flags_tree, hf_tcp_scpsoption_flags_snack2, tvb,
                            offset + 2, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(flags_tree, hf_tcp_scpsoption_flags_compress, tvb,
                            offset + 2, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(flags_tree, hf_tcp_scpsoption_flags_nlts, tvb,
                            offset + 2, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(flags_tree, hf_tcp_scpsoption_flags_reserved, tvb,
                            offset + 2, 1, ENC_BIG_ENDIAN);
        capvector = tvb_get_guint8(tvb, offset + 2);

        if (capvector) {
            struct capvec
            {
                guint8 mask;
                const gchar *str;
            } capvecs[] = {
                {0x80, "BETS"},
                {0x40, "SNACK1"},
                {0x20, "SNACK2"},
                {0x10, "COMP"},
                {0x08, "NLTS"},
                {0x07, "RESERVED"}
            };
            gboolean anyflag = FALSE;
            guint i;

            col_append_str(pinfo->cinfo, COL_INFO, " SCPS[");
            for (i = 0; i < sizeof(capvecs)/sizeof(struct capvec); i++) {
                if (capvector & capvecs[i].mask) {
                    proto_item_append_text(tf, "%s%s", anyflag ? ", " : " (",
                                           capvecs[i].str);
                    col_append_lstr(pinfo->cinfo, COL_INFO,
                                    anyflag ? ", " : "",
                                    capvecs[i].str,
                                    COL_ADD_LSTR_TERMINATOR);
                    anyflag = TRUE;
                }
            }
            col_append_str(pinfo->cinfo, COL_INFO, "]");
            proto_item_append_text(tf, ")");
        }

        proto_tree_add_item(field_tree, hf_tcp_scpsoption_connection_id, tvb,
                            offset + 3, 1, ENC_BIG_ENDIAN);
        connid = tvb_get_guint8(tvb, offset + 3);
        flow->scps_capable = 1;

        if (connid)
            tcp_info_append_uint(pinfo, "Connection ID", connid);
    } else {
        /* The option length != 4, so this is an infamous "extended capabilities
         * option. See "CCSDS 714.0-B-2 (CCSDS Recommended Standard for SCPS
         * Transport Protocol (SCPS-TP)" Section 3.2.5 for definition.
         *
         *  As the format of this option is only partially defined (it is
         * a community (or more likely vendor) defined format beyond that, so
         * at least for now, we only parse the standardized portion of the option.
         */
        guint8 local_offset = 2;
        guint8 binding_space;
        guint8 extended_cap_length;

        if (flow->scps_capable != 1) {
            /* There was no SCPS capabilities option preceding this */
            proto_item_set_text(item,
                                "Illegal SCPS Extended Capabilities (%u bytes)",
                                optlen);
        } else {
            proto_item_set_text(item,
                                "SCPS Extended Capabilities (%u bytes)",
                                optlen);

            /* There may be multiple binding spaces included in a single option,
             * so we will semi-parse each of the stacked binding spaces - skipping
             * over the octets following the binding space identifier and length.
             */
            while (optlen > local_offset) {

                /* 1st octet is Extended Capability Binding Space */
                binding_space = tvb_get_guint8(tvb, (offset + local_offset));

                /* 2nd octet (upper 4-bits) has binding space length in 16-bit words.
                 * As defined by the specification, this length is exclusive of the
                 * octets containing the extended capability type and length
                 */
                extended_cap_length =
                    (tvb_get_guint8(tvb, (offset + local_offset + 1)) >> 4);

                /* Convert the extended capabilities length into bytes for display */
                extended_cap_length = (extended_cap_length << 1);

                proto_tree_add_item(field_tree, hf_tcp_option_scps_binding, tvb, offset + local_offset, 1, ENC_BIG_ENDIAN);
                proto_tree_add_uint(field_tree, hf_tcp_option_scps_binding_len, tvb, offset + local_offset + 1, 1, extended_cap_length);

                /* Step past the binding space and length octets */
                local_offset += 2;

                proto_tree_add_item(field_tree, hf_tcp_option_scps_binding_data, tvb, offset + local_offset, extended_cap_length, ENC_NA);

                tcp_info_append_uint(pinfo, "EXCAP", binding_space);

                /* Step past the Extended capability data
                 * Treat the extended capability data area as opaque;
                 * If one desires to parse the extended capability data
                 * (say, in a vendor aware build of wireshark), it would
                 * be triggered here.
                 */
                local_offset += extended_cap_length;
            }
        }
    }

    return tvb_captured_length(tvb);
}

void proto_register_tcp(void);

void proto_reg_handoff_tcp(void);

static int tcp_tap = -1;

static int tcp_follow_tap = -1;

static int mptcp_tap = -1;

static int exported_pdu_tap = -1;

static gboolean tcp_summary_in_tree = TRUE;

static gboolean tcp_check_checksum = FALSE;

static int proto_tcp = -1;

static int proto_ip = -1;

static int proto_icmp = -1;

static int proto_tcp_option_nop = -1;

static int proto_tcp_option_eol = -1;

static int proto_tcp_option_timestamp = -1;

static int proto_tcp_option_mss = -1;

static int proto_tcp_option_wscale = -1;

static int proto_tcp_option_sack_perm = -1;

static int proto_tcp_option_sack = -1;

static int proto_tcp_option_echo = -1;

static int proto_tcp_option_echoreply = -1;

static int proto_tcp_option_cc = -1;

static int proto_tcp_option_cc_new = -1;

static int proto_tcp_option_cc_echo = -1;

static int proto_tcp_option_md5 = -1;

static int proto_tcp_option_scps = -1;

static int proto_tcp_option_snack = -1;

static int proto_tcp_option_scpsrec = -1;

static int proto_tcp_option_scpscor = -1;

static int proto_tcp_option_qs = -1;

static int proto_tcp_option_user_to = -1;

static int proto_tcp_option_tfo = -1;

static int proto_tcp_option_rvbd_probe = -1;

static int proto_tcp_option_rvbd_trpy = -1;

static int proto_tcp_option_exp = -1;

static int proto_tcp_option_unknown = -1;

static int proto_mptcp = -1;

static int hf_tcp_srcport = -1;

static int hf_tcp_dstport = -1;

static int hf_tcp_port = -1;

static int hf_tcp_stream = -1;

static int hf_tcp_seq = -1;

static int hf_tcp_nxtseq = -1;

static int hf_tcp_ack = -1;

static int hf_tcp_hdr_len = -1;

static int hf_tcp_flags = -1;

static int hf_tcp_flags_res = -1;

static int hf_tcp_flags_ns = -1;

static int hf_tcp_flags_cwr = -1;

static int hf_tcp_flags_ecn = -1;

static int hf_tcp_flags_urg = -1;

static int hf_tcp_flags_ack = -1;

static int hf_tcp_flags_push = -1;

static int hf_tcp_flags_reset = -1;

static int hf_tcp_flags_syn = -1;

static int hf_tcp_flags_fin = -1;

static int hf_tcp_flags_str = -1;

static int hf_tcp_window_size_value = -1;

static int hf_tcp_window_size = -1;

static int hf_tcp_window_size_scalefactor = -1;

static int hf_tcp_checksum = -1;

static int hf_tcp_checksum_status = -1;

static int hf_tcp_checksum_calculated = -1;

static int hf_tcp_len = -1;

static int hf_tcp_urgent_pointer = -1;

static int hf_tcp_analysis = -1;

static int hf_tcp_analysis_flags = -1;

static int hf_tcp_analysis_bytes_in_flight = -1;

static int hf_tcp_analysis_push_bytes_sent = -1;

static int hf_tcp_analysis_acks_frame = -1;

static int hf_tcp_analysis_ack_rtt = -1;

static int hf_tcp_analysis_first_rtt = -1;

static int hf_tcp_analysis_rto = -1;

static int hf_tcp_analysis_rto_frame = -1;

static int hf_tcp_analysis_duplicate_ack = -1;

static int hf_tcp_analysis_duplicate_ack_num = -1;

static int hf_tcp_analysis_duplicate_ack_frame = -1;


gchar* tcp_follow_address_filter(address* src_addr, address* dst_addr, int src_port, int dst_port)
{
    const gchar  *ip_version = src_addr->type == AT_IPv6 ? "v6" : "";
    gchar         src_addr_str[WS_INET6_ADDRSTRLEN];
    gchar         dst_addr_str[WS_INET6_ADDRSTRLEN];

    address_to_str_buf(src_addr, src_addr_str, sizeof(src_addr_str));
    address_to_str_buf(dst_addr, dst_addr_str, sizeof(dst_addr_str));

    return g_strdup_printf("((ip%s.src eq %s and tcp.srcport eq %d) and "
                     "(ip%s.dst eq %s and tcp.dstport eq %d))"
                     " or "
                     "((ip%s.src eq %s and tcp.srcport eq %d) and "
                     "(ip%s.dst eq %s and tcp.dstport eq %d))",
                     ip_version, src_addr_str, src_port,
                     ip_version, dst_addr_str, dst_port,
                     ip_version, dst_addr_str, dst_port,
                     ip_version, src_addr_str, src_port);

}


gchar* tcp_follow_index_filter(int stream)
{
    return g_strdup_printf("tcp.stream eq %d", stream);
}


tcp_flags_to_str_first_letter(const struct tcpheader *tcph)
{
    wmem_strbuf_t *buf = wmem_strbuf_new(wmem_packet_scope(), "");
    unsigned i;
    const unsigned flags_count = 12;
    const char first_letters[] = "RRRNCEUAPRSF";

    /* upper three bytes are marked as reserved ('R'). */
    for (i = 0; i < flags_count; i++) {
        if (((tcph->th_flags >> (flags_count - 1 - i)) & 1)) {
            wmem_strbuf_append_c(buf, first_letters[i]);
        } else {
            wmem_strbuf_append(buf, UTF8_MIDDLE_DOT);
        }
    }

    return wmem_strbuf_finalize(buf);
}

#define TCPOPT_EXP_FD           0xfd    /* Experimental, reserved */

#define TCPOPT_EXP_FE           0xfe    /* Experimental, reserved */

static const true_false_string tcp_option_user_to_granularity = {
  "Minutes", "Seconds"
};

static const value_string tcp_option_kind_vs[] = {
    { TCPOPT_EOL, "End of Option List" },
    { TCPOPT_NOP, "No-Operation" },
    { TCPOPT_MSS, "Maximum Segment Size" },
    { TCPOPT_WINDOW, "Window Scale" },
    { TCPOPT_SACK_PERM, "SACK Permitted" },
    { TCPOPT_SACK, "SACK" },
    { TCPOPT_ECHO, "Echo" },
    { TCPOPT_ECHOREPLY, "Echo Reply" },
    { TCPOPT_TIMESTAMP, "Time Stamp Option" },
    { 9, "Partial Order Connection Permitted" },
    { 10, "Partial Order Service Profile" },
    { TCPOPT_CC, "CC" },
    { TCPOPT_CCNEW, "CC.NEW" },
    { TCPOPT_CCECHO, "CC.ECHO" },
    { 14, "TCP Alternate Checksum Request" },
    { 15, "TCP Alternate Checksum Data" },
    { 16, "Skeeter" },
    { 17, "Bubba" },
    { 18, "Trailer Checksum Option" },
    { TCPOPT_MD5, "MD5 Signature Option" },
    { TCPOPT_SCPS, "SCPS Capabilities" },
    { TCPOPT_SNACK, "Selective Negative Acknowledgements" },
    { TCPOPT_RECBOUND, "Record Boundaries" },
    { TCPOPT_CORREXP, "Corruption experienced" },
    { 24, "SNAP" },
    { 25, "Unassigned" },
    { 26, "TCP Compression Filter" },
    { TCPOPT_QS, "Quick-Start Response" },
    { TCPOPT_USER_TO, "User Timeout Option" },
    { 29, "TCP Authentication Option" },
    { TCPOPT_MPTCP, "Multipath TCP" },
    { TCPOPT_TFO, "TCP Fast Open Cookie" },
    { TCPOPT_RVBD_PROBE, "Riverbed Probe" },
    { TCPOPT_RVBD_TRPY, "Riverbed Transparency" },
    { TCPOPT_EXP_FD, "RFC3692-style Experiment 1" },
    { TCPOPT_EXP_FE, "RFC3692-style Experiment 2" },
    { 0, NULL }
};

static const fragment_items tcp_segment_items = {
    &ett_tcp_segment,
    &ett_tcp_segments,
    &hf_tcp_segments,
    &hf_tcp_segment,
    &hf_tcp_segment_overlap,
    &hf_tcp_segment_overlap_conflict,
    &hf_tcp_segment_multiple_tails,
    &hf_tcp_segment_too_long_fragment,
    &hf_tcp_segment_error,
    &hf_tcp_segment_count,
    &hf_tcp_reassembled_in,
    &hf_tcp_reassembled_length,
    &hf_tcp_reassembled_data,
    "Segments"
};


mptcp_map_relssn_to_rawdsn(mptcp_dss_mapping_t *mapping, guint32 relssn, guint64 *dsn)
{
    if( (relssn < mapping->ssn_low) || (relssn > mapping->ssn_high)) {
        return FALSE;
    }

    *dsn = mapping->rawdsn + (relssn - mapping->ssn_low);
    return TRUE;
}

static gboolean tcp_summary_in_tree = TRUE;

static gboolean tcp_check_checksum = FALSE;

static gboolean tcp_no_subdissector_on_error = TRUE;

static gboolean tcp_exp_options_with_magic = TRUE;

static gboolean tcp_display_process_info = FALSE;

static guint32 tcp_stream_count;

static guint32 mptcp_stream_count;

static gboolean tcp_analyze_seq           = TRUE;

static gboolean tcp_relative_seq          = TRUE;

static gboolean tcp_track_bytes_in_flight = TRUE;

static gboolean tcp_calculate_ts          = TRUE;

static gboolean tcp_analyze_mptcp                   = TRUE;

static gboolean mptcp_relative_seq                  = TRUE;

static gboolean mptcp_analyze_mappings              = FALSE;

static gboolean mptcp_intersubflows_retransmission  = FALSE;

static void
process_tcp_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
    proto_tree *tree, proto_tree *tcp_tree, int src_port, int dst_port,
    guint32 seq, guint32 nxtseq, gboolean is_tcp_segment,
    struct tcp_analysis *tcpd, struct tcpinfo *tcpinfo);

static gboolean tcp_desegment = TRUE;

static gboolean tcp_reassemble_out_of_order = FALSE;

static gboolean tcp_ignore_timestamps = FALSE;

static gboolean try_heuristic_first = FALSE;


dissect_tcpopt_timestamp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
    proto_item *ti;
    proto_tree *ts_tree;
    proto_item *length_item;
    int offset = 0;
    guint32 ts_val, ts_ecr;
    int len = tvb_reported_length(tvb);

    ti = proto_tree_add_item(tree, proto_tcp_option_timestamp, tvb, offset, -1, ENC_NA);
    ts_tree = proto_item_add_subtree(ti, ett_tcp_option_timestamp);

    proto_tree_add_item(ts_tree, hf_tcp_option_kind, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;

    length_item = proto_tree_add_item(ts_tree, hf_tcp_option_len, tvb, offset, 1, ENC_BIG_ENDIAN);
    offset += 1;

    if (!tcp_option_len_check(length_item, pinfo, len, TCPOLEN_TIMESTAMP))
        return tvb_captured_length(tvb);

    proto_tree_add_item_ret_uint(ts_tree, hf_tcp_option_timestamp_tsval, tvb, offset,
                        4, ENC_BIG_ENDIAN, &ts_val);
    offset += 4;

    proto_tree_add_item_ret_uint(ts_tree, hf_tcp_option_timestamp_tsecr, tvb, offset,
                        4, ENC_BIG_ENDIAN, &ts_ecr);
    /* offset += 4; */

    proto_item_append_text(ti, ": TSval %u, TSecr %u", ts_val, ts_ecr);
    if (tcp_ignore_timestamps == FALSE) {
        tcp_info_append_uint(pinfo, "TSval", ts_val);
        tcp_info_append_uint(pinfo, "TSecr", ts_ecr);
    }

    return tvb_captured_length(tvb);
}

void proto_register_tcp(void);

void proto_reg_handoff_tcp(void);

static int tcp_tap = -1;

static int tcp_follow_tap = -1;

static int mptcp_tap = -1;

static int exported_pdu_tap = -1;

static gboolean tcp_check_checksum = FALSE;

static int proto_tcp = -1;

static int proto_ip = -1;

static int proto_icmp = -1;

static int proto_tcp_option_nop = -1;

static int proto_tcp_option_eol = -1;

static int proto_tcp_option_timestamp = -1;

static int proto_tcp_option_mss = -1;

static int proto_tcp_option_wscale = -1;

static int proto_tcp_option_sack_perm = -1;

static int proto_tcp_option_sack = -1;

static int proto_tcp_option_echo = -1;

static int proto_tcp_option_echoreply = -1;

static int proto_tcp_option_cc = -1;

static int proto_tcp_option_cc_new = -1;

static int proto_tcp_option_cc_echo = -1;

static int proto_tcp_option_md5 = -1;

static int proto_tcp_option_scps = -1;

static int proto_tcp_option_snack = -1;

static int proto_tcp_option_scpsrec = -1;

static int proto_tcp_option_scpscor = -1;

static int proto_tcp_option_qs = -1;

static int proto_tcp_option_user_to = -1;

static int proto_tcp_option_tfo = -1;

static int proto_tcp_option_rvbd_probe = -1;

static int proto_tcp_option_rvbd_trpy = -1;

static int proto_tcp_option_exp = -1;

static int proto_tcp_option_unknown = -1;

static int proto_mptcp = -1;

static int hf_tcp_srcport = -1;

static int hf_tcp_dstport = -1;

static int hf_tcp_port = -1;

static int hf_tcp_stream = -1;

static int hf_tcp_seq = -1;

static int hf_tcp_nxtseq = -1;

static int hf_tcp_ack = -1;

static int hf_tcp_hdr_len = -1;

static int hf_tcp_flags = -1;

static int hf_tcp_flags_res = -1;

static int hf_tcp_flags_ns = -1;

static int hf_tcp_flags_cwr = -1;

static int hf_tcp_flags_ecn = -1;

static int hf_tcp_flags_urg = -1;

static int hf_tcp_flags_ack = -1;

static int hf_tcp_flags_push = -1;

static int hf_tcp_flags_reset = -1;

static int hf_tcp_flags_syn = -1;

static int hf_tcp_flags_fin = -1;

static int hf_tcp_flags_str = -1;

static int hf_tcp_window_size_value = -1;

static int hf_tcp_window_size = -1;

static int hf_tcp_window_size_scalefactor = -1;

static int hf_tcp_checksum = -1;

static int hf_tcp_checksum_status = -1;

static int hf_tcp_checksum_calculated = -1;

static int hf_tcp_len = -1;

static int hf_tcp_urgent_pointer = -1;

static int hf_tcp_analysis = -1;

static int hf_tcp_analysis_flags = -1;

static int hf_tcp_analysis_bytes_in_flight = -1;

static int hf_tcp_analysis_push_bytes_sent = -1;

static int hf_tcp_analysis_acks_frame = -1;

static int hf_tcp_analysis_ack_rtt = -1;

static int hf_tcp_analysis_first_rtt = -1;

static int hf_tcp_analysis_rto = -1;

static int hf_tcp_analysis_rto_frame = -1;

static int hf_tcp_analysis_duplicate_ack = -1;

static int hf_tcp_analysis_duplicate_ack_num = -1;

static int hf_tcp_analysis_duplicate_ack_frame = -1;

static int hf_tcp_continuation_to = -1;


dissect_tcpopt_mss(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
    proto_item *item;
    proto_tree *exp_tree;
    proto_item *length_item;
    int offset = 0;
    guint32 mss;

    item = proto_tree_add_item(tree, proto_tcp_option_mss, tvb, offset, -1, ENC_NA);
    exp_tree = proto_item_add_subtree(item, ett_tcp_option_mss);

    proto_tree_add_item(exp_tree, hf_tcp_option_kind, tvb, offset, 1, ENC_BIG_ENDIAN);
    length_item = proto_tree_add_item(exp_tree, hf_tcp_option_len, tvb, offset + 1, 1, ENC_BIG_ENDIAN);

    if (!tcp_option_len_check(length_item, pinfo, tvb_reported_length(tvb), TCPOLEN_MSS))
        return tvb_captured_length(tvb);

    proto_tree_add_item_ret_uint(exp_tree, hf_tcp_option_mss_val, tvb, offset + 2, 2, ENC_BIG_ENDIAN, &mss);
    proto_item_append_text(item, ": %u bytes", mss);
    tcp_info_append_uint(pinfo, "MSS", mss);

    return tvb_captured_length(tvb);
}

void proto_register_tcp(void);

void proto_reg_handoff_tcp(void);

static int tcp_tap = -1;

static int tcp_follow_tap = -1;

static int mptcp_tap = -1;

static int exported_pdu_tap = -1;

static int proto_tcp = -1;

static int proto_ip = -1;

static int proto_icmp = -1;

static int proto_tcp_option_nop = -1;

static int proto_tcp_option_eol = -1;

static int proto_tcp_option_timestamp = -1;

static int proto_tcp_option_mss = -1;

static int proto_tcp_option_wscale = -1;

static int proto_tcp_option_sack_perm = -1;

static int proto_tcp_option_sack = -1;

static int proto_tcp_option_echo = -1;

static int proto_tcp_option_echoreply = -1;

static int proto_tcp_option_cc = -1;

static int proto_tcp_option_cc_new = -1;

static int proto_tcp_option_cc_echo = -1;

static int proto_tcp_option_md5 = -1;

static int proto_tcp_option_scps = -1;

static int proto_tcp_option_snack = -1;

static int proto_tcp_option_scpsrec = -1;

static int proto_tcp_option_scpscor = -1;

static int proto_tcp_option_qs = -1;

static int proto_tcp_option_user_to = -1;

static int proto_tcp_option_tfo = -1;

static int proto_tcp_option_rvbd_probe = -1;

static int proto_tcp_option_rvbd_trpy = -1;

static int proto_tcp_option_exp = -1;

static int proto_tcp_option_unknown = -1;

static int proto_mptcp = -1;

static int hf_tcp_srcport = -1;

static int hf_tcp_dstport = -1;

static int hf_tcp_port = -1;

static int hf_tcp_stream = -1;

static int hf_tcp_seq = -1;

static int hf_tcp_nxtseq = -1;

static int hf_tcp_ack = -1;

static int hf_tcp_hdr_len = -1;

static int hf_tcp_flags = -1;

static int hf_tcp_flags_res = -1;

static int hf_tcp_flags_ns = -1;

static int hf_tcp_flags_cwr = -1;

static int hf_tcp_flags_ecn = -1;

static int hf_tcp_flags_urg = -1;

static int hf_tcp_flags_ack = -1;

static int hf_tcp_flags_push = -1;

static int hf_tcp_flags_reset = -1;

static int hf_tcp_flags_syn = -1;

static int hf_tcp_flags_fin = -1;

static int hf_tcp_flags_str = -1;

static int hf_tcp_window_size_value = -1;

static int hf_tcp_window_size = -1;

static int hf_tcp_window_size_scalefactor = -1;

static int hf_tcp_checksum = -1;

static int hf_tcp_checksum_status = -1;

static int hf_tcp_checksum_calculated = -1;

static int hf_tcp_len = -1;

static int hf_tcp_urgent_pointer = -1;

static int hf_tcp_analysis = -1;

static int hf_tcp_analysis_flags = -1;

static int hf_tcp_analysis_bytes_in_flight = -1;

static int hf_tcp_analysis_push_bytes_sent = -1;

static int hf_tcp_analysis_acks_frame = -1;

static int hf_tcp_analysis_ack_rtt = -1;

static int hf_tcp_analysis_first_rtt = -1;

static int hf_tcp_analysis_rto = -1;

static int hf_tcp_analysis_rto_frame = -1;

static int hf_tcp_analysis_duplicate_ack = -1;

static int hf_tcp_analysis_duplicate_ack_num = -1;

static int hf_tcp_analysis_duplicate_ack_frame = -1;

static int hf_tcp_continuation_to = -1;

static int hf_tcp_pdu_time = -1;


tcp_info_append_uint(packet_info *pinfo, const char *abbrev, guint32 val)
{
    /* fstr(" %s=%u", abbrev, val) */
    col_append_str_uint(pinfo->cinfo, COL_INFO, abbrev, val, " ");
}


capture_tcp(const guchar *pd, int offset, int len, capture_packet_info_t *cpinfo, const union wtap_pseudo_header *pseudo_header)
{
    guint16 src_port, dst_port, low_port, high_port;

    if (!BYTES_ARE_IN_FRAME(offset, len, 4))
        return FALSE;

    capture_dissector_increment_count(cpinfo, proto_tcp);

    src_port = pntoh16(&pd[offset]);
    dst_port = pntoh16(&pd[offset+2]);

    if (src_port > dst_port) {
        low_port = dst_port;
        high_port = src_port;
    } else {
        low_port = src_port;
        high_port = dst_port;
    }

    if (low_port != 0 &&
        try_capture_dissector("tcp.port", low_port, pd, offset+20, len, cpinfo, pseudo_header))
        return TRUE;

    if (high_port != 0 &&
        try_capture_dissector("tcp.port", high_port, pd, offset+20, len, cpinfo, pseudo_header))
        return TRUE;

    /* We've at least identified one type of packet, so this shouldn't be "other" */
    return TRUE;
}

static int tcp_tap = -1;

static int tcp_follow_tap = -1;

static int mptcp_tap = -1;

static int exported_pdu_tap = -1;

static gboolean tcp_summary_in_tree = TRUE;

static gboolean tcp_check_checksum = FALSE;

static int proto_tcp = -1;

static int proto_ip = -1;

static int proto_icmp = -1;

static int proto_tcp_option_nop = -1;

static int proto_tcp_option_eol = -1;

static int proto_tcp_option_timestamp = -1;

static int proto_tcp_option_mss = -1;

static int proto_tcp_option_wscale = -1;

static int proto_tcp_option_sack_perm = -1;

static int proto_tcp_option_sack = -1;

static int proto_tcp_option_echo = -1;

static int proto_tcp_option_echoreply = -1;

static int proto_tcp_option_cc = -1;

static int proto_tcp_option_cc_new = -1;

static int proto_tcp_option_cc_echo = -1;

static int proto_tcp_option_md5 = -1;

static int proto_tcp_option_scps = -1;

static int proto_tcp_option_snack = -1;

static int proto_tcp_option_scpsrec = -1;

static int proto_tcp_option_scpscor = -1;

static int proto_tcp_option_qs = -1;

static int proto_tcp_option_user_to = -1;

static int proto_tcp_option_tfo = -1;

static int proto_tcp_option_rvbd_probe = -1;

static int proto_tcp_option_rvbd_trpy = -1;

static int proto_tcp_option_exp = -1;

static int proto_tcp_option_unknown = -1;

static int proto_mptcp = -1;

static int hf_tcp_srcport = -1;

static int hf_tcp_dstport = -1;

static int hf_tcp_port = -1;

static int hf_tcp_stream = -1;

static int hf_tcp_seq = -1;

static int hf_tcp_nxtseq = -1;

static int hf_tcp_ack = -1;

static int hf_tcp_hdr_len = -1;

static int hf_tcp_flags = -1;

static int hf_tcp_flags_res = -1;

static int hf_tcp_flags_ns = -1;

static int hf_tcp_flags_cwr = -1;

static int hf_tcp_flags_ecn = -1;

static int hf_tcp_flags_urg = -1;

static int hf_tcp_flags_ack = -1;

static int hf_tcp_flags_push = -1;

static int hf_tcp_flags_reset = -1;

static int hf_tcp_flags_syn = -1;

static int hf_tcp_flags_fin = -1;

static int hf_tcp_flags_str = -1;

static int hf_tcp_window_size_value = -1;

static int hf_tcp_window_size = -1;

static int hf_tcp_window_size_scalefactor = -1;

static int hf_tcp_checksum = -1;

static int hf_tcp_checksum_status = -1;

static int hf_tcp_checksum_calculated = -1;

static int hf_tcp_len = -1;

static int hf_tcp_urgent_pointer = -1;

static int hf_tcp_analysis = -1;

static int hf_tcp_analysis_flags = -1;

static int hf_tcp_analysis_bytes_in_flight = -1;

static int hf_tcp_analysis_push_bytes_sent = -1;

static int hf_tcp_analysis_acks_frame = -1;

static int hf_tcp_analysis_ack_rtt = -1;

static int hf_tcp_analysis_first_rtt = -1;

static int hf_tcp_analysis_rto = -1;

static int hf_tcp_analysis_rto_frame = -1;

static int hf_tcp_analysis_duplicate_ack = -1;

static int hf_tcp_analysis_duplicate_ack_num = -1;

static int hf_tcp_analysis_duplicate_ack_frame = -1;

static int hf_tcp_continuation_to = -1;

static int hf_tcp_pdu_time = -1;


get_tcp_conversation_data(conversation_t *conv, packet_info *pinfo)
{
    int direction;
    struct tcp_analysis *tcpd;
    gboolean clear_ta = TRUE;

    /* Did the caller supply the conversation pointer? */
    if( conv==NULL ) {
        /* If the caller didn't supply a conversation, don't
         * clear the analysis, it may be needed */
        clear_ta = FALSE;
        conv = find_or_create_conversation(pinfo);
    }

    /* Get the data for this conversation */
    tcpd=(struct tcp_analysis *)conversation_get_proto_data(conv, proto_tcp);

    /* If the conversation was just created or it matched a
     * conversation with template options, tcpd will not
     * have been initialized. So, initialize
     * a new tcpd structure for the conversation.
     */
    if (!tcpd) {
        tcpd = init_tcp_conversation_data(pinfo);
        conversation_add_proto_data(conv, proto_tcp, tcpd);
    }

    if (!tcpd) {
      return NULL;
    }

    /* check direction and get ua lists */
    direction=cmp_address(&pinfo->src, &pinfo->dst);
    /* if the addresses are equal, match the ports instead */
    if(direction==0) {
        direction= (pinfo->srcport > pinfo->destport) ? 1 : -1;
    }
    if(direction>=0) {
        tcpd->fwd=&(tcpd->flow1);
        tcpd->rev=&(tcpd->flow2);
    } else {
        tcpd->fwd=&(tcpd->flow2);
        tcpd->rev=&(tcpd->flow1);
    }

    if (clear_ta) {
        tcpd->ta=NULL;
    }
    return tcpd;
}


dissect_tcpopt_echo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
    proto_tree *field_tree;
    proto_item *item;
    proto_item *length_item;
    guint32 echo;
    int offset = 0;

    item = proto_tree_add_item(tree, proto_tcp_option_echo, tvb, offset, -1, ENC_NA);
    field_tree = proto_item_add_subtree(item, ett_tcp_opt_echo);

    proto_tree_add_item(field_tree, hf_tcp_option_kind, tvb,
                        offset, 1, ENC_BIG_ENDIAN);
    length_item = proto_tree_add_item(field_tree, hf_tcp_option_len, tvb,
                        offset + 1, 1, ENC_BIG_ENDIAN);

    if (!tcp_option_len_check(length_item, pinfo, tvb_reported_length(tvb), TCPOLEN_ECHO))
        return tvb_captured_length(tvb);

    proto_tree_add_item_ret_uint(field_tree, hf_tcp_option_echo, tvb,
                        offset + 2, 4, ENC_BIG_ENDIAN, &echo);

    proto_item_append_text(item, ": %u", echo);
    tcp_info_append_uint(pinfo, "ECHO", echo);

    return tvb_captured_length(tvb);
}


dissect_tcp_payload(tvbuff_t *tvb, packet_info *pinfo, int offset, guint32 seq,
            guint32 nxtseq, guint32 sport, guint32 dport,
            proto_tree *tree, proto_tree *tcp_tree,
            struct tcp_analysis *tcpd, struct tcpinfo *tcpinfo)
{
    gint nbytes;
    gboolean save_fragmented;

    nbytes = tvb_reported_length_remaining(tvb, offset);
    proto_tree_add_bytes_format(tcp_tree, hf_tcp_payload, tvb, offset,
        -1, NULL, "TCP payload (%u byte%s)", nbytes,
        plurality(nbytes, "", "s"));

    /* Can we desegment this segment? */
    if (pinfo->can_desegment) {
        /* Yes. */
        desegment_tcp(tvb, pinfo, offset, seq, nxtseq, sport, dport, tree,
                      tcp_tree, tcpd, tcpinfo);
    } else {
        /* No - just call the subdissector.
           Mark this as fragmented, so if somebody throws an exception,
           we don't report it as a malformed frame. */
        save_fragmented = pinfo->fragmented;
        pinfo->fragmented = TRUE;

        process_tcp_payload(tvb, offset, pinfo, tree, tcp_tree, sport, dport,
                            seq, nxtseq, TRUE, tcpd, tcpinfo);
        pinfo->fragmented = save_fragmented;
    }
}


tcp_analyze_get_acked_struct(guint32 frame, guint32 seq, guint32 ack, gboolean createflag, struct tcp_analysis *tcpd)
{

    wmem_tree_key_t key[4];

    key[0].length = 1;
    key[0].key = &frame;

    key[1].length = 1;
    key[1].key = &seq;

    key[2].length = 1;
    key[2].key = &ack;

    key[3].length = 0;
    key[3].key = NULL;

    if (!tcpd) {
        return;
    }

    tcpd->ta = (struct tcp_acked *)wmem_tree_lookup32_array(tcpd->acked_table, key);
    if((!tcpd->ta) && createflag) {
        tcpd->ta = wmem_new0(wmem_file_scope(), struct tcp_acked);
        wmem_tree_insert32_array(tcpd->acked_table, key, (void *)tcpd->ta);
    }
}

void proto_register_tcp(void);

void proto_reg_handoff_tcp(void);

static gboolean tcp_summary_in_tree = TRUE;

static gboolean tcp_check_checksum = FALSE;

static gboolean tcp_no_subdissector_on_error = TRUE;

static gboolean tcp_exp_options_with_magic = TRUE;

static gboolean tcp_display_process_info = FALSE;

static const value_string tcp_option_kind_vs[] = {
    { TCPOPT_EOL, "End of Option List" },
    { TCPOPT_NOP, "No-Operation" },
    { TCPOPT_MSS, "Maximum Segment Size" },
    { TCPOPT_WINDOW, "Window Scale" },
    { TCPOPT_SACK_PERM, "SACK Permitted" },
    { TCPOPT_SACK, "SACK" },
    { TCPOPT_ECHO, "Echo" },
    { TCPOPT_ECHOREPLY, "Echo Reply" },
    { TCPOPT_TIMESTAMP, "Time Stamp Option" },
    { 9, "Partial Order Connection Permitted" },
    { 10, "Partial Order Service Profile" },
    { TCPOPT_CC, "CC" },
    { TCPOPT_CCNEW, "CC.NEW" },
    { TCPOPT_CCECHO, "CC.ECHO" },
    { 14, "TCP Alternate Checksum Request" },
    { 15, "TCP Alternate Checksum Data" },
    { 16, "Skeeter" },
    { 17, "Bubba" },
    { 18, "Trailer Checksum Option" },
    { TCPOPT_MD5, "MD5 Signature Option" },
    { TCPOPT_SCPS, "SCPS Capabilities" },
    { TCPOPT_SNACK, "Selective Negative Acknowledgements" },
    { TCPOPT_RECBOUND, "Record Boundaries" },
    { TCPOPT_CORREXP, "Corruption experienced" },
    { 24, "SNAP" },
    { 25, "Unassigned" },
    { 26, "TCP Compression Filter" },
    { TCPOPT_QS, "Quick-Start Response" },
    { TCPOPT_USER_TO, "User Timeout Option" },
    { 29, "TCP Authentication Option" },
    { TCPOPT_MPTCP, "Multipath TCP" },
    { TCPOPT_TFO, "TCP Fast Open Cookie" },
    { TCPOPT_RVBD_PROBE, "Riverbed Probe" },
    { TCPOPT_RVBD_TRPY, "Riverbed Transparency" },
    { TCPOPT_EXP_FD, "RFC3692-style Experiment 1" },
    { TCPOPT_EXP_FE, "RFC3692-style Experiment 2" },
    { 0, NULL }
};

static const value_string mptcp_subtype_vs[] = {
    { TCPOPT_MPTCP_MP_CAPABLE, "Multipath Capable" },
    { TCPOPT_MPTCP_MP_JOIN, "Join Connection" },
    { TCPOPT_MPTCP_DSS, "Data Sequence Signal" },
    { TCPOPT_MPTCP_ADD_ADDR, "Add Address"},
    { TCPOPT_MPTCP_REMOVE_ADDR, "Remove Address" },
    { TCPOPT_MPTCP_MP_PRIO, "Change Subflow Priority" },
    { TCPOPT_MPTCP_MP_FAIL, "TCP Fallback" },
    { TCPOPT_MPTCP_MP_FASTCLOSE, "Fast Close" },
    { 0, NULL }
};

static guint32 tcp_stream_count;

static guint32 mptcp_stream_count;

static wmem_tree_t *mptcp_tokens = NULL;


print_pdu_tracking_data(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tcp_tree, struct tcp_multisegment_pdu *msp)
{
    proto_item *item;

    col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[Continuation to #%u] ", msp->first_frame);
    item=proto_tree_add_uint(tcp_tree, hf_tcp_continuation_to,
        tvb, 0, 0, msp->first_frame);
    PROTO_ITEM_SET_GENERATED(item);
}

static int hf_tcp_continuation_to = -1;

typedef struct tcp_follow_tap_data
{
    tvbuff_t *tvb;
    struct tcpheader* tcph;
    struct tcp_analysis *tcpd;

} tcp_follow_tap_data_t;

static void
process_tcp_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
    proto_tree *tree, proto_tree *tcp_tree, int src_port, int dst_port,
    guint32 seq, guint32 nxtseq, gboolean is_tcp_segment,
    struct tcp_analysis *tcpd, struct tcpinfo *tcpinfo);

typedef struct rvbd_option_data
{
    gboolean valid;
    guint8 type;
    guint8 probe_flags;

} rvbd_option_data;


dissect_tcpopt_md5(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
    proto_tree *field_tree;
    proto_item *item;
    proto_item *length_item;
    int offset = 0, optlen = tvb_reported_length(tvb);

    item = proto_tree_add_item(tree, proto_tcp_option_md5, tvb, offset, -1, ENC_NA);
    field_tree = proto_item_add_subtree(item, ett_tcp_opt_md5);

    col_append_lstr(pinfo->cinfo, COL_INFO, " MD5", COL_ADD_LSTR_TERMINATOR);
    proto_tree_add_item(field_tree, hf_tcp_option_kind, tvb,
                        offset, 1, ENC_BIG_ENDIAN);
    length_item = proto_tree_add_item(field_tree, hf_tcp_option_len, tvb,
                                      offset + 1, 1, ENC_BIG_ENDIAN);

    if (!tcp_option_len_check(length_item, pinfo, optlen, TCPOLEN_MD5))
        return tvb_captured_length(tvb);

    proto_tree_add_item(field_tree, hf_tcp_option_md5_digest, tvb,
                        offset + 2, optlen - 2, ENC_NA);

    return tvb_captured_length(tvb);
}


verify_scps(packet_info *pinfo,  proto_item *tf_syn, struct tcp_analysis *tcpd)
{
    tf_syn = 0x0;

    if(tcpd) {
        if ((!(tcpd->flow1.scps_capable)) || (!(tcpd->flow2.scps_capable))) {
            tcpd->flow1.scps_capable = 0;
            tcpd->flow2.scps_capable = 0;
        } else {
            expert_add_info(pinfo, tf_syn, &ei_tcp_scps_capable);
        }
    }
}

static expert_field ei_tcp_scps_capable = EI_INIT;

typedef struct tcp_follow_tap_data
{
    tvbuff_t *tvb;
    struct tcpheader* tcph;
    struct tcp_analysis *tcpd;

} tcp_follow_tap_data_t;

static void
process_tcp_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
    proto_tree *tree, proto_tree *tcp_tree, int src_port, int dst_port,
    guint32 seq, guint32 nxtseq, gboolean is_tcp_segment,
    struct tcp_analysis *tcpd, struct tcpinfo *tcpinfo);

typedef struct rvbd_option_data
{
    gboolean valid;
    guint8 type;
    guint8 probe_flags;

} rvbd_option_data;


mptcp_attach_subflow(struct mptcp_analysis* mptcpd, struct tcp_analysis* tcpd) {

    if(!wmem_list_find(mptcpd->subflows, tcpd)) {
        wmem_list_prepend(mptcpd->subflows, tcpd);
    }

    /* in case we merge 2 mptcp connections */
    tcpd->mptcp_analysis = mptcpd;
}

typedef struct tcp_follow_tap_data
{
    tvbuff_t *tvb;
    struct tcpheader* tcph;
    struct tcp_analysis *tcpd;

} tcp_follow_tap_data_t;

static void
process_tcp_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
    proto_tree *tree, proto_tree *tcp_tree, int src_port, int dst_port,
    guint32 seq, guint32 nxtseq, gboolean is_tcp_segment,
    struct tcp_analysis *tcpd, struct tcpinfo *tcpinfo);

typedef struct rvbd_option_data
{
    gboolean valid;
    guint8 type;
    guint8 probe_flags;

} rvbd_option_data;


mptcp_select_subflow_from_meta(const struct tcp_analysis *tcpd, const mptcp_meta_flow_t *meta)
{
    /* select the tcp_flow with appropriate direction */
    if( tcpd->flow1.mptcp_subflow->meta == meta) {
        return tcpd->flow1.mptcp_subflow;
    }
    else {
        return tcpd->flow2.mptcp_subflow;
    }
}


dissect_tcpopt_cc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
    proto_tree *field_tree;
    proto_item *item;
    proto_item *length_item;
    int offset = 0;
    guint32 cc;

    item = proto_tree_add_item(tree, proto_tcp_option_cc, tvb, offset, -1, ENC_NA);
    field_tree = proto_item_add_subtree(item, ett_tcp_opt_cc);

    proto_tree_add_item(field_tree, hf_tcp_option_kind, tvb,
                        offset, 1, ENC_BIG_ENDIAN);
    length_item = proto_tree_add_item(field_tree, hf_tcp_option_len, tvb,
                        offset + 1, 1, ENC_BIG_ENDIAN);

    if (!tcp_option_len_check(length_item, pinfo, tvb_reported_length(tvb), TCPOLEN_CC))
        return tvb_captured_length(tvb);

    proto_tree_add_item_ret_uint(field_tree, hf_tcp_option_cc, tvb,
                        offset + 2, 4, ENC_BIG_ENDIAN, &cc);

    tcp_info_append_uint(pinfo, "CC", cc);
    return tvb_captured_length(tvb);
}


dissect_tcpopt_rvbd_probe(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
{
    guint8 ver, type;
    proto_tree *field_tree;
    proto_item *pitem;
    proto_item *length_item;
    int offset = 0,
        optlen = tvb_reported_length(tvb);
    struct tcpheader *tcph = (struct tcpheader*)data;

    pitem = proto_tree_add_item(tree, proto_tcp_option_rvbd_probe, tvb, offset, -1, ENC_NA);
    field_tree = proto_item_add_subtree(pitem, ett_tcp_opt_rvbd_probe);

    proto_tree_add_item(field_tree, hf_tcp_option_kind, tvb,
                        offset, 1, ENC_BIG_ENDIAN);
    length_item = proto_tree_add_item(field_tree, hf_tcp_option_len, tvb,
                                      offset + 1, 1, ENC_BIG_ENDIAN);

    if (optlen < TCPOLEN_RVBD_PROBE_MIN) {
        /* Bogus - option length is less than what it's supposed to be for
           this option. */
        expert_add_info_format(pinfo, length_item, &ei_tcp_opt_len_invalid,
                            "option length should be >= %u)",
                            TCPOLEN_RVBD_PROBE_MIN);
        return tvb_captured_length(tvb);
    }

    rvbd_probe_decode_version_type(
        tvb_get_guint8(tvb, offset + PROBE_VERSION_TYPE_OFFSET),
        &ver, &type);

    proto_item_append_text(pitem, ": %s", val_to_str_const(type, rvbd_probe_type_vs, "Probe Unknown"));

    if (type >= PROBE_TYPE_MAX)
        return tvb_captured_length(tvb);

    if (ver == PROBE_VERSION_1) {
        guint16 port;

        proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_type1, tvb,
                            offset + PROBE_VERSION_TYPE_OFFSET, 1, ENC_BIG_ENDIAN);
        proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_version1, tvb,
                            offset + PROBE_VERSION_TYPE_OFFSET, 1, ENC_BIG_ENDIAN);

        if (type == PROBE_INTERNAL)
            return offset + PROBE_VERSION_TYPE_OFFSET;

        proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_reserved, tvb, offset + PROBE_V1_RESERVED_OFFSET, 1, ENC_BIG_ENDIAN);

        proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_prober, tvb,
                            offset + PROBE_V1_PROBER_OFFSET, 4, ENC_BIG_ENDIAN);

        switch (type) {

        case PROBE_QUERY:
        case PROBE_QUERY_SH:
        case PROBE_TRACE:
            {
            rvbd_option_data* option_data;
            proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_appli_ver, tvb,
                                offset + PROBE_V1_APPLI_VERSION_OFFSET, 2,
                                ENC_BIG_ENDIAN);

            proto_item_append_text(pitem, ", CSH IP: %s", tvb_ip_to_str(tvb, offset + PROBE_V1_PROBER_OFFSET));

            option_data = (rvbd_option_data*)p_get_proto_data(pinfo->pool, pinfo, proto_tcp_option_rvbd_probe, pinfo->curr_layer_num);
            if (option_data == NULL)
            {
                option_data = wmem_new0(pinfo->pool, rvbd_option_data);
                p_add_proto_data(pinfo->pool, pinfo, proto_tcp_option_rvbd_probe, pinfo->curr_layer_num, option_data);
            }

            option_data->valid = TRUE;
            option_data->type = type;

            }
            break;

        case PROBE_RESPONSE:
            proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_proxy, tvb,
                                offset + PROBE_V1_PROXY_ADDR_OFFSET, 4, ENC_BIG_ENDIAN);

            port = tvb_get_ntohs(tvb, offset + PROBE_V1_PROXY_PORT_OFFSET);
            proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_proxy_port, tvb,
                                offset + PROBE_V1_PROXY_PORT_OFFSET, 2, ENC_BIG_ENDIAN);

            rvbd_probe_resp_add_info(pitem, pinfo, tvb, offset + PROBE_V1_PROXY_ADDR_OFFSET, port);
            break;

        case PROBE_RESPONSE_SH:
            proto_tree_add_item(field_tree,
                                hf_tcp_option_rvbd_probe_client, tvb,
                                offset + PROBE_V1_SH_CLIENT_ADDR_OFFSET, 4,
                                ENC_BIG_ENDIAN);

            proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_proxy, tvb,
                                offset + PROBE_V1_SH_PROXY_ADDR_OFFSET, 4, ENC_BIG_ENDIAN);

            port = tvb_get_ntohs(tvb, offset + PROBE_V1_SH_PROXY_PORT_OFFSET);
            proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_proxy_port, tvb,
                                offset + PROBE_V1_SH_PROXY_PORT_OFFSET, 2, ENC_BIG_ENDIAN);

            rvbd_probe_resp_add_info(pitem, pinfo, tvb, offset + PROBE_V1_SH_PROXY_ADDR_OFFSET, port);
            break;
        }
    }
    else if (ver == PROBE_VERSION_2) {
        proto_item *ver_pi;
        proto_item *flag_pi;
        proto_tree *flag_tree;
        guint8 flags;

        proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_type2, tvb,
                            offset + PROBE_VERSION_TYPE_OFFSET, 1, ENC_BIG_ENDIAN);

        proto_tree_add_uint_format_value(
            field_tree, hf_tcp_option_rvbd_probe_version2, tvb,
            offset + PROBE_VERSION_TYPE_OFFSET, 1, ver, "%u", ver);
        /* Use version1 for filtering purposes because version2 packet
           value is 0, but filtering is usually done for value 2 */
        ver_pi = proto_tree_add_uint(field_tree, hf_tcp_option_rvbd_probe_version1, tvb,
                                     offset + PROBE_VERSION_TYPE_OFFSET, 1, ver);
        PROTO_ITEM_SET_HIDDEN(ver_pi);

        switch (type) {

        case PROBE_QUERY_INFO:
        case PROBE_QUERY_INFO_SH:
        case PROBE_QUERY_INFO_SID:
            flags = tvb_get_guint8(tvb, offset + PROBE_V2_INFO_OFFSET);
            flag_pi = proto_tree_add_uint(field_tree, hf_tcp_option_rvbd_probe_flags,
                                          tvb, offset + PROBE_V2_INFO_OFFSET,
                                          1, flags);

            flag_tree = proto_item_add_subtree(flag_pi, ett_tcp_opt_rvbd_probe_flags);
            proto_tree_add_item(flag_tree,
                                hf_tcp_option_rvbd_probe_flag_not_cfe,
                                tvb, offset + PROBE_V2_INFO_OFFSET, 1, ENC_BIG_ENDIAN);
            proto_tree_add_item(flag_tree,
                                hf_tcp_option_rvbd_probe_flag_last_notify,
                                tvb, offset + PROBE_V2_INFO_OFFSET, 1, ENC_BIG_ENDIAN);

            switch (type)
            {
            case PROBE_QUERY_INFO:
                {
                    rvbd_option_data* option_data = (rvbd_option_data*)p_get_proto_data(pinfo->pool, pinfo, proto_tcp_option_rvbd_probe, pinfo->curr_layer_num);
                    if (option_data == NULL)
                    {
                        option_data = wmem_new0(pinfo->pool, rvbd_option_data);
                        p_add_proto_data(pinfo->pool, pinfo, proto_tcp_option_rvbd_probe, pinfo->curr_layer_num, option_data);
                    }

                    option_data->probe_flags = flags;
                }
                break;
            case PROBE_QUERY_INFO_SH:
                proto_tree_add_item(flag_tree,
                                    hf_tcp_option_rvbd_probe_client, tvb,
                                    offset + PROBE_V2_INFO_CLIENT_ADDR_OFFSET,
                                    4, ENC_BIG_ENDIAN);
                break;
            case PROBE_QUERY_INFO_SID:
                proto_tree_add_item(flag_tree,
                                    hf_tcp_option_rvbd_probe_storeid, tvb,
                                    offset + PROBE_V2_INFO_STOREID_OFFSET,
                                    4, ENC_BIG_ENDIAN);
                break;
            }

            if (type != PROBE_QUERY_INFO_SID &&
                tcph != NULL &&
                (tcph->th_flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK) &&
                (flags & RVBD_FLAGS_PROBE_LAST)) {
                col_prepend_fstr(pinfo->cinfo, COL_INFO, "SA++, ");
            }

            break;

        case PROBE_RESPONSE_INFO:
            flag_pi = proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_flags,
                                          tvb, offset + PROBE_V2_INFO_OFFSET,
                                          1, ENC_BIG_ENDIAN);

            flag_tree = proto_item_add_subtree(flag_pi, ett_tcp_opt_rvbd_probe_flags);
            proto_tree_add_item(flag_tree,
                                hf_tcp_option_rvbd_probe_flag_probe_cache,
                                tvb, offset + PROBE_V2_INFO_OFFSET, 1, ENC_BIG_ENDIAN);
            proto_tree_add_item(flag_tree,
                                hf_tcp_option_rvbd_probe_flag_sslcert,
                                tvb, offset + PROBE_V2_INFO_OFFSET, 1, ENC_BIG_ENDIAN);
            proto_tree_add_item(flag_tree,
                                hf_tcp_option_rvbd_probe_flag_server_connected,
                                tvb, offset + PROBE_V2_INFO_OFFSET, 1, ENC_BIG_ENDIAN);
            break;

        case PROBE_RST:
            proto_tree_add_item(field_tree, hf_tcp_option_rvbd_probe_flags,
                                  tvb, offset + PROBE_V2_INFO_OFFSET,
                                  1, ENC_BIG_ENDIAN);
            break;
        }
    }

    return tvb_captured_length(tvb);
}

void proto_register_tcp(void);

void proto_reg_handoff_tcp(void);

static int tcp_tap = -1;

static int tcp_follow_tap = -1;

static int mptcp_tap = -1;

static int exported_pdu_tap = -1;

static gboolean tcp_summary_in_tree = TRUE;

static int proto_tcp = -1;

static int proto_ip = -1;

static int proto_icmp = -1;

static int proto_tcp_option_nop = -1;

static int proto_tcp_option_eol = -1;

static int proto_tcp_option_timestamp = -1;

static int proto_tcp_option_mss = -1;

static int proto_tcp_option_wscale = -1;

static int proto_tcp_option_sack_perm = -1;

static int proto_tcp_option_sack = -1;

static int proto_tcp_option_echo = -1;

static int proto_tcp_option_echoreply = -1;

static int proto_tcp_option_cc = -1;

static int proto_tcp_option_cc_new = -1;

static int proto_tcp_option_cc_echo = -1;

static int proto_tcp_option_md5 = -1;

static int proto_tcp_option_scps = -1;

static int proto_tcp_option_snack = -1;

static int proto_tcp_option_scpsrec = -1;

static int proto_tcp_option_scpscor = -1;

static int proto_tcp_option_qs = -1;

static int proto_tcp_option_user_to = -1;

static int proto_tcp_option_tfo = -1;

static int proto_tcp_option_rvbd_probe = -1;

static int proto_tcp_option_rvbd_trpy = -1;

static int proto_tcp_option_exp = -1;

static int proto_tcp_option_unknown = -1;

static int proto_mptcp = -1;

static int hf_tcp_srcport = -1;

static int hf_tcp_dstport = -1;

static int hf_tcp_port = -1;

static int hf_tcp_stream = -1;

static int hf_tcp_seq = -1;

static int hf_tcp_nxtseq = -1;

static int hf_tcp_ack = -1;

static int hf_tcp_hdr_len = -1;

static int hf_tcp_flags = -1;

static int hf_tcp_flags_res = -1;

static int hf_tcp_flags_ns = -1;

static int hf_tcp_flags_cwr = -1;

static int hf_tcp_flags_ecn = -1;

static int hf_tcp_flags_urg = -1;

static int hf_tcp_flags_ack = -1;

static int hf_tcp_flags_push = -1;

static int hf_tcp_flags_reset = -1;

static int hf_tcp_flags_syn = -1;

static int hf_tcp_flags_fin = -1;

static int hf_tcp_flags_str = -1;

static int hf_tcp_window_size_value = -1;

static int hf_tcp_window_size = -1;

static int hf_tcp_window_size_scalefactor = -1;

static int hf_tcp_checksum = -1;

static int hf_tcp_checksum_status = -1;

static int hf_tcp_checksum_calculated = -1;

static int hf_tcp_len = -1;

static int hf_tcp_urgent_pointer = -1;

static int hf_tcp_analysis = -1;

static int hf_tcp_analysis_flags = -1;

static int hf_tcp_analysis_bytes_in_flight = -1;

static int hf_tcp_analysis_push_bytes_sent = -1;

static int hf_tcp_analysis_acks_frame = -1;

static int hf_tcp_analysis_ack_rtt = -1;

static int hf_tcp_analysis_first_rtt = -1;

static int hf_tcp_analysis_rto = -1;

static int hf_tcp_analysis_rto_frame = -1;

static int hf_tcp_analysis_duplicate_ack = -1;

static int hf_tcp_analysis_duplicate_ack_num = -1;

static int hf_tcp_analysis_duplicate_ack_frame = -1;

static int hf_tcp_continuation_to = -1;


missing_segments(packet_info *pinfo, struct tcp_multisegment_pdu *msp, guint32 seq)
{
    fragment_head *fd_head;
    guint32 frag_offset = seq - msp->seq;

    if ((gint32)frag_offset <= 0) {
        return FALSE;
    }

    fd_head = fragment_get(&tcp_reassembly_table, pinfo, msp->first_frame, NULL);
    /* msp implies existence of fragments, this should never be NULL. */
    DISSECTOR_ASSERT(fd_head);

    /* Find length of contiguous fragments. */
    guint32 max = 0;
    for (fragment_item *frag = fd_head; frag; frag = frag->next) {
        guint32 frag_end = frag->offset + frag->len;
        if (frag->offset <= max && max < frag_end) {
            max = frag_end;
        }
    }
    return max < frag_offset;
}

static gboolean tcp_check_checksum = FALSE;

static gboolean tcp_display_process_info = FALSE;

static const value_string tcp_option_kind_vs[] = {
    { TCPOPT_EOL, "End of Option List" },
    { TCPOPT_NOP, "No-Operation" },
    { TCPOPT_MSS, "Maximum Segment Size" },
    { TCPOPT_WINDOW, "Window Scale" },
    { TCPOPT_SACK_PERM, "SACK Permitted" },
    { TCPOPT_SACK, "SACK" },
    { TCPOPT_ECHO, "Echo" },
    { TCPOPT_ECHOREPLY, "Echo Reply" },
    { TCPOPT_TIMESTAMP, "Time Stamp Option" },
    { 9, "Partial Order Connection Permitted" },
    { 10, "Partial Order Service Profile" },
    { TCPOPT_CC, "CC" },
    { TCPOPT_CCNEW, "CC.NEW" },
    { TCPOPT_CCECHO, "CC.ECHO" },
    { 14, "TCP Alternate Checksum Request" },
    { 15, "TCP Alternate Checksum Data" },
    { 16, "Skeeter" },
    { 17, "Bubba" },
    { 18, "Trailer Checksum Option" },
    { TCPOPT_MD5, "MD5 Signature Option" },
    { TCPOPT_SCPS, "SCPS Capabilities" },
    { TCPOPT_SNACK, "Selective Negative Acknowledgements" },
    { TCPOPT_RECBOUND, "Record Boundaries" },
    { TCPOPT_CORREXP, "Corruption experienced" },
    { 24, "SNAP" },
    { 25, "Unassigned" },
    { 26, "TCP Compression Filter" },
    { TCPOPT_QS, "Quick-Start Response" },
    { TCPOPT_USER_TO, "User Timeout Option" },
    { 29, "TCP Authentication Option" },
    { TCPOPT_MPTCP, "Multipath TCP" },
    { TCPOPT_TFO, "TCP Fast Open Cookie" },
    { TCPOPT_RVBD_PROBE, "Riverbed Probe" },
    { TCPOPT_RVBD_TRPY, "Riverbed Transparency" },
    { TCPOPT_EXP_FD, "RFC3692-style Experiment 1" },
    { TCPOPT_EXP_FE, "RFC3692-style Experiment 2" },
    { 0, NULL }
};

static const value_string mptcp_subtype_vs[] = {
    { TCPOPT_MPTCP_MP_CAPABLE, "Multipath Capable" },
    { TCPOPT_MPTCP_MP_JOIN, "Join Connection" },
    { TCPOPT_MPTCP_DSS, "Data Sequence Signal" },
    { TCPOPT_MPTCP_ADD_ADDR, "Add Address"},
    { TCPOPT_MPTCP_REMOVE_ADDR, "Remove Address" },
    { TCPOPT_MPTCP_MP_PRIO, "Change Subflow Priority" },
    { TCPOPT_MPTCP_MP_FAIL, "TCP Fallback" },
    { TCPOPT_MPTCP_MP_FASTCLOSE, "Fast Close" },
    { 0, NULL }
};

static guint32 tcp_stream_count;

static guint32 mptcp_stream_count;

static wmem_tree_t *mptcp_tokens = NULL;

static const int *tcp_option_mptcp_capable_flags[] = {
  &hf_tcp_option_mptcp_checksum_flag,
  &hf_tcp_option_mptcp_B_flag,
  &hf_tcp_option_mptcp_H_flag,
  &hf_tcp_option_mptcp_reserved_flag,
  NULL
};


tcp_dissect_options(tvbuff_t *tvb, int offset, guint length, int eol,
                       packet_info *pinfo, proto_tree *opt_tree,
                       proto_item *opt_item, void * data)
{
    guchar            opt;
    guint             optlen, nop_count = 0;
    proto_tree       *field_tree;
    const char       *name;
    dissector_handle_t option_dissector;
    tvbuff_t         *next_tvb;

    while (length > 0) {
        opt = tvb_get_guint8(tvb, offset);
        --length;      /* account for type byte */
        if ((opt == TCPOPT_EOL) || (opt == TCPOPT_NOP)) {
            int local_proto;
            proto_item* field_item;

            /* We assume that the only options with no length are EOL and
               NOP options, so that we can treat unknown options as having
               a minimum length of 2, and at least be able to move on to
               the next option by using the length in the option. */
            if (opt == TCPOPT_EOL) {
                local_proto = proto_tcp_option_eol;
            } else if (opt == TCPOPT_NOP) {
                local_proto = proto_tcp_option_nop;

                if (opt_item && (nop_count == 0 || offset % 4)) {
                    /* Count number of NOP in a row within a uint32 */
                    nop_count++;

                    if (nop_count == 4) {
                        expert_add_info(pinfo, opt_item, &ei_tcp_nop);
                    }
                } else {
                    nop_count = 0;
                }
            } else {
                g_assert_not_reached();
            }

            field_item = proto_tree_add_item(opt_tree, local_proto, tvb, offset, 1, ENC_NA);
            field_tree = proto_item_add_subtree(field_item, ett_tcp_option_other);
            proto_tree_add_item(field_tree, hf_tcp_option_kind, tvb, offset, 1, ENC_BIG_ENDIAN);
            proto_item_append_text(proto_tree_get_parent(opt_tree), ", %s", proto_get_protocol_short_name(find_protocol_by_id(local_proto)));
            offset += 1;
        } else {
            option_dissector = dissector_get_uint_handle(tcp_option_table, opt);
            if (option_dissector == NULL) {
                name = wmem_strdup_printf(wmem_packet_scope(), "Unknown (0x%02x)", opt);
                option_dissector = tcp_opt_unknown_handle;
            } else {
                name = dissector_handle_get_short_name(option_dissector);
            }

            /* Option has a length. Is it in the packet? */
            if (length == 0) {
                /* Bogus - packet must at least include option code byte and
                    length byte! */
                proto_tree_add_expert_format(opt_tree, pinfo, &ei_tcp_opt_len_invalid, tvb, offset, 1,
                                                "%s (length byte past end of options)", name);
                return;
            }

            optlen = tvb_get_guint8(tvb, offset + 1);  /* total including type, len */
            --length;    /* account for length byte */

            if (optlen < 2) {
                /* Bogus - option length is too short to include option code and
                    option length. */
                proto_tree_add_expert_format(opt_tree, pinfo, &ei_tcp_opt_len_invalid, tvb, offset, 2,
                                    "%s (with too-short option length = %u byte%s)",
                                    name, optlen, plurality(optlen, "", "s"));
                return;
            } else if (optlen - 2 > length) {
                /* Bogus - option goes past the end of the header. */
                proto_tree_add_expert_format(opt_tree, pinfo, &ei_tcp_opt_len_invalid, tvb, offset, length,
                                    "%s (option length = %u byte%s says option goes past end of options)",
                                    name, optlen, plurality(optlen, "", "s"));
                return;
            }

            next_tvb = tvb_new_subset_length(tvb, offset, optlen);
            call_dissector_with_data(option_dissector, next_tvb, pinfo, opt_tree/* tree */, data);
            proto_item_append_text(proto_tree_get_parent(opt_tree), ", %s", name);

            offset += optlen;
            length -= (optlen-2); //already accounted for type and len bytes
        }

        if (opt == eol)
            break;
    }
}


proto_reg_handoff_tcp(void)
{
    capture_dissector_handle_t tcp_cap_handle;

    dissector_add_uint("ip.proto", IP_PROTO_TCP, tcp_handle);
    dissector_add_for_decode_as_with_preference("udp.port", tcp_handle);
    data_handle = find_dissector("data");
    sport_handle = find_dissector("sport");
    tcp_tap = register_tap("tcp");
    tcp_follow_tap = register_tap("tcp_follow");

    tcp_cap_handle = create_capture_dissector_handle(capture_tcp, proto_tcp);
    capture_dissector_add_uint("ip.proto", IP_PROTO_TCP, tcp_cap_handle);

    /* Create dissection function handles for all TCP options */
    dissector_add_uint("tcp.option", TCPOPT_TIMESTAMP, create_dissector_handle( dissect_tcpopt_timestamp, proto_tcp_option_timestamp ));
    dissector_add_uint("tcp.option", TCPOPT_MSS, create_dissector_handle( dissect_tcpopt_mss, proto_tcp_option_mss ));
    dissector_add_uint("tcp.option", TCPOPT_WINDOW, create_dissector_handle( dissect_tcpopt_wscale, proto_tcp_option_wscale ));
    dissector_add_uint("tcp.option", TCPOPT_SACK_PERM, create_dissector_handle( dissect_tcpopt_sack_perm, proto_tcp_option_sack_perm ));
    dissector_add_uint("tcp.option", TCPOPT_SACK, create_dissector_handle( dissect_tcpopt_sack, proto_tcp_option_sack ));
    dissector_add_uint("tcp.option", TCPOPT_ECHO, create_dissector_handle( dissect_tcpopt_echo, proto_tcp_option_echo ));
    dissector_add_uint("tcp.option", TCPOPT_ECHOREPLY, create_dissector_handle( dissect_tcpopt_echo, proto_tcp_option_echoreply ));
    dissector_add_uint("tcp.option", TCPOPT_CC, create_dissector_handle( dissect_tcpopt_cc, proto_tcp_option_cc ));
    dissector_add_uint("tcp.option", TCPOPT_CCNEW, create_dissector_handle( dissect_tcpopt_cc, proto_tcp_option_cc_new ));
    dissector_add_uint("tcp.option", TCPOPT_CCECHO, create_dissector_handle( dissect_tcpopt_cc, proto_tcp_option_cc_echo ));
    dissector_add_uint("tcp.option", TCPOPT_MD5, create_dissector_handle( dissect_tcpopt_md5, proto_tcp_option_md5 ));
    dissector_add_uint("tcp.option", TCPOPT_SCPS, create_dissector_handle( dissect_tcpopt_scps, proto_tcp_option_scps ));
    dissector_add_uint("tcp.option", TCPOPT_SNACK, create_dissector_handle( dissect_tcpopt_snack, proto_tcp_option_snack ));
    dissector_add_uint("tcp.option", TCPOPT_RECBOUND, create_dissector_handle( dissect_tcpopt_recbound, proto_tcp_option_scpsrec ));
    dissector_add_uint("tcp.option", TCPOPT_CORREXP, create_dissector_handle( dissect_tcpopt_correxp, proto_tcp_option_scpscor ));
    dissector_add_uint("tcp.option", TCPOPT_QS, create_dissector_handle( dissect_tcpopt_qs, proto_tcp_option_qs ));
    dissector_add_uint("tcp.option", TCPOPT_USER_TO, create_dissector_handle( dissect_tcpopt_user_to, proto_tcp_option_user_to ));
    dissector_add_uint("tcp.option", TCPOPT_TFO, create_dissector_handle( dissect_tcpopt_tfo, proto_tcp_option_tfo ));
    dissector_add_uint("tcp.option", TCPOPT_RVBD_PROBE, create_dissector_handle( dissect_tcpopt_rvbd_probe, proto_tcp_option_rvbd_probe ));
    dissector_add_uint("tcp.option", TCPOPT_RVBD_TRPY, create_dissector_handle( dissect_tcpopt_rvbd_trpy, proto_tcp_option_rvbd_trpy ));
    dissector_add_uint("tcp.option", TCPOPT_EXP_FD, create_dissector_handle( dissect_tcpopt_exp, proto_tcp_option_exp ));
    dissector_add_uint("tcp.option", TCPOPT_EXP_FE, create_dissector_handle( dissect_tcpopt_exp, proto_tcp_option_exp ));
    dissector_add_uint("tcp.option", TCPOPT_MPTCP, create_dissector_handle( dissect_tcpopt_mptcp, proto_mptcp ));
    /* Common handle for all the unknown/unsupported TCP options */
    tcp_opt_unknown_handle = create_dissector_handle( dissect_tcpopt_unknown, proto_tcp_option_unknown );

    mptcp_tap = register_tap("mptcp");
    exported_pdu_tap = find_tap_id(EXPORT_PDU_TAP_NAME_LAYER_4);

    proto_ip = proto_get_id_by_filter_name("ip");
    proto_icmp = proto_get_id_by_filter_name("icmp");
}

void proto_register_tcp(void);

void proto_reg_handoff_tcp(void);

static int tcp_tap = -1;

static int tcp_follow_tap = -1;

static int mptcp_tap = -1;

static int exported_pdu_tap = -1;

static int proto_tcp = -1;

static int proto_ip = -1;

static int proto_icmp = -1;

static int proto_tcp_option_timestamp = -1;

static int proto_tcp_option_mss = -1;

static int proto_tcp_option_wscale = -1;

static int proto_tcp_option_sack_perm = -1;

static int proto_tcp_option_sack = -1;

static int proto_tcp_option_echo = -1;

static int proto_tcp_option_echoreply = -1;

static int proto_tcp_option_cc = -1;

static int proto_tcp_option_cc_new = -1;

static int proto_tcp_option_cc_echo = -1;

static int proto_tcp_option_md5 = -1;

static int proto_tcp_option_scps = -1;

static int proto_tcp_option_snack = -1;

static int proto_tcp_option_scpsrec = -1;

static int proto_tcp_option_scpscor = -1;

static int proto_tcp_option_qs = -1;

static int proto_tcp_option_user_to = -1;

static int proto_tcp_option_tfo = -1;

static int proto_tcp_option_rvbd_probe = -1;

static int proto_tcp_option_rvbd_trpy = -1;

static int proto_tcp_option_exp = -1;

static int proto_tcp_option_unknown = -1;

static int proto_mptcp = -1;

#define TCPOPT_EOL              0       /* End of options */

#define TCPOPT_MSS              2       /* Segment size negotiating */

#define TCPOPT_WINDOW           3       /* Window scaling */

#define TCPOPT_SACK_PERM        4       /* SACK Permitted */

#define TCPOPT_SACK             5       /* SACK Block */

#define TCPOPT_ECHO             6

#define TCPOPT_ECHOREPLY        7

#define TCPOPT_TIMESTAMP        8       /* Better RTT estimations/PAWS */

#define TCPOPT_CC               11

#define TCPOPT_CCNEW            12

#define TCPOPT_CCECHO           13

#define TCPOPT_MD5              19      /* RFC2385 */

#define TCPOPT_SCPS             20      /* SCPS Capabilities */

#define TCPOPT_SNACK            21      /* SCPS SNACK */

#define TCPOPT_RECBOUND         22      /* SCPS Record Boundary */

#define TCPOPT_CORREXP          23      /* SCPS Corruption Experienced */

#define TCPOPT_QS               27      /* RFC4782 Quick-Start Response */

#define TCPOPT_USER_TO          28      /* RFC5482 User Timeout Option */

#define TCPOPT_MPTCP            30      /* RFC6824 Multipath TCP */

#define TCPOPT_TFO              34      /* RFC7413 TCP Fast Open Cookie */

#define TCPOPT_EXP_FD           0xfd    /* Experimental, reserved */

#define TCPOPT_EXP_FE           0xfe    /* Experimental, reserved */

#define TCPOPT_RVBD_PROBE       76      /* Riverbed probe option */

#define TCPOPT_RVBD_TRPY        78      /* Riverbed transparency option */

#define TCPOPT_MPTCP_MP_CAPABLE    0x0    /* Multipath TCP Multipath Capable */

#define TCPOPT_MPTCP_MP_JOIN       0x1    /* Multipath TCP Join Connection */

#define TCPOPT_MPTCP_DSS           0x2    /* Multipath TCP Data Sequence Signal */


mptcp_convert_dsn(guint64 dsn, mptcp_meta_flow_t *meta, enum mptcp_dsn_conversion conv, gboolean relative, guint64 *result ) {

    *result = dsn;

    /* if relative is set then we need the 64 bits version anyway
     * we assume no wrapping was done on the 32 lsb so this may be wrong for elphant flows
     */
    if(conv == DSN_CONV_32_TO_64 || relative) {

        if(!(meta->static_flags & MPTCP_META_HAS_BASE_DSN_MSB)) {
            /* can't do those without the expected_idsn based on the key */
            return FALSE;
        }
    }

    if(conv == DSN_CONV_32_TO_64) {
        *result = KEEP_32MSB_OF_GUINT64(meta->base_dsn) | dsn;
    }

    if(relative) {
        *result -= meta->base_dsn;
    }

    if(conv == DSN_CONV_64_TO_32) {
        *result = (guint32) *result;
    }

    return TRUE;
}

static gboolean tcp_summary_in_tree = TRUE;

static gboolean tcp_check_checksum = FALSE;

enum scaling_window_value {
  WindowScaling_NotKnown=-1,
  WindowScaling_0=0,
  WindowScaling_1,
  WindowScaling_2,
  WindowScaling_3,
  WindowScaling_4,
  WindowScaling_5,
  WindowScaling_6,
  WindowScaling_7,
  WindowScaling_8,
  WindowScaling_9,
  WindowScaling_10,
  WindowScaling_11,
  WindowScaling_12,
  WindowScaling_13,
  WindowScaling_14
}

enum mptcp_dsn_conversion {
    DSN_CONV_64_TO_32,
    DSN_CONV_32_TO_64,
    DSN_CONV_NONE
}

static gboolean tcp_no_subdissector_on_error = TRUE;

static gboolean tcp_exp_options_with_magic = TRUE;

static gboolean tcp_display_process_info = FALSE;

static guint32 tcp_stream_count;

static guint32 mptcp_stream_count;

static const unit_name_string units_64bit_version = { " (64bits version)", NULL };

static gboolean tcp_analyze_seq           = TRUE;

static gboolean tcp_relative_seq          = TRUE;

static gboolean tcp_track_bytes_in_flight = TRUE;

static gboolean tcp_calculate_ts          = TRUE;

static gboolean tcp_analyze_mptcp                   = TRUE;

static gboolean mptcp_relative_seq                  = TRUE;

static gboolean mptcp_analyze_mappings              = FALSE;

static gboolean mptcp_intersubflows_retransmission  = FALSE;

#define MPTCP_META_HAS_BASE_DSN_MSB  0x01

static void
process_tcp_payload(tvbuff_t *tvb, volatile int offset, packet_info *pinfo,
    proto_tree *tree, proto_tree *tcp_tree, int src_port, int dst_port,
    guint32 seq, guint32 nxtseq, gboolean is_tcp_segment,
    struct tcp_analysis *tcpd, struct tcpinfo *tcpinfo);

static gboolean tcp_desegment = TRUE;

static gboolean tcp_reassemble_out_of_order = FALSE;

static gboolean tcp_ignore_timestamps = FALSE;

enum
{
    PROBE_VERSION_UNSPEC = 0,
    PROBE_VERSION_1      = 1,
    PROBE_VERSION_2      = 2,
    PROBE_VERSION_MAX
}

enum
{
    PROBE_QUERY          = 0,
    PROBE_RESPONSE       = 1,
    PROBE_INTERNAL       = 2,
    PROBE_TRACE          = 3,
    PROBE_QUERY_SH       = 4,
    PROBE_RESPONSE_SH    = 5,
    PROBE_QUERY_INFO     = 6,
    PROBE_RESPONSE_INFO  = 7,
    PROBE_QUERY_INFO_SH  = 8,
    PROBE_QUERY_INFO_SID = 9,
    PROBE_RST            = 10,
    PROBE_TYPE_MAX
}

typedef struct rvbd_option_data
{
    gboolean valid;
    guint8 type;
    guint8 probe_flags;

} rvbd_option_data;

enum {
    TRPY_OPTNUM_OFFSET        = 0,
    TRPY_OPTLEN_OFFSET        = 1,

    TRPY_OPTIONS_OFFSET       = 2,
    TRPY_SRC_ADDR_OFFSET      = 4,
    TRPY_DST_ADDR_OFFSET      = 8,
    TRPY_SRC_PORT_OFFSET      = 12,
    TRPY_DST_PORT_OFFSET      = 14,
    TRPY_CLIENT_PORT_OFFSET   = 16
}

static gboolean try_heuristic_first = FALSE;