7const char* TAG =
"FWD";
13 for (
auto& face : faces_) {
20 return Error::Success;
25 if (err != Error::Success) {
26 ESP_LOGE(TAG,
"Failed to initialize Content Store (size=%zu)", csMaxEntries);
34 return Error::Success;
38 if (face ==
nullptr) {
39 return Error::InvalidParam;
44 return Error::InvalidParam;
48 for (
auto& f : faces_) {
49 if (f !=
nullptr && f->id() == newFaceId) {
50 return Error::InvalidParam;
55 for (
auto& f : faces_) {
59 onPacketReceived(faceId, data, len);
62 return Error::Success;
70 for (
auto& f : faces_) {
71 if (f !=
nullptr && f->id() == faceId) {
84 for (
auto& pending : pendingInterests_) {
86 pending.interest = interest;
87 pending.dataCallback = onData;
88 pending.timeoutCallback = onTimeout;
95 if (result == PitInsertResult::Full) {
96 pending.inUse =
false;
103 return Error::Success;
114 return Error::Success;
118 for (
auto& reg : prefixRegs_) {
121 reg.callback = callback;
126 return Error::Success;
134 if (!nameResult.ok()) {
135 return nameResult.error;
141 for (
auto& reg : prefixRegs_) {
142 if (reg.inUse && reg.prefix.equals(prefix)) {
151 ESP_LOGI(TAG,
"putData called");
158 if (pitEntry !=
nullptr) {
159 ESP_LOGI(TAG,
"putData: PIT match found, forwarding to %zu faces", pitEntry->
faceCount());
160 forwardData(data, pitEntry);
163 ESP_LOGW(TAG,
"putData: no PIT match");
166 return Error::Success;
170 return fib_.
addRoute(prefix, faceId, cost);
175 if (!nameResult.ok()) {
176 return nameResult.error;
178 return fib_.
addRoute(nameResult.value, faceId, cost);
187 for (
auto& pending : pendingInterests_) {
188 if (pending.inUse && pending.interest.name().equals(entry.
name())) {
189 if (pending.timeoutCallback) {
190 pending.timeoutCallback(pending.interest);
192 pending.inUse =
false;
199 static TimeMs lastEviction = 0;
200 if (now - lastEviction > 10000) {
206void Forwarder::onPacketReceived(
FaceId faceId,
const uint8_t* data,
size_t len) {
212 uint32_t type = data[0];
213 if (type == 253 && len >= 3) {
214 type = (
static_cast<uint32_t
>(data[1]) << 8) | data[2];
220 ESP_LOGI(TAG,
"Interest received from face=%u", faceId);
222 onInterestReceived(faceId, result.value);
224 ESP_LOGW(TAG,
"Interest decode failed from face=%u, len=%zu", faceId, len);
229 ESP_LOGI(TAG,
"Data received from face=%u", faceId);
231 onDataReceived(faceId, result.value);
236void Forwarder::onInterestReceived(FaceId faceId,
const Interest& interest) {
238 const CsEntry* csEntry = cs_.
find(interest.name(), interest.mustBeFresh(),
currentTimeMs());
239 if (csEntry !=
nullptr) {
241 ESP_LOGI(TAG,
"Cache hit for Interest, sending Data to face=%u", faceId);
245 if (csEntry->data().encode(buf,
sizeof(buf), len) == Error::Success) {
247 for (
auto& f : faces_) {
249 const Error err = f->sendTo(faceId, buf, len);
250 if (err == Error::Success) {
252 ESP_LOGI(TAG,
"Cache hit: Data sent to face=%u", faceId);
264 PitEntry* pitEntry =
nullptr;
265 auto pitResult = pit_.
insert(interest, faceId, &pitEntry);
267 if (pitResult == PitInsertResult::Duplicate) {
269 ESP_LOGI(TAG,
"PIT: Duplicate Interest, dropping");
273 if (pitResult == PitInsertResult::Aggregated) {
275 ESP_LOGI(TAG,
"PIT: Aggregated Interest");
280 bool localHandled =
false;
281 char interestUri[128];
282 interest.name().toUri(interestUri,
sizeof(interestUri));
284 for (
auto& reg : prefixRegs_) {
287 reg.prefix.toUri(prefixUri,
sizeof(prefixUri));
288 const bool match = reg.prefix.isPrefixOf(interest.name());
289 ESP_LOGI(TAG,
"prefix check: interest=%s prefix=%s match=%d", interestUri, prefixUri,
293 reg.callback(interest, faceId);
303 forwardInterest(interest, faceId);
307void Forwarder::onDataReceived(FaceId faceId,
const Data& data) {
309 PitEntry* pitEntry = pit_.
find(data.name());
310 if (pitEntry ==
nullptr) {
319 for (
auto& pending : pendingInterests_) {
320 if (pending.inUse && pending.interest.name().equals(data.name())) {
321 if (pending.dataCallback) {
322 pending.dataCallback(data);
324 pending.inUse =
false;
330 forwardData(data, pitEntry);
336void Forwarder::forwardInterest(
const Interest& interest, FaceId incomingFace) {
338 if (fibEntry ==
nullptr || fibEntry->nexthopCount() == 0) {
339 ESP_LOGW(TAG,
"forwardInterest: no route");
344 interest.name().toUri(nameUri,
sizeof(nameUri));
345 ESP_LOGI(TAG,
"forwardInterest: name=%s nexthops=%zu incoming=%u", nameUri,
346 fibEntry->nexthopCount(), incomingFace);
351 if (interest.encode(buf,
sizeof(buf), len) != Error::Success) {
352 ESP_LOGE(TAG,
"forwardInterest: encode failed");
357 for (
size_t i = 0; i < fibEntry->nexthopCount(); ++i) {
358 const FaceId nextFace = fibEntry->nexthop(i).faceId;
360 ESP_LOGI(TAG,
"forwardInterest: nexthop[%zu]=%u", i, nextFace);
362 if (nextFace == incomingFace || nextFace == FACE_ID_LOCAL) {
363 ESP_LOGI(TAG,
"forwardInterest: skipping (incoming or local)");
367 for (
auto& f : faces_) {
368 if (f !=
nullptr && f->id() == nextFace) {
370 ESP_LOGI(TAG,
"forwardInterest: sent to face=%u len=%zu", nextFace, len);
378void Forwarder::forwardData(
const Data& data, PitEntry* pitEntry) {
379 if (pitEntry ==
nullptr) {
386 if (data.encode(buf,
sizeof(buf), len) != Error::Success) {
387 ESP_LOGE(TAG,
"forwardData: encode failed");
391 ESP_LOGI(TAG,
"forwardData: sending to %zu faces", pitEntry->faceCount());
394 for (
size_t i = 0; i < pitEntry->faceCount(); ++i) {
395 const FaceId destFaceId = pitEntry->face(i);
397 ESP_LOGI(TAG,
"forwardData: face[%zu]=%u", i, destFaceId);
399 if (destFaceId == FACE_ID_LOCAL) {
400 ESP_LOGI(TAG,
"forwardData: skipping local face");
406 for (
auto& f : faces_) {
408 const Error err = f->sendTo(destFaceId, buf, len);
409 if (err == Error::Success) {
410 ESP_LOGI(TAG,
"forwardData: sent to face=%u", destFaceId);
Error init(size_t maxEntries=CS_DEFAULT_ENTRIES)
Initialize the Content Store.
Error insert(const Data &data, TimeMs now)
Insert Data into the cache.
const CsEntry * find(const Name &name, bool mustBeFresh=false, TimeMs now=0) const
Search the cache by Name.
void evictStale(TimeMs now)
Remove stale entries.
const Name & name() const
Get the Name (const reference)
static Result< Data > fromWire(const uint8_t *buf, size_t len)
Decode a Data packet from TLV wire format.
NDN Face abstract base class.
void setPacketCallback(PacketCallback callback)
Set the packet receive callback.
virtual FaceId id() const =0
Get the Face ID.
const FibEntry * findLongestMatch(const Name &name) const
Look up using Longest Prefix Match.
void removeRoute(const Name &prefix, FaceId faceId)
Remove a specific next-hop.
void removeFace(FaceId faceId)
Remove a specified Face from all entries.
Error addRoute(const Name &prefix, FaceId faceId, uint8_t cost=0)
Add a route.
Error addRoute(const Name &prefix, FaceId faceId, uint8_t cost=0)
Add a route.
Error addFace(Face *face)
Add a Face.
Error registerPrefix(const Name &prefix, InterestCallback callback)
Register a prefix.
void unregisterPrefix(const Name &prefix)
Unregister a prefix.
void removeFace(FaceId faceId)
Remove a Face.
Error expressInterest(const Interest &interest, DataCallback onData, TimeoutCallback onTimeout=nullptr)
Send an Interest and wait for Data.
Error putData(const Data &data)
Send Data.
Error sendInterest(const Interest &interest)
Send an Interest (without PIT registration)
Error init(size_t csMaxEntries=CS_DEFAULT_ENTRIES)
Initialize the Forwarder.
void processEvents()
Process events.
static Result< Interest > fromWire(const uint8_t *buf, size_t len)
Decode an Interest from TLV wire format.
static Result< Name > fromUri(std::string_view uri)
Create a Name from a URI string.
size_t faceCount() const
Get the number of registered Faces.
const Name & name() const
Get the Interest Name.
PitInsertResult insert(const Interest &interest, FaceId incomingFace, PitEntry **outEntry=nullptr)
Insert an Interest.
void processTimeouts(TimeMs now, TimeoutCallback callback=nullptr)
Process timed-out entries.
PitEntry * find(const Name &name)
Find an entry by Name.
void remove(PitEntry *entry)
Remove an entry.
TimeMs currentTimeMs()
Get current time (milliseconds)
uint64_t TimeMs
Timestamp type (milliseconds)
constexpr FaceId FACE_ID_INVALID
Invalid Face ID.
constexpr FaceId FACE_ID_LOCAL
Face ID for local application.
constexpr size_t PACKET_MAX_SIZE
Maximum packet size (ESP-NOW v2.0 compatible)
uint16_t FaceId
Face identifier.
std::function< void(const Interest &, FaceId)> InterestCallback
Interest receive callback.
std::function< void(const Interest &)> TimeoutCallback
Timeout callback.
std::function< void(const Data &)> DataCallback
Data receive callback.
constexpr uint32_t Interest
Interest packet.
constexpr uint32_t Data
Data packet.
uint32_t interestsReceived
Number of Interests received.
uint32_t cacheHits
Number of cache hits.
uint32_t cacheMisses
Number of cache misses.
uint32_t dataReceived
Number of Data received.
uint32_t interestsSent
Number of Interests sent.
uint32_t dataSent
Number of Data sent.
NDN TLV (Type-Length-Value) encoding.