Kademlia Class Reference

#include <Kademlia.h>

Inheritance diagram for Kademlia:

BaseOverlay BaseRpc TopologyVis RpcListener

List of all members.


Detailed Description

Kademlia overlay module.

Author:
Sebastian Mies

Public Member Functions

 ~Kademlia ()
void initializeOverlay (int stage)
 Initializes derived-class-attributes.
void finishOverlay ()
 collects statistical data in derived class
void joinOverlay ()
 Join the overlay with a given nodeID in thisNode.key.
bool isSiblingFor (const NodeHandle &node, const OverlayKey &key, int numSiblings, bool *err)
 Query if a node is among the siblings for a given key.
int getMaxNumSiblings ()
 Query the maximum number of siblings (nodes close to a key) that are maintained by this overlay protocol.
int getMaxNumRedundantNodes ()
 Query the maximum number of redundant next hop nodes that are returned by findNode().
void handleTimerEvent (cMessage *msg)
 Processes "timer" self-messages.
void handleUDPMessage (BaseOverlayMessage *msg)
 Processes messages from underlay.
bool handleRpc (BaseCallMessage *msg)
 Processes Remote-Procedure-Call invokation messages.

Protected Member Functions

NodeVectorfindNode (const OverlayKey &key, int numRedundantNodes, int numSiblings, BaseOverlayMessage *msg)
 Implements the find node call.
void handleRpcResponse (BaseResponseMessage *msg, cPolymorphic *context, int rpcId, simtime_t rtt)
 This method is called if an RPC response has been received.
void handleRpcTimeout (BaseCallMessage *msg, const TransportAddress &dest, cPolymorphic *context, int rpcId, const OverlayKey &destKey)
 This method is called if an RPC timeout has been reached.
void handleBucketRefreshTimerExpired ()
 handle a expired stabilize timer
OverlayKey distance (const OverlayKey &x, const OverlayKey &y) const
 This method should implement the distance between two keys.
void updateTooltip ()
 updates information shown in GUI
virtual void lookupFinished (bool isValid)

Protected Attributes

uint k
uint b
uint s
uint r
uint maxStaleCount
bool pingNewSiblings
bool activePing
simtime_t minSiblingTableRefreshInterval
simtime_t minBucketRefreshInterval
cMessage * bucketRefreshTimer
uint nodesReplaced

Private Member Functions

void routingInit ()
void routingDeinit ()
int routingBucketIndex (const OverlayKey &key)
 Returns the index of the bucket the key would reside with respect to Kademlia parameters.
KademliaBucketroutingBucket (const OverlayKey &key, bool ensure)
 Returns a Bucket or NULL if the bucket has not yet allocated.
bool routingAdd (const NodeHandle &handle, bool isAlive, simtime_t rtt=MAXTIME)
 Adds a node to the routing table.
bool routingRemove (const OverlayKey &key)
 Removes a node from the routing table.
bool routingTimeout (const OverlayKey &key, bool immediately=false)
 Removes a node after a number of timeouts or immediately if immediately is true (behaves like routingRemove).
void refillSiblingTable ()
void setBucketUsage (const OverlayKey &key)
bool forwardMessageRecursive (const TransportAddress &dest, BaseRouteMessage *msg)
 Hook for forwarded message in recursive lookup mode.
bool handleFailedNode (const TransportAddress &failed)
 Handles a failed node.

Private Attributes

KeyDistanceComparator
< KeyXorMetric > * 
comparator
KademliaBucketsiblingTable
std::vector< KademliaBucket * > routingTable
int numBuckets
std::map< NodeHandle, NodeHandlereplacementCache

Friends

class KademliaLookupListener

Constructor & Destructor Documentation

Kademlia::~Kademlia (  ) 

00115 {
00116     routingDeinit();
00117 
00118     replacementCache.clear();
00119     delete comparator;
00120     cancelAndDelete(bucketRefreshTimer);
00121 }


Member Function Documentation

void Kademlia::initializeOverlay ( int  stage  )  [virtual]

Initializes derived-class-attributes.


Initializes derived-class-attributes, called by BaseOverlay::initialize(). By default this method is called once. If more stages are needed one can overload numInitStages() and add more stages.

Parameters:
stage the init stage

Reimplemented from BaseOverlay.

00086 {
00087     if (stage != MIN_STAGE_OVERLAY)
00088         return;
00089 
00090     // Kademlia provides KBR services
00091     kbr = true;
00092 
00093     // setup kademlia parameters
00094     minSiblingTableRefreshInterval = par("minSiblingTableRefreshInterval");
00095     minBucketRefreshInterval = par("minBucketRefreshInterval");
00096     maxStaleCount = par("maxStaleCount");
00097     pingNewSiblings = par("pingNewSiblings");
00098 
00099     activePing = par("activePing");
00100 
00101     k = par("k");
00102     b = par("b");
00103     s = par("s");
00104 
00105     // self-message
00106     bucketRefreshTimer = new cMessage("bucketRefreshTimer");
00107 
00108     nodesReplaced = 0;
00109 
00110     comparator = NULL;
00111     siblingTable = NULL;
00112 }

void Kademlia::finishOverlay (  )  [virtual]

collects statistical data in derived class

Reimplemented from BaseOverlay.

00124 {
00125     // cancel timer
00126     cancelEvent(bucketRefreshTimer);
00127 
00128     simtime_t time = globalStatistics->calcMeasuredLifetime(creationTime);
00129     if(time == 0) return;
00130 
00131     globalStatistics->addStdDev("Kademlia: Nodes replaced in buckets/s",
00132                                 nodesReplaced / time);
00133 }

void Kademlia::joinOverlay (  )  [virtual]

Join the overlay with a given nodeID in thisNode.key.

Join the overlay with a given nodeID in thisNode.key. This method may be called by an application to join the overlay with a specific nodeID. It is also called if the node's IP address changes.

Reimplemented from BaseOverlay.

Referenced by handleRpcTimeout(), and routingTimeout().

00136 {
00137     // remove current node handle from the bootstrap list
00138     if (!thisNode.key.isUnspecified()) {
00139         bootstrapList->removeBootstrapNode(thisNode);
00140     }
00141 
00142     // initialize routing
00143     routingDeinit();
00144     routingInit();
00145 
00146     const NodeHandle& handle = bootstrapList->getBootstrapNode();
00147 
00148     if (!handle.isUnspecified()) {
00149         // ping the bootstrap node to start bootstrapping
00150         pingNode(handle);
00151         //std::cout << thisNode << " bootstrapping via " << handle << std::endl;
00152 /*
00153         FindNodeCall* findNodeCall = new FindNodeCall();
00154         findNodeCall->setLookupKey(handle.getKey());
00155         findNodeCall->setSrcNode(getThisNode());
00156         findNodeCall->setNumRedundantNodes(getMaxNumRedundantNodes());
00157         findNodeCall->setNumSiblings(getMaxNumSiblings());
00158         findNodeCall->setLength(FINDNODECALL_L(call));
00159 
00160         RECORD_STATS(numFindNodeSent++;
00161             bytesFindNodeSent += findNodeCall->byteLength());
00162 
00163         sendUdpRpcCall(handle, findNodeCall);
00164 */
00165     } else {
00166         // we're the only node in the network
00167         state = READY;
00168         setOverlayReady(true);
00169 
00170         // schedule bucket refresh timer
00171         cancelEvent(bucketRefreshTimer);
00172         scheduleAt(simulation.simTime(), bucketRefreshTimer);
00173     }
00174 }

bool Kademlia::isSiblingFor ( const NodeHandle node,
const OverlayKey key,
int  numSiblings,
bool *  err 
) [virtual]

Query if a node is among the siblings for a given key.

Query if a node is among the siblings for a given key. This means, that the nodeId of this node is among the closest numSiblings nodes to the key and that by a local findNode() call all other siblings to this key can be retrieved.

Parameters:
node the NodeHandle
key destination key
numSiblings The nodes knows all numSiblings nodes close to this key
err return false if the range could not be determined
Returns:
bool true, if the node is responsible for the key.

Reimplemented from BaseOverlay.

Referenced by findNode().

00537 {
00538     if (key.isUnspecified())
00539         error("Kademlia::isSiblingFor(): key is unspecified!");
00540 
00541     if (state != READY) {
00542         *err = true;
00543         return false;
00544     }
00545 
00546     if (numSiblings > getMaxNumSiblings()) {
00547         opp_error("Kademlia::isSiblingFor(): numSiblings too big!");
00548     }
00549 
00550     // set default number of siblings to consider
00551     if (numSiblings == -1) {
00552         numSiblings = getMaxNumSiblings();
00553     }
00554 
00555     if (numSiblings == 0) {
00556         *err = false;
00557         return (node.getKey() == key);
00558     }
00559 
00560     if (siblingTable->isEmpty()) {
00561         *err = false;
00562         return true;
00563     }
00564 
00565     if ((thisNode.key ^ key) > (thisNode.key ^ siblingTable->back().key)) {
00566         *err = true;
00567         return false;
00568     }
00569 
00570     KeyDistanceComparator<KeyXorMetric>* comp =
00571         new KeyDistanceComparator<KeyXorMetric>(key);
00572 
00573     // create result vector
00574     NodeVector* result = new NodeVector(numSiblings, comp);
00575 
00576     for (KademliaBucket::iterator i=siblingTable->begin();
00577          i != siblingTable->end(); i++) {
00578         result->add( *i);
00579     }
00580 
00581     // add local node
00582     result->add(thisNode);
00583 
00584     *err = false;
00585     delete comp;
00586 
00587     if (result->contains(node.key)) {
00588         delete result;
00589         return true;
00590     } else {
00591         delete result;
00592         return false;
00593     }
00594 }

int Kademlia::getMaxNumSiblings (  )  [virtual]

Query the maximum number of siblings (nodes close to a key) that are maintained by this overlay protocol.

Returns:
int number of siblings.

Reimplemented from BaseOverlay.

Referenced by findNode(), handleFailedNode(), isSiblingFor(), routingAdd(), and routingTimeout().

00228 {
00229     return s;
00230 }

int Kademlia::getMaxNumRedundantNodes (  )  [virtual]

Query the maximum number of redundant next hop nodes that are returned by findNode().

Returns:
int number of redundant nodes returned by findNode().

Reimplemented from BaseOverlay.

Referenced by findNode().

00233 {
00234     return k;
00235 }

void Kademlia::handleTimerEvent ( cMessage *  msg  )  [virtual]

Processes "timer" self-messages.

Parameters:
msg A self-message

Reimplemented from BaseOverlay.

00723 {
00724     if (msg == bucketRefreshTimer) {
00725         handleBucketRefreshTimerExpired();
00726     }
00727 }

void Kademlia::handleUDPMessage ( BaseOverlayMessage *  msg  )  [virtual]

Processes messages from underlay.

Parameters:
msg Message from UDP

Reimplemented from BaseOverlay.

00730 {
00731     OverlayCtrlInfo* ctrlInfo =
00732             check_and_cast<OverlayCtrlInfo*>(msg->removeControlInfo());
00733     KademliaRoutingInfoMessage* kadRoutingInfoMsg = check_and_cast<
00734             KademliaRoutingInfoMessage*>(msg);
00735 
00736     routingAdd(kadRoutingInfoMsg->getSrcNode(), true);
00737 
00738     for (uint i = 0; i < kadRoutingInfoMsg->getNextHopsArraySize(); i++) {
00739         routingAdd(kadRoutingInfoMsg->getNextHops(i), false);
00740     }
00741 
00742     delete ctrlInfo;
00743     delete msg;
00744 }

bool Kademlia::handleRpc ( BaseCallMessage *  msg  )  [virtual]

Processes Remote-Procedure-Call invokation messages.


This method should be overloaded when the overlay provides RPC functionality.

Returns:
true, if rpc has been handled

Reimplemented from BaseRpc.

00748 {
00749     RPC_SWITCH_START( msg )
00750     RPC_ON_CALL( Ping ) {
00751         // add active node
00752         routingAdd( _PingCall->getSrcNode(), true );
00753         break;
00754     }
00755     RPC_ON_CALL(FindNode)
00756     {
00757         // add active node
00758         routingAdd(_FindNodeCall->getSrcNode(), true);
00759         break;
00760     }
00761     RPC_SWITCH_END( )
00762     return false;
00763 }

NodeVector * Kademlia::findNode ( const OverlayKey key,
int  numRedundantNodes,
int  numSiblings,
BaseOverlayMessage *  msg 
) [protected, virtual]

Implements the find node call.

This method simply returns the closest nodes known in the corresponding routing topology. If the node is a sibling for this key (isSiblingFor(key) = true), this method returns all numSiblings siblings, with the closest neighbor to the key first.

Parameters:
key The lookup key.
numRedundantNodes Maximum number of next hop nodes to return.
numSiblings number of siblings to return
msg A pointer to the BaseRouteMessage or FindNodeCall message of this lookup.
Returns:
NodeVector with closest nodes.

Reimplemented from BaseOverlay.

00656 {
00657     if ((numRedundantNodes > getMaxNumRedundantNodes()) || (numSiblings
00658             > getMaxNumSiblings())) {
00659 
00660         opp_error("(Kademlia::findNode()) numRedundantNodes or numSiblings "
00661                   "too big!");
00662     }
00663 
00664     if (numRedundantNodes < 2) {
00665         throw new cRuntimeError("Kademlia::findNode(): For Kademlia "
00666                                 "redundantNodes must be at least 2 "
00667                                 "and lookupMerge should be true!");
00668     }
00669 
00670     // create temporary comparator
00671     KeyDistanceComparator<KeyXorMetric>* comp =
00672         new KeyDistanceComparator<KeyXorMetric>( key );
00673 
00674     // select result set size
00675     bool err;
00676     int resultSize = isSiblingFor(thisNode, key, numSiblings, &err) ?
00677         (numSiblings ? numSiblings : 1) : numRedundantNodes;
00678 
00679     assert(numSiblings || numRedundantNodes);
00680 
00681     NodeVector* result = new NodeVector(resultSize, comp);
00682 
00683     NodeRttVector* resultProx = NULL;
00684     KeyDistanceComparator<KeyPrefixMetric>* compProx = NULL;
00685 
00686     // add items from buckets
00687     int mainIndex = routingBucketIndex(key);
00688 
00689     for (int i = 1; !result->isFull() && i < numBuckets * 3; i++) {
00690         int index = mainIndex + (((i & 1) == 1) ? -1 : 1) * (i / 2);
00691         if (index < 0 || index >= numBuckets)
00692             continue;
00693 
00694         // add bucket to result vector
00695         KademliaBucket* bucket = routingTable[index];
00696         if (bucket!=NULL) {
00697             for (KademliaBucket::iterator i=bucket->begin(); i!=bucket->end(); i++) {
00698                 result->add(*i);
00699             }
00700         }
00701     }
00702 
00703     // add nodes from sibling table
00704     for (KademliaBucket::iterator i = siblingTable->begin();
00705          i != siblingTable->end(); i++) {
00706         result->add(*i);
00707     }
00708 
00709     // add local node
00710     result->add(thisNode);
00711 
00712     delete comp;
00713     delete compProx;
00714     delete resultProx;
00715 
00716     return result;
00717 }

void Kademlia::handleRpcResponse ( BaseResponseMessage *  msg,
cPolymorphic *  context,
int  rpcId,
simtime_t  rtt 
) [protected, virtual]

This method is called if an RPC response has been received.

Parameters:
msg The response message.
context Pointer to an optional state object. The object has to be handled/deleted by the handleRpcResponse() code
rpcId The RPC id.
rtt The Round-Trip-Time of this RPC

Reimplemented from RpcListener.

00769 {
00770     // add node that reponded
00771     routingAdd(msg->getSrcNode(), true, rtt);
00772 
00773     RPC_SWITCH_START(msg)
00774         RPC_ON_RESPONSE(Ping) {
00775             if (state == INIT) {
00776                 // schedule bucket refresh timer
00777                 cancelEvent(bucketRefreshTimer);
00778                 scheduleAt(simulation.simTime(), bucketRefreshTimer);
00779                 state = JOIN;
00780             }
00781 
00782             // add active node
00783             replacementCache.erase(_PingResponse->getSrcNode());
00784             //routingAdd(_PingResponse->getSrcNode(), true , rtt);
00785         }
00786         RPC_ON_RESPONSE(FindNode)
00787         {
00788             // add active node
00789             if (defaultRoutingType == SEMI_RECURSIVE_ROUTING ||
00790                 defaultRoutingType == FULL_RECURSIVE_ROUTING ||
00791                 defaultRoutingType == RECURSIVE_SOURCE_ROUTING)
00792                 rtt = MAXTIME;
00793             //routingAdd(_FindNodeResponse->getSrcNode(), true, rtt);
00794             setBucketUsage(_FindNodeResponse->getSrcNode().key);
00795 
00796             // add inactive nodes
00797             for (uint i=0; i<_FindNodeResponse->getClosestNodesArraySize(); i++)
00798                 routingAdd(_FindNodeResponse->getClosestNodes(i), false);
00799             break;
00800         }
00801     RPC_SWITCH_END()
00802 }

void Kademlia::handleRpcTimeout ( BaseCallMessage *  msg,
const TransportAddress dest,
cPolymorphic *  context,
int  rpcId,
const OverlayKey destKey 
) [protected, virtual]

This method is called if an RPC timeout has been reached.

Parameters:
msg The original RPC message.
dest The destination node
context Pointer to an optional state object. The object has to be handled/deleted by the handleRpcResponse() code
rpcId The RPC id.
destKey the destination OverlayKey

Reimplemented from RpcListener.

00809 {
00810     if (dest.isUnspecified()) return;
00811 
00812     const NodeHandle& handle = dynamic_cast<const NodeHandle&>(dest);
00813 
00814     RPC_SWITCH_START(msg)
00815         if (state == JOIN) {
00816             joinOverlay();
00817         }
00818     RPC_ON_CALL(Ping) {
00819         routingTimeout(handle.key);
00820 
00821         // remove node from replacementCache
00822         std::map<NodeHandle, NodeHandle>::iterator it
00823             = replacementCache.find(handle);
00824         if (it != replacementCache.end()) {
00825             replacementCache.erase(it);
00826         }
00827 
00828         break;
00829     }
00830     RPC_ON_CALL(FindNode) {
00831         routingTimeout(handle.key);
00832         setBucketUsage(handle.key);
00833         break;
00834     }
00835     RPC_SWITCH_END()
00836 }

void Kademlia::handleBucketRefreshTimerExpired (  )  [protected]

handle a expired stabilize timer

handle a expired bucket refresh timer

Referenced by handleTimerEvent().

00877 {
00878     // refresh buckets
00879     if (state != READY || (((simTime() - siblingTable->getLastUsage()) >
00880                             minSiblingTableRefreshInterval)))
00881         //std::cout << thisNode << ": lookup" << std::endl;
00882         createLookup()->lookup(getThisNode().getKey() +
00883                                OverlayKey::ONE, 0, 0, 0,
00884                                new KademliaLookupListener(this));
00885 
00886     if (state == READY && siblingTable->size()) {
00887         uint diff = OverlayKey::getLength() - getThisNode().getKey().
00888                         sharedPrefixLength(siblingTable->front().key);
00889         for (uint i = OverlayKey::getLength() - 1; i >= (diff - 1); i--) {
00890             if ((routingTable[i] == NULL) ||
00891                  ((simTime() - routingTable[i]->getLastUsage()) >
00892                    minBucketRefreshInterval)) {
00893                 //EV << i << "bla";
00894                 createLookup()->lookup(getThisNode().getKey() ^
00895                     (OverlayKey::ONE << i), 0, 0, 0,
00896                     new KademliaLookupListener(this));
00897             }
00898         }
00899         // schedule next bucket refresh process
00900         cancelEvent(bucketRefreshTimer);
00901         scheduleAt(simulation.simTime() +
00902                    (min(minSiblingTableRefreshInterval,
00903                         minBucketRefreshInterval) / 10.0),
00904                    bucketRefreshTimer);
00905     }
00906 }

OverlayKey Kademlia::distance ( const OverlayKey x,
const OverlayKey y 
) const [protected, virtual]

This method should implement the distance between two keys.

It may be overloaded to implement a new metric. The default implementation uses the standard-metric d = abs(x-y).

Parameters:
x Left-hand-side Key
y Right-hand-side key
Returns:
OverlayKey Distance between x and y

Reimplemented from BaseOverlay.

00910 {
00911     return x^y;
00912 }

void Kademlia::updateTooltip (  )  [protected]

updates information shown in GUI

Referenced by handleFailedNode(), routingAdd(), routingInit(), and routingTimeout().

00915 {
00916     if (ev.isGUI()) {
00917         std::stringstream ttString;
00918 
00919         // show our nodeId in a tooltip
00920         ttString << "This: " << thisNode << endl << "Siblings: "
00921                  << siblingTable->size();
00922 
00923         parentModule()->parentModule()->displayString().
00924         setTagArg("tt", 0, ttString.str().c_str());
00925         parentModule()->displayString().
00926         setTagArg("tt", 0, ttString.str().c_str());
00927         displayString().setTagArg("tt", 0, ttString.str().c_str());
00928     }
00929 }

void Kademlia::lookupFinished ( bool  isValid  )  [protected, virtual]

Referenced by KademliaLookupListener::lookupFinished().

00839 {
00840     if (state != READY) {
00841         cancelEvent(bucketRefreshTimer);
00842         scheduleAt(simulation.simTime(), bucketRefreshTimer);
00843 
00844         state = READY;
00845         setOverlayReady(true);
00846     }
00847 }

void Kademlia::routingInit (  )  [private]

Referenced by joinOverlay().

00179 {
00180     // set join state
00181     state = INIT;
00182 
00183     setOverlayReady(false);
00184 
00185     // setup comparator
00186     comparator = new KeyDistanceComparator<KeyXorMetric>( thisNode.getKey() );
00187 
00188     // calculate number of buckets: ( (2^b)-1 ) * ( keylength / b )
00189     numBuckets = ( (1L << b) - 1L ) * (OverlayKey::getLength() / b );
00190 
00191     // init routing and sibling table
00192     siblingTable = new KademliaBucket ( s * 5, comparator );
00193 
00194     // initialize pointers
00195     routingTable.assign(numBuckets, (KademliaBucket*)NULL);
00196 
00197     WATCH_VECTOR(*siblingTable);
00198     WATCH_VECTOR(routingTable);
00199 
00200     updateTooltip();
00201     BUCKET_CONSISTENCY(routingInit: end);
00202 }

void Kademlia::routingDeinit (  )  [private]

Referenced by joinOverlay(), and ~Kademlia().

00205 {
00206     // delete buckets
00207     for (uint i = 0; i < routingTable.size(); i++) {
00208         if (routingTable[i] != NULL) {
00209             delete routingTable[i];
00210             routingTable[i] = NULL;
00211         }
00212     }
00213     routingTable.clear();
00214 
00215     // clear sibling table
00216     if (siblingTable != NULL) {
00217         delete siblingTable;
00218         siblingTable = NULL;
00219     }
00220 
00221     if (comparator != NULL) {
00222         delete comparator;
00223         comparator = NULL;
00224     }
00225 }

int Kademlia::routingBucketIndex ( const OverlayKey key  )  [private]

Returns the index of the bucket the key would reside with respect to Kademlia parameters.

Parameters:
key The key of the node
Returns:
int The index of the bucket

Referenced by findNode(), refillSiblingTable(), routingAdd(), and routingBucket().

00238 {
00239     // calculate XOR distance
00240     OverlayKey delta = key ^ getThisNode().getKey();
00241 
00242     // find first subinteger that is not zero...
00243     int i;
00244     for (i = key.getLength() - b; i >= 0 && delta.getBitRange(i, b) == 0; i -= b)
00245         ;
00246     if (i < 0)
00247         return -1;
00248 
00249     assert((i / b) * ((1 << b) - 1) + (delta.getBitRange(i, b) - 1) ==
00250            OverlayKey::getLength() -
00251                getThisNode().getKey().sharedPrefixLength(key, b) - 1);
00252 
00253     return (i / b) * ((1 << b) - 1) + (delta.getBitRange(i, b) - 1);
00254 }

KademliaBucket * Kademlia::routingBucket ( const OverlayKey key,
bool  ensure 
) [private]

Returns a Bucket or NULL if the bucket has not yet allocated.

If ensure is true, the bucket allocation is ensured.

Parameters:
key The key of the node
ensure If true, the bucket allocation is ensured
Returns:
Bucket* The Bucket

Referenced by routingAdd(), routingTimeout(), and setBucketUsage().

00257 {
00258     // get bucket index
00259     int num = routingBucketIndex(key);
00260     if (num < 0)
00261         return NULL;
00262 
00263     // get bucket and allocate if necessary
00264     KademliaBucket* bucket = routingTable[ num ];
00265     if (bucket == NULL && ensure)
00266         bucket = routingTable[ num ] = new KademliaBucket( k, comparator );
00267 
00268     // return bucket
00269     return bucket;
00270 }

bool Kademlia::routingAdd ( const NodeHandle handle,
bool  isAlive,
simtime_t  rtt = MAXTIME 
) [private]

Adds a node to the routing table.

Parameters:
handle handle to add
isAlive true, if it is known that the node is alive
rtt measured round-trip-time to node
Returns:
true, if the node was known or has been added

Referenced by handleRpc(), handleRpcResponse(), handleUDPMessage(), and routingTimeout().

00273 {
00274     BUCKET_CONSISTENCY(routingAdd: start);
00275     // never add unspecified node handles
00276     if (handle.isUnspecified() || handle.key == getThisNode().getKey() )
00277         return false;
00278 
00279     // bucket index
00280     KademliaBucket::iterator i;
00281     bool result = false;
00282 
00283     bool needsRtt = (activePing && ((rtt == MAXTIME) ? true : false));
00284 
00285     // convert node handle
00286     KademliaBucketEntry kadHandle = handle;
00287     kadHandle.setRtt(rtt);
00288     kadHandle.setLastSeen(simulation.simTime());
00289 
00290     /* check if node is already a sibling -----------------------------------*/
00291     if ((i = siblingTable->findIterator(handle.getKey()))
00292          != siblingTable->end()) {
00293         // not alive? -> do not change routing information
00294         if (isAlive) {
00295             if (kadHandle.getRtt() != i->getRtt()) {
00296                 siblingTable->setLastUpdate(simTime());
00297                 if (kadHandle.getRtt() == MAXTIME)
00298                     kadHandle.setRtt(i->getRtt());
00299             }
00300             // refresh sibling
00301             (*i) = kadHandle;
00302         }
00303         BUCKET_CONSISTENCY(routingAdd: node is sibling);
00304         return true;
00305     }
00306 
00307     /* check if node is already in a bucket ---------------------------------*/
00308     KademliaBucket* bucket = routingBucket(handle.getKey(), false);
00309     if (bucket != NULL && (i = bucket->findIterator(handle.getKey() ) )
00310             != bucket->end() ) {
00311         // not alive? -> do not change routing information
00312         if (isAlive) {
00313             if (kadHandle.getRtt() == MAXTIME) {
00314                 kadHandle.setRtt(i->getRtt());
00315             }
00316             // remove old handle
00317             bucket->erase(i);
00318             // re-add to tail
00319             bucket->push_back(kadHandle);
00320 
00321             bucket->setLastUpdate(simTime());
00322 
00323             if (needsRtt && (kadHandle.getRtt() == MAXTIME))
00324                 pingNode(handle);
00325         }
00326         BUCKET_CONSISTENCY(routingAdd: node is in bucket);
00327         return true;
00328     }
00329 
00330     /* check if node can be added to the sibling list -----------------------*/
00331     if (siblingTable->isAddable(handle) ) {
00332 
00333         bool finished = false;
00334         int siblingPos = -1;
00335 
00336         // check if sibling list is full so a handle is preemted from the list
00337         if (siblingTable->isFull()) {
00338             // get handle thats about to be preempted
00339             KademliaBucketEntry oldHandle = siblingTable->back();
00340             assert(oldHandle.key != kadHandle.key);
00341             // add handle to the sibling list
00342             siblingPos = siblingTable->add(kadHandle);
00343 
00344             // change, so that the preempted handle is added to a bucket
00345             kadHandle = oldHandle;
00346 
00347             // return always true, since the handle has been added
00348             result = true;
00349         } else {
00350             // simply add the handle and stop
00351             siblingPos = siblingTable->add(kadHandle);
00352 
00353             // don't need to add kadHandle also to regular buckets
00354             finished = true;
00355         }
00356         assert(siblingPos > -1);
00357 
00358         // ping new siblings
00359         if ((pingNewSiblings && !isAlive) || needsRtt) {
00360             pingNode(handle);
00361         }
00362 
00363         siblingTable->setLastUpdate(simTime());
00364 
00365         updateTooltip();
00366 
00367         // call update() for real siblings
00368         if (siblingPos < getMaxNumSiblings()) {
00369             if (siblingTable->size() > (uint)getMaxNumSiblings()) {
00370                 // removed old sibling
00371                 NodeHandle& removedSibling = siblingTable->at(getMaxNumSiblings());
00372                 deleteOverlayNeighborArrow(removedSibling);
00373                 callUpdate(removedSibling, false);
00374             }
00375             // new sibling
00376             showOverlayNeighborArrow(handle, false,
00377                                      "m=m,50,100,50,100;o=green,1");
00378             callUpdate(handle, true);
00379         }
00380 
00381         if (finished) {
00382             BUCKET_CONSISTENCY(routingAdd: node is now sibling);
00383             return true;
00384         }
00385     }
00386 
00387     /* add node to the appropriate bucket, if not full ---------------------*/
00388     bucket = routingBucket(kadHandle.getKey(), true);
00389     if (!bucket->isFull()) {
00390         EV << "Kademlia::routingAdd(): Adding new node " << kadHandle
00391            << " to bucket " << routingBucketIndex(kadHandle.getKey()) << endl;
00392         bucket->push_back(kadHandle);
00393         bucket->setLastUpdate(simTime());
00394         result = true;
00395     } else if (isAlive) {
00396         // save candidate, ping head,
00397         KademliaBucket::iterator it = bucket->begin();
00398         while (it != bucket->end() &&
00399                replacementCache.find(*it) != replacementCache.end()) {
00400             ++it;
00401         }
00402         if (it != bucket->end()) {
00403             replacementCache.insert(std::make_pair(*it, kadHandle));
00404             pingNode(*it, -1,-1, NULL, NULL, NULL, -1,
00405                      UDP_TRANSPORT, true);
00406         }
00407     }
00408 
00409     if (result && needsRtt)
00410         pingNode(handle);
00411 
00412     BUCKET_CONSISTENCY(routingAdd: end);
00413     return result;
00414 }

bool Kademlia::routingRemove ( const OverlayKey key  )  [private]

Removes a node from the routing table.

Parameters:
key Key of the Node
Returns:
true, if the node has been removed
00417 {
00418     return routingTimeout(key, true);
00419 }

bool Kademlia::routingTimeout ( const OverlayKey key,
bool  immediately = false 
) [private]

Removes a node after a number of timeouts or immediately if immediately is true (behaves like routingRemove).

Parameters:
key Node's key to remove
immediately If true, the node is removed immediately
Returns:
true, if the node has been removed

Referenced by handleRpcTimeout(), refillSiblingTable(), and routingRemove().

00422 {
00423     BUCKET_CONSISTENCY(routingTimeout: start);
00424     // key unspecified? yes -> ignore
00425     if (key.isUnspecified())
00426         return false;
00427 
00428     // bucket index
00429     KademliaBucket::iterator i;
00430 
00431     /* check if the node is one of the siblings -----------------------------*/
00432     if ((i = siblingTable->findIterator(key)) != siblingTable->end()) {
00433 
00434         i->incStaleCount();
00435 
00436         if (i->getStaleCount() > maxStaleCount || immediately) {
00437             // remove from sibling table
00438             NodeHandle oldSibling = *i;
00439             siblingTable->erase(i);
00440 
00441             if (siblingTable->size() < (uint)getMaxNumSiblings()) {
00442                 // no new replacement sibling
00443                 deleteOverlayNeighborArrow(oldSibling);
00444                 callUpdate(oldSibling, false);
00445             } else if (comparator->compare(oldSibling.key,
00446                                siblingTable->at(getMaxNumSiblings() - 1).key) < 0) {
00447                 // failed sibling was replaced by next closest node in siblingTable
00448                 deleteOverlayNeighborArrow(oldSibling);
00449                 callUpdate(oldSibling, false);
00450 
00451                 showOverlayNeighborArrow(siblingTable->at(getMaxNumSiblings() - 1),
00452                                          false, "m=m,50,100,50,100;o=green,1");
00453                 callUpdate(siblingTable->at(getMaxNumSiblings() - 1), true);
00454             }
00455 
00456             updateTooltip();
00457 
00458             // lost last sibling?
00459             if (siblingTable->size() == 0) {
00460                 joinOverlay();
00461                 return true;
00462             }
00463 
00464             BUCKET_CONSISTENCY(routingTimeout: is sibling);
00465 
00466             // try to refill with new closest contact
00467             refillSiblingTable();
00468 
00469             return true;
00470         }
00471     }
00472 
00473     /* check if node is already in a bucket ---------------------------------*/
00474     KademliaBucket* bucket = routingBucket(key, false);
00475     if (bucket != NULL && (i = bucket->findIterator(key) ) != bucket->end() ) {
00476 
00477         i->incStaleCount();
00478         std::map<NodeHandle, NodeHandle>::iterator it
00479                     = replacementCache.find(*i);
00480         if (i->getStaleCount() > maxStaleCount ||
00481             it != replacementCache.end() || immediately) {
00482             // remove from routing table
00483             bucket->erase(i);
00484         }
00485         if (it != replacementCache.end()) {
00486             routingAdd(it->second, true);
00487             nodesReplaced++;
00488             //EV << "node replaced" << endl;
00489         }
00490         BUCKET_CONSISTENCY(routingTimeout: is in bucket);
00491         return true;
00492     }
00493     BUCKET_CONSISTENCY(routingTimeout: end);
00494     return false;
00495 }

void Kademlia::refillSiblingTable (  )  [private]

Referenced by handleFailedNode(), and routingTimeout().

00498 {
00499     if (siblingTable->size() == 0 ||
00500         siblingTable->isFull())
00501         return;
00502 
00503     int index = routingBucketIndex(siblingTable->back().key) - 1;
00504     assert(index > 0);
00505 
00506     while ((routingTable[index] == NULL ||
00507             routingTable[index]->empty()) &&
00508             index < (int)(OverlayKey::getLength() - 1)) {
00509         index++;
00510     }
00511     if (index < (int)OverlayKey::getLength() &&
00512             routingTable[index] != NULL && routingTable[index]->size()) {
00513         KademliaBucket sortedBucket(k, comparator);
00514         for (uint i = 0; i < routingTable[index]->size(); ++i)
00515             sortedBucket.add(routingTable[index]->at(i));
00516         siblingTable->add(sortedBucket.front());
00517         // no need to callUpdate(), because s < siblingTable->size(), thus
00518         // new sibling talbe entry is no real sibling
00519         routingTable[index]->
00520         erase(routingTable[index]->
00521               findIterator(sortedBucket.front().key));
00522         assert(siblingTable->isFull());
00523         BUCKET_CONSISTENCY(routingTimeout: end refillSiblingTable());
00524     }
00525 }

void Kademlia::setBucketUsage ( const OverlayKey key  )  [private]

Referenced by handleRpcResponse(), and handleRpcTimeout().

00528 {
00529     KademliaBucket* bucket = routingBucket(key, false);
00530 
00531     if (bucket)
00532         bucket->setLastUsage(simTime());
00533 }

bool Kademlia::forwardMessageRecursive ( const TransportAddress dest,
BaseRouteMessage *  msg 
) [private, virtual]

Hook for forwarded message in recursive lookup mode.

This hook is called just before a message is forwarded to a next hop or if the message is at its destination just before it is sent to the app. Default implementation just returns true. This hook can for example be used to detect failed nodes and call handleFailedNode() before the actual forwarding takes place.

Parameters:
dest destination node
msg message to send
Returns:
true, if message should be forwarded; false, if message will be forwarded later by an other function or message has been discarded

Reimplemented from BaseOverlay.

00651 {
00652 }

bool Kademlia::handleFailedNode ( const TransportAddress failed  )  [private, virtual]

Handles a failed node.

This method is called whenever a node given by findNode() was unreachable. The default implementation does nothing at all.

Parameters:
failed the failed node
Returns:
true if lookup should retry here

Reimplemented from BaseOverlay.

00597 {
00598     assert(!failed.isUnspecified());
00599     //std::cout << "Kademlia::handleFailedNode()" << std::endl;
00600     KademliaBucket::iterator i;
00601     // check sibling table
00602     for (i = siblingTable->begin(); i != siblingTable->end(); ++i) {
00603         if (failed == *i) break;
00604     }
00605 
00606     if (i != siblingTable->end()) {
00607         // remove from sibling table
00608         NodeHandle oldSibling = *i;
00609         siblingTable->erase(i);
00610 
00611         if (siblingTable->size() < (uint)getMaxNumSiblings()) {
00612             // no new replacement sibling
00613             deleteOverlayNeighborArrow(oldSibling);
00614             callUpdate(oldSibling, false);
00615         } else if (comparator->compare(oldSibling.key,
00616                            siblingTable->at(getMaxNumSiblings() - 1).key) < 0) {
00617             // failed sibling was replaced by next closest node in siblingTable
00618             deleteOverlayNeighborArrow(oldSibling);
00619             callUpdate(oldSibling, false);
00620 
00621             showOverlayNeighborArrow(siblingTable->at(getMaxNumSiblings() - 1),
00622                                      false, "m=m,50,100,50,100;o=green,1");
00623             callUpdate(siblingTable->at(getMaxNumSiblings() - 1), true);
00624         }
00625 
00626         updateTooltip();
00627 
00628         // try to refill with new closest contact
00629         refillSiblingTable();
00630     } else {
00631         // check buckets
00632         uint m;
00633         for (m = 0; m < routingTable.size(); ++m) {
00634             if (routingTable[m] != NULL) {
00635                 for (i = routingTable[m]->begin(); i != routingTable[m]->end();
00636                      ++i) {
00637                     if (failed == *i) {
00638                         // remove from routing table
00639                         routingTable[m]->erase(i);
00640                         return (siblingTable->size() != 0);
00641                     }
00642                 }
00643             }
00644         }
00645     }
00646     return (siblingTable->size() != 0);
00647 }


Friends And Related Function Documentation

friend class KademliaLookupListener [friend]


Member Data Documentation

uint Kademlia::k [protected]

uint Kademlia::b [protected]

uint Kademlia::s [protected]

uint Kademlia::r [protected]

uint Kademlia::maxStaleCount [protected]

bool Kademlia::pingNewSiblings [protected]

Referenced by initializeOverlay(), and routingAdd().

bool Kademlia::activePing [protected]

Referenced by initializeOverlay(), and routingAdd().

simtime_t Kademlia::minBucketRefreshInterval [protected]

cMessage* Kademlia::bucketRefreshTimer [protected]

uint Kademlia::nodesReplaced [protected]

std::vector<KademliaBucket*> Kademlia::routingTable [private]

int Kademlia::numBuckets [private]

Referenced by findNode(), and routingInit().


The documentation for this class was generated from the following files:

Generated on Fri Sep 19 13:05:07 2008 for ITM OverSim by  doxygen 1.5.5