INET Framework for OMNeT++/OMNEST
PingApp.cc
Go to the documentation of this file.
00001 //
00002 // Copyright (C) 2001, 2003, 2004 Johnny Lai, Monash University, Melbourne, Australia
00003 // Copyright (C) 2005 Andras Varga
00004 //
00005 // This program is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU Lesser General Public License
00007 // as published by the Free Software Foundation; either version 2
00008 // of the License, or (at your option) any later version.
00009 //
00010 // This program is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 // GNU Lesser General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU Lesser General Public License
00016 // along with this program; if not, see <http://www.gnu.org/licenses/>.
00017 //
00018 
00019 #include <limits.h>
00020 #include <stdlib.h>
00021 #include <iostream>
00022 
00023 #include "IPAddressResolver.h"
00024 #include "PingApp.h"
00025 #include "PingPayload_m.h"
00026 #include "IPControlInfo.h"
00027 #include "IPv6ControlInfo.h"
00028 
00029 using std::cout;
00030 
00031 Define_Module(PingApp);
00032 
00033 void PingApp::initialize()
00034 {
00035     // read params
00036     // (defer reading srcAddr/destAddr to when ping starts, maybe
00037     // addresses will be assigned later by some protocol)
00038     packetSize = par("packetSize");
00039     intervalp = & par("interval");
00040     hopLimit = par("hopLimit");
00041     count = par("count");
00042     startTime = par("startTime");
00043     stopTime = par("stopTime");
00044     printPing = (bool)par("printPing");
00045 
00046     // state
00047     sendSeqNo = expectedReplySeqNo = 0;
00048     WATCH(sendSeqNo);
00049     WATCH(expectedReplySeqNo);
00050 
00051     // statistics
00052     delayStat.setName("pingRTT");
00053     delayVector.setName("pingRTT");
00054     dropVector.setName("pingDrop");
00055 
00056     dropCount = outOfOrderArrivalCount = numPongs = 0;
00057     WATCH(dropCount);
00058     WATCH(outOfOrderArrivalCount);
00059     WATCH(numPongs);
00060 
00061     // schedule first ping (use empty destAddr or stopTime<=startTime to disable)
00062     if (par("destAddr").stringValue()[0] && (stopTime==0 || stopTime>=startTime))
00063     {
00064         cMessage *msg = new cMessage("sendPing");
00065         scheduleAt(startTime, msg);
00066     }
00067 }
00068 
00069 void PingApp::handleMessage(cMessage *msg)
00070 {
00071     if (msg->isSelfMessage())
00072     {
00073         // on first call we need to initialize
00074         if (destAddr.isUnspecified())
00075         {
00076             destAddr = IPAddressResolver().resolve(par("destAddr"));
00077             ASSERT(!destAddr.isUnspecified());
00078             srcAddr = IPAddressResolver().resolve(par("srcAddr"));
00079             EV << "Starting up: dest=" << destAddr << "  src=" << srcAddr << "\n";
00080         }
00081 
00082         // send a ping
00083         sendPing();
00084 
00085         // then schedule next one if needed
00086         scheduleNextPing(msg);
00087     }
00088     else
00089     {
00090         // process ping response
00091         processPingResponse(check_and_cast<PingPayload *>(msg));
00092     }
00093 }
00094 
00095 void PingApp::sendPing()
00096 {
00097     EV << "Sending ping #" << sendSeqNo << "\n";
00098 
00099     char name[32];
00100     sprintf(name,"ping%ld", sendSeqNo);
00101 
00102     PingPayload *msg = new PingPayload(name);
00103     msg->setOriginatorId(getId());
00104     msg->setSeqNo(sendSeqNo);
00105     msg->setByteLength(packetSize);
00106 
00107     sendToICMP(msg, destAddr, srcAddr, hopLimit);
00108 }
00109 
00110 void PingApp::scheduleNextPing(cMessage *timer)
00111 {
00112     simtime_t nextPing = simTime() + intervalp->doubleValue();
00113     sendSeqNo++;
00114     if ((count==0 || sendSeqNo<count) && (stopTime==0 || nextPing<stopTime))
00115         scheduleAt(nextPing, timer);
00116     else
00117         delete timer;
00118 }
00119 
00120 void PingApp::sendToICMP(cMessage *msg, const IPvXAddress& destAddr, const IPvXAddress& srcAddr, int hopLimit)
00121 {
00122     if (!destAddr.isIPv6())
00123     {
00124         // send to IPv4
00125         IPControlInfo *ctrl = new IPControlInfo();
00126         ctrl->setSrcAddr(srcAddr.get4());
00127         ctrl->setDestAddr(destAddr.get4());
00128         ctrl->setTimeToLive(hopLimit);
00129         msg->setControlInfo(ctrl);
00130         send(msg, "pingOut");
00131     }
00132     else
00133     {
00134         // send to IPv6
00135         IPv6ControlInfo *ctrl = new IPv6ControlInfo();
00136         ctrl->setSrcAddr(srcAddr.get6());
00137         ctrl->setDestAddr(destAddr.get6());
00138         ctrl->setHopLimit(hopLimit);
00139         msg->setControlInfo(ctrl);
00140         send(msg, "pingv6Out");
00141     }
00142 }
00143 
00144 void PingApp::processPingResponse(PingPayload *msg)
00145 {
00146     // get src, hopCount etc from packet, and print them
00147     IPvXAddress src, dest;
00148     int msgHopCount = -1;
00149     if (dynamic_cast<IPControlInfo *>(msg->getControlInfo())!=NULL)
00150     {
00151         IPControlInfo *ctrl = (IPControlInfo *)msg->getControlInfo();
00152         src = ctrl->getSrcAddr();
00153         dest = ctrl->getDestAddr();
00154         msgHopCount = ctrl->getTimeToLive();
00155     }
00156     else if (dynamic_cast<IPv6ControlInfo *>(msg->getControlInfo())!=NULL)
00157     {
00158         IPv6ControlInfo *ctrl = (IPv6ControlInfo *)msg->getControlInfo();
00159         src = ctrl->getSrcAddr();
00160         dest = ctrl->getDestAddr();
00161         msgHopCount = ctrl->getHopLimit();
00162     }
00163 
00164     simtime_t rtt = simTime() - msg->getCreationTime();
00165 
00166     if (printPing)
00167     {
00168         cout << getFullPath() << ": reply of " << std::dec << msg->getByteLength()
00169              << " bytes from " << src
00170              << " icmp_seq=" << msg->getSeqNo() << " ttl=" << msgHopCount
00171              << " time=" << (rtt * 1000) << " msec"
00172              << " (" << msg->getName() << ")" << endl;
00173     }
00174 
00175     // update statistics
00176     countPingResponse(msg->getByteLength(), msg->getSeqNo(), rtt);
00177     delete msg;
00178 }
00179 
00180 void PingApp::countPingResponse(int bytes, long seqNo, simtime_t rtt)
00181 {
00182     EV << "Ping reply #" << seqNo << " arrived, rtt=" << rtt << "\n";
00183 
00184         numPongs++;
00185 
00186     delayStat.collect(rtt);
00187     delayVector.record(rtt);
00188 
00189     if (seqNo == expectedReplySeqNo)
00190     {
00191         // expected ping reply arrived; expect next sequence number
00192         expectedReplySeqNo++;
00193     }
00194     else if (seqNo > expectedReplySeqNo)
00195     {
00196         EV << "Jump in seq numbers, assuming pings since #" << expectedReplySeqNo << " got lost\n";
00197 
00198         // jump in the sequence: count pings in gap as lost
00199         long jump = seqNo - expectedReplySeqNo;
00200         dropCount += jump;
00201         dropVector.record(dropCount);
00202 
00203         // expect sequence numbers to continue from here
00204         expectedReplySeqNo = seqNo+1;
00205     }
00206     else // seqNo < expectedReplySeqNo
00207     {
00208         // ping arrived too late: count as out of order arrival
00209         EV << "Arrived out of order (too late)\n";
00210         outOfOrderArrivalCount++;
00211     }
00212 }
00213 
00214 void PingApp::finish()
00215 {
00216     if (sendSeqNo==0)
00217     {
00218         if (printPing)
00219                 EV << getFullPath() << ": No pings sent, skipping recording statistics and printing results.\n";
00220         recordScalar("Pings sent", sendSeqNo);
00221         return;
00222     }
00223 
00224     // record statistics
00225     recordScalar("Pings sent", sendSeqNo);
00226     recordScalar("Pings dropped", dropCount);
00227     recordScalar("Out-of-order ping arrivals", outOfOrderArrivalCount);
00228     recordScalar("Pings outstanding at end", sendSeqNo-expectedReplySeqNo);
00229 
00230     recordScalar("Ping drop rate (%)", 100 * dropCount / (double)sendSeqNo);
00231     recordScalar("Ping out-of-order rate (%)", 100 * outOfOrderArrivalCount / (double)sendSeqNo);
00232 
00233     delayStat.recordAs("Ping roundtrip delays");
00234 
00235     // print it to stdout as well
00236     if (printPing)
00237     {
00238         cout << "--------------------------------------------------------" << endl;
00239         cout << "\t" << getFullPath() << endl;
00240         cout << "--------------------------------------------------------" << endl;
00241 
00242         cout << "sent: " << sendSeqNo
00243                         << "   drop rate (%): " << (100 * dropCount / (double)sendSeqNo) << endl;
00244         cout << "round-trip min/avg/max (ms): "
00245                         << (delayStat.getMin()*1000.0) << "/"
00246                         << (delayStat.getMean()*1000.0) << "/"
00247                         << (delayStat.getMax()*1000.0) << endl;
00248         cout << "stddev (ms): "<< (delayStat.getStddev()*1000.0)
00249                          << "   variance:" << delayStat.getVariance() << endl;
00250         cout <<"--------------------------------------------------------" << endl;
00251     }
00252 }
00253 
00254