-
Notifications
You must be signed in to change notification settings - Fork 0
Routing and Mesh
CDP uses reactive, flood-based route discovery (RREQ/RREP) rather than a proactive routing protocol — appropriate for a mesh where nodes come and go and battery/airtime budgets are tight.
On boot, a Duck (other than DetectorDuck) is in NetworkState::SEARCHING. It attempts to join an existing CDP network; if none responds within NET_JOIN_DELAY * 5 + 5000 ms, it assumes it's the first node and sets its own state to NetworkState::PUBLIC, effectively creating a new network. MamaDuck::goPublic() can skip this phase entirely.
When Duck::sendData()/sendSealedData()/sendEncryptedData() has no cached route to the destination (and the destination isn't the broadcast or PapaDuck sentinel address), it still transmits the packet immediately (LoRa is a shared broadcast medium, so a directed send doesn't need a known route to go out over the air) and issues a route request (reservedTopic::rreq), rate-limited to once per 30 seconds per destination.
Route requests/replies are serialized as JSON via RouteJSON (src/routing/RouteJSON.h):
{"origin": "...", "destination": "...", "path": ["...", "..."]}-
RouteJSON(targetDevice, sourceDevice)builds a fresh RREQ. -
addToPath(deviceId)appends each hop as the request/response propagates. -
convertReqToRep()swaps origin/destination to turn a received RREQ into a RREP. -
getOrigin(),getDestination(),getlastInPath()extract fields on receipt.
Binary device IDs (Duid, an 8-byte array) are hex-encoded when placed into this JSON (duckutils::arrayToHexString()/hexStringToArray<T,S>()) since DUIDs can contain arbitrary/non-printable bytes.
DuckRouter (src/routing/DuckRouter.h/.cpp) maintains routingTable: a map from a (raw-byte, binary-safe) device ID string to a list of Neighbor entries, each recording a candidate next hop, its SignalScore, and last-seen time.
-
insertIntoRoutingTable(deviceID, nextHop, signalInfo)— add/update a route entry. -
getBestNextHop(targetDeviceId)— returns the neighbor with the best routing score (Neighbor::operator>()), orstd::nulloptif none is known. -
CullRoutingTable(maxSize = 3)— optionally caps entries per device to bound memory. -
getNetworkState()/setNetworkState()— theSEARCHING→PUBLIC↔DISCONNECTEDstate machine described above.
SignalScore (src/routing/SignalScore.h) captures rssi (dBm), snr (dB), and a derived overall signalScore used to rank candidate next hops.
BloomFilter (src/routing/bloomfilter.h/.cpp) prevents the same packet from being relayed/processed twice as it floods through the mesh.
- Constructed with
(numSectors, numHashes, bitsPerSector, maxMsgs)— defaults(312, 2, 32, 100). -
assignUniqueMessageId(packet)— assigns a message ID (MUID) to an outgoing packet. -
bloom_add(msg, msgSize)/bloom_check(msg, msgSize)— record/check whether a MUID has already been seen. - Internally maintains two filters (
filter1/filter2), rolling over to the second once the first fills up tomaxMsgs, so the effective "seen" window slides forward instead of the filter saturating permanently.
Every received packet's MUID is added to the filter in handleReceivedPacket() regardless of whether it was addressed to this device or being relayed, so a given packet is only ever relayed once per node.