enum t_ack ack_in (tcptrace_context_t *context, tcb * ptcb, seqnum ack, unsigned tcp_data_length, u_long eff_win) { tcptrace_runtime_options_t *options = context->options; quadrant *pquad; quadrant *pquad_prev; segment *pseg; Bool changed_one = FALSE; Bool intervening_xmits = FALSE; timeval last_xmit = { 0, 0 }; enum t_ack ret = 0; enum dup_ack_handling { BSD_VERSION = 1, /* Handling of duplicate ack's based on the specifications of BSD code */ LEGACY_VERSION = 2 /* Handling of duplicate ack's according to the old versions of "tcptrace" */ }; enum dup_ack_handling dup_ack_type; /* default type is the code based on BSD specifications */ /* check each segment in the segment list for the PREVIOUS quadrant */ pquad = whichquad (ptcb->ss, ack); pquad_prev = pquad->prev; for (pseg = pquad_prev->seglist_head; pseg != NULL; pseg = pseg->next) { if (!pseg->acked) { ++pseg->acked; changed_one = TRUE; ++ptcb->rtt_cumack; /* keep track of the newest transmission */ if (tv_gt (pseg->time, last_xmit)) last_xmit = pseg->time; } } if (changed_one) collapse_quad (pquad_prev); /* check each segment in the segment list for the CURRENT quadrant */ changed_one = FALSE; for (pseg = pquad->seglist_head; pseg != NULL; pseg = pseg->next) { if (ack <= pseg->seq_firstbyte) { /* doesn't cover anything else on the list */ break; } /* keep track of the newest transmission */ if (tv_gt (pseg->time, last_xmit)) last_xmit = pseg->time; /* (ELSE) ACK covers this sequence */ if (pseg->acked) { /* default will be the BSD version, it can be changed by giving '--turn_off_BSD_dupack' switch */ dup_ack_type = (options->dup_ack_handling) ? BSD_VERSION : LEGACY_VERSION; /* default type is the specifications based on BSD code */ switch (dup_ack_type) { case LEGACY_VERSION: if (ack == (pseg->seq_lastbyte + 1)) { ++pseg->acked; /* already acked this one */ ++ptcb->rtt_dupack; /* one more duplicate ack */ ret = CUMUL; if (pseg->acked == 4) { /* some people say these CAN'T have data */ if ((tcp_data_length == 0) || options->triple_dupack_allows_data) { ++ptcb->rtt_triple_dupack; ret = TRIPLE; } } } break; case BSD_VERSION: /* For an acknowledgement to be considered as duplicate ACK in BSD version, following rules must be followed: 1) the received segment should contain the biggest ACK TCP has seen, 2) the length of the segment containing dup ack should be 0, 3) advertised window in this segment should not change, 4) and there must be some outstanding data */ if ((ack == (pseg->seq_lastbyte + 1)) && (ack == ptcb->ptwin->ack) && (tcp_data_length == 0) && (eff_win == ptcb->ptwin->win_last) && (ptcb->owin_tot > 0)) { ++ptcb->rtt_dupack; ret = CUMUL; /* already acked this one */ ++pseg->acked; if (pseg->acked == 4) { ++ptcb->rtt_triple_dupack; ret = TRIPLE; } } else pseg->acked = 1; /* received segment is not pure duplicate acknowledgement */ } continue; } /* ELSE !acked */ ++pseg->acked; changed_one = TRUE; if (ack == (pseg->seq_lastbyte + 1)) { /* if ANY preceding segment was xmitted after this one, the the RTT sample is invalid */ intervening_xmits = (tv_gt (last_xmit, pseg->time)); ret = rtt_ackin(context, ptcb, pseg, intervening_xmits); } else { /* cumulatively ACKed */ ++ptcb->rtt_cumack; ret = CUMUL; } } if (changed_one) collapse_quad (pquad); return (ret); }
/** * //<aa> * - ptcb: the tcb of the direction opposite to the one in which this ack has been sent. * //</aa> */ enum t_ack ack_in (tcb * ptcb, seqnum ack, unsigned tcp_data_length) { quadrant *pquad; quadrant *pquad_prev; segment *pseg; Bool changed_one = FALSE; Bool intervening_xmits = FALSE; timeval last_xmit = { 0, 0 }; enum t_ack ret = 0; /* check each segment in the segment list for the PREVIOUS quadrant */ pquad = whichquad (ptcb->ss, ack); pquad_prev = pquad->prev; for (pseg = pquad_prev->seglist_head; pseg != NULL; pseg = pseg->next) { if (!pseg->acked) { ++pseg->acked; changed_one = TRUE; ++ptcb->rtt_cumack; /* keep track of the newest transmission */ if (tv_gt (pseg->time, last_xmit)) last_xmit = pseg->time; } } if (changed_one) collapse_quad (pquad_prev); /* check each segment in the segment list for the CURRENT quadrant */ changed_one = FALSE; for (pseg = pquad->seglist_head; pseg != NULL; pseg = pseg->next) { if (ack <= pseg->seq_firstbyte) { /* doesn't cover anything else on the list */ break; } /* keep track of the newest transmission */ if (tv_gt (pseg->time, last_xmit)) last_xmit = pseg->time; /* (ELSE) ACK covers this sequence */ if (pseg->acked) { /* already acked this one */ ++pseg->acked; if (ack == (pseg->seq_lastbyte + 1)){ ++ptcb->rtt_dupack; /* one more duplicate ack */ ret = CUMUL; if (pseg->acked == 4) { /* some people say these CAN'T have data */ if ((tcp_data_length == 0)) { ++ptcb->rtt_triple_dupack; ret = TRIPLE; } } } continue; } /* ELSE !acked */ //<aa>This is the firt time that this segment has been acked </aa> ++pseg->acked; changed_one = TRUE; if (ack == (pseg->seq_lastbyte + 1)){ /* if ANY preceding segment was xmitted after this one, the the RTT sample is invalid */ intervening_xmits = (tv_gt (last_xmit, pseg->time)); //<aa> The RTT is valid only if the segment being acked with the present ack is // the last transmitted one; // In order to be sure of this, we check if last_xmit > pseg_time. If this is the // case, intervening_xmits is true and the RTT is not valid. // If, instead, intervening_xmits is false, the RTT is valid </aa> // to be sure that the present ret = rtt_ackin (ptcb, pseg, intervening_xmits); } else { /* cumulatively ACKed */ ++ptcb->rtt_cumack; ret = CUMUL; } } if (changed_one) collapse_quad (pquad); return (ret); }