/*
* Copyright (c) 2006 University of Utah and the Flux Group.
*
* {{{EMULAB-LICENSE
*
* This file is part of the Emulab network testbed software.
*
* This file is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* This file is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this file. If not, see .
*
* }}}
*/
#include "UdpThroughputSensor.h"
using namespace std;
UdpThroughputSensor::UdpThroughputSensor(UdpPacketSensor const * udpPacketSensorVal)
: lastAckTime(0),
throughputKbps(0.0),
packetHistory(udpPacketSensorVal)
{
// Do nothing.
}
UdpThroughputSensor::~UdpThroughputSensor()
{
}
void UdpThroughputSensor::localSend(PacketInfo *packet)
{
sendValid = true;
ackValid = false;
}
void UdpThroughputSensor::localAck(PacketInfo *packet)
{
// This is a re-ordered ACK - don't do anything
// with it - just return.
sendValid = false;
if( packetHistory->isAckValid() != true )
{
ackValid = false;
return;
}
else
ackValid = true;
// Find out how many redundant ACKs this packet is carrying - 0 to 121.
unsigned char numRedunAcksChar = 0;
memcpy(&numRedunAcksChar, &packet->payload[0], global::UCHAR_SIZE);
int numRedunAcks = static_cast(numRedunAcksChar);
int numThroughputAcks = 1;
double avgThroughput = 0;
// This is the timestamp at the receiver, when the original packet was received.
unsigned long long currentAckTimeStamp = *(unsigned long long *)(packet->payload + 1 + 2*global::USHORT_INT_SIZE );
// This is the first ACK we have seen, store its receiver timestamp
// and return, we cannot calculate throughput from just one ACK - at least 2.
if(lastAckTime == 0)
{
lastAckTime = currentAckTimeStamp;
return;
}
unsigned short int seqNum = *(unsigned int *)(packet->payload + 1);
unsigned short int echoedPacketSize = *(unsigned short int *)(packet->payload + 1 + global::USHORT_INT_SIZE);
unsigned long long ackTimeDiff = currentAckTimeStamp - lastAckTime;
unsigned long long timeDiff = 0;
vector ackedPackets = packetHistory->getAckedPackets();
vector::iterator vecIterator;
// Average the throughput over all the packets being acknowledged.
if(numRedunAcks > 0)
{
int i;
unsigned short int redunSeqNum;
unsigned short int redunPacketSize;
for(i = 0;i < numRedunAcks; i++)
{
redunSeqNum = *(unsigned short int *)(packet->payload + 1 + global::udpMinAckPacketSize + i*global::udpRedunAckSize);
redunPacketSize = *(unsigned short int *)(packet->payload + 1 + global::udpMinAckPacketSize + i*global::udpRedunAckSize + global::USHORT_INT_SIZE);
// Find if this redundant ACK is useful - or it was acked before.
vecIterator = find_if(ackedPackets.begin(), ackedPackets.end(), bind2nd(equalSeqNum(), redunSeqNum));
if(vecIterator != ackedPackets.end())
{
timeDiff = *(unsigned long long *)(packet->payload + 1 + global::udpMinAckPacketSize + i*global::udpRedunAckSize + global::udpSeqNumSize);
// Avoid dividing by zero.
if(ackTimeDiff - timeDiff == 0)
continue;
// Calculate throughput for the packet being acked by
// the redundant ACK.
numThroughputAcks++;
// We lost the record of the size of this packet due to libpcap
// loss, use the length echoed back in the ACK.
if((*vecIterator).isFake == true)
avgThroughput += 8000000.0*( static_cast ( redunPacketSize )) / ( static_cast(ackTimeDiff - timeDiff)*1024.0 );
else
avgThroughput += 8000000.0*( static_cast ( (*vecIterator).packetSize )) / ( static_cast(ackTimeDiff - timeDiff)*1024.0 );
ackTimeDiff = timeDiff;
}
}
}
// Calculate the throughput for the current packet being ACKed.
vecIterator = find_if(ackedPackets.begin(), ackedPackets.end(), bind2nd(equalSeqNum(), seqNum));
// Avoid dividing by zero.
if(ackTimeDiff != 0)
{
// We lost the record of the size of this packet due to libpcap
// loss, use the length echoed back in the ACK.
if(packetHistory->isAckFake() == true)
avgThroughput += 8000000.0*( static_cast (echoedPacketSize )) / ( static_cast(ackTimeDiff)*1024.0 );
else
avgThroughput += 8000000.0*( static_cast ((*vecIterator).packetSize )) / ( static_cast(ackTimeDiff)*1024.0 );
}
throughputKbps = avgThroughput / (static_cast (numThroughputAcks) );
unsigned long long timeStamp = packet->packetTime.toMicroseconds();
// Send a message to the monitor with the new bandwidth.
if(packetHistory->getPacketLoss() == 0)
{
// Send this available bandwidth as a tentative value.
// To be used for dummynet events only if it is greater
// than the last seen value.
logWrite(SENSOR, "VALUE::Tentative bandwidth for seqNum = %d , value = %f, acktimeDiff = %llu,packet size = %u",seqNum, throughputKbps, ackTimeDiff, (*vecIterator).packetSize);
//CHANGE:
logWrite(SENSOR, "TPUT:TIME=%llu,TENTATIVE=%f",timeStamp,throughputKbps);
//logWrite(SENSOR, "LOSS:TIME=%llu,LOSS=0", timeStamp);
}
else
{
// Send this as the authoritative available bandwidth value.
logWrite(SENSOR,"VALUE::Authoritative bandwidth for seqNum = %d , value = %f ,ackTimeDiff = %llu ",seqNum,throughputKbps,ackTimeDiff);
logWrite(SENSOR, "TPUT:TIME=%llu,AUTHORITATIVE=%f",timeStamp,throughputKbps);
//logWrite(SENSOR,"LOSS:TIME=%llu,LOSS=%d",timeStamp,packetHistory->getPacketLoss());
}
// Save the receiver timestamp of this ACK packet, so that we can
// use for calculating throughput for the next ACK packet.
lastAckTime = currentAckTimeStamp;
}