RetroShare/libretroshare/src/unused/p3dsdv.cc
Gioacchino Mazzurco 329050a9c2
Use safer rstime_t instead of time_t
Avoid problems to serialization on different platforms, without breaking
nested STL containers serialization.

The conversion have been made with sed, and checked with grep, plus
kdiff3 visual ispection, plus rutime tests, so it should be fine.
2018-10-07 13:07:06 +02:00

911 lines
21 KiB
C++

/*******************************************************************************
* libretroshare/src/unused: p3dsdv.cc *
* *
* libretroshare: retroshare core library *
* *
* Copyright 2011 by Robert Fernie <retroshare@lunamutt.com> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program 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 Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#include <list>
#include <string>
#include "util/rstime.h"
//#include "serialiser/rsdsdvitems.h"
#include "services/p3dsdv.h"
#include "pqi/p3linkmgr.h"
#include "util/rsrandom.h"
#include <openssl/sha.h>
/****
* #define DEBUG_DSDV 1
****/
#define DEBUG_DSDV 1
/* DEFINE INTERFACE POINTER! */
RsDsdv *rsDsdv = NULL;
/*****
* Routing Services are provided for any service or peer that wants to access / receive info
* over the internal network.
*
* This is going to be based loosely on Ben's Algorithm...
* Each Service / Peer is identified by a HASH.
* This HASH is disguised by a temporary ANON-CHUNK.
* DSDVID = Sha1(ANONCHUNK + HASH).
*
* Each peer can advertise as many Services as they want.
* The Anon-chunk should be rotated regularly to hide it well.
* period to be defined.
*
*
* Once this Routing table has been established, Routes can be created - in a similar manner to turtle paths.
* (Send Path request (Path Id + Origin + Destination))... path is established.
*
* Then we can do Onion Routing etc, on top of this.
*
****/
p3Dsdv::p3Dsdv(p3ServiceControl *sc)
:p3Service(), /* p3Config(CONFIG_TYPE_DSDV), */ mDsdvMtx("p3Dsdv"), mServiceCtrl(sc)
{
addSerialType(new RsDsdvSerialiser());
mSentTablesTime = 0;
mSentIncrementTime = 0;
}
const std::string DSDV_APP_NAME = "dsdv";
const uint16_t DSDV_APP_MAJOR_VERSION = 1;
const uint16_t DSDV_APP_MINOR_VERSION = 0;
const uint16_t DSDV_MIN_MAJOR_VERSION = 1;
const uint16_t DSDV_MIN_MINOR_VERSION = 0;
RsServiceInfo p3Dsdv::getServiceInfo()
{
return RsServiceInfo(RS_SERVICE_TYPE_DSDV,
DSDV_APP_NAME,
DSDV_APP_MAJOR_VERSION,
DSDV_APP_MINOR_VERSION,
DSDV_MIN_MAJOR_VERSION,
DSDV_MIN_MINOR_VERSION);
}
int p3Dsdv::tick()
{
processIncoming();
sendTables();
return 0;
}
int p3Dsdv::status()
{
return 1;
}
#define DSDV_BROADCAST_PERIOD (60*10) // 10 Minutes.
#define DSDV_MIN_INCREMENT_PERIOD 5
#define DSDV_DISCARD_PERIOD (DSDV_BROADCAST_PERIOD * 2)
int p3Dsdv::sendTables()
{
rstime_t now = time(NULL);
rstime_t tt, it;
bool updateRequired = false;
{
RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
tt = mSentTablesTime;
it = mSentIncrementTime;
updateRequired = mSignificantChanges;
}
if (now - tt > DSDV_BROADCAST_PERIOD)
{
#ifdef DEBUG_DSDV
std::cerr << "p3Dsdv::sendTables() Broadcast Time";
std::cerr << std::endl;
#endif
selectStableRoutes();
clearOldRoutes();
generateRoutingTables(false);
printDsdvTable(std::cerr);
RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
mSentTablesTime = now;
mSentIncrementTime = now;
return true ;
}
/* otherwise send incremental changes */
if ((updateRequired) && (now - it > DSDV_MIN_INCREMENT_PERIOD))
{
selectStableRoutes();
generateRoutingTables(true);
RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
mSentIncrementTime = now;
}
return true;
}
#define RSDSDV_SEQ_INCREMENT 2
void p3Dsdv::advanceLocalSequenceNumbers()
{
RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
rstime_t now = time(NULL);
std::map<std::string, RsDsdvTableEntry>::iterator it;
for(it = mTable.begin(); it != mTable.end(); ++it)
{
RsDsdvTableEntry &v = (it->second);
if (v.mOwnSource)
{
v.mStableRoute.mSequence += RSDSDV_SEQ_INCREMENT;
v.mStableRoute.mReceived = now;
}
}
}
void p3Dsdv::clearSignificantChangesFlags()
{
RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
std::map<std::string, RsDsdvTableEntry>::iterator it;
for(it = mTable.begin(); it != mTable.end(); ++it)
{
RsDsdvTableEntry &v = (it->second);
if (v.mFlags & RSDSDV_FLAGS_SIGNIFICANT_CHANGE)
{
v.mFlags &= ~RSDSDV_FLAGS_SIGNIFICANT_CHANGE;
}
}
mSignificantChanges = false;
}
int p3Dsdv::generateRoutingTables(bool incremental)
{
/* we ping our peers */
/* who is online? */
std::set<RsPeerId> idList;
mServiceCtrl->getPeersConnected(getServiceInfo().mServiceType, idList);
#ifdef DEBUG_DSDV
std::cerr << "p3Dsdv::generateRoutingTables(" << incremental << ")";
std::cerr << std::endl;
#endif
if (!incremental)
{
/* now clear significant flag */
advanceLocalSequenceNumbers();
}
/* prepare packets */
std::set<RsPeerId>::iterator it;
for(it = idList.begin(); it != idList.end(); ++it)
{
#ifdef DEBUG_DSDV
std::cerr << "p3Dsdv::generateRoutingTables() For: " << *it;
std::cerr << std::endl;
#endif
generateRoutingTable(*it, incremental);
}
/* now clear significant flag */
clearSignificantChangesFlags();
return 1;
}
int p3Dsdv::generateRoutingTable(const RsPeerId &peerId, bool incremental)
{
RsDsdvRouteItem *dsdv = new RsDsdvRouteItem();
dsdv->PeerId(peerId);
RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
std::map<std::string, RsDsdvTableEntry>::iterator it;
for(it = mTable.begin(); it != mTable.end(); ++it)
{
RsDsdvTableEntry &v = (it->second);
/* discard/ignore criterion */
if (!v.mIsStable)
{
continue;
}
if (v.mStableRoute.mDistance >= RSDSDV_MAX_DISTANCE)
{
continue;
}
if (incremental)
{
if (v.mFlags & RSDSDV_FLAGS_SIGNIFICANT_CHANGE)
{
// Done elsewhere.
//v.mFlags &= ~SIGNIFICANT_CHANGE;
}
else
{
/* ignore non-significant changes */
continue;
}
}
RsTlvDsdvEntry entry;
entry.endPoint.idType = v.mDest.mIdType;
entry.endPoint.anonChunk = v.mDest.mAnonChunk;
entry.endPoint.serviceId = v.mDest.mHash;
entry.sequence = v.mStableRoute.mSequence;
entry.distance = v.mStableRoute.mDistance;
//dsdv->routes.entries.push_back(entry);
dsdv->routes.mList.push_back(entry);
//if (dsdv->routes.entries.size() > RSDSDV_MAX_ROUTE_TABLE)
if (dsdv->routes.mList.size() > RSDSDV_MAX_ROUTE_TABLE)
{
sendItem(dsdv);
dsdv = new RsDsdvRouteItem();
dsdv->PeerId(peerId);
}
}
sendItem(dsdv);
return 1;
}
/****************************************************************************
****************************************************************************/
int p3Dsdv::processIncoming()
{
/* for each packet - pass to specific handler */
RsItem *item = NULL;
while(NULL != (item = recvItem()))
{
switch(item->PacketSubType())
{
default:
break;
case RS_PKT_SUBTYPE_DSDV_ROUTE:
{
handleDSDV((RsDsdvRouteItem *) item);
}
break;
}
/* clean up */
delete item;
}
return true ;
}
int p3Dsdv::handleDSDV(RsDsdvRouteItem *dsdv)
{
/* iterate over the entries */
RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
rstime_t now = time(NULL);
#ifdef DEBUG_DSDV
std::cerr << "p3Dsdv::handleDSDV() Received Pkt from: " << dsdv->PeerId();
std::cerr << std::endl;
//dsdv->print(std::cerr);
//std::cerr << std::endl;
#endif
std::list<RsTlvDsdvEntry>::iterator it;
//for(it = dsdv->routes.entries.begin(); it != dsdv->routes.entries.end(); ++it)
for(it = dsdv->routes.mList.begin(); it != dsdv->routes.mList.end(); ++it)
{
/* check for existing */
RsTlvDsdvEntry &entry = *it;
uint32_t realDistance = entry.distance + 1; // metric.
/* find the entry */
std::map<std::string, RsDsdvTableEntry>::iterator tit;
tit = mTable.find(entry.endPoint.serviceId);
if (tit == mTable.end())
{
#ifdef DEBUG_DSDV
std::cerr << "p3Dsdv::handleDSDV() Adding Entry for New ServiceId: ";
std::cerr << entry.endPoint.serviceId;
std::cerr << std::endl;
#endif
/* new entry! */
RsDsdvTableEntry v;
v.mDest.mIdType = entry.endPoint.idType;
v.mDest.mAnonChunk = entry.endPoint.anonChunk;
v.mDest.mHash = entry.endPoint.serviceId;
/* add as a possible route */
RsDsdvRoute newRoute;
newRoute.mNextHop = dsdv->PeerId();
newRoute.mReceived = now;
newRoute.mSequence = entry.sequence;
newRoute.mDistance = realDistance;
newRoute.mValidSince = now;
v.mAllRoutes[dsdv->PeerId()] = newRoute;
v.mIsStable = false;
v.mFlags = RSDSDV_FLAGS_NEW_ROUTE;
v.mOwnSource = false;
v.mMatched = false;
// store in table.
mTable[v.mDest.mHash] = v;
}
else
{
RsDsdvTableEntry &v = tit->second;
if (v.mOwnSource)
{
#ifdef DEBUG_DSDV
std::cerr << "p3Dsdv::handleDSDV() Ignoring OwnSource Entry:";
std::cerr << entry.endPoint.serviceId;
std::cerr << std::endl;
#endif
continue; // Ignore if we are source.
}
/* look for this in mAllRoutes */
std::map<RsPeerId, RsDsdvRoute>::iterator rit;
rit = v.mAllRoutes.find(dsdv->PeerId());
if (rit == v.mAllRoutes.end())
{
/* add a new entry in */
RsDsdvRoute newRoute;
newRoute.mNextHop = dsdv->PeerId();
newRoute.mReceived = now;
newRoute.mSequence = entry.sequence;
newRoute.mDistance = realDistance;
newRoute.mValidSince = now;
v.mAllRoutes[dsdv->PeerId()] = newRoute;
/* if we've just added it in - can't be stable one */
#ifdef DEBUG_DSDV
std::cerr << "p3Dsdv::handleDSDV() Adding NewRoute Entry:";
std::cerr << entry.endPoint.serviceId;
std::cerr << std::endl;
#endif
}
else
{
if (rit->second.mSequence >= entry.sequence)
{
#ifdef DEBUG_DSDV
std::cerr << "p3Dsdv::handleDSDV() Ignoring OLDSEQ Entry:";
std::cerr << entry.endPoint.serviceId;
std::cerr << std::endl;
#endif
/* ignore same/old sequence number??? */
continue;
}
#ifdef DEBUG_DSDV
std::cerr << "p3Dsdv::handleDSDV() Updating Entry:";
std::cerr << entry.endPoint.serviceId;
std::cerr << std::endl;
#endif
/* update seq,dist,etc */
if (rit->second.mSequence + 2 < entry.sequence)
{
/* skipped a sequence number - reset timer */
rit->second.mValidSince = now;
}
//rit->second.mNextHop; // unchanged.
rit->second.mReceived = now;
rit->second.mSequence = entry.sequence;
rit->second.mDistance = realDistance;
/* if consistent route... maintain */
if ((v.mIsStable) &&
(rit->second.mNextHop == v.mStableRoute.mNextHop))
{
v.mStableRoute = rit->second;
}
else
{
/* otherwise we need to wait - see if we get new update */
}
}
}
}
return 1;
}
int p3Dsdv::selectStableRoutes()
{
/* iterate over the entries */
RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
rstime_t now = time(NULL);
#ifdef DEBUG_DSDV
std::cerr << "p3Dsdv::selectStableRoutes()";
std::cerr << std::endl;
#endif
/* find the entry */
std::map<std::string, RsDsdvTableEntry>::iterator tit;
for(tit = mTable.begin(); tit != mTable.end(); ++tit)
{
#ifdef DEBUG_DSDV
std::cerr << "p3Dsdv::selectStableRoutes() For Entry: ";
std::cerr << tit->second;
std::cerr << std::endl;
#endif
if (tit->second.mOwnSource)
{
#ifdef DEBUG_DSDV
std::cerr << "p3Dsdv::selectStableRoutes() OwnSource... Ignoring";
std::cerr << std::endl;
#endif
continue; // Ignore if we are source.
}
std::map<RsPeerId, RsDsdvRoute>::iterator rit;
uint32_t newest = 0;
RsPeerId newestId;
uint32_t closest = RSDSDV_MAX_DISTANCE + 1;
RsPeerId closestId;
rstime_t closestAge = 0;
/* find newest sequence number */
for(rit = tit->second.mAllRoutes.begin();
rit != tit->second.mAllRoutes.end(); ++rit)
{
if ((now - rit->second.mReceived <= DSDV_DISCARD_PERIOD) &&
(rit->second.mSequence >= newest))
{
newest = rit->second.mSequence;
newestId = rit->first;
/* also becomes default for closest (later) */
closest = rit->second.mDistance;
closestId = rit->first;
closestAge = now - rit->second.mValidSince;
}
}
if (closest >= RSDSDV_MAX_DISTANCE + 1)
{
#ifdef DEBUG_DSDV
std::cerr << "\tNo Suitable Route";
std::cerr << std::endl;
#endif
tit->second.mIsStable = false;
continue;
}
uint32_t currseq = newest - (newest % 2); // remove 'kill'=ODD Seq.
#ifdef DEBUG_DSDV
std::cerr << "\t Newest Seq: " << newest << " from: " << newestId;
std::cerr << std::endl;
#endif
/* find closest distance - with valid seq & max valid time */
for(rit = tit->second.mAllRoutes.begin();
rit != tit->second.mAllRoutes.end(); ++rit)
{
/* Maximum difference in Sequence number is 2*DISTANCE
* Otherwise it must be old.
*/
if (rit->second.mSequence + rit->second.mDistance * 2 < currseq)
{
#ifdef DEBUG_DSDV
std::cerr << "\t\tIgnoring OLD SEQ Entry: " << rit->first;
std::cerr << std::endl;
#endif
continue; // ignore.
}
/* if we haven't received an update in ages - old */
if (now - rit->second.mReceived > DSDV_DISCARD_PERIOD)
{
#ifdef DEBUG_DSDV
std::cerr << "\t\tIgnoring OLD TIME Entry: " << rit->first;
std::cerr << std::endl;
#endif
continue; // ignore.
}
if (rit->second.mDistance < closest)
{
closest = rit->second.mDistance;
closestId = rit->first;
closestAge = now - rit->second.mValidSince;
#ifdef DEBUG_DSDV
std::cerr << "\t\tUpdating to Closer Entry: " << rit->first;
std::cerr << std::endl;
#endif
}
else if ((rit->second.mDistance == closest) &&
(closestAge < now - rit->second.mValidSince))
{
/* have a more stable (older) one */
closest = rit->second.mDistance;
closestId = rit->first;
closestAge = now - rit->second.mValidSince;
#ifdef DEBUG_DSDV
std::cerr << "\t\tUpdating to Stabler Entry: " << rit->first;
std::cerr << std::endl;
#endif
}
else
{
#ifdef DEBUG_DSDV
std::cerr << "\t\tIgnoring Distant Entry: " << rit->first;
std::cerr << std::endl;
#endif
}
}
tit->second.mIsStable = true;
rit = tit->second.mAllRoutes.find(closestId);
tit->second.mStableRoute = rit->second;
tit->second.mFlags &= ~RSDSDV_FLAGS_NEW_ROUTE;
#ifdef DEBUG_DSDV
std::cerr << "\tStable Route: " << tit->second.mStableRoute;
std::cerr << std::endl;
#endif
}
return 1;
}
int p3Dsdv::clearOldRoutes()
{
/* iterate over the entries */
RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
#ifdef DEBUG_DSDV
std::cerr << "p3Dsdv::clearOldRoutes()";
std::cerr << std::endl;
#endif
/* find the entry */
std::map<std::string, RsDsdvTableEntry>::iterator it, it2;
for(it = mTable.begin(); it != mTable.end(); ++it)
{
if (it->second.mOwnSource)
{
continue;
}
if (it->second.mIsStable)
{
continue;
}
if (it->second.mFlags & RSDSDV_FLAGS_NEW_ROUTE)
{
continue;
}
/* backstep iterator for loop, and delete original */
it2 = it;
it--;
#ifdef DEBUG_DSDV
std::cerr << "p3Dsdv::clearOldRoutes() Deleting OLD ServiceId: " << it2->first;
std::cerr << std::endl;
#endif
mTable.erase(it2);
}
return 1;
}
/*************** pqiMonitor callback ***********************/
void p3Dsdv::statusChange(const std::list<pqiServicePeer> &plist)
{
std::list<pqiServicePeer>::const_iterator it;
for(it = plist.begin(); it != plist.end(); ++it)
{
/* only care about disconnected / not friends cases */
if ( 1 )
{
RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
}
}
}
int p3Dsdv::addTestService()
{
RsDsdvId testId;
int rndhash1[SHA_DIGEST_LENGTH / 4];
int rndhash2[SHA_DIGEST_LENGTH / 4];
std::string realHash;
std::string seedHash;
for(int i = 0; i < SHA_DIGEST_LENGTH / 4; i++)
{
rndhash1[i] = RSRandom::random_u32();
rndhash2[i] = RSRandom::random_u32();
}
for(int i = 0; i < SHA_DIGEST_LENGTH; i++)
{
rs_sprintf_append(realHash, "%02x", (uint32_t) ((uint8_t *) rndhash1)[i]);
rs_sprintf_append(seedHash, "%02x", (uint32_t) ((uint8_t *) rndhash2)[i]);
}
uint8_t sha_hash[SHA_DIGEST_LENGTH];
memset(sha_hash,0,SHA_DIGEST_LENGTH*sizeof(uint8_t)) ;
SHA_CTX *sha_ctx = new SHA_CTX;
SHA1_Init(sha_ctx);
SHA1_Update(sha_ctx, realHash.c_str(), realHash.length());
SHA1_Update(sha_ctx, seedHash.c_str(), seedHash.length());
SHA1_Final(sha_hash, sha_ctx);
delete sha_ctx;
for(int i = 0; i < SHA_DIGEST_LENGTH; i++)
{
rs_sprintf_append(testId.mHash, "%02x", (uint32_t) (sha_hash)[i]);
}
testId.mIdType = RSDSDV_IDTYPE_TEST;
testId.mAnonChunk = seedHash;
addDsdvId(&testId, realHash);
return 1;
}
int p3Dsdv::addDsdvId(RsDsdvId *id, std::string realHash)
{
RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
#ifdef DEBUG_DSDV
std::cerr << "p3Dsdv::addDsdvId() ID: " << *id << " RealHash: " << realHash;
std::cerr << std::endl;
#endif
rstime_t now = time(NULL);
/* check for duplicate */
std::map<std::string, RsDsdvTableEntry>::iterator it;
it = mTable.find(id->mHash);
if (it != mTable.end())
{
/* error */
std::cerr << "p3Dsdv::addDsdvId() ERROR Duplicate ID";
std::cerr << std::endl;
return 0;
}
/* new entry! */
RsDsdvTableEntry v;
v.mDest = *id;
v.mStableRoute.mNextHop = mServiceCtrl->getOwnId();
v.mStableRoute.mReceived = now;
v.mStableRoute.mValidSince = now;
v.mStableRoute.mSequence = 0;
v.mStableRoute.mDistance = 0;
v.mIsStable = true;
v.mFlags = RSDSDV_FLAGS_OWN_SERVICE;
v.mOwnSource = true;
v.mMatched = true;
v.mMatchedHash = realHash;
// store in table.
mTable[v.mDest.mHash] = v;
return 1;
}
int p3Dsdv::dropDsdvId(RsDsdvId *id)
{
RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
#ifdef DEBUG_DSDV
std::cerr << "p3Dsdv::dropDsdvId() ID: " << *id;
std::cerr << std::endl;
#endif
/* This should send out an infinity packet... and flag for deletion */
std::map<std::string, RsDsdvTableEntry>::iterator it;
it = mTable.find(id->mHash);
if (it == mTable.end())
{
/* error */
std::cerr << "p3Dsdv::addDsdvId() ERROR Unknown ID";
std::cerr << std::endl;
return 0;
}
mTable.erase(it);
return 1;
}
int p3Dsdv::printDsdvTable(std::ostream &out)
{
RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
/* iterate over the entries */
std::map<std::string, RsDsdvTableEntry>::iterator it;
for(it = mTable.begin(); it != mTable.end(); ++it)
{
RsDsdvTableEntry &v = it->second;
out << v;
out << std::endl;
}
return 1;
}
/*****************************************/
uint32_t p3Dsdv::getLocalServices(std::list<std::string> &hashes)
{
RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
/* iterate over the entries */
std::map<std::string, RsDsdvTableEntry>::iterator it;
for(it = mTable.begin(); it != mTable.end(); ++it)
{
if (it->second.mOwnSource)
{
hashes.push_back(it->first);
}
}
return 1;
}
uint32_t p3Dsdv::getAllServices(std::list<std::string> &hashes)
{
RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
/* iterate over the entries */
std::map<std::string, RsDsdvTableEntry>::iterator it;
for(it = mTable.begin(); it != mTable.end(); ++it)
{
hashes.push_back(it->first);
}
return 1;
}
int p3Dsdv::getDsdvEntry(const std::string &hash, RsDsdvTableEntry &entry)
{
RsStackMutex stack(mDsdvMtx); /****** LOCKED MUTEX *******/
/* iterate over the entries */
std::map<std::string, RsDsdvTableEntry>::iterator it;
it = mTable.find(hash);
if (it == mTable.end())
{
return 0;
}
entry = it->second;
return 1;
}
std::ostream &operator<<(std::ostream &out, const RsDsdvId &id)
{
out << "[Type: " << id.mIdType << " AMZ: " << id.mAnonChunk << " THASH: " << id.mHash;
out << "]";
return out;
}
std::ostream &operator<<(std::ostream &out, const RsDsdvRoute &route)
{
rstime_t now = time(NULL);
out << "< Seq: " << route.mSequence << " Dist: " << route.mDistance;
out << " NextHop: " << route.mNextHop;
out << " recvd: " << now-route.mReceived;
out << " validSince: " << now-route.mValidSince;
out << " >";
return out;
}
std::ostream &operator<<(std::ostream &out, const RsDsdvTableEntry &entry)
{
out << "DSDV Route for: " << entry.mDest << std::endl;
if (entry.mIsStable)
{
out << "\tStable: " << entry.mStableRoute << std::endl;
}
else
{
out << "\tNo Stable Route" << std::endl;
}
out << "\tOwnSource: " << entry.mOwnSource;
out << " Flags: " << entry.mFlags;
if (entry.mMatched)
{
out << " Matched: " << entry.mMatchedHash;
}
else
{
out << " Non Matched";
}
out << std::endl;
if (entry.mAllRoutes.size() > 0)
{
out << "\tAll Routes:" << std::endl;
}
std::map<RsPeerId, RsDsdvRoute>::const_iterator it;
for(it = entry.mAllRoutes.begin(); it != entry.mAllRoutes.end(); ++it)
{
out << "\t\t" << it->second << std::endl;
}
return out;
}