Kademlia Class Reference

Kademlia overlay module. More...

#include <Kademlia.h>

Inheritance diagram for Kademlia:

BaseOverlay BaseRpc TopologyVis RpcListener

List of all members.

Public Member Functions

 Kademlia ()
 ~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)
bool handleRpcCall (BaseCallMessage *msg)
 Processes Remote-Procedure-Call invocation 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 bucket refresh 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

uint32_t k
uint32_t b
uint32_t s
uint32_t maxStaleCount
bool exhaustiveRefresh
bool pingNewSiblings
simtime_t minSiblingTableRefreshInterval
simtime_t minBucketRefreshInterval
cMessage * bucketRefreshTimer

Private Member Functions

void routingInit ()
void routingDeinit ()
int routingBucketIndex (const OverlayKey &key, bool firstOnLayer=false)
 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 handleFailedNode (const TransportAddress &failed)
 Handles a failed node.

Private Attributes

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

Friends

class KademliaLookupListener


Detailed Description

Kademlia overlay module.

Author:
Sebastian Mies, Ingmar Baumgart, Bernhard Heep

Definition at line 42 of file Kademlia.h.


Constructor & Destructor Documentation

Kademlia::Kademlia (  ) 

Definition at line 136 of file Kademlia.cc.

00137 {
00138     siblingTable = NULL;
00139     comparator = NULL;
00140 }

Kademlia::~Kademlia (  ) 

Definition at line 142 of file Kademlia.cc.

00143 {
00144     routingDeinit();
00145 
00146     replacementCache.clear();
00147     delete siblingTable;
00148     delete comparator;
00149     cancelAndDelete(bucketRefreshTimer);
00150 }


Member Function Documentation

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.

Definition at line 989 of file Kademlia.cc.

00990 {
00991     return x^y;
00992 }

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.

Definition at line 678 of file Kademlia.cc.

00680 {
00681     if ((numRedundantNodes > getMaxNumRedundantNodes()) ||
00682             (numSiblings > getMaxNumSiblings())) {
00683 
00684         opp_error("(Kademlia::findNode()) numRedundantNodes or numSiblings "
00685                   "too big!");
00686     }
00687 
00688     if (numRedundantNodes < 2) {
00689         throw cRuntimeError("Kademlia::findNode(): For Kademlia "
00690                                 "redundantNodes must be at least 2 "
00691                                 "and lookupMerge should be true!");
00692     }
00693 
00694     // create temporary comparator
00695     KeyDistanceComparator<KeyXorMetric>* comp =
00696         new KeyDistanceComparator<KeyXorMetric>( key );
00697 
00698     // select result set size
00699     bool err;
00700     int resultSize;
00701 
00702     if (numSiblings < 0) {
00703         // exhaustive iterative doesn't care about siblings
00704         resultSize = numRedundantNodes;
00705     } else {
00706         resultSize = isSiblingFor(thisNode, key, numSiblings, &err) ?
00707                 (numSiblings ? numSiblings : 1) : numRedundantNodes;
00708     }
00709     assert(numSiblings || numRedundantNodes);
00710 
00711     NodeVector* result = new NodeVector(resultSize, comp);
00712 
00713     if (siblingTable->isEmpty()) {
00714         result->add(thisNode);
00715         delete comp;
00716         return result;
00717     }
00718 
00719     // add items from buckets
00720     int index;
00721     int mainIndex = routingBucketIndex(key);
00722     int startIndex = routingBucketIndex(key, true);
00723     int endIndex = routingBucketIndex(siblingTable->back().getKey());
00724 
00725     // add nodes from best fitting bucket
00726     if (mainIndex != -1) {
00727         KademliaBucket* bucket = routingTable[mainIndex];
00728         if (bucket != NULL && bucket->size()) {
00729             for (KademliaBucket::iterator i=bucket->begin(); i!=bucket->end(); i++) {
00730                 result->add(*i);
00731                 //EV << "Kademlia::findNode(): Adding "
00732                 //   << *i << " from bucket " << mainIndex << endl;
00733             }
00734         }
00735     }
00736 
00737     // add most fitting buckets
00738     if (startIndex >= endIndex || !result->isFull()) {
00739         for (index = startIndex; index >= endIndex; --index) {
00740             // old code...
00741             /*
00742             for (int i = 1; !result->isFull() && i < numBuckets * 3; i++) {
00743                 int index = mainIndex + (((i & 1) == 1) ? -1 : 1) * (i / 2);
00744                 if (index < 0 || index >= numBuckets)
00745                     continue;
00746             */
00747 
00748             // add bucket to result vector
00749             if (index == mainIndex) continue;
00750             KademliaBucket* bucket = routingTable[index];
00751             if (bucket != NULL && bucket->size()) {
00752                 for (KademliaBucket::iterator i=bucket->begin(); i!=bucket->end(); i++) {
00753                     result->add(*i);
00754                     //EV << "Kademlia::routingGetClosestNodes(): Adding "
00755                     //   << *i << " from bucket " << index << endl;
00756                 }
00757             }
00758         }
00759 
00760         // add nodes from sibling table
00761         for (KademliaBucket::iterator i = siblingTable->begin();
00762              i != siblingTable->end(); i++) {
00763             result->add(*i);
00764         }
00765         // add local node
00766         result->add(thisNode);
00767     }
00768 
00769     // add more distant buckets
00770     for (index = mainIndex + 1; !result->isFull() && index < numBuckets;
00771          ++index) {
00772         // add bucket to result vector
00773         KademliaBucket* bucket = routingTable[index];
00774         if (bucket != NULL && bucket->size()) {
00775             for (KademliaBucket::iterator i=bucket->begin(); i!=bucket->end(); i++) {
00776                 result->add(*i);
00777                 //EV << "[Kademlia::routingGetClosestNodes()]\n"
00778                 //   << "    Adding " << *i << " from bucket " << index
00779                 //   << endl;
00780             }
00781         }
00782     }
00783 
00784     delete comp;
00785 
00786     return result;
00787 }

void Kademlia::finishOverlay (  )  [virtual]

collects statistical data in derived class

Reimplemented from BaseOverlay.

Definition at line 152 of file Kademlia.cc.

00153 {
00154     simtime_t time = globalStatistics->calcMeasuredLifetime(creationTime);
00155     if (time < GlobalStatistics::MIN_MEASURED) return;
00156 
00157     globalStatistics->addStdDev("Kademlia: Nodes replaced in buckets/s",
00158                                 nodesReplaced / time);
00159     globalStatistics->addStdDev("Kademlia: Bucket Refreshes/s",
00160                                 bucketRefreshCount / time);
00161     globalStatistics->addStdDev("Kademlia: Sibling Table Refreshes/s",
00162                                 siblingTableRefreshCount / time);
00163 }

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.

Definition at line 250 of file Kademlia.cc.

Referenced by findNode().

00251 {
00252     return k;
00253 }

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.

Definition at line 245 of file Kademlia.cc.

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

00246 {
00247     return s;
00248 }

void Kademlia::handleBucketRefreshTimerExpired (  )  [protected]

handle a expired bucket refresh timer

Definition at line 927 of file Kademlia.cc.

Referenced by handleTimerEvent().

00928 {
00929     // refresh buckets
00930     if (state != READY || (((simTime() - siblingTable->getLastUsage()) >
00931                             minSiblingTableRefreshInterval))) {
00932         if (exhaustiveRefresh) {
00933             // TODO: replace redundantNodes by k?
00934             createLookup(EXHAUSTIVE_ITERATIVE_ROUTING)->lookup(
00935                  getThisNode().getKey(), iterativeLookupConfig.redundantNodes,
00936                  0, 0, new KademliaLookupListener(this));
00937         } else {
00938             createLookup()->lookup(getThisNode().getKey(), s, 0, 0,
00939                                    new KademliaLookupListener(this));
00940         }
00941         ++siblingTableRefreshCount;
00942     }
00943 
00944     if (state == READY) {
00945         if (siblingTable->size()) {
00946             // get bit index of most significant digit that differs
00947             // from our next sibling's key to prevent us from refreshing
00948             // buckets, which can't contain any nodes
00949             int32_t diff = OverlayKey::getLength() - b*(getThisNode().getKey().
00950                 sharedPrefixLength(siblingTable->front().getKey(), b) + 1);
00951             int bucketsRefreshedPerTask = 0;
00952             for (int32_t i = OverlayKey::getLength() - b; i >= diff; i -=b ) {
00953                 for (int32_t d=0; d < ((1 << b) - 1); d++) {
00954                     int32_t index = (i / b) * ((1 << b) - 1) + d;
00955                     if ((routingTable[index] == NULL) ||
00956                         ((simTime() - routingTable[index]->getLastUsage()) >
00957                         minBucketRefreshInterval)) {
00958 
00959                         OverlayKey refreshKey =
00960                             getThisNode().getKey() ^ (OverlayKey(d+1) << i);
00961 
00962                         if (exhaustiveRefresh) {
00963                             createLookup(EXHAUSTIVE_ITERATIVE_ROUTING)->lookup(
00964                                 refreshKey, iterativeLookupConfig.redundantNodes,
00965                                 0, 0, new KademliaLookupListener(this));
00966                         } else {
00967                             createLookup()->lookup(refreshKey, s, 0, 0,
00968                                              new KademliaLookupListener(this));
00969                         }
00970 
00971                         ++bucketsRefreshedPerTask;
00972                         ++bucketRefreshCount;
00973                         setBucketUsage(refreshKey);
00974                     }
00975                 }
00976             }
00977             RECORD_STATS(globalStatistics->recordOutVector(
00978                                    "Kademlia: Buckets Refreshed Per Task",
00979                                    bucketsRefreshedPerTask));
00980         }
00981         // schedule next bucket refresh process
00982         cancelEvent(bucketRefreshTimer);
00983         scheduleAt(simTime() + (std::min(minSiblingTableRefreshInterval,
00984                         minBucketRefreshInterval) / 10.0), bucketRefreshTimer);
00985     }
00986 }

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.

Definition at line 625 of file Kademlia.cc.

00626 {
00627     assert(!failed.isUnspecified());
00628 
00629     KademliaBucket::iterator i;
00630     // check sibling table
00631     for (i = siblingTable->begin(); i != siblingTable->end(); ++i) {
00632         if (failed == *i) break;
00633     }
00634 
00635     if (i != siblingTable->end()) {
00636         // remove from sibling table
00637         NodeHandle oldSibling = *i;
00638         siblingTable->erase(i);
00639 
00640         if (siblingTable->size() < (uint32_t)getMaxNumSiblings()) {
00641             // no new replacement sibling
00642             deleteOverlayNeighborArrow(oldSibling);
00643             callUpdate(oldSibling, false);
00644         } else if (comparator->compare(oldSibling.getKey(),
00645                            siblingTable->at(getMaxNumSiblings() - 1).getKey()) < 0) {
00646             // failed sibling was replaced by next closest node in siblingTable
00647             deleteOverlayNeighborArrow(oldSibling);
00648             callUpdate(oldSibling, false);
00649 
00650             showOverlayNeighborArrow(siblingTable->at(getMaxNumSiblings() - 1),
00651                                      false, "m=m,50,100,50,100;ls=green,1");
00652             callUpdate(siblingTable->at(getMaxNumSiblings() - 1), true);
00653         }
00654 
00655         updateTooltip();
00656 
00657         // try to refill with new closest contact
00658         refillSiblingTable();
00659     } else {
00660         // check buckets
00661         uint32_t m;
00662         for (m = 0; m < routingTable.size(); ++m) {
00663             if (routingTable[m] != NULL) {
00664                 for (i = routingTable[m]->begin(); i != routingTable[m]->end();
00665                      ++i) {
00666                     if (failed == *i) {
00667                         // remove from routing table
00668                         routingTable[m]->erase(i);
00669                         return (siblingTable->size() != 0);
00670                     }
00671                 }
00672             }
00673         }
00674     }
00675     return (siblingTable->size() != 0);
00676 }

bool Kademlia::handleRpcCall ( BaseCallMessage msg  )  [virtual]

Processes Remote-Procedure-Call invocation messages.


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

Returns:
true, if rpc has been handled

Reimplemented from BaseRpc.

Definition at line 800 of file Kademlia.cc.

00801 {
00802     RPC_SWITCH_START(msg)
00803     RPC_ON_CALL(Ping) {
00804         // add active node
00805         OverlayCtrlInfo* ctrlInfo =
00806             check_and_cast<OverlayCtrlInfo*>(msg->getControlInfo());
00807         routingAdd(ctrlInfo->getSrcRoute(), true );
00808         break;
00809     }
00810     RPC_ON_CALL(FindNode)
00811     {
00812         // add active node
00813         OverlayCtrlInfo* ctrlInfo =
00814             check_and_cast<OverlayCtrlInfo*>(msg->getControlInfo());
00815         routingAdd(ctrlInfo->getSrcRoute(), true);
00816         break;
00817     }
00818     RPC_SWITCH_END()
00819     return false;
00820 }

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.

Definition at line 823 of file Kademlia.cc.

00826 {
00827     OverlayCtrlInfo* ctrlInfo =
00828         dynamic_cast<OverlayCtrlInfo*>(msg->getControlInfo());
00829     NodeHandle srcRoute = (ctrlInfo ? ctrlInfo->getSrcRoute()
00830                                     : msg->getSrcNode());
00831 
00832     RPC_SWITCH_START(msg)
00833         RPC_ON_RESPONSE(Ping) {
00834             if (state == INIT) {
00835                 // schedule bucket refresh timer
00836                 cancelEvent(bucketRefreshTimer);
00837                 scheduleAt(simTime(), bucketRefreshTimer);
00838                 state = JOIN;
00839             }
00840 
00841             // delete replacement candidate //TODO
00842             replacementCache.erase(srcRoute);
00843         }
00844         RPC_ON_RESPONSE(FindNode)
00845         {
00846             // add active node
00847             if (defaultRoutingType == SEMI_RECURSIVE_ROUTING ||
00848                 defaultRoutingType == FULL_RECURSIVE_ROUTING ||
00849                 defaultRoutingType == RECURSIVE_SOURCE_ROUTING) {
00850                 rtt = MAXTIME;
00851             }
00852             setBucketUsage(srcRoute.getKey());
00853 
00854             // add inactive nodes
00855             for (uint32_t i=0; i<_FindNodeResponse->getClosestNodesArraySize(); i++)
00856                 routingAdd(_FindNodeResponse->getClosestNodes(i), false);
00857             break;
00858         }
00859     RPC_SWITCH_END()
00860 
00861     // add node that responded
00862     routingAdd(srcRoute, true, rtt);
00863 }

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.

Definition at line 866 of file Kademlia.cc.

00870 {
00871     if (dest.isUnspecified()) return;
00872     try {
00873         RPC_SWITCH_START(msg)
00874         RPC_ON_CALL(Ping) {
00875             if (state == INIT) {
00876                 join();
00877                 return;
00878             }
00879 
00880             const NodeHandle& handle = dynamic_cast<const NodeHandle&>(dest);
00881             routingTimeout(handle.getKey());
00882 
00883             // remove node from replacementCache
00884             std::map<NodeHandle, NodeHandle>::iterator it
00885                 = replacementCache.find(handle);
00886             if (it != replacementCache.end()) {
00887                 replacementCache.erase(it);
00888             }
00889 
00890             break;
00891         }
00892         RPC_ON_CALL(FindNode) {
00893             const NodeHandle& handle = dynamic_cast<const NodeHandle&>(dest);
00894             routingTimeout(handle.getKey());
00895             setBucketUsage(handle.getKey());
00896             break;
00897         }
00898         RPC_SWITCH_END()
00899     } catch (...) {
00900         EV << "[Kademlia:handleRpcTimout() @ " << thisNode.getAddress()
00901            << " (" << thisNode.getKey().toString(16) << ")]\n"
00902            << "    ERROR: RPC timeout without key ("
00903            << msg << " -> " << dest << ")" << endl;
00904         return;
00905     }
00906 }

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

Reimplemented from BaseRpc.

Definition at line 792 of file Kademlia.cc.

00793 {
00794     if (msg == bucketRefreshTimer) {
00795         handleBucketRefreshTimerExpired();
00796     }
00797 }

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.

Definition at line 94 of file Kademlia.cc.

00095 {
00096     if (stage != MIN_STAGE_OVERLAY)
00097         return;
00098 
00099     // Kademlia provides KBR services
00100     kbr = true;
00101 
00102     // setup kademlia parameters
00103     minSiblingTableRefreshInterval = par("minSiblingTableRefreshInterval");
00104     minBucketRefreshInterval = par("minBucketRefreshInterval");
00105     exhaustiveRefresh = par("exhaustiveRefresh");
00106     maxStaleCount = par("maxStaleCount");
00107     pingNewSiblings = par("pingNewSiblings");
00108 
00109     k = par("k");
00110     b = par("b");
00111     s = par("s");
00112 
00113     // calculate number of buckets: ( (2^b)-1 ) * ( keylength / b )
00114     numBuckets = ((1L << b) - 1L) * (OverlayKey::getLength() / b);
00115 
00116     // init routing and sibling table
00117     siblingTable = new KademliaBucket(s * 5, NULL);
00118 
00119     // initialize pointers
00120     routingTable.assign(numBuckets, (KademliaBucket*)NULL);
00121 
00122     WATCH_VECTOR(*siblingTable);
00123     WATCH_VECTOR(routingTable);
00124 
00125     // self-message
00126     bucketRefreshTimer = new cMessage("bucketRefreshTimer");
00127 
00128     // statistics
00129     bucketRefreshCount = 0;
00130     siblingTableRefreshCount = 0;
00131     nodesReplaced = 0;
00132 
00133     comparator = NULL;
00134 }

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.

Definition at line 563 of file Kademlia.cc.

Referenced by findNode().

00565 {
00566     if (key.isUnspecified())
00567         error("Kademlia::isSiblingFor(): key is unspecified!");
00568 
00569     if (state != READY) {
00570         *err = true;
00571         return false;
00572     }
00573 
00574     if (numSiblings > getMaxNumSiblings()) {
00575         opp_error("Kademlia::isSiblingFor(): numSiblings too big!");
00576     }
00577 
00578     // set default number of siblings to consider
00579     if (numSiblings == -1) {
00580         numSiblings = getMaxNumSiblings();
00581     }
00582 
00583     if (numSiblings == 0) {
00584         *err = false;
00585         return (node.getKey() == key);
00586     }
00587 
00588     if (siblingTable->size() < (uint)numSiblings) {
00589         *err = false;
00590         return true;
00591     }
00592 
00593     if ((thisNode.getKey() ^ key) > (thisNode.getKey() ^ siblingTable->back().getKey())) {
00594         *err = true;
00595         return false;
00596     }
00597 
00598     KeyDistanceComparator<KeyXorMetric>* comp =
00599         new KeyDistanceComparator<KeyXorMetric>(key);
00600 
00601     // create result vector
00602     NodeVector* result = new NodeVector(numSiblings, comp);
00603 
00604     for (KademliaBucket::iterator i=siblingTable->begin();
00605          i != siblingTable->end(); i++) {
00606         result->add( *i);
00607     }
00608 
00609     // add local node
00610     result->add(thisNode);
00611 
00612     *err = false;
00613     delete comp;
00614 
00615     if (result->contains(node.getKey())) {
00616         delete result;
00617         return true;
00618     } else {
00619         delete result;
00620         assert(!(numSiblings == 1 && key == node.getKey()));
00621         return false;
00622     }
00623 }

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.

Definition at line 165 of file Kademlia.cc.

00166 {
00167     // remove current node handle from the bootstrap list
00168     if (!thisNode.getKey().isUnspecified()) {
00169         bootstrapList->removeBootstrapNode(thisNode);
00170     }
00171 
00172     // initialize routing
00173     routingDeinit();
00174     routingInit();
00175 
00176     TransportAddress handle = bootstrapList->getBootstrapNode();
00177 
00178     if (!handle.isUnspecified()) {
00179         // ping the bootstrap node to start bootstrapping
00180         pingNode(handle);
00181 
00182         /*
00183         FindNodeCall* findNodeCall = new FindNodeCall();
00184         findNodeCall->setLookupKey(handle.getKey());
00185         findNodeCall->setSrcNode(getThisNode());
00186         findNodeCall->setNumRedundantNodes(getMaxNumRedundantNodes());
00187         findNodeCall->setNumSiblings(getMaxNumSiblings());
00188         findNodeCall->setBitLength(FINDNODECALL_L(call));
00189 
00190         RECORD_STATS(numFindNodeSent++;
00191             bytesFindNodeSent += findNodeCall->getByteLength());
00192 
00193         sendUdpRpcCall(handle, findNodeCall);
00194          */
00195 
00196     } else {
00197         // we're the only node in the network
00198         state = READY;
00199         setOverlayReady(true);
00200 
00201         // schedule bucket refresh timer
00202         cancelEvent(bucketRefreshTimer);
00203         scheduleAt(simTime(), bucketRefreshTimer);
00204     }
00205 }

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

Definition at line 908 of file Kademlia.cc.

Referenced by KademliaLookupListener::lookupFinished().

00909 {
00910     if (state == JOIN) {
00911         cancelEvent(bucketRefreshTimer);
00912 
00913         if (siblingTable->size() == 0) {
00914             // initial lookup failed - get new bootstrap node
00915             join();
00916             return;
00917         }
00918 
00919         scheduleAt(simTime(), bucketRefreshTimer);
00920 
00921         state = READY;
00922         setOverlayReady(true);
00923     }
00924 }

void Kademlia::refillSiblingTable (  )  [private]

Definition at line 512 of file Kademlia.cc.

Referenced by handleFailedNode(), and routingTimeout().

00513 {
00514     if (siblingTable->size() == 0 ||
00515         siblingTable->isFull())
00516         return;
00517 
00518     int index = routingBucketIndex(siblingTable->back().getKey()) - 1;
00519     assert(index > 0);
00520 
00521     while ((routingTable[index] == NULL ||
00522             routingTable[index]->empty()) &&
00523             index < (int)(OverlayKey::getLength() - 1)) {
00524         index++;
00525     }
00526     if (index < (int)OverlayKey::getLength() &&
00527             routingTable[index] != NULL && routingTable[index]->size()) {
00528         KademliaBucket sortedBucket(k, comparator);
00529         for (uint32_t i = 0; i < routingTable[index]->size(); ++i)
00530             sortedBucket.add(routingTable[index]->at(i));
00531         siblingTable->add(sortedBucket.front());
00532         // no need to callUpdate(), because s < siblingTable->size(), thus
00533         // new sibling table entry is no real sibling
00534         routingTable[index]->
00535         erase(routingTable[index]->
00536               findIterator(sortedBucket.front().getKey()));
00537         assert(siblingTable->isFull());
00538         BUCKET_CONSISTENCY(routingTimeout: end refillSiblingTable());
00539     }
00540 }

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

Definition at line 290 of file Kademlia.cc.

Referenced by handleRpcCall(), handleRpcResponse(), and routingTimeout().

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

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

Definition at line 274 of file Kademlia.cc.

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

00275 {
00276     // get bucket index
00277     int num = routingBucketIndex(key);
00278     if (num < 0)
00279         return NULL;
00280 
00281     // get bucket and allocate if necessary
00282     KademliaBucket* bucket = routingTable[ num ];
00283     if (bucket == NULL && ensure)
00284         bucket = routingTable[ num ] = new KademliaBucket( k, comparator );
00285 
00286     // return bucket
00287     return bucket;
00288 }

int Kademlia::routingBucketIndex ( const OverlayKey key,
bool  firstOnLayer = false 
) [private]

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

Parameters:
key The key of the node
firstOnLayer If true bucket with smallest index on same layer is returned
Returns:
int The index of the bucket

Definition at line 255 of file Kademlia.cc.

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

00256 {
00257     // calculate XOR distance
00258     OverlayKey delta = key ^ getThisNode().getKey();
00259 
00260     // find first subinteger that is not zero...
00261     int i;
00262     for (i = key.getLength() - b; i >= 0 && delta.getBitRange(i, b) == 0;
00263          i -= b);
00264 
00265     if (i < 0)
00266         return -1;
00267 
00268     if (!firstOnLayer)
00269         return (i / b) * ((1 << b) - 1) + (delta.getBitRange(i, b) - 1);
00270     else
00271         return (i / b) * ((1 << b) - 1) + (pow(2, b) - 2);
00272 }

void Kademlia::routingDeinit (  )  [private]

Definition at line 225 of file Kademlia.cc.

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

00226 {
00227     // delete buckets
00228     for (uint32_t i = 0; i < routingTable.size(); i++) {
00229         if (routingTable[i] != NULL) {
00230             delete routingTable[i];
00231             routingTable[i] = NULL;
00232         }
00233     }
00234 
00235     if (siblingTable != NULL) {
00236         siblingTable->clear();
00237     }
00238 
00239     if (comparator != NULL) {
00240         delete comparator;
00241         comparator = NULL;
00242     }
00243 }

void Kademlia::routingInit (  )  [private]

Definition at line 209 of file Kademlia.cc.

Referenced by joinOverlay().

00210 {
00211     // set join state
00212     state = INIT;
00213 
00214     setOverlayReady(false);
00215 
00216     // setup comparator
00217     comparator = new KeyDistanceComparator<KeyXorMetric>( thisNode.getKey() );
00218 
00219     siblingTable->setComparator(comparator);
00220 
00221     updateTooltip();
00222     BUCKET_CONSISTENCY(routingInit: end);
00223 }

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

Definition at line 431 of file Kademlia.cc.

00432 {
00433     return routingTimeout(key, true);
00434 }

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

Definition at line 436 of file Kademlia.cc.

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

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

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

Definition at line 542 of file Kademlia.cc.

Referenced by handleBucketRefreshTimerExpired(), handleRpcResponse(), and handleRpcTimeout().

00543 {
00544     KademliaBucket* bucket = routingBucket(key, true);
00545 
00546     if (bucket)
00547         bucket->setLastUsage(simTime());
00548 
00549 /*
00550     if (!siblingTable->size() || ((siblingTable->back().getKey() ^ thisNode.getKey()) >=
00551                                   (key ^ thisNode.getKey())))
00552         siblingTable->setLastUsage(simTime());
00553  */
00554 
00555     if (((siblingTable->size() + 1) < (uint32_t)getMaxNumSiblings())
00556         || ((siblingTable->at(getMaxNumSiblings() - 2).getKey() ^ thisNode.getKey())
00557                 >= (key ^ thisNode.getKey()))) {
00558         siblingTable->setLastUsage(simTime());
00559     }
00560 
00561 }

void Kademlia::updateTooltip (  )  [protected]

updates information shown in GUI

Definition at line 994 of file Kademlia.cc.

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

00995 {
00996     if (ev.isGUI()) {
00997         std::stringstream ttString;
00998 
00999         // show our nodeId in a tooltip
01000         ttString << "This: " << thisNode << endl << "Siblings: "
01001                  << siblingTable->size();
01002 
01003         getParentModule()->getParentModule()->getDisplayString().
01004         setTagArg("tt", 0, ttString.str().c_str());
01005         getParentModule()->getDisplayString().
01006         setTagArg("tt", 0, ttString.str().c_str());
01007         getDisplayString().setTagArg("tt", 0, ttString.str().c_str());
01008     }
01009 }


Friends And Related Function Documentation

friend class KademliaLookupListener [friend]

Definition at line 112 of file Kademlia.h.

Referenced by handleBucketRefreshTimerExpired().


Member Data Documentation

uint32_t Kademlia::b [protected]

uint32_t Kademlia::bucketRefreshCount [private]

Definition at line 116 of file Kademlia.h.

Referenced by finishOverlay(), handleBucketRefreshTimerExpired(), and initializeOverlay().

cMessage* Kademlia::bucketRefreshTimer [protected]

bool Kademlia::exhaustiveRefresh [protected]

Definition at line 53 of file Kademlia.h.

Referenced by handleBucketRefreshTimerExpired(), and initializeOverlay().

uint32_t Kademlia::k [protected]

uint32_t Kademlia::maxStaleCount [protected]

Definition at line 50 of file Kademlia.h.

Referenced by initializeOverlay(), and routingTimeout().

simtime_t Kademlia::minBucketRefreshInterval [protected]

Definition at line 57 of file Kademlia.h.

Referenced by handleBucketRefreshTimerExpired(), and initializeOverlay().

Definition at line 56 of file Kademlia.h.

Referenced by handleBucketRefreshTimerExpired(), and initializeOverlay().

uint32_t Kademlia::nodesReplaced [private]

Definition at line 118 of file Kademlia.h.

Referenced by finishOverlay(), initializeOverlay(), and routingTimeout().

int Kademlia::numBuckets [private]

Definition at line 124 of file Kademlia.h.

Referenced by findNode(), and initializeOverlay().

bool Kademlia::pingNewSiblings [protected]

Definition at line 54 of file Kademlia.h.

Referenced by initializeOverlay(), and routingAdd().

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

uint32_t Kademlia::s [protected]

Definition at line 117 of file Kademlia.h.

Referenced by finishOverlay(), handleBucketRefreshTimerExpired(), and initializeOverlay().


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

Generated on Tue Sep 8 17:26:59 2009 for OverSim by  doxygen 1.5.8