[Bug 199287] Missing TCP retransmit timer reset

bugzilla-noreply at freebsd.org bugzilla-noreply at freebsd.org
Wed Apr 8 08:17:17 UTC 2015


https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=199287

            Bug ID: 199287
           Summary: Missing TCP retransmit timer reset
           Product: Base System
           Version: 9.3-RELEASE
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Only Me
          Priority: ---
         Component: kern
          Assignee: freebsd-bugs at FreeBSD.org
          Reporter: sebastian.huber at embedded-brains.de

There is a potential problem in the TCP implementation.  I don't use FreeBSD
directly, instead I use a port of the FreeBSD 9.3 network stack to the RTEMS
real-time operating system.  Nonetheless, this problem might exist in the real
FreeBSD system.

During tests under heavy load conditions the following situation occurred. I
transfer data from my development hosts /dev/zero to the targets (RTEMS running
the FreeBSD network stack) /dev/null via TCP. Thus the target sends only ACKs
after the TCP connection setup. The interface driver uses an equal count for
the rx and tx DMA descriptors.  I use a small value to provoke packet loss at
the rx side.  This packet loss is dealt via SACK, so the transfer rate is
nearly constant and at a high level for the target.  In this setup the tx queue
overflows occasionally, thus in ip_output() we end up at

    /*
     * Verify that we have any chance at all of being able to queue the
     * packet or packet fragments, unless ALTQ is enabled on the given
     * interface in which case packetdrop should be done by queueing.
     */
    n = ip->ip_len / mtu + 1; /* how many fragments ? */
    if (
#ifdef ALTQ
        (!ALTQ_IS_ENABLED(&ifp->if_snd)) &&
#endif /* ALTQ */
        (ifp->if_snd.ifq_len + n) >= ifp->if_snd.ifq_maxlen ) {
        error = ENOBUFS;
        IPSTAT_INC(ips_odropped);
        ifp->if_snd.ifq_drops += n;
        goto bad;
    }

and return ENOBUFS.  This leads to tcp_ouput()

out:
        SOCKBUF_UNLOCK_ASSERT(&so->so_snd);    /* Check gotos. */
        switch (error) {
        case EPERM:
            tp->t_softerror = error;
            return (error);
        case ENOBUFS:
                    if (!tcp_timer_active(tp, TT_REXMT) &&
                !tcp_timer_active(tp, TT_PERSIST))
                            tcp_timer_activate(tp, TT_REXMT, tp->t_rxtcur);
            tp->snd_cwnd = tp->t_maxseg;
            return (0);

So here we start the retransmit timer.  An occasional drop of ACK-only packets
doesn't hurt in this scenario so we don't observe any problems and on the rx
side we nearly always end up in tcp_do_segment() with tlen != 0


    /*
     * Header prediction: check for the two common cases
     * of a uni-directional data xfer.  If the packet has
     * no control flags, is in-sequence, the window didn't
     * change and we're not retransmitting, it's a
     * candidate.  If the length is zero and the ack moved
     * forward, we're the sender side of the xfer.  Just
     * free the data acked & wake any higher level process
     * that was blocked waiting for space.  If the length
     * is non-zero and the ack didn't move, we're the
     * receiver side.  If we're getting packets in-order
     * (the reassembly queue is empty), add the data to
     * the socket buffer and note that we need a delayed ack.
     * Make sure that the hidden state-flags are also off.
     * Since we check for TCPS_ESTABLISHED first, it can only
     * be TH_NEEDSYN.
     */
    if (tp->t_state == TCPS_ESTABLISHED &&
        th->th_seq == tp->rcv_nxt &&
        (thflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK &&
        tp->snd_nxt == tp->snd_max &&
        tiwin && tiwin == tp->snd_wnd && 
        ((tp->t_flags & (TF_NEEDSYN|TF_NEEDFIN)) == 0) &&
        LIST_EMPTY(&tp->t_segq) &&
        ((to.to_flags & TOF_TS) == 0 ||
         TSTMP_GEQ(to.to_tsval, tp->ts_recent)) ) {

In this path the retransmit timer is never reset, so in the end the connection
is dropped by tcp_timer_rexmt() even though there is no real connection
problem.

-- 
You are receiving this mail because:
You are the assignee for the bug.


More information about the freebsd-bugs mailing list