diff --git a/pelab/magent/PacketSensor.cc b/pelab/magent/PacketSensor.cc index 80ffc981545e2ae4c028b297645cfb11657bcc99..c2f02eca6df116c6eb1be6335bf2259f8d50ef02 100644 --- a/pelab/magent/PacketSensor.cc +++ b/pelab/magent/PacketSensor.cc @@ -76,7 +76,7 @@ void PacketSensor::localSend(PacketInfo * packet) } } // Assume this packet is not a retransmit unless proven otherwise - ackValid = false; + ackValid = true; isRetransmit = false; if (state->isSendValid() && state->getState() == StateSensor::ESTABLISHED) { @@ -187,95 +187,188 @@ void PacketSensor::localAck(PacketInfo * packet) { // Set it to true, and then set it to false if we encounter an error. ackValid = true; + + /* + * Thanks to SACKs, we might have to worry about more than one range of + * ACKed packets. + */ + rangelist ranges; + /* * When we get an ACK, the sequence number is really the next one the peer * excects to see: thus, the last sequence number it's ACKing is one less * than this. - * Note: This should handle wraparound properly */ uint32_t ack_for = ntohl(packet->tcp->ack_seq) - 1; - logWrite(SENSOR_DETAIL, "PacketSensor::localAck() for sequence number %u", ack_for); - /* - * Check to see if the ack is for a seq number smaller than the global - * sequence - this could mean two things: (1) it's a duplicate ack (in - * which case it means packet loss or packet re-ordering) (2) we got two - * (or more) acks out of order - * No action taken yet other than logging - * XXX: Needs support for wraparound! + * We assume that the ACK field of the packet is acking from the smallest + * sequence number we know of up to it value + * XXX: When the ACK is for a sequence number smaller than any we know of, + * we could assume it is not acking any new packets - but, this requires + * some more thought about wraparaound, so we'll live with spurious errors + * for now. */ - if (ack_for < globalSequence.seqStart) - { - if (ack_for == (globalSequence.seqStart - 1)) { - logWrite(SENSOR_DETAIL, "PacketSensor::localAck() detected a " - "duplicate ack"); - return; - } else { - logWrite(SENSOR_DETAIL, "PacketSensor::localAck() detected an " - "old ack"); - } - } - - list<SentPacket>::iterator pos = unacked.begin(); - list<SentPacket>::iterator limit = unacked.end(); - bool found = false; - ackedSize = 0; + ranges.push_back(rangepair(globalSequence.seqStart,ack_for)); /* - * Make sure this packet doesn't have a SACK option, in which case our - * calculation is wrong. + * Now look for SACKs */ list<Option>::iterator opt; + bool hasSack = false; for (opt = packet->tcpOptions->begin(); opt != packet->tcpOptions->end(); ++opt) { if (opt->type == TCPOPT_SACK) { - logWrite(ERROR,"Packet has a SACK option!"); - ackValid = false; + struct SACKOption *optheader = (struct SACKOption*)(opt->buffer); + /* + * Figure out how many regions there are in this option header. There + * are two octets for the 'kind' and length fields of the header, then + * two 4-octet sequence numbers for each region + */ + int num_sacks = (optheader->length - 2) / 8; + if ((optheader->length - 2) % 8 == 0) { + logWrite(SENSOR,"Bad SACK header length: %i", (optheader->length)); + return; + } + for (int i = 0; i < num_sacks; i++) { + uint32_t start = ntohl(optheader->regions[i*2]); + /* + * Like a reguar ACK, the 'end' is the first sequence number *after* + * the range + */ + uint32_t end = ntohl(optheader->regions[i*2 + 1]) - 1; + ranges.push_back(rangepair(start, end)); + logWrite(SENSOR_COMPLETE,"PacketSensor::localAck() found SACK " + "range %u to %u", start, end); + } + + hasSack = true; } } - while (pos != limit && !found) - { - found = pos->inSequenceBlock(ack_for); + /* + * Now, take evry range we know about and handle it + * XXX: Needs some though wrt wraparound + */ + ackedSize = 0; + ackedSendTime = Time(); + rangelist::iterator range; + for (range = ranges.begin(); range != ranges.end(); range++) { + uint32_t range_start = range->first; + uint32_t range_end = range->second; + + logWrite(SENSOR_DETAIL, "PacketSensor::localAck() handling range " + "%u to %u", range_start, range_end); + /* - * XXX: Assumes that SACK is not in use - assumes that this ACK - * is for all sequence numbers up to the one it's ACKing + * Check to see if the ack is for a seq number smaller than the global + * sequence - this could mean two things: (1) it's a duplicate ack (in + * which case it means packet loss or packet re-ordering) (2) we got two + * (or more) acks out of order + * No action taken yet other than logging + * XXX: Needs support for wraparound! */ - ackedSize += pos->totalLength; - if (found) + if (range_end < globalSequence.seqStart) { - ackedSendTime = pos->timestamp; + if (range_end == (globalSequence.seqStart - 1)) { + logWrite(SENSOR_DETAIL, "PacketSensor::localAck() detected a " + "duplicate ack"); + continue; + } + else + { + logWrite(SENSOR_DETAIL, "PacketSensor::localAck() detected an " + "old ack"); + continue; + } + } + + /* + * Now, we find the packets which contain the start and the end of the + * region being acked. For now, if one of these packets is missing, we + * just indicate that our value is invalid. In the future, we could try + * something fancier, like guess the length of the missing packets (from + * the sequence number). + * XXX: Again, needs support for wraparound! + */ + list<SentPacket>::iterator firstPacket = unacked.begin(); + while (firstPacket != unacked.end() && + !firstPacket->inSequenceBlock(range_start)) { + firstPacket++; } - if (pos != limit) + + if (firstPacket == unacked.end()) { + logWrite(ERROR, "Range starts in unknown packet"); + ackValid = false; + break; + } + + list<SentPacket>::iterator lastPacket = unacked.begin(); + while (lastPacket != unacked.end() && + !lastPacket->inSequenceBlock(range_end)) { + lastPacket++; + } + + if (lastPacket == unacked.end()) { + logWrite(ERROR, "Range ends in unknown packet"); + ackValid = false; + break; + } + + /* + * Now, iterate between the two packets, looking at timestamps and adding + * up sizes + */ + list<SentPacket>::iterator pos = firstPacket; + while (1) { - ++pos; + ackedSize += pos->totalLength; + /* + * We want the time for the most recently acked packet, not the one + * that has the highest sequence number + */ + if (pos->timestamp > ackedSendTime) { + ackedSendTime = pos->timestamp; + } + /* + * XXX - should we be looking for packets in the range that we didn't + * know about? + */ + if (pos == lastPacket) { + break; + } else { + ++pos; + } + } + + /* + * Now, remove these packets from our list. + * XXX: This makes the assumption that all acks are on packet boundaries + * - this seems be be true in practice, but might not be true in theory + * XXX: In the future, we may want to marked SACKed packets, not totally + * remove them + */ + unacked.erase(firstPacket, lastPacket); + // erase() does not erase the final member of the range, so we have + // to do that ourselves + unacked.erase(lastPacket); + + if (unacked.empty()) + { + globalSequence.seqStart = 0; + globalSequence.seqEnd = 0; + } + else + { + globalSequence.seqStart = unacked.front().seqStart; + globalSequence.seqEnd = unacked.back().seqEnd; } - } - if (!found) - { - logWrite(ERROR, "Could not find the ack sequence number in list " - "of unacked packets."); - ackedSize = 0; - ackedSendTime = Time(); - ackValid = false; - return; - } - unacked.erase(unacked.begin(), pos); - if (unacked.empty()) - { - globalSequence.seqStart = 0; - globalSequence.seqEnd = 0; - } - else - { - globalSequence.seqStart = unacked.front().seqStart; } - logWrite(SENSOR_DETAIL, "PacketSensor::localAck() decided on size %u", - ackedSize); + logWrite(SENSOR_DETAIL,"PacketSensor::localAck() decided on size %i", + ackedSize); } else { diff --git a/pelab/magent/PacketSensor.h b/pelab/magent/PacketSensor.h index 5f1892e2a58b194c1a84c5bbc01707ad2fbeacd7..50a9e192eddaa9cb2ae37d28a5c3442f16e06e35 100644 --- a/pelab/magent/PacketSensor.h +++ b/pelab/magent/PacketSensor.h @@ -7,6 +7,7 @@ #include "Sensor.h" #include "Time.h" +#include "lib.h" class StateSensor; @@ -35,6 +36,15 @@ private: unsigned int totalLength; Time timestamp; }; + struct SACKOption + { + unsigned char kind; + unsigned char length; + uint32_t regions[]; + } __attribute__((__packed__)); + + typedef std::pair<uint32_t, uint32_t> rangepair; + typedef std::list<rangepair> rangelist; private: int ackedSize; Time ackedSendTime;