//=========================================================================== // @(#) $Name: arts++-1-1-a12 $ // @(#) $Id: ArtsIpPathData.cc,v 1.9 2004/05/26 20:02:38 youngh Exp $ //=========================================================================== // Copyright Notice // // By accessing this software, arts++, you are duly informed // of and agree to be bound by the conditions described below in this // notice: // // This software product, arts++, is developed by Daniel W. McRobb, and // copyrighted(C) 1998 by the University of California, San Diego // (UCSD), with all rights reserved. UCSD administers the CAIDA grant, // NCR-9711092, under which part of this code was developed. // // There is no charge for arts++ software. You can redistribute it // and/or modify it under the terms of the GNU Lesser General Public // License, Version 2.1, February 1999, which is incorporated by // reference herein. // // arts++ is distributed WITHOUT ANY WARRANTY, IMPLIED OR EXPRESS, OF // MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE or that the use // of it will not infringe on any third party's intellectual // property rights. // // You should have received a copy of the GNU Lesser General Public // License along with arts++. Copies can also be obtained from: // // http://www.gnu.org/copyleft/lesser.html // // or by writing to: // // Free Software Foundation, Inc. // 59 Temple Place, Suite 330 // Boston, MA 02111-1307 // USA // // Or contact: // // info@caida.org //=========================================================================== extern "C" { #include #include #include #include } #include #include #include "Arts.hh" #include "ArtsIpPathData.hh" using namespace std; static const std::string rcsid = "@(#) $Name: arts++-1-1-a12 $ $Id: ArtsIpPathData.cc,v 1.9 2004/05/26 20:02:38 youngh Exp $"; //------------------------------------------------------------------------ // ArtsIpPathData::ArtsIpPathData(ipv4addr_t src, ipv4addr_t dst) //------------------------------------------------------------------------ ArtsIpPathData::ArtsIpPathData(ipv4addr_t src, ipv4addr_t dst) { this->Clear(); this->_src = src; this->_dst = dst; #ifndef NDEBUG ++this->_numObjects; #endif } //------------------------------------------------------------------------- // ArtsIpPathData::ArtsIpPathData(const ArtsIpPathData & artsIpPathData) //......................................................................... // //------------------------------------------------------------------------- ArtsIpPathData::ArtsIpPathData(const ArtsIpPathData & artsIpPathData) { _src = artsIpPathData._src; _dst = artsIpPathData._dst; _listId = artsIpPathData._listId; _cycleId = artsIpPathData._cycleId; _rtt = artsIpPathData._rtt; _hopDistance = artsIpPathData._hopDistance; _destinationReplied = artsIpPathData._destinationReplied; _replyTtl = artsIpPathData._replyTtl; _numHops = artsIpPathData._numHops; _haltReason = artsIpPathData._haltReason; _haltReasonData = artsIpPathData._haltReasonData; _path = artsIpPathData._path; #ifndef NDEBUG ++_numObjects; #endif } //------------------------------------------------------------------------ // //------------------------------------------------------------------------ ArtsIpPathData::ArtsIpPathData() { this->Clear(); #ifndef NDEBUG ++this->_numObjects; #endif } //---------------------------------------------------------------------------- // uint8_t ArtsIpPathData::LoopLength() const //............................................................................ // //---------------------------------------------------------------------------- uint8_t ArtsIpPathData::LoopLength() const { return (_haltReason == k_loopDetected) ? _haltReasonData : 0; } //---------------------------------------------------------------------------- // uint8_t ArtsIpPathData::LoopLength(uint8_t loopLength) //............................................................................ // //---------------------------------------------------------------------------- uint8_t ArtsIpPathData::LoopLength(uint8_t loopLength) { _haltReason = k_loopDetected; _haltReasonData = loopLength; return _haltReasonData; } //---------------------------------------------------------------------------- // uint8_t ArtsIpPathData::GapLimit() const //............................................................................ // //---------------------------------------------------------------------------- uint8_t ArtsIpPathData::GapLimit() const { return (_haltReason == k_gapLimitExceeded) ? _haltReasonData : 0; } //---------------------------------------------------------------------------- // uint8_t ArtsIpPathData::GapLimit(uint8_t gapLimit) //............................................................................ // //---------------------------------------------------------------------------- uint8_t ArtsIpPathData::GapLimit(uint8_t gapLimit) { _haltReason = k_gapLimitExceeded; _haltReasonData = gapLimit; return _haltReasonData; } uint8_t ArtsIpPathData::IcmpCode() const { return (_haltReason == k_icmpUnreachable) ? _haltReasonData : 0; } uint8_t ArtsIpPathData::IcmpCode(uint8_t icmpCode) { _haltReason = k_icmpUnreachable; _haltReasonData = icmpCode; return _haltReasonData; } //---------------------------------------------------------------------------- // void ArtsIpPathData::Clear() //............................................................................ // //---------------------------------------------------------------------------- void ArtsIpPathData::Clear() { _src = 0; _dst = 0; _listId = 0; _cycleId = 0; _hopDistance = 0; _destinationReplied = 0; _numHops = 0; _haltReason = k_noHalt; _haltReasonData = 0; _replyTtl = 0; return; } //------------------------------------------------------------------------ // ArtsIpPathData::~ArtsIpPathData() //------------------------------------------------------------------------ ArtsIpPathData::~ArtsIpPathData() { #ifndef NDEBUG --this->_numObjects; #endif } uint32_t ArtsIpPathData::Length(uint8_t version, uint8_t flags) const { uint32_t len = sizeof(_src) + sizeof(_dst) + sizeof(_hopDistance) + sizeof(_numHops); if (version >= 3) { len += sizeof(_listId) + sizeof(_cycleId); } if (version >= 2) { len += sizeof(_rtt); } else { len += 2*sizeof(uint32_t); } if (version >= 1) { // Only version 1 has conditional reason codes. if (version != 1 || _destinationReplied) { len += sizeof(_haltReason) + sizeof(_haltReasonData); } } if (version >= 2) { len += sizeof(_replyTtl); } vector::const_iterator pathEntry; for (pathEntry = this->Path().begin(); pathEntry != this->Path().end(); pathEntry++) { len += pathEntry->Length(version, flags); } return(len); } ostream& ArtsIpPathData::write(ostream& os, uint8_t version, uint8_t flags) { uint32_t timeDatum; uint8_t repliedAndNumHops; os.write((char*)&this->_src,sizeof(this->_src)); os.write((char*)&this->_dst,sizeof(this->_dst)); if (version >= 3) { uint32_t id = htonl(_listId); os.write((char*)&id,sizeof(id)); id = htonl(_cycleId); os.write((char*)&id,sizeof(id)); } if (version >= 2) { timeDatum = htonl(_rtt); } else { timeDatum = htonl(_rtt / 1000000); os.write((char*)&timeDatum,sizeof(timeDatum)); timeDatum = htonl(_rtt % 1000000); } os.write((char*)&timeDatum,sizeof(timeDatum)); os.write((char*)&this->_hopDistance,sizeof(this->_hopDistance)); repliedAndNumHops = (this->_destinationReplied << 7) | this->_numHops; os.write((char*)&repliedAndNumHops,sizeof(repliedAndNumHops)); if (version >= 1) { // Only version 1 has conditional reason codes. if (version != 1 || _destinationReplied) { os.write((char*)&_haltReason, sizeof(_haltReason)); os.write((char*)&_haltReasonData, sizeof(_haltReasonData)); } } if (version >= 2) { os.write((char*)&_replyTtl, sizeof(_replyTtl)); } // sort by hop number sort(this->_path.begin(),this->_path.end(),less()); // write each hop vector::const_iterator pathEntry; for (pathEntry = this->_path.begin(); pathEntry != this->_path.end(); pathEntry++) { pathEntry->write(os,version, flags); } return(os); } int ArtsIpPathData::write(int fd, uint8_t version, uint8_t flags) { uint32_t timeDatum; uint8_t repliedAndNumHops; int rc; int bytesWritten = 0; rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&this->_src,sizeof(this->_src)); if (rc != sizeof(this->_src)) { return(-1); } bytesWritten += rc; rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&this->_dst,sizeof(this->_dst)); if (rc != sizeof(this->_dst)) { return(-1); } bytesWritten += rc; if (version >= 3) { uint32_t id = htonl(_listId); rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&id,sizeof(id)); if (rc != sizeof(id)) { return(-1); } bytesWritten += rc; id = htonl(_cycleId); rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&id,sizeof(id)); if (rc != sizeof(id)) { return(-1); } bytesWritten += rc; } if (version >= 2) { timeDatum = htonl(_rtt); } else { timeDatum = htonl(_rtt / 1000000); rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&timeDatum,sizeof(timeDatum)); if (rc != sizeof(timeDatum)) { return(-1); } bytesWritten += rc; timeDatum = htonl(_rtt % 1000000); } rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&timeDatum,sizeof(timeDatum)); if (rc != sizeof(timeDatum)) { return(-1); } bytesWritten += rc; rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&this->_hopDistance, sizeof(this->_hopDistance)); if (rc != sizeof(this->_hopDistance)) { return(-1); } bytesWritten += rc; repliedAndNumHops = (this->_destinationReplied << 7) | this->_numHops; rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&repliedAndNumHops, sizeof(repliedAndNumHops)); if (rc != sizeof(repliedAndNumHops)) { return(-1); } bytesWritten += rc; if (version >= 1) { // Only version 1 has conditional reason codes. if (version != 1 || _destinationReplied) { rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&_haltReason, sizeof(_haltReason)); if (rc != sizeof(_haltReason)) return(-1); bytesWritten += rc; rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&_haltReasonData, sizeof(_haltReasonData)); if (rc != sizeof(_haltReasonData)) return(-1); bytesWritten += rc; } } if (version >= 2) { rc = g_ArtsLibInternal_Primitive.FdWrite(fd,&_replyTtl, sizeof(_replyTtl)); if (rc != sizeof(_replyTtl)) return(-1); bytesWritten += rc; } // sort by hop number sort(this->_path.begin(),this->_path.end(),less()); // write each hop vector::const_iterator pathEntry; for (pathEntry = this->_path.begin(); pathEntry != this->_path.end(); pathEntry++) { if ((rc = pathEntry->write(fd,version, flags)) < 0) { return(-1); } bytesWritten += rc; } return(bytesWritten); } istream& ArtsIpPathData::read(istream& is, uint8_t version, uint8_t flags) { uint32_t timeDatum; uint8_t repliedAndNumHops; size_t hopNum; is.read((char*)&this->_src,sizeof(this->_src)); is.read((char*)&this->_dst,sizeof(this->_dst)); if (version >= 3) { uint32_t id; is.read((char*)&id,sizeof(id)); _listId = ntohl(id); is.read((char*)&id,sizeof(id)); _cycleId = ntohl(id); } is.read((char*)&timeDatum,sizeof(timeDatum)); if (version >= 2) { _rtt = ntohl(timeDatum); } else { _rtt = ntohl(timeDatum) * 1000000; is.read((char*)&timeDatum,sizeof(timeDatum)); _rtt += ntohl(timeDatum); } is.read((char*)&this->_hopDistance,sizeof(this->_hopDistance)); is.read((char*)&repliedAndNumHops,sizeof(repliedAndNumHops)); this->_destinationReplied = repliedAndNumHops >> 7; this->_numHops = repliedAndNumHops & 0x7f; if (version >= 1) { // Only version 1 has conditional reason codes. if (version != 1 || _destinationReplied) { is.read((char*)&_haltReason, sizeof(_haltReason)); is.read((char*)&_haltReasonData, sizeof(_haltReasonData)); } } if (version >= 2) { is.read((char*)&_replyTtl, sizeof(_replyTtl)); } if (this->_path.size() > 0) { this->_path.erase(this->_path.begin(),this->_path.end()); } this->_path.reserve(this->_numHops); // read in contents for each hop ArtsIpPathEntry pathEntry; for (hopNum = 0; hopNum < this->_numHops; hopNum++) { pathEntry.read(is,version, flags); this->_path.push_back(pathEntry); } assert(_numHops == _path.size()); return(is); } int ArtsIpPathData::read(int fd, uint8_t version, uint8_t flags) { uint32_t timeDatum; uint8_t repliedAndNumHops; size_t hopNum; int rc; int bytesRead = 0; rc = g_ArtsLibInternal_Primitive.FdRead(fd,&this->_src,sizeof(this->_src)); if (rc <= 0) return(rc); bytesRead += rc; rc = g_ArtsLibInternal_Primitive.FdRead(fd,&this->_dst,sizeof(this->_dst)); if (rc <= 0) return(rc); bytesRead += rc; if (version >= 3) { uint32_t id; rc = g_ArtsLibInternal_Primitive.FdRead(fd,&id,sizeof(id)); if (rc <= 0) return(rc); bytesRead += rc; _listId = ntohl(id); rc = g_ArtsLibInternal_Primitive.FdRead(fd,&id,sizeof(id)); if (rc <= 0) return(rc); bytesRead += rc; _cycleId = ntohl(id); } rc = g_ArtsLibInternal_Primitive.FdRead(fd,&timeDatum,sizeof(timeDatum)); if (rc <= 0) return(rc); bytesRead += rc; if (version >= 2) { _rtt = ntohl(timeDatum); } else { _rtt = ntohl(timeDatum) * 1000000; rc = g_ArtsLibInternal_Primitive.FdRead(fd,&timeDatum,sizeof(timeDatum)); if (rc <= 0) return(rc); bytesRead += rc; _rtt += ntohl(timeDatum); } rc = g_ArtsLibInternal_Primitive.FdRead(fd,&this->_hopDistance, sizeof(this->_hopDistance)); if (rc <= 0) return(rc); bytesRead += rc; rc = g_ArtsLibInternal_Primitive.FdRead(fd,&repliedAndNumHops, sizeof(repliedAndNumHops)); if (rc <= 0) return(rc); bytesRead += rc; this->_destinationReplied = repliedAndNumHops >> 7; this->_numHops = repliedAndNumHops & 0x7f; if (version >= 1) { // Only version 1 has conditional reason codes. if (version != 1 || _destinationReplied) { rc = g_ArtsLibInternal_Primitive.FdRead(fd,&_haltReason, sizeof(_haltReason)); if (rc <= 0) return(rc); bytesRead += rc; rc = g_ArtsLibInternal_Primitive.FdRead(fd,&_haltReasonData, sizeof(_haltReasonData)); if (rc <= 0) return(rc); bytesRead += rc; } } if (version >= 2) { rc = g_ArtsLibInternal_Primitive.FdRead(fd,&_replyTtl, sizeof(_replyTtl)); if (rc <= 0) return(rc); bytesRead += rc; } if (this->_path.size() > 0) { this->_path.erase(this->_path.begin(),this->_path.end()); } this->_path.reserve(this->_numHops); // read in contents for each hop ArtsIpPathEntry pathEntry; for (hopNum = 0; hopNum < this->_numHops; hopNum++) { rc = pathEntry.read(fd,version, flags); if (rc <= 0) return(rc); bytesRead += rc; this->_path.push_back(pathEntry); } assert(_numHops == _path.size()); return(bytesRead); } //------------------------------------------------------------------------- // bool // ArtsIpPathData:: // Distinguishable(const vector & ipPath) const //......................................................................... // //------------------------------------------------------------------------- bool ArtsIpPathData::Distinguishable(const vector & ipPath) const { vector::const_iterator thisHopIter; vector::const_iterator inHopIter; for (thisHopIter = (*this)._path.begin(); thisHopIter != (*this)._path.end(); thisHopIter++) { for (inHopIter = ipPath.begin(); inHopIter != ipPath.end(); inHopIter++) { if ((*thisHopIter).HopNum() == (*inHopIter).HopNum()) { if ((*thisHopIter).IpAddr() != (*inHopIter).IpAddr()) { return(true); } } } } return(false); } //------------------------------------------------------------------------- // bool // ArtsIpPathData::Distinguishable(const ArtsIpPathData & ipPathData) const //......................................................................... // //------------------------------------------------------------------------- bool ArtsIpPathData::Distinguishable(const ArtsIpPathData & ipPathData) const { if (this->_src != ipPathData.Src() || this->_dst != ipPathData.Dst()) { // different source or destination addresses return(true); } if (_destinationReplied && ipPathData.DestinationReplied()) { // both paths claim to be complete; compare the hop distance if (this->_hopDistance != ipPathData.HopDistance()) { return(true); } } if (this->Distinguishable(ipPathData.Path())) { // the hop vectors are distinguishable return(true); } return(false); } //------------------------------------------------------------------------- // vector * ArtsIpPathData::HopAddresses() const //......................................................................... // //------------------------------------------------------------------------- vector * ArtsIpPathData::HopAddresses() const { vector *newAddrVect = new vector; vector::const_iterator thisHopIter; for (thisHopIter = (*this)._path.begin(); thisHopIter != (*this)._path.end(); thisHopIter++) { (*newAddrVect).push_back((*thisHopIter).IpAddr()); } return(newAddrVect); } //------------------------------------------------------------------------- // bool // ArtsIpPathData:: // CommonHopAddresses(const vector & ipPath, // vector & commonHopAddrs) const //......................................................................... // //------------------------------------------------------------------------- bool ArtsIpPathData::CommonHopAddresses(const vector & ipPath, vector & commonHopAddrs) const { vector::const_iterator thisHopIter; vector::const_iterator inHopIter; bool rc = false; for (thisHopIter = (*this)._path.begin(); thisHopIter != (*this)._path.end(); thisHopIter++) { for (inHopIter = ipPath.begin(); inHopIter != ipPath.end(); inHopIter++) { if ((*thisHopIter).IpAddr() == (*inHopIter).IpAddr()) { rc = true; if (find(commonHopAddrs.begin(),commonHopAddrs.end(), (*thisHopIter).IpAddr()) == commonHopAddrs.end()) { commonHopAddrs.push_back((*thisHopIter).IpAddr()); } } } } return(rc); } //------------------------------------------------------------------------- // bool // ArtsIpPathData:: // CommonHopAddresses(const vector & hopAddresses, // vector & commonHopAddresses) const //......................................................................... // //------------------------------------------------------------------------- bool ArtsIpPathData:: CommonHopAddresses(const vector & hopAddresses, vector & commonHopAddresses) const { vector::const_iterator thisHopIter; bool rc; if (commonHopAddresses.size() > 0) { commonHopAddresses.erase(commonHopAddresses.begin(), commonHopAddresses.end()); } for (thisHopIter = (*this)._path.begin(); thisHopIter != (*this)._path.end(); thisHopIter++) { if (find(hopAddresses.begin(),hopAddresses.end(),(*thisHopIter).IpAddr()) != hopAddresses.end()) { rc = true; if (find(commonHopAddresses.begin(),commonHopAddresses.end(), (*thisHopIter).IpAddr()) == commonHopAddresses.end()) { commonHopAddresses.push_back((*thisHopIter).IpAddr()); } } } return(rc); } //------------------------------------------------------------------------- // void // ArtsIpPathData::MergeWithPath(const vector & ipPath, // vector & mergedPath) const //......................................................................... // //------------------------------------------------------------------------- void ArtsIpPathData::MergeWithPath(const vector & ipPath, vector & mergedPath) const { vector::iterator unionIterBegin, unionIterEnd; ArtsIpPathEntryLessByHopNumber hopsLess; mergedPath.reserve((*this)._path.size() + ipPath.size()); unionIterBegin = mergedPath.begin(); unionIterEnd = set_union((*this)._path.begin(),(*this)._path.end(), ipPath.begin(),ipPath.end(), unionIterBegin,hopsLess); mergedPath.insert(mergedPath.begin(),unionIterBegin,unionIterEnd); return; } //------------------------------------------------------------------------- // ArtsIpPathData & // ArtsIpPathData::operator = (const ArtsIpPathData & artsIpPathData) //......................................................................... // //------------------------------------------------------------------------- ArtsIpPathData & ArtsIpPathData::operator = (const ArtsIpPathData & artsIpPathData) { this->_src = artsIpPathData._src; this->_dst = artsIpPathData._dst; this->_listId = artsIpPathData._listId; this->_cycleId = artsIpPathData._cycleId; this->_rtt = artsIpPathData._rtt; this->_hopDistance = artsIpPathData._hopDistance; this->_destinationReplied = artsIpPathData._destinationReplied; this->_numHops = artsIpPathData._numHops; this->_haltReason = artsIpPathData._haltReason; this->_haltReasonData = artsIpPathData._haltReasonData; this->_path = artsIpPathData._path; return(*this); } //------------------------------------------------------------------------ // //------------------------------------------------------------------------ ostream& operator << (ostream& os, const ArtsIpPathData & artsIpPathData) { struct in_addr inAddr; os << "IPPATH OBJECT DATA" << endl; inAddr.s_addr = artsIpPathData.Src(); os << "\tSrc: " << setiosflags(ios::left) << setw(16) << inet_ntoa(inAddr) << setiosflags(ios::showbase) << " (" << setw(8) << hex << ntohl(artsIpPathData.Src()) << ")" << endl; inAddr.s_addr = artsIpPathData.Dst(); os << "\tDst: " << setiosflags(ios::left) << setw(16) << inet_ntoa(inAddr) << " (" << setiosflags(ios::showbase) << setw(8) << hex << ntohl(artsIpPathData.Dst()) << ")" << dec << endl; os << "\tListId: " << artsIpPathData.ListId() << " (" << hex << artsIpPathData.ListId() << ")" << dec << endl; // Timestamp printing code copied from ArtsAttribute.cc::operator<<() // We should probably print in GMT, but convention seems to prefer local. time_t timestamp = artsIpPathData.CycleId(); struct tm *Tm = localtime(×tamp); os << setiosflags(ios::internal); os << "\tCycleID: " << setfill('0') << setw(2) << Tm->tm_mon+1 << "/" << setw(2) << Tm->tm_mday << "/" << setw(4) << Tm->tm_year+1900 << " " << setw(2) << Tm->tm_hour << ":" << setw(2) << Tm->tm_min << ":" << setw(2) << Tm->tm_sec << " (" << hex << timestamp << ")" << dec << endl; os << setfill(' '); os << "\tRtt: " << (float)((artsIpPathData.Rtt().tv_sec * 1000.0 + artsIpPathData.Rtt().tv_usec)/1000.0) << " ms" << endl; os << "\tHopDistance: " << dec << (int)artsIpPathData.HopDistance() << " (" << hex << (int)artsIpPathData.HopDistance() << ")" << endl; os << "\tDestinationReplied: "; if (artsIpPathData.DestinationReplied()) os << "true\n"; else { os << "false\n"; } uint8_t reason = artsIpPathData.HaltReason(); if (reason != ArtsIpPathData::k_noHalt) { os << "\tHaltReason: "; if (reason == ArtsIpPathData::k_icmpUnreachable) { os << "ICMP unreachable, code: " << dec << (unsigned int)artsIpPathData.IcmpCode() << hex << endl; } else if (reason == ArtsIpPathData::k_loopDetected) { os << "loop detected, loop length: " << dec << (unsigned int)artsIpPathData.LoopLength() << hex << endl; } else if (reason == ArtsIpPathData::k_gapLimitExceeded) { os << "gap limit reached: " << dec << (unsigned int)artsIpPathData.GapLimit() << hex << endl; } } if (artsIpPathData.ReplyTtl()) { os << "\tReply TTL: " << dec << (unsigned int)artsIpPathData.ReplyTtl() << hex << endl; } os << endl; os << "\tNumHops: " << dec << (int)artsIpPathData.NumHops() << " (" << hex << (int)artsIpPathData.NumHops() << ")" << endl; assert(artsIpPathData.Path().size() == artsIpPathData.NumHops()); vector::const_iterator pathEntry; for (pathEntry = artsIpPathData.Path().begin(); pathEntry != artsIpPathData.Path().end(); pathEntry++) { os << (*pathEntry); } return(os); } //AL: //---------------------------------------------------------------------------- // void ArtsIpPathData::AddHop(ipv4addr_t ipAddr, uint8_t hopNum, // const struct timeval & rtt, uint8_t numTries) //............................................................................ // //---------------------------------------------------------------------------- void ArtsIpPathData::AddHop(ipv4addr_t ipAddr, uint8_t hopNum, const struct timeval & rtt, uint8_t numTries) { ArtsIpPathEntry pathEntry(ipAddr,hopNum); pathEntry.Rtt(rtt); pathEntry.NumTries(numTries); this->_path.push_back(pathEntry); this->NumHops(this->Path().size()); } uint32_t ArtsIpPathData::_numObjects = 0;