desegment_tcp(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)
{
    fragment_head *ipfd_head;
    int last_fragment_len;
    gboolean must_desegment;
    gboolean called_dissector;
    int another_pdu_follows;
    int deseg_offset;
    guint32 deseg_seq;
    gint nbytes;
    proto_item *item;
    struct tcp_multisegment_pdu *msp;
    gboolean cleared_writable = col_get_writable(pinfo->cinfo, COL_PROTOCOL);
    const gboolean reassemble_ooo = tcp_desegment && tcp_reassemble_out_of_order;

again:
    ipfd_head = NULL;
    last_fragment_len = 0;
    must_desegment = FALSE;
    called_dissector = FALSE;
    another_pdu_follows = 0;
    msp = NULL;

    /*
     * Initialize these to assume no desegmentation.
     * If that's not the case, these will be set appropriately
     * by the subdissector.
     */
    pinfo->desegment_offset = 0;
    pinfo->desegment_len = 0;

    /*
     * Initialize this to assume that this segment will just be
     * added to the middle of a desegmented chunk of data, so
     * that we should show it all as data.
     * If that's not the case, it will be set appropriately.
     */
    deseg_offset = offset;

    if (tcpd) {
        /* Have we seen this PDU before (and is it the start of a multi-
         * segment PDU)?
         *
         * If the sequence number was seen before, it is part of a
         * retransmission if the whole segment fits within the MSP.
         * (But if this is this frame was already visited and the first frame of
         * the MSP matches the current frame, then it is not a retransmission,
         * but the start of a new MSP.)
         *
         * If only part of the segment fits in the MSP, then either:
         * - The previous segment included with the MSP was a Zero Window Probe
         *   with one byte of data and the subdissector just asked for one more
         *   byte. Do not mark it as retransmission (Bug 15427).
         * - Data was actually being retransmitted, but with additional data
         *   (Bug 13523). Do not mark it as retransmission to handle the extra
         *   bytes. (NOTE Due to the TCP_A_RETRANSMISSION check below, such
         *   extra data will still be ignored.)
         * - The MSP contains multiple segments, but the subdissector finished
         *   reassembly using a subset of the final segment (thus "msp->nxtpdu"
         *   is smaller than the nxtseq of the previous segment). If that final
         *   segment was retransmitted, then "nxtseq > msp->nxtpdu".
         *   Unfortunately that will *not* be marked as retransmission here.
         *   The next TCP_A_RETRANSMISSION hopefully takes care of it though.
         *
         * Only shortcircuit here when the first segment of the MSP is known,
         * and when this this first segment is not one to complete the MSP.
         */
        if ((msp = (struct tcp_multisegment_pdu *)wmem_tree_lookup32(tcpd->fwd->multisegment_pdus, seq)) &&
                nxtseq <= msp->nxtpdu &&
                !(msp->flags & MSP_FLAGS_MISSING_FIRST_SEGMENT) && msp->last_frame != pinfo->num) {
            const char* str;
            gboolean is_retransmission = FALSE;

            /* Yes.  This could be because we've dissected this frame before
             * or because this is a retransmission of a previously-seen
             * segment.  Either way, we don't need to hand it off to the
             * subdissector and we certainly don't want to re-add it to the
             * multisegment_pdus list: if we did, subsequent lookups would
             * find this retransmission instead of the original transmission
             * (breaking desegmentation if we'd already linked other segments
             * to the original transmission's entry).
             *
             * Cases to handle here:
             * - In-order stream, pinfo->num matches begin of MSP.
             * - In-order stream, but pinfo->num does not match the begin of the
             *   MSP. Must be a retransmission.
             * - OoO stream where this segment fills the gap in the begin of the
             *   MSP. msp->first_frame is the start where the gap was detected
             *   (and does NOT match pinfo->num).
             */

            if (msp->first_frame == pinfo->num || msp->first_frame_with_seq == pinfo->num) {
                str = "";
                col_append_sep_str(pinfo->cinfo, COL_INFO, " ", "[TCP segment of a reassembled PDU]");
            } else {
                str = "Retransmitted ";
                is_retransmission = TRUE;
                /* TCP analysis already flags this (in COL_INFO) as a retransmission--if it's enabled */
            }

            /* Fix for bug 3264: look up ipfd for this (first) segment,
               so can add tcp.reassembled_in generated field on this code path. */
            if (!is_retransmission) {
                ipfd_head = fragment_get(&tcp_reassembly_table, pinfo, msp->first_frame, NULL);
                if (ipfd_head) {
                    if (ipfd_head->reassembled_in != 0) {
                        item = proto_tree_add_uint(tcp_tree, hf_tcp_reassembled_in, tvb, 0,
                                           0, ipfd_head->reassembled_in);
                        PROTO_ITEM_SET_GENERATED(item);
                    }
                }
            }

            nbytes = tvb_reported_length_remaining(tvb, offset);

            proto_tree_add_bytes_format(tcp_tree, hf_tcp_segment_data, tvb, offset,
                nbytes, NULL, "%sTCP segment data (%u byte%s)", str, nbytes,
                plurality(nbytes, "", "s"));
            return;
        }

        /* The above code only finds retransmission if the PDU boundaries and the seq coincide I think
         * If we have sequence analysis active use the TCP_A_RETRANSMISSION flag.
         * XXXX Could the above code be improved?
         * XXX the following check works great for filtering duplicate
         * retransmissions, but could there be a case where it prevents
         * "tcp_reassemble_out_of_order" from functioning due to skipping
         * retransmission of a lost segment?
         * If the latter is enabled, it could use use "maxnextseq" for ignoring
         * retransmitted single-segment PDUs (that would require storing
         * per-packet state (tcp_per_packet_data_t) to make it work for two-pass
         * and random access dissection). Retransmitted segments that are part
         * of a MSP should already be passed only once to subdissectors due to
         * the "reassembled_in" check below.
         * The following should also check for TCP_A_SPURIOUS_RETRANSMISSION to
         * address bug 10289.
         */
        if((tcpd->ta) && ((tcpd->ta->flags&TCP_A_RETRANSMISSION) == TCP_A_RETRANSMISSION)){
            const char* str = "Retransmitted ";
            nbytes = tvb_reported_length_remaining(tvb, offset);
            proto_tree_add_bytes_format(tcp_tree, hf_tcp_segment_data, tvb, offset,
                nbytes, NULL, "%sTCP segment data (%u byte%s)", str, nbytes,
                plurality(nbytes, "", "s"));
            return;
        }
        /* Else, find the most previous PDU starting before this sequence number */
        if (!msp) {
            msp = (struct tcp_multisegment_pdu *)wmem_tree_lookup32_le(tcpd->fwd->multisegment_pdus, seq-1);
        }
    }

    if (reassemble_ooo && tcpd && !PINFO_FD_VISITED(pinfo)) {
        /* If there is a gap between this segment and any previous ones (that
         * is, seqno is larger than the maximum expected seqno), then it is
         * possibly an out-of-order segment. The very first segment is expected
         * to be in-order though (otherwise captures starting in midst of a
         * connection would never be reassembled).
         */
        if (tcpd->fwd->maxnextseq) {
            /* Segments may be missing due to packet loss (assume later
             * retransmission) or out-of-order (assume it will appear later).
             *
             * Extend an unfinished MSP when (1) missing segments exist between
             * the start of the previous, (2) unfinished MSP and new segment.
             *
             * Create a new MSP when no (1) previous MSP exists and (2) a gap is
             * detected between the previous largest nxtseq and the new segment.
             */
            /* Whether a previous MSP exists with missing segments. */
            gboolean has_unfinished_msp = msp && !(msp->flags & MSP_FLAGS_GOT_ALL_SEGMENTS);
            /* Whether the new segment creates a new gap. */
            gboolean has_gap = LT_SEQ(tcpd->fwd->maxnextseq, seq);

            if (has_unfinished_msp && missing_segments(pinfo, msp, seq)) {
                /* The last PDU is part of a MSP which still needed more data,
                 * extend it (if necessary) to cover the entire new segment.
                 */
                if (LT_SEQ(msp->nxtpdu, nxtseq)) {
                    msp->nxtpdu = nxtseq;
                }
            } else if (!has_unfinished_msp && has_gap) {
                /* Either the previous segment was a single PDU that did not
                 * belong to a MSP, or the previous MSP was completed and cannot
                 * be extended.
                 * Create a new one starting at the expected next position and
                 * extend it to the end of the new segment.
                 */
                msp = pdu_store_sequencenumber_of_next_pdu(pinfo,
                    tcpd->fwd->maxnextseq, nxtseq,
                    tcpd->fwd->multisegment_pdus);

                msp->flags |= MSP_FLAGS_MISSING_FIRST_SEGMENT;
            }
            /* Now that the MSP is updated or created, continue adding the
             * segments to the MSP below. The subdissector will not be called as
             * the MSP is not complete yet. */
        }
        if (tcpd->fwd->maxnextseq == 0 || LT_SEQ(tcpd->fwd->maxnextseq, nxtseq)) {
            /* Update the maximum expected seqno if no SYN packet was seen
             * before, or if the new segment succeeds previous segments. */
            tcpd->fwd->maxnextseq = nxtseq;
        }
    }

    if (msp && msp->seq <= seq && msp->nxtpdu > seq) {
        int len;

        if (!PINFO_FD_VISITED(pinfo)) {
            msp->last_frame=pinfo->num;
            msp->last_frame_time=pinfo->abs_ts;
        }

        /* OK, this PDU was found, which means the segment continues
         * a higher-level PDU and that we must desegment it.
         */
        if (msp->flags&MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT) {
            /* The dissector asked for the entire segment */
            len = tvb_captured_length_remaining(tvb, offset);
        } else {
            len = MIN(nxtseq, msp->nxtpdu) - seq;
        }
        last_fragment_len = len;

        if (reassemble_ooo) {
            /*
             * If the previous segment requested more data (setting
             * FD_PARTIAL_REASSEMBLY as the next segment length is unknown), but
             * subsequently an OoO segment was received (for an earlier hole),
             * then "fragment_add" would truncate the reassembled PDU to the end
             * of this OoO segment. To prevent that, explicitly specify the MSP
             * length before calling "fragment_add".
             */
            fragment_reset_tot_len(&tcp_reassembly_table, pinfo,
                                   msp->first_frame, NULL,
                                   MAX(seq + len, msp->nxtpdu) - msp->seq);
        }

        ipfd_head = fragment_add(&tcp_reassembly_table, tvb, offset,
                                 pinfo, msp->first_frame, NULL,
                                 seq - msp->seq, len,
                                 (LT_SEQ (nxtseq,msp->nxtpdu)) );

        if (!PINFO_FD_VISITED(pinfo)
        && msp->flags & MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT) {
            msp->flags &= (~MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT);

            /* If we consumed the entire segment there is no
             * other pdu starting anywhere inside this segment.
             * So update nxtpdu to point at least to the start
             * of the next segment.
             * (If the subdissector asks for even more data we
             * will advance nxtpdu even further later down in
             * the code.)
             */
            msp->nxtpdu = nxtseq;
        }

        if (reassemble_ooo && !PINFO_FD_VISITED(pinfo)) {
            /* If the first segment of the MSP was seen, remember it. */
            if (msp->seq == seq && msp->flags & MSP_FLAGS_MISSING_FIRST_SEGMENT) {
                msp->first_frame_with_seq = pinfo->num;
                msp->flags &= ~MSP_FLAGS_MISSING_FIRST_SEGMENT;
            }
            /* Remember when all segments are ready to avoid subsequent
             * out-of-order packets from extending this MSP. If a subsdissector
             * needs more segments, the flag will be cleared below. */
            if (ipfd_head) {
                msp->flags |= MSP_FLAGS_GOT_ALL_SEGMENTS;
            }
        }

        if( (msp->nxtpdu < nxtseq)
        &&  (msp->nxtpdu >= seq)
        &&  (len > 0)) {
            another_pdu_follows=msp->nxtpdu - seq;
        }
    } else {
        /* This segment was not found in our table, so it doesn't
         * contain a continuation of a higher-level PDU.
         * Call the normal subdissector.
         */

        /*
         * Supply the sequence number of this segment. We set this here
         * because this segment could be after another in the same packet,
         * in which case seq was incremented at the end of the loop.
         */
        tcpinfo->seq = seq;

        process_tcp_payload(tvb, offset, pinfo, tree, tcp_tree,
                            sport, dport, 0, 0, FALSE, tcpd, tcpinfo);
        called_dissector = TRUE;

        /* Did the subdissector ask us to desegment some more data
         * before it could handle the packet?
         * If so we have to create some structures in our table but
         * this is something we only do the first time we see this
         * packet.
         */
        if(pinfo->desegment_len) {
            if (!PINFO_FD_VISITED(pinfo))
                must_desegment = TRUE;

            /*
             * Set "deseg_offset" to the offset in "tvb"
             * of the first byte of data that the
             * subdissector didn't process.
             */
            deseg_offset = offset + pinfo->desegment_offset;
        }

        /* Either no desegmentation is necessary, or this is
         * segment contains the beginning but not the end of
         * a higher-level PDU and thus isn't completely
         * desegmented.
         */
        ipfd_head = NULL;
    }

    /* is it completely desegmented? */
    if (ipfd_head) {
        /*
         * Yes, we think it is.
         * We only call subdissector for the last segment.
         * Note that the last segment may include more than what
         * we needed.
         */
        if(ipfd_head->reassembled_in == pinfo->num) {
            /*
             * OK, this is the last segment.
             * Let's call the subdissector with the desegmented
             * data.
             */
            tvbuff_t *next_tvb;
            int old_len;

            /* create a new TVB structure for desegmented data */
            next_tvb = tvb_new_chain(tvb, ipfd_head->tvb_data);

            /* add desegmented data to the data source list */
            add_new_data_source(pinfo, next_tvb, "Reassembled TCP");

            /*
             * Supply the sequence number of the first of the
             * reassembled bytes.
             */
            tcpinfo->seq = msp->seq;

            /* indicate that this is reassembled data */
            tcpinfo->is_reassembled = TRUE;

            /* call subdissector */
            process_tcp_payload(next_tvb, 0, pinfo, tree, tcp_tree, sport,
                                dport, 0, 0, FALSE, tcpd, tcpinfo);
            called_dissector = TRUE;

            /*
             * OK, did the subdissector think it was completely
             * desegmented, or does it think we need even more
             * data?
             */
            if (reassemble_ooo && !PINFO_FD_VISITED(pinfo) && pinfo->desegment_len) {
                /* "desegment_len" isn't 0, so it needs more data to extend the MSP. */
                msp->flags &= ~MSP_FLAGS_GOT_ALL_SEGMENTS;
            }
            old_len = (int)(tvb_reported_length(next_tvb) - last_fragment_len);
            if (pinfo->desegment_len &&
                pinfo->desegment_offset<=old_len) {
                /*
                 * "desegment_len" isn't 0, so it needs more
                 * data for something - and "desegment_offset"
                 * is before "old_len", so it needs more data
                 * to dissect the stuff we thought was
                 * completely desegmented (as opposed to the
                 * stuff at the beginning being completely
                 * desegmented, but the stuff at the end
                 * being a new higher-level PDU that also
                 * needs desegmentation).
                 *
                 * If "desegment_offset" is 0, then nothing in the reassembled
                 * TCP segments was dissected, so remove the data source.
                 */
                if (pinfo->desegment_offset == 0)
                    remove_last_data_source(pinfo);
                fragment_set_partial_reassembly(&tcp_reassembly_table,
                                                pinfo, msp->first_frame, NULL);

                /* Update msp->nxtpdu to point to the new next
                 * pdu boundary.
                 */
                if (pinfo->desegment_len == DESEGMENT_ONE_MORE_SEGMENT) {
                    /* We want reassembly of at least one
                     * more segment so set the nxtpdu
                     * boundary to one byte into the next
                     * segment.
                     * This means that the next segment
                     * will complete reassembly even if it
                     * is only one single byte in length.
                     * If this is an OoO segment, then increment the MSP end.
                     */
                    msp->nxtpdu = MAX(seq + tvb_reported_length_remaining(tvb, offset), msp->nxtpdu) + 1;
                    msp->flags |= MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT;
                } else if (pinfo->desegment_len == DESEGMENT_UNTIL_FIN) {
                    tcpd->fwd->flags |= TCP_FLOW_REASSEMBLE_UNTIL_FIN;
                } else {
                    if (seq + last_fragment_len >= msp->nxtpdu) {
                        /* This is the segment (overlapping) the end of the MSP. */
                        msp->nxtpdu = seq + last_fragment_len + pinfo->desegment_len;
                    } else {
                        /* This is a segment before the end of the MSP, so it
                         * must be an out-of-order segmented that completed the
                         * MSP. The requested additional data is relative to
                         * that end.
                         */
                        msp->nxtpdu += pinfo->desegment_len;
                    }
                }

                /* Since we need at least some more data
                 * there can be no pdu following in the
                 * tail of this segment.
                 */
                another_pdu_follows = 0;
                offset += last_fragment_len;
                seq += last_fragment_len;
                if (tvb_captured_length_remaining(tvb, offset) > 0)
                    goto again;
            } else {
                /*
                 * Show the stuff in this TCP segment as
                 * just raw TCP segment data.
                 */
                nbytes = another_pdu_follows > 0
                    ? another_pdu_follows
                    : tvb_reported_length_remaining(tvb, offset);
                proto_tree_add_bytes_format(tcp_tree, hf_tcp_segment_data, tvb, offset,
                    nbytes, NULL, "TCP segment data (%u byte%s)", nbytes,
                    plurality(nbytes, "", "s"));

                print_tcp_fragment_tree(ipfd_head, tree, tcp_tree, pinfo, next_tvb);

                /* Did the subdissector ask us to desegment
                 * some more data?  This means that the data
                 * at the beginning of this segment completed
                 * a higher-level PDU, but the data at the
                 * end of this segment started a higher-level
                 * PDU but didn't complete it.
                 *
                 * If so, we have to create some structures
                 * in our table, but this is something we
                 * only do the first time we see this packet.
                 */
                if(pinfo->desegment_len) {
                    if (!PINFO_FD_VISITED(pinfo))
                        must_desegment = TRUE;

                    /* The stuff we couldn't dissect
                     * must have come from this segment,
                     * so it's all in "tvb".
                     *
                     * "pinfo->desegment_offset" is
                     * relative to the beginning of
                     * "next_tvb"; we want an offset
                     * relative to the beginning of "tvb".
                     *
                     * First, compute the offset relative
                     * to the *end* of "next_tvb" - i.e.,
                     * the number of bytes before the end
                     * of "next_tvb" at which the
                     * subdissector stopped.  That's the
                     * length of "next_tvb" minus the
                     * offset, relative to the beginning
                     * of "next_tvb, at which the
                     * subdissector stopped.
                     */
                    deseg_offset = ipfd_head->datalen - pinfo->desegment_offset;

                    /* "tvb" and "next_tvb" end at the
                     * same byte of data, so the offset
                     * relative to the end of "next_tvb"
                     * of the byte at which we stopped
                     * is also the offset relative to
                     * the end of "tvb" of the byte at
                     * which we stopped.
                     *
                     * Convert that back into an offset
                     * relative to the beginning of
                     * "tvb", by taking the length of
                     * "tvb" and subtracting the offset
                     * relative to the end.
                     */
                    deseg_offset = tvb_reported_length(tvb) - deseg_offset;
                }
            }
        }
    }

    if (must_desegment) {
        /* If the dissector requested "reassemble until FIN"
         * just set this flag for the flow and let reassembly
         * proceed at normal.  We will check/pick up these
         * reassembled PDUs later down in dissect_tcp() when checking
         * for the FIN flag.
         */
        if (tcpd && pinfo->desegment_len == DESEGMENT_UNTIL_FIN) {
            tcpd->fwd->flags |= TCP_FLOW_REASSEMBLE_UNTIL_FIN;
        }
        /*
         * The sequence number at which the stuff to be desegmented
         * starts is the sequence number of the byte at an offset
         * of "deseg_offset" into "tvb".
         *
         * The sequence number of the byte at an offset of "offset"
         * is "seq", i.e. the starting sequence number of this
         * segment, so the sequence number of the byte at
         * "deseg_offset" is "seq + (deseg_offset - offset)".
         */
        deseg_seq = seq + (deseg_offset - offset);

        if (tcpd && ((nxtseq - deseg_seq) <= 1024*1024)
            && (!PINFO_FD_VISITED(pinfo))) {
            if(pinfo->desegment_len == DESEGMENT_ONE_MORE_SEGMENT) {
                /* The subdissector asked to reassemble using the
                 * entire next segment.
                 * Just ask reassembly for one more byte
                 * but set this msp flag so we can pick it up
                 * above.
                 */
                msp = pdu_store_sequencenumber_of_next_pdu(pinfo, deseg_seq,
                    nxtseq+1, tcpd->fwd->multisegment_pdus);
                msp->flags |= MSP_FLAGS_REASSEMBLE_ENTIRE_SEGMENT;
            } else {
                msp = pdu_store_sequencenumber_of_next_pdu(pinfo,
                    deseg_seq, nxtseq+pinfo->desegment_len, tcpd->fwd->multisegment_pdus);
            }

            /* add this segment as the first one for this new pdu */
            fragment_add(&tcp_reassembly_table, tvb, deseg_offset,
                         pinfo, msp->first_frame, NULL,
                         0, nxtseq - deseg_seq,
                         LT_SEQ(nxtseq, msp->nxtpdu));
        }
    }

    if (!called_dissector || pinfo->desegment_len != 0) {
        if (ipfd_head != NULL && ipfd_head->reassembled_in != 0 &&
            !(ipfd_head->flags & FD_PARTIAL_REASSEMBLY)) {
            /*
             * We know what frame this PDU is reassembled in;
             * let the user know.
             */
            item = proto_tree_add_uint(tcp_tree, hf_tcp_reassembled_in, tvb, 0,
                                       0, ipfd_head->reassembled_in);
            PROTO_ITEM_SET_GENERATED(item);
        }

        /*
         * Either we didn't call the subdissector at all (i.e.,
         * this is a segment that contains the middle of a
         * higher-level PDU, but contains neither the beginning
         * nor the end), or the subdissector couldn't dissect it
         * all, as some data was missing (i.e., it set
         * "pinfo->desegment_len" to the amount of additional
         * data it needs).
         */
        if (pinfo->desegment_offset == 0) {
            /*
             * It couldn't, in fact, dissect any of it (the
             * first byte it couldn't dissect is at an offset
             * of "pinfo->desegment_offset" from the beginning
             * of the payload, and that's 0).
             * Just mark this as TCP.
             */
            col_set_str(pinfo->cinfo, COL_PROTOCOL, "TCP");
            col_append_sep_str(pinfo->cinfo, COL_INFO, " ", "[TCP segment of a reassembled PDU]");
        }

        /*
         * Show what's left in the packet as just raw TCP segment
         * data.
         * XXX - remember what protocol the last subdissector
         * was, and report it as a continuation of that, instead?
         */
        nbytes = tvb_reported_length_remaining(tvb, deseg_offset);

        proto_tree_add_bytes_format(tcp_tree, hf_tcp_segment_data, tvb, deseg_offset,
            -1, NULL, "TCP segment data (%u byte%s)", nbytes,
            plurality(nbytes, "", "s"));
    }
    pinfo->can_desegment = 0;
    pinfo->desegment_offset = 0;
    pinfo->desegment_len = 0;

    if(another_pdu_follows) {
        /* there was another pdu following this one. */
        pinfo->can_desegment = 2;
        /* we also have to prevent the dissector from changing the
         * PROTOCOL and INFO columns since what follows may be an
         * incomplete PDU and we don't want it be changed back from
         *  <Protocol>   to <TCP>
         */
        col_set_fence(pinfo->cinfo, COL_INFO);
        cleared_writable |= col_get_writable(pinfo->cinfo, COL_PROTOCOL);
        col_set_writable(pinfo->cinfo, COL_PROTOCOL, FALSE);
        offset += another_pdu_follows;
        seq += another_pdu_follows;
        goto again;
    } else {
        /* remove any blocking set above otherwise the
         * proto,colinfo tap will break
         */
        if(cleared_writable) {
            col_set_writable(pinfo->cinfo, COL_PROTOCOL, 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 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;


tcp_src_prompt(packet_info *pinfo, gchar *result)
{
    guint32 port = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, hf_tcp_srcport, pinfo->curr_layer_num));

    g_snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "source (%u%s)", port, UTF8_RIGHTWARDS_ARROW);
}

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

static int hf_tcp_srcport = -1;

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


rvbd_probe_resp_add_info(proto_item *pitem, packet_info *pinfo, tvbuff_t *tvb, int ip_offset, guint16 port)
{
    proto_item_append_text(pitem, ", Server Steelhead: %s:%u", tvb_ip_to_str(tvb, ip_offset), port);

    col_prepend_fstr(pinfo->cinfo, COL_INFO, "SA+, ");
}

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;


mptcp_add_duplicated_dsn(packet_info *pinfo _U_, proto_tree *tree, tvbuff_t *tvb, struct mptcp_subflow *subflow,
guint64 rawdsn64low, guint64 rawdsn64high
)
{
    wmem_list_t *results = NULL;
    wmem_list_frame_t *packet_it = NULL;
    mptcp_dsn2packet_mapping_t *packet = NULL;
    proto_item *item = NULL;

    results = wmem_itree_find_intervals(subflow->dsn2packet_map,
                    wmem_packet_scope(),
                    rawdsn64low,
                    rawdsn64high
                    );

    for(packet_it = wmem_list_head(results);
        packet_it != NULL;
        packet_it = wmem_list_frame_next(packet_it))
    {

        packet = (mptcp_dsn2packet_mapping_t *) wmem_list_frame_data(packet_it);
        DISSECTOR_ASSERT(packet);

        if(pinfo->num > packet->frame) {
            item = proto_tree_add_uint(tree, hf_mptcp_reinjection_of, tvb, 0, 0, packet->frame);
        }
        else {
            item = proto_tree_add_uint(tree, hf_mptcp_reinjected_in, tvb, 0, 0, packet->frame);
        }
        PROTO_ITEM_SET_GENERATED(item);
    }

    return packet;
}

static int hf_mptcp_reinjection_of = -1;

static int hf_mptcp_reinjected_in = -1;

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 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_src_value(packet_info *pinfo)
{
    return p_get_proto_data(pinfo->pool, pinfo, hf_tcp_srcport, pinfo->curr_layer_num);
}

static int hf_tcp_srcport = -1;

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


handle_export_pdu_dissection_table(packet_info *pinfo, tvbuff_t *tvb, guint32 port, struct tcpinfo *tcpinfo)
{
    if (have_tap_listener(exported_pdu_tap)) {
        exp_pdu_data_item_t exp_pdu_data_table_value = {exp_pdu_data_dissector_table_num_value_size, exp_pdu_data_dissector_table_num_value_populate_data, 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_table_value,
            &exp_pdu_data_dissector_data,
            NULL
        };

        exp_pdu_data_t *exp_pdu_data;

        exp_pdu_data_table_value.data = GUINT_TO_POINTER(port);
        exp_pdu_data_dissector_data.data = tcpinfo;

        exp_pdu_data = export_pdu_create_tags(pinfo, "tcp.port", EXP_PDU_TAG_DISSECTOR_TABLE_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 exported_pdu_tap = -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"
};


tcp_sequence_number_analysis_print_bytes_in_flight(packet_info * pinfo _U_,
                          tvbuff_t * tvb,
                          proto_tree * flags_tree,
                          struct tcp_acked *ta
                        )
{
    proto_item * flags_item;

    if (tcp_track_bytes_in_flight) {
        flags_item=proto_tree_add_uint(flags_tree,
                                       hf_tcp_analysis_bytes_in_flight,
                                       tvb, 0, 0, ta->bytes_in_flight);

        PROTO_ITEM_SET_GENERATED(flags_item);
    }
}

static int hf_tcp_analysis_bytes_in_flight = -1;

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

} tcp_follow_tap_data_t;

static gboolean tcp_track_bytes_in_flight = TRUE;

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;


tcp_sequence_number_analysis_print_zero_window(packet_info * pinfo,
                          proto_item * flags_item,
                          struct tcp_acked *ta
                        )
{
    /* TCP Zero Window Probe */
    if (ta->flags & TCP_A_ZERO_WINDOW_PROBE) {
        expert_add_info(pinfo, flags_item, &ei_tcp_analysis_zero_window_probe);
        col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP ZeroWindowProbe] ");
    }
    /* TCP Zero Window */
    if (ta->flags&TCP_A_ZERO_WINDOW) {
        expert_add_info(pinfo, flags_item, &ei_tcp_analysis_zero_window);
        col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[TCP ZeroWindow] ");
    }
    /* TCP Zero Window Probe Ack */
    if (ta->flags & TCP_A_ZERO_WINDOW_PROBE_ACK) {
        expert_add_info(pinfo, flags_item, &ei_tcp_analysis_zero_window_probe_ack);
        col_prepend_fence_fstr(pinfo->cinfo, COL_INFO,
                               "[TCP ZeroWindowProbeAck] ");
    }
}

static expert_field ei_tcp_analysis_zero_window_probe = EI_INIT;

static expert_field ei_tcp_analysis_zero_window = EI_INIT;

static expert_field ei_tcp_analysis_zero_window_probe_ack = EI_INIT;

#define TCPOPT_WINDOW           3       /* Window scaling */

#define TCPOPT_MPTCP            30      /* RFC6824 Multipath TCP */

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

#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 */

#define TCPOPT_MPTCP_ADD_ADDR      0x3    /* Multipath TCP Add Address */

#define TCPOPT_MPTCP_REMOVE_ADDR   0x4    /* Multipath TCP Remove Address */

#define TCPOPT_MPTCP_MP_PRIO       0x5    /* Multipath TCP Change Subflow Priority */

#define TCPOPT_MPTCP_MP_FAIL       0x6    /* Multipath TCP Fallback */

#define TCPOPT_MPTCP_MP_FASTCLOSE  0x7    /* Multipath TCP Fast Close */


mptcp_analysis_add_subflows(packet_info *pinfo _U_,  tvbuff_t *tvb,
    proto_tree *parent_tree, struct mptcp_analysis* mptcpd)
{
    wmem_list_frame_t *it;
    proto_tree *tree;
    proto_item *item;

    item=proto_tree_add_item(parent_tree, hf_mptcp_analysis_subflows, tvb, 0, 0, ENC_NA);
    PROTO_ITEM_SET_GENERATED(item);

    tree=proto_item_add_subtree(item, ett_mptcp_analysis_subflows);

    /* for the analysis, we set each subflow tcp stream id */
    for(it = wmem_list_head(mptcpd->subflows); it != NULL; it = wmem_list_frame_next(it)) {
        struct tcp_analysis *sf = (struct tcp_analysis *)wmem_list_frame_data(it);
        proto_item *subflow_item;
        subflow_item=proto_tree_add_uint(tree, hf_mptcp_analysis_subflows_stream_id, tvb, 0, 0, sf->stream);
        PROTO_ITEM_SET_HIDDEN(subflow_item);

        proto_item_append_text(item, " %d", sf->stream);
    }

    PROTO_ITEM_SET_GENERATED(item);
}

static int hf_mptcp_analysis_subflows_stream_id = -1;

static int hf_mptcp_analysis_subflows = -1;

static gint ett_mptcp_analysis_subflows = -1;

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 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_analyze_sequence_number(packet_info *pinfo, guint32 seq, guint32 ack, guint32 seglen, guint16 flags, guint32 window, struct tcp_analysis *tcpd)
{
    tcp_unacked_t *ual=NULL;
    tcp_unacked_t *prevual=NULL;
    guint32 nextseq;
    int ackcount;

#if 0
    printf("\nanalyze_sequence numbers   frame:%u\n",pinfo->num);
    printf("FWD list lastflags:0x%04x base_seq:%u: nextseq:%u lastack:%u\n",tcpd->fwd->lastsegmentflags,tcpd->fwd->base_seq,tcpd->fwd->tcp_analyze_seq_info->nextseq,tcpd->rev->tcp_analyze_seq_info->lastack);
    for(ual=tcpd->fwd->tcp_analyze_seq_info->segments; ual; ual=ual->next)
            printf("Frame:%d Seq:%u Nextseq:%u\n",ual->frame,ual->seq,ual->nextseq);
    printf("REV list lastflags:0x%04x base_seq:%u nextseq:%u lastack:%u\n",tcpd->rev->lastsegmentflags,tcpd->rev->base_seq,tcpd->rev->tcp_analyze_seq_info->nextseq,tcpd->fwd->tcp_analyze_seq_info->lastack);
    for(ual=tcpd->rev->tcp_analyze_seq_info->segments; ual; ual=ual->next)
            printf("Frame:%d Seq:%u Nextseq:%u\n",ual->frame,ual->seq,ual->nextseq);
#endif

    if (!tcpd) {
        return;
    }

    /* if this is the first segment for this list we need to store the
     * base_seq
     * We use TCP_S_SAW_SYN/SYNACK to distinguish between client and server
     *
     * Start relative seq and ack numbers at 1 if this
     * is not a SYN packet. This makes the relative
     * seq/ack numbers to be displayed correctly in the
     * event that the SYN or SYN/ACK packet is not seen
     * (this solves bug 1542)
     */
    if( !(tcpd->fwd->static_flags & TCP_S_BASE_SEQ_SET)) {
        if(flags & TH_SYN) {
            tcpd->fwd->base_seq = seq;
            tcpd->fwd->static_flags |= (flags & TH_ACK) ? TCP_S_SAW_SYNACK : TCP_S_SAW_SYN;
        }
        else {
            tcpd->fwd->base_seq = seq-1;
        }
        tcpd->fwd->static_flags |= TCP_S_BASE_SEQ_SET;
    }

    /* Only store reverse sequence if this isn't the SYN
     * There's no guarantee that the ACK field of a SYN
     * contains zeros; get the ISN from the first segment
     * with the ACK bit set instead (usually the SYN/ACK).
     *
     * If the SYN and SYN/ACK were received out-of-order,
     * the ISN is ack-1. If we missed the SYN/ACK, but got
     * the last ACK of the 3WHS, the ISN is ack-1. For all
     * other packets the ISN is unknown, so ack-1 is
     * as good a guess as ack.
     */
    if( !(tcpd->rev->static_flags & TCP_S_BASE_SEQ_SET) && (flags & TH_ACK) ) {
        tcpd->rev->base_seq = ack-1;
        tcpd->rev->static_flags |= TCP_S_BASE_SEQ_SET;
    }

    if( flags & TH_ACK ) {
        tcpd->rev->valid_bif = 1;
    }

    /* ZERO WINDOW PROBE
     * it is a zero window probe if
     *  the sequence number is the next expected one
     *  the window in the other direction is 0
     *  the segment is exactly 1 byte
     */
    if( seglen==1
    &&  seq==tcpd->fwd->tcp_analyze_seq_info->nextseq
    &&  tcpd->rev->window==0 ) {
        if(!tcpd->ta) {
            tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
        }
        tcpd->ta->flags|=TCP_A_ZERO_WINDOW_PROBE;
        goto finished_fwd;
    }

    /* ZERO WINDOW
     * a zero window packet has window == 0   but none of the SYN/FIN/RST set
     */
    if( window==0
    && (flags&(TH_RST|TH_FIN|TH_SYN))==0 ) {
        if(!tcpd->ta) {
            tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
        }
        tcpd->ta->flags|=TCP_A_ZERO_WINDOW;
    }

    /* LOST PACKET
     * If this segment is beyond the last seen nextseq we must
     * have missed some previous segment
     *
     * We only check for this if we have actually seen segments prior to this
     * one.
     * RST packets are not checked for this.
     */
    if( tcpd->fwd->tcp_analyze_seq_info->nextseq
    &&  GT_SEQ(seq, tcpd->fwd->tcp_analyze_seq_info->nextseq)
    &&  (flags&(TH_RST))==0 ) {
        if(!tcpd->ta) {
            tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
        }
        tcpd->ta->flags|=TCP_A_LOST_PACKET;

        /* Disable BiF until an ACK is seen in the other direction */
        tcpd->fwd->valid_bif = 0;
    }

    /* KEEP ALIVE
     * a keepalive contains 0 or 1 bytes of data and starts one byte prior
     * to what should be the next sequence number.
     * SYN/FIN/RST segments are never keepalives
     */
    if( (seglen==0||seglen==1)
    &&  seq==(tcpd->fwd->tcp_analyze_seq_info->nextseq-1)
    &&  (flags&(TH_SYN|TH_FIN|TH_RST))==0 ) {
        if(!tcpd->ta) {
            tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
        }
        tcpd->ta->flags|=TCP_A_KEEP_ALIVE;
    }

    /* WINDOW UPDATE
     * A window update is a 0 byte segment with the same SEQ/ACK numbers as
     * the previous seen segment and with a new window value
     */
    if( seglen==0
    &&  window
    &&  window!=tcpd->fwd->window
    &&  seq==tcpd->fwd->tcp_analyze_seq_info->nextseq
    &&  ack==tcpd->fwd->tcp_analyze_seq_info->lastack
    &&  (flags&(TH_SYN|TH_FIN|TH_RST))==0 ) {
        if(!tcpd->ta) {
            tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
        }
        tcpd->ta->flags|=TCP_A_WINDOW_UPDATE;
    }

    /* WINDOW FULL
     * If we know the window scaling
     * and if this segment contains data and goes all the way to the
     * edge of the advertised window
     * then we mark it as WINDOW FULL
     * SYN/RST/FIN packets are never WINDOW FULL
     */
    if( seglen>0
    &&  tcpd->rev->win_scale!=-1
    &&  (seq+seglen)==(tcpd->rev->tcp_analyze_seq_info->lastack+(tcpd->rev->window<<(tcpd->rev->win_scale==-2?0:tcpd->rev->win_scale)))
    &&  (flags&(TH_SYN|TH_FIN|TH_RST))==0 ) {
        if(!tcpd->ta) {
            tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
        }
        tcpd->ta->flags|=TCP_A_WINDOW_FULL;
    }

    /* KEEP ALIVE ACK
     * It is a keepalive ack if it repeats the previous ACK and if
     * the last segment in the reverse direction was a keepalive
     */
    if( seglen==0
    &&  window
    &&  window==tcpd->fwd->window
    &&  seq==tcpd->fwd->tcp_analyze_seq_info->nextseq
    &&  ack==tcpd->fwd->tcp_analyze_seq_info->lastack
    && (tcpd->rev->lastsegmentflags&TCP_A_KEEP_ALIVE)
    &&  (flags&(TH_SYN|TH_FIN|TH_RST))==0 ) {
        if(!tcpd->ta) {
            tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
        }
        tcpd->ta->flags|=TCP_A_KEEP_ALIVE_ACK;
        goto finished_fwd;
    }

    /* ZERO WINDOW PROBE ACK
     * It is a zerowindowprobe ack if it repeats the previous ACK and if
     * the last segment in the reverse direction was a zerowindowprobe
     * It also repeats the previous zero window indication
     */
    if( seglen==0
    &&  window==0
    &&  window==tcpd->fwd->window
    &&  seq==tcpd->fwd->tcp_analyze_seq_info->nextseq
    &&  ack==tcpd->fwd->tcp_analyze_seq_info->lastack
    && (tcpd->rev->lastsegmentflags&TCP_A_ZERO_WINDOW_PROBE)
    &&  (flags&(TH_SYN|TH_FIN|TH_RST))==0 ) {
        if(!tcpd->ta) {
            tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
        }
        tcpd->ta->flags|=TCP_A_ZERO_WINDOW_PROBE_ACK;
        goto finished_fwd;
    }

    /* DUPLICATE ACK
     * It is a duplicate ack if window/seq/ack is the same as the previous
     * segment and if the segment length is 0
     */
    if( seglen==0
    &&  window
    &&  window==tcpd->fwd->window
    &&  seq==tcpd->fwd->tcp_analyze_seq_info->nextseq
    &&  ack==tcpd->fwd->tcp_analyze_seq_info->lastack
    &&  (flags&(TH_SYN|TH_FIN|TH_RST))==0 ) {
        tcpd->fwd->tcp_analyze_seq_info->dupacknum++;
        if(!tcpd->ta) {
            tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
        }
        tcpd->ta->flags|=TCP_A_DUPLICATE_ACK;
        tcpd->ta->dupack_num=tcpd->fwd->tcp_analyze_seq_info->dupacknum;
        tcpd->ta->dupack_frame=tcpd->fwd->tcp_analyze_seq_info->lastnondupack;
    }

finished_fwd:
    /* If the ack number changed we must reset the dupack counters */
    if( ack != tcpd->fwd->tcp_analyze_seq_info->lastack ) {
        tcpd->fwd->tcp_analyze_seq_info->lastnondupack=pinfo->num;
        tcpd->fwd->tcp_analyze_seq_info->dupacknum=0;
    }

    /* ACKED LOST PACKET
     * If this segment acks beyond the 'max seq to be acked' in the other direction
     * then that means we have missed packets going in the
     * other direction
     *
     * We only check this if we have actually seen some seq numbers
     * in the other direction.
     */
    if( tcpd->rev->tcp_analyze_seq_info->maxseqtobeacked
    &&  GT_SEQ(ack, tcpd->rev->tcp_analyze_seq_info->maxseqtobeacked )
    &&  (flags&(TH_ACK))!=0 ) {
        if(!tcpd->ta) {
            tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
        }
        tcpd->ta->flags|=TCP_A_ACK_LOST_PACKET;
        /* update 'max seq to be acked' in the other direction so we don't get
         * this indication again.
         */
        tcpd->rev->tcp_analyze_seq_info->maxseqtobeacked=tcpd->rev->tcp_analyze_seq_info->nextseq;
    }

    /* RETRANSMISSION/FAST RETRANSMISSION/OUT-OF-ORDER
     * If the segment contains data (or is a SYN or a FIN) and
     * if it does not advance the sequence number, it must be one
     * of these three.
     * Only test for this if we know what the seq number should be
     * (tcpd->fwd->nextseq)
     *
     * Note that a simple KeepAlive is not a retransmission
     */
    if (seglen>0 || flags&(TH_SYN|TH_FIN)) {
        gboolean seq_not_advanced = tcpd->fwd->tcp_analyze_seq_info->nextseq
                && (LT_SEQ(seq, tcpd->fwd->tcp_analyze_seq_info->nextseq));

        guint64 t;
        guint64 ooo_thres;

        if(tcpd->ta && (tcpd->ta->flags&TCP_A_KEEP_ALIVE) ) {
            goto finished_checking_retransmission_type;
        }

        /* This segment is *not* considered a retransmission/out-of-order if
         *  the segment length is larger than one (it really adds new data)
         *  the sequence number is one less than the previous nextseq and
         *      (the previous segment is possibly a zero window probe)
         *
         * We should still try to flag Spurious Retransmissions though.
         */
        if (seglen > 1 && tcpd->fwd->tcp_analyze_seq_info->nextseq - 1 == seq) {
            seq_not_advanced = FALSE;
        }

        /* If there were >=2 duplicate ACKs in the reverse direction
         * (there might be duplicate acks missing from the trace)
         * and if this sequence number matches those ACKs
         * and if the packet occurs within 20ms of the last
         * duplicate ack
         * then this is a fast retransmission
         */
        t=(pinfo->abs_ts.secs-tcpd->rev->tcp_analyze_seq_info->lastacktime.secs)*1000000000;
        t=t+(pinfo->abs_ts.nsecs)-tcpd->rev->tcp_analyze_seq_info->lastacktime.nsecs;
        if( seq_not_advanced
        &&  tcpd->rev->tcp_analyze_seq_info->dupacknum>=2
        &&  tcpd->rev->tcp_analyze_seq_info->lastack==seq
        &&  t<20000000 ) {
            if(!tcpd->ta) {
                tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
            }
            tcpd->ta->flags|=TCP_A_FAST_RETRANSMISSION;
            goto finished_checking_retransmission_type;
        }

        /* If the segment came relatively close since the segment with the highest
         * seen sequence number and it doesn't look like a retransmission
         * then it is an OUT-OF-ORDER segment.
         */
        t=(pinfo->abs_ts.secs-tcpd->fwd->tcp_analyze_seq_info->nextseqtime.secs)*1000000000;
        t=t+(pinfo->abs_ts.nsecs)-tcpd->fwd->tcp_analyze_seq_info->nextseqtime.nsecs;
        if (tcpd->ts_first_rtt.nsecs == 0 && tcpd->ts_first_rtt.secs == 0) {
            ooo_thres = 3000000;
        } else {
            ooo_thres = tcpd->ts_first_rtt.nsecs + tcpd->ts_first_rtt.secs*1000000000;
        }

        if( seq_not_advanced // XXX is this neccessary?
        && t < ooo_thres
        && tcpd->fwd->tcp_analyze_seq_info->nextseq != seq + seglen ) {
            if(!tcpd->ta) {
                tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
            }
            tcpd->ta->flags|=TCP_A_OUT_OF_ORDER;
            goto finished_checking_retransmission_type;
        }

        /* Check for spurious retransmission. If the current seq + segment length
         * is less than or equal to the current lastack, the packet contains
         * duplicate data and may be considered spurious.
         */
        if ( seglen > 0
        && tcpd->rev->tcp_analyze_seq_info->lastack
        && LE_SEQ(seq + seglen, tcpd->rev->tcp_analyze_seq_info->lastack) ) {
            if(!tcpd->ta){
                tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
            }
            tcpd->ta->flags|=TCP_A_SPURIOUS_RETRANSMISSION;
            goto finished_checking_retransmission_type;
        }

        if (seq_not_advanced) {
            /* Then it has to be a generic retransmission */
            if(!tcpd->ta) {
                tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
            }
            tcpd->ta->flags|=TCP_A_RETRANSMISSION;
            nstime_delta(&tcpd->ta->rto_ts, &pinfo->abs_ts, &tcpd->fwd->tcp_analyze_seq_info->nextseqtime);
            tcpd->ta->rto_frame=tcpd->fwd->tcp_analyze_seq_info->nextseqframe;
        }
    }

finished_checking_retransmission_type:

    nextseq = seq+seglen;
    if ((seglen || flags&(TH_SYN|TH_FIN)) && tcpd->fwd->tcp_analyze_seq_info->segment_count < TCP_MAX_UNACKED_SEGMENTS) {
        /* Add this new sequence number to the fwd list.  But only if there
         * aren't "too many" unacked segments (e.g., we're not seeing the ACKs).
         */
        ual = wmem_new(wmem_file_scope(), tcp_unacked_t);
        ual->next=tcpd->fwd->tcp_analyze_seq_info->segments;
        tcpd->fwd->tcp_analyze_seq_info->segments=ual;
        tcpd->fwd->tcp_analyze_seq_info->segment_count++;
        ual->frame=pinfo->num;
        ual->seq=seq;
        ual->ts=pinfo->abs_ts;

        /* next sequence number is seglen bytes away, plus SYN/FIN which counts as one byte */
        if( (flags&(TH_SYN|TH_FIN)) ) {
            nextseq+=1;
        }
        ual->nextseq=nextseq;
    }

    /* Store the highest number seen so far for nextseq so we can detect
     * when we receive segments that arrive with a "hole"
     * If we don't have anything since before, just store what we got.
     * ZeroWindowProbes are special and don't really advance the nextseq
     */
    if(GT_SEQ(nextseq, tcpd->fwd->tcp_analyze_seq_info->nextseq) || !tcpd->fwd->tcp_analyze_seq_info->nextseq) {
        if( !tcpd->ta || !(tcpd->ta->flags&TCP_A_ZERO_WINDOW_PROBE) ) {
            tcpd->fwd->tcp_analyze_seq_info->nextseq=nextseq;
            tcpd->fwd->tcp_analyze_seq_info->nextseqframe=pinfo->num;
            tcpd->fwd->tcp_analyze_seq_info->nextseqtime.secs=pinfo->abs_ts.secs;
            tcpd->fwd->tcp_analyze_seq_info->nextseqtime.nsecs=pinfo->abs_ts.nsecs;
        }
    }

    /* Store the highest continuous seq number seen so far for 'max seq to be acked',
     so we can detect TCP_A_ACK_LOST_PACKET condition
     */
    if(EQ_SEQ(seq, tcpd->fwd->tcp_analyze_seq_info->maxseqtobeacked) || !tcpd->fwd->tcp_analyze_seq_info->maxseqtobeacked) {
        if( !tcpd->ta || !(tcpd->ta->flags&TCP_A_ZERO_WINDOW_PROBE) ) {
            tcpd->fwd->tcp_analyze_seq_info->maxseqtobeacked=tcpd->fwd->tcp_analyze_seq_info->nextseq;
        }
    }

    /* remember what the ack/window is so we can track window updates and retransmissions */
    tcpd->fwd->window=window;
    tcpd->fwd->tcp_analyze_seq_info->lastack=ack;
    tcpd->fwd->tcp_analyze_seq_info->lastacktime.secs=pinfo->abs_ts.secs;
    tcpd->fwd->tcp_analyze_seq_info->lastacktime.nsecs=pinfo->abs_ts.nsecs;

    /* if there were any flags set for this segment we need to remember them
     * we only remember the flags for the very last segment though.
     */
    if(tcpd->ta) {
        tcpd->fwd->lastsegmentflags=tcpd->ta->flags;
    } else {
        tcpd->fwd->lastsegmentflags=0;
    }

    /* remove all segments this ACKs and we don't need to keep around any more
     */
    ackcount=0;
    prevual = NULL;
    ual = tcpd->rev->tcp_analyze_seq_info->segments;
    while(ual) {
        tcp_unacked_t *tmpual;

        /* If this ack matches the segment, process accordingly */
        if(ack==ual->nextseq) {
            tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
            tcpd->ta->frame_acked=ual->frame;
            nstime_delta(&tcpd->ta->ts, &pinfo->abs_ts, &ual->ts);
        }
        /* If this acknowledges part of the segment, adjust the segment info for the acked part */
        else if (GT_SEQ(ack, ual->seq) && LE_SEQ(ack, ual->nextseq)) {
            ual->seq = ack;
            continue;
        }
        /* If this acknowledges a segment prior to this one, leave this segment alone and move on */
        else if (GT_SEQ(ual->nextseq,ack)) {
            prevual = ual;
            ual = ual->next;
            continue;
        }

        /* This segment is old, or an exact match.  Delete the segment from the list */
        ackcount++;
        tmpual=ual->next;

        if (tcpd->rev->scps_capable) {
          /* Track largest segment successfully sent for SNACK analysis*/
          if ((ual->nextseq - ual->seq) > tcpd->fwd->maxsizeacked) {
            tcpd->fwd->maxsizeacked = (ual->nextseq - ual->seq);
          }
        }

        if (!prevual) {
            tcpd->rev->tcp_analyze_seq_info->segments = tmpual;
        }
        else{
            prevual->next = tmpual;
        }
        wmem_free(wmem_file_scope(), ual);
        ual = tmpual;
        tcpd->rev->tcp_analyze_seq_info->segment_count--;
    }

    /* how many bytes of data are there in flight after this frame
     * was sent
     */
    ual=tcpd->fwd->tcp_analyze_seq_info->segments;
    if (tcp_track_bytes_in_flight && seglen!=0 && ual && tcpd->fwd->valid_bif) {
        guint32 first_seq, last_seq, in_flight;

        first_seq = ual->seq - tcpd->fwd->base_seq;
        last_seq = ual->nextseq - tcpd->fwd->base_seq;
        while (ual) {
            if ((ual->nextseq-tcpd->fwd->base_seq)>last_seq) {
                last_seq = ual->nextseq-tcpd->fwd->base_seq;
            }
            if ((ual->seq-tcpd->fwd->base_seq)<first_seq) {
                first_seq = ual->seq-tcpd->fwd->base_seq;
            }
            ual = ual->next;
        }
        in_flight = last_seq-first_seq;

        if (in_flight>0 && in_flight<2000000000) {
            if(!tcpd->ta) {
                tcp_analyze_get_acked_struct(pinfo->num, seq, ack, TRUE, tcpd);
            }
            tcpd->ta->bytes_in_flight = in_flight;
        }

        if((flags & TH_PUSH) && !tcpd->fwd->push_set_last) {
          tcpd->fwd->push_bytes_sent += seglen;
          tcpd->fwd->push_set_last = TRUE;
        } else if ((flags & TH_PUSH) && tcpd->fwd->push_set_last) {
          tcpd->fwd->push_bytes_sent = seglen;
          tcpd->fwd->push_set_last = TRUE;
        } else if (tcpd->fwd->push_set_last) {
          tcpd->fwd->push_bytes_sent = seglen;
          tcpd->fwd->push_set_last = FALSE;
        } else {
          tcpd->fwd->push_bytes_sent += seglen;
        }
        if(!tcpd->ta) {
          tcp_analyze_get_acked_struct(pinfo->fd->num, seq, ack, TRUE, tcpd);
        }
        tcpd->ta->push_bytes_sent = tcpd->fwd->push_bytes_sent;
    }

}

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;


tcp_sequence_number_analysis_print_push_bytes_sent(packet_info * pinfo _U_,
                          tvbuff_t * tvb,
                          proto_tree * flags_tree,
                          struct tcp_acked *ta
                        )
{
    proto_item * flags_item;

    if (tcp_track_bytes_in_flight) {
        flags_item=proto_tree_add_uint(flags_tree,
                                       hf_tcp_analysis_push_bytes_sent,
                                       tvb, 0, 0, ta->push_bytes_sent);

        PROTO_ITEM_SET_GENERATED(flags_item);
    }
}

static int hf_tcp_analysis_push_bytes_sent = -1;

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

} tcp_follow_tap_data_t;

static gboolean tcp_track_bytes_in_flight = TRUE;

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;


pdu_store_window_scale_option(guint8 ws, struct tcp_analysis *tcpd)
{
    if (tcpd)
        tcpd->fwd->win_scale=ws;
}

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;


tcp_init(void)
{
    tcp_stream_count = 0;

    /* MPTCP init */
    mptcp_stream_count = 0;
    mptcp_tokens = wmem_tree_new(wmem_file_scope());
}

void proto_register_tcp(void);

void proto_reg_handoff_tcp(void);

static guint32 tcp_stream_count;

static guint32 mptcp_stream_count;

static wmem_tree_t *mptcp_tokens = NULL;

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 const char* tcp_conv_get_filter_type(conv_item_t* conv, 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(!conv) {
        return CONV_FILTER_INVALID;
    }

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

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

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

    return CONV_FILTER_INVALID;
}


tcp_flags_to_str(wmem_allocator_t *scope, const struct tcpheader *tcph)
{
    static const char flags[][4] = { "FIN", "SYN", "RST", "PSH", "ACK", "URG", "ECN", "CWR", "NS" };
    const int maxlength = 64; /* upper bounds, max 53B: 8 * 3 + 2 + strlen("Reserved") + 9 * 2 + 1 */

    char *pbuf;
    const char *buf;

    int i;

    buf = pbuf = (char *) wmem_alloc(scope, maxlength);
    *pbuf = '\0';

    for (i = 0; i < 9; i++) {
        if (tcph->th_flags & (1 << i)) {
            if (buf[0])
                pbuf = g_stpcpy(pbuf, ", ");
            pbuf = g_stpcpy(pbuf, flags[i]);
        }
    }

    if (tcph->th_flags & TH_RES) {
        if (buf[0])
            pbuf = g_stpcpy(pbuf, ", ");
        g_stpcpy(pbuf, "Reserved");
    }

    if (buf[0] == '\0')
        buf = "<None>";

    return buf;
}


dissect_tcpopt_correxp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
    return dissect_tcpopt_default_option(tvb, pinfo, tree, proto_tcp_option_scpscor, ett_tcp_opt_scpscor);
}

void proto_register_tcp(void);

void proto_reg_handoff_tcp(void);

static int proto_tcp_option_scpscor = -1;

static gint ett_tcp_opt_scpscor = -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);


mptcp_cryptodata_sha1(const guint64 key, guint32 *token, guint64 *idsn)
{
    guint8 digest_buf[HASH_SHA1_LENGTH];
    guint64 pseudokey = GUINT64_TO_BE(key);
    guint32 _token;
    guint64 _isdn;

    gcry_md_hash_buffer(GCRY_MD_SHA1, digest_buf, (const guint8 *)&pseudokey, 8);

    /* memcpy to prevent -Wstrict-aliasing errors with GCC 4 */
    memcpy(&_token, digest_buf, sizeof(_token));
    *token = GUINT32_FROM_BE(_token);
    memcpy(&_isdn, digest_buf + HASH_SHA1_LENGTH - sizeof(_isdn), sizeof(_isdn));
    *idsn = GUINT64_FROM_BE(_isdn);
}

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"
};


handle_export_pdu_heuristic(packet_info *pinfo, tvbuff_t *tvb, heur_dtbl_entry_t *hdtbl_entry, struct tcpinfo *tcpinfo)
{
    exp_pdu_data_t *exp_pdu_data = NULL;

    if (have_tap_listener(exported_pdu_tap)) {
        if ((!hdtbl_entry->enabled) ||
            (hdtbl_entry->protocol != NULL && !proto_is_protocol_enabled(hdtbl_entry->protocol))) {
            exp_pdu_data = export_pdu_create_common_tags(pinfo, "data", EXP_PDU_TAG_PROTO_NAME);
        } else if (hdtbl_entry->protocol != 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_dissector_data.data = tcpinfo;

            exp_pdu_data = export_pdu_create_tags(pinfo, hdtbl_entry->short_name, EXP_PDU_TAG_HEUR_PROTO_NAME, tcp_exp_pdu_items);
        }

        if (exp_pdu_data != NULL) {
            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);
        }
    }
}


tcp_dst_value(packet_info *pinfo)
{
    return p_get_proto_data(pinfo->pool, pinfo, hf_tcp_dstport, pinfo->curr_layer_num);
}

static int hf_tcp_dstport = -1;

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


dissect_tcpopt_wscale(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
    guint8 val;
    guint32 shift;
    proto_item *wscale_pi, *shift_pi, *gen_pi;
    proto_tree *wscale_tree;
    proto_item *length_item;
    int offset = 0;
    struct tcp_analysis *tcpd;

    tcpd=get_tcp_conversation_data(NULL,pinfo);

    wscale_pi = proto_tree_add_item(tree, proto_tcp_option_wscale, tvb, offset, -1, ENC_NA);
    wscale_tree = proto_item_add_subtree(wscale_pi, ett_tcp_option_wscale);

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

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

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

    shift_pi = proto_tree_add_item_ret_uint(wscale_tree, hf_tcp_option_wscale_shift, tvb, offset, 1, ENC_BIG_ENDIAN, &shift);
    if (shift > 14) {
        /* RFC 1323: "If a Window Scale option is received with a shift.cnt
         * value exceeding 14, the TCP should log the error but use 14 instead
         * of the specified value." */
        shift = 14;
        expert_add_info(pinfo, shift_pi, &ei_tcp_option_wscale_shift_invalid);
    }

    gen_pi = proto_tree_add_uint(wscale_tree, hf_tcp_option_wscale_multiplier, tvb,
                                 offset, 1, 1 << shift);
    PROTO_ITEM_SET_GENERATED(gen_pi);
    val = tvb_get_guint8(tvb, offset);

    proto_item_append_text(wscale_pi, ": %u (multiply by %u)", val, 1 << shift);

    tcp_info_append_uint(pinfo, "WS", 1 << shift);

    if(!pinfo->fd->visited) {
        pdu_store_window_scale_option(shift, tcpd);
    }

    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;